mirror of
https://github.com/qemu/qemu.git
synced 2026-04-05 22:00:58 +00:00
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:
@@ -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
|
||||
|
||||
12
MAINTAINERS
12
MAINTAINERS
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
4
configure
vendored
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
10
meson.build
10
meson.build
@@ -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.
@@ -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
|
||||
|
||||
@@ -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.
BIN
python/wheels/meson-1.9.0-py3-none-any.whl
Normal file
BIN
python/wheels/meson-1.9.0-py3-none-any.whl
Normal file
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
125
rust/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
23
rust/bql/Cargo.toml
Normal 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
1
rust/bql/build.rs
Symbolic link
@@ -0,0 +1 @@
|
||||
../util/build.rs
|
||||
52
rust/bql/meson.build
Normal file
52
rust/bql/meson.build
Normal 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
25
rust/bql/src/bindings.rs
Normal 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"));
|
||||
@@ -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
29
rust/bql/src/lib.rs
Normal 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
27
rust/bql/wrapper.h
Normal 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
23
rust/chardev/Cargo.toml
Normal 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
1
rust/chardev/build.rs
Symbolic link
@@ -0,0 +1 @@
|
||||
../util/build.rs
|
||||
41
rust/chardev/meson.build
Normal file
41
rust/chardev/meson.build
Normal 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])
|
||||
36
rust/chardev/src/bindings.rs
Normal file
36
rust/chardev/src/bindings.rs
Normal 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 {}
|
||||
@@ -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
6
rust/chardev/src/lib.rs
Normal 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
28
rust/chardev/wrapper.h
Normal 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
20
rust/common/Cargo.toml
Normal 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
34
rust/common/meson.build
Normal 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'])
|
||||
@@ -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]
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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
22
rust/common/src/lib.rs
Normal 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
236
rust/common/src/opaque.rs
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
18
rust/common/src/zeroable.rs
Normal file
18
rust/common/src/zeroable.rs
Normal 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() };
|
||||
}
|
||||
@@ -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
1
rust/hw/char/pl011/build.rs
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../util/build.rs
|
||||
@@ -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'},
|
||||
)])
|
||||
|
||||
27
rust/hw/char/pl011/src/bindings.rs
Normal file
27
rust/hw/char/pl011/src/bindings.rs
Normal 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"));
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
51
rust/hw/char/pl011/wrapper.h
Normal file
51
rust/hw/char/pl011/wrapper.h
Normal 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
26
rust/hw/core/Cargo.toml
Normal 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
1
rust/hw/core/build.rs
Symbolic link
@@ -0,0 +1 @@
|
||||
../../util/build.rs
|
||||
80
rust/hw/core/meson.build
Normal file
80
rust/hw/core/meson.build
Normal 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
Reference in New Issue
Block a user