mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-31 05:51:53 -06:00
* ci: enable RISC-V cross jobs
* rust: bump minimum supported version to 1.77 * rust: enable uninlined_format_args lint * initial Emscripten support * small fixes -----BEGIN PGP SIGNATURE----- iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmgaFq8UHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroOxAAf/YPKw5KAoE5SwUqJ0oSOMHrU0w4jc A2Qiw1uziA6kDmUMUXwWR7Qbd8V7jtihGrgTrIPopeavgWWQeNsBHjN4WxHRI7aq +429rjzFo9V9tSfgp6UcLQSk/9kC4ygEwPnesHpKd27fS6z9Wg4AQkj1iFipR179 wC3fqwOqqWZSjfUd7wjo7McFYZgL5j/cxmFXePh8+fdT+6PUKdG9nRW86KUPDZ+A 8HxcuOW7GZd+LhnYUhi7vlLFo/RgVsGQWj0Q4BDJvUkKa13t9UUCGff7uQP2AC3v ny0gWDcmbWY1L/QXyNzhgd44X4LAjCmpnonlYnrdZizEmhv3aByd+fANgw== =uIJK -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * ci: enable RISC-V cross jobs * rust: bump minimum supported version to 1.77 * rust: enable uninlined_format_args lint * initial Emscripten support * small fixes # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmgaFq8UHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroOxAAf/YPKw5KAoE5SwUqJ0oSOMHrU0w4jc # A2Qiw1uziA6kDmUMUXwWR7Qbd8V7jtihGrgTrIPopeavgWWQeNsBHjN4WxHRI7aq # +429rjzFo9V9tSfgp6UcLQSk/9kC4ygEwPnesHpKd27fS6z9Wg4AQkj1iFipR179 # wC3fqwOqqWZSjfUd7wjo7McFYZgL5j/cxmFXePh8+fdT+6PUKdG9nRW86KUPDZ+A # 8HxcuOW7GZd+LhnYUhi7vlLFo/RgVsGQWj0Q4BDJvUkKa13t9UUCGff7uQP2AC3v # ny0gWDcmbWY1L/QXyNzhgd44X4LAjCmpnonlYnrdZizEmhv3aByd+fANgw== # =uIJK # -----END PGP SIGNATURE----- # gpg: Signature made Tue 06 May 2025 10:03:27 EDT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # 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: (30 commits) gitlab: Enable CI for wasm build tests: Add Dockerfile containing dependencies for Emscripten build meson: Add wasm build in build scripts util: Add coroutine backend for emscripten util: exclude mmap-alloc.c from compilation target on Emscripten Disable options unsupported on Emscripten include/qemu/osdep.h: Add Emscripten-specific OS dependencies block: Fix type conflict of the copy_file_range stub block: Add including of ioctl header for Emscripten build util/cacheflush.c: Update cache flushing mechanism for Emscripten include/glib-compat.h: Poison g_list_sort and g_slist_sort target/s390x: Fix type conflict of GLib function pointers target/ppc: Fix type conflict of GLib function pointers target/i386/cpu.c: Fix type conflict of GLib function pointers target/arm/helper.c: Fix type conflict of GLib function pointers docs: build-system: fix typo ci: run RISC-V cross jobs by default rust: clippy: enable uninlined_format_args lint target/i386/emulate: fix target_ulong format strings docs: rust: update for newer minimum supported version ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
c5f122fdcc
78 changed files with 861 additions and 654 deletions
|
@ -115,3 +115,30 @@
|
||||||
- du -chs ${CI_PROJECT_DIR}/*-cache
|
- du -chs ${CI_PROJECT_DIR}/*-cache
|
||||||
variables:
|
variables:
|
||||||
QEMU_JOB_FUNCTIONAL: 1
|
QEMU_JOB_FUNCTIONAL: 1
|
||||||
|
|
||||||
|
.wasm_build_job_template:
|
||||||
|
extends: .base_job_template
|
||||||
|
stage: build
|
||||||
|
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
|
||||||
|
before_script:
|
||||||
|
- source scripts/ci/gitlab-ci-section
|
||||||
|
- section_start setup "Pre-script setup"
|
||||||
|
- JOBS=$(expr $(nproc) + 1)
|
||||||
|
- section_end setup
|
||||||
|
script:
|
||||||
|
- du -sh .git
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- section_start configure "Running configure"
|
||||||
|
- emconfigure ../configure --disable-docs
|
||||||
|
${TARGETS:+--target-list="$TARGETS"}
|
||||||
|
$CONFIGURE_ARGS ||
|
||||||
|
{ cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||||
|
- if test -n "$LD_JOBS";
|
||||||
|
then
|
||||||
|
pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||||
|
fi || exit 1;
|
||||||
|
- section_end configure
|
||||||
|
- section_start build "Building QEMU"
|
||||||
|
- emmake make -j"$JOBS"
|
||||||
|
- section_end build
|
||||||
|
|
|
@ -792,3 +792,12 @@ coverity:
|
||||||
when: never
|
when: never
|
||||||
# Always manual on forks even if $QEMU_CI == "2"
|
# Always manual on forks even if $QEMU_CI == "2"
|
||||||
- when: manual
|
- when: manual
|
||||||
|
|
||||||
|
build-wasm:
|
||||||
|
extends: .wasm_build_job_template
|
||||||
|
timeout: 2h
|
||||||
|
needs:
|
||||||
|
job: wasm-emsdk-cross-container
|
||||||
|
variables:
|
||||||
|
IMAGE: emsdk-wasm32-cross
|
||||||
|
CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter
|
||||||
|
|
|
@ -67,11 +67,8 @@ ppc64el-debian-cross-container:
|
||||||
riscv64-debian-cross-container:
|
riscv64-debian-cross-container:
|
||||||
extends: .container_job_template
|
extends: .container_job_template
|
||||||
stage: containers
|
stage: containers
|
||||||
# as we are currently based on 'sid/unstable' we may break so...
|
|
||||||
allow_failure: true
|
|
||||||
variables:
|
variables:
|
||||||
NAME: debian-riscv64-cross
|
NAME: debian-riscv64-cross
|
||||||
QEMU_JOB_OPTIONAL: 1
|
|
||||||
|
|
||||||
s390x-debian-cross-container:
|
s390x-debian-cross-container:
|
||||||
extends: .container_job_template
|
extends: .container_job_template
|
||||||
|
@ -94,3 +91,8 @@ win64-fedora-cross-container:
|
||||||
extends: .container_job_template
|
extends: .container_job_template
|
||||||
variables:
|
variables:
|
||||||
NAME: fedora-win64-cross
|
NAME: fedora-win64-cross
|
||||||
|
|
||||||
|
wasm-emsdk-cross-container:
|
||||||
|
extends: .container_job_template
|
||||||
|
variables:
|
||||||
|
NAME: emsdk-wasm32-cross
|
||||||
|
|
|
@ -118,12 +118,8 @@ cross-ppc64el-kvm-only:
|
||||||
IMAGE: debian-ppc64el-cross
|
IMAGE: debian-ppc64el-cross
|
||||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
|
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
|
||||||
|
|
||||||
# The riscv64 cross-builds currently use a 'sid' container to get
|
|
||||||
# compilers and libraries. Until something more stable is found we
|
|
||||||
# allow_failure so as not to block CI.
|
|
||||||
cross-riscv64-system:
|
cross-riscv64-system:
|
||||||
extends: .cross_system_build_job
|
extends: .cross_system_build_job
|
||||||
allow_failure: true
|
|
||||||
needs:
|
needs:
|
||||||
job: riscv64-debian-cross-container
|
job: riscv64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
|
@ -131,7 +127,6 @@ cross-riscv64-system:
|
||||||
|
|
||||||
cross-riscv64-user:
|
cross-riscv64-user:
|
||||||
extends: .cross_user_build_job
|
extends: .cross_user_build_job
|
||||||
allow_failure: true
|
|
||||||
needs:
|
needs:
|
||||||
job: riscv64-debian-cross-container
|
job: riscv64-debian-cross-container
|
||||||
variables:
|
variables:
|
||||||
|
|
|
@ -627,6 +627,15 @@ F: .gitlab-ci.d/cirrus/macos-*
|
||||||
F: */*.m
|
F: */*.m
|
||||||
F: scripts/entitlement.sh
|
F: scripts/entitlement.sh
|
||||||
|
|
||||||
|
WebAssembly
|
||||||
|
M: Kohei Tokunaga <ktokunaga.mail@gmail.com>
|
||||||
|
S: Maintained
|
||||||
|
F: include/system/os-wasm.h
|
||||||
|
F: os-wasm.c
|
||||||
|
F: util/coroutine-wasm.c
|
||||||
|
F: configs/meson/emscripten.txt
|
||||||
|
F: tests/docker/dockerfiles/emsdk-wasm32-cross.docker
|
||||||
|
|
||||||
Alpha Machines
|
Alpha Machines
|
||||||
--------------
|
--------------
|
||||||
M: Richard Henderson <richard.henderson@linaro.org>
|
M: Richard Henderson <richard.henderson@linaro.org>
|
||||||
|
|
|
@ -12,8 +12,10 @@ system_ss.add([files(
|
||||||
|
|
||||||
if host_os != 'windows'
|
if host_os != 'windows'
|
||||||
system_ss.add(files('rng-random.c'))
|
system_ss.add(files('rng-random.c'))
|
||||||
system_ss.add(files('hostmem-file.c'))
|
if host_os != 'emscripten'
|
||||||
system_ss.add([files('hostmem-shm.c'), rt])
|
system_ss.add(files('hostmem-file.c'))
|
||||||
|
system_ss.add([files('hostmem-shm.c'), rt])
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
if host_os == 'linux'
|
if host_os == 'linux'
|
||||||
system_ss.add(files('hostmem-memfd.c'))
|
system_ss.add(files('hostmem-memfd.c'))
|
||||||
|
|
|
@ -110,6 +110,10 @@
|
||||||
#include <sys/diskslice.h>
|
#include <sys/diskslice.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* OS X does not have O_DSYNC */
|
/* OS X does not have O_DSYNC */
|
||||||
#ifndef O_DSYNC
|
#ifndef O_DSYNC
|
||||||
#ifdef O_SYNC
|
#ifdef O_SYNC
|
||||||
|
@ -2076,8 +2080,11 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_COPY_FILE_RANGE
|
#ifndef HAVE_COPY_FILE_RANGE
|
||||||
static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd,
|
#ifndef EMSCRIPTEN
|
||||||
off_t *out_off, size_t len, unsigned int flags)
|
static
|
||||||
|
#endif
|
||||||
|
ssize_t copy_file_range(int in_fd, off_t *in_off, int out_fd,
|
||||||
|
off_t *out_off, size_t len, unsigned int flags)
|
||||||
{
|
{
|
||||||
#ifdef __NR_copy_file_range
|
#ifdef __NR_copy_file_range
|
||||||
return syscall(__NR_copy_file_range, in_fd, in_off, out_fd,
|
return syscall(__NR_copy_file_range, in_fd, in_off, out_fd,
|
||||||
|
|
8
configs/meson/emscripten.txt
Normal file
8
configs/meson/emscripten.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[built-in options]
|
||||||
|
c_args = ['-pthread']
|
||||||
|
cpp_args = ['-pthread']
|
||||||
|
objc_args = ['-pthread']
|
||||||
|
# -sPROXY_TO_PTHREAD link time flag always requires -pthread even during
|
||||||
|
# configuration so explicitly add the flag here.
|
||||||
|
c_link_args = ['-pthread','-sASYNCIFY=1','-sPROXY_TO_PTHREAD=1','-sFORCE_FILESYSTEM','-sALLOW_TABLE_GROWTH','-sTOTAL_MEMORY=2GB','-sWASM_BIGINT','-sEXPORT_ES6=1','-sASYNCIFY_IMPORTS=ffi_call_js','-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction,TTY,FS']
|
||||||
|
cpp_link_args = ['-pthread','-sASYNCIFY=1','-sPROXY_TO_PTHREAD=1','-sFORCE_FILESYSTEM','-sALLOW_TABLE_GROWTH','-sTOTAL_MEMORY=2GB','-sWASM_BIGINT','-sEXPORT_ES6=1','-sASYNCIFY_IMPORTS=ffi_call_js','-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction,TTY,FS']
|
7
configure
vendored
7
configure
vendored
|
@ -360,6 +360,10 @@ elif check_define __NetBSD__; then
|
||||||
host_os=netbsd
|
host_os=netbsd
|
||||||
elif check_define __APPLE__; then
|
elif check_define __APPLE__; then
|
||||||
host_os=darwin
|
host_os=darwin
|
||||||
|
elif check_define EMSCRIPTEN ; then
|
||||||
|
host_os=emscripten
|
||||||
|
cpu=wasm32
|
||||||
|
cross_compile="yes"
|
||||||
else
|
else
|
||||||
# This is a fatal error, but don't report it yet, because we
|
# This is a fatal error, but don't report it yet, because we
|
||||||
# might be going to just print the --help text, or it might
|
# might be going to just print the --help text, or it might
|
||||||
|
@ -526,6 +530,9 @@ case "$cpu" in
|
||||||
linux_arch=x86
|
linux_arch=x86
|
||||||
CPU_CFLAGS="-m64"
|
CPU_CFLAGS="-m64"
|
||||||
;;
|
;;
|
||||||
|
wasm32)
|
||||||
|
CPU_CFLAGS="-m32"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if test -n "$host_arch" && {
|
if test -n "$host_arch" && {
|
||||||
|
|
|
@ -118,9 +118,14 @@ Rust build dependencies
|
||||||
include bindgen or have an older version, it is recommended to install
|
include bindgen or have an older version, it is recommended to install
|
||||||
a newer version using ``cargo install bindgen-cli``.
|
a newer version using ``cargo install bindgen-cli``.
|
||||||
|
|
||||||
Developers may want to use Cargo-based tools in the QEMU source tree;
|
QEMU requires Rust 1.77.0. This is available on all supported platforms
|
||||||
this requires Cargo 1.74.0. Note that Cargo is not required in order
|
with one exception, namely the ``mips64el`` architecture on Debian bookworm.
|
||||||
to build QEMU.
|
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.
|
||||||
|
|
||||||
Optional build dependencies
|
Optional build dependencies
|
||||||
Build components whose absence does not affect the ability to build QEMU
|
Build components whose absence does not affect the ability to build QEMU
|
||||||
|
|
|
@ -168,7 +168,7 @@ The required versions of the packages are stored in a configuration file
|
||||||
``pythondeps.toml``. The format is custom to QEMU, but it is documented
|
``pythondeps.toml``. The format is custom to QEMU, but it is documented
|
||||||
at the top of the file itself and it should be easy to understand. The
|
at the top of the file itself and it should be easy to understand. The
|
||||||
requirements should make it possible to use the version that is packaged
|
requirements should make it possible to use the version that is packaged
|
||||||
that is provided by supported distros.
|
by QEMU's supported distros.
|
||||||
|
|
||||||
When dependencies are downloaded, instead, ``configure`` uses a "known
|
When dependencies are downloaded, instead, ``configure`` uses a "known
|
||||||
good" version that is also listed in ``pythondeps.toml``. In this
|
good" version that is also listed in ``pythondeps.toml``. In this
|
||||||
|
|
|
@ -71,36 +71,9 @@ Building Rust code with ``--enable-modules`` is not supported yet.
|
||||||
Supported tools
|
Supported tools
|
||||||
'''''''''''''''
|
'''''''''''''''
|
||||||
|
|
||||||
QEMU supports rustc version 1.63.0 and newer. Notably, the following features
|
QEMU supports rustc version 1.77.0 and newer. Notably, the following features
|
||||||
are missing:
|
are missing:
|
||||||
|
|
||||||
* ``core::ffi`` (1.64.0). Use ``std::os::raw`` and ``std::ffi`` instead.
|
|
||||||
|
|
||||||
* ``cast_mut()``/``cast_const()`` (1.65.0). Use ``as`` instead.
|
|
||||||
|
|
||||||
* "let ... else" (1.65.0). Use ``if let`` instead. This is currently patched
|
|
||||||
in QEMU's vendored copy of the bilge crate.
|
|
||||||
|
|
||||||
* Generic Associated Types (1.65.0)
|
|
||||||
|
|
||||||
* ``CStr::from_bytes_with_nul()`` as a ``const`` function (1.72.0).
|
|
||||||
|
|
||||||
* "Return position ``impl Trait`` in Traits" (1.75.0, blocker for including
|
|
||||||
the pinned-init create).
|
|
||||||
|
|
||||||
* ``MaybeUninit::zeroed()`` as a ``const`` function (1.75.0). QEMU's
|
|
||||||
``Zeroable`` trait can be implemented without ``MaybeUninit::zeroed()``,
|
|
||||||
so this would be just a cleanup.
|
|
||||||
|
|
||||||
* ``c"" literals`` (stable in 1.77.0). QEMU provides a ``c_str!()`` macro
|
|
||||||
to define ``CStr`` constants easily
|
|
||||||
|
|
||||||
* ``offset_of!`` (stable in 1.77.0). QEMU uses ``offset_of!()`` heavily; it
|
|
||||||
provides a replacement in the ``qemu_api`` crate, but it does not support
|
|
||||||
lifetime parameters and therefore ``&'a Something`` fields in the struct
|
|
||||||
may have to be replaced by ``NonNull<Something>``. *Nested* ``offset_of!``
|
|
||||||
was only stabilized in Rust 1.82.0, but it is not used.
|
|
||||||
|
|
||||||
* inline const expression (stable in 1.79.0), currently worked around with
|
* inline const expression (stable in 1.79.0), currently worked around with
|
||||||
associated constants in the ``FnCall`` trait.
|
associated constants in the ``FnCall`` trait.
|
||||||
|
|
||||||
|
@ -125,12 +98,6 @@ are missing:
|
||||||
|
|
||||||
__ https://github.com/rust-lang/rust/pull/125258
|
__ https://github.com/rust-lang/rust/pull/125258
|
||||||
|
|
||||||
It is expected that QEMU will advance its minimum supported version of
|
|
||||||
rustc to 1.77.0 as soon as possible; as of January 2025, blockers
|
|
||||||
for that right now are Debian bookworm and 32-bit MIPS processors.
|
|
||||||
This unfortunately means that references to statics in constants will
|
|
||||||
remain an issue.
|
|
||||||
|
|
||||||
QEMU also supports version 0.60.x of bindgen, which is missing option
|
QEMU also supports version 0.60.x of bindgen, which is missing option
|
||||||
``--generate-cstr``. This option requires version 0.66.x and will
|
``--generate-cstr``. This option requires version 0.66.x and will
|
||||||
be adopted as soon as supporting these older versions is not necessary
|
be adopted as soon as supporting these older versions is not necessary
|
||||||
|
@ -183,7 +150,6 @@ module status
|
||||||
``bitops`` complete
|
``bitops`` complete
|
||||||
``callbacks`` complete
|
``callbacks`` complete
|
||||||
``cell`` stable
|
``cell`` stable
|
||||||
``c_str`` complete
|
|
||||||
``errno`` complete
|
``errno`` complete
|
||||||
``irq`` complete
|
``irq`` complete
|
||||||
``memory`` stable
|
``memory`` stable
|
||||||
|
@ -440,7 +406,7 @@ Adding dependencies
|
||||||
Generally, the set of dependent crates is kept small. Think twice before
|
Generally, the set of dependent crates is kept small. Think twice before
|
||||||
adding a new external crate, especially if it comes with a large set of
|
adding a new external crate, especially if it comes with a large set of
|
||||||
dependencies itself. Sometimes QEMU only needs a small subset of the
|
dependencies itself. Sometimes QEMU only needs a small subset of the
|
||||||
functionality; see for example QEMU's ``assertions`` or ``c_str`` modules.
|
functionality; see for example QEMU's ``assertions`` module.
|
||||||
|
|
||||||
On top of this recommendation, adding external crates to QEMU is a
|
On top of this recommendation, adding external crates to QEMU is a
|
||||||
slightly complicated process, mostly due to the need to teach Meson how
|
slightly complicated process, mostly due to the need to teach Meson how
|
||||||
|
|
|
@ -36,6 +36,13 @@
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These functions perform function pointer casts which can cause function call
|
||||||
|
* failure on Emscripten. Use g_slist_sort_with_data and g_list_sort_with_data
|
||||||
|
* instead of these functions.
|
||||||
|
*/
|
||||||
|
#pragma GCC poison g_slist_sort g_list_sort
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing
|
* Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing
|
||||||
* use of functions from newer GLib via this compat header needs a little
|
* use of functions from newer GLib via this compat header needs a little
|
||||||
|
|
|
@ -26,6 +26,13 @@ static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len)
|
||||||
/* icache is coherent and does not require flushing. */
|
/* icache is coherent and does not require flushing. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(EMSCRIPTEN)
|
||||||
|
|
||||||
|
static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len)
|
||||||
|
{
|
||||||
|
/* Wasm doesn't have executable region of memory. */
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len);
|
void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* To avoid getting into possible circular include dependencies, this
|
* To avoid getting into possible circular include dependencies, this
|
||||||
* file should not include any other QEMU headers, with the exceptions
|
* file should not include any other QEMU headers, with the exceptions
|
||||||
* of config-host.h, config-target.h, qemu/compiler.h,
|
* of config-host.h, config-target.h, qemu/compiler.h,
|
||||||
* system/os-posix.h, system/os-win32.h, glib-compat.h and
|
* system/os-posix.h, system/os-win32.h, system/os-wasm.h, glib-compat.h and
|
||||||
* qemu/typedefs.h, all of which are doing a similar job to this file
|
* qemu/typedefs.h, all of which are doing a similar job to this file
|
||||||
* and are under similar constraints.
|
* and are under similar constraints.
|
||||||
*
|
*
|
||||||
|
@ -164,10 +164,14 @@ QEMU_EXTERN_C int daemon(int, int);
|
||||||
#include "system/os-win32.h"
|
#include "system/os-win32.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
#include "system/os-posix.h"
|
#include "system/os-posix.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(EMSCRIPTEN)
|
||||||
|
#include "system/os-wasm.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
104
include/system/os-wasm.h
Normal file
104
include/system/os-wasm.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* posix specific declarations forked from os-posix.h, removing functions not
|
||||||
|
* working on Emscripten
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
* Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QEMU_OS_WASM_H
|
||||||
|
#define QEMU_OS_WASM_H
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYSMACROS
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void os_set_line_buffering(void);
|
||||||
|
void os_setup_early_signal_handling(void);
|
||||||
|
void os_set_proc_name(const char *s);
|
||||||
|
void os_setup_signal_handling(void);
|
||||||
|
void os_setup_limits(void);
|
||||||
|
void os_setup_post(void);
|
||||||
|
int os_mlock(bool on_fault);
|
||||||
|
static inline int os_set_daemonize(bool d)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
bool is_daemonized(void);
|
||||||
|
static inline void os_daemonize(void) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qemu_alloc_stack:
|
||||||
|
* @sz: pointer to a size_t holding the requested usable stack size
|
||||||
|
*
|
||||||
|
* Allocate memory that can be used as a stack, for instance for
|
||||||
|
* coroutines. If the memory cannot be allocated, this function
|
||||||
|
* will abort (like g_malloc()). This function also inserts an
|
||||||
|
* additional guard page to catch a potential stack overflow.
|
||||||
|
* Note that the memory required for the guard page and alignment
|
||||||
|
* and minimal stack size restrictions will increase the value of sz.
|
||||||
|
*
|
||||||
|
* The allocated stack must be freed with qemu_free_stack().
|
||||||
|
*
|
||||||
|
* Returns: pointer to (the lowest address of) the stack memory.
|
||||||
|
*/
|
||||||
|
void *qemu_alloc_stack(size_t *sz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qemu_free_stack:
|
||||||
|
* @stack: stack to free
|
||||||
|
* @sz: size of stack in bytes
|
||||||
|
*
|
||||||
|
* Free a stack allocated via qemu_alloc_stack(). Note that sz must
|
||||||
|
* be exactly the adjusted stack size returned by qemu_alloc_stack.
|
||||||
|
*/
|
||||||
|
void qemu_free_stack(void *stack, size_t sz);
|
||||||
|
|
||||||
|
/* POSIX and Mingw32 differ in the name of the stdio lock functions. */
|
||||||
|
|
||||||
|
static inline void qemu_flockfile(FILE *f)
|
||||||
|
{
|
||||||
|
flockfile(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qemu_funlockfile(FILE *f)
|
||||||
|
{
|
||||||
|
funlockfile(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
35
meson.build
35
meson.build
|
@ -50,9 +50,9 @@ genh = []
|
||||||
qapi_trace_events = []
|
qapi_trace_events = []
|
||||||
|
|
||||||
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
|
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
|
||||||
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
|
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux', 'emscripten']
|
||||||
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
|
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
|
||||||
'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64']
|
'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64', 'wasm32']
|
||||||
|
|
||||||
cpu = host_machine.cpu_family()
|
cpu = host_machine.cpu_family()
|
||||||
|
|
||||||
|
@ -94,12 +94,12 @@ have_rust = have_rust and add_languages('rust', native: true,
|
||||||
required: get_option('rust').disable_auto_if(not have_system))
|
required: get_option('rust').disable_auto_if(not have_system))
|
||||||
if have_rust
|
if have_rust
|
||||||
rustc = meson.get_compiler('rust')
|
rustc = meson.get_compiler('rust')
|
||||||
if rustc.version().version_compare('<1.63.0')
|
if rustc.version().version_compare('<1.77.0')
|
||||||
if get_option('rust').enabled()
|
if get_option('rust').enabled()
|
||||||
error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.63.0')
|
error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.77.0')
|
||||||
else
|
else
|
||||||
warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.')
|
warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.')
|
||||||
message('Please upgrade to at least 1.63.0 to use Rust.')
|
message('Please upgrade to at least 1.77.0 to use Rust.')
|
||||||
have_rust = false
|
have_rust = false
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -353,6 +353,8 @@ foreach lang : all_languages
|
||||||
# endif
|
# endif
|
||||||
#endif''')
|
#endif''')
|
||||||
# ok
|
# ok
|
||||||
|
elif compiler.get_id() == 'emscripten'
|
||||||
|
# ok
|
||||||
else
|
else
|
||||||
error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v15.0) to compile QEMU')
|
error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v15.0) to compile QEMU')
|
||||||
endif
|
endif
|
||||||
|
@ -470,7 +472,10 @@ endif
|
||||||
# instead, we can't add -no-pie because it overrides -shared: the linker then
|
# instead, we can't add -no-pie because it overrides -shared: the linker then
|
||||||
# tries to build an executable instead of a shared library and fails. So
|
# tries to build an executable instead of a shared library and fails. So
|
||||||
# don't add -no-pie anywhere and cross fingers. :(
|
# don't add -no-pie anywhere and cross fingers. :(
|
||||||
if not get_option('b_pie')
|
#
|
||||||
|
# Emscripten doesn't support -no-pie but meson can't catch the compiler
|
||||||
|
# warning. So explicitly omit the flag for Emscripten.
|
||||||
|
if not get_option('b_pie') and host_os != 'emscripten'
|
||||||
qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie')
|
qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -514,6 +519,8 @@ ucontext_probe = '''
|
||||||
supported_backends = []
|
supported_backends = []
|
||||||
if host_os == 'windows'
|
if host_os == 'windows'
|
||||||
supported_backends += ['windows']
|
supported_backends += ['windows']
|
||||||
|
elif host_os == 'emscripten'
|
||||||
|
supported_backends += ['wasm']
|
||||||
else
|
else
|
||||||
if host_os != 'darwin' and cc.links(ucontext_probe)
|
if host_os != 'darwin' and cc.links(ucontext_probe)
|
||||||
supported_backends += ['ucontext']
|
supported_backends += ['ucontext']
|
||||||
|
@ -902,6 +909,10 @@ if get_option('tcg').allowed()
|
||||||
if not get_option('tcg_interpreter')
|
if not get_option('tcg_interpreter')
|
||||||
error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu))
|
error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu))
|
||||||
endif
|
endif
|
||||||
|
elif host_arch == 'wasm32'
|
||||||
|
if not get_option('tcg_interpreter')
|
||||||
|
error('WebAssembly host requires --enable-tcg-interpreter')
|
||||||
|
endif
|
||||||
elif get_option('tcg_interpreter')
|
elif get_option('tcg_interpreter')
|
||||||
warning('Use of the TCG interpreter is not recommended on this host')
|
warning('Use of the TCG interpreter is not recommended on this host')
|
||||||
warning('architecture. There is a native TCG execution backend available')
|
warning('architecture. There is a native TCG execution backend available')
|
||||||
|
@ -2962,7 +2973,9 @@ config_host_data.set('CONFIG_ATOMIC64', cc.links('''
|
||||||
return 0;
|
return 0;
|
||||||
}''', args: qemu_isa_flags))
|
}''', args: qemu_isa_flags))
|
||||||
|
|
||||||
has_int128_type = cc.compiles('''
|
# has_int128_type is set to false on Emscripten to avoid errors by libffi
|
||||||
|
# during runtime.
|
||||||
|
has_int128_type = host_os != 'emscripten' and cc.compiles('''
|
||||||
__int128_t a;
|
__int128_t a;
|
||||||
__uint128_t b;
|
__uint128_t b;
|
||||||
int main(void) { b = a; }''')
|
int main(void) { b = a; }''')
|
||||||
|
@ -3775,6 +3788,8 @@ if have_block
|
||||||
# os-win32.c does not
|
# os-win32.c does not
|
||||||
if host_os == 'windows'
|
if host_os == 'windows'
|
||||||
system_ss.add(files('os-win32.c'))
|
system_ss.add(files('os-win32.c'))
|
||||||
|
elif host_os == 'emscripten'
|
||||||
|
blockdev_ss.add(files('os-wasm.c'))
|
||||||
else
|
else
|
||||||
blockdev_ss.add(files('os-posix.c'))
|
blockdev_ss.add(files('os-posix.c'))
|
||||||
endif
|
endif
|
||||||
|
@ -4516,7 +4531,11 @@ subdir('scripts')
|
||||||
subdir('tools')
|
subdir('tools')
|
||||||
subdir('pc-bios')
|
subdir('pc-bios')
|
||||||
subdir('docs')
|
subdir('docs')
|
||||||
subdir('tests')
|
# Tests are disabled on emscripten because they rely on host features that aren't
|
||||||
|
# supported by emscripten (e.g. fork and unix socket).
|
||||||
|
if host_os != 'emscripten'
|
||||||
|
subdir('tests')
|
||||||
|
endif
|
||||||
if gtk.found()
|
if gtk.found()
|
||||||
subdir('po')
|
subdir('po')
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -34,7 +34,7 @@ option('fuzzing_engine', type : 'string', value : '',
|
||||||
option('trace_file', type: 'string', value: 'trace',
|
option('trace_file', type: 'string', value: 'trace',
|
||||||
description: 'Trace file prefix for simple backend')
|
description: 'Trace file prefix for simple backend')
|
||||||
option('coroutine_backend', type: 'combo',
|
option('coroutine_backend', type: 'combo',
|
||||||
choices: ['ucontext', 'sigaltstack', 'windows', 'auto'],
|
choices: ['ucontext', 'sigaltstack', 'windows', 'wasm', 'auto'],
|
||||||
value: 'auto', description: 'coroutine backend to use')
|
value: 'auto', description: 'coroutine backend to use')
|
||||||
|
|
||||||
# Everything else can be set via --enable/--disable-* option
|
# Everything else can be set via --enable/--disable-* option
|
||||||
|
|
119
os-wasm.c
Normal file
119
os-wasm.c
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
/*
|
||||||
|
* os-wasm.c
|
||||||
|
* Forked from os-posix.c, removing functions not working on Emscripten
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||||
|
* Copyright (c) 2010 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "system/runstate.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
|
void os_setup_post(void){}
|
||||||
|
void os_set_line_buffering(void)
|
||||||
|
{
|
||||||
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
}
|
||||||
|
void os_setup_early_signal_handling(void)
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
sigfillset(&act.sa_mask);
|
||||||
|
act.sa_flags = 0;
|
||||||
|
act.sa_handler = SIG_IGN;
|
||||||
|
sigaction(SIGPIPE, &act, NULL);
|
||||||
|
}
|
||||||
|
void os_set_proc_name(const char *s)
|
||||||
|
{
|
||||||
|
error_report("Change of process name not supported by your OS");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
static void termsig_handler(int signal, siginfo_t *info, void *c)
|
||||||
|
{
|
||||||
|
qemu_system_killed(info->si_signo, info->si_pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_setup_signal_handling(void)
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
|
||||||
|
memset(&act, 0, sizeof(act));
|
||||||
|
act.sa_sigaction = termsig_handler;
|
||||||
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
sigaction(SIGINT, &act, NULL);
|
||||||
|
sigaction(SIGHUP, &act, NULL);
|
||||||
|
sigaction(SIGTERM, &act, NULL);
|
||||||
|
}
|
||||||
|
void os_setup_limits(void)
|
||||||
|
{
|
||||||
|
struct rlimit nofile;
|
||||||
|
|
||||||
|
if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) {
|
||||||
|
warn_report("unable to query NOFILE limit: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nofile.rlim_cur == nofile.rlim_max) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nofile.rlim_cur = nofile.rlim_max;
|
||||||
|
|
||||||
|
if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) {
|
||||||
|
warn_report("unable to set NOFILE limit: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int os_mlock(bool on_fault)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_MLOCKALL
|
||||||
|
int ret = 0;
|
||||||
|
int flags = MCL_CURRENT | MCL_FUTURE;
|
||||||
|
|
||||||
|
if (on_fault) {
|
||||||
|
#ifdef HAVE_MLOCK_ONFAULT
|
||||||
|
flags |= MCL_ONFAULT;
|
||||||
|
#else
|
||||||
|
error_report("mlockall: on_fault not supported");
|
||||||
|
return -EINVAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mlockall(flags);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("mlockall: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
(void)on_fault;
|
||||||
|
return -ENOSYS;
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -4862,7 +4862,7 @@ SRST
|
||||||
Start right away with a saved state (``loadvm`` in monitor)
|
Start right away with a saved state (``loadvm`` in monitor)
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !defined(EMSCRIPTEN)
|
||||||
DEF("daemonize", 0, QEMU_OPTION_daemonize, \
|
DEF("daemonize", 0, QEMU_OPTION_daemonize, \
|
||||||
"-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
|
"-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
|
||||||
#endif
|
#endif
|
||||||
|
@ -5249,7 +5249,7 @@ HXCOMM Internal use
|
||||||
DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL)
|
DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL)
|
||||||
DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL)
|
DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL)
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
DEF("run-with", HAS_ARG, QEMU_OPTION_run_with,
|
DEF("run-with", HAS_ARG, QEMU_OPTION_run_with,
|
||||||
"-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]\n"
|
"-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]\n"
|
||||||
" Set miscellaneous QEMU process lifecycle options:\n"
|
" Set miscellaneous QEMU process lifecycle options:\n"
|
||||||
|
|
1
rust/Cargo.lock
generated
1
rust/Cargo.lock
generated
|
@ -108,7 +108,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"qemu_api_macros",
|
"qemu_api_macros",
|
||||||
"version_check",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -12,12 +12,12 @@ edition = "2021"
|
||||||
homepage = "https://www.qemu.org"
|
homepage = "https://www.qemu.org"
|
||||||
license = "GPL-2.0-or-later"
|
license = "GPL-2.0-or-later"
|
||||||
repository = "https://gitlab.com/qemu-project/qemu/"
|
repository = "https://gitlab.com/qemu-project/qemu/"
|
||||||
rust-version = "1.63.0"
|
rust-version = "1.77.0"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
unexpected_cfgs = { level = "deny", check-cfg = [
|
unexpected_cfgs = { level = "deny", check-cfg = [
|
||||||
'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
|
'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
|
||||||
'cfg(has_offset_of)'] }
|
] }
|
||||||
|
|
||||||
# Occasionally, we may need to silence warnings and clippy lints that
|
# Occasionally, we may need to silence warnings and clippy lints that
|
||||||
# were only introduced in newer Rust compiler versions. Do not croak
|
# were only introduced in newer Rust compiler versions. Do not croak
|
||||||
|
@ -71,6 +71,7 @@ no_effect_underscore_binding = "deny"
|
||||||
option_option = "deny"
|
option_option = "deny"
|
||||||
or_fun_call = "deny"
|
or_fun_call = "deny"
|
||||||
ptr_as_ptr = "deny"
|
ptr_as_ptr = "deny"
|
||||||
|
ptr_cast_constness = "deny"
|
||||||
pub_underscore_fields = "deny"
|
pub_underscore_fields = "deny"
|
||||||
redundant_clone = "deny"
|
redundant_clone = "deny"
|
||||||
redundant_closure_for_method_calls = "deny"
|
redundant_closure_for_method_calls = "deny"
|
||||||
|
@ -88,11 +89,11 @@ suspicious_operation_groupings = "deny"
|
||||||
transmute_ptr_to_ptr = "deny"
|
transmute_ptr_to_ptr = "deny"
|
||||||
transmute_undefined_repr = "deny"
|
transmute_undefined_repr = "deny"
|
||||||
type_repetition_in_bounds = "deny"
|
type_repetition_in_bounds = "deny"
|
||||||
|
uninlined_format_args = "deny"
|
||||||
used_underscore_binding = "deny"
|
used_underscore_binding = "deny"
|
||||||
|
|
||||||
# nice to have, but cannot be enabled yet
|
# nice to have, but cannot be enabled yet
|
||||||
#wildcard_imports = "deny" # still have many bindings::* imports
|
#wildcard_imports = "deny" # still have many bindings::* imports
|
||||||
#ptr_cast_constness = "deny" # needs 1.65.0 for cast_mut()/cast_const()
|
|
||||||
|
|
||||||
# these may have false positives
|
# these may have false positives
|
||||||
#option_if_let_else = "deny"
|
#option_if_let_else = "deny"
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
doc-valid-idents = ["PrimeCell", ".."]
|
doc-valid-idents = ["PrimeCell", ".."]
|
||||||
msrv = "1.63.0"
|
allow-mixed-uninlined-format-args = false
|
||||||
|
msrv = "1.77.0"
|
||||||
|
|
|
@ -74,7 +74,7 @@ impl std::ops::Index<u32> for Fifo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, qemu_api_macros::offsets)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PL011Registers {
|
pub struct PL011Registers {
|
||||||
#[doc(alias = "fr")]
|
#[doc(alias = "fr")]
|
||||||
pub flags: registers::Flags,
|
pub flags: registers::Flags,
|
||||||
|
@ -98,7 +98,7 @@ pub struct PL011Registers {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)]
|
#[derive(qemu_api_macros::Object)]
|
||||||
/// PL011 Device Model in QEMU
|
/// PL011 Device Model in QEMU
|
||||||
pub struct PL011State {
|
pub struct PL011State {
|
||||||
pub parent_obj: ParentField<SysBusDevice>,
|
pub parent_obj: ParentField<SysBusDevice>,
|
||||||
|
|
|
@ -3,13 +3,12 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
os::raw::{c_int, c_void},
|
ffi::{c_int, c_void},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::{qdev_prop_bool, qdev_prop_chr},
|
bindings::{qdev_prop_bool, qdev_prop_chr},
|
||||||
c_str,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
|
vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
|
||||||
|
@ -25,7 +24,7 @@ extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
|
||||||
|
|
||||||
/// Migration subsection for [`PL011State`] clock.
|
/// Migration subsection for [`PL011State`] clock.
|
||||||
static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
|
static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("pl011/clock").as_ptr(),
|
name: c"pl011/clock".as_ptr(),
|
||||||
version_id: 1,
|
version_id: 1,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
needed: Some(pl011_clock_needed),
|
needed: Some(pl011_clock_needed),
|
||||||
|
@ -46,7 +45,7 @@ extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
|
||||||
}
|
}
|
||||||
|
|
||||||
static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
|
static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("pl011/regs").as_ptr(),
|
name: c"pl011/regs".as_ptr(),
|
||||||
version_id: 2,
|
version_id: 2,
|
||||||
minimum_version_id: 2,
|
minimum_version_id: 2,
|
||||||
fields: vmstate_fields! {
|
fields: vmstate_fields! {
|
||||||
|
@ -70,7 +69,7 @@ static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("pl011").as_ptr(),
|
name: c"pl011".as_ptr(),
|
||||||
version_id: 2,
|
version_id: 2,
|
||||||
minimum_version_id: 2,
|
minimum_version_id: 2,
|
||||||
post_load: Some(pl011_post_load),
|
post_load: Some(pl011_post_load),
|
||||||
|
@ -87,14 +86,14 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
|
||||||
qemu_api::declare_properties! {
|
qemu_api::declare_properties! {
|
||||||
PL011_PROPERTIES,
|
PL011_PROPERTIES,
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c_str!("chardev"),
|
c"chardev",
|
||||||
PL011State,
|
PL011State,
|
||||||
char_backend,
|
char_backend,
|
||||||
unsafe { &qdev_prop_chr },
|
unsafe { &qdev_prop_chr },
|
||||||
CharBackend
|
CharBackend
|
||||||
),
|
),
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c_str!("migrate-clk"),
|
c"migrate-clk",
|
||||||
PL011State,
|
PL011State,
|
||||||
migrate_clock,
|
migrate_clock,
|
||||||
unsafe { &qdev_prop_bool },
|
unsafe { &qdev_prop_bool },
|
||||||
|
|
|
@ -12,13 +12,11 @@
|
||||||
//! See [`PL011State`](crate::device::PL011State) for the device model type and
|
//! See [`PL011State`](crate::device::PL011State) for the device model type and
|
||||||
//! the [`registers`] module for register types.
|
//! the [`registers`] module for register types.
|
||||||
|
|
||||||
use qemu_api::c_str;
|
|
||||||
|
|
||||||
mod device;
|
mod device;
|
||||||
mod device_class;
|
mod device_class;
|
||||||
mod registers;
|
mod registers;
|
||||||
|
|
||||||
pub use device::pl011_create;
|
pub use device::pl011_create;
|
||||||
|
|
||||||
pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011");
|
pub const TYPE_PL011: &::std::ffi::CStr = c"pl011";
|
||||||
pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary");
|
pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c"pl011_luminary";
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use std::ptr::addr_of_mut;
|
use std::ptr::addr_of_mut;
|
||||||
|
|
||||||
use qemu_api::{cell::bql_locked, impl_zeroable, zeroable::Zeroable};
|
use qemu_api::{cell::bql_locked, zeroable::Zeroable};
|
||||||
|
|
||||||
/// Each `HPETState` represents a Event Timer Block. The v1 spec supports
|
/// Each `HPETState` represents a Event Timer Block. The v1 spec supports
|
||||||
/// up to 8 blocks. QEMU only uses 1 block (in PC machine).
|
/// up to 8 blocks. QEMU only uses 1 block (in PC machine).
|
||||||
|
@ -18,7 +18,7 @@ pub struct HPETFwEntry {
|
||||||
pub min_tick: u16,
|
pub min_tick: u16,
|
||||||
pub page_prot: u8,
|
pub page_prot: u8,
|
||||||
}
|
}
|
||||||
impl_zeroable!(HPETFwEntry);
|
unsafe impl Zeroable for HPETFwEntry {}
|
||||||
|
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
|
@ -26,7 +26,7 @@ pub struct HPETFwConfig {
|
||||||
pub count: u8,
|
pub count: u8,
|
||||||
pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK],
|
pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK],
|
||||||
}
|
}
|
||||||
impl_zeroable!(HPETFwConfig);
|
unsafe impl Zeroable for HPETFwConfig {}
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::{c_int, c_void, CStr},
|
||||||
os::raw::{c_int, c_void},
|
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr::{addr_of_mut, null_mut, NonNull},
|
ptr::{addr_of_mut, null_mut, NonNull},
|
||||||
slice::from_ref,
|
slice::from_ref,
|
||||||
|
@ -15,7 +14,6 @@ use qemu_api::{
|
||||||
address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
|
address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
|
||||||
qdev_prop_uint32, qdev_prop_uint8,
|
qdev_prop_uint32, qdev_prop_uint8,
|
||||||
},
|
},
|
||||||
c_str,
|
|
||||||
cell::{BqlCell, BqlRefCell},
|
cell::{BqlCell, BqlRefCell},
|
||||||
irq::InterruptSource,
|
irq::InterruptSource,
|
||||||
memory::{
|
memory::{
|
||||||
|
@ -184,7 +182,7 @@ fn timer_handler(timer_cell: &BqlRefCell<HPETTimer>) {
|
||||||
|
|
||||||
/// HPET Timer Abstraction
|
/// HPET Timer Abstraction
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, qemu_api_macros::offsets)]
|
#[derive(Debug)]
|
||||||
pub struct HPETTimer {
|
pub struct HPETTimer {
|
||||||
/// timer N index within the timer block (`HPETState`)
|
/// timer N index within the timer block (`HPETState`)
|
||||||
#[doc(alias = "tn")]
|
#[doc(alias = "tn")]
|
||||||
|
@ -220,7 +218,7 @@ impl HPETTimer {
|
||||||
// SAFETY: the HPETTimer will only be used after the timer
|
// SAFETY: the HPETTimer will only be used after the timer
|
||||||
// is initialized below.
|
// is initialized below.
|
||||||
qemu_timer: unsafe { Timer::new() },
|
qemu_timer: unsafe { Timer::new() },
|
||||||
state: NonNull::new(state as *const _ as *mut _).unwrap(),
|
state: NonNull::new((state as *const HPETState).cast_mut()).unwrap(),
|
||||||
config: 0,
|
config: 0,
|
||||||
cmp: 0,
|
cmp: 0,
|
||||||
fsb: 0,
|
fsb: 0,
|
||||||
|
@ -524,7 +522,7 @@ impl HPETTimer {
|
||||||
|
|
||||||
/// HPET Event Timer Block Abstraction
|
/// HPET Event Timer Block Abstraction
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)]
|
#[derive(qemu_api_macros::Object)]
|
||||||
pub struct HPETState {
|
pub struct HPETState {
|
||||||
parent_obj: ParentField<SysBusDevice>,
|
parent_obj: ParentField<SysBusDevice>,
|
||||||
iomem: MemoryRegion,
|
iomem: MemoryRegion,
|
||||||
|
@ -909,7 +907,7 @@ impl ObjectImpl for HPETState {
|
||||||
qemu_api::declare_properties! {
|
qemu_api::declare_properties! {
|
||||||
HPET_PROPERTIES,
|
HPET_PROPERTIES,
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c_str!("timers"),
|
c"timers",
|
||||||
HPETState,
|
HPETState,
|
||||||
num_timers,
|
num_timers,
|
||||||
unsafe { &qdev_prop_uint8 },
|
unsafe { &qdev_prop_uint8 },
|
||||||
|
@ -917,7 +915,7 @@ qemu_api::declare_properties! {
|
||||||
default = HPET_MIN_TIMERS
|
default = HPET_MIN_TIMERS
|
||||||
),
|
),
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c_str!("msi"),
|
c"msi",
|
||||||
HPETState,
|
HPETState,
|
||||||
flags,
|
flags,
|
||||||
unsafe { &qdev_prop_bit },
|
unsafe { &qdev_prop_bit },
|
||||||
|
@ -926,7 +924,7 @@ qemu_api::declare_properties! {
|
||||||
default = false,
|
default = false,
|
||||||
),
|
),
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c_str!("hpet-intcap"),
|
c"hpet-intcap",
|
||||||
HPETState,
|
HPETState,
|
||||||
int_route_cap,
|
int_route_cap,
|
||||||
unsafe { &qdev_prop_uint32 },
|
unsafe { &qdev_prop_uint32 },
|
||||||
|
@ -934,7 +932,7 @@ qemu_api::declare_properties! {
|
||||||
default = 0
|
default = 0
|
||||||
),
|
),
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
c_str!("hpet-offset-saved"),
|
c"hpet-offset-saved",
|
||||||
HPETState,
|
HPETState,
|
||||||
hpet_offset_saved,
|
hpet_offset_saved,
|
||||||
unsafe { &qdev_prop_bool },
|
unsafe { &qdev_prop_bool },
|
||||||
|
@ -975,7 +973,7 @@ unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c
|
||||||
}
|
}
|
||||||
|
|
||||||
static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription {
|
static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("hpet/rtc_irq_level").as_ptr(),
|
name: c"hpet/rtc_irq_level".as_ptr(),
|
||||||
version_id: 1,
|
version_id: 1,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
needed: Some(hpet_rtc_irq_level_needed),
|
needed: Some(hpet_rtc_irq_level_needed),
|
||||||
|
@ -986,7 +984,7 @@ static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription {
|
||||||
};
|
};
|
||||||
|
|
||||||
static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription {
|
static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("hpet/offset").as_ptr(),
|
name: c"hpet/offset".as_ptr(),
|
||||||
version_id: 1,
|
version_id: 1,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
needed: Some(hpet_offset_needed),
|
needed: Some(hpet_offset_needed),
|
||||||
|
@ -997,7 +995,7 @@ static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription {
|
||||||
};
|
};
|
||||||
|
|
||||||
static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription {
|
static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("hpet_timer").as_ptr(),
|
name: c"hpet_timer".as_ptr(),
|
||||||
version_id: 1,
|
version_id: 1,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
fields: vmstate_fields! {
|
fields: vmstate_fields! {
|
||||||
|
@ -1012,10 +1010,10 @@ static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription {
|
||||||
..Zeroable::ZERO
|
..Zeroable::ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
const VALIDATE_TIMERS_NAME: &CStr = c_str!("num_timers must match");
|
const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match";
|
||||||
|
|
||||||
static VMSTATE_HPET: VMStateDescription = VMStateDescription {
|
static VMSTATE_HPET: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("hpet").as_ptr(),
|
name: c"hpet".as_ptr(),
|
||||||
version_id: 2,
|
version_id: 2,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
pre_save: Some(hpet_pre_save),
|
pre_save: Some(hpet_pre_save),
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
//! This library implements a device model for the IA-PC HPET (High
|
//! This library implements a device model for the IA-PC HPET (High
|
||||||
//! Precision Event Timers) device in QEMU.
|
//! Precision Event Timers) device in QEMU.
|
||||||
|
|
||||||
use qemu_api::c_str;
|
|
||||||
|
|
||||||
pub mod fw_cfg;
|
pub mod fw_cfg;
|
||||||
pub mod hpet;
|
pub mod hpet;
|
||||||
|
|
||||||
pub const TYPE_HPET: &::std::ffi::CStr = c_str!("hpet");
|
pub const TYPE_HPET: &::std::ffi::CStr = c"hpet";
|
||||||
|
|
|
@ -6,7 +6,7 @@ use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
|
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
|
||||||
DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Type, Variant, Visibility,
|
DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -16,50 +16,41 @@ fn get_fields<'a>(
|
||||||
input: &'a DeriveInput,
|
input: &'a DeriveInput,
|
||||||
msg: &str,
|
msg: &str,
|
||||||
) -> Result<&'a Punctuated<Field, Comma>, MacroError> {
|
) -> Result<&'a Punctuated<Field, Comma>, MacroError> {
|
||||||
if let Data::Struct(s) = &input.data {
|
let Data::Struct(ref s) = &input.data else {
|
||||||
if let Fields::Named(fs) = &s.fields {
|
return Err(MacroError::Message(
|
||||||
Ok(&fs.named)
|
format!("Struct required for {msg}"),
|
||||||
} else {
|
|
||||||
Err(MacroError::Message(
|
|
||||||
format!("Named fields required for {}", msg),
|
|
||||||
input.ident.span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(MacroError::Message(
|
|
||||||
format!("Struct required for {}", msg),
|
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
));
|
||||||
}
|
};
|
||||||
|
let Fields::Named(ref fs) = &s.fields else {
|
||||||
|
return Err(MacroError::Message(
|
||||||
|
format!("Named fields required for {msg}"),
|
||||||
|
input.ident.span(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
Ok(&fs.named)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> {
|
fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> {
|
||||||
if let Data::Struct(s) = &input.data {
|
let Data::Struct(ref s) = &input.data else {
|
||||||
let unnamed = match &s.fields {
|
return Err(MacroError::Message(
|
||||||
Fields::Unnamed(FieldsUnnamed {
|
format!("Struct required for {msg}"),
|
||||||
unnamed: ref fields,
|
|
||||||
..
|
|
||||||
}) => fields,
|
|
||||||
_ => {
|
|
||||||
return Err(MacroError::Message(
|
|
||||||
format!("Tuple struct required for {}", msg),
|
|
||||||
s.fields.span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if unnamed.len() != 1 {
|
|
||||||
return Err(MacroError::Message(
|
|
||||||
format!("A single field is required for {}", msg),
|
|
||||||
s.fields.span(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(&unnamed[0])
|
|
||||||
} else {
|
|
||||||
Err(MacroError::Message(
|
|
||||||
format!("Struct required for {}", msg),
|
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
));
|
||||||
|
};
|
||||||
|
let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else {
|
||||||
|
return Err(MacroError::Message(
|
||||||
|
format!("Tuple struct required for {msg}"),
|
||||||
|
s.fields.span(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
if unnamed.len() != 1 {
|
||||||
|
return Err(MacroError::Message(
|
||||||
|
format!("A single field is required for {msg}"),
|
||||||
|
s.fields.span(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
Ok(&unnamed[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
||||||
|
@ -69,7 +60,7 @@ fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(MacroError::Message(
|
Err(MacroError::Message(
|
||||||
format!("#[repr(C)] required for {}", msg),
|
format!("#[repr(C)] required for {msg}"),
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -82,7 +73,7 @@ fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError>
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(MacroError::Message(
|
Err(MacroError::Message(
|
||||||
format!("#[repr(transparent)] required for {}", msg),
|
format!("#[repr(transparent)] required for {msg}"),
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -160,33 +151,6 @@ pub fn derive_opaque(input: TokenStream) -> TokenStream {
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip::macros(quote)]
|
|
||||||
fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
|
||||||
is_c_repr(&input, "#[derive(offsets)]")?;
|
|
||||||
|
|
||||||
let name = &input.ident;
|
|
||||||
let fields = get_fields(&input, "#[derive(offsets)]")?;
|
|
||||||
let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
|
|
||||||
let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect();
|
|
||||||
let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect();
|
|
||||||
|
|
||||||
Ok(quote! {
|
|
||||||
::qemu_api::with_offsets! {
|
|
||||||
struct #name {
|
|
||||||
#(#field_vis #field_names: #field_types,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(offsets)]
|
|
||||||
pub fn derive_offsets(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
let expanded = derive_offsets_or_error(input).unwrap_or_else(Into::into);
|
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
|
fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
|
||||||
let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
|
let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
|
||||||
|
@ -204,26 +168,25 @@ fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(MacroError::Message(
|
Err(MacroError::Message(
|
||||||
format!("#[repr(u8/u16/u32/u64) required for {}", msg),
|
format!("#[repr(u8/u16/u32/u64) required for {msg}"),
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> {
|
fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> {
|
||||||
if let Data::Enum(e) = &input.data {
|
let Data::Enum(ref e) = &input.data else {
|
||||||
if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
|
return Err(MacroError::Message(
|
||||||
return Err(MacroError::Message(
|
|
||||||
"Cannot derive TryInto for enum with non-unit variants.".to_string(),
|
|
||||||
v.fields.span(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(&e.variants)
|
|
||||||
} else {
|
|
||||||
Err(MacroError::Message(
|
|
||||||
"Cannot derive TryInto for union or struct.".to_string(),
|
"Cannot derive TryInto for union or struct.".to_string(),
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
));
|
||||||
|
};
|
||||||
|
if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
|
||||||
|
return Err(MacroError::Message(
|
||||||
|
"Cannot derive TryInto for enum with non-unit variants.".to_string(),
|
||||||
|
v.fields.span(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
Ok(&e.variants)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip::macros(quote)]
|
#[rustfmt::skip::macros(quote)]
|
||||||
|
|
|
@ -17,9 +17,6 @@ rust-version.workspace = true
|
||||||
qemu_api_macros = { path = "../qemu-api-macros" }
|
qemu_api_macros = { path = "../qemu-api-macros" }
|
||||||
libc = "0.2.162"
|
libc = "0.2.162"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
version_check = "~0.9"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["debug_cell"]
|
default = ["debug_cell"]
|
||||||
allocator = []
|
allocator = []
|
||||||
|
|
|
@ -8,15 +8,13 @@ use std::os::unix::fs::symlink as symlink_file;
|
||||||
use std::os::windows::fs::symlink_file;
|
use std::os::windows::fs::symlink_file;
|
||||||
use std::{env, fs::remove_file, io::Result, path::Path};
|
use std::{env, fs::remove_file, io::Result, path::Path};
|
||||||
|
|
||||||
use version_check as rustc;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// Placing bindings.inc.rs in the source directory is supported
|
// Placing bindings.inc.rs in the source directory is supported
|
||||||
// but not documented or encouraged.
|
// but not documented or encouraged.
|
||||||
let path = env::var("MESON_BUILD_ROOT")
|
let path = env::var("MESON_BUILD_ROOT")
|
||||||
.unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
|
.unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
|
||||||
|
|
||||||
let file = format!("{}/bindings.inc.rs", path);
|
let file = format!("{path}/bindings.inc.rs");
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
if !Path::new(&file).exists() {
|
if !Path::new(&file).exists() {
|
||||||
panic!(concat!(
|
panic!(concat!(
|
||||||
|
@ -31,18 +29,13 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let dest_path = format!("{}/bindings.inc.rs", out_dir);
|
let dest_path = format!("{out_dir}/bindings.inc.rs");
|
||||||
let dest_path = Path::new(&dest_path);
|
let dest_path = Path::new(&dest_path);
|
||||||
if dest_path.symlink_metadata().is_ok() {
|
if dest_path.symlink_metadata().is_ok() {
|
||||||
remove_file(dest_path)?;
|
remove_file(dest_path)?;
|
||||||
}
|
}
|
||||||
symlink_file(file, dest_path)?;
|
symlink_file(file, dest_path)?;
|
||||||
|
|
||||||
// Check for available rustc features
|
|
||||||
if rustc::is_min_version("1.77.0").unwrap_or(false) {
|
|
||||||
println!("cargo:rustc-cfg=has_offset_of");
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,6 @@ _qemu_api_cfg = run_command(rustc_args,
|
||||||
libc_dep = dependency('libc-0.2-rs')
|
libc_dep = dependency('libc-0.2-rs')
|
||||||
|
|
||||||
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
||||||
if rustc.version().version_compare('>=1.77.0')
|
|
||||||
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
|
||||||
endif
|
|
||||||
if get_option('debug_mutex')
|
if get_option('debug_mutex')
|
||||||
_qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
|
_qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
|
||||||
endif
|
endif
|
||||||
|
@ -23,12 +20,10 @@ _qemu_api_rs = static_library(
|
||||||
'src/callbacks.rs',
|
'src/callbacks.rs',
|
||||||
'src/cell.rs',
|
'src/cell.rs',
|
||||||
'src/chardev.rs',
|
'src/chardev.rs',
|
||||||
'src/c_str.rs',
|
|
||||||
'src/errno.rs',
|
'src/errno.rs',
|
||||||
'src/irq.rs',
|
'src/irq.rs',
|
||||||
'src/memory.rs',
|
'src/memory.rs',
|
||||||
'src/module.rs',
|
'src/module.rs',
|
||||||
'src/offset_of.rs',
|
|
||||||
'src/prelude.rs',
|
'src/prelude.rs',
|
||||||
'src/qdev.rs',
|
'src/qdev.rs',
|
||||||
'src/qom.rs',
|
'src/qom.rs',
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
// Copyright 2024 Red Hat, Inc.
|
|
||||||
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#![doc(hidden)]
|
|
||||||
//! This module provides a macro to define a constant of type
|
|
||||||
//! [`CStr`](std::ffi::CStr), for compatibility with versions of
|
|
||||||
//! Rust that lack `c""` literals.
|
|
||||||
//!
|
|
||||||
//! Documentation is hidden because it only exposes macros, which
|
|
||||||
//! are exported directly from `qemu_api`.
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
/// Given a string constant _without_ embedded or trailing NULs, return
|
|
||||||
/// a `CStr`.
|
|
||||||
///
|
|
||||||
/// Needed for compatibility with Rust <1.77.
|
|
||||||
macro_rules! c_str {
|
|
||||||
($str:expr) => {{
|
|
||||||
const STRING: &str = concat!($str, "\0");
|
|
||||||
const BYTES: &[u8] = STRING.as_bytes();
|
|
||||||
|
|
||||||
// "for" is not allowed in const context... oh well,
|
|
||||||
// everybody loves some lisp. This could be turned into
|
|
||||||
// a procedural macro if this is a problem; alternatively
|
|
||||||
// Rust 1.72 makes CStr::from_bytes_with_nul a const function.
|
|
||||||
const fn f(b: &[u8], i: usize) {
|
|
||||||
if i == b.len() - 1 {
|
|
||||||
} else if b[i] == 0 {
|
|
||||||
panic!("c_str argument contains NUL")
|
|
||||||
} else {
|
|
||||||
f(b, i + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f(BYTES, 0);
|
|
||||||
|
|
||||||
// SAFETY: absence of NULs apart from the final byte was checked above
|
|
||||||
unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) }
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::ffi::CStr;
|
|
||||||
|
|
||||||
use crate::c_str;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cstr_macro() {
|
|
||||||
let good = c_str!("🦀");
|
|
||||||
let good_bytes = b"\xf0\x9f\xa6\x80\0";
|
|
||||||
assert_eq!(good.to_bytes_with_nul(), good_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cstr_macro_const() {
|
|
||||||
const GOOD: &CStr = c_str!("🦀");
|
|
||||||
const GOOD_BYTES: &[u8] = b"\xf0\x9f\xa6\x80\0";
|
|
||||||
assert_eq!(GOOD.to_bytes_with_nul(), GOOD_BYTES);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -77,13 +77,13 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use qemu_api::prelude::*;
|
//! # use qemu_api::prelude::*;
|
||||||
//! # use qemu_api::{c_str, cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
|
//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
|
||||||
//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
|
//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
|
||||||
//! # const N_GPIOS: usize = 8;
|
//! # const N_GPIOS: usize = 8;
|
||||||
//! # struct PL061Registers { /* ... */ }
|
//! # struct PL061Registers { /* ... */ }
|
||||||
//! # unsafe impl ObjectType for PL061State {
|
//! # unsafe impl ObjectType for PL061State {
|
||||||
//! # type Class = <SysBusDevice as ObjectType>::Class;
|
//! # type Class = <SysBusDevice as ObjectType>::Class;
|
||||||
//! # const TYPE_NAME: &'static std::ffi::CStr = c_str!("pl061");
|
//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061";
|
||||||
//! # }
|
//! # }
|
||||||
//! struct PL061State {
|
//! struct PL061State {
|
||||||
//! parent_obj: ParentField<SysBusDevice>,
|
//! parent_obj: ParentField<SysBusDevice>,
|
||||||
|
@ -1016,7 +1016,7 @@ impl<T> Opaque<T> {
|
||||||
|
|
||||||
/// Returns a raw pointer to the opaque data.
|
/// Returns a raw pointer to the opaque data.
|
||||||
pub const fn as_ptr(&self) -> *const T {
|
pub const fn as_ptr(&self) -> *const T {
|
||||||
self.as_mut_ptr() as *const _
|
self.as_mut_ptr().cast_const()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a raw pointer to the opaque data that can be passed to a
|
/// Returns a raw pointer to the opaque data that can be passed to a
|
||||||
|
|
|
@ -10,11 +10,10 @@
|
||||||
//! called.
|
//! called.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::{c_int, c_void, CStr},
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
io::{self, ErrorKind, Write},
|
io::{self, ErrorKind, Write},
|
||||||
marker::PhantomPinned,
|
marker::PhantomPinned,
|
||||||
os::raw::{c_int, c_void},
|
|
||||||
ptr::addr_of_mut,
|
ptr::addr_of_mut,
|
||||||
slice,
|
slice,
|
||||||
};
|
};
|
||||||
|
@ -161,7 +160,7 @@ impl CharBackend {
|
||||||
receive_cb,
|
receive_cb,
|
||||||
event_cb,
|
event_cb,
|
||||||
None,
|
None,
|
||||||
(owner as *const T as *mut T).cast::<c_void>(),
|
(owner as *const T).cast_mut().cast::<c_void>(),
|
||||||
core::ptr::null_mut(),
|
core::ptr::null_mut(),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
//! Bindings for interrupt sources
|
//! Bindings for interrupt sources
|
||||||
|
|
||||||
use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr};
|
use std::{
|
||||||
|
ffi::{c_int, CStr},
|
||||||
|
marker::PhantomData,
|
||||||
|
ptr,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bindings::{self, qemu_set_irq},
|
bindings::{self, qemu_set_irq},
|
||||||
|
|
|
@ -15,7 +15,6 @@ pub mod prelude;
|
||||||
|
|
||||||
pub mod assertions;
|
pub mod assertions;
|
||||||
pub mod bitops;
|
pub mod bitops;
|
||||||
pub mod c_str;
|
|
||||||
pub mod callbacks;
|
pub mod callbacks;
|
||||||
pub mod cell;
|
pub mod cell;
|
||||||
pub mod chardev;
|
pub mod chardev;
|
||||||
|
@ -23,7 +22,6 @@ pub mod errno;
|
||||||
pub mod irq;
|
pub mod irq;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod offset_of;
|
|
||||||
pub mod qdev;
|
pub mod qdev;
|
||||||
pub mod qom;
|
pub mod qom;
|
||||||
pub mod sysbus;
|
pub mod sysbus;
|
||||||
|
@ -33,7 +31,7 @@ pub mod zeroable;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
alloc::{GlobalAlloc, Layout},
|
alloc::{GlobalAlloc, Layout},
|
||||||
os::raw::c_void,
|
ffi::c_void,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
|
||||||
|
@ -165,6 +163,3 @@ unsafe impl GlobalAlloc for QemuAllocator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_offset_of)]
|
|
||||||
pub use core::mem::offset_of;
|
|
||||||
|
|
|
@ -5,9 +5,8 @@
|
||||||
//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs`
|
//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs`
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString},
|
ffi::{c_uint, c_void, CStr, CString},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
os::raw::{c_uint, c_void},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use bindings::{hwaddr, MemTxAttrs};
|
pub use bindings::{hwaddr, MemTxAttrs};
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#![doc(hidden)]
|
|
||||||
//! This module provides macros that emulate the functionality of
|
|
||||||
//! `core::mem::offset_of` on older versions of Rust.
|
|
||||||
//!
|
|
||||||
//! Documentation is hidden because it only exposes macros, which
|
|
||||||
//! are exported directly from `qemu_api`.
|
|
||||||
|
|
||||||
/// This macro provides the same functionality as `core::mem::offset_of`,
|
|
||||||
/// except that only one level of field access is supported. The declaration
|
|
||||||
/// of the struct must be wrapped with `with_offsets! { }`.
|
|
||||||
///
|
|
||||||
/// It is needed because `offset_of!` was only stabilized in Rust 1.77.
|
|
||||||
#[cfg(not(has_offset_of))]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! offset_of {
|
|
||||||
($Container:ty, $field:ident) => {
|
|
||||||
<$Container>::OFFSET_TO__.$field
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper for struct declarations, that allows using `offset_of!` in
|
|
||||||
/// versions of Rust prior to 1.77
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! with_offsets {
|
|
||||||
// This method to generate field offset constants comes from:
|
|
||||||
//
|
|
||||||
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df
|
|
||||||
//
|
|
||||||
// used under MIT license with permission of Yandros aka Daniel Henry-Mantilla
|
|
||||||
(
|
|
||||||
$(#[$struct_meta:meta])*
|
|
||||||
$struct_vis:vis
|
|
||||||
struct $StructName:ident {
|
|
||||||
$(
|
|
||||||
$(#[$field_meta:meta])*
|
|
||||||
$field_vis:vis
|
|
||||||
$field_name:ident : $field_ty:ty
|
|
||||||
),*
|
|
||||||
$(,)?
|
|
||||||
}
|
|
||||||
) => (
|
|
||||||
#[cfg(not(has_offset_of))]
|
|
||||||
const _: () = {
|
|
||||||
struct StructOffsetsHelper<T>(std::marker::PhantomData<T>);
|
|
||||||
const END_OF_PREV_FIELD: usize = 0;
|
|
||||||
|
|
||||||
// populate StructOffsetsHelper<T> with associated consts,
|
|
||||||
// one for each field
|
|
||||||
$crate::with_offsets! {
|
|
||||||
@struct $StructName
|
|
||||||
@names [ $($field_name)* ]
|
|
||||||
@tys [ $($field_ty ,)*]
|
|
||||||
}
|
|
||||||
|
|
||||||
// now turn StructOffsetsHelper<T>'s consts into a single struct,
|
|
||||||
// applying field visibility. This provides better error messages
|
|
||||||
// than if offset_of! used StructOffsetsHelper::<T> directly.
|
|
||||||
pub
|
|
||||||
struct StructOffsets {
|
|
||||||
$(
|
|
||||||
$field_vis
|
|
||||||
$field_name: usize,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
impl $StructName {
|
|
||||||
pub
|
|
||||||
const OFFSET_TO__: StructOffsets = StructOffsets {
|
|
||||||
$(
|
|
||||||
$field_name: StructOffsetsHelper::<$StructName>::$field_name,
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
(
|
|
||||||
@struct $StructName:ident
|
|
||||||
@names []
|
|
||||||
@tys []
|
|
||||||
) => ();
|
|
||||||
|
|
||||||
(
|
|
||||||
@struct $StructName:ident
|
|
||||||
@names [$field_name:ident $($other_names:tt)*]
|
|
||||||
@tys [$field_ty:ty , $($other_tys:tt)*]
|
|
||||||
) => (
|
|
||||||
#[allow(non_local_definitions)]
|
|
||||||
#[allow(clippy::modulo_one)]
|
|
||||||
impl StructOffsetsHelper<$StructName> {
|
|
||||||
#[allow(nonstandard_style)]
|
|
||||||
const $field_name: usize = {
|
|
||||||
const ALIGN: usize = std::mem::align_of::<$field_ty>();
|
|
||||||
const TRAIL: usize = END_OF_PREV_FIELD % ALIGN;
|
|
||||||
END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const _: () = {
|
|
||||||
const END_OF_PREV_FIELD: usize =
|
|
||||||
StructOffsetsHelper::<$StructName>::$field_name +
|
|
||||||
std::mem::size_of::<$field_ty>()
|
|
||||||
;
|
|
||||||
$crate::with_offsets! {
|
|
||||||
@struct $StructName
|
|
||||||
@names [$($other_names)*]
|
|
||||||
@tys [$($other_tys)*]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::offset_of;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct Foo {
|
|
||||||
a: u16,
|
|
||||||
b: u32,
|
|
||||||
c: u64,
|
|
||||||
d: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct Bar {
|
|
||||||
pub a: u16,
|
|
||||||
pub b: u64,
|
|
||||||
c: Foo,
|
|
||||||
d: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::with_offsets! {
|
|
||||||
#[repr(C)]
|
|
||||||
struct Bar {
|
|
||||||
pub a: u16,
|
|
||||||
pub b: u64,
|
|
||||||
c: Foo,
|
|
||||||
d: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Baz {
|
|
||||||
b: u32,
|
|
||||||
a: u8,
|
|
||||||
}
|
|
||||||
crate::with_offsets! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Baz {
|
|
||||||
b: u32,
|
|
||||||
a: u8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_offset_of() {
|
|
||||||
const OFFSET_TO_C: usize = offset_of!(Bar, c);
|
|
||||||
|
|
||||||
assert_eq!(offset_of!(Bar, a), 0);
|
|
||||||
assert_eq!(offset_of!(Bar, b), 8);
|
|
||||||
assert_eq!(OFFSET_TO_C, 16);
|
|
||||||
assert_eq!(offset_of!(Bar, d), 40);
|
|
||||||
|
|
||||||
assert_eq!(offset_of!(Baz, b), 0);
|
|
||||||
assert_eq!(offset_of!(Baz, a), 4);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,8 +5,7 @@
|
||||||
//! Bindings to create devices and access device functionality from Rust.
|
//! Bindings to create devices and access device functionality from Rust.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString},
|
ffi::{c_int, c_void, CStr, CString},
|
||||||
os::raw::{c_int, c_void},
|
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,7 +190,7 @@ macro_rules! define_property {
|
||||||
// use associated function syntax for type checking
|
// use associated function syntax for type checking
|
||||||
name: ::std::ffi::CStr::as_ptr($name),
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
info: $prop,
|
info: $prop,
|
||||||
offset: $crate::offset_of!($state, $field) as isize,
|
offset: ::std::mem::offset_of!($state, $field) as isize,
|
||||||
bitnr: $bitnr,
|
bitnr: $bitnr,
|
||||||
set_default: true,
|
set_default: true,
|
||||||
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
|
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
|
||||||
|
@ -203,7 +202,7 @@ macro_rules! define_property {
|
||||||
// use associated function syntax for type checking
|
// use associated function syntax for type checking
|
||||||
name: ::std::ffi::CStr::as_ptr($name),
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
info: $prop,
|
info: $prop,
|
||||||
offset: $crate::offset_of!($state, $field) as isize,
|
offset: ::std::mem::offset_of!($state, $field) as isize,
|
||||||
set_default: true,
|
set_default: true,
|
||||||
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
|
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
|
||||||
..$crate::zeroable::Zeroable::ZERO
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
|
@ -214,7 +213,7 @@ macro_rules! define_property {
|
||||||
// use associated function syntax for type checking
|
// use associated function syntax for type checking
|
||||||
name: ::std::ffi::CStr::as_ptr($name),
|
name: ::std::ffi::CStr::as_ptr($name),
|
||||||
info: $prop,
|
info: $prop,
|
||||||
offset: $crate::offset_of!($state, $field) as isize,
|
offset: ::std::mem::offset_of!($state, $field) as isize,
|
||||||
set_default: false,
|
set_default: false,
|
||||||
..$crate::zeroable::Zeroable::ZERO
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,10 @@
|
||||||
//! without incurring into violations of orphan rules for traits.
|
//! without incurring into violations of orphan rules for traits.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::{c_void, CStr},
|
||||||
fmt,
|
fmt,
|
||||||
mem::ManuallyDrop,
|
mem::ManuallyDrop,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
os::raw::c_void,
|
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -389,7 +388,7 @@ where
|
||||||
{
|
{
|
||||||
#[allow(clippy::as_ptr_cast_mut)]
|
#[allow(clippy::as_ptr_cast_mut)]
|
||||||
{
|
{
|
||||||
self.as_ptr::<U>() as *mut _
|
self.as_ptr::<U>().cast_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,9 +534,10 @@ pub trait ObjectImpl: ObjectType + IsA<Object> {
|
||||||
/// While `klass`'s parent class is initialized on entry, the other fields
|
/// While `klass`'s parent class is initialized on entry, the other fields
|
||||||
/// are all zero; it is therefore assumed that all fields in `T` can be
|
/// are all zero; it is therefore assumed that all fields in `T` can be
|
||||||
/// zeroed, otherwise it would not be possible to provide the class as a
|
/// zeroed, otherwise it would not be possible to provide the class as a
|
||||||
/// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable)
|
/// `&mut T`. TODO: it may be possible to add an unsafe trait that checks
|
||||||
/// to T; this is more easily done once Zeroable does not require a manual
|
/// that all fields *after the parent class* (but not the parent class
|
||||||
/// implementation (Rust 1.75.0).
|
/// itself) are Zeroable. This unsafe trait can be added via a derive
|
||||||
|
/// macro.
|
||||||
const CLASS_INIT: fn(&mut Self::Class);
|
const CLASS_INIT: fn(&mut Self::Class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,7 +638,7 @@ impl<T: ObjectType> Owned<T> {
|
||||||
// SAFETY NOTE: while NonNull requires a mutable pointer, only
|
// SAFETY NOTE: while NonNull requires a mutable pointer, only
|
||||||
// Deref is implemented so the pointer passed to from_raw
|
// Deref is implemented so the pointer passed to from_raw
|
||||||
// remains const
|
// remains const
|
||||||
Owned(NonNull::new(ptr as *mut T).unwrap())
|
Owned(NonNull::new(ptr.cast_mut()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a raw C pointer from a reference. `src` is consumed
|
/// Obtain a raw C pointer from a reference. `src` is consumed
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
os::raw::{c_int, c_void},
|
ffi::{c_int, c_void},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ impl Timer {
|
||||||
scale as c_int,
|
scale as c_int,
|
||||||
attributes as c_int,
|
attributes as c_int,
|
||||||
Some(timer_cb),
|
Some(timer_cb),
|
||||||
(opaque as *const T).cast::<c_void>() as *mut c_void,
|
(opaque as *const T).cast::<c_void>().cast_mut(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
//! functionality that is missing from `vmstate_of!`.
|
//! functionality that is missing from `vmstate_of!`.
|
||||||
|
|
||||||
use core::{marker::PhantomData, mem, ptr::NonNull};
|
use core::{marker::PhantomData, mem, ptr::NonNull};
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::ffi::{c_int, c_void};
|
||||||
|
|
||||||
pub use crate::bindings::{VMStateDescription, VMStateField};
|
pub use crate::bindings::{VMStateDescription, VMStateField};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -205,8 +205,8 @@ macro_rules! vmstate_of {
|
||||||
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.as_ptr() as *const ::std::os::raw::c_char,
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
offset: $crate::offset_of!($struct_name, $field_name),
|
offset: ::std::mem::offset_of!($struct_name, $field_name),
|
||||||
$(num_offset: $crate::offset_of!($struct_name, $num),)?
|
$(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
|
||||||
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
|
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
|
||||||
// The calls to `call_func_with_field!` are the magic that
|
// The calls to `call_func_with_field!` are the magic that
|
||||||
// computes most of the VMStateField from the type of the field.
|
// computes most of the VMStateField from the type of the field.
|
||||||
|
@ -427,7 +427,7 @@ unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
|
||||||
macro_rules! vmstate_unused {
|
macro_rules! vmstate_unused {
|
||||||
($size:expr) => {{
|
($size:expr) => {{
|
||||||
$crate::bindings::VMStateField {
|
$crate::bindings::VMStateField {
|
||||||
name: $crate::c_str!("unused").as_ptr(),
|
name: c"unused".as_ptr(),
|
||||||
size: $size,
|
size: $size,
|
||||||
info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
|
info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
|
||||||
flags: $crate::bindings::VMStateFlags::VMS_BUFFER,
|
flags: $crate::bindings::VMStateFlags::VMS_BUFFER,
|
||||||
|
@ -483,10 +483,10 @@ macro_rules! vmstate_struct {
|
||||||
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.as_ptr() as *const ::std::os::raw::c_char,
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
$(num_offset: $crate::offset_of!($struct_name, $num),)?
|
$(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
|
||||||
offset: {
|
offset: {
|
||||||
$crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?);
|
$crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?);
|
||||||
$crate::offset_of!($struct_name, $field_name)
|
::std::mem::offset_of!($struct_name, $field_name)
|
||||||
},
|
},
|
||||||
size: ::core::mem::size_of::<$type>(),
|
size: ::core::mem::size_of::<$type>(),
|
||||||
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
||||||
|
@ -518,7 +518,7 @@ macro_rules! vmstate_clock {
|
||||||
$field_name,
|
$field_name,
|
||||||
$crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
|
$crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
|
||||||
);
|
);
|
||||||
$crate::offset_of!($struct_name, $field_name)
|
::std::mem::offset_of!($struct_name, $field_name)
|
||||||
},
|
},
|
||||||
size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
|
size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
|
||||||
flags: $crate::bindings::VMStateFlags(
|
flags: $crate::bindings::VMStateFlags(
|
||||||
|
|
|
@ -4,89 +4,17 @@
|
||||||
|
|
||||||
/// Encapsulates the requirement that
|
/// Encapsulates the requirement that
|
||||||
/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
|
/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
|
||||||
/// behavior. This trait in principle could be implemented as just:
|
/// behavior.
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// pub unsafe trait Zeroable: Default {
|
|
||||||
/// const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The need for a manual implementation is only because `zeroed()` cannot
|
|
||||||
/// be used as a `const fn` prior to Rust 1.75.0. Once we can assume a new
|
|
||||||
/// enough version of the compiler, we could provide a `#[derive(Zeroable)]`
|
|
||||||
/// macro to check at compile-time that all struct fields are Zeroable, and
|
|
||||||
/// use the above blanket implementation of the `ZERO` constant.
|
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Because the implementation of `ZERO` is manual, it does not make
|
/// Do not add this trait to a type unless all-zeroes is a valid value for the
|
||||||
/// any assumption on the safety of `zeroed()`. However, other users of the
|
/// type. In particular, raw pointers can be zero, but references and
|
||||||
/// trait could use it that way. Do not add this trait to a type unless
|
/// `NonNull<T>` cannot.
|
||||||
/// all-zeroes is a valid value for the type. In particular, remember that
|
|
||||||
/// raw pointers can be zero, but references and `NonNull<T>` cannot
|
|
||||||
pub unsafe trait Zeroable: Default {
|
pub unsafe trait Zeroable: Default {
|
||||||
const ZERO: Self;
|
/// 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() };
|
||||||
/// A macro that acts similarly to [`core::mem::zeroed()`], only is const
|
|
||||||
///
|
|
||||||
/// ## Safety
|
|
||||||
///
|
|
||||||
/// Similar to `core::mem::zeroed()`, except this zeroes padding bits. Zeroed
|
|
||||||
/// padding usually isn't relevant to safety, but might be if a C union is used.
|
|
||||||
///
|
|
||||||
/// Just like for `core::mem::zeroed()`, an all zero byte pattern might not
|
|
||||||
/// be a valid value for a type, as is the case for references `&T` and `&mut
|
|
||||||
/// T`. Reference types trigger a (denied by default) lint and cause immediate
|
|
||||||
/// undefined behavior if the lint is ignored
|
|
||||||
///
|
|
||||||
/// ```rust compile_fail
|
|
||||||
/// use const_zero::const_zero;
|
|
||||||
/// // error: any use of this value will cause an error
|
|
||||||
/// // note: `#[deny(const_err)]` on by default
|
|
||||||
/// const STR: &str = unsafe{const_zero!(&'static str)};
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// `const_zero` does not work on unsized types:
|
|
||||||
///
|
|
||||||
/// ```rust compile_fail
|
|
||||||
/// use const_zero::const_zero;
|
|
||||||
/// // error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
|
|
||||||
/// const BYTES: [u8] = unsafe{const_zero!([u8])};
|
|
||||||
/// ```
|
|
||||||
/// ## Differences with `core::mem::zeroed`
|
|
||||||
///
|
|
||||||
/// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! const_zero {
|
|
||||||
// This macro to produce a type-generic zero constant is taken from the
|
|
||||||
// const_zero crate (v0.1.1):
|
|
||||||
//
|
|
||||||
// https://docs.rs/const-zero/latest/src/const_zero/lib.rs.html
|
|
||||||
//
|
|
||||||
// and used under MIT license
|
|
||||||
($type_:ty) => {{
|
|
||||||
const TYPE_SIZE: ::core::primitive::usize = ::core::mem::size_of::<$type_>();
|
|
||||||
union TypeAsBytes {
|
|
||||||
bytes: [::core::primitive::u8; TYPE_SIZE],
|
|
||||||
inner: ::core::mem::ManuallyDrop<$type_>,
|
|
||||||
}
|
|
||||||
const ZERO_BYTES: TypeAsBytes = TypeAsBytes {
|
|
||||||
bytes: [0; TYPE_SIZE],
|
|
||||||
};
|
|
||||||
::core::mem::ManuallyDrop::<$type_>::into_inner(ZERO_BYTES.inner)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper to implement the `Zeroable` trait through the `const_zero` macro.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! impl_zeroable {
|
|
||||||
($type:ty) => {
|
|
||||||
unsafe impl $crate::zeroable::Zeroable for $type {
|
|
||||||
const ZERO: Self = unsafe { $crate::const_zero!($type) };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindgen does not derive Default here
|
// bindgen does not derive Default here
|
||||||
|
@ -97,13 +25,13 @@ impl Default for crate::bindings::VMStateFlags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_zeroable!(crate::bindings::Property__bindgen_ty_1);
|
unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
|
||||||
impl_zeroable!(crate::bindings::Property);
|
unsafe impl Zeroable for crate::bindings::Property {}
|
||||||
impl_zeroable!(crate::bindings::VMStateFlags);
|
unsafe impl Zeroable for crate::bindings::VMStateFlags {}
|
||||||
impl_zeroable!(crate::bindings::VMStateField);
|
unsafe impl Zeroable for crate::bindings::VMStateField {}
|
||||||
impl_zeroable!(crate::bindings::VMStateDescription);
|
unsafe impl Zeroable for crate::bindings::VMStateDescription {}
|
||||||
impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1);
|
unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
|
||||||
impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2);
|
unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
|
||||||
impl_zeroable!(crate::bindings::MemoryRegionOps);
|
unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
|
||||||
impl_zeroable!(crate::bindings::MemTxAttrs);
|
unsafe impl Zeroable for crate::bindings::MemTxAttrs {}
|
||||||
impl_zeroable!(crate::bindings::CharBackend);
|
unsafe impl Zeroable for crate::bindings::CharBackend {}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use std::{ffi::CStr, ptr::addr_of};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::{module_call_init, module_init_type, qdev_prop_bool},
|
bindings::{module_call_init, module_init_type, qdev_prop_bool},
|
||||||
c_str,
|
|
||||||
cell::{self, BqlCell},
|
cell::{self, BqlCell},
|
||||||
declare_properties, define_property,
|
declare_properties, define_property,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
@ -21,12 +20,11 @@ mod vmstate_tests;
|
||||||
|
|
||||||
// Test that macros can compile.
|
// Test that macros can compile.
|
||||||
pub static VMSTATE: VMStateDescription = VMStateDescription {
|
pub static VMSTATE: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("name").as_ptr(),
|
name: c"name".as_ptr(),
|
||||||
unmigratable: true,
|
unmigratable: true,
|
||||||
..Zeroable::ZERO
|
..Zeroable::ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(qemu_api_macros::offsets)]
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::Object)]
|
#[derive(qemu_api_macros::Object)]
|
||||||
pub struct DummyState {
|
pub struct DummyState {
|
||||||
|
@ -49,7 +47,7 @@ impl DummyClass {
|
||||||
declare_properties! {
|
declare_properties! {
|
||||||
DUMMY_PROPERTIES,
|
DUMMY_PROPERTIES,
|
||||||
define_property!(
|
define_property!(
|
||||||
c_str!("migrate-clk"),
|
c"migrate-clk",
|
||||||
DummyState,
|
DummyState,
|
||||||
migrate_clock,
|
migrate_clock,
|
||||||
unsafe { &qdev_prop_bool },
|
unsafe { &qdev_prop_bool },
|
||||||
|
@ -59,7 +57,7 @@ declare_properties! {
|
||||||
|
|
||||||
unsafe impl ObjectType for DummyState {
|
unsafe impl ObjectType for DummyState {
|
||||||
type Class = DummyClass;
|
type Class = DummyClass;
|
||||||
const TYPE_NAME: &'static CStr = c_str!("dummy");
|
const TYPE_NAME: &'static CStr = c"dummy";
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for DummyState {
|
impl ObjectImpl for DummyState {
|
||||||
|
@ -79,7 +77,6 @@ impl DeviceImpl for DummyState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(qemu_api_macros::offsets)]
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::Object)]
|
#[derive(qemu_api_macros::Object)]
|
||||||
pub struct DummyChildState {
|
pub struct DummyChildState {
|
||||||
|
@ -94,7 +91,7 @@ pub struct DummyChildClass {
|
||||||
|
|
||||||
unsafe impl ObjectType for DummyChildState {
|
unsafe impl ObjectType for DummyChildState {
|
||||||
type Class = DummyChildClass;
|
type Class = DummyChildClass;
|
||||||
const TYPE_NAME: &'static CStr = c_str!("dummy_child");
|
const TYPE_NAME: &'static CStr = c"dummy_child";
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for DummyChildState {
|
impl ObjectImpl for DummyChildState {
|
||||||
|
|
|
@ -2,14 +2,18 @@
|
||||||
// Author(s): Zhao Liu <zhai1.liu@intel.com>
|
// Author(s): Zhao Liu <zhai1.liu@intel.com>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use std::{ffi::CStr, mem::size_of, os::raw::c_void, ptr::NonNull, slice};
|
use std::{
|
||||||
|
ffi::{c_void, CStr},
|
||||||
|
mem::size_of,
|
||||||
|
ptr::NonNull,
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::{
|
bindings::{
|
||||||
vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
|
vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
|
||||||
vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
|
vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
|
||||||
},
|
},
|
||||||
c_str,
|
|
||||||
cell::{BqlCell, Opaque},
|
cell::{BqlCell, Opaque},
|
||||||
impl_vmstate_forward,
|
impl_vmstate_forward,
|
||||||
vmstate::{VMStateDescription, VMStateField},
|
vmstate::{VMStateDescription, VMStateField},
|
||||||
|
@ -28,7 +32,7 @@ const FOO_ARRAY_MAX: usize = 3;
|
||||||
// - VMSTATE_VARRAY_UINT16_UNSAFE
|
// - VMSTATE_VARRAY_UINT16_UNSAFE
|
||||||
// - VMSTATE_VARRAY_MULTIPLY
|
// - VMSTATE_VARRAY_MULTIPLY
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, qemu_api_macros::offsets)]
|
#[derive(Default)]
|
||||||
struct FooA {
|
struct FooA {
|
||||||
arr: [u8; FOO_ARRAY_MAX],
|
arr: [u8; FOO_ARRAY_MAX],
|
||||||
num: u16,
|
num: u16,
|
||||||
|
@ -38,7 +42,7 @@ struct FooA {
|
||||||
}
|
}
|
||||||
|
|
||||||
static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
|
static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("foo_a").as_ptr(),
|
name: c"foo_a".as_ptr(),
|
||||||
version_id: 1,
|
version_id: 1,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
fields: vmstate_fields! {
|
fields: vmstate_fields! {
|
||||||
|
@ -149,7 +153,7 @@ fn test_vmstate_varray_multiply() {
|
||||||
// - VMSTATE_ARRAY
|
// - VMSTATE_ARRAY
|
||||||
// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn
|
// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, qemu_api_macros::offsets)]
|
#[derive(Default)]
|
||||||
struct FooB {
|
struct FooB {
|
||||||
arr_a: [FooA; FOO_ARRAY_MAX],
|
arr_a: [FooA; FOO_ARRAY_MAX],
|
||||||
num_a: u8,
|
num_a: u8,
|
||||||
|
@ -168,7 +172,7 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
|
static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("foo_b").as_ptr(),
|
name: c"foo_b".as_ptr(),
|
||||||
version_id: 2,
|
version_id: 2,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
fields: vmstate_fields! {
|
fields: vmstate_fields! {
|
||||||
|
@ -324,7 +328,6 @@ struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array i
|
||||||
impl_vmstate_forward!(FooCWrapper);
|
impl_vmstate_forward!(FooCWrapper);
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::offsets)]
|
|
||||||
struct FooC {
|
struct FooC {
|
||||||
ptr: *const i32,
|
ptr: *const i32,
|
||||||
ptr_a: NonNull<FooA>,
|
ptr_a: NonNull<FooA>,
|
||||||
|
@ -333,7 +336,7 @@ struct FooC {
|
||||||
}
|
}
|
||||||
|
|
||||||
static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
|
static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("foo_c").as_ptr(),
|
name: c"foo_c".as_ptr(),
|
||||||
version_id: 3,
|
version_id: 3,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
fields: vmstate_fields! {
|
fields: vmstate_fields! {
|
||||||
|
@ -448,13 +451,13 @@ fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
|
static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("foo_d").as_ptr(),
|
name: c"foo_d".as_ptr(),
|
||||||
version_id: 3,
|
version_id: 3,
|
||||||
minimum_version_id: 1,
|
minimum_version_id: 1,
|
||||||
fields: vmstate_fields! {
|
fields: vmstate_fields! {
|
||||||
vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0),
|
vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0),
|
||||||
vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1),
|
vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1),
|
||||||
vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2),
|
vmstate_validate!(FooD, c"foo_d_2", validate_food_2),
|
||||||
},
|
},
|
||||||
..Zeroable::ZERO
|
..Zeroable::ZERO
|
||||||
};
|
};
|
||||||
|
|
|
@ -114,7 +114,7 @@ packages:
|
||||||
- python3-venv
|
- python3-venv
|
||||||
- python3-yaml
|
- python3-yaml
|
||||||
- rpm2cpio
|
- rpm2cpio
|
||||||
- rustc
|
- rustc-1.77
|
||||||
- sed
|
- sed
|
||||||
- socat
|
- socat
|
||||||
- sparse
|
- sparse
|
||||||
|
|
|
@ -112,7 +112,7 @@ packages:
|
||||||
- python3-venv
|
- python3-venv
|
||||||
- python3-yaml
|
- python3-yaml
|
||||||
- rpm2cpio
|
- rpm2cpio
|
||||||
- rustc
|
- rustc-1.77
|
||||||
- sed
|
- sed
|
||||||
- socat
|
- socat
|
||||||
- sparse
|
- sparse
|
||||||
|
|
|
@ -80,7 +80,7 @@ meson_options_help() {
|
||||||
printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string'
|
printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string'
|
||||||
printf "%s\n" ' [NORMAL]'
|
printf "%s\n" ' [NORMAL]'
|
||||||
printf "%s\n" ' --with-coroutine=CHOICE coroutine backend to use (choices:'
|
printf "%s\n" ' --with-coroutine=CHOICE coroutine backend to use (choices:'
|
||||||
printf "%s\n" ' auto/sigaltstack/ucontext/windows)'
|
printf "%s\n" ' auto/sigaltstack/ucontext/windows/wasm)'
|
||||||
printf "%s\n" ' --with-pkgversion=VALUE use specified string as sub-version of the'
|
printf "%s\n" ' --with-pkgversion=VALUE use specified string as sub-version of the'
|
||||||
printf "%s\n" ' package'
|
printf "%s\n" ' package'
|
||||||
printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories'
|
printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories'
|
||||||
|
|
|
@ -5,7 +5,6 @@ source_filename = bilge-impl-0.2.0.tar.gz
|
||||||
source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8
|
source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = bilge-impl-0.2-rs
|
patch_directory = bilge-impl-0.2-rs
|
||||||
diff_files = bilge-impl-1.63.0.patch
|
|
||||||
|
|
||||||
# bump this version number on every change to meson.build or the patches:
|
# bump this version number on every change to meson.build or the patches:
|
||||||
# v2
|
# v2
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
--- a/src/shared/discriminant_assigner.rs
|
|
||||||
+++ b/src/shared/discriminant_assigner.rs
|
|
||||||
@@ -26,20 +26,20 @@
|
|
||||||
let discriminant_expr = &discriminant.1;
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
|
|
||||||
- let Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) = discriminant_expr else {
|
|
||||||
+ if let Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) = discriminant_expr {
|
|
||||||
+ let discriminant_value: u128 = int.base10_parse().unwrap_or_else(unreachable);
|
|
||||||
+ if discriminant_value > self.max_value() {
|
|
||||||
+ abort!(variant, "Value of variant exceeds the given number of bits")
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ Some(discriminant_value)
|
|
||||||
+ } else {
|
|
||||||
abort!(
|
|
||||||
discriminant_expr,
|
|
||||||
"variant `{}` is not a number", variant_name;
|
|
||||||
help = "only literal integers currently supported"
|
|
||||||
)
|
|
||||||
- };
|
|
||||||
-
|
|
||||||
- let discriminant_value: u128 = int.base10_parse().unwrap_or_else(unreachable);
|
|
||||||
- if discriminant_value > self.max_value() {
|
|
||||||
- abort!(variant, "Value of variant exceeds the given number of bits")
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- Some(discriminant_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assign(&mut self, variant: &Variant) -> u128 {
|
|
||||||
--- a/src/shared/fallback.rs
|
|
||||||
+++ b/src/shared/fallback.rs
|
|
||||||
@@ -22,8 +22,9 @@
|
|
||||||
}
|
|
||||||
Unnamed(fields) => {
|
|
||||||
let variant_fields = fields.unnamed.iter();
|
|
||||||
- let Ok(fallback_value) = variant_fields.exactly_one() else {
|
|
||||||
- abort!(variant, "fallback variant must have exactly one field"; help = "use only one field or change to a unit variant")
|
|
||||||
+ let fallback_value = match variant_fields.exactly_one() {
|
|
||||||
+ Ok(ok) => ok,
|
|
||||||
+ _ => abort!(variant, "fallback variant must have exactly one field"; help = "use only one field or change to a unit variant")
|
|
||||||
};
|
|
||||||
|
|
||||||
if !is_last_variant {
|
|
|
@ -1627,7 +1627,7 @@ bool memory_region_init_resizeable_ram(MemoryRegion *mr,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
bool memory_region_init_ram_from_file(MemoryRegion *mr,
|
bool memory_region_init_ram_from_file(MemoryRegion *mr,
|
||||||
Object *owner,
|
Object *owner,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
|
|
@ -1264,7 +1264,7 @@ long qemu_maxrampagesize(void)
|
||||||
return pagesize;
|
return pagesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
static int64_t get_file_size(int fd)
|
static int64_t get_file_size(int fd)
|
||||||
{
|
{
|
||||||
int64_t size;
|
int64_t size;
|
||||||
|
@ -1999,7 +1999,7 @@ out_free:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size,
|
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size,
|
||||||
qemu_ram_resize_cb resized, MemoryRegion *mr,
|
qemu_ram_resize_cb resized, MemoryRegion *mr,
|
||||||
uint32_t ram_flags, int fd, off_t offset,
|
uint32_t ram_flags, int fd, off_t offset,
|
||||||
|
@ -2179,7 +2179,8 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
||||||
assert(!host ^ (ram_flags & RAM_PREALLOC));
|
assert(!host ^ (ram_flags & RAM_PREALLOC));
|
||||||
assert(max_size >= size);
|
assert(max_size >= size);
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX /* ignore RAM_SHARED for Windows */
|
/* ignore RAM_SHARED for Windows and emscripten*/
|
||||||
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
if (!host) {
|
if (!host) {
|
||||||
if (!share_flags && current_machine->aux_ram_share) {
|
if (!share_flags && current_machine->aux_ram_share) {
|
||||||
ram_flags |= RAM_SHARED;
|
ram_flags |= RAM_SHARED;
|
||||||
|
@ -2276,7 +2277,7 @@ static void reclaim_ramblock(RAMBlock *block)
|
||||||
;
|
;
|
||||||
} else if (xen_enabled()) {
|
} else if (xen_enabled()) {
|
||||||
xen_invalidate_map_cache_entry(block->host);
|
xen_invalidate_map_cache_entry(block->host);
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !defined(EMSCRIPTEN)
|
||||||
} else if (block->fd >= 0) {
|
} else if (block->fd >= 0) {
|
||||||
qemu_ram_munmap(block->fd, block->host, block->max_length);
|
qemu_ram_munmap(block->fd, block->host, block->max_length);
|
||||||
close(block->fd);
|
close(block->fd);
|
||||||
|
|
|
@ -768,7 +768,7 @@ static QemuOptsList qemu_smp_opts = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(CONFIG_POSIX)
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
static QemuOptsList qemu_run_with_opts = {
|
static QemuOptsList qemu_run_with_opts = {
|
||||||
.name = "run-with",
|
.name = "run-with",
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head),
|
||||||
|
@ -3679,7 +3679,7 @@ void qemu_init(int argc, char **argv)
|
||||||
case QEMU_OPTION_nouserconfig:
|
case QEMU_OPTION_nouserconfig:
|
||||||
/* Nothing to be parsed here. Especially, do not error out below. */
|
/* Nothing to be parsed here. Especially, do not error out below. */
|
||||||
break;
|
break;
|
||||||
#if defined(CONFIG_POSIX)
|
#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN)
|
||||||
case QEMU_OPTION_daemonize:
|
case QEMU_OPTION_daemonize:
|
||||||
os_set_daemonize(true);
|
os_set_daemonize(true);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -222,7 +222,7 @@ static void count_cpreg(gpointer key, gpointer opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint cpreg_key_compare(gconstpointer a, gconstpointer b)
|
static gint cpreg_key_compare(gconstpointer a, gconstpointer b, gpointer d)
|
||||||
{
|
{
|
||||||
uint64_t aidx = cpreg_to_kvm_id((uintptr_t)a);
|
uint64_t aidx = cpreg_to_kvm_id((uintptr_t)a);
|
||||||
uint64_t bidx = cpreg_to_kvm_id((uintptr_t)b);
|
uint64_t bidx = cpreg_to_kvm_id((uintptr_t)b);
|
||||||
|
@ -246,7 +246,7 @@ void init_cpreg_list(ARMCPU *cpu)
|
||||||
int arraylen;
|
int arraylen;
|
||||||
|
|
||||||
keys = g_hash_table_get_keys(cpu->cp_regs);
|
keys = g_hash_table_get_keys(cpu->cp_regs);
|
||||||
keys = g_list_sort(keys, cpreg_key_compare);
|
keys = g_list_sort_with_data(keys, cpreg_key_compare, NULL);
|
||||||
|
|
||||||
cpu->cpreg_array_len = 0;
|
cpu->cpreg_array_len = 0;
|
||||||
|
|
||||||
|
|
|
@ -6240,7 +6240,7 @@ static void listflags(GList *features)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort alphabetically by type name, respecting X86CPUClass::ordering. */
|
/* Sort alphabetically by type name, respecting X86CPUClass::ordering. */
|
||||||
static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b)
|
static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d)
|
||||||
{
|
{
|
||||||
ObjectClass *class_a = (ObjectClass *)a;
|
ObjectClass *class_a = (ObjectClass *)a;
|
||||||
ObjectClass *class_b = (ObjectClass *)b;
|
ObjectClass *class_b = (ObjectClass *)b;
|
||||||
|
@ -6261,7 +6261,7 @@ static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b)
|
||||||
static GSList *get_sorted_cpu_model_list(void)
|
static GSList *get_sorted_cpu_model_list(void)
|
||||||
{
|
{
|
||||||
GSList *list = object_class_get_list(TYPE_X86_CPU, false);
|
GSList *list = object_class_get_list(TYPE_X86_CPU, false);
|
||||||
list = g_slist_sort(list, x86_cpu_list_compare);
|
list = g_slist_sort_with_data(list, x86_cpu_list_compare, NULL);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6318,6 +6318,11 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data)
|
||||||
qemu_printf(" %-20s %s\n", name, desc);
|
qemu_printf(" %-20s %s\n", name, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gint strcmp_wrap(gconstpointer a, gconstpointer b, gpointer d)
|
||||||
|
{
|
||||||
|
return strcmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
/* list available CPU models and flags */
|
/* list available CPU models and flags */
|
||||||
static void x86_cpu_list(void)
|
static void x86_cpu_list(void)
|
||||||
{
|
{
|
||||||
|
@ -6340,7 +6345,7 @@ static void x86_cpu_list(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
names = g_list_sort(names, (GCompareFunc)strcmp);
|
names = g_list_sort_with_data(names, strcmp_wrap, NULL);
|
||||||
|
|
||||||
qemu_printf("\nRecognized CPUID flags:\n");
|
qemu_printf("\nRecognized CPUID flags:\n");
|
||||||
listflags(names);
|
listflags(names);
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
static void decode_invalid(CPUX86State *env, struct x86_decode *decode)
|
static void decode_invalid(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
printf("%llx: failed to decode instruction ", env->eip);
|
printf(TARGET_FMT_lx ": failed to decode instruction ", env->eip);
|
||||||
for (int i = 0; i < decode->opcode_len; i++) {
|
for (int i = 0; i < decode->opcode_len; i++) {
|
||||||
printf("%x ", decode->opcode[i]);
|
printf("%x ", decode->opcode[i]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1241,7 +1241,7 @@ static void init_cmd_handler(void)
|
||||||
bool exec_instruction(CPUX86State *env, struct x86_decode *ins)
|
bool exec_instruction(CPUX86State *env, struct x86_decode *ins)
|
||||||
{
|
{
|
||||||
if (!_cmd_handler[ins->cmd].handler) {
|
if (!_cmd_handler[ins->cmd].handler) {
|
||||||
printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip,
|
printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x) \n", env->eip,
|
||||||
ins->cmd, ins->opcode[0],
|
ins->cmd, ins->opcode[0],
|
||||||
ins->opcode_len > 1 ? ins->opcode[1] : 0);
|
ins->opcode_len > 1 ? ins->opcode[1] : 0);
|
||||||
env->eip += ins->len;
|
env->eip += ins->len;
|
||||||
|
|
|
@ -7114,7 +7114,7 @@ PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort by PVR, ordering special case "host" last. */
|
/* Sort by PVR, ordering special case "host" last. */
|
||||||
static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b)
|
static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d)
|
||||||
{
|
{
|
||||||
ObjectClass *oc_a = (ObjectClass *)a;
|
ObjectClass *oc_a = (ObjectClass *)a;
|
||||||
ObjectClass *oc_b = (ObjectClass *)b;
|
ObjectClass *oc_b = (ObjectClass *)b;
|
||||||
|
@ -7182,7 +7182,7 @@ static void ppc_cpu_list(void)
|
||||||
|
|
||||||
qemu_printf("Available CPUs:\n");
|
qemu_printf("Available CPUs:\n");
|
||||||
list = object_class_get_list(TYPE_POWERPC_CPU, false);
|
list = object_class_get_list(TYPE_POWERPC_CPU, false);
|
||||||
list = g_slist_sort(list, ppc_cpu_list_compare);
|
list = g_slist_sort_with_data(list, ppc_cpu_list_compare, NULL);
|
||||||
g_slist_foreach(list, ppc_cpu_list_entry, NULL);
|
g_slist_foreach(list, ppc_cpu_list_entry, NULL);
|
||||||
g_slist_free(list);
|
g_slist_free(list);
|
||||||
|
|
||||||
|
|
|
@ -373,7 +373,7 @@ static void s390_print_cpu_model_list_entry(gpointer data, gpointer user_data)
|
||||||
g_free(name);
|
g_free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint s390_cpu_list_compare(gconstpointer a, gconstpointer b)
|
static gint s390_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d)
|
||||||
{
|
{
|
||||||
const S390CPUClass *cc_a = S390_CPU_CLASS((ObjectClass *)a);
|
const S390CPUClass *cc_a = S390_CPU_CLASS((ObjectClass *)a);
|
||||||
const S390CPUClass *cc_b = S390_CPU_CLASS((ObjectClass *)b);
|
const S390CPUClass *cc_b = S390_CPU_CLASS((ObjectClass *)b);
|
||||||
|
@ -415,7 +415,7 @@ void s390_cpu_list(void)
|
||||||
|
|
||||||
qemu_printf("Available CPUs:\n");
|
qemu_printf("Available CPUs:\n");
|
||||||
list = object_class_get_list(TYPE_S390_CPU, false);
|
list = object_class_get_list(TYPE_S390_CPU, false);
|
||||||
list = g_slist_sort(list, s390_cpu_list_compare);
|
list = g_slist_sort_with_data(list, s390_cpu_list_compare, NULL);
|
||||||
g_slist_foreach(list, s390_print_cpu_model_list_entry, NULL);
|
g_slist_foreach(list, s390_print_cpu_model_list_entry, NULL);
|
||||||
g_slist_free(list);
|
g_slist_free(list);
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -50,7 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
|
@ -122,7 +122,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-web \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
|
145
tests/docker/dockerfiles/emsdk-wasm32-cross.docker
Normal file
145
tests/docker/dockerfiles/emsdk-wasm32-cross.docker
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
# syntax = docker/dockerfile:1.5
|
||||||
|
|
||||||
|
ARG EMSDK_VERSION_QEMU=3.1.50
|
||||||
|
ARG ZLIB_VERSION=1.3.1
|
||||||
|
ARG GLIB_MINOR_VERSION=2.84
|
||||||
|
ARG GLIB_VERSION=${GLIB_MINOR_VERSION}.0
|
||||||
|
ARG PIXMAN_VERSION=0.44.2
|
||||||
|
ARG FFI_VERSION=v3.4.7
|
||||||
|
ARG MESON_VERSION=1.5.0
|
||||||
|
|
||||||
|
FROM emscripten/emsdk:$EMSDK_VERSION_QEMU AS build-base
|
||||||
|
ARG MESON_VERSION
|
||||||
|
ENV TARGET=/builddeps/target
|
||||||
|
ENV CPATH="$TARGET/include"
|
||||||
|
ENV PKG_CONFIG_PATH="$TARGET/lib/pkgconfig"
|
||||||
|
ENV EM_PKG_CONFIG_PATH="$PKG_CONFIG_PATH"
|
||||||
|
ENV CFLAGS="-O3 -pthread -DWASM_BIGINT"
|
||||||
|
ENV CXXFLAGS="$CFLAGS"
|
||||||
|
ENV LDFLAGS="-sWASM_BIGINT -sASYNCIFY=1 -L$TARGET/lib"
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
autoconf \
|
||||||
|
build-essential \
|
||||||
|
libglib2.0-dev \
|
||||||
|
libtool \
|
||||||
|
pkgconf \
|
||||||
|
ninja-build \
|
||||||
|
python3-pip
|
||||||
|
RUN pip3 install meson==${MESON_VERSION} tomli
|
||||||
|
RUN mkdir /build
|
||||||
|
WORKDIR /build
|
||||||
|
RUN mkdir -p $TARGET
|
||||||
|
RUN <<EOF
|
||||||
|
cat <<EOT > /cross.meson
|
||||||
|
[host_machine]
|
||||||
|
system = 'emscripten'
|
||||||
|
cpu_family = 'wasm32'
|
||||||
|
cpu = 'wasm32'
|
||||||
|
endian = 'little'
|
||||||
|
|
||||||
|
[binaries]
|
||||||
|
c = 'emcc'
|
||||||
|
cpp = 'em++'
|
||||||
|
ar = 'emar'
|
||||||
|
ranlib = 'emranlib'
|
||||||
|
pkgconfig = ['pkg-config', '--static']
|
||||||
|
EOT
|
||||||
|
EOF
|
||||||
|
|
||||||
|
FROM build-base AS zlib-dev
|
||||||
|
ARG ZLIB_VERSION
|
||||||
|
RUN mkdir -p /zlib
|
||||||
|
RUN curl -Ls https://zlib.net/zlib-$ZLIB_VERSION.tar.xz | \
|
||||||
|
tar xJC /zlib --strip-components=1
|
||||||
|
WORKDIR /zlib
|
||||||
|
RUN emconfigure ./configure --prefix=$TARGET --static
|
||||||
|
RUN emmake make install -j$(nproc)
|
||||||
|
|
||||||
|
FROM build-base AS libffi-dev
|
||||||
|
ARG FFI_VERSION
|
||||||
|
RUN mkdir -p /libffi
|
||||||
|
RUN git clone https://github.com/libffi/libffi /libffi
|
||||||
|
WORKDIR /libffi
|
||||||
|
RUN git checkout $FFI_VERSION
|
||||||
|
RUN autoreconf -fiv
|
||||||
|
RUN emconfigure ./configure --host=wasm32-unknown-linux \
|
||||||
|
--prefix=$TARGET --enable-static \
|
||||||
|
--disable-shared --disable-dependency-tracking \
|
||||||
|
--disable-builddir --disable-multi-os-directory \
|
||||||
|
--disable-raw-api --disable-docs
|
||||||
|
RUN emmake make install SUBDIRS='include' -j$(nproc)
|
||||||
|
|
||||||
|
FROM build-base AS pixman-dev
|
||||||
|
ARG PIXMAN_VERSION
|
||||||
|
RUN mkdir /pixman/
|
||||||
|
RUN git clone https://gitlab.freedesktop.org/pixman/pixman /pixman/
|
||||||
|
WORKDIR /pixman
|
||||||
|
RUN git checkout pixman-$PIXMAN_VERSION
|
||||||
|
RUN <<EOF
|
||||||
|
cat <<EOT >> /cross.meson
|
||||||
|
[built-in options]
|
||||||
|
c_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')]
|
||||||
|
cpp_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')]
|
||||||
|
objc_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')]
|
||||||
|
c_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')]
|
||||||
|
cpp_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')]
|
||||||
|
EOT
|
||||||
|
EOF
|
||||||
|
RUN meson setup _build --prefix=$TARGET --cross-file=/cross.meson \
|
||||||
|
--default-library=static \
|
||||||
|
--buildtype=release -Dtests=disabled -Ddemos=disabled
|
||||||
|
RUN meson install -C _build
|
||||||
|
|
||||||
|
FROM build-base AS glib-dev
|
||||||
|
ARG GLIB_VERSION
|
||||||
|
ARG GLIB_MINOR_VERSION
|
||||||
|
RUN mkdir -p /stub
|
||||||
|
WORKDIR /stub
|
||||||
|
RUN <<EOF
|
||||||
|
cat <<'EOT' > res_query.c
|
||||||
|
#include <netdb.h>
|
||||||
|
int res_query(const char *name, int class,
|
||||||
|
int type, unsigned char *dest, int len)
|
||||||
|
{
|
||||||
|
h_errno = HOST_NOT_FOUND;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
EOF
|
||||||
|
RUN emcc ${CFLAGS} -c res_query.c -fPIC -o libresolv.o
|
||||||
|
RUN ar rcs libresolv.a libresolv.o
|
||||||
|
RUN mkdir -p $TARGET/lib/
|
||||||
|
RUN cp libresolv.a $TARGET/lib/
|
||||||
|
|
||||||
|
RUN mkdir -p /glib
|
||||||
|
RUN curl -Lks https://download.gnome.org/sources/glib/${GLIB_MINOR_VERSION}/glib-$GLIB_VERSION.tar.xz | \
|
||||||
|
tar xJC /glib --strip-components=1
|
||||||
|
|
||||||
|
COPY --link --from=zlib-dev /builddeps/ /builddeps/
|
||||||
|
COPY --link --from=libffi-dev /builddeps/ /builddeps/
|
||||||
|
|
||||||
|
WORKDIR /glib
|
||||||
|
RUN <<EOF
|
||||||
|
CFLAGS="$CFLAGS -Wno-incompatible-function-pointer-types" ;
|
||||||
|
cat <<EOT >> /cross.meson
|
||||||
|
[built-in options]
|
||||||
|
c_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')]
|
||||||
|
cpp_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')]
|
||||||
|
objc_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')]
|
||||||
|
c_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')]
|
||||||
|
cpp_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')]
|
||||||
|
EOT
|
||||||
|
EOF
|
||||||
|
RUN meson setup _build --prefix=$TARGET --cross-file=/cross.meson \
|
||||||
|
--default-library=static --buildtype=release --force-fallback-for=pcre2 \
|
||||||
|
-Dselinux=disabled -Dxattr=false -Dlibmount=disabled -Dnls=disabled \
|
||||||
|
-Dtests=false -Dglib_debug=disabled -Dglib_assert=false -Dglib_checks=false
|
||||||
|
# FIXME: emscripten doesn't provide some pthread functions in the final link,
|
||||||
|
# which isn't detected during meson setup.
|
||||||
|
RUN sed -i -E "/#define HAVE_POSIX_SPAWN 1/d" ./_build/config.h
|
||||||
|
RUN sed -i -E "/#define HAVE_PTHREAD_GETNAME_NP 1/d" ./_build/config.h
|
||||||
|
RUN meson install -C _build
|
||||||
|
|
||||||
|
FROM build-base
|
||||||
|
COPY --link --from=glib-dev /builddeps/ /builddeps/
|
||||||
|
COPY --link --from=pixman-dev /builddeps/ /builddeps/
|
|
@ -121,7 +121,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
rpm2cpio \
|
rpm2cpio \
|
||||||
rustc \
|
rustc-1.77 \
|
||||||
sed \
|
sed \
|
||||||
socat \
|
socat \
|
||||||
sparse \
|
sparse \
|
||||||
|
@ -150,6 +150,7 @@ ENV LANG "en_US.UTF-8"
|
||||||
ENV MAKE "/usr/bin/make"
|
ENV MAKE "/usr/bin/make"
|
||||||
ENV NINJA "/usr/bin/ninja"
|
ENV NINJA "/usr/bin/ninja"
|
||||||
ENV PYTHON "/usr/bin/python3"
|
ENV PYTHON "/usr/bin/python3"
|
||||||
|
ENV RUSTC=/usr/bin/rustc-1.77
|
||||||
ENV CARGO_HOME=/usr/local/cargo
|
ENV CARGO_HOME=/usr/local/cargo
|
||||||
ENV PATH=$CARGO_HOME/bin:$PATH
|
ENV PATH=$CARGO_HOME/bin:$PATH
|
||||||
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
|
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
|
||||||
|
|
|
@ -64,6 +64,11 @@ mappings:
|
||||||
python3-wheel:
|
python3-wheel:
|
||||||
OpenSUSELeap15: python311-pip
|
OpenSUSELeap15: python311-pip
|
||||||
|
|
||||||
|
rust:
|
||||||
|
Debian12: rustc-web
|
||||||
|
Ubuntu2204: rustc-1.77
|
||||||
|
Ubuntu2404: rustc-1.77
|
||||||
|
|
||||||
pypi_mappings:
|
pypi_mappings:
|
||||||
# Request more recent version
|
# Request more recent version
|
||||||
meson:
|
meson:
|
||||||
|
|
|
@ -141,7 +141,8 @@ fedora_rustup_nightly_extras = [
|
||||||
'RUN $CARGO --list\n',
|
'RUN $CARGO --list\n',
|
||||||
]
|
]
|
||||||
|
|
||||||
ubuntu2204_bindgen_extras = [
|
ubuntu2204_rust_extras = [
|
||||||
|
"ENV RUSTC=/usr/bin/rustc-1.77\n",
|
||||||
"ENV CARGO_HOME=/usr/local/cargo\n",
|
"ENV CARGO_HOME=/usr/local/cargo\n",
|
||||||
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
|
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
|
||||||
"RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n",
|
"RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n",
|
||||||
|
@ -170,7 +171,7 @@ try:
|
||||||
generate_dockerfile("fedora", "fedora-40")
|
generate_dockerfile("fedora", "fedora-40")
|
||||||
generate_dockerfile("opensuse-leap", "opensuse-leap-15")
|
generate_dockerfile("opensuse-leap", "opensuse-leap-15")
|
||||||
generate_dockerfile("ubuntu2204", "ubuntu-2204",
|
generate_dockerfile("ubuntu2204", "ubuntu-2204",
|
||||||
trailer="".join(ubuntu2204_bindgen_extras))
|
trailer="".join(ubuntu2204_rust_extras))
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-fatal Rust-enabled build
|
# Non-fatal Rust-enabled build
|
||||||
|
|
|
@ -229,6 +229,10 @@ static void __attribute__((constructor)) init_cache_info(void)
|
||||||
|
|
||||||
/* Caches are coherent and do not require flushing; symbol inline. */
|
/* Caches are coherent and do not require flushing; symbol inline. */
|
||||||
|
|
||||||
|
#elif defined(EMSCRIPTEN)
|
||||||
|
|
||||||
|
/* Wasm doesn't have executable region of memory. */
|
||||||
|
|
||||||
#elif defined(__aarch64__) && !defined(CONFIG_WIN32)
|
#elif defined(__aarch64__) && !defined(CONFIG_WIN32)
|
||||||
/*
|
/*
|
||||||
* For Windows, we use generic implementation of flush_idcache_range, that
|
* For Windows, we use generic implementation of flush_idcache_range, that
|
||||||
|
|
127
util/coroutine-wasm.c
Normal file
127
util/coroutine-wasm.c
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* emscripten fiber coroutine initialization code
|
||||||
|
* based on coroutine-ucontext.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
|
||||||
|
* Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.0 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/coroutine_int.h"
|
||||||
|
#include "qemu/coroutine-tls.h"
|
||||||
|
|
||||||
|
#include <emscripten/fiber.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Coroutine base;
|
||||||
|
void *stack;
|
||||||
|
size_t stack_size;
|
||||||
|
|
||||||
|
void *asyncify_stack;
|
||||||
|
size_t asyncify_stack_size;
|
||||||
|
|
||||||
|
CoroutineAction action;
|
||||||
|
|
||||||
|
emscripten_fiber_t fiber;
|
||||||
|
} CoroutineEmscripten;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-thread coroutine bookkeeping
|
||||||
|
*/
|
||||||
|
QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current);
|
||||||
|
QEMU_DEFINE_STATIC_CO_TLS(CoroutineEmscripten *, leader);
|
||||||
|
size_t leader_asyncify_stack_size = COROUTINE_STACK_SIZE;
|
||||||
|
|
||||||
|
static void coroutine_trampoline(void *co_)
|
||||||
|
{
|
||||||
|
Coroutine *co = co_;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
co->entry(co->entry_arg);
|
||||||
|
qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Coroutine *qemu_coroutine_new(void)
|
||||||
|
{
|
||||||
|
CoroutineEmscripten *co;
|
||||||
|
|
||||||
|
co = g_malloc0(sizeof(*co));
|
||||||
|
|
||||||
|
co->stack_size = COROUTINE_STACK_SIZE;
|
||||||
|
co->stack = qemu_alloc_stack(&co->stack_size);
|
||||||
|
|
||||||
|
co->asyncify_stack_size = COROUTINE_STACK_SIZE;
|
||||||
|
co->asyncify_stack = g_malloc0(co->asyncify_stack_size);
|
||||||
|
emscripten_fiber_init(&co->fiber, coroutine_trampoline, &co->base,
|
||||||
|
co->stack, co->stack_size, co->asyncify_stack,
|
||||||
|
co->asyncify_stack_size);
|
||||||
|
|
||||||
|
return &co->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_coroutine_delete(Coroutine *co_)
|
||||||
|
{
|
||||||
|
CoroutineEmscripten *co = DO_UPCAST(CoroutineEmscripten, base, co_);
|
||||||
|
|
||||||
|
qemu_free_stack(co->stack, co->stack_size);
|
||||||
|
g_free(co->asyncify_stack);
|
||||||
|
g_free(co);
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
|
||||||
|
CoroutineAction action)
|
||||||
|
{
|
||||||
|
CoroutineEmscripten *from = DO_UPCAST(CoroutineEmscripten, base, from_);
|
||||||
|
CoroutineEmscripten *to = DO_UPCAST(CoroutineEmscripten, base, to_);
|
||||||
|
|
||||||
|
set_current(to_);
|
||||||
|
to->action = action;
|
||||||
|
emscripten_fiber_swap(&from->fiber, &to->fiber);
|
||||||
|
return from->action;
|
||||||
|
}
|
||||||
|
|
||||||
|
Coroutine *qemu_coroutine_self(void)
|
||||||
|
{
|
||||||
|
Coroutine *self = get_current();
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
CoroutineEmscripten *leaderp = get_leader();
|
||||||
|
if (!leaderp) {
|
||||||
|
leaderp = g_malloc0(sizeof(*leaderp));
|
||||||
|
leaderp->asyncify_stack = g_malloc0(leader_asyncify_stack_size);
|
||||||
|
leaderp->asyncify_stack_size = leader_asyncify_stack_size;
|
||||||
|
emscripten_fiber_init_from_current_context(
|
||||||
|
&leaderp->fiber,
|
||||||
|
leaderp->asyncify_stack,
|
||||||
|
leaderp->asyncify_stack_size);
|
||||||
|
leaderp->stack = leaderp->fiber.stack_limit;
|
||||||
|
leaderp->stack_size =
|
||||||
|
leaderp->fiber.stack_base - leaderp->fiber.stack_limit;
|
||||||
|
set_leader(leaderp);
|
||||||
|
}
|
||||||
|
self = &leaderp->base;
|
||||||
|
set_current(self);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool qemu_in_coroutine(void)
|
||||||
|
{
|
||||||
|
Coroutine *self = get_current();
|
||||||
|
|
||||||
|
return self && self->caller;
|
||||||
|
}
|
|
@ -11,7 +11,9 @@ if host_os != 'windows'
|
||||||
endif
|
endif
|
||||||
util_ss.add(files('compatfd.c'))
|
util_ss.add(files('compatfd.c'))
|
||||||
util_ss.add(files('event_notifier-posix.c'))
|
util_ss.add(files('event_notifier-posix.c'))
|
||||||
util_ss.add(files('mmap-alloc.c'))
|
if host_os != 'emscripten'
|
||||||
|
util_ss.add(files('mmap-alloc.c'))
|
||||||
|
endif
|
||||||
freebsd_dep = []
|
freebsd_dep = []
|
||||||
if host_os == 'freebsd'
|
if host_os == 'freebsd'
|
||||||
freebsd_dep = util
|
freebsd_dep = util
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
#include <lwp.h>
|
#include <lwp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "qemu/memalign.h"
|
||||||
#include "qemu/mmap-alloc.h"
|
#include "qemu/mmap-alloc.h"
|
||||||
|
|
||||||
#define MAX_MEM_PREALLOC_THREAD_COUNT 16
|
#define MAX_MEM_PREALLOC_THREAD_COUNT 16
|
||||||
|
@ -210,11 +211,21 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared,
|
||||||
const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) |
|
const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) |
|
||||||
(noreserve ? QEMU_MAP_NORESERVE : 0);
|
(noreserve ? QEMU_MAP_NORESERVE : 0);
|
||||||
size_t align = QEMU_VMALLOC_ALIGN;
|
size_t align = QEMU_VMALLOC_ALIGN;
|
||||||
|
#ifndef EMSCRIPTEN
|
||||||
void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0);
|
void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0);
|
||||||
|
|
||||||
if (ptr == MAP_FAILED) {
|
if (ptr == MAP_FAILED) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* qemu_ram_mmap is not implemented for Emscripten. Use qemu_memalign
|
||||||
|
* for the anonymous allocation. noreserve is ignored as there is no swap
|
||||||
|
* space on Emscripten, and shared is ignored as there is no other
|
||||||
|
* processes on Emscripten.
|
||||||
|
*/
|
||||||
|
void *ptr = qemu_memalign(align, size);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (alignment) {
|
if (alignment) {
|
||||||
*alignment = align;
|
*alignment = align;
|
||||||
|
@ -227,7 +238,16 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared,
|
||||||
void qemu_anon_ram_free(void *ptr, size_t size)
|
void qemu_anon_ram_free(void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
trace_qemu_anon_ram_free(ptr, size);
|
trace_qemu_anon_ram_free(ptr, size);
|
||||||
|
#ifndef EMSCRIPTEN
|
||||||
qemu_ram_munmap(-1, ptr, size);
|
qemu_ram_munmap(-1, ptr, size);
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* qemu_ram_munmap is not implemented for Emscripten and qemu_memalign
|
||||||
|
* was used for the allocation. Use the corresponding freeing function
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
qemu_vfree(ptr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_socket_set_block(int fd)
|
void qemu_socket_set_block(int fd)
|
||||||
|
@ -588,7 +608,15 @@ bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads,
|
||||||
{
|
{
|
||||||
static gsize initialized;
|
static gsize initialized;
|
||||||
int ret;
|
int ret;
|
||||||
|
#ifndef EMSCRIPTEN
|
||||||
size_t hpagesize = qemu_fd_getpagesize(fd);
|
size_t hpagesize = qemu_fd_getpagesize(fd);
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* mmap-alloc.c is excluded from Emscripten build, so qemu_fd_getpagesize
|
||||||
|
* is unavailable. Fallback to the lower level implementation.
|
||||||
|
*/
|
||||||
|
size_t hpagesize = qemu_real_host_page_size();
|
||||||
|
#endif
|
||||||
size_t numpages = DIV_ROUND_UP(sz, hpagesize);
|
size_t numpages = DIV_ROUND_UP(sz, hpagesize);
|
||||||
bool use_madv_populate_write;
|
bool use_madv_populate_write;
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue