Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging

* cpu-exec: more cleanups to CPU loop exits
* python: bump bundled Meson to 1.9.0
* rust: require Rust 1.83.0
* rust: temporarily remove from Ubuntu CI
* rust: vmstate: convert to use builder pattern
* rust: split "qemu-api" crate
* rust: rename qemu_api_macros -> qemu_macros
* rust: re-export qemu macros from other crates
* x86: fix functional test failure for Xen emulation
* x86: cleanups

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmjK6ZsUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroNBKwf/aadInCT4vASOfpxbwZgYfYgR2m2m
# BJE9oYKxZJ6MlEOU/1Wfywf9fg4leMSh3XxkDKkEIL19yS6emwin8n3SNYrdAFn3
# 6u4IIWO4NI1Ht3NKytrqFk9wtbH9pAs/gVHLlnmpMxIqtOtZLumPAKNz8rlantmK
# UVDYL3Y0L4pD9i5FK1ObMNpk5AsWNr8Tr64fmb+nTkHutld3sBrEMCLI0+EByGyN
# lQ16sLn9PGqHOr210zuQP7wP2T3NCI3YokFSPQrUUL8LZGxRdXoNF4hI4uZDKGdn
# UbtRu9EkM052qzfsFMrEw5JSbdxEfIjKlPoFKseMv+aWvNAuximAraD3Vg==
# =Lr+x
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 17 Sep 2025 10:02:19 AM PDT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [unknown]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (60 commits)
  accel/kvm: Set guest_memfd_offset to non-zero value only when guest_memfd is valid
  accel/kvm: Zero out mem explicitly in kvm_set_user_memory_region()
  accel/kvm: Switch to check KVM_CAP_GUEST_MEMFD and KVM_CAP_USER_MEMORY2 on VM
  i386/kvm: Drop KVM_CAP_X86_SMM check in kvm_arch_init()
  multiboot: Fix the split lock
  target/i386: Define enum X86ASIdx for x86's address spaces
  i386/cpu: Enable SMM cpu address space under KVM
  hpet: guard IRQ handling with BQL
  rust: do not inline do_init_io
  rust: meson: remove unnecessary complication in device crates
  docs: update rust.rst
  rust: re-export qemu macros from common/qom/hwcore
  rust: re-export qemu_macros internal helper in "bits"
  rust: repurpose qemu_api -> tests
  rust/pl011: drop dependency on qemu_api
  rust/hpet: drop now unneeded qemu_api dep
  rust: rename qemu_api_macros -> qemu_macros
  rust: split "hwcore" crate
  rust: split "system" crate
  rust: split "chardev" crate
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2025-09-18 07:05:58 -07:00
188 changed files with 3558 additions and 2156 deletions

View File

@@ -39,9 +39,9 @@ build-system-ubuntu:
job: amd64-ubuntu2204-container
variables:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-docs --enable-rust
CONFIGURE_ARGS: --enable-docs
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
MAKE_CHECK_ARGS: check-build check-doc
MAKE_CHECK_ARGS: check-build
check-system-ubuntu:
extends: .native_test_job_template

View File

@@ -3515,9 +3515,17 @@ F: include/hw/registerfields.h
Rust
M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
S: Maintained
F: rust/qemu-api
F: rust/qemu-api-macros
F: rust/bql/
F: rust/chardev/
F: rust/common/
F: rust/hw/core/
F: rust/migration/
F: rust/qemu-macros/
F: rust/qom/
F: rust/rustfmt.toml
F: rust/system/
F: rust/tests/
F: rust/util/
F: scripts/get-wraps-from-cargo-registry.py
Rust-related patches CC here

View File

@@ -43,6 +43,7 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
bql_unlock();
#ifndef _WIN32
do {
@@ -57,7 +58,6 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_sem_wait(&cpu->sem);
#endif
bql_lock();
qemu_wait_io_event(cpu);
} while (!cpu->unplug);
bql_unlock();

View File

@@ -192,13 +192,13 @@ static void *hvf_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
r = hvf_vcpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
hvf_vcpu_destroy(cpu);

View File

@@ -47,13 +47,14 @@ static void *kvm_vcpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);

View File

@@ -358,7 +358,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
{
KVMState *s = kvm_state;
struct kvm_userspace_memory_region2 mem;
struct kvm_userspace_memory_region2 mem = {};
int ret;
mem.slot = slot->slot | (kml->as_id << 16);
@@ -1595,7 +1595,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
mem->guest_memfd = mr->ram_block->guest_memfd;
mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host;
mem->guest_memfd_offset = mem->guest_memfd >= 0 ?
(uint8_t*)ram - mr->ram_block->host : 0;
kvm_slot_init_dirty_bitmap(mem);
err = kvm_set_user_memory_region(kml, mem, true);
@@ -2776,8 +2777,8 @@ static int kvm_init(AccelState *as, MachineState *ms)
kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES);
kvm_guest_memfd_supported =
kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
kvm_vm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_vm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
@@ -3029,10 +3030,6 @@ static void kvm_eat_signals(CPUState *cpu)
if (kvm_immediate_exit) {
qatomic_set(&cpu->kvm_run->immediate_exit, 0);
/* Write kvm_run->immediate_exit before the cpu->exit_request
* write in kvm_cpu_exec.
*/
smp_wmb();
return;
}
@@ -3159,7 +3156,6 @@ int kvm_cpu_exec(CPUState *cpu)
trace_kvm_cpu_exec();
if (kvm_arch_process_async_events(cpu)) {
qatomic_set(&cpu->exit_request, 0);
return EXCP_HLT;
}
@@ -3187,7 +3183,8 @@ int kvm_cpu_exec(CPUState *cpu)
}
kvm_arch_pre_run(cpu, run);
if (qatomic_read(&cpu->exit_request)) {
/* Corresponding store-release is in cpu_exit. */
if (qatomic_load_acquire(&cpu->exit_request)) {
trace_kvm_interrupt_exit_request();
/*
* KVM requires us to reenter the kernel after IO exits to complete
@@ -3197,13 +3194,15 @@ int kvm_cpu_exec(CPUState *cpu)
kvm_cpu_kick_self();
}
/* Read cpu->exit_request before KVM_RUN reads run->immediate_exit.
* Matching barrier in kvm_eat_signals.
*/
smp_rmb();
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
/*
* After writing cpu->exit_request, cpu_exit() sends a signal that writes
* kvm->run->immediate_exit. The signal is already happening after the
* write to cpu->exit_request so, if KVM read kvm->run->immediate_exit
* as true, cpu->exit_request will always read as true.
*/
attrs = kvm_arch_post_run(cpu, run);
#ifdef KVM_HAVE_MCE_INJECTION
@@ -3346,7 +3345,6 @@ int kvm_cpu_exec(CPUState *cpu)
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
qatomic_set(&cpu->exit_request, 0);
return ret;
}
@@ -3731,7 +3729,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
have_sigbus_pending = true;
pending_sigbus_addr = addr;
pending_sigbus_code = code;
qatomic_set(&cpu->exit_request, 1);
qatomic_set(&cpu->exit_request, true);
return 0;
#else
return 1;

View File

@@ -40,6 +40,7 @@
#include "exec/replay-core.h"
#include "system/tcg.h"
#include "exec/helper-proto-common.h"
#include "tcg-accel-ops.h"
#include "tb-jmp-cache.h"
#include "tb-hash.h"
#include "tb-context.h"
@@ -748,6 +749,22 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
return false;
}
void tcg_kick_vcpu_thread(CPUState *cpu)
{
#ifndef CONFIG_USER_ONLY
/*
* Ensure cpu_exec will see the reason why the exit request was set.
* FIXME: this is not always needed. Other accelerators instead
* read interrupt_request and set exit_request on demand from the
* CPU thread; see kvm_arch_pre_run() for example.
*/
qatomic_store_release(&cpu->exit_request, true);
#endif
/* Ensure cpu_exec will see the exit request after TCG has exited. */
qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1);
}
static inline bool icount_exit_request(CPUState *cpu)
{
if (!icount_enabled()) {
@@ -774,7 +791,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request.
* Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
* cpu->interrupt_request (see also store-release in
* tcg_kick_vcpu_thread())
*/
qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0);
@@ -784,7 +802,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
if (unlikely(cpu_test_interrupt(cpu, ~0))) {
bql_lock();
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) {
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG);
cpu->exception_index = EXCP_DEBUG;
bql_unlock();
return true;
@@ -793,7 +811,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* Do nothing */
} else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) {
replay_interrupt();
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
cpu->halted = 1;
cpu->exception_index = EXCP_HLT;
bql_unlock();
@@ -840,7 +858,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
}
}
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) {
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB);
/* ensure that no TB jump will be modified as
the program flow was changed */
*last_tb = NULL;
@@ -851,9 +869,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
}
#endif /* !CONFIG_USER_ONLY */
/* Finally, check if we need to exit to the main loop. */
if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) {
qatomic_set(&cpu->exit_request, 0);
/*
* Finally, check if we need to exit to the main loop.
* The corresponding store-release is in cpu_exit.
*/
if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) {
if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT;
}

View File

@@ -84,10 +84,9 @@ static void *mttcg_cpu_thread_fn(void *arg)
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
/* process any pending work */
cpu->exit_request = 1;
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
int r;
bql_unlock();
@@ -112,8 +111,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
break;
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
tcg_cpu_destroy(cpu);
@@ -123,11 +120,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
return NULL;
}
void mttcg_kick_vcpu_thread(CPUState *cpu)
{
cpu_exit(cpu);
}
void mttcg_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];

View File

@@ -10,9 +10,6 @@
#ifndef TCG_ACCEL_OPS_MTTCG_H
#define TCG_ACCEL_OPS_MTTCG_H
/* kick MTTCG vCPU thread */
void mttcg_kick_vcpu_thread(CPUState *cpu);
/* start an mttcg vCPU thread */
void mttcg_start_vcpu_thread(CPUState *cpu);

View File

