* 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:
Stefan Hajnoczi 2025-05-07 16:10:59 -04:00
commit c5f122fdcc
78 changed files with 861 additions and 654 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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>

View file

@ -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'))

View file

@ -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,

View 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
View file

@ -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" && {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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
View 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

View file

@ -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

View file

@ -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
View 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
}

View file

@ -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
View file

@ -108,7 +108,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"libc", "libc",
"qemu_api_macros", "qemu_api_macros",
"version_check",
] ]
[[package]] [[package]]

View file

@ -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"

View file

@ -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"

View file

@ -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>,

View file

@ -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 },

View file

@ -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";

View file

@ -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]

View file

@ -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),

View file

@ -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";

View file

@ -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)]

View file

@ -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 = []

View file

@ -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(())
} }

View file

@ -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',

View file

@ -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);
}
}

View file

@ -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

View file

@ -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,
); );

View file

@ -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},

View file

@ -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;

View file

@ -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};

View file

@ -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);
}
}

View file

@ -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
} }

View file

@ -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

View file

@ -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(),
) )
} }
} }

View file

@ -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(

View file

@ -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 {}

View file

@ -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 {

View file

@ -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
}; };

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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 {

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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]);
} }

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View 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/

View file

@ -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 \

View file

@ -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:

View file

@ -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

View file

@ -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
View 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;
}

View file

@ -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

View file

@ -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;