@@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused)
CPUState *cpu;
CPU_FOREACH(cpu) {
cpu_exit(cpu);
tcg_kick_vcpu_thread(cpu);
};
}
@@ -117,7 +117,7 @@ static void rr_wait_io_event(void)
rr_start_kick_timer();
CPU_FOREACH(cpu) {
qemu_wait_io_event_common(cpu);
qemu_process_cpu_events_common(cpu);
}
}
@@ -203,7 +203,7 @@ static void *rr_cpu_thread_fn(void *arg)
/* process any pending work */
CPU_FOREACH(cpu) {
current_cpu = cpu;
qemu_wait_io_event_common(cpu);
qemu_process_cpu_events_common(cpu);
}
}
@@ -211,13 +211,30 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu;
/* process any pending work */
cpu->exit_request = 1;
while (1) {
/* Only used for icount_enabled() */
int64_t cpu_budget = 0;
if (cpu) {
/*
* This could even reset exit_request for all CPUs, but in practice
* races between CPU exits and changes to "cpu" are so rare that
* there's no advantage in doing so.
*/
qatomic_set(&cpu->exit_request, false);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
rr_wait_io_event();
rr_deal_with_unplugged_cpus();
bql_unlock();
replay_mutex_lock();
bql_lock();
@@ -242,10 +259,17 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu;
}
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
/* Store rr_current_cpu before evaluating cpu_can_run(). */
while (cpu && cpu_work_list_empty(cpu)) {
/*
* Store rr_current_cpu before evaluating cpu->exit_request.
* Pairs with rr_kick_next_cpu().
*/
qatomic_set_mb(&rr_current_cpu, cpu);
/* Pairs with store-release in cpu_exit. */
if (qatomic_load_acquire(&cpu->exit_request)) {
break;
}
current_cpu = cpu;
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
@@ -285,21 +309,6 @@ static void *rr_cpu_thread_fn(void *arg)
/* Does not need a memory barrier because a spurious wakeup is okay. */
qatomic_set(&rr_current_cpu, NULL);
if (cpu && cpu->exit_request) {
qatomic_set_mb(&cpu->exit_request, 0);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
rr_wait_io_event();
rr_deal_with_unplugged_cpus();
}
g_assert_not_reached();

View File

@@ -82,8 +82,6 @@ int tcg_cpu_exec(CPUState *cpu)
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
qatomic_set_mb(&cpu->exit_request, 0);
return ret;
}
@@ -206,7 +204,7 @@ static void tcg_accel_ops_init(AccelClass *ac)
if (qemu_tcg_mttcg_enabled()) {
ops->create_vcpu_thread = mttcg_start_vcpu_thread;
ops->kick_vcpu_thread = mttcg_kick_vcpu_thread;
ops->kick_vcpu_thread = tcg_kick_vcpu_thread;
ops->handle_interrupt = tcg_handle_interrupt;
} else {
ops->create_vcpu_thread = rr_start_vcpu_thread;

View File

@@ -18,5 +18,6 @@ void tcg_cpu_destroy(CPUState *cpu);
int tcg_cpu_exec(CPUState *cpu);
void tcg_handle_interrupt(CPUState *cpu, int mask);
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
void tcg_kick_vcpu_thread(CPUState *cpu);
#endif /* TCG_ACCEL_OPS_H */

View File

@@ -38,6 +38,7 @@
#include "qemu/int128.h"
#include "trace.h"
#include "tcg/tcg-ldst.h"
#include "tcg-accel-ops.h"
#include "backend-ldst.h"
#include "internal-common.h"
#include "tb-internal.h"
@@ -46,9 +47,15 @@ __thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL
void cpu_interrupt(CPUState *cpu, int mask)
void qemu_cpu_kick(CPUState *cpu)
{
g_assert_not_reached();
tcg_kick_vcpu_thread(cpu);
}
void qemu_process_cpu_events(CPUState *cpu)
{
qatomic_set(&cpu->exit_request, false);
process_queued_cpu_work(cpu);
}
/*

View File

@@ -54,7 +54,7 @@ static inline G_NORETURN void target_cpu_loop(CPUARMState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_SWI:

View File

@@ -46,7 +46,7 @@ static inline G_NORETURN void target_cpu_loop(CPUARMState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_UDEF:
case EXCP_NOCP:

View File

@@ -113,7 +113,7 @@ static inline G_NORETURN void target_cpu_loop(CPUX86State *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case 0x80: {

View File

@@ -214,11 +214,6 @@ bool qemu_cpu_is_self(CPUState *cpu)
return thread_cpu == cpu;
}
void qemu_cpu_kick(CPUState *cpu)
{
cpu_exit(cpu);
}
/* Assumes contents are already zeroed. */
static void init_task_state(TaskState *ts)
{

View File

@@ -49,7 +49,7 @@ static inline G_NORETURN void target_cpu_loop(CPURISCVState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
signo = 0;

View File

@@ -121,7 +121,7 @@ static inline G_NORETURN void target_cpu_loop(CPUX86State *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_SYSCALL:

View File

@@ -1,3 +1,3 @@
doc-valid-idents = ["IrDA", "PrimeCell", ".."]
allow-mixed-uninlined-format-args = false
msrv = "1.77.0"
msrv = "1.83.0"

4
configure vendored
View File

@@ -1184,12 +1184,12 @@ fi
# detect rust triple
meson_version=$($meson --version)
if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then
if test "$rust" != disabled && ! version_ge "$meson_version" 1.9.0; then
if test "$rust" = enabled; then
$mkvenv ensuregroup --dir "${source_path}/python/wheels" \
${source_path}/pythondeps.toml meson-rust || exit 1
else
echo "Rust needs Meson 1.8.1, disabling" 2>&1
echo "Rust needs Meson 1.9.0, disabling" 2>&1
rust=disabled
fi
fi

View File

@@ -137,7 +137,8 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
wi->done = false;
qemu_mutex_unlock(&cpu->work_mutex);
qemu_cpu_kick(cpu);
/* exit the inner loop and reach qemu_process_cpu_events_common(). */
cpu_exit(cpu);
}
void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data,

View File

@@ -118,14 +118,15 @@ Rust build dependencies
include bindgen or have an older version, it is recommended to install
a newer version using ``cargo install bindgen-cli``.
QEMU requires Rust 1.77.0. This is available on all supported platforms
with one exception, namely the ``mips64el`` architecture on Debian bookworm.
For all other architectures, Debian bookworm provides a new-enough Rust
compiler in the ``rustc-web`` package.
QEMU requires Rust 1.83.0. This is available on all supported platforms
with two exception: Ubuntu LTS releases 22.04 and 24.04, and the
``mips64el`` architecture on Debian bookworm. For all other
architectures, Debian bookworm provides a new-enough Rust compiler
in the ``rustc-web`` package.
Also, on Ubuntu 22.04 or 24.04 this requires the ``rustc-1.77``
(or newer) package. The path to ``rustc`` and ``rustdoc`` must be
provided manually to the configure script.
It is expected that in the future Ubuntu will provide updated packages
like the existing ``rustc-1.82`` package. The path to ``rustc`` and
``rustdoc`` will have to be provided manually to the configure script.
Some distros prefer to avoid vendored crate sources, and instead use
local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a

View File

@@ -1,4 +1,4 @@
.. |msrv| replace:: 1.63.0
.. |msrv| replace:: 1.83.0
Rust in QEMU
============
@@ -75,35 +75,23 @@ Note that doctests require all ``.o`` files from the build to be available.
Supported tools
'''''''''''''''
QEMU supports rustc version 1.77.0 and newer. Notably, the following features
are missing:
QEMU supports rustc version 1.83.0 and newer. The following features
from relatively new versions of Rust are not used for historical reasons;
patches are welcome:
* inline const expression (stable in 1.79.0), currently worked around with
associated constants in the ``FnCall`` trait.
* associated constants have to be explicitly marked ``'static`` (`changed in
* associated constants are still explicitly marked ``'static`` (`changed in
1.81.0`__)
* ``&raw`` (stable in 1.82.0). Use ``addr_of!`` and ``addr_of_mut!`` instead,
though hopefully the need for raw pointers will go down over time.
* ``new_uninit`` (stable in 1.82.0). This is used internally by the ``pinned_init``
crate, which is planned for inclusion in QEMU, but it can be easily patched
out.
* referencing statics in constants (stable in 1.83.0). For now use a const
function; this is an important limitation for QEMU's migration stream
architecture (VMState). Right now, VMState lacks type safety because
it is hard to place the ``VMStateField`` definitions in traits.
* ``&raw`` (stable in 1.82.0).
* NUL-terminated file names with ``#[track_caller]`` are scheduled for
inclusion as ``#![feature(location_file_nul)]``, but it will be a while
before QEMU can use them. For now, there is special code in
``util/error.c`` to support non-NUL-terminated file names.
* associated const equality would be nice to have for some users of
``callbacks::FnCall``, but is still experimental. ``ASSERT_IS_SOME``
replaces it.
Associated const equality would be nice to have for some users of
``callbacks::FnCall``, but is still experimental. Const assertions
are used instead.
__ https://github.com/rust-lang/rust/pull/125258
@@ -115,15 +103,18 @@ anymore.
Writing Rust code in QEMU
-------------------------
QEMU includes four crates:
QEMU includes several crates:
* ``qemu_api`` for bindings to C code and useful functionality
* ``common`` provides Rust-only utilities
* ``qemu_api_macros`` defines several procedural macros that are useful when
* ``bql``, ``chardev``, ``hw/core``, ``migration``, ``qom``, ``system``,
``util`` for bindings to respective QEMU C library APIs
* ``qemu_macros`` defines several procedural macros that are useful when
writing C code
* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``)
are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are
are sample devices that demonstrate Rust binding usage and ``qemu_macros``, and are
used to further develop them. These two crates are functional\ [#issues]_ replacements
for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files.
@@ -136,7 +127,7 @@ This section explains how to work with them.
Status
''''''
Modules of ``qemu_api`` can be defined as:
The stability of the modules can be defined as:
- *complete*: ready for use in new devices; if applicable, the API supports the
full functionality available in C
@@ -152,26 +143,26 @@ Modules of ``qemu_api`` can be defined as:
The status of the modules is as follows:
================ ======================
module status
================ ======================
``assertions`` stable
``bitops`` complete
``callbacks`` complete
``cell`` stable
``errno`` complete
``error`` stable
``irq`` complete
``log`` proof of concept
``memory`` stable
``module`` complete
``qdev`` stable
``qom`` stable
``sysbus`` stable
``timer`` stable
``vmstate`` proof of concept
``zeroable`` stable
================ ======================
========================== ======================
module status
========================== ======================
``bql::cell`` stable
``common::assertions`` stable
``common::bitops`` complete
``common::callbacks`` complete
``common::errno`` complete
``common::zeroable`` stable
``hwcore::irq`` complete
``hwcore::qdev`` stable
``hwcore::sysbus`` stable
``migration::vmstate`` stable
``qom`` stable
``system::memory`` stable
``util::error`` stable
``util::log`` proof of concept
``util::module`` complete
``util::timer`` stable
========================== ======================
.. note::
API stability is not a promise, if anything because the C APIs are not a stable
@@ -272,7 +263,7 @@ to go from a shared reference to a ``&mut``.
Whenever C code provides you with an opaque ``void *``, avoid converting it
to a Rust mutable reference, and use a shared reference instead. The
``qemu_api::cell`` module provides wrappers that can be used to tell the
``bql::cell`` module provides wrappers that can be used to tell the
Rust compiler about interior mutability, and optionally to enforce locking
rules for the "Big QEMU Lock". In the future, similar cell types might
also be provided for ``AioContext``-based locking as well.
@@ -290,7 +281,7 @@ a raw pointer, for use in calls to C functions. It can be used for
example as follows::
#[repr(transparent)]
#[derive(Debug, qemu_api_macros::Wrapper)]
#[derive(Debug, common::Wrapper)]
pub struct Object(Opaque<bindings::Object>);
where the special ``derive`` macro provides useful methods such as
@@ -304,7 +295,7 @@ the wrapper to be declared thread-safe::
Writing bindings to C code
''''''''''''''''''''''''''
Here are some things to keep in mind when working on the ``qemu_api`` crate.
Here are some things to keep in mind when working on the QEMU Rust crate.
**Look at existing code**
Very often, similar idioms in C code correspond to similar tricks in
@@ -367,7 +358,7 @@ from the type after ``as`` in the invocation of ``parse_macro_input!``::
.into()
}
The ``qemu_api_macros`` crate has utility functions to examine a
The ``qemu_macros`` crate has utility functions to examine a
``DeriveInput`` and perform common checks (e.g. looking for a struct
with named fields). These functions return ``Result<..., syn::Error>``
and can be used easily in the procedural macro function::
@@ -408,7 +399,7 @@ Right now, only the nightly version of ``rustfmt`` is supported. This
might change in the future. While CI checks for correct formatting via
``cargo fmt --check``, maintainers can fix this for you when applying patches.
It is expected that ``qemu_api`` provides full ``rustdoc`` documentation for
It is expected that QEMU Rust crates provides full ``rustdoc`` documentation for
bindings that are in their final shape or close.
Adding dependencies

View File

@@ -37,7 +37,7 @@ translator starts by allocating a budget of instructions to be
executed. The budget of instructions is limited by how long it will be
until the next timer will expire. We store this budget as part of a
vCPU icount_decr field which shared with the machinery for handling
cpu_exit(). The whole field is checked at the start of every
qemu_cpu_kick(). The whole field is checked at the start of every
translated block and will cause a return to the outer loop to deal
with whatever caused the exit.

View File

@@ -67,27 +67,16 @@ CPUState *cpu_create(const char *typename)
return cpu;
}
/* Resetting the IRQ comes from across the code base so we take the
* BQL here if we need to. cpu_interrupt assumes it is held.*/
void cpu_reset_interrupt(CPUState *cpu, int mask)
{
bool need_lock = !bql_locked();
if (need_lock) {
bql_lock();
}
cpu->interrupt_request &= ~mask;
if (need_lock) {
bql_unlock();
}
qatomic_and(&cpu->interrupt_request, ~mask);
}
void cpu_exit(CPUState *cpu)
{
qatomic_set(&cpu->exit_request, 1);
/* Ensure cpu_exec will see the exit request after TCG has exited. */
smp_wmb();
qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
/* Ensure cpu_exec will see the reason why the exit request was set. */
qatomic_store_release(&cpu->exit_request, true);
qemu_cpu_kick(cpu);
}
static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)

View File

@@ -204,7 +204,7 @@ static int cpu_common_post_load(void *opaque, int version_id)
* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the
* version_id is increased.
*/
cpu->interrupt_request &= ~0x01;
cpu_reset_interrupt(cpu, 0x01);
tlb_flush(cpu);

View File

@@ -190,6 +190,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level)
if (level) {
trace_ppc_irq_cpu("stop");
cs->halted = 1;
cpu_exit(cs);
} else {
trace_ppc_irq_cpu("restart");
cs->halted = 0;
@@ -386,6 +387,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level)
if (level) {
trace_ppc_irq_cpu("stop");
cs->halted = 1;
cpu_exit(cs);
} else {
trace_ppc_irq_cpu("restart");
cs->halted = 0;

View File

@@ -509,8 +509,8 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr,
if (!cpu_has_work(cs)) {
cs->halted = 1;
cs->exception_index = EXCP_HLT;
cs->exit_request = 1;
ppc_maybe_interrupt(env);
cpu_exit(cs);
}
return H_SUCCESS;
@@ -531,8 +531,8 @@ static target_ulong h_confer_self(PowerPCCPU *cpu)
}
cs->halted = 1;
cs->exception_index = EXCP_HALTED;
cs->exit_request = 1;
ppc_maybe_interrupt(&cpu->env);
cpu_exit(cs);
return H_SUCCESS;
}
@@ -624,8 +624,7 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr,
}
cs->exception_index = EXCP_YIELD;
cs->exit_request = 1;
cpu_loop_exit(cs);
cpu_exit(cs);
return H_SUCCESS;
}

View File

@@ -221,7 +221,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr,
cs->halted = 1;
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
kvmppc_set_reg_ppc_online(cpu, 0);
qemu_cpu_kick(cs);
cpu_exit(cs);
}
static void rtas_ibm_suspend_me(PowerPCCPU *cpu, SpaprMachineState *spapr,

View File

@@ -40,6 +40,7 @@
#include "qom/object.h"
#include "qemu/lockable.h"
#include "qemu/seqlock.h"
#include "qemu/main-loop.h"
#include "trace.h"
struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX};
@@ -222,12 +223,15 @@ static void update_irq(struct HPETTimer *timer, int set)
timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED,
NULL);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
BQL_LOCK_GUARD();
qemu_irq_raise(s->irqs[route]);
} else {
BQL_LOCK_GUARD();
qemu_irq_pulse(s->irqs[route]);
}
} else {
if (!timer_fsb_route(timer)) {
BQL_LOCK_GUARD();
qemu_irq_lower(s->irqs[route]);
}
}
@@ -534,10 +538,12 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
/* i8254 and RTC output pins are disabled
* when HPET is in legacy mode */
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
BQL_LOCK_GUARD();
qemu_set_irq(s->pit_enabled, 0);
qemu_irq_lower(s->irqs[0]);
qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
BQL_LOCK_GUARD();
qemu_irq_lower(s->irqs[0]);
qemu_set_irq(s->pit_enabled, 1);
qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
@@ -683,11 +689,13 @@ static void hpet_handle_legacy_irq(void *opaque, int n, int level)
if (n == HPET_LEGACY_PIT_INT) {
if (!hpet_in_legacy_mode(s)) {
BQL_LOCK_GUARD();
qemu_set_irq(s->irqs[0], level);
}
} else {
s->rtc_irq_level = level;
if (!hpet_in_legacy_mode(s)) {
BQL_LOCK_GUARD();
qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
}
}

View File

@@ -422,6 +422,15 @@ struct qemu_work_item;
* valid under cpu_list_lock.
* @created: Indicates whether the CPU thread has been successfully created.
* @halt_cond: condition variable sleeping threads can wait on.
* @exit_request: Another thread requests the CPU to call qemu_process_cpu_events().
* Should be read only by CPU thread with load-acquire, to synchronize with
* other threads' store-release operation.
*
* In some cases, accelerator-specific code will write exit_request from
* within the same thread, to "bump" the effect of qemu_cpu_kick() to
* the one provided by cpu_exit(), especially when processing interrupt
* flags. In this case, the write and read happen in the same thread
* and the write therefore can use qemu_atomic_set().
* @interrupt_request: Indicates a pending interrupt request.
* Only used by system emulation.
* @halted: Nonzero if the CPU is in suspended state.
@@ -495,7 +504,6 @@ struct CPUState {
bool exit_request;
int exclusive_context_count;
uint32_t cflags_next_tb;
/* updates protected by BQL */
uint32_t interrupt_request;
int singlestep_enabled;
int64_t icount_budget;
@@ -830,7 +838,8 @@ bool qemu_cpu_is_self(CPUState *cpu);
* qemu_cpu_kick:
* @cpu: The vCPU to kick.
*
* Kicks @cpu's thread.
* Kicks @cpu's thread to exit the accelerator. For accelerators that
* can do that, the target vCPU thread will try not to take the BQL.
*/
void qemu_cpu_kick(CPUState *cpu);
@@ -1136,6 +1145,15 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx);
G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
/**
* qemu_process_cpu_events:
* @cpu: CPU that left the execution loop
*
* Perform accelerator-independent work after the CPU has left
* the inner execution loop.
*/
void qemu_process_cpu_events(CPUState *cpu);
/* $(top_srcdir)/cpu.c */
void cpu_class_init_props(DeviceClass *dc);
void cpu_exec_class_post_init(CPUClass *cc);

View File

@@ -17,8 +17,7 @@ bool cpu_work_list_empty(CPUState *cpu);
bool cpu_thread_is_idle(CPUState *cpu);
bool all_cpu_threads_idle(void);
bool cpu_can_run(CPUState *cpu);
void qemu_wait_io_event_common(CPUState *cpu);
void qemu_wait_io_event(CPUState *cpu);
void qemu_process_cpu_events_common(CPUState *cpu);
void cpu_thread_signal_created(CPUState *cpu);
void cpu_thread_signal_destroyed(CPUState *cpu);
void cpu_handle_guest_debug(CPUState *cpu);

View File

@@ -159,7 +159,7 @@ void cpu_loop(CPUARMState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_SWI:

View File

@@ -35,7 +35,7 @@ void cpu_loop(CPUAlphaState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_RESET:

View File

@@ -295,7 +295,7 @@ void cpu_loop(CPUARMState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch(trapnr) {
case EXCP_UDEF:

View File

@@ -36,7 +36,7 @@ void cpu_loop(CPUHexagonState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_INTERRUPT:

View File

@@ -119,7 +119,7 @@ void cpu_loop(CPUHPPAState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_SYSCALL:

View File

@@ -214,7 +214,7 @@ void cpu_loop(CPUX86State *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch(trapnr) {
case 0x80:

View File

@@ -27,7 +27,7 @@ void cpu_loop(CPULoongArchState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_INTERRUPT:

View File

@@ -33,7 +33,7 @@ void cpu_loop(CPUM68KState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch(trapnr) {
case EXCP_ILLEGAL:

View File

@@ -189,11 +189,6 @@ bool qemu_cpu_is_self(CPUState *cpu)
return thread_cpu == cpu;
}
void qemu_cpu_kick(CPUState *cpu)
{
cpu_exit(cpu);
}
void task_settid(TaskState *ts)
{
if (ts->ts_tid == 0) {

View File

@@ -32,7 +32,7 @@ void cpu_loop(CPUMBState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_INTERRUPT:

View File

@@ -74,7 +74,7 @@ void cpu_loop(CPUMIPSState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch(trapnr) {
case EXCP_SYSCALL:

View File

@@ -33,7 +33,7 @@ void cpu_loop(CPUOpenRISCState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_SYSCALL:

View File

@@ -77,7 +77,7 @@ void cpu_loop(CPUPPCState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
arch_interrupt = true;
switch (trapnr) {

View File

@@ -36,7 +36,7 @@ void cpu_loop(CPURISCVState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_INTERRUPT:

View File

@@ -64,7 +64,7 @@ void cpu_loop(CPUS390XState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case EXCP_INTERRUPT:

View File

@@ -34,7 +34,7 @@ void cpu_loop(CPUSH4State *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case 0x160:

View File

@@ -220,7 +220,7 @@ void cpu_loop (CPUSPARCState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
switch (trapnr) {
case TARGET_TT_SYSCALL:

View File

@@ -133,7 +133,7 @@ void cpu_loop(CPUXtensaState *env)
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
qemu_process_cpu_events(cs);
env->sregs[PS] &= ~PS_EXCM;
switch (trapnr) {

View File

@@ -94,12 +94,12 @@ have_rust = have_rust and add_languages('rust', native: true,
required: get_option('rust').disable_auto_if(not have_system))
if have_rust
rustc = meson.get_compiler('rust')
if rustc.version().version_compare('<1.77.0')
if rustc.version().version_compare('<1.83.0')
if get_option('rust').enabled()
error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.77.0')
error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.83.0')
else
warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.')
message('Please upgrade to at least 1.77.0 to use Rust.')
message('Please upgrade to at least 1.83.0 to use Rust.')
have_rust = false
endif
endif
@@ -1086,9 +1086,6 @@ glib = declare_dependency(dependencies: [glib_pc, gmodule],
# TODO: remove this check and the corresponding workaround (qtree) when
# the minimum supported glib is >= 2.75.3
glib_has_gslice = glib.version().version_compare('<2.75.3')
# Check whether glib has the aligned_alloc family of functions.
# <https://docs.gtk.org/glib/func.aligned_alloc.html>
glib_has_aligned_alloc = glib.version().version_compare('>=2.72.0')
# override glib dep to include the above refinements
meson.override_dependency('glib-2.0', glib)
@@ -2702,7 +2699,6 @@ config_host_data.set('CONFIG_GETLOADAVG', cc.has_function('getloadavg'))
config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range'))
config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs'))
config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice)
config_host_data.set('HAVE_GLIB_WITH_ALIGNED_ALLOC', glib_has_aligned_alloc)
config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul', prefix: osdep_prefix))
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))

Binary file not shown.

View File

@@ -208,7 +208,7 @@ ljmp2:
prot_jump: .long prot_mode
.short 8
.align 4, 0
.align 8, 0
gdt:
/* 0x00 */
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

View File

@@ -41,8 +41,8 @@ def main() -> int:
parser.parse_args()
packages = {
"meson==1.8.1":
"374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da",
"meson==1.9.0":
"45e51ddc41e37d961582d06e78c48e0f9039011587f3495c4d6b0781dad92357",
}
vendor_dir = Path(__file__, "..", "..", "wheels").resolve()

Binary file not shown.

View File

@@ -19,12 +19,12 @@
[meson]
# The install key should match the version in python/wheels/
meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" }
meson = { accepted = ">=1.5.0", installed = "1.9.0", canary = "meson" }
pycotap = { accepted = ">=1.1.0", installed = "1.3.1" }
[meson-rust]
# The install key should match the version in python/wheels/
meson = { accepted = ">=1.8.1", installed = "1.8.1", canary = "meson" }
meson = { accepted = ">=1.9.0", installed = "1.9.0", canary = "meson" }
[docs]
# Please keep the installed versions in sync with docs/requirements.txt

View File

@@ -118,7 +118,8 @@ void replay_add_event(ReplayAsyncEventKind event_kind,
g_assert(replay_mutex_locked());
QTAILQ_INSERT_TAIL(&events_list, event, events);
qemu_cpu_kick(first_cpu);
/* Kick the TCG thread out of tcg_cpu_exec(). */
cpu_exit(first_cpu);
}
void replay_bh_schedule_event(QEMUBH *bh)

125
rust/Cargo.lock generated
View File

@@ -41,7 +41,33 @@ dependencies = [
name = "bits"
version = "0.1.0"
dependencies = [
"qemu_api_macros",
"qemu_macros",
]
[[package]]
name = "bql"
version = "0.1.0"
dependencies = [
"migration",
]
[[package]]
name = "chardev"
version = "0.1.0"
dependencies = [
"bql",
"common",
"migration",
"qom",
"util",
]
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"libc",
"qemu_macros",
]
[[package]]
@@ -63,8 +89,27 @@ dependencies = [
name = "hpet"
version = "0.1.0"
dependencies = [
"qemu_api",
"qemu_api_macros",
"bql",
"common",
"hwcore",
"migration",
"qom",
"system",
"util",
]
[[package]]
name = "hwcore"
version = "0.1.0"
dependencies = [
"bql",
"chardev",
"common",
"migration",
"qemu_macros",
"qom",
"system",
"util",
]
[[package]]
@@ -82,6 +127,14 @@ version = "0.2.162"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]]
name = "migration"
version = "0.1.0"
dependencies = [
"common",
"util",
]
[[package]]
name = "pl011"
version = "0.1.0"
@@ -89,8 +142,14 @@ dependencies = [
"bilge",
"bilge-impl",
"bits",
"qemu_api",
"qemu_api_macros",
"bql",
"chardev",
"common",
"hwcore",
"migration",
"qom",
"system",
"util",
]
[[package]]
@@ -126,17 +185,7 @@ dependencies = [
]
[[package]]
name = "qemu_api"
version = "0.1.0"
dependencies = [
"anyhow",
"foreign",
"libc",
"qemu_api_macros",
]
[[package]]
name = "qemu_api_macros"
name = "qemu_macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
@@ -144,6 +193,17 @@ dependencies = [
"syn",
]
[[package]]
name = "qom"
version = "0.1.0"
dependencies = [
"bql",
"common",
"migration",
"qemu_macros",
"util",
]
[[package]]
name = "quote"
version = "1.0.36"
@@ -164,12 +224,45 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "system"
version = "0.1.0"
dependencies = [
"common",
"qom",
"util",
]
[[package]]
name = "tests"
version = "0.1.0"
dependencies = [
"bql",
"chardev",
"common",
"hwcore",
"migration",
"qom",
"system",
"util",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "util"
version = "0.1.0"
dependencies = [
"anyhow",
"common",
"foreign",
"libc",
]
[[package]]
name = "version_check"
version = "0.9.4"

View File

@@ -2,10 +2,17 @@
resolver = "2"
members = [
"bits",
"qemu-api-macros",
"qemu-api",
"bql",
"common",
"migration",
"qemu-macros",
"qom",
"system",
"hw/core",
"hw/char/pl011",
"hw/timer/hpet",
"util",
"tests",
]
[workspace.package]
@@ -13,7 +20,9 @@ edition = "2021"
homepage = "https://www.qemu.org"
license = "GPL-2.0-or-later"
repository = "https://gitlab.com/qemu-project/qemu/"
rust-version = "1.77.0"
# don't forget to update docs/devel/rust.rst msrv
rust-version = "1.83.0"
authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
[workspace.dependencies]
anyhow = "~1.0"
@@ -21,9 +30,7 @@ foreign = "~0.3.1"
libc = "0.2.162"
[workspace.lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = [
'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
] }
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] }
# Occasionally, we may need to silence warnings and clippy lints that
# were only introduced in newer Rust compiler versions. Do not croak

View File

@@ -6,7 +6,6 @@
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unnecessary_transmutes,
unsafe_op_in_unsafe_fn,
clippy::pedantic,
clippy::restriction,
@@ -14,8 +13,7 @@
clippy::missing_const_for_fn,
clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc,
clippy::too_many_arguments
clippy::missing_safety_doc
)]
//! `bindgen`-generated declarations.
@@ -56,3 +54,11 @@ unsafe impl Sync for VMStateField {}
unsafe impl Send for VMStateInfo {}
unsafe impl Sync for VMStateInfo {}
// bindgen does not derive Default here
#[allow(clippy::derivable_impls)]
impl Default for VMStateFlags {
fn default() -> Self {
Self(0)
}
}

View File

@@ -13,7 +13,7 @@ repository.workspace = true
rust-version.workspace = true
[dependencies]
qemu_api_macros = { path = "../qemu-api-macros" }
qemu_macros = { path = "../qemu-macros" }
[lints]
workspace = true

View File

@@ -3,7 +3,7 @@ _bits_rs = static_library(
'src/lib.rs',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [qemu_api_macros],
dependencies: [qemu_macros],
)
bits_rs = declare_dependency(link_with: _bits_rs)

View File

@@ -165,19 +165,19 @@ macro_rules! bits {
#[allow(dead_code)]
#[inline(always)]
pub fn set(&mut self, rhs: Self) {
pub const fn set(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
#[allow(dead_code)]
#[inline(always)]
pub fn clear(&mut self, rhs: Self) {
pub const fn clear(&mut self, rhs: Self) {
self.0 &= !rhs.0;
}
#[allow(dead_code)]
#[inline(always)]
pub fn toggle(&mut self, rhs: Self) {
pub const fn toggle(&mut self, rhs: Self) {
self.0 ^= rhs.0;
}
@@ -380,14 +380,17 @@ macro_rules! bits {
};
{ $type:ty: $expr:expr } => {
::qemu_api_macros::bits_const_internal! { $type @ ($expr) }
$crate::bits_const_internal! { $type @ ($expr) }
};
{ $type:ty as $int_type:ty: $expr:expr } => {
(::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
($crate::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
};
}
#[doc(hidden)]
pub use qemu_macros::bits_const_internal;
#[cfg(test)]
mod test {
bits! {

23
rust/bql/Cargo.toml Normal file
View File

@@ -0,0 +1,23 @@
[package]
name = "bql"
version = "0.1.0"
description = "Rust bindings for QEMU/BQL"
resolver = "2"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
migration = { path = "../migration" }
[features]
default = ["debug_cell"]
debug_cell = []
[lints]
workspace = true

1
rust/bql/build.rs Symbolic link
View File

@@ -0,0 +1 @@
../util/build.rs

52
rust/bql/meson.build Normal file
View File

@@ -0,0 +1,52 @@
_bql_cfg = run_command(rustc_args,
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
capture: true, check: true).stdout().strip().splitlines()
if get_option('debug_mutex')
_bql_cfg += ['--cfg', 'feature="debug_cell"']
endif
#
# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
#
# Rust bindings generation with `bindgen` might fail in some cases where the
# detected `libclang` does not match the expected `clang` version/target. In
# this case you must pass the path to `clang` and `libclang` to your build
# command invocation using the environment variables CLANG_PATH and
# LIBCLANG_PATH
_bql_bindings_inc_rs = rust.bindgen(
input: 'wrapper.h',
dependencies: common_ss.all_dependencies(),
output: 'bindings.inc.rs',
include_directories: bindings_incdir,
bindgen_version: ['>=0.60.0'],
args: bindgen_args_common,
)
_bql_rs = static_library(
'bql',
structured_sources(
[
'src/lib.rs',
'src/bindings.rs',
'src/cell.rs',
],
{'.': _bql_bindings_inc_rs}
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _bql_cfg,
link_with: [_migration_rs],
)
bql_rs = declare_dependency(link_with: [_bql_rs],
dependencies: [qemuutil])
# Doctests are essentially integration tests, so they need the same dependencies.
# Note that running them requires the object files for C code, so place them
# in a separate suite that is run by the "build" CI jobs rather than "check".
rust.doctest('rust-bql-rs-doctests',
_bql_rs,
protocol: 'rust',
dependencies: bql_rs,
suite: ['doc', 'rust'])

25
rust/bql/src/bindings.rs Normal file
View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#![allow(
dead_code,
improper_ctypes_definitions,
improper_ctypes,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unnecessary_transmutes,
unsafe_op_in_unsafe_fn,
clippy::pedantic,
clippy::restriction,
clippy::style,
clippy::missing_const_for_fn,
clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc,
clippy::too_many_arguments
)]
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));

View File

@@ -75,10 +75,10 @@
//!
//! ### Example
//!
//! ```
//! # use qemu_api::prelude::*;
//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
//! ```ignore
//! # use bql::BqlRefCell;
//! # use qom::{Owned, ParentField};
//! # use system::{InterruptSource, IRQState, SysBusDevice};
//! # const N_GPIOS: usize = 8;
//! # struct PL061Registers { /* ... */ }
//! # unsafe impl ObjectType for PL061State {
@@ -141,109 +141,17 @@
//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The
//! thread will panic if these rules are violated or if the BQL is not held.
//!
//! ## Opaque wrappers
//!
//! The cell types from the previous section are useful at the boundaries
//! of code that requires interior mutability. When writing glue code that
//! interacts directly with C structs, however, it is useful to operate
//! at a lower level.
//!
//! C functions often violate Rust's fundamental assumptions about memory
//! safety by modifying memory even if it is shared. Furthermore, C structs
//! often start their life uninitialized and may be populated lazily.
//!
//! For this reason, this module provides the [`Opaque<T>`] type to opt out
//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
//! value is always through raw pointers, obtained via methods like
//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
//! pointers can then be passed to C functions or dereferenced; both actions
//! require `unsafe` blocks, making it clear where safety guarantees must be
//! manually verified. For example
//!
//! ```ignore
//! unsafe {
//! let state = Opaque::<MyStruct>::uninit();
//! qemu_struct_init(state.as_mut_ptr());
//! }
//! ```
//!
//! [`Opaque<T>`] will usually be wrapped one level further, so that
//! bridge methods can be added to the wrapper:
//!
//! ```ignore
//! pub struct MyStruct(Opaque<bindings::MyStruct>);
//!
//! impl MyStruct {
//! fn new() -> Pin<Box<MyStruct>> {
//! let result = Box::pin(unsafe { Opaque::uninit() });
//! unsafe { qemu_struct_init(result.as_mut_ptr()) };
//! result
//! }
//! }
//! ```
//!
//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
//! several advantages:
//!
//! * The choice of traits to be implemented is not limited by the
//! bindgen-generated code. For example, [`Drop`] can be added without
//! disabling [`Copy`] on the underlying bindgen type
//!
//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
//! type rather than being automatically derived from the C struct's layout
//!
//! * Methods can be implemented in a separate crate from the bindgen-generated
//! bindings
//!
//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
//! implementations can be customized to be more readable than the raw C
//! struct representation
//!
//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
//! assert in the code that the right lock is taken, to use it together
//! with a custom lock guard type, or to let C code take the lock, as
//! appropriate. It is also possible to use it with non-thread-safe
//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
//! it is neither `Sync` nor `Send`.
//!
//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
//! and only at FFI boundaries. For QEMU-specific types that need interior
//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
use std::{
cell::{Cell, UnsafeCell},
cmp::Ordering,
fmt,
marker::{PhantomData, PhantomPinned},
mem::{self, MaybeUninit},
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
ptr::NonNull,
};
use crate::bindings;
/// An internal function that is used by doctests.
pub fn bql_start_test() {
// SAFETY: integration tests are run with --test-threads=1, while
// unit tests and doctests are not multithreaded and do not have
// any BQL-protected data. Just set bql_locked to true.
unsafe {
bindings::rust_bql_mock_lock();
}
}
pub fn bql_locked() -> bool {
// SAFETY: the function does nothing but return a thread-local bool
unsafe { bindings::bql_locked() }
}
fn bql_block_unlock(increase: bool) {
// SAFETY: this only adjusts a counter
unsafe {
bindings::bql_block_unlock(increase);
}
}
use migration::impl_vmstate_transparent;
/// A mutable memory location that is protected by the Big QEMU Lock.
///
@@ -323,8 +231,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let c = BqlCell::new(5);
/// ```
@@ -340,8 +248,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let c = BqlCell::new(5);
///
@@ -358,8 +266,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let cell = BqlCell::new(5);
/// assert_eq!(cell.get(), 5);
@@ -368,7 +276,7 @@ impl<T> BqlCell<T> {
/// ```
#[inline]
pub fn replace(&self, val: T) -> T {
assert!(bql_locked());
assert!(crate::is_locked());
// SAFETY: This can cause data races if called from multiple threads,
// but it won't happen as long as C code accesses the value
// under BQL protection only.
@@ -380,8 +288,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let c = BqlCell::new(5);
/// let five = c.into_inner();
@@ -389,7 +297,7 @@ impl<T> BqlCell<T> {
/// assert_eq!(five, 5);
/// ```
pub fn into_inner(self) -> T {
assert!(bql_locked());
assert!(crate::is_locked());
self.value.into_inner()
}
}
@@ -400,8 +308,8 @@ impl<T: Copy> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let c = BqlCell::new(5);
///
@@ -409,7 +317,7 @@ impl<T: Copy> BqlCell<T> {
/// ```
#[inline]
pub fn get(&self) -> T {
assert!(bql_locked());
assert!(crate::is_locked());
// SAFETY: This can cause data races if called from multiple threads,
// but it won't happen as long as C code accesses the value
// under BQL protection only.
@@ -423,8 +331,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let c = BqlCell::new(5);
///
@@ -442,8 +350,8 @@ impl<T: Default> BqlCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlCell;
/// # bql::start_test();
///
/// let c = BqlCell::new(5);
/// let five = c.take();
@@ -456,6 +364,8 @@ impl<T: Default> BqlCell<T> {
}
}
impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
/// A mutable memory location with dynamically checked borrow rules,
/// protected by the Big QEMU Lock.
///
@@ -512,7 +422,7 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlRefCell;
/// use bql::BqlRefCell;
///
/// let c = BqlRefCell::new(5);
/// ```
@@ -571,8 +481,8 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlRefCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlRefCell;
/// # bql::start_test();
///
/// let c = BqlRefCell::new(5);
///
@@ -583,8 +493,8 @@ impl<T> BqlRefCell<T> {
/// An example of panic:
///
/// ```should_panic
/// use qemu_api::cell::BqlRefCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlRefCell;
/// # bql::start_test();
///
/// let c = BqlRefCell::new(5);
///
@@ -601,7 +511,7 @@ impl<T> BqlRefCell<T> {
self.borrowed_at.set(Some(std::panic::Location::caller()));
}
bql_block_unlock(true);
crate::block_unlock(true);
// SAFETY: `BorrowRef` ensures that there is only immutable access
// to the value while borrowed.
@@ -625,8 +535,8 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlRefCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlRefCell;
/// # bql::start_test();
///
/// let c = BqlRefCell::new("hello".to_owned());
///
@@ -638,8 +548,8 @@ impl<T> BqlRefCell<T> {
/// An example of panic:
///
/// ```should_panic
/// use qemu_api::cell::BqlRefCell;
/// # qemu_api::cell::bql_start_test();
/// use bql::BqlRefCell;
/// # bql::start_test();
///
/// let c = BqlRefCell::new(5);
/// let m = c.borrow();
@@ -656,7 +566,7 @@ impl<T> BqlRefCell<T> {
}
// SAFETY: this only adjusts a counter
bql_block_unlock(true);
crate::block_unlock(true);
// SAFETY: `BorrowRefMut` guarantees unique access.
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
@@ -675,7 +585,7 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
/// use qemu_api::cell::BqlRefCell;
/// use bql::BqlRefCell;
///
/// let c = BqlRefCell::new(5);
///
@@ -764,6 +674,8 @@ impl<T> From<T> for BqlRefCell<T> {
}
}
impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
struct BorrowRef<'b> {
borrow: &'b Cell<BorrowFlag>,
}
@@ -800,7 +712,7 @@ impl Drop for BorrowRef<'_> {
let borrow = self.borrow.get();
debug_assert!(is_reading(borrow));
self.borrow.set(borrow - 1);
bql_block_unlock(false)
crate::block_unlock(false)
}
}
@@ -890,7 +802,7 @@ impl Drop for BorrowRefMut<'_> {
let borrow = self.borrow.get();
debug_assert!(is_writing(borrow));
self.borrow.set(borrow + 1);
bql_block_unlock(false)
crate::block_unlock(false)
}
}
@@ -935,167 +847,3 @@ impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
(**self).fmt(f)
}
}
/// Stores an opaque value that is shared with C code.
///
/// Often, C structs can changed when calling a C function even if they are
/// behind a shared Rust reference, or they can be initialized lazily and have
/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's
/// strict aliasing rules, which normally prevent mutation through shared
/// references.
///
/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
/// assume the usual constraints that Rust structs require, and allows using
/// shared references on the Rust side.
///
/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
/// of `T`.
#[repr(transparent)]
pub struct Opaque<T> {
value: UnsafeCell<MaybeUninit<T>>,
// PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
// one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
// see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
_pin: PhantomPinned,
}
impl<T> Opaque<T> {
/// Creates a new shared reference from a C pointer
///
/// # Safety
///
/// The pointer must be valid, though it need not point to a valid value.
pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
// SAFETY: Self is a transparent wrapper over T
unsafe { ptr.as_ref() }
}
/// Creates a new opaque object with uninitialized contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be initialized and pinned before
/// calling them.
#[allow(clippy::missing_const_for_fn)]
pub unsafe fn uninit() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::uninit()),
_pin: PhantomPinned,
}
}
/// Creates a new opaque object with zeroed contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be pinned (and possibly initialized)
/// before calling them.
#[allow(clippy::missing_const_for_fn)]
pub unsafe fn zeroed() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::zeroed()),
_pin: PhantomPinned,
}
}
/// Returns a raw mutable pointer to the opaque data.
pub const fn as_mut_ptr(&self) -> *mut T {
UnsafeCell::get(&self.value).cast()
}
/// Returns a raw pointer to the opaque data.
pub const fn as_ptr(&self) -> *const T {
self.as_mut_ptr().cast_const()
}
/// Returns a raw pointer to the opaque data that can be passed to a
/// C function as `void *`.
pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
UnsafeCell::get(&self.value).cast()
}
/// Converts a raw pointer to the wrapped type.
pub const fn raw_get(slot: *mut Self) -> *mut T {
// Compare with Linux's raw_get method, which goes through an UnsafeCell
// because it takes a *const Self instead.
slot.cast()
}
}
impl<T> fmt::Debug for Opaque<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut name: String = "Opaque<".to_string();
name += std::any::type_name::<T>();
name += ">";
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
impl<T: Default> Opaque<T> {
/// Creates a new opaque object with default contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be pinned before calling them.
pub unsafe fn new() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::new(T::default())),
_pin: PhantomPinned,
}
}
}
/// Annotates [`Self`] as a transparent wrapper for another type.
///
/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro.
///
/// # Examples
///
/// ```
/// # use std::mem::ManuallyDrop;
/// # use qemu_api::cell::Wrapper;
/// #[repr(transparent)]
/// pub struct Example {
/// inner: ManuallyDrop<String>,
/// }
///
/// unsafe impl Wrapper for Example {
/// type Wrapped = String;
/// }
/// ```
///
/// # Safety
///
/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
/// whether directly or indirectly.
///
/// # Methods
///
/// By convention, types that implement Wrapper also implement the following
/// methods:
///
/// ```ignore
/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
/// ```
///
/// They are not defined here to allow them to be `const`.
pub unsafe trait Wrapper {
type Wrapped;
}
unsafe impl<T> Wrapper for Opaque<T> {
type Wrapped = T;
}

29
rust/bql/src/lib.rs Normal file
View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
mod bindings;
use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock};
mod cell;
pub use cell::*;
/// An internal function that is used by doctests.
pub fn start_test() {
// SAFETY: integration tests are run with --test-threads=1, while
// unit tests and doctests are not multithreaded and do not have
// any BQL-protected data. Just set bql_locked to true.
unsafe {
rust_bql_mock_lock();
}
}
pub fn is_locked() -> bool {
// SAFETY: the function does nothing but return a thread-local bool
unsafe { bql_locked() }
}
pub fn block_unlock(increase: bool) {
// SAFETY: this only adjusts a counter
unsafe {
bql_block_unlock(increase);
}
}

27
rust/bql/wrapper.h Normal file
View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* This header file is meant to be used as input to the `bindgen` application
* in order to generate C FFI compatible Rust bindings.
*/
#ifndef __CLANG_STDATOMIC_H
#define __CLANG_STDATOMIC_H
/*
* Fix potential missing stdatomic.h error in case bindgen does not insert the
* correct libclang header paths on its own. We do not use stdatomic.h symbols
* in QEMU code, so it's fine to declare dummy types instead.
*/
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
} memory_order;
#endif /* __CLANG_STDATOMIC_H */
#include "qemu/osdep.h"
#include "qemu/main-loop.h"

23
rust/chardev/Cargo.toml Normal file
View File

@@ -0,0 +1,23 @@
[package]
name = "chardev"
version = "0.1.0"
description = "Rust bindings for QEMU/chardev"
resolver = "2"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
common = { path = "../common" }
bql = { path = "../bql" }
migration = { path = "../migration" }
qom = { path = "../qom" }
util = { path = "../util" }
[lints]
workspace = true

1
rust/chardev/build.rs Symbolic link
View File

@@ -0,0 +1 @@
../util/build.rs

41
rust/chardev/meson.build Normal file
View File

@@ -0,0 +1,41 @@
c_enums = [
'QEMUChrEvent',
]
_chardev_bindgen_args = []
foreach enum : c_enums
_chardev_bindgen_args += ['--rustified-enum', enum]
endforeach
# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
#
# Rust bindings generation with `bindgen` might fail in some cases where the
# detected `libclang` does not match the expected `clang` version/target. In
# this case you must pass the path to `clang` and `libclang` to your build
# command invocation using the environment variables CLANG_PATH and
# LIBCLANG_PATH
_chardev_bindings_inc_rs = rust.bindgen(
input: 'wrapper.h',
dependencies: common_ss.all_dependencies(),
output: 'bindings.inc.rs',
include_directories: bindings_incdir,
bindgen_version: ['>=0.60.0'],
args: bindgen_args_common + _chardev_bindgen_args,
)
_chardev_rs = static_library(
'chardev',
structured_sources(
[
'src/lib.rs',
'src/bindings.rs',
'src/chardev.rs',
],
{'.': _chardev_bindings_inc_rs}
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
dependencies: [common_rs, qemu_macros],
)
chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil])

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#![allow(
dead_code,
improper_ctypes_definitions,
improper_ctypes,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unnecessary_transmutes,
unsafe_op_in_unsafe_fn,
clippy::pedantic,
clippy::restriction,
clippy::style,
clippy::missing_const_for_fn,
clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc,
clippy::too_many_arguments
)]
use common::Zeroable;
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
// SAFETY: these are implemented in C; the bindings need to assert that the
// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
// When bindings for character devices are introduced, this can be
// moved to the Opaque<> wrapper in src/chardev.rs.
unsafe impl Send for CharBackend {}
unsafe impl Sync for CharBackend {}
unsafe impl Zeroable for CharBackend {}

View File

@@ -18,16 +18,15 @@ use std::{
slice,
};
use crate::{
bindings,
callbacks::FnCall,
cell::{BqlRefMut, Opaque},
prelude::*,
};
use bql::{BqlRefCell, BqlRefMut};
use common::{callbacks::FnCall, errno, Opaque};
use qom::prelude::*;
use crate::bindings;
/// A safe wrapper around [`bindings::Chardev`].
#[repr(transparent)]
#[derive(qemu_api_macros::Wrapper)]
#[derive(common::Wrapper)]
pub struct Chardev(Opaque<bindings::Chardev>);
pub type ChardevClass = bindings::ChardevClass;
@@ -43,13 +42,15 @@ pub struct CharBackend {
_pin: PhantomPinned,
}
impl Write for BqlRefMut<'_, bindings::CharBackend> {
pub struct CharBackendMut<'a>(BqlRefMut<'a, bindings::CharBackend>);
impl Write for CharBackendMut<'_> {
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let chr: &mut bindings::CharBackend = self;
let chr: &mut bindings::CharBackend = &mut self.0;
let len = buf.len().try_into().unwrap();
let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -57,7 +58,7 @@ impl Write for BqlRefMut<'_, bindings::CharBackend> {
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
let chr: &mut bindings::CharBackend = self;
let chr: &mut bindings::CharBackend = &mut self.0;
let len = buf.len().try_into().unwrap();
let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -138,7 +139,7 @@ impl CharBackend {
F::call((owner, event))
}
let _: () = CanReceiveFn::ASSERT_IS_SOME;
const { assert!(CanReceiveFn::IS_SOME) };
let receive_cb: Option<unsafe extern "C" fn(*mut c_void, *const u8, c_int)> =
if ReceiveFn::is_some() {
Some(rust_receive_cb::<T, ReceiveFn>)
@@ -197,7 +198,7 @@ impl CharBackend {
/// the big QEMU lock while the character device is borrowed, as
/// that might cause C code to write to the character device.
pub fn borrow_mut(&self) -> impl Write + '_ {
self.inner.borrow_mut()
CharBackendMut(self.inner.borrow_mut())
}
/// Send a continuous stream of zero bits on the line if `enabled` is

6
rust/chardev/src/lib.rs Normal file
View File

@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pub mod bindings;
mod chardev;
pub use chardev::*;

28
rust/chardev/wrapper.h Normal file
View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* This header file is meant to be used as input to the `bindgen` application
* in order to generate C FFI compatible Rust bindings.
*/
#ifndef __CLANG_STDATOMIC_H
#define __CLANG_STDATOMIC_H
/*
* Fix potential missing stdatomic.h error in case bindgen does not insert the
* correct libclang header paths on its own. We do not use stdatomic.h symbols
* in QEMU code, so it's fine to declare dummy types instead.
*/
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
} memory_order;
#endif /* __CLANG_STDATOMIC_H */
#include "qemu/osdep.h"
#include "chardev/char-fe.h"
#include "chardev/char-serial.h"

20
rust/common/Cargo.toml Normal file
View File

@@ -0,0 +1,20 @@
[package]
name = "common"
version = "0.1.0"
description = "Rust common code for QEMU"
resolver = "2"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
libc.workspace = true
qemu_macros = { path = "../qemu-macros" }
[lints]
workspace = true

34
rust/common/meson.build Normal file
View File

@@ -0,0 +1,34 @@
_common_cfg = run_command(rustc_args,
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
capture: true, check: true).stdout().strip().splitlines()
_common_rs = static_library(
'common',
structured_sources(
[
'src/lib.rs',
'src/assertions.rs',
'src/bitops.rs',
'src/callbacks.rs',
'src/errno.rs',
'src/opaque.rs',
'src/uninit.rs',
'src/zeroable.rs',
],
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _common_cfg,
dependencies: [libc_rs, qemu_macros],
)
common_rs = declare_dependency(link_with: [_common_rs])
# Doctests are essentially integration tests, so they need the same dependencies.
# Note that running them requires the object files for C code, so place them
# in a separate suite that is run by the "build" CI jobs rather than "check".
rust.doctest('rust-common-doctests',
_common_rs,
protocol: 'rust',
dependencies: common_rs,
suite: ['doc', 'rust'])

View File

@@ -8,7 +8,7 @@
//! types match the expectations of C code.
//!
//! Documentation is hidden because it only exposes macros, which
//! are exported directly from `qemu_api`.
//! are exported directly from `common`.
// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292
// (stackoverflow answers are released under MIT license).
@@ -27,7 +27,7 @@ impl<T> EqType for T {
/// # Examples
///
/// ```
/// # use qemu_api::assert_same_type;
/// # use common::assert_same_type;
/// # use std::ops::Deref;
/// assert_same_type!(u32, u32);
/// assert_same_type!(<Box<u32> as Deref>::Target, u32);
@@ -36,7 +36,7 @@ impl<T> EqType for T {
/// Different types will cause a compile failure
///
/// ```compile_fail
/// # use qemu_api::assert_same_type;
/// # use common::assert_same_type;
/// assert_same_type!(&Box<u32>, &u32);
/// ```
#[macro_export]
@@ -61,7 +61,7 @@ macro_rules! assert_same_type {
/// # Examples
///
/// ```
/// # use qemu_api::assert_field_type;
/// # use common::assert_field_type;
/// pub struct A {
/// field1: u32,
/// }
@@ -72,7 +72,7 @@ macro_rules! assert_same_type {
/// Different types will cause a compile failure
///
/// ```compile_fail
/// # use qemu_api::assert_field_type;
/// # use common::assert_field_type;
/// # pub struct A { field1: u32 }
/// assert_field_type!(A, field1, i32);
/// ```
@@ -81,8 +81,8 @@ macro_rules! assert_field_type {
(@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => {
const _: () = {
#[allow(unused)]
fn assert_field_type($param_name: &$t) {
fn types_must_be_equal<T, U>(_: &T)
const fn assert_field_type($param_name: &$t) {
const fn types_must_be_equal<T, U>(_: &T)
where
T: $crate::assertions::EqType<Itself = U>,
{
@@ -95,10 +95,6 @@ macro_rules! assert_field_type {
($t:ty, $i:tt, $ti:ty) => {
$crate::assert_field_type!(@internal v, $ti, $t, v.$i);
};
($t:ty, $i:tt, $ti:ty, num = $num:ident) => {
$crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]);
};
}
/// Assert that an expression matches a pattern. This can also be
@@ -107,7 +103,7 @@ macro_rules! assert_field_type {
/// # Examples
///
/// ```
/// # use qemu_api::assert_match;
/// # use common::assert_match;
/// // JoinHandle does not implement `Eq`, therefore the result
/// // does not either.
/// let result: Result<std::thread::JoinHandle<()>, u32> = Err(42);
@@ -136,12 +132,12 @@ macro_rules! assert_match {
/// # Examples
///
/// ```
/// # use qemu_api::static_assert;
/// # use common::static_assert;
/// static_assert!("abc".len() == 3);
/// ```
///
/// ```compile_fail
/// # use qemu_api::static_assert;
/// # use common::static_assert;
/// static_assert!("abc".len() == 2); // does not compile
/// ```
#[macro_export]

View File

@@ -3,7 +3,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//! This module provides bit operation extensions to integer types.
//! It is usually included via the `qemu_api` prelude.
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,

View File

@@ -55,7 +55,7 @@ use std::{mem, ptr::NonNull};
/// # Examples
///
/// ```
/// # use qemu_api::callbacks::FnCall;
/// # use common::callbacks::FnCall;
/// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// F::call((s,))
/// }
@@ -71,7 +71,7 @@ use std::{mem, ptr::NonNull};
/// Attempting to pass a non-zero-sized closure causes a compile-time failure:
///
/// ```compile_fail
/// # use qemu_api::callbacks::FnCall;
/// # use common::callbacks::FnCall;
/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String {
/// # F::call((s,))
/// # }
@@ -82,7 +82,7 @@ use std::{mem, ptr::NonNull};
/// `()` can be used to indicate "no function":
///
/// ```
/// # use qemu_api::callbacks::FnCall;
/// # use common::callbacks::FnCall;
/// fn optional<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
/// if F::IS_SOME {
/// Some(F::call((s,)))
@@ -97,7 +97,7 @@ use std::{mem, ptr::NonNull};
/// Invoking `F::call` will then be a run-time error.
///
/// ```should_panic
/// # use qemu_api::callbacks::FnCall;
/// # use common::callbacks::FnCall;
/// # fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// # F::call((s,))
/// # }
@@ -113,31 +113,6 @@ use std::{mem, ptr::NonNull};
/// This is always true for zero-capture closures and function pointers, as long
/// as the code is able to name the function in the first place.
pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
/// Referring to this internal constant asserts that the `Self` type is
/// zero-sized. Can be replaced by an inline const expression in
/// Rust 1.79.0+.
const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
/// Referring to this constant asserts that the `Self` type is an actual
/// function type, which can be used to catch incorrect use of `()`
/// at compile time.
///
/// # Examples
///
/// ```compile_fail
/// # use qemu_api::callbacks::FnCall;
/// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// let _: () = F::ASSERT_IS_SOME;
/// F::call((s,))
/// }
///
/// let s: String = call_it((), "hello world"); // does not compile
/// ```
///
/// Note that this can be more simply `const { assert!(F::IS_SOME) }` in
/// Rust 1.79.0 or newer.
const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) };
/// `true` if `Self` is an actual function type and not `()`.
///
/// # Examples
@@ -145,7 +120,7 @@ pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
/// You can use `IS_SOME` to catch this at compile time:
///
/// ```compile_fail
/// # use qemu_api::callbacks::FnCall;
/// # use common::callbacks::FnCall;
/// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// const { assert!(F::IS_SOME) }
/// F::call((s,))
@@ -195,7 +170,7 @@ macro_rules! impl_call {
#[inline(always)]
fn call(a: ($($args,)*)) -> R {
let _: () = Self::ASSERT_ZERO_SIZED;
const { assert!(mem::size_of::<Self>() == 0) };
// SAFETY: the safety of this method is the condition for implementing
// `FnCall`. As to the `NonNull` idiom to create a zero-sized type,

View File

@@ -7,7 +7,10 @@
//! convention. This module provides functions to portably convert an integer
//! into an [`io::Result`] and back.
use std::{convert::TryFrom, io, io::ErrorKind};
use std::{
convert::{self, TryFrom},
io::{self, ErrorKind},
};
/// An `errno` value that can be converted into an [`io::Error`]
pub struct Errno(pub u16);
@@ -99,6 +102,12 @@ impl From<io::Error> for Errno {
}
}
impl From<convert::Infallible> for Errno {
fn from(_value: convert::Infallible) -> Errno {
panic!("unreachable")
}
}
/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`]
/// for the "right" set of types.
mod traits {
@@ -176,7 +185,7 @@ use traits::{GetErrno, MergeErrno};
/// are interpreted as negated `errno` and turned into an `Err`.
///
/// ```
/// # use qemu_api::errno::into_io_result;
/// # use common::errno::into_io_result;
/// # use std::io::ErrorKind;
/// let ok = into_io_result(1i32).unwrap();
/// assert_eq!(ok, 1u32);
@@ -192,7 +201,7 @@ use traits::{GetErrno, MergeErrno};
/// likely overflows and will panic:
///
/// ```should_panic
/// # use qemu_api::errno::into_io_result;
/// # use common::errno::into_io_result;
/// # #[allow(dead_code)]
/// let err = into_io_result(-0x1234_5678i32); // panic
/// ```
@@ -204,7 +213,7 @@ pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
/// values to report errors.
///
/// ```
/// # use qemu_api::errno::into_neg_errno;
/// # use common::errno::into_neg_errno;
/// # use std::io::{self, ErrorKind};
/// let ok: io::Result<()> = Ok(());
/// assert_eq!(into_neg_errno(ok), 0);
@@ -223,7 +232,7 @@ pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
/// positive:
///
/// ```should_panic
/// # use qemu_api::errno::into_neg_errno;
/// # use common::errno::into_neg_errno;
/// # use std::io;
/// let err: io::Result<u32> = Ok(0x8899_AABB);
/// into_neg_errno(err) // panic

22
rust/common/src/lib.rs Normal file
View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pub use qemu_macros::{TryInto, Wrapper};
pub mod assertions;
pub mod bitops;
pub mod callbacks;
pub use callbacks::FnCall;
pub mod errno;
pub use errno::Errno;
pub mod opaque;
pub use opaque::{Opaque, Wrapper};
pub mod uninit;
pub use uninit::MaybeUninitField;
pub mod zeroable;
pub use zeroable::Zeroable;

236
rust/common/src/opaque.rs Normal file
View File

@@ -0,0 +1,236 @@
// SPDX-License-Identifier: MIT
//! ## Opaque wrappers
//!
//! The cell types from the previous section are useful at the boundaries
//! of code that requires interior mutability. When writing glue code that
//! interacts directly with C structs, however, it is useful to operate
//! at a lower level.
//!
//! C functions often violate Rust's fundamental assumptions about memory
//! safety by modifying memory even if it is shared. Furthermore, C structs
//! often start their life uninitialized and may be populated lazily.
//!
//! For this reason, this module provides the [`Opaque<T>`] type to opt out
//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
//! value is always through raw pointers, obtained via methods like
//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
//! pointers can then be passed to C functions or dereferenced; both actions
//! require `unsafe` blocks, making it clear where safety guarantees must be
//! manually verified. For example
//!
//! ```ignore
//! unsafe {
//! let state = Opaque::<MyStruct>::uninit();
//! qemu_struct_init(state.as_mut_ptr());
//! }
//! ```
//!
//! [`Opaque<T>`] will usually be wrapped one level further, so that
//! bridge methods can be added to the wrapper:
//!
//! ```ignore
//! pub struct MyStruct(Opaque<bindings::MyStruct>);
//!
//! impl MyStruct {
//! fn new() -> Pin<Box<MyStruct>> {
//! let result = Box::pin(unsafe { Opaque::uninit() });
//! unsafe { qemu_struct_init(result.as_mut_ptr()) };
//! result
//! }
//! }
//! ```
//!
//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
//! several advantages:
//!
//! * The choice of traits to be implemented is not limited by the
//! bindgen-generated code. For example, [`Drop`] can be added without
//! disabling [`Copy`] on the underlying bindgen type
//!
//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
//! type rather than being automatically derived from the C struct's layout
//!
//! * Methods can be implemented in a separate crate from the bindgen-generated
//! bindings
//!
//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
//! implementations can be customized to be more readable than the raw C
//! struct representation
//!
//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
//! assert in the code that the right lock is taken, to use it together
//! with a custom lock guard type, or to let C code take the lock, as
//! appropriate. It is also possible to use it with non-thread-safe
//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
//! it is neither `Sync` nor `Send`.
//!
//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
//! and only at FFI boundaries. For QEMU-specific types that need interior
//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
//!
//! [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
//! [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull};
/// Stores an opaque value that is shared with C code.
///
/// Often, C structs can changed when calling a C function even if they are
/// behind a shared Rust reference, or they can be initialized lazily and have
/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's
/// strict aliasing rules, which normally prevent mutation through shared
/// references.
///
/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
/// assume the usual constraints that Rust structs require, and allows using
/// shared references on the Rust side.
///
/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
/// of `T`.
#[repr(transparent)]
pub struct Opaque<T> {
value: UnsafeCell<MaybeUninit<T>>,
// PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
// one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
// see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
_pin: PhantomPinned,
}
impl<T> Opaque<T> {
/// Creates a new shared reference from a C pointer
///
/// # Safety
///
/// The pointer must be valid, though it need not point to a valid value.
pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
// SAFETY: Self is a transparent wrapper over T
unsafe { ptr.as_ref() }
}
/// Creates a new opaque object with uninitialized contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be initialized and pinned before
/// calling them.
pub const unsafe fn uninit() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::uninit()),
_pin: PhantomPinned,
}
}
/// Creates a new opaque object with zeroed contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be pinned (and possibly initialized)
/// before calling them.
pub const unsafe fn zeroed() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::zeroed()),
_pin: PhantomPinned,
}
}
/// Returns a raw mutable pointer to the opaque data.
pub const fn as_mut_ptr(&self) -> *mut T {
UnsafeCell::get(&self.value).cast()
}
/// Returns a raw pointer to the opaque data.
pub const fn as_ptr(&self) -> *const T {
self.as_mut_ptr().cast_const()
}
/// Returns a raw pointer to the opaque data that can be passed to a
/// C function as `void *`.
pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
UnsafeCell::get(&self.value).cast()
}
/// Converts a raw pointer to the wrapped type.
pub const fn raw_get(slot: *mut Self) -> *mut T {
// Compare with Linux's raw_get method, which goes through an UnsafeCell
// because it takes a *const Self instead.
slot.cast()
}
}
impl<T> fmt::Debug for Opaque<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut name: String = "Opaque<".to_string();
name += std::any::type_name::<T>();
name += ">";
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
impl<T: Default> Opaque<T> {
/// Creates a new opaque object with default contents.
///
/// # Safety
///
/// Ultimately the pointer to the returned value will be dereferenced
/// in another `unsafe` block, for example when passing it to a C function,
/// but the functions containing the dereference are usually safe. The
/// value returned from `uninit()` must be pinned before calling them.
pub unsafe fn new() -> Self {
Self {
value: UnsafeCell::new(MaybeUninit::new(T::default())),
_pin: PhantomPinned,
}
}
}
/// Annotates [`Self`] as a transparent wrapper for another type.
///
/// Usually defined via the [`crate::Wrapper`] derive macro.
///
/// # Examples
///
/// ```
/// # use std::mem::ManuallyDrop;
/// # use common::opaque::Wrapper;
/// #[repr(transparent)]
/// pub struct Example {
/// inner: ManuallyDrop<String>,
/// }
///
/// unsafe impl Wrapper for Example {
/// type Wrapped = String;
/// }
/// ```
///
/// # Safety
///
/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
/// whether directly or indirectly.
///
/// # Methods
///
/// By convention, types that implement Wrapper also implement the following
/// methods:
///
/// ```ignore
/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
/// ```
///
/// They are not defined here to allow them to be `const`.
pub unsafe trait Wrapper {
type Wrapped;
}
unsafe impl<T> Wrapper for Opaque<T> {
type Wrapped = T;
}

View File

@@ -12,7 +12,7 @@ pub struct MaybeUninitField<'a, T, U> {
impl<'a, T, U> MaybeUninitField<'a, T, U> {
#[doc(hidden)]
pub fn new(parent: &'a mut MaybeUninit<T>, child: *mut U) -> Self {
pub const fn new(parent: &'a mut MaybeUninit<T>, child: *mut U) -> Self {
MaybeUninitField { parent, child }
}
@@ -21,7 +21,7 @@ impl<'a, T, U> MaybeUninitField<'a, T, U> {
/// Because the `MaybeUninitField` remembers the containing object,
/// it is possible to use it in foreign APIs that initialize the
/// child.
pub fn parent(f: &Self) -> *const T {
pub const fn parent(f: &Self) -> *const T {
f.parent.as_ptr()
}
@@ -30,7 +30,7 @@ impl<'a, T, U> MaybeUninitField<'a, T, U> {
/// Because the `MaybeUninitField` remembers the containing object,
/// it is possible to use it in foreign APIs that initialize the
/// child.
pub fn parent_mut(f: &mut Self) -> *mut T {
pub const fn parent_mut(f: &mut Self) -> *mut T {
f.parent.as_mut_ptr()
}
}
@@ -63,7 +63,7 @@ impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> {
/// }
///
/// # use std::mem::MaybeUninit;
/// # use qemu_api::{assert_match, uninit_field_mut};
/// # use common::{assert_match, uninit_field_mut};
///
/// let mut s: MaybeUninit<S> = MaybeUninit::zeroed();
/// uninit_field_mut!(s, x).write(5);

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//! Defines a trait for structs that can be safely initialized with zero bytes.
/// Encapsulates the requirement that
/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
/// behavior.
///
/// # Safety
///
/// Do not add this trait to a type unless all-zeroes is a valid value for the
/// type. In particular, raw pointers can be zero, but references and
/// `NonNull<T>` cannot.
pub unsafe trait Zeroable: Default {
/// Return a value of Self whose memory representation consists of all
/// zeroes, with the possible exclusion of padding bytes.
const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
}

View File

@@ -16,8 +16,14 @@ rust-version.workspace = true
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
bits = { path = "../../../bits" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
common = { path = "../../../common" }
util = { path = "../../../util" }
bql = { path = "../../../bql" }
migration = { path = "../../../migration" }
qom = { path = "../../../qom" }
chardev = { path = "../../../chardev" }
system = { path = "../../../system" }
hwcore = { path = "../../../hw/core" }
[lints]
workspace = true

1
rust/hw/char/pl011/build.rs Symbolic link
View File

@@ -0,0 +1 @@
../../../util/build.rs

View File

@@ -1,21 +1,48 @@
# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
#
# Rust bindings generation with `bindgen` might fail in some cases where the
# detected `libclang` does not match the expected `clang` version/target. In
# this case you must pass the path to `clang` and `libclang` to your build
# command invocation using the environment variables CLANG_PATH and
# LIBCLANG_PATH
_libpl011_bindings_inc_rs = rust.bindgen(
input: 'wrapper.h',
dependencies: common_ss.all_dependencies(),
output: 'bindings.inc.rs',
include_directories: bindings_incdir,
bindgen_version: ['>=0.60.0'],
args: bindgen_args_common,
)
_libpl011_rs = static_library(
'pl011',
files('src/lib.rs'),
structured_sources(
[
'src/lib.rs',
'src/bindings.rs',
'src/device.rs',
'src/registers.rs',
],
{'.' : _libpl011_bindings_inc_rs},
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [
bilge_rs,
bilge_impl_rs,
bits_rs,
qemu_api,
qemu_api_macros,
common_rs,
util_rs,
migration_rs,
bql_rs,
qom_rs,
chardev_rs,
system_rs,
hwcore_rs,
],
)
rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
link_whole: [_libpl011_rs],
# Putting proc macro crates in `dependencies` is necessary for Meson to find
# them when compiling the root per-target static rust lib.
dependencies: [bilge_impl_rs, qemu_api_macros],
variables: {'crate': 'pl011'},
)])

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#![allow(
dead_code,
improper_ctypes_definitions,
improper_ctypes,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unnecessary_transmutes,
unsafe_op_in_unsafe_fn,
clippy::pedantic,
clippy::restriction,
clippy::style,
clippy::missing_const_for_fn,
clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc,
clippy::too_many_arguments
)]
//! `bindgen`-generated declarations.
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));

View File

@@ -2,30 +2,22 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
ffi::{c_int, c_void, CStr},
mem::size_of,
ptr::NonNull,
};
use std::{ffi::CStr, mem::size_of};
use qemu_api::{
bindings::{qdev_prop_bool, qdev_prop_chr},
chardev::{CharBackend, Chardev, Event},
impl_vmstate_forward,
irq::{IRQState, InterruptSource},
log::Log,
log_mask_ln,
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
prelude::*,
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
qom::{ObjectImpl, Owned, ParentField, ParentInit},
static_assert,
sysbus::{SysBusDevice, SysBusDeviceImpl},
uninit_field_mut,
vmstate::VMStateDescription,
vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
zeroable::Zeroable,
use bql::BqlRefCell;
use chardev::{CharBackend, Chardev, Event};
use common::{static_assert, uninit_field_mut};
use hwcore::{
Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource,
ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
};
use migration::{
self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
};
use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
use util::{log::Log, log_mask_ln};
use crate::registers::{self, Interrupt, RegisterOffset};
@@ -105,12 +97,13 @@ pub struct PL011Registers {
}
#[repr(C)]
#[derive(qemu_api_macros::Object)]
#[derive(qom::Object, hwcore::Device)]
/// PL011 Device Model in QEMU
pub struct PL011State {
pub parent_obj: ParentField<SysBusDevice>,
pub iomem: MemoryRegion,
#[doc(alias = "chr")]
#[property(rename = "chardev")]
pub char_backend: CharBackend,
pub regs: BqlRefCell<PL011Registers>,
/// QEMU interrupts
@@ -129,6 +122,7 @@ pub struct PL011State {
#[doc(alias = "clk")]
pub clock: Owned<Clock>,
#[doc(alias = "migrate_clk")]
#[property(rename = "migrate-clk", default = true)]
pub migrate_clock: bool,
}
@@ -136,7 +130,7 @@ pub struct PL011State {
// structs, so the size of the Rust version must not be any larger
// than the size of the C one. If this assert triggers you need to
// expand the padding_for_rust[] array in the C PL011State struct.
static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>());
static_assert!(size_of::<PL011State>() <= size_of::<crate::bindings::PL011State>());
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
@@ -176,13 +170,8 @@ impl ObjectImpl for PL011State {
}
impl DeviceImpl for PL011State {
fn properties() -> &'static [Property] {
&PL011_PROPERTIES
}
fn vmsd() -> Option<&'static VMStateDescription> {
Some(&VMSTATE_PL011)
}
const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_PL011);
const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for PL011State {
@@ -469,10 +458,10 @@ impl PL011Registers {
false
}
pub fn post_load(&mut self) -> Result<(), ()> {
pub fn post_load(&mut self) -> Result<(), migration::InvalidError> {
/* Sanity-check input state */
if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
return Err(());
return Err(migration::InvalidError);
}
if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
@@ -531,6 +520,10 @@ impl PL011State {
/* pl011_trace_baudrate_change(s); */
}
pub fn clock_needed(&self) -> bool {
self.migrate_clock
}
fn post_init(&self) {
self.init_mmio(&self.iomem);
for irq in self.interrupts.iter() {
@@ -629,7 +622,7 @@ impl PL011State {
}
}
fn realize(&self) -> qemu_api::Result<()> {
fn realize(&self) -> util::Result<()> {
self.char_backend
.enable_handlers(self, Self::can_receive, Self::receive, Self::event);
Ok(())
@@ -647,7 +640,7 @@ impl PL011State {
}
}
pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> {
self.regs.borrow_mut().post_load()
}
}
@@ -690,7 +683,7 @@ pub unsafe extern "C" fn pl011_create(
}
#[repr(C)]
#[derive(qemu_api_macros::Object)]
#[derive(qom::Object, hwcore::Device)]
/// PL011 Luminary device model.
pub struct PL011Luminary {
parent_obj: ParentField<PL011State>,
@@ -717,87 +710,55 @@ impl DeviceImpl for PL011Luminary {}
impl ResettablePhasesImpl for PL011Luminary {}
impl SysBusDeviceImpl for PL011Luminary {}
extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
unsafe { state.as_ref().migrate_clock }
}
/// Migration subsection for [`PL011State`] clock.
static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
name: c"pl011/clock".as_ptr(),
version_id: 1,
minimum_version_id: 1,
needed: Some(pl011_clock_needed),
fields: vmstate_fields! {
vmstate_clock!(PL011State, clock),
},
..Zeroable::ZERO
};
static VMSTATE_PL011_CLOCK: VMStateDescription<PL011State> =
VMStateDescriptionBuilder::<PL011State>::new()
.name(c"pl011/clock")
.version_id(1)
.minimum_version_id(1)
.needed(&PL011State::clock_needed)
.fields(vmstate_fields! {
vmstate_of!(PL011State, clock),
})
.build();
extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
let result = unsafe { state.as_ref().post_load(version_id as u32) };
if result.is_err() {
-1
} else {
0
}
}
impl_vmstate_struct!(
PL011Registers,
VMStateDescriptionBuilder::<PL011Registers>::new()
.name(c"pl011/regs")
.version_id(2)
.minimum_version_id(2)
.fields(vmstate_fields! {
vmstate_of!(PL011Registers, flags),
vmstate_of!(PL011Registers, line_control),
vmstate_of!(PL011Registers, receive_status_error_clear),
vmstate_of!(PL011Registers, control),
vmstate_of!(PL011Registers, dmacr),
vmstate_of!(PL011Registers, int_enabled),
vmstate_of!(PL011Registers, int_level),
vmstate_of!(PL011Registers, read_fifo),
vmstate_of!(PL011Registers, ilpr),
vmstate_of!(PL011Registers, ibrd),
vmstate_of!(PL011Registers, fbrd),
vmstate_of!(PL011Registers, ifl),
vmstate_of!(PL011Registers, read_pos),
vmstate_of!(PL011Registers, read_count),
vmstate_of!(PL011Registers, read_trigger),
})
.build()
);
static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
name: c"pl011/regs".as_ptr(),
version_id: 2,
minimum_version_id: 2,
fields: vmstate_fields! {
vmstate_of!(PL011Registers, flags),
vmstate_of!(PL011Registers, line_control),
vmstate_of!(PL011Registers, receive_status_error_clear),
vmstate_of!(PL011Registers, control),
vmstate_of!(PL011Registers, dmacr),
vmstate_of!(PL011Registers, int_enabled),
vmstate_of!(PL011Registers, int_level),
vmstate_of!(PL011Registers, read_fifo),
vmstate_of!(PL011Registers, ilpr),
vmstate_of!(PL011Registers, ibrd),
vmstate_of!(PL011Registers, fbrd),
vmstate_of!(PL011Registers, ifl),
vmstate_of!(PL011Registers, read_pos),
vmstate_of!(PL011Registers, read_count),
vmstate_of!(PL011Registers, read_trigger),
},
..Zeroable::ZERO
};
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
name: c"pl011".as_ptr(),
version_id: 2,
minimum_version_id: 2,
post_load: Some(pl011_post_load),
fields: vmstate_fields! {
vmstate_unused!(core::mem::size_of::<u32>()),
vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
},
subsections: vmstate_subsections! {
VMSTATE_PL011_CLOCK
},
..Zeroable::ZERO
};
qemu_api::declare_properties! {
PL011_PROPERTIES,
qemu_api::define_property!(
c"chardev",
PL011State,
char_backend,
unsafe { &qdev_prop_chr },
CharBackend
),
qemu_api::define_property!(
c"migrate-clk",
PL011State,
migrate_clock,
unsafe { &qdev_prop_bool },
bool,
default = true
),
}
pub const VMSTATE_PL011: VMStateDescription<PL011State> =
VMStateDescriptionBuilder::<PL011State>::new()
.name(c"pl011")
.version_id(2)
.minimum_version_id(2)
.post_load(&PL011State::post_load)
.fields(vmstate_fields! {
vmstate_unused!(core::mem::size_of::<u32>()),
vmstate_of!(PL011State, regs),
})
.subsections(vmstate_subsections! {
VMSTATE_PL011_CLOCK
})
.build();

View File

@@ -12,6 +12,7 @@
//! See [`PL011State`](crate::device::PL011State) for the device model type and
//! the [`registers`] module for register types.
mod bindings;
mod device;
mod registers;

View File

@@ -10,13 +10,13 @@
use bilge::prelude::*;
use bits::bits;
use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward};
use migration::{impl_vmstate_bitsized, impl_vmstate_forward};
/// Offset of each register from the base memory address of the device.
#[doc(alias = "offset")]
#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
#[derive(Debug, Eq, PartialEq, common::TryInto)]
pub enum RegisterOffset {
/// Data Register
///

View File

@@ -0,0 +1,51 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2024 Linaro Ltd.
*
* Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* This header file is meant to be used as input to the `bindgen` application
* in order to generate C FFI compatible Rust bindings.
*/
#ifndef __CLANG_STDATOMIC_H
#define __CLANG_STDATOMIC_H
/*
* Fix potential missing stdatomic.h error in case bindgen does not insert the
* correct libclang header paths on its own. We do not use stdatomic.h symbols
* in QEMU code, so it's fine to declare dummy types instead.
*/
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
} memory_order;
#endif /* __CLANG_STDATOMIC_H */
#include "qemu/osdep.h"
#include "hw/char/pl011.h"

26
rust/hw/core/Cargo.toml Normal file
View File

@@ -0,0 +1,26 @@
[package]
name = "hwcore"
version = "0.1.0"
description = "Rust bindings for QEMU/hwcore"
resolver = "2"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
qemu_macros = { path = "../../qemu-macros" }
common = { path = "../../common" }
bql = { path = "../../bql" }
qom = { path = "../../qom" }
chardev = { path = "../../chardev" }
migration = { path = "../../migration" }
system = { path = "../../system" }
util = { path = "../../util" }
[lints]
workspace = true

1
rust/hw/core/build.rs Symbolic link
View File

@@ -0,0 +1 @@
../../util/build.rs

80
rust/hw/core/meson.build Normal file
View File

@@ -0,0 +1,80 @@
_hwcore_bindgen_args = []
c_enums = [
'DeviceCategory',
'GpioPolarity',
'MachineInitPhase',
'ResetType',
]
foreach enum : c_enums
_hwcore_bindgen_args += ['--rustified-enum', enum]
endforeach
blocked_type = [
'Chardev',
'Error',
'ObjectClass',
'MemoryRegion',
'VMStateDescription',
]
foreach type: blocked_type
_hwcore_bindgen_args += ['--blocklist-type', type]
endforeach
c_bitfields = [
'ClockEvent',
]
foreach enum : c_bitfields
_hwcore_bindgen_args += ['--bitfield-enum', enum]
endforeach
# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
#
# Rust bindings generation with `bindgen` might fail in some cases where the
# detected `libclang` does not match the expected `clang` version/target. In
# this case you must pass the path to `clang` and `libclang` to your build
# command invocation using the environment variables CLANG_PATH and
# LIBCLANG_PATH
_hwcore_bindings_inc_rs = rust.bindgen(
input: 'wrapper.h',
dependencies: common_ss.all_dependencies(),
output: 'bindings.inc.rs',
include_directories: bindings_incdir,
bindgen_version: ['>=0.60.0'],
args: bindgen_args_common + _hwcore_bindgen_args,
)
_hwcore_rs = static_library(
'hwcore',
structured_sources(
[
'src/lib.rs',
'src/bindings.rs',
'src/irq.rs',
'src/qdev.rs',
'src/sysbus.rs',
],
{'.': _hwcore_bindings_inc_rs}
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs],
dependencies: [qemu_macros, common_rs],
)
hwcore_rs = declare_dependency(link_with: [_hwcore_rs],
dependencies: [qom_rs, hwcore])
test('rust-hwcore-rs-integration',
executable(
'rust-hwcore-rs-integration',
files('tests/tests.rs'),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: ['--test'],
install: false,
dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]),
args: [
'--test', '--test-threads', '1',
'--format', 'pretty',
],
protocol: 'rust',
suite: ['unit', 'rust'])

Some files were not shown because too many files have changed in this diff Show More