diff --git a/Kconfig b/Kconfig index 63ca7f46d..fb6a24a2d 100644 --- a/Kconfig +++ b/Kconfig @@ -4,4 +4,3 @@ source accel/Kconfig source target/Kconfig source hw/Kconfig source semihosting/Kconfig -source rust/Kconfig diff --git a/Kconfig.host b/Kconfig.host index 933425c74..21ca0a82e 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -59,8 +59,5 @@ config VFIO_USER_SERVER_ALLOWED config HV_BALLOON_POSSIBLE bool -config HAVE_RUST - bool - config MAC_PVG bool diff --git a/MAINTAINERS b/MAINTAINERS index f1dfc6290..c4a2925c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1228,11 +1228,6 @@ F: tests/qtest/microbit-test.c F: tests/functional/arm/test_microbit.py F: docs/system/arm/nrf.rst -ARM PL011 Rust device -M: Manos Pitsidianakis -S: Maintained -F: rust/hw/char/pl011/ - AVR Machines ------------- @@ -3479,27 +3474,6 @@ F: hw/core/register.c F: include/hw/register.h F: include/hw/registerfields.h -Rust -M: Manos Pitsidianakis -S: Maintained -F: rust/bql/ -F: rust/chardev/ -F: rust/common/ -F: rust/hw/core/ -F: rust/migration/ -F: rust/qemu-macros/ -F: rust/qom/ -F: rust/rustfmt.toml -F: rust/system/ -F: rust/tests/ -F: rust/util/ -F: scripts/get-wraps-from-cargo-registry.py - -Rust-related patches CC here -L: qemu-rust@nongnu.org -F: tests/docker/test-rust -F: rust/ - SLIRP M: Samuel Thibault S: Maintained @@ -4426,16 +4400,6 @@ F: docs/devel/qapi-domain.rst F: scripts/kernel-doc F: scripts/lib/kdoc/ -Rust build system integration -M: Manos Pitsidianakis -L: qemu-rust@nongnu.org -S: Maintained -F: scripts/rust/ -F: rust/.gitignore -F: rust/Kconfig -F: rust/meson.build -F: rust/wrapper.h - Miscellaneous ------------- Performance Tools and Tests diff --git a/clippy.toml b/clippy.toml deleted file mode 100644 index 204f5713c..000000000 --- a/clippy.toml +++ /dev/null @@ -1,3 +0,0 @@ -doc-valid-idents = ["IrDA", "PrimeCell", ".."] -allow-mixed-uninlined-format-args = false -msrv = "1.83.0" diff --git a/configure b/configure index 0f7eb9558..dedf401d9 100755 --- a/configure +++ b/configure @@ -207,10 +207,6 @@ for opt do ;; --objcc=*) objcc="$optarg" ;; - --rustc=*) RUSTC="$optarg" - ;; - --rustdoc=*) RUSTDOC="$optarg" - ;; --cpu=*) cpu="$optarg" ;; --extra-cflags=*) @@ -256,8 +252,6 @@ python= download="enabled" skip_meson=no use_containers="yes" -rust="disabled" -rust_target_triple="" gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb") gdb_arches="" @@ -324,8 +318,6 @@ windmc="${WINDMC-${cross_prefix}windmc}" pkg_config="${PKG_CONFIG-${cross_prefix}pkg-config}" sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" -rustc="${RUSTC-rustc}" -rustdoc="${RUSTDOC-rustdoc}" check_define() { cat > $TMPC <&1 - rust=disabled - fi -fi -if test "$rust" != disabled && has "$rustc" && $rustc -vV > "${TMPDIR1}/${TMPB}.out"; then - rust_host_triple=$(sed -n 's/^host: //p' "${TMPDIR1}/${TMPB}.out") -else - if test "$rust" = enabled; then - error_exit "could not execute rustc binary \"$rustc\"" - fi - rust=disabled -fi -if test "$rust" != disabled && test -z "$rust_target_triple"; then - # arch and os generally matches between meson and rust - rust_arch=$host_arch - rust_os=$host_os - rust_machine=unknown - rust_osvariant= - - # tweak rust_os if needed; also, machine and variant depend on the OS - android=no - case "$host_os" in - darwin) - # e.g. aarch64-apple-darwin - rust_machine=apple - ;; - - linux) - # detect android/glibc/musl - if check_define __ANDROID__; then - rust_osvariant=android - android=yes - else - cat > $TMPC << EOF -#define _GNU_SOURCE -#include -#ifndef __USE_GNU -error using musl -#endif -EOF - if compile_object; then - rust_osvariant=gnu - else - rust_osvariant=musl - fi - fi - - case "$cpu" in - arm) - # e.g. arm-unknown-linux-gnueabi, arm-unknown-linux-gnueabihf - write_c_skeleton - compile_object - if $READELF -A $TMPO | grep Tag_API_VFP_args: > /dev/null; then - rust_osvariant=${rust_osvariant}eabihf - else - rust_osvariant=${rust_osvariant}eabi - fi - ;; - - mips64) - # e.g. mips64-unknown-linux-gnuabi64 - rust_osvariant=${rust_osvariant}abi64 - ;; - esac - ;; - - netbsd) - # e.g. arm-unknown-netbsd-eabihf - test "$host_arch" = arm && rust_osvariant=eabihf - ;; - - sunos) - rust_machine=pc - rust_os=solaris - ;; - - windows) - # e.g. aarch64-pc-windows-gnullvm, x86_64-pc-windows-gnu (MSVC not supported) - rust_machine=pc - if test "$host_arch" = aarch64; then - rust_osvariant=gnullvm - else - rust_osvariant=gnu - fi - ;; - esac - - # now tweak the architecture part, possibly based on pre-canonicalization --cpu - case "$host_arch" in - arm) - # preserve ISA version (armv7 etc.) from $raw_cpu if passed via --cpu - rust_arch=$raw_cpu - test "$rust_arch" = arm && test "$rust_os" != linux && rust_arch=armv7 - ;; - - mips) - # preserve ISA version (mipsisa64r6 etc.) and include endianness - rust_arch=${raw_cpu%el} - test "$bigendian" = no && rust_arch=${rust_arch}el - ;; - - riscv32|riscv64) - # e.g. riscv64gc-unknown-linux-gnu, but riscv64-linux-android - test "$android" = no && rust_arch=${rust_arch}gc - ;; - - sparc64) - if test "$rust_os" = solaris; then - rust_arch=sparcv9 - rust_machine=sun - fi - ;; - - x86_64) - # e.g. x86_64-unknown-linux-gnux32 - test "$raw_cpu" = x32 && rust_osvariant=${rust_osvariant}x32 - ;; - esac - - if test "$android" = yes; then - # e.g. aarch64-linux-android - rust_target_triple=$rust_arch-$rust_os-$rust_osvariant - else - rust_target_triple=$rust_arch-$rust_machine-$rust_os${rust_osvariant:+-$rust_osvariant} - fi -fi - ########################################## # functions to probe cross compilers @@ -1782,9 +1625,6 @@ if test "$container" != no; then echo "RUNC=$runc" >> $config_host_mak fi echo "SUBDIRS=$subdirs" >> $config_host_mak -if test "$rust" != disabled; then - echo "RUST_TARGET_TRIPLE=$rust_target_triple" >> $config_host_mak -fi echo "PYTHON=$python" >> $config_host_mak echo "MKVENV_ENSUREGROUP=$mkvenv ensuregroup $mkvenv_online_flag" >> $config_host_mak echo "GENISOIMAGE=$genisoimage" >> $config_host_mak @@ -1912,15 +1752,6 @@ if test "$skip_meson" = no; then echo "c = [$(meson_quote $cc $CPU_CFLAGS)]" >> $cross test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross test -n "$objcc" && echo "objc = [$(meson_quote $objcc $CPU_CFLAGS)]" >> $cross - if test "$rust" != disabled; then - if test "$rust_host_triple" != "$rust_target_triple"; then - echo "rust = [$(meson_quote $rustc --target "$rust_target_triple")]" >> $cross - echo "rustdoc = [$(meson_quote $rustdoc --target "$rust_target_triple")]" >> $cross - else - echo "rust = [$(meson_quote $rustc)]" >> $cross - echo "rustdoc = [$(meson_quote $rustdoc)]" >> $cross - fi - fi echo "ar = [$(meson_quote $ar)]" >> $cross echo "dlltool = [$(meson_quote $dlltool)]" >> $cross echo "nm = [$(meson_quote $nm)]" >> $cross @@ -1957,9 +1788,6 @@ if test "$skip_meson" = no; then echo "# Automatically generated by configure - do not modify" > $native echo "[binaries]" >> $native echo "c = [$(meson_quote $host_cc)]" >> $native - if test "$rust" != disabled; then - echo "rust = [$(meson_quote $rustc)]" >> $cross - fi mv $native config-meson.native meson_option_add --native-file meson_option_add config-meson.native @@ -1978,7 +1806,6 @@ if test "$skip_meson" = no; then test "$pie" = no && meson_option_add -Db_pie=false # QEMU options - test "$rust" != "disabled" && meson_option_add "-Drust=$rust" test "$cfi" != false && meson_option_add "-Dcfi=$cfi" "-Db_lto=$cfi" test "$docs" != auto && meson_option_add "-Ddocs=$docs" test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE" @@ -2063,11 +1890,3 @@ echo ' "$@"' >>config.status chmod +x config.status rm -r "$TMPDIR1" - -if test "$rust" != disabled; then - echo - echo 'INFO: Rust bindings generation with `bindgen` might fail in some cases where' - echo 'the detected `libclang` does not match the expected `clang` version/target. In' - echo 'this case you must pass the path to `clang` and `libclang` to your build' - echo 'command invocation using the environment variables CLANG_PATH and LIBCLANG_PATH' -fi diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 0160d3adb..15b983bfa 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -110,32 +110,6 @@ Python build dependencies required, it may be necessary to fetch python modules from the Python Package Index (PyPI) via ``pip``, in order to build QEMU. -Rust build dependencies - QEMU is generally conservative in adding new Rust dependencies, and all - of them are included in the distributed tarballs. One exception is the - bindgen tool, which is too big to package and distribute. The minimum - supported version of bindgen is 0.60.x. For distributions that do not - include bindgen or have an older version, it is recommended to install - a newer version using ``cargo install bindgen-cli``. - - QEMU requires Rust 1.83.0. This is available on all supported platforms - with two exception: Ubuntu LTS releases 22.04 and 24.04, and the - ``mips64el`` architecture on Debian bookworm. For all other - architectures, Debian bookworm provides a new-enough Rust compiler - in the ``rustc-web`` package. - - It is expected that in the future Ubuntu will provide updated packages - like the existing ``rustc-1.82`` package. The path to ``rustc`` and - ``rustdoc`` will have to be provided manually to the configure script. - - Some distros prefer to avoid vendored crate sources, and instead use - local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a - script, ``scripts/get-wraps-from-cargo-registry.py``, that automatically - performs this task. The script is meant to be invoked after unpacking - the QEMU tarball. QEMU also includes ``rust/Cargo.toml`` and - ``rust/Cargo.lock`` files that can be used to compute QEMU's build - dependencies, e.g. using ``cargo2rpm -p rust/Cargo.toml buildrequires``. - Optional build dependencies Build components whose absence does not affect the ability to build QEMU may not be available in distros, or may be too old for our requirements. diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst index 2a3143787..ee259f740 100644 --- a/docs/devel/codebase.rst +++ b/docs/devel/codebase.rst @@ -144,9 +144,6 @@ yet, so sometimes the source code is all you have. * `roms `_: Contains source code for various firmware and ROMs, which can be compiled if custom or updated versions are needed. -* `rust `_: - Rust integration in QEMU. It contains the new interfaces defined and - associated devices using it. * `scripts `_: Collection of scripts used in build and test systems, and various tools for QEMU codebase and execution traces. diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst deleted file mode 100644 index 13a20e86a..000000000 --- a/docs/devel/rust.rst +++ /dev/null @@ -1,470 +0,0 @@ -.. |msrv| replace:: 1.83.0 - -Rust in QEMU -============ - -Rust in QEMU is a project to enable using the Rust programming language -to add new functionality to QEMU. - -Right now, the focus is on making it possible to write devices that inherit -from ``SysBusDevice`` in `*safe*`__ Rust. Later, it may become possible -to write other kinds of devices (e.g. PCI devices that can do DMA), -complete boards, or backends (e.g. block device formats). - -__ https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html - -Building the Rust in QEMU code ------------------------------- - -The Rust in QEMU code is included in the emulators via Meson. Meson -invokes rustc directly, building static libraries that are then linked -together with the C code. This is completely automatic when you run -``make`` or ``ninja``. - -However, QEMU's build system also tries to be easy to use for people who -are accustomed to the more "normal" Cargo-based development workflow. -In particular: - -* the set of warnings and lints that are used to build QEMU always - comes from the ``rust/Cargo.toml`` workspace file - -* it is also possible to use ``cargo`` for common Rust-specific coding - tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. - -To this end, QEMU includes a ``build.rs`` build script that picks up -generated sources from QEMU's build directory and puts it in Cargo's -output directory (typically ``rust/target/``). A vanilla invocation -of Cargo will complain that it cannot find the generated sources, -which can be fixed in different ways: - -* by using Makefile targets, provided by Meson, that run ``clippy`` or - ``rustdoc``: - - make clippy - make rustdoc - -A target for ``rustfmt`` is also declared in ``rust/meson.build``: - - make rustfmt - -* by invoking ``cargo`` through the Meson `development environment`__ - feature:: - - pyvenv/bin/meson devenv -w ../rust cargo clippy --tests - pyvenv/bin/meson devenv -w ../rust cargo fmt - - If you are going to use ``cargo`` repeatedly, ``pyvenv/bin/meson devenv`` - will enter a shell where commands like ``cargo fmt`` just work. - -__ https://mesonbuild.com/Commands.html#devenv - -* by pointing the ``MESON_BUILD_ROOT`` to the top of your QEMU build - tree. This third method is useful if you are using ``rust-analyzer``; - you can set the environment variable through the - ``rust-analyzer.cargo.extraEnv`` setting. - -As shown above, you can use the ``--tests`` option as usual to operate on test -code. Note however that you cannot *build* or run tests via ``cargo``, because -they need support C code from QEMU that Cargo does not know about. Tests can -be run via ``meson test`` or ``make``:: - - make check-rust - -Note that doctests require all ``.o`` files from the build to be available. - -Supported tools -''''''''''''''' - -QEMU supports rustc version 1.83.0 and newer. The following features -from relatively new versions of Rust are not used for historical reasons; -patches are welcome: - -* associated constants are still explicitly marked ``'static`` (`changed in - 1.81.0`__) - -* ``&raw`` (stable in 1.82.0). - -* NUL-terminated file names with ``#[track_caller]`` are scheduled for - inclusion as ``#![feature(location_file_nul)]``, but it will be a while - before QEMU can use them. For now, there is special code in - ``util/error.c`` to support non-NUL-terminated file names. - -Associated const equality would be nice to have for some users of -``callbacks::FnCall``, but is still experimental. Const assertions -are used instead. - -__ https://github.com/rust-lang/rust/pull/125258 - -QEMU also supports version 0.60.x of bindgen, which is missing option -``--generate-cstr``. This option requires version 0.66.x and will -be adopted as soon as supporting these older versions is not necessary -anymore. - -Writing Rust code in QEMU -------------------------- - -QEMU includes several crates: - -* ``common`` provides Rust-only utilities - -* ``bql``, ``chardev``, ``hw/core``, ``migration``, ``qom``, ``system``, - ``util`` for bindings to respective QEMU C library APIs - -* ``qemu_macros`` defines several procedural macros that are useful when - writing C code - -* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``) - are sample devices that demonstrate Rust binding usage and ``qemu_macros``, and are - used to further develop them. These two crates are functional\ [#issues]_ replacements - for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files. - -.. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` - as of commit 3e0f118f82. The ``hpet`` crate is synchronized as of - commit 1433e38cc8. Both are lacking tracing functionality. - -This section explains how to work with them. - -Status -'''''' - -The stability of the modules can be defined as: - -- *complete*: ready for use in new devices; if applicable, the API supports the - full functionality available in C - -- *stable*: ready for production use, the API is safe and should not undergo - major changes - -- *proof of concept*: the API is subject to change but allows working with safe - Rust - -- *initial*: the API is in its initial stages; it requires large amount of - unsafe code; it might have soundness or type-safety issues - -The status of the modules is as follows: - -========================== ====================== -module status -========================== ====================== -``bql::cell`` stable -``common::assertions`` stable -``common::bitops`` complete -``common::callbacks`` complete -``common::errno`` complete -``common::zeroable`` stable -``hwcore::irq`` complete -``hwcore::qdev`` stable -``hwcore::sysbus`` stable -``migration::vmstate`` stable -``qom`` stable -``system::memory`` stable -``util::error`` stable -``util::log`` proof of concept -``util::module`` complete -``util::timer`` stable -========================== ====================== - -.. note:: - API stability is not a promise, if anything because the C APIs are not a stable - interface either. Also, ``unsafe`` interfaces may be replaced by safe interfaces - later. - -Naming convention -''''''''''''''''' - -C function names usually are prefixed according to the data type that they -apply to, for example ``timer_mod`` or ``sysbus_connect_irq``. Furthermore, -both function and structs sometimes have a ``qemu_`` or ``QEMU`` prefix. -Generally speaking, these are all removed in the corresponding Rust functions: -``QEMUTimer`` becomes ``timer::Timer``, ``timer_mod`` becomes ``Timer::modify``, -``sysbus_connect_irq`` becomes ``SysBusDeviceMethods::connect_irq``. - -Sometimes however a name appears multiple times in the QOM class hierarchy, -and the only difference is in the prefix. An example is ``qdev_realize`` and -``sysbus_realize``. In such cases, whenever a name is not unique in -the hierarchy, always add the prefix to the classes that are lower in -the hierarchy; for the top class, decide on a case by case basis. - -For example: - -========================== ========================================= -``device_cold_reset()`` ``DeviceMethods::cold_reset()`` -``pci_device_reset()`` ``PciDeviceMethods::pci_device_reset()`` -``pci_bridge_reset()`` ``PciBridgeMethods::pci_bridge_reset()`` -========================== ========================================= - -Here, the name is not exactly the same, but nevertheless ``PciDeviceMethods`` -adds the prefix to avoid confusion, because the functionality of -``device_cold_reset()`` and ``pci_device_reset()`` is subtly different. - -In this case, however, no prefix is needed: - -========================== ========================================= -``device_realize()`` ``DeviceMethods::realize()`` -``sysbus_realize()`` ``SysbusDeviceMethods::sysbus_realize()`` -``pci_realize()`` ``PciDeviceMethods::pci_realize()`` -========================== ========================================= - -Here, the lower classes do not add any functionality, and mostly -provide extra compile-time checking; the basic *realize* functionality -is the same for all devices. Therefore, ``DeviceMethods`` does not -add the prefix. - -Whenever a name is unique in the hierarchy, instead, you should -always remove the class name prefix. - -Common pitfalls -''''''''''''''' - -Rust has very strict rules with respect to how you get an exclusive (``&mut``) -reference; failure to respect those rules is a source of undefined behavior. -In particular, even if a value is loaded from a raw mutable pointer (``*mut``), -it *cannot* be casted to ``&mut`` unless the value was stored to the ``*mut`` -from a mutable reference. Furthermore, it is undefined behavior if any -shared reference was created between the store to the ``*mut`` and the load:: - - let mut p: u32 = 42; - let p_mut = &mut p; // 1 - let p_raw = p_mut as *mut u32; // 2 - - // p_raw keeps the mutable reference "alive" - - let p_shared = &p; // 3 - println!("access from &u32: {}", *p_shared); - - // Bring back the mutable reference, its lifetime overlaps - // with that of a shared reference. - let p_mut = unsafe { &mut *p_raw }; // 4 - println!("access from &mut 32: {}", *p_mut); - - println!("access from &u32: {}", *p_shared); // 5 - -These rules can be tested with `MIRI`__, for example. - -__ https://github.com/rust-lang/miri - -Almost all Rust code in QEMU will involve QOM objects, and pointers to these -objects are *shared*, for example because they are part of the QOM composition -tree. This creates exactly the above scenario: - -1. a QOM object is created - -2. a ``*mut`` is created, for example as the opaque value for a ``MemoryRegion`` - -3. the QOM object is placed in the composition tree - -4. a memory access dereferences the opaque value to a ``&mut`` - -5. but the shared reference is still present in the composition tree - -Because of this, QOM objects should almost always use ``&self`` instead -of ``&mut self``; access to internal fields must use *interior mutability* -to go from a shared reference to a ``&mut``. - -Whenever C code provides you with an opaque ``void *``, avoid converting it -to a Rust mutable reference, and use a shared reference instead. The -``bql::cell`` module provides wrappers that can be used to tell the -Rust compiler about interior mutability, and optionally to enforce locking -rules for the "Big QEMU Lock". In the future, similar cell types might -also be provided for ``AioContext``-based locking as well. - -In particular, device code will usually rely on the ``BqlRefCell`` and -``BqlCell`` type to ensure that data is accessed correctly under the -"Big QEMU Lock". These cell types are also known to the ``vmstate`` -crate, which is able to "look inside" them when building an in-memory -representation of a ``struct``'s layout. Note that the same is not true -of a ``RefCell`` or ``Mutex``. - -Bindings code instead will usually use the ``Opaque`` type, which hides -the contents of the underlying struct and can be easily converted to -a raw pointer, for use in calls to C functions. It can be used for -example as follows:: - - #[repr(transparent)] - #[derive(Debug, common::Wrapper)] - pub struct Object(Opaque); - -where the special ``derive`` macro provides useful methods such as -``from_raw``, ``as_ptr`, ``as_mut_ptr`` and ``raw_get``. The bindings will -then manually check for the big QEMU lock with assertions, which allows -the wrapper to be declared thread-safe:: - - unsafe impl Send for Object {} - unsafe impl Sync for Object {} - -Writing bindings to C code -'''''''''''''''''''''''''' - -Here are some things to keep in mind when working on the QEMU Rust crate. - -**Look at existing code** - Very often, similar idioms in C code correspond to similar tricks in - Rust bindings. If the C code uses ``offsetof``, look at qdev properties - or ``vmstate``. If the C code has a complex const struct, look at - ``MemoryRegion``. Reuse existing patterns for handling lifetimes; - for example use ``&T`` for QOM objects that do not need a reference - count (including those that can be embedded in other objects) and - ``Owned`` for those that need it. - -**Use the type system** - Bindings often will need access information that is specific to a type - (either a builtin one or a user-defined one) in order to pass it to C - functions. Put them in a trait and access it through generic parameters. - The ``vmstate`` module has examples of how to retrieve type information - for the fields of a Rust ``struct``. - -**Prefer unsafe traits to unsafe functions** - Unsafe traits are much easier to prove correct than unsafe functions. - They are an excellent place to store metadata that can later be accessed - by generic functions. C code usually places metadata in global variables; - in Rust, they can be stored in traits and then turned into ``static`` - variables. Often, unsafe traits can be generated by procedural macros. - -**Document limitations due to old Rust versions** - If you need to settle for an inferior solution because of the currently - supported set of Rust versions, document it in the source and in this - file. This ensures that it can be fixed when the minimum supported - version is bumped. - -**Keep locking in mind**. - When marking a type ``Sync``, be careful of whether it needs the big - QEMU lock. Use ``BqlCell`` and ``BqlRefCell`` for interior data, - or assert ``bql_locked()``. - -**Don't be afraid of complexity, but document and isolate it** - It's okay to be tricky; device code is written more often than bindings - code and it's important that it is idiomatic. However, you should strive - to isolate any tricks in a place (for example a ``struct``, a trait - or a macro) where it can be documented and tested. If needed, include - toy versions of the code in the documentation. - -Writing procedural macros -''''''''''''''''''''''''' - -By conventions, procedural macros are split in two functions, one -returning ``Result`` with the body of -the procedural macro, and the second returning ``proc_macro::TokenStream`` -which is the actual procedural macro. The former's name is the same as -the latter with the ``_or_error`` suffix. The code for the latter is more -or less fixed; it follows the following template, which is fixed apart -from the type after ``as`` in the invocation of ``parse_macro_input!``:: - - #[proc_macro_derive(Object)] - pub fn derive_object(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_object_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() - } - -The ``qemu_macros`` crate has utility functions to examine a -``DeriveInput`` and perform common checks (e.g. looking for a struct -with named fields). These functions return ``Result<..., syn::Error>`` -and can be used easily in the procedural macro function:: - - fn derive_object_or_error(input: DeriveInput) -> - Result - { - is_c_repr(&input, "#[derive(Object)]")?; - - let name = &input.ident; - let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident; - ... - } - -Use procedural macros with care. They are mostly useful for two purposes: - -* Performing consistency checks; for example ``#[derive(Object)]`` checks - that the structure has ``#[repr[C])`` and that the type of the first field - is consistent with the ``ObjectType`` declaration. - -* Extracting information from Rust source code into traits, typically based - on types and attributes. For example, ``#[derive(TryInto)]`` builds an - implementation of ``TryFrom``, and it uses the ``#[repr(...)]`` attribute - as the ``TryFrom`` source and error types. - -Procedural macros can be hard to debug and test; if the code generation -exceeds a few lines of code, it may be worthwhile to delegate work to -"regular" declarative (``macro_rules!``) macros and write unit tests for -those instead. - - -Coding style -'''''''''''' - -Code should pass clippy and be formatted with rustfmt. - -Right now, only the nightly version of ``rustfmt`` is supported. This -might change in the future. While CI checks for correct formatting via -``cargo fmt --check``, maintainers can fix this for you when applying patches. - -It is expected that QEMU Rust crates provides full ``rustdoc`` documentation for -bindings that are in their final shape or close. - -Adding dependencies -------------------- - -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 -dependencies itself. Sometimes QEMU only needs a small subset of the -functionality; see for example QEMU's ``assertions`` module. - -On top of this recommendation, adding external crates to QEMU is a -slightly complicated process, mostly due to the need to teach Meson how -to build them. While Meson has initial support for parsing ``Cargo.lock`` -files, it is still highly experimental and is therefore not used. - -Therefore, external crates must be added as subprojects for Meson to -learn how to build them, as well as to the relevant ``Cargo.toml`` files. -The versions specified in ``rust/Cargo.lock`` must be the same as the -subprojects; note that the ``rust/`` directory forms a Cargo `workspace`__, -and therefore there is a single lock file for the whole build. - -__ https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-workspace - -Choose a version of the crate that works with QEMU's minimum supported -Rust version (|msrv|). - -Second, a new ``wrap`` file must be added to teach Meson how to download the -crate. The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME`` -is the name of the crate and ``SEMVER`` is the version up to and including the -first non-zero number. For example, a crate with version ``0.2.3`` will use -``0.2`` for its ``SEMVER``, while a crate with version ``1.0.84`` will use ``1``. - -Third, the Meson rules to build the crate must be added at -``subprojects/NAME-SEMVER-rs/meson.build``. Generally this includes: - -* ``subproject`` and ``dependency`` lines for all dependent crates - -* a ``static_library`` or ``rust.proc_macro`` line to perform the actual build - -* ``declare_dependency`` and a ``meson.override_dependency`` lines to expose - the result to QEMU and to other subprojects - -Remember to add ``native: true`` to ``dependency``, ``static_library`` and -``meson.override_dependency`` for dependencies of procedural macros. -If a crate is needed in both procedural macros and QEMU binaries, everything -apart from ``subproject`` must be duplicated to build both native and -non-native versions of the crate. - -It's important to specify the right compiler options. These include: - -* the language edition (which can be found in the ``Cargo.toml`` file) - -* the ``--cfg`` (which have to be "reverse engineered" from the ``build.rs`` - file of the crate). - -* usually, a ``--cap-lints allow`` argument to hide warnings from rustc - or clippy. - -After every change to the ``meson.build`` file you have to update the patched -version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. This might -be automated in the future. - -Also, after every change to the ``meson.build`` file it is strongly suggested to -do a dummy change to the ``.wrap`` file (for example adding a comment like -``# version 2``), which will help Meson notice that the subproject is out of date. - -As a last step, add the new subproject to ``scripts/archive-source.sh``, -``scripts/make-release`` and ``subprojects/.gitignore``. diff --git a/docs/glossary.rst b/docs/glossary.rst index 4fa044bfb..0a403d5ba 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -221,13 +221,6 @@ Record/replay :ref:`Record/replay ` is a feature of QEMU allowing to have a deterministic and reproducible execution of a virtual machine. -Rust ----- - -`A new programming language `_, memory safe by -default. There is a work in progress to integrate it in QEMU codebase for -various subsystems. - System mode ----------- diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 020c0a84b..a8c1705e8 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -11,9 +11,7 @@ config PARALLEL config PL011 bool - # The PL011 has both a Rust and a C implementation - select PL011_C if !HAVE_RUST - select X_PL011_RUST if HAVE_RUST + select PL011_C config PL011_C bool diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index b3d823ce2..80df1ffb5 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -12,9 +12,7 @@ config A9_GTIMER config HPET bool default y if PC - # The HPET has both a Rust and a C implementation - select HPET_C if !HAVE_RUST - select X_HPET_RUST if HAVE_RUST + select HPET_C config HPET_C bool diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index 299ca9b18..4fcaf3d7d 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -52,11 +52,6 @@ struct PL011State { Clock *clk; bool migrate_clk; const unsigned char *id; - /* - * Since some users embed this struct directly, we must - * ensure that the C struct is at least as big as the Rust one. - */ - uint8_t padding_for_rust[16]; }; DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr); diff --git a/include/qemu/log.h b/include/qemu/log.h index aae72985f..60da703e6 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -84,8 +84,6 @@ typedef struct QEMULogItem { extern const QEMULogItem qemu_log_items[]; -ssize_t rust_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); - bool qemu_set_log(int log_flags, Error **errp); bool qemu_set_log_filename(const char *filename, Error **errp); bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp); diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 4e2436b19..bdbfba273 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -247,14 +247,6 @@ void event_notifier_set_handler(EventNotifier *e, GSource *iohandler_get_g_source(void); AioContext *iohandler_get_aio_context(void); -/** - * rust_bql_mock_lock: - * - * Called from Rust doctests to make bql_lock() return true. - * Do not touch. - */ -void rust_bql_mock_lock(void); - /** * bql_locked: Return lock status of the Big QEMU Lock (BQL) * @@ -273,11 +265,9 @@ bool bql_locked(void); /** * bql_block: Allow/deny releasing the BQL * - * The Big QEMU Lock (BQL) is used to provide interior mutability to - * Rust code, but this only works if other threads cannot run while - * the Rust code has an active borrow. This is because C code in - * other threads could come in and mutate data under the Rust code's - * feet. + * Some internal helpers temporarily block releasing the BQL while they + * manipulate shared state. This guard lets those helpers enforce that + * the lock stays held for the duration of the critical section. * * @increase: Whether to increase or decrease the blocking counter. * Releasing the BQL while the counter is nonzero triggers diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 1b38cb7e4..ff7bec8ce 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -93,9 +93,8 @@ QEMU_EXTERN_C int daemon(int, int); #endif /* - * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system - * calls since it doesn't use libc at all, so we have to emulate that despite - * FreeBSD 11 being EOL'd. + * We rely on the FreeBSD 11 "legacy" definitions in a few places, so request + * them explicitly. */ #ifdef __FreeBSD__ #define _WANT_FREEBSD11_STAT diff --git a/meson.build b/meson.build index 375518420..50de0c9ae 100644 --- a/meson.build +++ b/meson.build @@ -20,7 +20,6 @@ meson.add_postconf_script(find_program('scripts/symlink-install-tree.py')) not_found = dependency('', required: false) keyval = import('keyval') -rust = import('rust') ss = import('sourceset') fs = import('fs') @@ -88,65 +87,6 @@ if host_os == 'darwin' and \ objc = meson.get_compiler('objc') endif -have_rust = add_languages('rust', native: false, - required: get_option('rust').disable_auto_if(not have_system)) -have_rust = have_rust and add_languages('rust', native: true, - required: get_option('rust').disable_auto_if(not have_system)) -if have_rust - rustc = meson.get_compiler('rust') - if rustc.version().version_compare('<1.83.0') - if get_option('rust').enabled() - error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.83.0') - else - warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.') - message('Please upgrade to at least 1.83.0 to use Rust.') - have_rust = false - endif - endif -endif - -if have_rust - rustdoc = find_program('rustdoc', required: get_option('rust')) - bindgen = find_program('bindgen', required: get_option('rust')) - if not bindgen.found() or bindgen.version().version_compare('<0.60.0') - if get_option('rust').enabled() - error('bindgen version ' + bindgen.version() + ' is unsupported. You can install a new version with "cargo install bindgen-cli"') - else - if bindgen.found() - warning('bindgen version ' + bindgen.version() + ' is unsupported, disabling Rust compilation.') - else - warning('bindgen not found, disabling Rust compilation.') - endif - message('To use Rust you can install a new version with "cargo install bindgen-cli"') - have_rust = false - endif - endif -endif - -if have_rust - rustc_args = [find_program('scripts/rust/rustc_args.py'), - '--rustc-version', rustc.version(), - '--workspace', meson.project_source_root() / 'rust'] - if get_option('strict_rust_lints') - rustc_args += ['--strict-lints'] - endif - - rustfmt = find_program('rustfmt', required: false) - - rustc_lint_args = run_command(rustc_args, '--lints', - capture: true, check: true).stdout().strip().splitlines() - - # Apart from procedural macros, our Rust executables will often link - # with C code, so include all the libraries that C code needs. This - # is safe; https://github.com/rust-lang/rust/pull/54675 says that - # passing -nodefaultlibs to the linker "was more ideological to - # start with than anything". - add_project_arguments(rustc_lint_args + - ['--cfg', 'MESON', '-C', 'default-linker-libraries'], - native: false, language: 'rust') - add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], - native: true, language: 'rust') -endif dtrace = not_found stap = not_found @@ -2269,8 +2209,6 @@ endif config_host_data = configuration_data() -config_host_data.set('CONFIG_HAVE_RUST', have_rust) - have_host_block_device = (host_os != 'darwin' or cc.has_header('IOKit/storage/IOMedia.h')) @@ -3231,8 +3169,7 @@ host_kconfig = \ (host_os == 'linux' ? ['CONFIG_LINUX=y'] : []) + \ (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) + \ (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + \ - (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=y'] : []) + \ - (have_rust ? ['CONFIG_HAVE_RUST=y'] : []) + (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=y'] : []) ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ] @@ -3643,7 +3580,6 @@ qom_ss = ss.source_set() system_ss = ss.source_set() specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() -rust_devices_ss = ss.source_set() stub_ss = ss.source_set() trace_ss = ss.source_set() user_ss = ss.source_set() @@ -4111,47 +4047,8 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak endif endforeach -if have_rust - bindings_incdir = include_directories('.', 'include') - # We would like to use --generate-cstr, but it is only available - # starting with bindgen 0.66.0. The oldest supported versions - # is 0.60.x (Debian 12 has 0.60.1) which introduces --allowlist-file. - bindgen_args_common = [ - '--disable-header-comment', - '--raw-line', '// @generated', - '--ctypes-prefix', 'std::os::raw', - '--generate-block', - '--impl-debug', - '--no-doc-comments', - '--with-derive-default', - '--no-layout-tests', - '--no-prepend-enum-name', - '--allowlist-file', meson.project_source_root() + '/include/.*', - '--allowlist-file', meson.project_source_root() + '/.*', - '--allowlist-file', meson.project_build_root() + '/.*' - ] - if not rustfmt.found() - if bindgen.version().version_compare('<0.65.0') - bindgen_args_common += ['--no-rustfmt-bindings'] - else - bindgen_args_common += ['--formatter', 'none'] - endif - endif - if bindgen.version().version_compare('>=0.66.0') - bindgen_args_common += ['--rust-target', '1.59'] - endif - if bindgen.version().version_compare('<0.61.0') - # default in 0.61+ - bindgen_args_common += ['--size_t-is-usize'] - else - bindgen_args_common += ['--merge-extern-blocks'] - endif - subdir('rust') -endif - feature_to_c = find_program('scripts/feature_to_c.py') -rust_root_crate = find_program('scripts/rust/rust_root_crate.sh') if host_os == 'darwin' entitlement = find_program('scripts/entitlement.sh') @@ -4271,28 +4168,6 @@ foreach target : target_dirs arch_srcs += target_specific.sources() arch_deps += target_specific.dependencies() - if have_rust and target_type == 'system' - target_rust = rust_devices_ss.apply(config_target, strict: false) - crates = [] - foreach dep : target_rust.dependencies() - crates += dep.get_variable('crate') - endforeach - if crates.length() > 0 - rlib_rs = custom_target('rust_' + target.underscorify() + '.rs', - output: 'rust_' + target.underscorify() + '.rs', - command: [rust_root_crate, crates], - capture: true, - build_by_default: true, - build_always_stale: true) - rlib = static_library('rust_' + target.underscorify(), - structured_sources([], {'.': rlib_rs}), - dependencies: target_rust.dependencies(), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'c') - arch_deps += declare_dependency(link_whole: [rlib]) - endif - endif - # allow using headers from the dependencies but do not include the sources, # because this emulator only needs those in "objects". For external # dependencies, the full dependency is included below in the executable. @@ -4631,15 +4506,6 @@ if 'objc' in all_languages else summary_info += {'Objective-C compiler': false} endif -summary_info += {'Rust support': have_rust} -if have_rust - summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']} - summary_info += {'rustc': ' '.join(rustc.cmd_array())} - summary_info += {'rustc version': rustc.version()} - summary_info += {'rustdoc': rustdoc} - summary_info += {'bindgen': bindgen.full_path()} - summary_info += {'bindgen version': bindgen.version()} -endif option_cflags = (get_option('debug') ? ['-g'] : []) if get_option('optimization') != 'plain' option_cflags += ['-O' + get_option('optimization')] diff --git a/meson_options.txt b/meson_options.txt index b8c4e05f2..3f8c96461 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -361,8 +361,3 @@ option('hexagon_idef_parser', type : 'boolean', value : true, option('x86_version', type : 'combo', choices : ['0', '1', '2', '3', '4'], value: '1', description: 'tweak required x86_64 architecture version beyond compiler default') - -option('rust', type: 'feature', value: 'disabled', - description: 'Rust support') -option('strict_rust_lints', type: 'boolean', value: false, - description: 'Enable stricter set of Rust warnings') diff --git a/pythondeps.toml b/pythondeps.toml index 16fb2a989..1f17e764d 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -22,10 +22,6 @@ meson = { accepted = ">=1.5.0", installed = "1.9.0", canary = "meson" } pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } -[meson-rust] -# The install key should match the version in python/wheels/ -meson = { accepted = ">=1.9.0", installed = "1.9.0", canary = "meson" } - [docs] # Please keep the installed versions in sync with docs/requirements.txt sphinx = { accepted = ">=3.4.3", installed = "6.2.1", canary = "sphinx-build" } diff --git a/rust/.gitignore b/rust/.gitignore deleted file mode 100644 index 1bf71b1f6..000000000 --- a/rust/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Ignore any cargo development build artifacts; for qemu-wide builds, all build -# artifacts will go to the meson build directory. -target diff --git a/rust/Cargo.lock b/rust/Cargo.lock deleted file mode 100644 index eea928621..000000000 --- a/rust/Cargo.lock +++ /dev/null @@ -1,270 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - -[[package]] -name = "arbitrary-int" -version = "1.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" - -[[package]] -name = "bilge" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" -dependencies = [ - "arbitrary-int", - "bilge-impl", -] - -[[package]] -name = "bilge-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" -dependencies = [ - "itertools", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bits" -version = "0.1.0" -dependencies = [ - "qemu_macros", -] - -[[package]] -name = "bql" -version = "0.1.0" -dependencies = [ - "migration", -] - -[[package]] -name = "chardev" -version = "0.1.0" -dependencies = [ - "bql", - "common", - "migration", - "qom", - "util", -] - -[[package]] -name = "common" -version = "0.1.0" -dependencies = [ - "libc", - "qemu_macros", -] - -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - -[[package]] -name = "foreign" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846" -dependencies = [ - "libc", -] - -[[package]] -name = "hpet" -version = "0.1.0" -dependencies = [ - "bql", - "common", - "hwcore", - "migration", - "qom", - "system", - "util", -] - -[[package]] -name = "hwcore" -version = "0.1.0" -dependencies = [ - "bql", - "chardev", - "common", - "migration", - "qemu_macros", - "qom", - "system", - "util", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "libc" -version = "0.2.162" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" - -[[package]] -name = "migration" -version = "0.1.0" -dependencies = [ - "common", - "util", -] - -[[package]] -name = "pl011" -version = "0.1.0" -dependencies = [ - "bilge", - "bilge-impl", - "bits", - "bql", - "chardev", - "common", - "hwcore", - "migration", - "qom", - "system", - "util", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "qemu_macros" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "qom" -version = "0.1.0" -dependencies = [ - "bql", - "common", - "migration", - "qemu_macros", - "util", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system" -version = "0.1.0" -dependencies = [ - "common", - "qom", - "util", -] - -[[package]] -name = "tests" -version = "0.1.0" -dependencies = [ - "bql", - "chardev", - "common", - "hwcore", - "migration", - "qom", - "system", - "util", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "util" -version = "0.1.0" -dependencies = [ - "anyhow", - "common", - "foreign", - "libc", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/rust/Cargo.toml b/rust/Cargo.toml deleted file mode 100644 index d8183c614..000000000 --- a/rust/Cargo.toml +++ /dev/null @@ -1,112 +0,0 @@ -[workspace] -resolver = "2" -members = [ - "bits", - "bql", - "common", - "migration", - "qemu-macros", - "qom", - "system", - "hw/core", - "hw/char/pl011", - "hw/timer/hpet", - "util", - "tests", -] - -[workspace.package] -edition = "2021" -homepage = "https://www.qemu.org" -license = "GPL-2.0-or-later" -repository = "https://gitlab.com/qemu-project/qemu/" -# don't forget to update docs/devel/rust.rst msrv -rust-version = "1.83.0" -authors = ["The QEMU Project Developers "] - -[workspace.dependencies] -anyhow = "~1.0" -foreign = "~0.3.1" -libc = "0.2.162" - -[workspace.lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] } - -# Occasionally, we may need to silence warnings and clippy lints that -# were only introduced in newer Rust compiler versions. Do not croak -# in that case; a CI job with rust_strict_lints == true disables this -# and ensures that we do not have misspelled allow() attributes. -unknown_lints = "allow" - -# Prohibit code that is forbidden in Rust 2024 -unsafe_op_in_unsafe_fn = "deny" - -[workspace.lints.rustdoc] -private_intra_doc_links = "allow" - -broken_intra_doc_links = "deny" -invalid_html_tags = "deny" -invalid_rust_codeblocks = "deny" -bare_urls = "deny" -unescaped_backticks = "deny" -redundant_explicit_links = "deny" - -[workspace.lints.clippy] -# default-warn lints -result_unit_err = "allow" -should_implement_trait = "deny" -# can be for a reason, e.g. in callbacks -unused_self = "allow" -# common in device crates -upper_case_acronyms = "allow" - -# default-allow lints -as_ptr_cast_mut = "deny" -as_underscore = "deny" -assertions_on_result_states = "deny" -bool_to_int_with_if = "deny" -cast_lossless = "deny" -dbg_macro = "deny" -debug_assert_with_mut_call = "deny" -derive_partial_eq_without_eq = "deny" -doc_markdown = "deny" -empty_structs_with_brackets = "deny" -ignored_unit_patterns = "deny" -implicit_clone = "deny" -macro_use_imports = "deny" -missing_safety_doc = "deny" -mut_mut = "deny" -needless_bitwise_bool = "deny" -needless_pass_by_ref_mut = "deny" -needless_update = "deny" -no_effect_underscore_binding = "deny" -option_option = "deny" -or_fun_call = "deny" -ptr_as_ptr = "deny" -ptr_cast_constness = "deny" -pub_underscore_fields = "deny" -redundant_clone = "deny" -redundant_closure_for_method_calls = "deny" -redundant_else = "deny" -redundant_pub_crate = "deny" -ref_binding_to_reference = "deny" -ref_option_ref = "deny" -return_self_not_must_use = "deny" -same_name_method = "deny" -semicolon_inside_block = "deny" -shadow_unrelated = "deny" -significant_drop_in_scrutinee = "deny" -significant_drop_tightening = "deny" -suspicious_operation_groupings = "deny" -transmute_ptr_to_ptr = "deny" -transmute_undefined_repr = "deny" -type_repetition_in_bounds = "deny" -uninlined_format_args = "deny" -used_underscore_binding = "deny" - -# nice to have, but cannot be enabled yet -#wildcard_imports = "deny" # still have many bindings::* imports - -# these may have false positives -#option_if_let_else = "deny" -cognitive_complexity = "deny" diff --git a/rust/Kconfig b/rust/Kconfig deleted file mode 100644 index f9f5c3909..000000000 --- a/rust/Kconfig +++ /dev/null @@ -1 +0,0 @@ -source hw/Kconfig diff --git a/rust/bindings/src/lib.rs b/rust/bindings/src/lib.rs deleted file mode 100644 index 5bf03b137..000000000 --- a/rust/bindings/src/lib.rs +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc -)] - -//! `bindgen`-generated declarations. - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -// SAFETY: these are implemented in C; the bindings need to assert that the -// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. -// When bindings for character devices are introduced, this can be -// moved to the Opaque<> wrapper in src/chardev.rs. -unsafe impl Send for CharBackend {} -unsafe impl Sync for CharBackend {} - -// SAFETY: this is a pure data struct -unsafe impl Send for CoalescedMemoryRange {} -unsafe impl Sync for CoalescedMemoryRange {} - -// SAFETY: these are constants and vtables; the Send and Sync requirements -// are deferred to the unsafe callbacks that they contain -unsafe impl Send for MemoryRegionOps {} -unsafe impl Sync for MemoryRegionOps {} - -unsafe impl Send for Property {} -unsafe impl Sync for Property {} - -unsafe impl Send for TypeInfo {} -unsafe impl Sync for TypeInfo {} - -unsafe impl Send for VMStateDescription {} -unsafe impl Sync for VMStateDescription {} - -unsafe impl Send for VMStateField {} -unsafe impl Sync for VMStateField {} - -unsafe impl Send for VMStateInfo {} -unsafe impl Sync for VMStateInfo {} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for VMStateFlags { - fn default() -> Self { - Self(0) - } -} diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml deleted file mode 100644 index 7fce972b2..000000000 --- a/rust/bits/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "bits" -version = "0.1.0" -authors = ["Paolo Bonzini "] -description = "const-friendly bit flags" -resolver = "2" -publish = false - -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -qemu_macros = { path = "../qemu-macros" } - -[lints] -workspace = true diff --git a/rust/bits/meson.build b/rust/bits/meson.build deleted file mode 100644 index 359ca86f1..000000000 --- a/rust/bits/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -_bits_rs = static_library( - 'bits', - 'src/lib.rs', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [qemu_macros], -) - -bits_rs = declare_dependency(link_with: _bits_rs) - -rust.test('rust-bits-tests', _bits_rs, - suite: ['unit', 'rust']) - -rust.doctest('rust-bits-doctests', _bits_rs, - dependencies: bits_rs, - suite: ['doc', 'rust']) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs deleted file mode 100644 index d1141f7c8..000000000 --- a/rust/bits/src/lib.rs +++ /dev/null @@ -1,446 +0,0 @@ -// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later - -/// # Definition entry point -/// -/// Define a struct with a single field of type $type. Include public constants -/// for each element listed in braces. -/// -/// The unnamed element at the end, if present, can be used to enlarge the set -/// of valid bits. Bits that are valid but not listed are treated normally for -/// the purpose of arithmetic operations, and are printed with their hexadecimal -/// value. -/// -/// The struct implements the following traits: [`BitAnd`](std::ops::BitAnd), -/// [`BitOr`](std::ops::BitOr), [`BitXor`](std::ops::BitXor), -/// [`Not`](std::ops::Not), [`Sub`](std::ops::Sub); [`Debug`](std::fmt::Debug), -/// [`Display`](std::fmt::Display), [`Binary`](std::fmt::Binary), -/// [`Octal`](std::fmt::Octal), [`LowerHex`](std::fmt::LowerHex), -/// [`UpperHex`](std::fmt::UpperHex); [`From`]``/[`Into`]`` where -/// type is the type specified in the definition. -/// -/// ## Example -/// -/// ``` -/// # use bits::bits; -/// bits! { -/// pub struct Colors(u8) { -/// BLACK = 0, -/// RED = 1, -/// GREEN = 1 << 1, -/// BLUE = 1 << 2, -/// WHITE = (1 << 0) | (1 << 1) | (1 << 2), -/// } -/// } -/// ``` -/// -/// ``` -/// # use bits::bits; -/// # bits! { pub struct Colors(u8) { BLACK = 0, RED = 1, GREEN = 1 << 1, BLUE = 1 << 2, } } -/// -/// bits! { -/// pub struct Colors8(u8) { -/// BLACK = 0, -/// RED = 1, -/// GREEN = 1 << 1, -/// BLUE = 1 << 2, -/// WHITE = (1 << 0) | (1 << 1) | (1 << 2), -/// -/// _ = 255, -/// } -/// } -/// -/// // The previously defined struct ignores bits not explicitly defined. -/// assert_eq!( -/// Colors::from(255).into_bits(), -/// (Colors::RED | Colors::GREEN | Colors::BLUE).into_bits() -/// ); -/// -/// // Adding "_ = 255" makes it retain other bits as well. -/// assert_eq!(Colors8::from(255).into_bits(), 255); -/// -/// // all() does not include the additional bits, valid_bits() does -/// assert_eq!(Colors8::all().into_bits(), Colors::all().into_bits()); -/// assert_eq!(Colors8::valid_bits().into_bits(), 255); -/// ``` -/// -/// # Evaluation entry point -/// -/// Return a constant corresponding to the boolean expression `$expr`. -/// Identifiers in the expression correspond to values defined for the -/// type `$type`. Supported operators are `!` (unary), `-`, `&`, `^`, `|`. -/// -/// ## Examples -/// -/// ``` -/// # use bits::bits; -/// bits! { -/// pub struct Colors(u8) { -/// BLACK = 0, -/// RED = 1, -/// GREEN = 1 << 1, -/// BLUE = 1 << 2, -/// // same as "WHITE = 7", -/// WHITE = bits!(Self as u8: RED | GREEN | BLUE), -/// } -/// } -/// -/// let rgb = bits! { Colors: RED | GREEN | BLUE }; -/// assert_eq!(rgb, Colors::WHITE); -/// ``` -#[macro_export] -macro_rules! bits { - { - $(#[$struct_meta:meta])* - $struct_vis:vis struct $struct_name:ident($field_vis:vis $type:ty) { - $($(#[$const_meta:meta])* $const:ident = $val:expr),+ - $(,_ = $mask:expr)? - $(,)? - } - } => { - $(#[$struct_meta])* - #[derive(Clone, Copy, PartialEq, Eq)] - #[repr(transparent)] - $struct_vis struct $struct_name($field_vis $type); - - impl $struct_name { - $( #[allow(dead_code)] $(#[$const_meta])* - pub const $const: $struct_name = $struct_name($val); )+ - - #[doc(hidden)] - const VALID__: $type = $( Self::$const.0 )|+ $(|$mask)?; - - #[allow(dead_code)] - #[inline(always)] - pub const fn empty() -> Self { - Self(0) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn all() -> Self { - Self($( Self::$const.0 )|+) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn valid_bits() -> Self { - Self(Self::VALID__) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn valid(val: $type) -> bool { - (val & !Self::VALID__) == 0 - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn any_set(self, mask: Self) -> bool { - (self.0 & mask.0) != 0 - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn all_set(self, mask: Self) -> bool { - (self.0 & mask.0) == mask.0 - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn none_set(self, mask: Self) -> bool { - (self.0 & mask.0) == 0 - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn from_bits(value: $type) -> Self { - $struct_name(value) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn into_bits(self) -> $type { - self.0 - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn set(&mut self, rhs: Self) { - self.0 |= rhs.0; - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn clear(&mut self, rhs: Self) { - self.0 &= !rhs.0; - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn toggle(&mut self, rhs: Self) { - self.0 ^= rhs.0; - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn intersection(self, rhs: Self) -> Self { - $struct_name(self.0 & rhs.0) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn difference(self, rhs: Self) -> Self { - $struct_name(self.0 & !rhs.0) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn symmetric_difference(self, rhs: Self) -> Self { - $struct_name(self.0 ^ rhs.0) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn union(self, rhs: Self) -> Self { - $struct_name(self.0 | rhs.0) - } - - #[allow(dead_code)] - #[inline(always)] - pub const fn invert(self) -> Self { - $struct_name(self.0 ^ Self::VALID__) - } - } - - impl ::std::fmt::Binary for $struct_name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - // If no width, use the highest valid bit - let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize); - write!(f, "{:0>width$.precision$b}", self.0, - width = width, - precision = f.precision().unwrap_or(width)) - } - } - - impl ::std::fmt::LowerHex for $struct_name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - <$type as ::std::fmt::LowerHex>::fmt(&self.0, f) - } - } - - impl ::std::fmt::Octal for $struct_name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - <$type as ::std::fmt::Octal>::fmt(&self.0, f) - } - } - - impl ::std::fmt::UpperHex for $struct_name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - <$type as ::std::fmt::UpperHex>::fmt(&self.0, f) - } - } - - impl ::std::fmt::Debug for $struct_name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}({})", stringify!($struct_name), self) - } - } - - impl ::std::fmt::Display for $struct_name { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - use ::std::fmt::Display; - let mut first = true; - let mut left = self.0; - $(if Self::$const.0.is_power_of_two() && (self & Self::$const).0 != 0 { - if first { first = false } else { Display::fmt(&'|', f)?; } - Display::fmt(stringify!($const), f)?; - left -= Self::$const.0; - })+ - if first { - Display::fmt(&'0', f) - } else if left != 0 { - write!(f, "|{left:#x}") - } else { - Ok(()) - } - } - } - - impl ::std::cmp::PartialEq<$type> for $struct_name { - fn eq(&self, rhs: &$type) -> bool { - self.0 == *rhs - } - } - - impl ::std::ops::BitAnd<$struct_name> for &$struct_name { - type Output = $struct_name; - fn bitand(self, rhs: $struct_name) -> Self::Output { - $struct_name(self.0 & rhs.0) - } - } - - impl ::std::ops::BitAndAssign<$struct_name> for $struct_name { - fn bitand_assign(&mut self, rhs: $struct_name) { - self.0 = self.0 & rhs.0 - } - } - - impl ::std::ops::BitXor<$struct_name> for &$struct_name { - type Output = $struct_name; - fn bitxor(self, rhs: $struct_name) -> Self::Output { - $struct_name(self.0 ^ rhs.0) - } - } - - impl ::std::ops::BitXorAssign<$struct_name> for $struct_name { - fn bitxor_assign(&mut self, rhs: $struct_name) { - self.0 = self.0 ^ rhs.0 - } - } - - impl ::std::ops::BitOr<$struct_name> for &$struct_name { - type Output = $struct_name; - fn bitor(self, rhs: $struct_name) -> Self::Output { - $struct_name(self.0 | rhs.0) - } - } - - impl ::std::ops::BitOrAssign<$struct_name> for $struct_name { - fn bitor_assign(&mut self, rhs: $struct_name) { - self.0 = self.0 | rhs.0 - } - } - - impl ::std::ops::Sub<$struct_name> for &$struct_name { - type Output = $struct_name; - fn sub(self, rhs: $struct_name) -> Self::Output { - $struct_name(self.0 & !rhs.0) - } - } - - impl ::std::ops::SubAssign<$struct_name> for $struct_name { - fn sub_assign(&mut self, rhs: $struct_name) { - self.0 = self.0 - rhs.0 - } - } - - impl ::std::ops::Not for &$struct_name { - type Output = $struct_name; - fn not(self) -> Self::Output { - $struct_name(self.0 ^ $struct_name::VALID__) - } - } - - impl ::std::ops::BitAnd<$struct_name> for $struct_name { - type Output = Self; - fn bitand(self, rhs: Self) -> Self::Output { - $struct_name(self.0 & rhs.0) - } - } - - impl ::std::ops::BitXor<$struct_name> for $struct_name { - type Output = Self; - fn bitxor(self, rhs: Self) -> Self::Output { - $struct_name(self.0 ^ rhs.0) - } - } - - impl ::std::ops::BitOr<$struct_name> for $struct_name { - type Output = Self; - fn bitor(self, rhs: Self) -> Self::Output { - $struct_name(self.0 | rhs.0) - } - } - - impl ::std::ops::Sub<$struct_name> for $struct_name { - type Output = Self; - fn sub(self, rhs: Self) -> Self::Output { - $struct_name(self.0 & !rhs.0) - } - } - - impl ::std::ops::Not for $struct_name { - type Output = Self; - fn not(self) -> Self::Output { - $struct_name(self.0 ^ Self::VALID__) - } - } - - impl From<$struct_name> for $type { - fn from(x: $struct_name) -> $type { - x.0 - } - } - - impl From<$type> for $struct_name { - fn from(x: $type) -> Self { - $struct_name(x & Self::VALID__) - } - } - }; - - { $type:ty: $expr:expr } => { - $crate::bits_const_internal! { $type @ ($expr) } - }; - - { $type:ty as $int_type:ty: $expr:expr } => { - ($crate::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type - }; -} - -#[doc(hidden)] -pub use qemu_macros::bits_const_internal; - -#[cfg(test)] -mod test { - bits! { - pub struct InterruptMask(u32) { - OE = 1 << 10, - BE = 1 << 9, - PE = 1 << 8, - FE = 1 << 7, - RT = 1 << 6, - TX = 1 << 5, - RX = 1 << 4, - DSR = 1 << 3, - DCD = 1 << 2, - CTS = 1 << 1, - RI = 1 << 0, - - E = bits!(Self as u32: OE | BE | PE | FE), - MS = bits!(Self as u32: RI | DSR | DCD | CTS), - } - } - - #[test] - pub fn test_not() { - assert_eq!( - !InterruptMask::from(InterruptMask::RT.0), - InterruptMask::E | InterruptMask::MS | InterruptMask::TX | InterruptMask::RX - ); - } - - #[test] - pub fn test_and() { - assert_eq!( - InterruptMask::from(0), - InterruptMask::MS & InterruptMask::OE - ) - } - - #[test] - pub fn test_or() { - assert_eq!( - InterruptMask::E, - InterruptMask::OE | InterruptMask::BE | InterruptMask::PE | InterruptMask::FE - ); - } - - #[test] - pub fn test_xor() { - assert_eq!( - InterruptMask::E ^ InterruptMask::BE, - InterruptMask::OE | InterruptMask::PE | InterruptMask::FE - ); - } -} diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml deleted file mode 100644 index 1041bd4ea..000000000 --- a/rust/bql/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "bql" -version = "0.1.0" -description = "Rust bindings for QEMU/BQL" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -migration = { path = "../migration" } - -[features] -default = ["debug_cell"] -debug_cell = [] - -[lints] -workspace = true diff --git a/rust/bql/build.rs b/rust/bql/build.rs deleted file mode 120000 index 71a316788..000000000 --- a/rust/bql/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/bql/meson.build b/rust/bql/meson.build deleted file mode 100644 index f369209df..000000000 --- a/rust/bql/meson.build +++ /dev/null @@ -1,52 +0,0 @@ -_bql_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), - capture: true, check: true).stdout().strip().splitlines() - -if get_option('debug_mutex') - _bql_cfg += ['--cfg', 'feature="debug_cell"'] -endif - -# -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_bql_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common, -) - -_bql_rs = static_library( - 'bql', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/cell.rs', - ], - {'.': _bql_bindings_inc_rs} - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: _bql_cfg, - link_with: [_migration_rs], -) - -bql_rs = declare_dependency(link_with: [_bql_rs], - dependencies: [qemuutil]) - -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-bql-rs-doctests', - _bql_rs, - protocol: 'rust', - dependencies: bql_rs, - suite: ['doc', 'rust']) diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs deleted file mode 100644 index 9ffff12cd..000000000 --- a/rust/bql/src/bindings.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs deleted file mode 100644 index 24ab294b6..000000000 --- a/rust/bql/src/cell.rs +++ /dev/null @@ -1,849 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// This file is based on library/core/src/cell.rs from -// Rust 1.82.0. -// -// 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. - -//! QEMU-specific mutable containers -//! -//! Rust memory safety is based on this rule: Given an object `T`, it is only -//! possible to have one of the following: -//! -//! - Having several immutable references (`&T`) to the object (also known as -//! **aliasing**). -//! - Having one mutable reference (`&mut T`) to the object (also known as -//! **mutability**). -//! -//! This is enforced by the Rust compiler. However, there are situations where -//! this rule is not flexible enough. Sometimes it is required to have multiple -//! references to an object and yet mutate it. In particular, QEMU objects -//! usually have their pointer shared with the "outside world very early in -//! their lifetime", for example when they create their -//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual -//! parts of a device must be made mutable in a controlled manner; this module -//! provides the tools to do so. -//! -//! ## Cell types -//! -//! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. -//! While they are essentially the same single-threaded primitives that are -//! available in `std::cell`, the BQL allows them to be used from a -//! multi-threaded context and to share references across threads, while -//! maintaining Rust's safety guarantees. For this reason, unlike -//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the -//! `Sync` trait. -//! -//! BQL checks are performed in debug builds but can be optimized away in -//! release builds, providing runtime safety during development with no overhead -//! in production. -//! -//! The two provide different ways of handling interior mutability. -//! `BqlRefCell` is best suited for data that is primarily accessed by the -//! device's own methods, where multiple reads and writes can be grouped within -//! a single borrow and a mutable reference can be passed around. Instead, -//! [`BqlCell`] is a better choice when sharing small pieces of data with -//! external code (especially C code), because it provides simple get/set -//! operations that can be used one at a time. -//! -//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell` -//! counterparts, they are not interchangeable. Using `std::cell` types in -//! QEMU device implementations is usually incorrect and can lead to -//! thread-safety issues. -//! -//! ### Example -//! -//! ```ignore -//! # use bql::BqlRefCell; -//! # use qom::{Owned, ParentField}; -//! # use system::{InterruptSource, IRQState, SysBusDevice}; -//! # const N_GPIOS: usize = 8; -//! # struct PL061Registers { /* ... */ } -//! # unsafe impl ObjectType for PL061State { -//! # type Class = ::Class; -//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061"; -//! # } -//! struct PL061State { -//! parent_obj: ParentField, -//! -//! // Configuration is read-only after initialization -//! pullups: u32, -//! pulldowns: u32, -//! -//! // Single values shared with C code use BqlCell, in this case via InterruptSource -//! out: [InterruptSource; N_GPIOS], -//! interrupt: InterruptSource, -//! -//! // Larger state accessed by device methods uses BqlRefCell or Mutex -//! registers: BqlRefCell, -//! } -//! ``` -//! -//! ### `BqlCell` -//! -//! [`BqlCell`] implements interior mutability by moving values in and out of -//! the cell. That is, an `&mut T` to the inner value can never be obtained as -//! long as the cell is shared. The value itself cannot be directly obtained -//! without copying it, cloning it, or replacing it with something else. This -//! type provides the following methods, all of which can be called only while -//! the BQL is held: -//! -//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method -//! retrieves the current interior value by duplicating it. -//! - For types that implement [`Default`], the [`take`](BqlCell::take) method -//! replaces the current interior value with [`Default::default()`] and -//! returns the replaced value. -//! - All types have: -//! - [`replace`](BqlCell::replace): replaces the current interior value and -//! returns the replaced value. -//! - [`set`](BqlCell::set): this method replaces the interior value, -//! dropping the replaced value. -//! -//! ### `BqlRefCell` -//! -//! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a -//! process whereby one can claim temporary, exclusive, mutable access to the -//! inner value: -//! -//! ```ignore -//! fn clear_interrupts(&self, val: u32) { -//! // A mutable borrow gives read-write access to the registers -//! let mut regs = self.registers.borrow_mut(); -//! let old = regs.interrupt_status(); -//! regs.update_interrupt_status(old & !val); -//! } -//! ``` -//! -//! Borrows for `BqlRefCell`s are tracked at _runtime_, unlike Rust's native -//! reference types which are entirely tracked statically, at compile time. -//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), -//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The -//! thread will panic if these rules are violated or if the BQL is not held. -use std::{ - cell::{Cell, UnsafeCell}, - cmp::Ordering, - fmt, - marker::PhantomData, - mem, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; - -use migration::impl_vmstate_transparent; - -/// A mutable memory location that is protected by the Big QEMU Lock. -/// -/// # Memory layout -/// -/// `BqlCell` has the same in-memory representation as its inner type `T`. -#[repr(transparent)] -pub struct BqlCell { - value: UnsafeCell, -} - -// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex, -// except it is stored out-of-line -unsafe impl Send for BqlCell {} -unsafe impl Sync for BqlCell {} - -impl Clone for BqlCell { - #[inline] - fn clone(&self) -> BqlCell { - BqlCell::new(self.get()) - } -} - -impl Default for BqlCell { - /// Creates a `BqlCell`, with the `Default` value for T. - #[inline] - fn default() -> BqlCell { - BqlCell::new(Default::default()) - } -} - -impl PartialEq for BqlCell { - #[inline] - fn eq(&self, other: &BqlCell) -> bool { - self.get() == other.get() - } -} - -impl Eq for BqlCell {} - -impl PartialOrd for BqlCell { - #[inline] - fn partial_cmp(&self, other: &BqlCell) -> Option { - self.get().partial_cmp(&other.get()) - } -} - -impl Ord for BqlCell { - #[inline] - fn cmp(&self, other: &BqlCell) -> Ordering { - self.get().cmp(&other.get()) - } -} - -impl From for BqlCell { - /// Creates a new `BqlCell` containing the given value. - fn from(t: T) -> BqlCell { - BqlCell::new(t) - } -} - -impl fmt::Debug for BqlCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} - -impl fmt::Display for BqlCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} - -impl BqlCell { - /// Creates a new `BqlCell` containing the given value. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let c = BqlCell::new(5); - /// ``` - #[inline] - pub const fn new(value: T) -> BqlCell { - BqlCell { - value: UnsafeCell::new(value), - } - } - - /// Sets the contained value. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let c = BqlCell::new(5); - /// - /// c.set(10); - /// ``` - #[inline] - pub fn set(&self, val: T) { - self.replace(val); - } - - /// Replaces the contained value with `val`, and returns the old contained - /// value. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let cell = BqlCell::new(5); - /// assert_eq!(cell.get(), 5); - /// assert_eq!(cell.replace(10), 5); - /// assert_eq!(cell.get(), 10); - /// ``` - #[inline] - pub fn replace(&self, val: T) -> T { - assert!(crate::is_locked()); - // SAFETY: This can cause data races if called from multiple threads, - // but it won't happen as long as C code accesses the value - // under BQL protection only. - mem::replace(unsafe { &mut *self.value.get() }, val) - } - - /// Unwraps the value, consuming the cell. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let c = BqlCell::new(5); - /// let five = c.into_inner(); - /// - /// assert_eq!(five, 5); - /// ``` - pub fn into_inner(self) -> T { - assert!(crate::is_locked()); - self.value.into_inner() - } -} - -impl BqlCell { - /// Returns a copy of the contained value. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let c = BqlCell::new(5); - /// - /// let five = c.get(); - /// ``` - #[inline] - pub fn get(&self) -> T { - assert!(crate::is_locked()); - // SAFETY: This can cause data races if called from multiple threads, - // but it won't happen as long as C code accesses the value - // under BQL protection only. - unsafe { *self.value.get() } - } -} - -impl BqlCell { - /// Returns a raw pointer to the underlying data in this cell. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let c = BqlCell::new(5); - /// - /// let ptr = c.as_ptr(); - /// ``` - #[inline] - pub const fn as_ptr(&self) -> *mut T { - self.value.get() - } -} - -impl BqlCell { - /// Takes the value of the cell, leaving `Default::default()` in its place. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlCell; - /// # bql::start_test(); - /// - /// let c = BqlCell::new(5); - /// let five = c.take(); - /// - /// assert_eq!(five, 5); - /// assert_eq!(c.into_inner(), 0); - /// ``` - pub fn take(&self) -> T { - self.replace(Default::default()) - } -} - -impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); - -/// A mutable memory location with dynamically checked borrow rules, -/// protected by the Big QEMU Lock. -/// -/// See the [module-level documentation](self) for more. -/// -/// # Memory layout -/// -/// `BqlRefCell` starts with the same in-memory representation as its -/// inner type `T`. -#[repr(C)] -pub struct BqlRefCell { - // It is important that this is the first field (which is not the case - // for std::cell::BqlRefCell), so that we can use offset_of! on it. - // UnsafeCell and repr(C) both prevent usage of niches. - value: UnsafeCell, - borrow: Cell, - // Stores the location of the earliest currently active borrow. - // This gets updated whenever we go from having zero borrows - // to having a single borrow. When a borrow occurs, this gets included - // in the panic message - #[cfg(feature = "debug_cell")] - borrowed_at: Cell>>, -} - -// Positive values represent the number of `BqlRef` active. Negative values -// represent the number of `BqlRefMut` active. Right now QEMU's implementation -// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping -// components of a `BqlRefCell` (e.g., different ranges of a slice). -// -// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely -// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of -// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or -// underflow. However, this is not a guarantee, as a pathological program could -// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all -// code must explicitly check for overflow and underflow in order to avoid -// unsafety, or at least behave correctly in the event that overflow or -// underflow happens (e.g., see BorrowRef::new). -type BorrowFlag = isize; -const UNUSED: BorrowFlag = 0; - -#[inline(always)] -const fn is_writing(x: BorrowFlag) -> bool { - x < UNUSED -} - -#[inline(always)] -const fn is_reading(x: BorrowFlag) -> bool { - x > UNUSED -} - -impl BqlRefCell { - /// Creates a new `BqlRefCell` containing `value`. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlRefCell; - /// - /// let c = BqlRefCell::new(5); - /// ``` - #[inline] - pub const fn new(value: T) -> BqlRefCell { - BqlRefCell { - value: UnsafeCell::new(value), - borrow: Cell::new(UNUSED), - #[cfg(feature = "debug_cell")] - borrowed_at: Cell::new(None), - } - } -} - -// This ensures the panicking code is outlined from `borrow_mut` for -// `BqlRefCell`. -#[inline(never)] -#[cold] -#[cfg(feature = "debug_cell")] -fn panic_already_borrowed(source: &Cell>>) -> ! { - // If a borrow occurred, then we must already have an outstanding borrow, - // so `borrowed_at` will be `Some` - panic!("already borrowed at {:?}", source.take().unwrap()) -} - -#[inline(never)] -#[cold] -#[cfg(not(feature = "debug_cell"))] -fn panic_already_borrowed() -> ! { - panic!("already borrowed") -} - -impl BqlRefCell { - #[inline] - #[allow(clippy::unused_self)] - fn panic_already_borrowed(&self) -> ! { - #[cfg(feature = "debug_cell")] - { - panic_already_borrowed(&self.borrowed_at) - } - #[cfg(not(feature = "debug_cell"))] - { - panic_already_borrowed() - } - } - - /// Immutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `BqlRef` exits scope. Multiple - /// immutable borrows can be taken out at the same time. - /// - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlRefCell; - /// # bql::start_test(); - /// - /// let c = BqlRefCell::new(5); - /// - /// let borrowed_five = c.borrow(); - /// let borrowed_five2 = c.borrow(); - /// ``` - /// - /// An example of panic: - /// - /// ```should_panic - /// use bql::BqlRefCell; - /// # bql::start_test(); - /// - /// let c = BqlRefCell::new(5); - /// - /// let m = c.borrow_mut(); - /// let b = c.borrow(); // this causes a panic - /// ``` - #[inline] - #[track_caller] - pub fn borrow(&self) -> BqlRef<'_, T> { - if let Some(b) = BorrowRef::new(&self.borrow) { - // `borrowed_at` is always the *first* active borrow - if b.borrow.get() == 1 { - #[cfg(feature = "debug_cell")] - self.borrowed_at.set(Some(std::panic::Location::caller())); - } - - crate::block_unlock(true); - - // SAFETY: `BorrowRef` ensures that there is only immutable access - // to the value while borrowed. - let value = unsafe { NonNull::new_unchecked(self.value.get()) }; - BqlRef { value, borrow: b } - } else { - self.panic_already_borrowed() - } - } - - /// Mutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s - /// derived from it exit scope. The value cannot be borrowed while this - /// borrow is active. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlRefCell; - /// # bql::start_test(); - /// - /// let c = BqlRefCell::new("hello".to_owned()); - /// - /// *c.borrow_mut() = "bonjour".to_owned(); - /// - /// assert_eq!(&*c.borrow(), "bonjour"); - /// ``` - /// - /// An example of panic: - /// - /// ```should_panic - /// use bql::BqlRefCell; - /// # bql::start_test(); - /// - /// let c = BqlRefCell::new(5); - /// let m = c.borrow(); - /// - /// let b = c.borrow_mut(); // this causes a panic - /// ``` - #[inline] - #[track_caller] - pub fn borrow_mut(&self) -> BqlRefMut<'_, T> { - if let Some(b) = BorrowRefMut::new(&self.borrow) { - #[cfg(feature = "debug_cell")] - { - self.borrowed_at.set(Some(std::panic::Location::caller())); - } - - // SAFETY: this only adjusts a counter - crate::block_unlock(true); - - // SAFETY: `BorrowRefMut` guarantees unique access. - let value = unsafe { NonNull::new_unchecked(self.value.get()) }; - BqlRefMut { - value, - _borrow: b, - marker: PhantomData, - } - } else { - self.panic_already_borrowed() - } - } - - /// Returns a raw pointer to the underlying data in this cell. - /// - /// # Examples - /// - /// ``` - /// use bql::BqlRefCell; - /// - /// let c = BqlRefCell::new(5); - /// - /// let ptr = c.as_ptr(); - /// ``` - #[inline] - pub const fn as_ptr(&self) -> *mut T { - self.value.get() - } -} - -// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is -// stored out-of-line. Even though BqlRefCell includes Cells, they are -// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU -// Lock cannot be released while any borrows is active. -unsafe impl Send for BqlRefCell where T: Send {} -unsafe impl Sync for BqlRefCell {} - -impl Clone for BqlRefCell { - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. - #[inline] - #[track_caller] - fn clone(&self) -> BqlRefCell { - BqlRefCell::new(self.borrow().clone()) - } - - /// # Panics - /// - /// Panics if `source` is currently mutably borrowed. - #[inline] - #[track_caller] - fn clone_from(&mut self, source: &Self) { - self.value.get_mut().clone_from(&source.borrow()) - } -} - -impl Default for BqlRefCell { - /// Creates a `BqlRefCell`, with the `Default` value for T. - #[inline] - fn default() -> BqlRefCell { - BqlRefCell::new(Default::default()) - } -} - -impl PartialEq for BqlRefCell { - /// # Panics - /// - /// Panics if the value in either `BqlRefCell` is currently mutably - /// borrowed. - #[inline] - fn eq(&self, other: &BqlRefCell) -> bool { - *self.borrow() == *other.borrow() - } -} - -impl Eq for BqlRefCell {} - -impl PartialOrd for BqlRefCell { - /// # Panics - /// - /// Panics if the value in either `BqlRefCell` is currently mutably - /// borrowed. - #[inline] - fn partial_cmp(&self, other: &BqlRefCell) -> Option { - self.borrow().partial_cmp(&*other.borrow()) - } -} - -impl Ord for BqlRefCell { - /// # Panics - /// - /// Panics if the value in either `BqlRefCell` is currently mutably - /// borrowed. - #[inline] - fn cmp(&self, other: &BqlRefCell) -> Ordering { - self.borrow().cmp(&*other.borrow()) - } -} - -impl From for BqlRefCell { - /// Creates a new `BqlRefCell` containing the given value. - fn from(t: T) -> BqlRefCell { - BqlRefCell::new(t) - } -} - -impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); - -struct BorrowRef<'b> { - borrow: &'b Cell, -} - -impl<'b> BorrowRef<'b> { - #[inline] - fn new(borrow: &'b Cell) -> Option> { - let b = borrow.get().wrapping_add(1); - if !is_reading(b) { - // Incrementing borrow can result in a non-reading value (<= 0) in these cases: - // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read - // borrow due to Rust's reference aliasing rules - // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed - // into isize::MIN (the max amount of writing borrows) so we can't allow an - // additional read borrow because isize can't represent so many read borrows - // (this can only happen if you mem::forget more than a small constant amount - // of `BqlRef`s, which is not good practice) - None - } else { - // Incrementing borrow can result in a reading value (> 0) in these cases: - // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read - // borrow - // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is - // large enough to represent having one more read borrow - borrow.set(b); - Some(BorrowRef { borrow }) - } - } -} - -impl Drop for BorrowRef<'_> { - #[inline] - fn drop(&mut self) { - let borrow = self.borrow.get(); - debug_assert!(is_reading(borrow)); - self.borrow.set(borrow - 1); - crate::block_unlock(false) - } -} - -impl Clone for BorrowRef<'_> { - #[inline] - fn clone(&self) -> Self { - BorrowRef::new(self.borrow).unwrap() - } -} - -/// Wraps a borrowed reference to a value in a `BqlRefCell` box. -/// A wrapper type for an immutably borrowed value from a `BqlRefCell`. -/// -/// See the [module-level documentation](self) for more. -pub struct BqlRef<'b, T: 'b> { - // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a - // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops. - // `NonNull` is also covariant over `T`, just like we would have with `&T`. - value: NonNull, - borrow: BorrowRef<'b>, -} - -impl Deref for BqlRef<'_, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - // SAFETY: the value is accessible as long as we hold our borrow. - unsafe { self.value.as_ref() } - } -} - -impl<'b, T> BqlRef<'b, T> { - /// Copies a `BqlRef`. - /// - /// The `BqlRefCell` is already immutably borrowed, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `BqlRef::clone(...)`. A `Clone` implementation or a method would - /// interfere with the widespread use of `r.borrow().clone()` to clone - /// the contents of a `BqlRefCell`. - #[must_use] - #[inline] - #[allow(clippy::should_implement_trait)] - pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> { - BqlRef { - value: orig.value, - borrow: orig.borrow.clone(), - } - } -} - -impl fmt::Debug for BqlRef<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Display for BqlRef<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -struct BorrowRefMut<'b> { - borrow: &'b Cell, -} - -impl<'b> BorrowRefMut<'b> { - #[inline] - fn new(borrow: &'b Cell) -> Option> { - // There must currently be no existing references when borrow_mut() is - // called, so we explicitly only allow going from UNUSED to UNUSED - 1. - match borrow.get() { - UNUSED => { - borrow.set(UNUSED - 1); - Some(BorrowRefMut { borrow }) - } - _ => None, - } - } -} - -impl Drop for BorrowRefMut<'_> { - #[inline] - fn drop(&mut self) { - let borrow = self.borrow.get(); - debug_assert!(is_writing(borrow)); - self.borrow.set(borrow + 1); - crate::block_unlock(false) - } -} - -/// A wrapper type for a mutably borrowed value from a `BqlRefCell`. -/// -/// See the [module-level documentation](self) for more. -pub struct BqlRefMut<'b, T: 'b> { - // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a - // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. - value: NonNull, - _borrow: BorrowRefMut<'b>, - // `NonNull` is covariant over `T`, so we need to reintroduce invariance. - marker: PhantomData<&'b mut T>, -} - -impl Deref for BqlRefMut<'_, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - // SAFETY: the value is accessible as long as we hold our borrow. - unsafe { self.value.as_ref() } - } -} - -impl DerefMut for BqlRefMut<'_, T> { - #[inline] - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the value is accessible as long as we hold our borrow. - unsafe { self.value.as_mut() } - } -} - -impl fmt::Debug for BqlRefMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Display for BqlRefMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} diff --git a/rust/bql/src/lib.rs b/rust/bql/src/lib.rs deleted file mode 100644 index ef08221e9..000000000 --- a/rust/bql/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -mod bindings; -use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock}; - -mod cell; -pub use cell::*; - -/// An internal function that is used by doctests. -pub fn start_test() { - // SAFETY: integration tests are run with --test-threads=1, while - // unit tests and doctests are not multithreaded and do not have - // any BQL-protected data. Just set bql_locked to true. - unsafe { - rust_bql_mock_lock(); - } -} - -pub fn is_locked() -> bool { - // SAFETY: the function does nothing but return a thread-local bool - unsafe { bql_locked() } -} - -pub fn block_unlock(increase: bool) { - // SAFETY: this only adjusts a counter - unsafe { - bql_block_unlock(increase); - } -} diff --git a/rust/bql/wrapper.h b/rust/bql/wrapper.h deleted file mode 100644 index 2ef9a96e1..000000000 --- a/rust/bql/wrapper.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" - -#include "qemu/main-loop.h" diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml deleted file mode 100644 index 3e7797254..000000000 --- a/rust/chardev/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "chardev" -version = "0.1.0" -description = "Rust bindings for QEMU/chardev" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../common" } -bql = { path = "../bql" } -migration = { path = "../migration" } -qom = { path = "../qom" } -util = { path = "../util" } - -[lints] -workspace = true diff --git a/rust/chardev/build.rs b/rust/chardev/build.rs deleted file mode 120000 index 71a316788..000000000 --- a/rust/chardev/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build deleted file mode 100644 index 370895c11..000000000 --- a/rust/chardev/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -c_enums = [ - 'QEMUChrEvent', -] -_chardev_bindgen_args = [] -foreach enum : c_enums - _chardev_bindgen_args += ['--rustified-enum', enum] -endforeach - -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_chardev_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _chardev_bindgen_args, -) - -_chardev_rs = static_library( - 'chardev', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/chardev.rs', - ], - {'.': _chardev_bindings_inc_rs} - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_macros], -) - -chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs deleted file mode 100644 index 2d98026d6..000000000 --- a/rust/chardev/src/bindings.rs +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -use common::Zeroable; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -// SAFETY: these are implemented in C; the bindings need to assert that the -// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. -// When bindings for character devices are introduced, this can be -// moved to the Opaque<> wrapper in src/chardev.rs. -unsafe impl Send for CharBackend {} -unsafe impl Sync for CharBackend {} - -unsafe impl Zeroable for CharBackend {} diff --git a/rust/chardev/src/chardev.rs b/rust/chardev/src/chardev.rs deleted file mode 100644 index 201447967..000000000 --- a/rust/chardev/src/chardev.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for character devices -//! -//! Character devices in QEMU can run under the big QEMU lock or in a separate -//! `GMainContext`. Here we only support the former, because the bindings -//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are -//! called. - -use std::{ - ffi::{c_int, c_void, CStr}, - fmt::{self, Debug}, - io::{self, ErrorKind, Write}, - marker::PhantomPinned, - ptr::addr_of_mut, - slice, -}; - -use bql::{BqlRefCell, BqlRefMut}; -use common::{callbacks::FnCall, errno, Opaque}; -use qom::prelude::*; - -use crate::bindings; - -/// A safe wrapper around [`bindings::Chardev`]. -#[repr(transparent)] -#[derive(common::Wrapper)] -pub struct Chardev(Opaque); - -pub type ChardevClass = bindings::ChardevClass; -pub type Event = bindings::QEMUChrEvent; - -/// A safe wrapper around [`bindings::CharBackend`], denoting the character -/// back-end that is used for example by a device. Compared to the -/// underlying C struct it adds BQL protection, and is marked as pinned -/// because the QOM object ([`bindings::Chardev`]) contains a pointer to -/// the `CharBackend`. -pub struct CharBackend { - inner: BqlRefCell, - _pin: PhantomPinned, -} - -pub struct CharBackendMut<'a>(BqlRefMut<'a, bindings::CharBackend>); - -impl Write for CharBackendMut<'_> { - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, buf: &[u8]) -> io::Result { - let chr: &mut bindings::CharBackend = &mut self.0; - - let len = buf.len().try_into().unwrap(); - let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) }; - errno::into_io_result(r).map(|cnt| cnt as usize) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let chr: &mut bindings::CharBackend = &mut self.0; - - let len = buf.len().try_into().unwrap(); - let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) }; - errno::into_io_result(r).and_then(|cnt| { - if cnt as usize == buf.len() { - Ok(()) - } else { - Err(ErrorKind::WriteZero.into()) - } - }) - } -} - -impl Debug for CharBackend { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // SAFETY: accessed just to print the values - let chr = self.inner.as_ptr(); - Debug::fmt(unsafe { &*chr }, f) - } -} - -// FIXME: use something like PinnedDrop from the pinned_init crate -impl Drop for CharBackend { - fn drop(&mut self) { - self.disable_handlers(); - } -} - -impl CharBackend { - /// Enable the front-end's character device handlers, if there is an - /// associated `Chardev`. - pub fn enable_handlers< - 'chardev, - 'owner: 'chardev, - T, - CanReceiveFn: for<'a> FnCall<(&'a T,), u32>, - ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>, - EventFn: for<'a> FnCall<(&'a T, Event)>, - >( - // When "self" is dropped, the handlers are automatically disabled. - // However, this is not necessarily true if the owner is dropped. - // So require the owner to outlive the character device. - &'chardev self, - owner: &'owner T, - _can_receive: CanReceiveFn, - _receive: ReceiveFn, - _event: EventFn, - ) { - unsafe extern "C" fn rust_can_receive_cb FnCall<(&'a T,), u32>>( - opaque: *mut c_void, - ) -> c_int { - // SAFETY: the values are safe according to the contract of - // enable_handlers() and qemu_chr_fe_set_handlers() - let owner: &T = unsafe { &*(opaque.cast::()) }; - let r = F::call((owner,)); - r.try_into().unwrap() - } - - unsafe extern "C" fn rust_receive_cb FnCall<(&'a T, &'b [u8])>>( - opaque: *mut c_void, - buf: *const u8, - size: c_int, - ) { - // SAFETY: the values are safe according to the contract of - // enable_handlers() and qemu_chr_fe_set_handlers() - let owner: &T = unsafe { &*(opaque.cast::()) }; - let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) }; - F::call((owner, buf)) - } - - unsafe extern "C" fn rust_event_cb FnCall<(&'a T, Event)>>( - opaque: *mut c_void, - event: Event, - ) { - // SAFETY: the values are safe according to the contract of - // enable_handlers() and qemu_chr_fe_set_handlers() - let owner: &T = unsafe { &*(opaque.cast::()) }; - F::call((owner, event)) - } - - const { assert!(CanReceiveFn::IS_SOME) }; - let receive_cb: Option = - if ReceiveFn::is_some() { - Some(rust_receive_cb::) - } else { - None - }; - let event_cb: Option = if EventFn::is_some() { - Some(rust_event_cb::) - } else { - None - }; - - let mut chr = self.inner.borrow_mut(); - // SAFETY: the borrow promises that the BQL is taken - unsafe { - bindings::qemu_chr_fe_set_handlers( - addr_of_mut!(*chr), - Some(rust_can_receive_cb::), - receive_cb, - event_cb, - None, - (owner as *const T).cast_mut().cast::(), - core::ptr::null_mut(), - true, - ); - } - } - - /// Disable the front-end's character device handlers. - pub fn disable_handlers(&self) { - let mut chr = self.inner.borrow_mut(); - // SAFETY: the borrow promises that the BQL is taken - unsafe { - bindings::qemu_chr_fe_set_handlers( - addr_of_mut!(*chr), - None, - None, - None, - None, - core::ptr::null_mut(), - core::ptr::null_mut(), - true, - ); - } - } - - /// Notify that the frontend is ready to receive data. - pub fn accept_input(&self) { - let mut chr = self.inner.borrow_mut(); - // SAFETY: the borrow promises that the BQL is taken - unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) } - } - - /// Temporarily borrow the character device, allowing it to be used - /// as an implementor of `Write`. Note that it is not valid to drop - /// the big QEMU lock while the character device is borrowed, as - /// that might cause C code to write to the character device. - pub fn borrow_mut(&self) -> impl Write + '_ { - CharBackendMut(self.inner.borrow_mut()) - } - - /// Send a continuous stream of zero bits on the line if `enabled` is - /// true, or a short stream if `enabled` is false. - pub fn send_break(&self, long: bool) -> io::Result<()> { - let mut chr = self.inner.borrow_mut(); - let mut duration: c_int = long.into(); - // SAFETY: the borrow promises that the BQL is taken - let r = unsafe { - bindings::qemu_chr_fe_ioctl( - addr_of_mut!(*chr), - bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32, - addr_of_mut!(duration).cast::(), - ) - }; - - errno::into_io_result(r).map(|_| ()) - } - - /// Write data to a character backend from the front end. This function - /// will send data from the front end to the back end. Unlike - /// `write`, this function will block if the back end cannot - /// consume all of the data attempted to be written. - /// - /// Returns the number of bytes consumed (0 if no associated Chardev) or an - /// error. - pub fn write(&self, buf: &[u8]) -> io::Result { - let len = buf.len().try_into().unwrap(); - // SAFETY: qemu_chr_fe_write is thread-safe - let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) }; - errno::into_io_result(r).map(|cnt| cnt as usize) - } - - /// Write data to a character backend from the front end. This function - /// will send data from the front end to the back end. Unlike - /// `write`, this function will block if the back end cannot - /// consume all of the data attempted to be written. - /// - /// Returns the number of bytes consumed (0 if no associated Chardev) or an - /// error. - pub fn write_all(&self, buf: &[u8]) -> io::Result<()> { - let len = buf.len().try_into().unwrap(); - // SAFETY: qemu_chr_fe_write_all is thread-safe - let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) }; - errno::into_io_result(r).and_then(|cnt| { - if cnt as usize == buf.len() { - Ok(()) - } else { - Err(ErrorKind::WriteZero.into()) - } - }) - } -} - -unsafe impl ObjectType for Chardev { - type Class = ChardevClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) }; -} -qom_isa!(Chardev: Object); diff --git a/rust/chardev/src/lib.rs b/rust/chardev/src/lib.rs deleted file mode 100644 index 2e549f99d..000000000 --- a/rust/chardev/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub mod bindings; - -mod chardev; -pub use chardev::*; diff --git a/rust/chardev/wrapper.h b/rust/chardev/wrapper.h deleted file mode 100644 index 65ede6ea6..000000000 --- a/rust/chardev/wrapper.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" - -#include "chardev/char-fe.h" -#include "chardev/char-serial.h" diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml deleted file mode 100644 index 0e1b4fc50..000000000 --- a/rust/common/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "common" -version = "0.1.0" -description = "Rust common code for QEMU" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -libc.workspace = true -qemu_macros = { path = "../qemu-macros" } - -[lints] -workspace = true diff --git a/rust/common/meson.build b/rust/common/meson.build deleted file mode 100644 index b805e0faf..000000000 --- a/rust/common/meson.build +++ /dev/null @@ -1,34 +0,0 @@ -_common_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), - capture: true, check: true).stdout().strip().splitlines() - -_common_rs = static_library( - 'common', - structured_sources( - [ - 'src/lib.rs', - 'src/assertions.rs', - 'src/bitops.rs', - 'src/callbacks.rs', - 'src/errno.rs', - 'src/opaque.rs', - 'src/uninit.rs', - 'src/zeroable.rs', - ], - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: _common_cfg, - dependencies: [libc_rs, qemu_macros], -) - -common_rs = declare_dependency(link_with: [_common_rs]) - -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-common-doctests', - _common_rs, - protocol: 'rust', - dependencies: common_rs, - suite: ['doc', 'rust']) diff --git a/rust/common/src/assertions.rs b/rust/common/src/assertions.rs deleted file mode 100644 index 91f83a5d3..000000000 --- a/rust/common/src/assertions.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2024, Red Hat Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -#![doc(hidden)] -//! This module provides macros to check the equality of types and -//! the type of `struct` fields. This can be useful to ensure that -//! types match the expectations of C code. -//! -//! Documentation is hidden because it only exposes macros, which -//! are exported directly from `common`. - -// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 -// (stackoverflow answers are released under MIT license). - -#[doc(hidden)] -pub trait EqType { - type Itself; -} - -impl EqType for T { - type Itself = T; -} - -/// Assert that two types are the same. -/// -/// # Examples -/// -/// ``` -/// # use common::assert_same_type; -/// # use std::ops::Deref; -/// assert_same_type!(u32, u32); -/// assert_same_type!( as Deref>::Target, u32); -/// ``` -/// -/// Different types will cause a compile failure -/// -/// ```compile_fail -/// # use common::assert_same_type; -/// assert_same_type!(&Box, &u32); -/// ``` -#[macro_export] -macro_rules! assert_same_type { - ($t1:ty, $t2:ty) => { - const _: () = { - #[allow(unused)] - fn assert_same_type(v: $t1) { - fn types_must_be_equal(_: T) - where - T: $crate::assertions::EqType, - { - } - types_must_be_equal::<_, $t2>(v); - } - }; - }; -} - -/// Assert that a field of a struct has the given type. -/// -/// # Examples -/// -/// ``` -/// # use common::assert_field_type; -/// pub struct A { -/// field1: u32, -/// } -/// -/// assert_field_type!(A, field1, u32); -/// ``` -/// -/// Different types will cause a compile failure -/// -/// ```compile_fail -/// # use common::assert_field_type; -/// # pub struct A { field1: u32 } -/// assert_field_type!(A, field1, i32); -/// ``` -#[macro_export] -macro_rules! assert_field_type { - (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { - const _: () = { - #[allow(unused)] - const fn assert_field_type($param_name: &$t) { - const fn types_must_be_equal(_: &T) - where - T: $crate::assertions::EqType, - { - } - types_must_be_equal::<_, $ti>(&$($field)*); - } - }; - }; - - ($t:ty, $i:tt, $ti:ty) => { - $crate::assert_field_type!(@internal v, $ti, $t, v.$i); - }; -} - -/// Assert that an expression matches a pattern. This can also be -/// useful to compare enums that do not implement `Eq`. -/// -/// # Examples -/// -/// ``` -/// # use common::assert_match; -/// // JoinHandle does not implement `Eq`, therefore the result -/// // does not either. -/// let result: Result, u32> = Err(42); -/// assert_match!(result, Err(42)); -/// ``` -#[macro_export] -macro_rules! assert_match { - ($a:expr, $b:pat) => { - assert!( - match $a { - $b => true, - _ => false, - }, - "{} = {:?} does not match {}", - stringify!($a), - $a, - stringify!($b) - ); - }; -} - -/// Assert at compile time that an expression is true. This is similar -/// to `const { assert!(...); }` but it works outside functions, as well as -/// on versions of Rust before 1.79. -/// -/// # Examples -/// -/// ``` -/// # use common::static_assert; -/// static_assert!("abc".len() == 3); -/// ``` -/// -/// ```compile_fail -/// # use common::static_assert; -/// static_assert!("abc".len() == 2); // does not compile -/// ``` -#[macro_export] -macro_rules! static_assert { - ($x:expr) => { - const _: () = assert!($x); - }; -} diff --git a/rust/common/src/bitops.rs b/rust/common/src/bitops.rs deleted file mode 100644 index 06c78c3b8..000000000 --- a/rust/common/src/bitops.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -//! This module provides bit operation extensions to integer types. - -use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, - Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, -}; - -/// Trait for extensions to integer types -pub trait IntegerExt: - Add + AddAssign + - BitAnd + BitAndAssign + - BitOr + BitOrAssign + - BitXor + BitXorAssign + - Copy + - Div + DivAssign + - Eq + - Mul + MulAssign + - Not + Ord + PartialOrd + - Rem + RemAssign + - Shl + ShlAssign + - Shl + ShlAssign + // add more as needed - Shr + ShrAssign + - Shr + ShrAssign // add more as needed -{ - const BITS: u32; - const MAX: Self; - const MIN: Self; - const ONE: Self; - const ZERO: Self; - - #[inline] - #[must_use] - fn bit(start: u32) -> Self - { - debug_assert!(start < Self::BITS); - - Self::ONE << start - } - - #[inline] - #[must_use] - fn mask(start: u32, length: u32) -> Self - { - /* FIXME: Implement a more elegant check with error handling support? */ - debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); - - (Self::MAX >> (Self::BITS - length)) << start - } - - #[inline] - #[must_use] - fn deposit(self, start: u32, length: u32, - fieldval: U) -> Self - where Self: From - { - debug_assert!(length <= U::BITS); - - let mask = Self::mask(start, length); - (self & !mask) | ((Self::from(fieldval) << start) & mask) - } - - #[inline] - #[must_use] - fn extract(self, start: u32, length: u32) -> Self - { - let mask = Self::mask(start, length); - (self & mask) >> start - } -} - -macro_rules! impl_num_ext { - ($type:ty) => { - impl IntegerExt for $type { - const BITS: u32 = <$type>::BITS; - const MAX: Self = <$type>::MAX; - const MIN: Self = <$type>::MIN; - const ONE: Self = 1; - const ZERO: Self = 0; - } - }; -} - -impl_num_ext!(u8); -impl_num_ext!(u16); -impl_num_ext!(u32); -impl_num_ext!(u64); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_deposit() { - assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); - assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); - } - - #[test] - fn test_extract() { - assert_eq!(15u32.extract(2, 4), 3); - } - - #[test] - fn test_bit() { - assert_eq!(u8::bit(7), 128); - assert_eq!(u32::bit(16), 0x10000); - } - - #[test] - fn test_mask() { - assert_eq!(u8::mask(7, 1), 128); - assert_eq!(u32::mask(8, 8), 0xff00); - } -} diff --git a/rust/common/src/callbacks.rs b/rust/common/src/callbacks.rs deleted file mode 100644 index b8898fe96..000000000 --- a/rust/common/src/callbacks.rs +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: MIT - -//! Utility functions to deal with callbacks from C to Rust. - -use std::{mem, ptr::NonNull}; - -/// Trait for functions (types implementing [`Fn`]) that can be used as -/// callbacks. These include both zero-capture closures and function pointers. -/// -/// In Rust, calling a function through the `Fn` trait normally requires a -/// `self` parameter, even though for zero-sized functions (including function -/// pointers) the type itself contains all necessary information to call the -/// function. This trait provides a `call` function that doesn't require `self`, -/// allowing zero-sized functions to be called using only their type. -/// -/// This enables zero-sized functions to be passed entirely through generic -/// parameters and resolved at compile-time. A typical use is a function -/// receiving an unused parameter of generic type `F` and calling it via -/// `F::call` or passing it to another function via `func::`. -/// -/// QEMU uses this trick to create wrappers to C callbacks. The wrappers -/// are needed to convert an opaque `*mut c_void` into a Rust reference, -/// but they only have a single opaque that they can use. The `FnCall` -/// trait makes it possible to use that opaque for `self` or any other -/// reference: -/// -/// ```ignore -/// // The compiler creates a new `rust_bh_cb` wrapper for each function -/// // passed to `qemu_bh_schedule_oneshot` below. -/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( -/// opaque: *mut c_void, -/// ) { -/// // SAFETY: the opaque was passed as a reference to `T`. -/// F::call((unsafe { &*(opaque.cast::()) }, )) -/// } -/// -/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. -/// // Using a reference allows usage in const context. -/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { -/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; -/// unsafe { -/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) -/// } -/// } -/// ``` -/// -/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore -/// compiled to a separate function ("monomorphization"). If you wanted -/// to pass `self` as the opaque value, the generic parameters would be -/// `rust_bh_cb::`. -/// -/// `Args` is a tuple type whose types are the arguments of the function, -/// while `R` is the returned type. -/// -/// # Examples -/// -/// ``` -/// # use common::callbacks::FnCall; -/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { -/// F::call((s,)) -/// } -/// -/// let s: String = call_it(&str::to_owned, "hello world"); -/// assert_eq!(s, "hello world"); -/// ``` -/// -/// Note that the compiler will produce a different version of `call_it` for -/// each function that is passed to it. Therefore the argument is not really -/// used, except to decide what is `F` and what `F::call` does. -/// -/// Attempting to pass a non-zero-sized closure causes a compile-time failure: -/// -/// ```compile_fail -/// # use common::callbacks::FnCall; -/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { -/// # F::call((s,)) -/// # } -/// let x: &'static str = "goodbye world"; -/// call_it(&move |_| String::from(x), "hello workd"); -/// ``` -/// -/// `()` can be used to indicate "no function": -/// -/// ``` -/// # use common::callbacks::FnCall; -/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { -/// if F::IS_SOME { -/// Some(F::call((s,))) -/// } else { -/// None -/// } -/// } -/// -/// assert!(optional(&(), "hello world").is_none()); -/// ``` -/// -/// Invoking `F::call` will then be a run-time error. -/// -/// ```should_panic -/// # use common::callbacks::FnCall; -/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { -/// # F::call((s,)) -/// # } -/// let s: String = call_it(&(), "hello world"); // panics -/// ``` -/// -/// # Safety -/// -/// Because `Self` is a zero-sized type, all instances of the type are -/// equivalent. However, in addition to this, `Self` must have no invariants -/// that could be violated by creating a reference to it. -/// -/// This is always true for zero-capture closures and function pointers, as long -/// as the code is able to name the function in the first place. -pub unsafe trait FnCall: 'static + Sync + Sized { - /// `true` if `Self` is an actual function type and not `()`. - /// - /// # Examples - /// - /// You can use `IS_SOME` to catch this at compile time: - /// - /// ```compile_fail - /// # use common::callbacks::FnCall; - /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { - /// const { assert!(F::IS_SOME) } - /// F::call((s,)) - /// } - /// - /// let s: String = call_it((), "hello world"); // does not compile - /// ``` - const IS_SOME: bool; - - /// `false` if `Self` is an actual function type, `true` if it is `()`. - fn is_none() -> bool { - !Self::IS_SOME - } - - /// `true` if `Self` is an actual function type, `false` if it is `()`. - fn is_some() -> bool { - Self::IS_SOME - } - - /// Call the function with the arguments in args. - fn call(a: Args) -> R; -} - -/// `()` acts as a "null" callback. Using `()` and `function` is nicer -/// than `None` and `Some(function)`, because the compiler is unable to -/// infer the type of just `None`. Therefore, the trait itself acts as the -/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. -unsafe impl FnCall for () { - const IS_SOME: bool = false; - - /// Call the function with the arguments in args. - fn call(_a: Args) -> R { - panic!("callback not specified") - } -} - -macro_rules! impl_call { - ($($args:ident,)* ) => ( - // SAFETY: because each function is treated as a separate type, - // accessing `FnCall` is only possible in code that would be - // allowed to call the function. - unsafe impl FnCall<($($args,)*), R> for F - where - F: 'static + Sync + Sized + Fn($($args, )*) -> R, - { - const IS_SOME: bool = true; - - #[inline(always)] - fn call(a: ($($args,)*)) -> R { - const { assert!(mem::size_of::() == 0) }; - - // SAFETY: the safety of this method is the condition for implementing - // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, - // see https://github.com/rust-lang/libs-team/issues/292. - let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; - let ($($args,)*) = a; - f($($args,)*) - } - } - ) -} - -impl_call!(_1, _2, _3, _4, _5,); -impl_call!(_1, _2, _3, _4,); -impl_call!(_1, _2, _3,); -impl_call!(_1, _2,); -impl_call!(_1,); -impl_call!(); - -#[cfg(test)] -mod tests { - use super::*; - - // The `_f` parameter is unused but it helps the compiler infer `F`. - fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { - F::call(("hello world",)) - } - - #[test] - fn test_call() { - assert_eq!(do_test_call(&str::to_owned), "hello world") - } - - // The `_f` parameter is unused but it helps the compiler infer `F`. - fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { - assert!(F::is_some()); - } - - #[test] - fn test_is_some() { - do_test_is_some(&str::to_owned); - } -} diff --git a/rust/common/src/errno.rs b/rust/common/src/errno.rs deleted file mode 100644 index 64b2933b0..000000000 --- a/rust/common/src/errno.rs +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Utility functions to convert `errno` to and from -//! [`io::Error`]/[`io::Result`] -//! -//! QEMU C functions often have a "positive success/negative `errno`" calling -//! convention. This module provides functions to portably convert an integer -//! into an [`io::Result`] and back. - -use std::{ - convert::{self, TryFrom}, - io::{self, ErrorKind}, -}; - -/// An `errno` value that can be converted into an [`io::Error`] -pub struct Errno(pub u16); - -// On Unix, from_raw_os_error takes an errno value and OS errors -// are printed using strerror. On Windows however it takes a -// GetLastError() value; therefore we need to convert errno values -// into io::Error by hand. This is the same mapping that the -// standard library uses to retrieve the kind of OS errors -// (`std::sys::pal::unix::decode_error_kind`). -impl From for ErrorKind { - fn from(value: Errno) -> ErrorKind { - use ErrorKind::*; - let Errno(errno) = value; - match i32::from(errno) { - libc::EPERM | libc::EACCES => PermissionDenied, - libc::ENOENT => NotFound, - libc::EINTR => Interrupted, - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - libc::ENOMEM => OutOfMemory, - libc::EEXIST => AlreadyExists, - libc::EINVAL => InvalidInput, - libc::EPIPE => BrokenPipe, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::ENOTCONN => NotConnected, - libc::ENOTSUP => Unsupported, - libc::ETIMEDOUT => TimedOut, - _ => Other, - } - } -} - -// This is used on Windows for all io::Errors, but also on Unix if the -// io::Error does not have a raw OS error. This is the reversed -// mapping of the above; EIO is returned for unknown ErrorKinds. -impl From for Errno { - fn from(value: io::ErrorKind) -> Errno { - use ErrorKind::*; - let errno = match value { - // can be both EPERM or EACCES :( pick one - PermissionDenied => libc::EPERM, - NotFound => libc::ENOENT, - Interrupted => libc::EINTR, - WouldBlock => libc::EAGAIN, - OutOfMemory => libc::ENOMEM, - AlreadyExists => libc::EEXIST, - InvalidInput => libc::EINVAL, - BrokenPipe => libc::EPIPE, - AddrInUse => libc::EADDRINUSE, - AddrNotAvailable => libc::EADDRNOTAVAIL, - ConnectionAborted => libc::ECONNABORTED, - ConnectionRefused => libc::ECONNREFUSED, - ConnectionReset => libc::ECONNRESET, - NotConnected => libc::ENOTCONN, - Unsupported => libc::ENOTSUP, - TimedOut => libc::ETIMEDOUT, - _ => libc::EIO, - }; - Errno(errno as u16) - } -} - -impl From for io::Error { - #[cfg(unix)] - fn from(value: Errno) -> io::Error { - let Errno(errno) = value; - io::Error::from_raw_os_error(errno.into()) - } - - #[cfg(windows)] - fn from(value: Errno) -> io::Error { - let error_kind: ErrorKind = value.into(); - error_kind.into() - } -} - -impl From for Errno { - fn from(value: io::Error) -> Errno { - if cfg!(unix) { - if let Some(errno) = value.raw_os_error() { - return Errno(u16::try_from(errno).unwrap()); - } - } - value.kind().into() - } -} - -impl From for Errno { - fn from(_value: convert::Infallible) -> Errno { - panic!("unreachable") - } -} - -/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] -/// for the "right" set of types. -mod traits { - use super::Errno; - - /// A signed type that can be converted into an - /// [`io::Result`](std::io::Result) - pub trait GetErrno { - /// Unsigned variant of `Self`, used as the type for the `Ok` case. - type Out; - - /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative - fn into_errno_result(self) -> Result; - } - - /// A type that can be taken out of an [`io::Result`](std::io::Result) and - /// converted into "positive success/negative `errno`" convention. - pub trait MergeErrno { - /// Signed variant of `Self`, used as the return type of - /// [`into_neg_errno`](super::into_neg_errno). - type Out: From + std::ops::Neg; - - /// Return `self`, asserting that it is in range - fn map_ok(self) -> Self::Out; - } - - macro_rules! get_errno { - ($t:ty, $out:ty) => { - impl GetErrno for $t { - type Out = $out; - fn into_errno_result(self) -> Result { - match self { - 0.. => Ok(self as $out), - -65535..=-1 => Err(Errno(-self as u16)), - _ => panic!("{self} is not a negative errno"), - } - } - } - }; - } - - get_errno!(i32, u32); - get_errno!(i64, u64); - get_errno!(isize, usize); - - macro_rules! merge_errno { - ($t:ty, $out:ty) => { - impl MergeErrno for $t { - type Out = $out; - fn map_ok(self) -> Self::Out { - self.try_into().unwrap() - } - } - }; - } - - merge_errno!(u8, i32); - merge_errno!(u16, i32); - merge_errno!(u32, i32); - merge_errno!(u64, i64); - - impl MergeErrno for () { - type Out = i32; - fn map_ok(self) -> i32 { - 0 - } - } -} - -use traits::{GetErrno, MergeErrno}; - -/// Convert an integer value into a [`io::Result`]. -/// -/// Positive values are turned into an `Ok` result; negative values -/// are interpreted as negated `errno` and turned into an `Err`. -/// -/// ``` -/// # use common::errno::into_io_result; -/// # use std::io::ErrorKind; -/// let ok = into_io_result(1i32).unwrap(); -/// assert_eq!(ok, 1u32); -/// -/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM -/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); -/// ``` -/// -/// # Panics -/// -/// Since the result is an unsigned integer, negative values must -/// be close to 0; values that are too far away are considered -/// likely overflows and will panic: -/// -/// ```should_panic -/// # use common::errno::into_io_result; -/// # #[allow(dead_code)] -/// let err = into_io_result(-0x1234_5678i32); // panic -/// ``` -pub fn into_io_result(value: T) -> io::Result { - value.into_errno_result().map_err(Into::into) -} - -/// Convert a [`Result`] into an integer value, using negative `errno` -/// values to report errors. -/// -/// ``` -/// # use common::errno::into_neg_errno; -/// # use std::io::{self, ErrorKind}; -/// let ok: io::Result<()> = Ok(()); -/// assert_eq!(into_neg_errno(ok), 0); -/// -/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); -/// assert_eq!(into_neg_errno(err), -22); // -EINVAL -/// ``` -/// -/// Since this module also provides the ability to convert [`io::Error`] -/// to an `errno` value, [`io::Result`] is the most commonly used type -/// for the argument of this function: -/// -/// # Panics -/// -/// Since the result is a signed integer, integer `Ok` values must remain -/// positive: -/// -/// ```should_panic -/// # use common::errno::into_neg_errno; -/// # use std::io; -/// let err: io::Result = Ok(0x8899_AABB); -/// into_neg_errno(err) // panic -/// # ; -/// ``` -pub fn into_neg_errno>(value: Result) -> T::Out { - match value { - Ok(x) => x.map_ok(), - Err(err) => -T::Out::from(err.into().0), - } -} - -#[cfg(test)] -mod tests { - use std::io::ErrorKind; - - use super::*; - use crate::assert_match; - - #[test] - pub fn test_from_u8() { - let ok: io::Result<_> = Ok(42u8); - assert_eq!(into_neg_errno(ok), 42); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_from_u16() { - let ok: io::Result<_> = Ok(1234u16); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_i32() { - assert_match!(into_io_result(1234i32), Ok(1234)); - - let err = into_io_result(-1i32).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(1)); - assert_match!(err.kind(), ErrorKind::PermissionDenied); - } - - #[test] - pub fn test_from_u32() { - let ok: io::Result<_> = Ok(1234u32); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); - assert_eq!(into_neg_errno(err), -1); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); - assert_eq!(into_neg_errno(os_err), -10); - } - } - - #[test] - pub fn test_i64() { - assert_match!(into_io_result(1234i64), Ok(1234)); - - let err = into_io_result(-22i64).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(22)); - assert_match!(err.kind(), ErrorKind::InvalidInput); - } - - #[test] - pub fn test_from_u64() { - let ok: io::Result<_> = Ok(1234u64); - assert_eq!(into_neg_errno(ok), 1234); - - let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); - assert_eq!(into_neg_errno(err), -22); - - if cfg!(unix) { - let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); - assert_eq!(into_neg_errno(os_err), -6); - } - } - - #[test] - pub fn test_isize() { - assert_match!(into_io_result(1234isize), Ok(1234)); - - let err = into_io_result(-4isize).unwrap_err(); - #[cfg(unix)] - assert_match!(err.raw_os_error(), Some(4)); - assert_match!(err.kind(), ErrorKind::Interrupted); - } - - #[test] - pub fn test_from_unit() { - let ok: io::Result<_> = Ok(()); - assert_eq!(into_neg_errno(ok), 0); - - let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); - assert_eq!(into_neg_errno(err), -12); - - if cfg!(unix) { - let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); - assert_eq!(into_neg_errno(os_err), -2); - } - } -} diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs deleted file mode 100644 index 8311bf945..000000000 --- a/rust/common/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub use qemu_macros::{TryInto, Wrapper}; - -pub mod assertions; - -pub mod bitops; - -pub mod callbacks; -pub use callbacks::FnCall; - -pub mod errno; -pub use errno::Errno; - -pub mod opaque; -pub use opaque::{Opaque, Wrapper}; - -pub mod uninit; -pub use uninit::MaybeUninitField; - -pub mod zeroable; -pub use zeroable::Zeroable; diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs deleted file mode 100644 index c941fb454..000000000 --- a/rust/common/src/opaque.rs +++ /dev/null @@ -1,236 +0,0 @@ -// SPDX-License-Identifier: MIT - -//! ## Opaque wrappers -//! -//! The cell types from the previous section are useful at the boundaries -//! of code that requires interior mutability. When writing glue code that -//! interacts directly with C structs, however, it is useful to operate -//! at a lower level. -//! -//! C functions often violate Rust's fundamental assumptions about memory -//! safety by modifying memory even if it is shared. Furthermore, C structs -//! often start their life uninitialized and may be populated lazily. -//! -//! For this reason, this module provides the [`Opaque`] type to opt out -//! of Rust's usual guarantees about the wrapped type. Access to the wrapped -//! value is always through raw pointers, obtained via methods like -//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These -//! pointers can then be passed to C functions or dereferenced; both actions -//! require `unsafe` blocks, making it clear where safety guarantees must be -//! manually verified. For example -//! -//! ```ignore -//! unsafe { -//! let state = Opaque::::uninit(); -//! qemu_struct_init(state.as_mut_ptr()); -//! } -//! ``` -//! -//! [`Opaque`] will usually be wrapped one level further, so that -//! bridge methods can be added to the wrapper: -//! -//! ```ignore -//! pub struct MyStruct(Opaque); -//! -//! impl MyStruct { -//! fn new() -> Pin> { -//! let result = Box::pin(unsafe { Opaque::uninit() }); -//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; -//! result -//! } -//! } -//! ``` -//! -//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides -//! several advantages: -//! -//! * The choice of traits to be implemented is not limited by the -//! bindgen-generated code. For example, [`Drop`] can be added without -//! disabling [`Copy`] on the underlying bindgen type -//! -//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper -//! type rather than being automatically derived from the C struct's layout -//! -//! * Methods can be implemented in a separate crate from the bindgen-generated -//! bindings -//! -//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) -//! implementations can be customized to be more readable than the raw C -//! struct representation -//! -//! The [`Opaque`] type does not include BQL validation; it is possible to -//! assert in the code that the right lock is taken, to use it together -//! with a custom lock guard type, or to let C code take the lock, as -//! appropriate. It is also possible to use it with non-thread-safe -//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] -//! it is neither `Sync` nor `Send`. -//! -//! While [`Opaque`] is necessary for C interop, it should be used sparingly -//! and only at FFI boundaries. For QEMU-specific types that need interior -//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. -//! -//! [`BqlCell`]: ../../bql/cell/struct.BqlCell.html -//! [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html -use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull}; - -/// Stores an opaque value that is shared with C code. -/// -/// Often, C structs can changed when calling a C function even if they are -/// behind a shared Rust reference, or they can be initialized lazily and have -/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's -/// strict aliasing rules, which normally prevent mutation through shared -/// references. -/// -/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not -/// assume the usual constraints that Rust structs require, and allows using -/// shared references on the Rust side. -/// -/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout -/// of `T`. -#[repr(transparent)] -pub struct Opaque { - value: UnsafeCell>, - // PhantomPinned also allows multiple references to the `Opaque`, i.e. - // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; - // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. - _pin: PhantomPinned, -} - -impl Opaque { - /// Creates a new shared reference from a C pointer - /// - /// # Safety - /// - /// The pointer must be valid, though it need not point to a valid value. - pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { - let ptr = NonNull::new(ptr).unwrap().cast::(); - // SAFETY: Self is a transparent wrapper over T - unsafe { ptr.as_ref() } - } - - /// Creates a new opaque object with uninitialized contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be initialized and pinned before - /// calling them. - pub const unsafe fn uninit() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::uninit()), - _pin: PhantomPinned, - } - } - - /// Creates a new opaque object with zeroed contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned (and possibly initialized) - /// before calling them. - pub const unsafe fn zeroed() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::zeroed()), - _pin: PhantomPinned, - } - } - - /// Returns a raw mutable pointer to the opaque data. - pub const fn as_mut_ptr(&self) -> *mut T { - UnsafeCell::get(&self.value).cast() - } - - /// Returns a raw pointer to the opaque data. - pub const fn as_ptr(&self) -> *const T { - self.as_mut_ptr().cast_const() - } - - /// Returns a raw pointer to the opaque data that can be passed to a - /// C function as `void *`. - pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { - UnsafeCell::get(&self.value).cast() - } - - /// Converts a raw pointer to the wrapped type. - pub const fn raw_get(slot: *mut Self) -> *mut T { - // Compare with Linux's raw_get method, which goes through an UnsafeCell - // because it takes a *const Self instead. - slot.cast() - } -} - -impl fmt::Debug for Opaque { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut name: String = "Opaque<".to_string(); - name += std::any::type_name::(); - name += ">"; - f.debug_tuple(&name).field(&self.as_ptr()).finish() - } -} - -impl Opaque { - /// Creates a new opaque object with default contents. - /// - /// # Safety - /// - /// Ultimately the pointer to the returned value will be dereferenced - /// in another `unsafe` block, for example when passing it to a C function, - /// but the functions containing the dereference are usually safe. The - /// value returned from `uninit()` must be pinned before calling them. - pub unsafe fn new() -> Self { - Self { - value: UnsafeCell::new(MaybeUninit::new(T::default())), - _pin: PhantomPinned, - } - } -} - -/// Annotates [`Self`] as a transparent wrapper for another type. -/// -/// Usually defined via the [`crate::Wrapper`] derive macro. -/// -/// # Examples -/// -/// ``` -/// # use std::mem::ManuallyDrop; -/// # use common::opaque::Wrapper; -/// #[repr(transparent)] -/// pub struct Example { -/// inner: ManuallyDrop, -/// } -/// -/// unsafe impl Wrapper for Example { -/// type Wrapped = String; -/// } -/// ``` -/// -/// # Safety -/// -/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, -/// whether directly or indirectly. -/// -/// # Methods -/// -/// By convention, types that implement Wrapper also implement the following -/// methods: -/// -/// ```ignore -/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; -/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; -/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; -/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; -/// ``` -/// -/// They are not defined here to allow them to be `const`. -pub unsafe trait Wrapper { - type Wrapped; -} - -unsafe impl Wrapper for Opaque { - type Wrapped = T; -} diff --git a/rust/common/src/uninit.rs b/rust/common/src/uninit.rs deleted file mode 100644 index e7f9fcd2e..000000000 --- a/rust/common/src/uninit.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Access fields of a [`MaybeUninit`] - -use std::{ - mem::MaybeUninit, - ops::{Deref, DerefMut}, -}; - -pub struct MaybeUninitField<'a, T, U> { - parent: &'a mut MaybeUninit, - child: *mut U, -} - -impl<'a, T, U> MaybeUninitField<'a, T, U> { - #[doc(hidden)] - pub const fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { - MaybeUninitField { parent, child } - } - - /// Return a constant pointer to the containing object of the field. - /// - /// Because the `MaybeUninitField` remembers the containing object, - /// it is possible to use it in foreign APIs that initialize the - /// child. - pub const fn parent(f: &Self) -> *const T { - f.parent.as_ptr() - } - - /// Return a mutable pointer to the containing object. - /// - /// Because the `MaybeUninitField` remembers the containing object, - /// it is possible to use it in foreign APIs that initialize the - /// child. - pub const fn parent_mut(f: &mut Self) -> *mut T { - f.parent.as_mut_ptr() - } -} - -impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { - type Target = MaybeUninit; - - fn deref(&self) -> &MaybeUninit { - // SAFETY: self.child was obtained by dereferencing a valid mutable - // reference; the content of the memory may be invalid or uninitialized - // but MaybeUninit<_> makes no assumption on it - unsafe { &*(self.child.cast()) } - } -} - -impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { - fn deref_mut(&mut self) -> &mut MaybeUninit { - // SAFETY: self.child was obtained by dereferencing a valid mutable - // reference; the content of the memory may be invalid or uninitialized - // but MaybeUninit<_> makes no assumption on it - unsafe { &mut *(self.child.cast()) } - } -} - -/// ``` -/// #[derive(Debug)] -/// struct S { -/// x: u32, -/// y: u32, -/// } -/// -/// # use std::mem::MaybeUninit; -/// # use common::{assert_match, uninit_field_mut}; -/// -/// let mut s: MaybeUninit = MaybeUninit::zeroed(); -/// uninit_field_mut!(s, x).write(5); -/// let s = unsafe { s.assume_init() }; -/// assert_match!(s, S { x: 5, y: 0 }); -/// ``` -#[macro_export] -macro_rules! uninit_field_mut { - ($container:expr, $($field:tt)+) => {{ - let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; - let container_ptr__ = container__.as_mut_ptr(); - - // SAFETY: the container is not used directly, only through a MaybeUninit<>, - // so the safety is delegated to the caller and to final invocation of - // assume_init() - let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; - $crate::uninit::MaybeUninitField::new(container__, target__) - }}; -} diff --git a/rust/common/src/zeroable.rs b/rust/common/src/zeroable.rs deleted file mode 100644 index fd056deb1..000000000 --- a/rust/common/src/zeroable.rs +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Defines a trait for structs that can be safely initialized with zero bytes. - -/// Encapsulates the requirement that -/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined -/// behavior. -/// -/// # Safety -/// -/// Do not add this trait to a type unless all-zeroes is a valid value for the -/// type. In particular, raw pointers can be zero, but references and -/// `NonNull` cannot. -pub unsafe trait Zeroable: Default { - /// Return a value of Self whose memory representation consists of all - /// zeroes, with the possible exclusion of padding bytes. - const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; -} diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig deleted file mode 100644 index 36f92ec02..000000000 --- a/rust/hw/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -# devices Kconfig -source char/Kconfig -source timer/Kconfig diff --git a/rust/hw/char/Kconfig b/rust/hw/char/Kconfig deleted file mode 100644 index 5fe800c48..000000000 --- a/rust/hw/char/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -config X_PL011_RUST - bool diff --git a/rust/hw/char/meson.build b/rust/hw/char/meson.build deleted file mode 100644 index 5716dc43e..000000000 --- a/rust/hw/char/meson.build +++ /dev/null @@ -1 +0,0 @@ -subdir('pl011') diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml deleted file mode 100644 index b2418abc4..000000000 --- a/rust/hw/char/pl011/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "pl011" -version = "0.1.0" -authors = ["Manos Pitsidianakis "] -description = "pl011 device model for QEMU" -resolver = "2" -publish = false - -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -bilge = { version = "0.2.0" } -bilge-impl = { version = "0.2.0" } -bits = { path = "../../../bits" } -common = { path = "../../../common" } -util = { path = "../../../util" } -bql = { path = "../../../bql" } -migration = { path = "../../../migration" } -qom = { path = "../../../qom" } -chardev = { path = "../../../chardev" } -system = { path = "../../../system" } -hwcore = { path = "../../../hw/core" } - -[lints] -workspace = true diff --git a/rust/hw/char/pl011/build.rs b/rust/hw/char/pl011/build.rs deleted file mode 120000 index 5f5060db3..000000000 --- a/rust/hw/char/pl011/build.rs +++ /dev/null @@ -1 +0,0 @@ -../../../util/build.rs \ No newline at end of file diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build deleted file mode 100644 index ffdc8af53..000000000 --- a/rust/hw/char/pl011/meson.build +++ /dev/null @@ -1,48 +0,0 @@ -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_libpl011_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common, -) - -_libpl011_rs = static_library( - 'pl011', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/device.rs', - 'src/registers.rs', - ], - {'.' : _libpl011_bindings_inc_rs}, - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [ - bilge_rs, - bilge_impl_rs, - bits_rs, - common_rs, - util_rs, - migration_rs, - bql_rs, - qom_rs, - chardev_rs, - system_rs, - hwcore_rs, - ], -) - -rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( - link_whole: [_libpl011_rs], - variables: {'crate': 'pl011'}, -)]) diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs deleted file mode 100644 index bd5ea840c..000000000 --- a/rust/hw/char/pl011/src/bindings.rs +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -//! `bindgen`-generated declarations. - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs deleted file mode 100644 index 1b4587d5f..000000000 --- a/rust/hw/char/pl011/src/device.rs +++ /dev/null @@ -1,764 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ffi::CStr, mem::size_of}; - -use bql::BqlRefCell; -use chardev::{CharBackend, Chardev, Event}; -use common::{static_assert, uninit_field_mut}; -use hwcore::{ - Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource, - ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, -}; -use migration::{ - self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of, - vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder, -}; -use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; -use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}; -use util::{log::Log, log_mask_ln}; - -use crate::registers::{self, Interrupt, RegisterOffset}; - -// TODO: You must disable the UART before any of the control registers are -// reprogrammed. When the UART is disabled in the middle of transmission or -// reception, it completes the current character before stopping - -/// Integer Baud Rate Divider, `UARTIBRD` -const IBRD_MASK: u32 = 0xffff; - -/// Fractional Baud Rate Divider, `UARTFBRD` -const FBRD_MASK: u32 = 0x3f; - -/// QEMU sourced constant. -pub const PL011_FIFO_DEPTH: u32 = 16; - -#[derive(Clone, Copy)] -struct DeviceId(&'static [u8; 8]); - -impl std::ops::Index for DeviceId { - type Output = u8; - - fn index(&self, idx: hwaddr) -> &Self::Output { - &self.0[idx as usize] - } -} - -// FIFOs use 32-bit indices instead of usize, for compatibility with -// the migration stream produced by the C version of this device. -#[repr(transparent)] -#[derive(Debug, Default)] -pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); -impl_vmstate_forward!(Fifo); - -impl Fifo { - const fn len(&self) -> u32 { - self.0.len() as u32 - } -} - -impl std::ops::IndexMut for Fifo { - fn index_mut(&mut self, idx: u32) -> &mut Self::Output { - &mut self.0[idx as usize] - } -} - -impl std::ops::Index for Fifo { - type Output = registers::Data; - - fn index(&self, idx: u32) -> &Self::Output { - &self.0[idx as usize] - } -} - -#[repr(C)] -#[derive(Debug, Default)] -pub struct PL011Registers { - #[doc(alias = "fr")] - pub flags: registers::Flags, - #[doc(alias = "lcr")] - pub line_control: registers::LineControl, - #[doc(alias = "rsr")] - pub receive_status_error_clear: registers::ReceiveStatusErrorClear, - #[doc(alias = "cr")] - pub control: registers::Control, - pub dmacr: u32, - pub int_enabled: Interrupt, - pub int_level: Interrupt, - pub read_fifo: Fifo, - pub ilpr: u32, - pub ibrd: u32, - pub fbrd: u32, - pub ifl: u32, - pub read_pos: u32, - pub read_count: u32, - pub read_trigger: u32, -} - -#[repr(C)] -#[derive(qom::Object, hwcore::Device)] -/// PL011 Device Model in QEMU -pub struct PL011State { - pub parent_obj: ParentField, - pub iomem: MemoryRegion, - #[doc(alias = "chr")] - #[property(rename = "chardev")] - pub char_backend: CharBackend, - pub regs: BqlRefCell, - /// QEMU interrupts - /// - /// ```text - /// * sysbus MMIO region 0: device registers - /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line) - /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line) - /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line) - /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line) - /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line) - /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line) - /// ``` - #[doc(alias = "irq")] - pub interrupts: [InterruptSource; IRQMASK.len()], - #[doc(alias = "clk")] - pub clock: Owned, - #[doc(alias = "migrate_clk")] - #[property(rename = "migrate-clk", default = true)] - pub migrate_clock: bool, -} - -// Some C users of this device embed its state struct into their own -// structs, so the size of the Rust version must not be any larger -// than the size of the C one. If this assert triggers you need to -// expand the padding_for_rust[] array in the C PL011State struct. -static_assert!(size_of::() <= size_of::()); - -qom_isa!(PL011State : SysBusDevice, DeviceState, Object); - -#[repr(C)] -pub struct PL011Class { - parent_class: ::Class, - /// The byte string that identifies the device. - device_id: DeviceId, -} - -trait PL011Impl: SysBusDeviceImpl + IsA { - const DEVICE_ID: DeviceId; -} - -impl PL011Class { - fn class_init(&mut self) { - self.device_id = T::DEVICE_ID; - self.parent_class.class_init::(); - } -} - -unsafe impl ObjectType for PL011State { - type Class = PL011Class; - const TYPE_NAME: &'static CStr = crate::TYPE_PL011; -} - -impl PL011Impl for PL011State { - const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); -} - -impl ObjectImpl for PL011State { - type ParentType = SysBusDevice; - - const INSTANCE_INIT: Option)> = Some(Self::init); - const INSTANCE_POST_INIT: Option = Some(Self::post_init); - const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; -} - -impl DeviceImpl for PL011State { - const VMSTATE: Option> = Some(VMSTATE_PL011); - const REALIZE: Option util::Result<()>> = Some(Self::realize); -} - -impl ResettablePhasesImpl for PL011State { - const HOLD: Option = Some(Self::reset_hold); -} - -impl SysBusDeviceImpl for PL011State {} - -impl PL011Registers { - pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { - use RegisterOffset::*; - - let mut update = false; - let result = match offset { - DR => self.read_data_register(&mut update), - RSR => u32::from(self.receive_status_error_clear), - FR => u32::from(self.flags), - FBRD => self.fbrd, - ILPR => self.ilpr, - IBRD => self.ibrd, - LCR_H => u32::from(self.line_control), - CR => u32::from(self.control), - FLS => self.ifl, - IMSC => u32::from(self.int_enabled), - RIS => u32::from(self.int_level), - MIS => u32::from(self.int_level & self.int_enabled), - ICR => { - // "The UARTICR Register is the interrupt clear register and is write-only" - // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR - 0 - } - DMACR => self.dmacr, - }; - (update, result) - } - - pub(self) fn write( - &mut self, - offset: RegisterOffset, - value: u32, - char_backend: &CharBackend, - ) -> bool { - // eprintln!("write offset {offset} value {value}"); - use RegisterOffset::*; - match offset { - DR => return self.write_data_register(value), - RSR => { - self.receive_status_error_clear = 0.into(); - } - FR => { - // flag writes are ignored - } - ILPR => { - self.ilpr = value; - } - IBRD => { - self.ibrd = value; - } - FBRD => { - self.fbrd = value; - } - LCR_H => { - let new_val: registers::LineControl = value.into(); - // Reset the FIFO state on FIFO enable or disable - if self.line_control.fifos_enabled() != new_val.fifos_enabled() { - self.reset_rx_fifo(); - self.reset_tx_fifo(); - } - let update = (self.line_control.send_break() != new_val.send_break()) && { - let break_enable = new_val.send_break(); - let _ = char_backend.send_break(break_enable); - self.loopback_break(break_enable) - }; - self.line_control = new_val; - self.set_read_trigger(); - return update; - } - CR => { - // ??? Need to implement the enable bit. - self.control = value.into(); - return self.loopback_mdmctrl(); - } - FLS => { - self.ifl = value; - self.set_read_trigger(); - } - IMSC => { - self.int_enabled = Interrupt::from(value); - return true; - } - RIS => {} - MIS => {} - ICR => { - self.int_level &= !Interrupt::from(value); - return true; - } - DMACR => { - self.dmacr = value; - if value & 3 > 0 { - log_mask_ln!(Log::Unimp, "pl011: DMA not implemented"); - } - } - } - false - } - - fn read_data_register(&mut self, update: &mut bool) -> u32 { - self.flags.set_receive_fifo_full(false); - let c = self.read_fifo[self.read_pos]; - - if self.read_count > 0 { - self.read_count -= 1; - self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); - } - if self.read_count == 0 { - self.flags.set_receive_fifo_empty(true); - } - if self.read_count + 1 == self.read_trigger { - self.int_level &= !Interrupt::RX; - } - self.receive_status_error_clear.set_from_data(c); - *update = true; - u32::from(c) - } - - fn write_data_register(&mut self, value: u32) -> bool { - if !self.control.enable_uart() { - log_mask_ln!(Log::GuestError, "PL011 data written to disabled UART"); - } - if !self.control.enable_transmit() { - log_mask_ln!(Log::GuestError, "PL011 data written to disabled TX UART"); - } - // interrupts always checked - let _ = self.loopback_tx(value.into()); - self.int_level |= Interrupt::TX; - true - } - - #[inline] - #[must_use] - fn loopback_tx(&mut self, value: registers::Data) -> bool { - // Caveat: - // - // In real hardware, TX loopback happens at the serial-bit level - // and then reassembled by the RX logics back into bytes and placed - // into the RX fifo. That is, loopback happens after TX fifo. - // - // Because the real hardware TX fifo is time-drained at the frame - // rate governed by the configured serial format, some loopback - // bytes in TX fifo may still be able to get into the RX fifo - // that could be full at times while being drained at software - // pace. - // - // In such scenario, the RX draining pace is the major factor - // deciding which loopback bytes get into the RX fifo, unless - // hardware flow-control is enabled. - // - // For simplicity, the above described is not emulated. - self.loopback_enabled() && self.fifo_rx_put(value) - } - - #[must_use] - fn loopback_mdmctrl(&mut self) -> bool { - if !self.loopback_enabled() { - return false; - } - - /* - * Loopback software-driven modem control outputs to modem status inputs: - * FR.RI <= CR.Out2 - * FR.DCD <= CR.Out1 - * FR.CTS <= CR.RTS - * FR.DSR <= CR.DTR - * - * The loopback happens immediately even if this call is triggered - * by setting only CR.LBE. - * - * CTS/RTS updates due to enabled hardware flow controls are not - * dealt with here. - */ - - self.flags.set_ring_indicator(self.control.out_2()); - self.flags.set_data_carrier_detect(self.control.out_1()); - self.flags.set_clear_to_send(self.control.request_to_send()); - self.flags - .set_data_set_ready(self.control.data_transmit_ready()); - - // Change interrupts based on updated FR - let mut il = self.int_level; - - il &= !Interrupt::MS; - - if self.flags.data_set_ready() { - il |= Interrupt::DSR; - } - if self.flags.data_carrier_detect() { - il |= Interrupt::DCD; - } - if self.flags.clear_to_send() { - il |= Interrupt::CTS; - } - if self.flags.ring_indicator() { - il |= Interrupt::RI; - } - self.int_level = il; - true - } - - fn loopback_break(&mut self, enable: bool) -> bool { - enable && self.loopback_tx(registers::Data::BREAK) - } - - fn set_read_trigger(&mut self) { - self.read_trigger = 1; - } - - pub fn reset(&mut self) { - self.line_control.reset(); - self.receive_status_error_clear.reset(); - self.dmacr = 0; - self.int_enabled = 0.into(); - self.int_level = 0.into(); - self.ilpr = 0; - self.ibrd = 0; - self.fbrd = 0; - self.read_trigger = 1; - self.ifl = 0x12; - self.control.reset(); - self.flags.reset(); - self.reset_rx_fifo(); - self.reset_tx_fifo(); - } - - pub fn reset_rx_fifo(&mut self) { - self.read_count = 0; - self.read_pos = 0; - - // Reset FIFO flags - self.flags.set_receive_fifo_full(false); - self.flags.set_receive_fifo_empty(true); - } - - pub fn reset_tx_fifo(&mut self) { - // Reset FIFO flags - self.flags.set_transmit_fifo_full(false); - self.flags.set_transmit_fifo_empty(true); - } - - #[inline] - pub fn fifo_enabled(&self) -> bool { - self.line_control.fifos_enabled() == registers::Mode::FIFO - } - - #[inline] - pub fn loopback_enabled(&self) -> bool { - self.control.enable_loopback() - } - - #[inline] - pub fn fifo_depth(&self) -> u32 { - // Note: FIFO depth is expected to be power-of-2 - if self.fifo_enabled() { - return PL011_FIFO_DEPTH; - } - 1 - } - - #[must_use] - pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool { - let depth = self.fifo_depth(); - assert!(depth > 0); - let slot = (self.read_pos + self.read_count) & (depth - 1); - self.read_fifo[slot] = value; - self.read_count += 1; - self.flags.set_receive_fifo_empty(false); - if self.read_count == depth { - self.flags.set_receive_fifo_full(true); - } - - if self.read_count == self.read_trigger { - self.int_level |= Interrupt::RX; - return true; - } - false - } - - pub fn post_load(&mut self) -> Result<(), migration::InvalidError> { - /* Sanity-check input state */ - if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { - return Err(migration::InvalidError); - } - - if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 { - // Older versions of PL011 didn't ensure that the single - // character in the FIFO in FIFO-disabled mode is in - // element 0 of the array; convert to follow the current - // code's assumptions. - self.read_fifo[0] = self.read_fifo[self.read_pos]; - self.read_pos = 0; - } - - self.ibrd &= IBRD_MASK; - self.fbrd &= FBRD_MASK; - - Ok(()) - } -} - -impl PL011State { - /// Initializes a pre-allocated, uninitialized instance of `PL011State`. - /// - /// # Safety - /// - /// `self` must point to a correctly sized and aligned location for the - /// `PL011State` type. It must not be called more than once on the same - /// location/instance. All its fields are expected to hold uninitialized - /// values with the sole exception of `parent_obj`. - unsafe fn init(mut this: ParentInit) { - static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() - .read(&PL011State::read) - .write(&PL011State::write) - .native_endian() - .impl_sizes(4, 4) - .build(); - - // SAFETY: this and this.iomem are guaranteed to be valid at this point - MemoryRegion::init_io( - &mut uninit_field_mut!(*this, iomem), - &PL011_OPS, - "pl011", - 0x1000, - ); - - uninit_field_mut!(*this, regs).write(Default::default()); - - let clock = DeviceState::init_clock_in( - &mut this, - "clk", - &Self::clock_update, - ClockEvent::ClockUpdate, - ); - uninit_field_mut!(*this, clock).write(clock); - } - - const fn clock_update(&self, _event: ClockEvent) { - /* pl011_trace_baudrate_change(s); */ - } - - pub fn clock_needed(&self) -> bool { - self.migrate_clock - } - - fn post_init(&self) { - self.init_mmio(&self.iomem); - for irq in self.interrupts.iter() { - self.init_irq(irq); - } - } - - fn read(&self, offset: hwaddr, _size: u32) -> u64 { - match RegisterOffset::try_from(offset) { - Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - let device_id = self.get_class().device_id; - u64::from(device_id[(offset - 0xfe0) >> 2]) - } - Err(_) => { - log_mask_ln!(Log::GuestError, "PL011State::read: Bad offset {offset}"); - 0 - } - Ok(field) => { - let (update_irq, result) = self.regs.borrow_mut().read(field); - if update_irq { - self.update(); - self.char_backend.accept_input(); - } - result.into() - } - } - } - - fn write(&self, offset: hwaddr, value: u64, _size: u32) { - let mut update_irq = false; - if let Ok(field) = RegisterOffset::try_from(offset) { - // qemu_chr_fe_write_all() calls into the can_receive - // callback, so handle writes before entering PL011Registers. - if field == RegisterOffset::DR { - // ??? Check if transmitter is enabled. - let ch: [u8; 1] = [value as u8]; - // XXX this blocks entire thread. Rewrite to use - // qemu_chr_fe_write and background I/O callbacks - let _ = self.char_backend.write_all(&ch); - } - - update_irq = self - .regs - .borrow_mut() - .write(field, value as u32, &self.char_backend); - } else { - log_mask_ln!( - Log::GuestError, - "PL011State::write: Bad offset {offset} value {value}" - ); - } - if update_irq { - self.update(); - } - } - - fn can_receive(&self) -> u32 { - let regs = self.regs.borrow(); - // trace_pl011_can_receive(s->lcr, s->read_count, r); - regs.fifo_depth() - regs.read_count - } - - fn receive(&self, buf: &[u8]) { - let mut regs = self.regs.borrow_mut(); - if regs.loopback_enabled() { - // In loopback mode, the RX input signal is internally disconnected - // from the entire receiving logics; thus, all inputs are ignored, - // and BREAK detection on RX input signal is also not performed. - return; - } - - let mut update_irq = false; - for &c in buf { - let c: u32 = c.into(); - update_irq |= regs.fifo_rx_put(c.into()); - } - - // Release the BqlRefCell before calling self.update() - drop(regs); - if update_irq { - self.update(); - } - } - - fn event(&self, event: Event) { - let mut update_irq = false; - let mut regs = self.regs.borrow_mut(); - if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() { - update_irq = regs.fifo_rx_put(registers::Data::BREAK); - } - // Release the BqlRefCell before calling self.update() - drop(regs); - - if update_irq { - self.update() - } - } - - fn realize(&self) -> util::Result<()> { - self.char_backend - .enable_handlers(self, Self::can_receive, Self::receive, Self::event); - Ok(()) - } - - fn reset_hold(&self, _type: ResetType) { - self.regs.borrow_mut().reset(); - } - - fn update(&self) { - let regs = self.regs.borrow(); - let flags = regs.int_level & regs.int_enabled; - for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - irq.set(flags.any_set(i)); - } - } - - pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> { - self.regs.borrow_mut().post_load() - } -} - -/// Which bits in the interrupt status matter for each outbound IRQ line ? -const IRQMASK: [Interrupt; 6] = [ - Interrupt::all(), - Interrupt::RX, - Interrupt::TX, - Interrupt::RT, - Interrupt::MS, - Interrupt::E, -]; - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer for `chr` -/// and `irq`. -#[no_mangle] -pub unsafe extern "C" fn pl011_create( - addr: u64, - irq: *mut IRQState, - chr: *mut Chardev, -) -> *mut DeviceState { - // SAFETY: The callers promise that they have owned references. - // They do not gift them to pl011_create, so use `Owned::from`. - let irq = unsafe { Owned::::from(&*irq) }; - - let dev = PL011State::new(); - if !chr.is_null() { - let chr = unsafe { Owned::::from(&*chr) }; - dev.prop_set_chr("chardev", &chr); - } - dev.sysbus_realize(); - dev.mmio_map(0, addr); - dev.connect_irq(0, &irq); - - // The pointer is kept alive by the QOM tree; drop the owned ref - dev.as_mut_ptr() -} - -#[repr(C)] -#[derive(qom::Object, hwcore::Device)] -/// PL011 Luminary device model. -pub struct PL011Luminary { - parent_obj: ParentField, -} - -qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); - -unsafe impl ObjectType for PL011Luminary { - type Class = ::Class; - const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; -} - -impl ObjectImpl for PL011Luminary { - type ParentType = PL011State; - - const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; -} - -impl PL011Impl for PL011Luminary { - const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); -} - -impl DeviceImpl for PL011Luminary {} -impl ResettablePhasesImpl for PL011Luminary {} -impl SysBusDeviceImpl for PL011Luminary {} - -/// Migration subsection for [`PL011State`] clock. -static VMSTATE_PL011_CLOCK: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"pl011/clock") - .version_id(1) - .minimum_version_id(1) - .needed(&PL011State::clock_needed) - .fields(vmstate_fields! { - vmstate_of!(PL011State, clock), - }) - .build(); - -impl_vmstate_struct!( - PL011Registers, - VMStateDescriptionBuilder::::new() - .name(c"pl011/regs") - .version_id(2) - .minimum_version_id(2) - .fields(vmstate_fields! { - vmstate_of!(PL011Registers, flags), - vmstate_of!(PL011Registers, line_control), - vmstate_of!(PL011Registers, receive_status_error_clear), - vmstate_of!(PL011Registers, control), - vmstate_of!(PL011Registers, dmacr), - vmstate_of!(PL011Registers, int_enabled), - vmstate_of!(PL011Registers, int_level), - vmstate_of!(PL011Registers, read_fifo), - vmstate_of!(PL011Registers, ilpr), - vmstate_of!(PL011Registers, ibrd), - vmstate_of!(PL011Registers, fbrd), - vmstate_of!(PL011Registers, ifl), - vmstate_of!(PL011Registers, read_pos), - vmstate_of!(PL011Registers, read_count), - vmstate_of!(PL011Registers, read_trigger), - }) - .build() -); - -pub const VMSTATE_PL011: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"pl011") - .version_id(2) - .minimum_version_id(2) - .post_load(&PL011State::post_load) - .fields(vmstate_fields! { - vmstate_unused!(core::mem::size_of::()), - vmstate_of!(PL011State, regs), - }) - .subsections(vmstate_subsections! { - VMSTATE_PL011_CLOCK - }) - .build(); diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs deleted file mode 100644 index 0c19b708c..000000000 --- a/rust/hw/char/pl011/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! PL011 QEMU Device Model -//! -//! This library implements a device model for the PrimeCell® UART (PL011) -//! device in QEMU. -//! -//! # Library crate -//! -//! See [`PL011State`](crate::device::PL011State) for the device model type and -//! the [`registers`] module for register types. - -mod bindings; -mod device; -mod registers; - -pub use device::pl011_create; - -pub const TYPE_PL011: &::std::ffi::CStr = c"pl011"; -pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c"pl011_luminary"; diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs deleted file mode 100644 index 0c3a4d7d2..000000000 --- a/rust/hw/char/pl011/src/registers.rs +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Device registers exposed as typed structs which are backed by arbitrary -//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. - -// For more detail see the PL011 Technical Reference Manual DDI0183: -// https://developer.arm.com/documentation/ddi0183/latest/ - -use bilge::prelude::*; -use bits::bits; -use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; - -/// Offset of each register from the base memory address of the device. -#[doc(alias = "offset")] -#[allow(non_camel_case_types)] -#[repr(u64)] -#[derive(Debug, Eq, PartialEq, common::TryInto)] -pub enum RegisterOffset { - /// Data Register - /// - /// A write to this register initiates the actual data transmission - #[doc(alias = "UARTDR")] - DR = 0x000, - /// Receive Status Register or Error Clear Register - #[doc(alias = "UARTRSR")] - #[doc(alias = "UARTECR")] - RSR = 0x004, - /// Flag Register - /// - /// A read of this register shows if transmission is complete - #[doc(alias = "UARTFR")] - FR = 0x018, - /// Fractional Baud Rate Register - /// - /// responsible for baud rate speed - #[doc(alias = "UARTFBRD")] - FBRD = 0x028, - /// `IrDA` Low-Power Counter Register - #[doc(alias = "UARTILPR")] - ILPR = 0x020, - /// Integer Baud Rate Register - /// - /// Responsible for baud rate speed - #[doc(alias = "UARTIBRD")] - IBRD = 0x024, - /// line control register (data frame format) - #[doc(alias = "UARTLCR_H")] - LCR_H = 0x02C, - /// Toggle UART, transmission or reception - #[doc(alias = "UARTCR")] - CR = 0x030, - /// Interrupt FIFO Level Select Register - #[doc(alias = "UARTIFLS")] - FLS = 0x034, - /// Interrupt Mask Set/Clear Register - #[doc(alias = "UARTIMSC")] - IMSC = 0x038, - /// Raw Interrupt Status Register - #[doc(alias = "UARTRIS")] - RIS = 0x03C, - /// Masked Interrupt Status Register - #[doc(alias = "UARTMIS")] - MIS = 0x040, - /// Interrupt Clear Register - #[doc(alias = "UARTICR")] - ICR = 0x044, - /// DMA control Register - #[doc(alias = "UARTDMACR")] - DMACR = 0x048, - ///// Reserved, offsets `0x04C` to `0x07C`. - //Reserved = 0x04C, -} - -/// Receive Status Register / Data Register common error bits -/// -/// The `UARTRSR` register is updated only when a read occurs -/// from the `UARTDR` register with the same status information -/// that can also be obtained by reading the `UARTDR` register -#[bitsize(8)] -#[derive(Clone, Copy, Default, DebugBits, FromBits)] -pub struct Errors { - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, - _reserved_unpredictable: u4, -} - -/// Data Register, `UARTDR` -/// -/// The `UARTDR` register is the data register; write for TX and -/// read for RX. It is a 12-bit register, where bits 7..0 are the -/// character and bits 11..8 are error bits. -#[bitsize(32)] -#[derive(Clone, Copy, Default, DebugBits, FromBits)] -#[doc(alias = "UARTDR")] -pub struct Data { - pub data: u8, - pub errors: Errors, - _reserved: u16, -} -impl_vmstate_bitsized!(Data); - -impl Data { - // bilge is not very const-friendly, unfortunately - pub const BREAK: Self = Self { value: 1 << 10 }; -} - -/// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` -/// -/// This register provides a different way to read the four receive -/// status error bits that can be found in bits 11..8 of the UARTDR -/// on a read. It gets updated when the guest reads UARTDR, and the -/// status bits correspond to that character that was just read. -/// -/// The TRM confusingly describes this offset as UARTRSR for reads -/// and UARTECR for writes, but really it's a single error status -/// register where writing anything to the register clears the error -/// bits. -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] -pub struct ReceiveStatusErrorClear { - pub errors: Errors, - _reserved_unpredictable: u24, -} -impl_vmstate_bitsized!(ReceiveStatusErrorClear); - -impl ReceiveStatusErrorClear { - pub fn set_from_data(&mut self, data: Data) { - self.set_errors(data.errors()); - } - - pub fn reset(&mut self) { - // All the bits are cleared to 0 on reset. - *self = Self::default(); - } -} - -impl Default for ReceiveStatusErrorClear { - fn default() -> Self { - 0.into() - } -} - -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] -/// Flag Register, `UARTFR` -/// -/// This has the usual inbound RS232 modem-control signals, plus flags -/// for RX and TX FIFO fill levels and a BUSY flag. -#[doc(alias = "UARTFR")] -pub struct Flags { - /// CTS: Clear to send - pub clear_to_send: bool, - /// DSR: Data set ready - pub data_set_ready: bool, - /// DCD: Data carrier detect - pub data_carrier_detect: bool, - /// BUSY: UART busy. In real hardware, set while the UART is - /// busy transmitting data. QEMU's implementation never sets BUSY. - pub busy: bool, - /// RXFE: Receive FIFO empty - pub receive_fifo_empty: bool, - /// TXFF: Transmit FIFO full - pub transmit_fifo_full: bool, - /// RXFF: Receive FIFO full - pub receive_fifo_full: bool, - /// TXFE: Transmit FIFO empty - pub transmit_fifo_empty: bool, - /// RI: Ring indicator - pub ring_indicator: bool, - _reserved_zero_no_modify: u23, -} -impl_vmstate_bitsized!(Flags); - -impl Flags { - pub fn reset(&mut self) { - *self = Self::default(); - } -} - -impl Default for Flags { - fn default() -> Self { - let mut ret: Self = 0.into(); - // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - ret.set_receive_fifo_empty(true); - ret.set_transmit_fifo_empty(true); - ret - } -} - -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] -/// Line Control Register, `UARTLCR_H` -#[doc(alias = "UARTLCR_H")] -pub struct LineControl { - /// BRK: Send break - pub send_break: bool, - /// PEN: Parity enable - pub parity_enabled: bool, - /// EPS: Even parity select - pub parity: Parity, - /// STP2: Two stop bits select - pub two_stops_bits: bool, - /// FEN: Enable FIFOs - pub fifos_enabled: Mode, - /// WLEN: Word length in bits - /// b11 = 8 bits - /// b10 = 7 bits - /// b01 = 6 bits - /// b00 = 5 bits. - pub word_length: WordLength, - /// SPS Stick parity select - pub sticky_parity: bool, - /// 31:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u24, -} -impl_vmstate_bitsized!(LineControl); - -impl LineControl { - pub fn reset(&mut self) { - // All the bits are cleared to 0 when reset. - *self = 0.into(); - } -} - -impl Default for LineControl { - fn default() -> Self { - 0.into() - } -} - -#[bitsize(1)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] -/// `EPS` "Even parity select", field of [Line Control -/// register](LineControl). -pub enum Parity { - Odd = 0, - Even = 1, -} - -#[bitsize(1)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] -/// `FEN` "Enable FIFOs" or Device mode, field of [Line Control -/// register](LineControl). -pub enum Mode { - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers - Character = 0, - /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). - FIFO = 1, -} - -#[bitsize(2)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] -/// `WLEN` Word length, field of [Line Control register](LineControl). -/// -/// These bits indicate the number of data bits transmitted or received in a -/// frame as follows: -pub enum WordLength { - /// b11 = 8 bits - _8Bits = 0b11, - /// b10 = 7 bits - _7Bits = 0b10, - /// b01 = 6 bits - _6Bits = 0b01, - /// b00 = 5 bits. - _5Bits = 0b00, -} - -/// Control Register, `UARTCR` -/// -/// The `UARTCR` register is the control register. It contains various -/// enable bits, and the bits to write to set the usual outbound RS232 -/// modem control signals. All bits reset to 0 except TXE and RXE. -#[bitsize(32)] -#[doc(alias = "UARTCR")] -#[derive(Clone, Copy, DebugBits, FromBits)] -pub struct Control { - /// `UARTEN` UART enable: 0 = UART is disabled. - pub enable_uart: bool, - /// `SIREN` `SIR` enable: disable or enable IrDA SIR ENDEC. - /// QEMU does not model this. - pub enable_sir: bool, - /// `SIRLP` SIR low-power IrDA mode. QEMU does not model this. - pub sir_lowpower_irda_mode: u1, - /// Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u4, - /// `LBE` Loopback enable: feed UART output back to the input - pub enable_loopback: bool, - /// `TXE` Transmit enable - pub enable_transmit: bool, - /// `RXE` Receive enable - pub enable_receive: bool, - /// `DTR` Data transmit ready - pub data_transmit_ready: bool, - /// `RTS` Request to send - pub request_to_send: bool, - /// `Out1` UART Out1 signal; can be used as DCD - pub out_1: bool, - /// `Out2` UART Out2 signal; can be used as RI - pub out_2: bool, - /// `RTSEn` RTS hardware flow control enable - pub rts_hardware_flow_control_enable: bool, - /// `CTSEn` CTS hardware flow control enable - pub cts_hardware_flow_control_enable: bool, - /// 31:16 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify2: u16, -} -impl_vmstate_bitsized!(Control); - -impl Control { - pub fn reset(&mut self) { - *self = 0.into(); - self.set_enable_receive(true); - self.set_enable_transmit(true); - } -} - -impl Default for Control { - fn default() -> Self { - let mut ret: Self = 0.into(); - ret.reset(); - ret - } -} - -bits! { - /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC - #[derive(Default)] - pub struct Interrupt(u32) { - OE = 1 << 10, - BE = 1 << 9, - PE = 1 << 8, - FE = 1 << 7, - RT = 1 << 6, - TX = 1 << 5, - RX = 1 << 4, - DSR = 1 << 3, - DCD = 1 << 2, - CTS = 1 << 1, - RI = 1 << 0, - - E = bits!(Self as u32: OE | BE | PE | FE), - MS = bits!(Self as u32: RI | DSR | DCD | CTS), - } -} -impl_vmstate_forward!(Interrupt); diff --git a/rust/hw/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h deleted file mode 100644 index 87a5a589c..000000000 --- a/rust/hw/char/pl011/wrapper.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2024 Linaro Ltd. - * - * Authors: Manos Pitsidianakis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" -#include "hw/char/pl011.h" diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml deleted file mode 100644 index 9a9aa5170..000000000 --- a/rust/hw/core/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "hwcore" -version = "0.1.0" -description = "Rust bindings for QEMU/hwcore" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -qemu_macros = { path = "../../qemu-macros" } -common = { path = "../../common" } -bql = { path = "../../bql" } -qom = { path = "../../qom" } -chardev = { path = "../../chardev" } -migration = { path = "../../migration" } -system = { path = "../../system" } -util = { path = "../../util" } - -[lints] -workspace = true diff --git a/rust/hw/core/build.rs b/rust/hw/core/build.rs deleted file mode 120000 index 2a79ee31b..000000000 --- a/rust/hw/core/build.rs +++ /dev/null @@ -1 +0,0 @@ -../../util/build.rs \ No newline at end of file diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build deleted file mode 100644 index 81d8c77f9..000000000 --- a/rust/hw/core/meson.build +++ /dev/null @@ -1,80 +0,0 @@ -_hwcore_bindgen_args = [] -c_enums = [ - 'DeviceCategory', - 'GpioPolarity', - 'MachineInitPhase', - 'ResetType', -] -foreach enum : c_enums - _hwcore_bindgen_args += ['--rustified-enum', enum] -endforeach - -blocked_type = [ - 'Chardev', - 'Error', - 'ObjectClass', - 'MemoryRegion', - 'VMStateDescription', -] -foreach type: blocked_type - _hwcore_bindgen_args += ['--blocklist-type', type] -endforeach - -c_bitfields = [ - 'ClockEvent', -] -foreach enum : c_bitfields - _hwcore_bindgen_args += ['--bitfield-enum', enum] -endforeach - -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_hwcore_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _hwcore_bindgen_args, -) - -_hwcore_rs = static_library( - 'hwcore', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/irq.rs', - 'src/qdev.rs', - 'src/sysbus.rs', - ], - {'.': _hwcore_bindings_inc_rs} - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], - dependencies: [qemu_macros, common_rs], -) - -hwcore_rs = declare_dependency(link_with: [_hwcore_rs], - dependencies: [qom_rs, hwcore]) - -test('rust-hwcore-rs-integration', - executable( - 'rust-hwcore-rs-integration', - files('tests/tests.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: ['--test'], - install: false, - dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]), - args: [ - '--test', '--test-threads', '1', - '--format', 'pretty', - ], - protocol: 'rust', - suite: ['unit', 'rust']) diff --git a/rust/hw/core/src/bindings.rs b/rust/hw/core/src/bindings.rs deleted file mode 100644 index 919c02b56..000000000 --- a/rust/hw/core/src/bindings.rs +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -use chardev::bindings::Chardev; -use common::Zeroable; -use migration::bindings::VMStateDescription; -use qom::bindings::ObjectClass; -use system::bindings::MemoryRegion; -use util::bindings::Error; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -unsafe impl Send for Property {} -unsafe impl Sync for Property {} - -unsafe impl Send for TypeInfo {} -unsafe impl Sync for TypeInfo {} - -unsafe impl Zeroable for Property__bindgen_ty_1 {} -unsafe impl Zeroable for Property {} diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs deleted file mode 100644 index e0d7784d9..000000000 --- a/rust/hw/core/src/irq.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for interrupt sources - -use std::{ - ffi::{c_int, CStr}, - marker::PhantomData, - ptr, -}; - -use bql::BqlCell; -use common::Opaque; -use qom::{prelude::*, ObjectClass}; - -use crate::bindings::{self, qemu_set_irq}; - -/// An opaque wrapper around [`bindings::IRQState`]. -#[repr(transparent)] -#[derive(Debug, common::Wrapper)] -pub struct IRQState(Opaque); - -/// Interrupt sources are used by devices to pass changes to a value (typically -/// a boolean). The interrupt sink is usually an interrupt controller or -/// GPIO controller. -/// -/// As far as devices are concerned, interrupt sources are always active-high: -/// for example, `InterruptSource`'s [`raise`](InterruptSource::raise) -/// method sends a `true` value to the sink. If the guest has to see a -/// different polarity, that change is performed by the board between the -/// device and the interrupt controller. -/// -/// Interrupts are implemented as a pointer to the interrupt "sink", which has -/// type [`IRQState`]. A device exposes its source as a QOM link property using -/// a function such as [`crate::sysbus::SysBusDeviceMethods::init_irq`], and -/// initially leaves the pointer to a NULL value, representing an unconnected -/// interrupt. To connect it, whoever creates the device fills the pointer with -/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because -/// devices are generally shared objects, interrupt sources are an example of -/// the interior mutability pattern. -/// -/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell` -/// allows access from whatever thread has it. -#[derive(Debug)] -#[repr(transparent)] -pub struct InterruptSource -where - c_int: From, -{ - cell: BqlCell<*mut bindings::IRQState>, - _marker: PhantomData, -} - -// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken -unsafe impl Sync for InterruptSource where c_int: From {} - -impl InterruptSource { - /// Send a low (`false`) value to the interrupt sink. - pub fn lower(&self) { - self.set(false); - } - - /// Send a high-low pulse to the interrupt sink. - pub fn pulse(&self) { - self.set(true); - self.set(false); - } - - /// Send a high (`true`) value to the interrupt sink. - pub fn raise(&self) { - self.set(true); - } -} - -impl InterruptSource -where - c_int: From, -{ - /// Send `level` to the interrupt sink. - pub fn set(&self, level: T) { - let ptr = self.cell.get(); - // SAFETY: the pointer is retrieved under the BQL and remains valid - // until the BQL is released, which is after qemu_set_irq() is entered. - unsafe { - qemu_set_irq(ptr, level.into()); - } - } - - pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState { - self.cell.as_ptr() - } - - pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { - assert!(!slice.is_empty()); - slice[0].as_ptr() - } -} - -impl Default for InterruptSource { - fn default() -> Self { - InterruptSource { - cell: BqlCell::new(ptr::null_mut()), - _marker: PhantomData, - } - } -} - -unsafe impl ObjectType for IRQState { - type Class = ObjectClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; -} - -qom_isa!(IRQState: Object); diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs deleted file mode 100644 index b40801eb8..000000000 --- a/rust/hw/core/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub use qemu_macros::Device; -pub use qom; - -pub mod bindings; - -mod irq; -pub use irq::*; - -mod qdev; -pub use qdev::*; - -mod sysbus; -pub use sysbus::*; diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs deleted file mode 100644 index 71b9ef141..000000000 --- a/rust/hw/core/src/qdev.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to create devices and access device functionality from Rust. - -use std::{ - ffi::{c_int, c_void, CStr, CString}, - ptr::NonNull, -}; - -use chardev::Chardev; -use common::{callbacks::FnCall, Opaque}; -use migration::{impl_vmstate_c_struct, VMStateDescription}; -use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; -use util::{Error, Result}; - -pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType}; -use crate::{ - bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, - irq::InterruptSource, -}; - -/// A safe wrapper around [`bindings::Clock`]. -#[repr(transparent)] -#[derive(Debug, common::Wrapper)] -pub struct Clock(Opaque); - -unsafe impl Send for Clock {} -unsafe impl Sync for Clock {} - -/// A safe wrapper around [`bindings::DeviceState`]. -#[repr(transparent)] -#[derive(Debug, common::Wrapper)] -pub struct DeviceState(Opaque); - -unsafe impl Send for DeviceState {} -unsafe impl Sync for DeviceState {} - -/// Trait providing the contents of the `ResettablePhases` struct, -/// which is part of the QOM `Resettable` interface. -pub trait ResettablePhasesImpl { - /// If not None, this is called when the object enters reset. It - /// can reset local state of the object, but it must not do anything that - /// has a side-effect on other objects, such as raising or lowering an - /// [`InterruptSource`], or reading or writing guest memory. It takes the - /// reset's type as argument. - const ENTER: Option = None; - - /// If not None, this is called when the object for entry into reset, once - /// every object in the system which is being reset has had its - /// `ResettablePhasesImpl::ENTER` method called. At this point devices - /// can do actions that affect other objects. - /// - /// If in doubt, implement this method. - const HOLD: Option = None; - - /// If not None, this phase is called when the object leaves the reset - /// state. Actions affecting other objects are permitted. - const EXIT: Option = None; -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_resettable_enter_fn( - obj: *mut bindings::Object, - typ: ResetType, -) { - let state = NonNull::new(obj).unwrap().cast::(); - T::ENTER.unwrap()(unsafe { state.as_ref() }, typ); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_resettable_hold_fn( - obj: *mut bindings::Object, - typ: ResetType, -) { - let state = NonNull::new(obj).unwrap().cast::(); - T::HOLD.unwrap()(unsafe { state.as_ref() }, typ); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_resettable_exit_fn( - obj: *mut bindings::Object, - typ: ResetType, -) { - let state = NonNull::new(obj).unwrap().cast::(); - T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); -} - -/// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type. -/// -/// This trait is used by [`qemu_macros::Device`] derive macro. -/// -/// Base types that already have `qdev_prop_*` globals in the QEMU API should -/// use those values as exported by the [`bindings`] module, instead of -/// redefining them. -/// -/// # Safety -/// -/// This trait is marked as `unsafe` because currently having a `const` refer to -/// an `extern static` as a reference instead of a raw pointer results in this -/// compiler error: -/// -/// ```text -/// constructing invalid value: encountered reference to `extern` static in `const` -/// ``` -/// -/// This is because the compiler generally might dereference a normal reference -/// during const evaluation, but not in this case (if it did, it'd need to -/// dereference the raw pointer so this would fail to compile). -/// -/// It is the implementer's responsibility to provide a valid -/// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe. -pub unsafe trait QDevProp { - const VALUE: *const bindings::PropertyInfo; -} - -/// Use [`bindings::qdev_prop_bool`] for `bool`. -unsafe impl QDevProp for bool { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_bool }; -} - -/// Use [`bindings::qdev_prop_uint64`] for `u64`. -unsafe impl QDevProp for u64 { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_uint64 }; -} - -/// Use [`bindings::qdev_prop_chr`] for [`chardev::CharBackend`]. -unsafe impl QDevProp for chardev::CharBackend { - const VALUE: *const bindings::PropertyInfo = unsafe { &bindings::qdev_prop_chr }; -} - -/// Trait to define device properties. -/// -/// # Safety -/// -/// Caller is responsible for the validity of properties array. -pub unsafe trait DevicePropertiesImpl { - /// An array providing the properties that the user can set on the - /// device. - const PROPERTIES: &'static [Property] = &[]; -} - -/// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: - ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA -{ - /// _Realization_ is the second stage of device creation. It contains - /// all operations that depend on device properties and can fail (note: - /// this is not yet supported for Rust devices). - /// - /// If not `None`, the parent class's `realize` method is overridden - /// with the function pointed to by `REALIZE`. - const REALIZE: Option Result<()>> = None; - - /// A `VMStateDescription` providing the migration format for the device - /// Not a `const` because referencing statics in constants is unstable - /// until Rust 1.83.0. - const VMSTATE: Option> = None; -} - -/// # Safety -/// -/// This function is only called through the QOM machinery and -/// used by `DeviceClass::class_init`. -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_realize_fn( - dev: *mut bindings::DeviceState, - errp: *mut *mut util::bindings::Error, -) { - let state = NonNull::new(dev).unwrap().cast::(); - let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); - unsafe { - Error::ok_or_propagate(result, errp); - } -} - -unsafe impl InterfaceType for ResettableClass { - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; -} - -impl ResettableClass { - /// Fill in the virtual methods of `ResettableClass` based on the - /// definitions in the `ResettablePhasesImpl` trait. - pub fn class_init(&mut self) { - if ::ENTER.is_some() { - self.phases.enter = Some(rust_resettable_enter_fn::); - } - if ::HOLD.is_some() { - self.phases.hold = Some(rust_resettable_hold_fn::); - } - if ::EXIT.is_some() { - self.phases.exit = Some(rust_resettable_exit_fn::); - } - } -} - -impl DeviceClass { - /// Fill in the virtual methods of `DeviceClass` based on the definitions in - /// the `DeviceImpl` trait. - pub fn class_init(&mut self) { - if ::REALIZE.is_some() { - self.realize = Some(rust_realize_fn::); - } - if let Some(ref vmsd) = ::VMSTATE { - self.vmsd = vmsd.as_ref(); - } - let prop = ::PROPERTIES; - if !prop.is_empty() { - unsafe { - bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); - } - } - - ResettableClass::cast::(self).class_init::(); - self.parent_class.class_init::(); - } -} - -#[macro_export] -macro_rules! define_property { - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - bitnr: $bitnr, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..::common::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, - ..::common::zeroable::Zeroable::ZERO - } - }; - ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { - $crate::bindings::Property { - // use associated function syntax for type checking - name: ::std::ffi::CStr::as_ptr($name), - info: $prop, - offset: ::std::mem::offset_of!($state, $field) as isize, - set_default: false, - ..::common::zeroable::Zeroable::ZERO - } - }; -} - -#[macro_export] -macro_rules! declare_properties { - ($ident:ident, $($prop:expr),*$(,)*) => { - pub static $ident: [$crate::bindings::Property; { - let mut len = 0; - $({ - _ = stringify!($prop); - len += 1; - })* - len - }] = [ - $($prop),*, - ]; - }; -} - -unsafe impl ObjectType for DeviceState { - type Class = DeviceClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; -} - -qom_isa!(DeviceState: Object); - -/// Initialization methods take a [`ParentInit`] and can be called as -/// associated functions. -impl DeviceState { - /// Add an input clock named `name`. Invoke the callback with - /// `self` as the first parameter for the events that are requested. - /// - /// The resulting clock is added as a child of `self`, but it also - /// stays alive until after `Drop::drop` is called because C code - /// keeps an extra reference to it until `device_finalize()` calls - /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in - /// which Rust code has a reference to a child object) it would be - /// possible for this function to return a `&Clock` too. - #[inline] - pub fn init_clock_in FnCall<(&'a T, ClockEvent)>>( - this: &mut ParentInit, - name: &str, - _cb: &F, - events: ClockEvent, - ) -> Owned - where - T::ParentType: IsA, - { - fn do_init_clock_in( - dev: &DeviceState, - name: &str, - cb: Option, - events: ClockEvent, - ) -> Owned { - assert!(bql::is_locked()); - - // SAFETY: the clock is heap allocated, but qdev_init_clock_in() - // does not gift the reference to its caller; so use Owned::from to - // add one. The callback is disabled automatically when the clock - // is unparented, which happens before the device is finalized. - unsafe { - let cstr = CString::new(name).unwrap(); - let clk = bindings::qdev_init_clock_in( - dev.0.as_mut_ptr(), - cstr.as_ptr(), - cb, - dev.0.as_void_ptr(), - events.0, - ); - - let clk: &Clock = Clock::from_raw(clk); - Owned::from(clk) - } - } - - let cb: Option = if F::is_some() { - unsafe extern "C" fn rust_clock_cb FnCall<(&'a T, ClockEvent)>>( - opaque: *mut c_void, - event: ClockEvent, - ) { - // SAFETY: the opaque is "this", which is indeed a pointer to T - F::call((unsafe { &*(opaque.cast::()) }, event)) - } - Some(rust_clock_cb::) - } else { - None - }; - - do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events) - } - - /// Add an output clock named `name`. - /// - /// The resulting clock is added as a child of `self`, but it also - /// stays alive until after `Drop::drop` is called because C code - /// keeps an extra reference to it until `device_finalize()` calls - /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in - /// which Rust code has a reference to a child object) it would be - /// possible for this function to return a `&Clock` too. - #[inline] - pub fn init_clock_out(this: &mut ParentInit, name: &str) -> Owned - where - T::ParentType: IsA, - { - unsafe { - let cstr = CString::new(name).unwrap(); - let dev: &mut DeviceState = this.upcast_mut(); - let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr()); - - let clk: &Clock = Clock::from_raw(clk); - Owned::from(clk) - } - } -} - -/// Trait for methods exposed by the [`DeviceState`] class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA`. -pub trait DeviceMethods: ObjectDeref -where - Self::Target: IsA, -{ - fn prop_set_chr(&self, propname: &str, chr: &Owned) { - assert!(bql::is_locked()); - let c_propname = CString::new(propname).unwrap(); - let chr: &Chardev = chr; - unsafe { - bindings::qdev_prop_set_chr( - self.upcast().as_mut_ptr(), - c_propname.as_ptr(), - chr.as_mut_ptr(), - ); - } - } - - fn init_gpio_in FnCall<(&'a Self::Target, u32, u32)>>( - &self, - num_lines: u32, - _cb: F, - ) { - fn do_init_gpio_in( - dev: &DeviceState, - num_lines: u32, - gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int), - ) { - unsafe { - qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int); - } - } - - const { assert!(F::IS_SOME) }; - unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( - opaque: *mut c_void, - line: c_int, - level: c_int, - ) { - // SAFETY: the opaque was passed as a reference to `T` - F::call((unsafe { &*(opaque.cast::()) }, line as u32, level as u32)) - } - - let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = - rust_irq_handler::; - - do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb); - } - - fn init_gpio_out(&self, pins: &[InterruptSource]) { - unsafe { - qdev_init_gpio_out( - self.upcast().as_mut_ptr(), - InterruptSource::slice_as_ptr(pins), - pins.len() as c_int, - ); - } - } -} - -impl DeviceMethods for R where R::Target: IsA {} - -unsafe impl ObjectType for Clock { - type Class = ObjectClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; -} - -qom_isa!(Clock: Object); - -impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs deleted file mode 100644 index 282315fce..000000000 --- a/rust/hw/core/src/sysbus.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to access `sysbus` functionality from Rust. - -use std::{ffi::CStr, ptr::addr_of_mut}; - -pub use bindings::SysBusDeviceClass; -use common::Opaque; -use qom::{prelude::*, Owned}; -use system::MemoryRegion; - -use crate::{ - bindings, - irq::{IRQState, InterruptSource}, - qdev::{DeviceImpl, DeviceState}, -}; - -/// A safe wrapper around [`bindings::SysBusDevice`]. -#[repr(transparent)] -#[derive(Debug, common::Wrapper)] -pub struct SysBusDevice(Opaque); - -unsafe impl Send for SysBusDevice {} -unsafe impl Sync for SysBusDevice {} - -unsafe impl ObjectType for SysBusDevice { - type Class = SysBusDeviceClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; -} - -qom_isa!(SysBusDevice: DeviceState, Object); - -// TODO: add virtual methods -pub trait SysBusDeviceImpl: DeviceImpl + IsA {} - -impl SysBusDeviceClass { - /// Fill in the virtual methods of `SysBusDeviceClass` based on the - /// definitions in the `SysBusDeviceImpl` trait. - pub fn class_init(self: &mut SysBusDeviceClass) { - self.parent_class.class_init::(); - } -} - -/// Trait for methods of [`SysBusDevice`] and its subclasses. -pub trait SysBusDeviceMethods: ObjectDeref -where - Self::Target: IsA, -{ - /// Expose a memory region to the board so that it can give it an address - /// in guest memory. Note that the ordering of calls to `init_mmio` is - /// important, since whoever creates the sysbus device will refer to the - /// region with a number that corresponds to the order of calls to - /// `init_mmio`. - fn init_mmio(&self, iomem: &MemoryRegion) { - assert!(bql::is_locked()); - unsafe { - bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); - } - } - - /// Expose an interrupt source outside the device as a qdev GPIO output. - /// Note that the ordering of calls to `init_irq` is important, since - /// whoever creates the sysbus device will refer to the interrupts with - /// a number that corresponds to the order of calls to `init_irq`. - fn init_irq(&self, irq: &InterruptSource) { - assert!(bql::is_locked()); - unsafe { - bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); - } - } - - // TODO: do we want a type like GuestAddress here? - fn mmio_addr(&self, id: u32) -> Option { - assert!(bql::is_locked()); - // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and - // the SysBusDevice must be initialized to get an IsA. - let sbd = unsafe { *self.upcast().as_ptr() }; - let id: usize = id.try_into().unwrap(); - if sbd.mmio[id].memory.is_null() { - None - } else { - Some(sbd.mmio[id].addr) - } - } - - // TODO: do we want a type like GuestAddress here? - fn mmio_map(&self, id: u32, addr: u64) { - assert!(bql::is_locked()); - let id: i32 = id.try_into().unwrap(); - unsafe { - bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); - } - } - - // Owned<> is used here because sysbus_connect_irq (via - // object_property_set_link) adds a reference to the IRQState, - // which can prolong its life - fn connect_irq(&self, id: u32, irq: &Owned) { - assert!(bql::is_locked()); - let id: i32 = id.try_into().unwrap(); - let irq: &IRQState = irq; - unsafe { - bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); - } - } - - fn sysbus_realize(&self) { - // TODO: return an Error - assert!(bql::is_locked()); - unsafe { - bindings::sysbus_realize( - self.upcast().as_mut_ptr(), - addr_of_mut!(util::bindings::error_fatal), - ); - } - } -} - -impl SysBusDeviceMethods for R where R::Target: IsA {} diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs deleted file mode 100644 index 247d81286..000000000 --- a/rust/hw/core/tests/tests.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ffi::CStr, ptr::addr_of}; - -use bql::BqlCell; -use hwcore::{DeviceImpl, DeviceState, ResettablePhasesImpl, SysBusDevice}; -use migration::{VMStateDescription, VMStateDescriptionBuilder}; -use qom::{prelude::*, ObjectImpl, ParentField}; -use util::bindings::{module_call_init, module_init_type}; - -// Test that macros can compile. -pub const VMSTATE: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"name") - .unmigratable() - .build(); - -#[repr(C)] -#[derive(qom::Object, hwcore::Device)] -pub struct DummyState { - parent: ParentField, - #[property(rename = "migrate-clk", default = true)] - migrate_clock: bool, -} - -qom_isa!(DummyState: Object, DeviceState); - -pub struct DummyClass { - parent_class: ::Class, -} - -impl DummyClass { - pub fn class_init(self: &mut DummyClass) { - self.parent_class.class_init::(); - } -} - -unsafe impl ObjectType for DummyState { - type Class = DummyClass; - const TYPE_NAME: &'static CStr = c"dummy"; -} - -impl ObjectImpl for DummyState { - type ParentType = DeviceState; - const ABSTRACT: bool = false; - const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::; -} - -impl ResettablePhasesImpl for DummyState {} - -impl DeviceImpl for DummyState { - const VMSTATE: Option> = Some(VMSTATE); -} - -#[repr(C)] -#[derive(qom::Object, hwcore::Device)] -pub struct DummyChildState { - parent: ParentField, -} - -qom_isa!(DummyChildState: Object, DeviceState, DummyState); - -pub struct DummyChildClass { - parent_class: ::Class, -} - -unsafe impl ObjectType for DummyChildState { - type Class = DummyChildClass; - const TYPE_NAME: &'static CStr = c"dummy_child"; -} - -impl ObjectImpl for DummyChildState { - type ParentType = DummyState; - const ABSTRACT: bool = false; - const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::; -} - -impl ResettablePhasesImpl for DummyChildState {} -impl DeviceImpl for DummyChildState {} - -impl DummyChildClass { - pub fn class_init(self: &mut DummyChildClass) { - self.parent_class.class_init::(); - } -} - -fn init_qom() { - static ONCE: BqlCell = BqlCell::new(false); - - bql::start_test(); - if !ONCE.get() { - unsafe { - module_call_init(module_init_type::MODULE_INIT_QOM); - } - ONCE.set(true); - } -} - -#[test] -/// Create and immediately drop an instance. -fn test_object_new() { - init_qom(); - drop(DummyState::new()); - drop(DummyChildState::new()); -} - -#[test] -#[allow(clippy::redundant_clone)] -/// Create, clone and then drop an instance. -fn test_clone() { - init_qom(); - let p = DummyState::new(); - assert_eq!(p.clone().typename(), "dummy"); - drop(p); -} - -#[test] -/// Try invoking a method on an object. -fn test_typename() { - init_qom(); - let p = DummyState::new(); - assert_eq!(p.typename(), "dummy"); -} - -// a note on all "cast" tests: usually, especially for downcasts the desired -// class would be placed on the right, for example: -// -// let sbd_ref = p.dynamic_cast::(); -// -// Here I am doing the opposite to check that the resulting type is correct. - -#[test] -#[allow(clippy::shadow_unrelated)] -/// Test casts on shared references. -fn test_cast() { - init_qom(); - let p = DummyState::new(); - let p_ptr: *mut DummyState = p.as_mut_ptr(); - let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; - - let obj_ref: &Object = p_ref.upcast(); - assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); - - let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); - assert!(sbd_ref.is_none()); - - let dev_ref: Option<&DeviceState> = obj_ref.downcast(); - assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); - - // SAFETY: the cast is wrong, but the value is only used for comparison - unsafe { - let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); - assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); - } -} diff --git a/rust/hw/core/wrapper.h b/rust/hw/core/wrapper.h deleted file mode 100644 index 3bdbd1249..000000000 --- a/rust/hw/core/wrapper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" - -#include "hw/sysbus.h" -#include "hw/clock.h" -#include "hw/qdev-clock.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/irq.h" diff --git a/rust/hw/meson.build b/rust/hw/meson.build deleted file mode 100644 index 9749d4adf..000000000 --- a/rust/hw/meson.build +++ /dev/null @@ -1,2 +0,0 @@ -subdir('char') -subdir('timer') diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig deleted file mode 100644 index afd980335..000000000 --- a/rust/hw/timer/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -config X_HPET_RUST - bool diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml deleted file mode 100644 index f781b28d8..000000000 --- a/rust/hw/timer/hpet/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "hpet" -version = "0.1.0" -authors = ["Zhao Liu "] -description = "IA-PC High Precision Event Timer emulation in Rust" - -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../../../common" } -util = { path = "../../../util" } -migration = { path = "../../../migration" } -bql = { path = "../../../bql" } -qom = { path = "../../../qom" } -system = { path = "../../../system" } -hwcore = { path = "../../../hw/core" } - -[lints] -workspace = true diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build deleted file mode 100644 index bb64b9667..000000000 --- a/rust/hw/timer/hpet/meson.build +++ /dev/null @@ -1,20 +0,0 @@ -_libhpet_rs = static_library( - 'hpet', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [ - common_rs, - util_rs, - migration_rs, - bql_rs, - qom_rs, - system_rs, - hwcore_rs, - ], -) - -rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( - link_whole: [_libhpet_rs], - variables: {'crate': 'hpet'}, -)]) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs deleted file mode 100644 index 3cfbe9c32..000000000 --- a/rust/hw/timer/hpet/src/device.rs +++ /dev/null @@ -1,1019 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::CStr, - mem::MaybeUninit, - pin::Pin, - ptr::{addr_of_mut, null_mut, NonNull}, - slice::from_ref, -}; - -use bql::{BqlCell, BqlRefCell}; -use common::{bitops::IntegerExt, uninit_field_mut}; -use hwcore::{ - bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, - declare_properties, define_property, DeviceImpl, DeviceMethods, DeviceState, InterruptSource, - Property, ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods, -}; -use migration::{ - self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate, - VMStateDescription, VMStateDescriptionBuilder, -}; -use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; -use system::{ - bindings::{address_space_memory, address_space_stl_le, hwaddr}, - MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, -}; -use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; - -use crate::fw_cfg::HPETFwConfig; - -/// Register space for each timer block (`HPET_BASE` is defined in hpet.h). -const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes - -/// Minimum recommended hardware implementation. -const HPET_MIN_TIMERS: usize = 3; -/// Maximum timers in each timer block. -const HPET_MAX_TIMERS: usize = 32; - -/// Flags that HPETState.flags supports. -const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0; - -const HPET_NUM_IRQ_ROUTES: usize = 32; -const HPET_LEGACY_PIT_INT: u32 = 0; // HPET_LEGACY_RTC_INT isn't defined here. -const RTC_ISA_IRQ: usize = 8; - -const HPET_CLK_PERIOD: u64 = 10; // 10 ns -const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns - -/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec). -const HPET_CAP_REV_ID_VALUE: u64 = 0x1; -const HPET_CAP_REV_ID_SHIFT: usize = 0; -/// Number of Timers (bits 8:12) -const HPET_CAP_NUM_TIM_SHIFT: usize = 8; -/// Counter Size (bit 13) -const HPET_CAP_COUNT_SIZE_CAP_SHIFT: usize = 13; -/// Legacy Replacement Route Capable (bit 15) -const HPET_CAP_LEG_RT_CAP_SHIFT: usize = 15; -/// Vendor ID (bits 16:31) -const HPET_CAP_VENDER_ID_VALUE: u64 = 0x8086; -const HPET_CAP_VENDER_ID_SHIFT: usize = 16; -/// Main Counter Tick Period (bits 32:63) -const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32; - -/// Overall Enable (bit 0) -const HPET_CFG_ENABLE_SHIFT: usize = 0; -/// Legacy Replacement Route (bit 1) -const HPET_CFG_LEG_RT_SHIFT: usize = 1; -/// Other bits are reserved. -const HPET_CFG_WRITE_MASK: u64 = 0x003; - -/// bit 0, 7, and bits 16:31 are reserved. -/// bit 4, 5, 15, and bits 32:64 are read-only. -const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e; -/// Timer N Interrupt Type (bit 1) -const HPET_TN_CFG_INT_TYPE_SHIFT: usize = 1; -/// Timer N Interrupt Enable (bit 2) -const HPET_TN_CFG_INT_ENABLE_SHIFT: usize = 2; -/// Timer N Type (Periodic enabled or not, bit 3) -const HPET_TN_CFG_PERIODIC_SHIFT: usize = 3; -/// Timer N Periodic Interrupt Capable (support Periodic or not, bit 4) -const HPET_TN_CFG_PERIODIC_CAP_SHIFT: usize = 4; -/// Timer N Size (timer size is 64-bits or 32 bits, bit 5) -const HPET_TN_CFG_SIZE_CAP_SHIFT: usize = 5; -/// Timer N Value Set (bit 6) -const HPET_TN_CFG_SETVAL_SHIFT: usize = 6; -/// Timer N 32-bit Mode (bit 8) -const HPET_TN_CFG_32BIT_SHIFT: usize = 8; -/// Timer N Interrupt Rout (bits 9:13) -const HPET_TN_CFG_INT_ROUTE_MASK: u64 = 0x3e00; -const HPET_TN_CFG_INT_ROUTE_SHIFT: usize = 9; -/// Timer N FSB Interrupt Enable (bit 14) -const HPET_TN_CFG_FSB_ENABLE_SHIFT: usize = 14; -/// Timer N FSB Interrupt Delivery (bit 15) -const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; -/// Timer N Interrupt Routing Capability (bits 32:63) -const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; - -#[derive(common::TryInto)] -#[repr(u64)] -#[allow(non_camel_case_types)] -/// Timer registers, masked by 0x18 -enum TimerRegister { - /// Timer N Configuration and Capability Register - CFG = 0, - /// Timer N Comparator Value Register - CMP = 8, - /// Timer N FSB Interrupt Route Register - ROUTE = 16, -} - -#[derive(common::TryInto)] -#[repr(u64)] -#[allow(non_camel_case_types)] -/// Global registers -enum GlobalRegister { - /// General Capabilities and ID Register - CAP = 0, - /// General Configuration Register - CFG = 0x10, - /// General Interrupt Status Register - INT_STATUS = 0x20, - /// Main Counter Value Register - COUNTER = 0xF0, -} - -enum HPETRegister<'a> { - /// Global register in the range from `0` to `0xff` - Global(GlobalRegister), - - /// Register in the timer block `0x100`...`0x3ff` - Timer(&'a BqlRefCell, TimerRegister), - - /// Invalid address - #[allow(dead_code)] - Unknown(hwaddr), -} - -struct HPETAddrDecode<'a> { - shift: u32, - len: u32, - reg: HPETRegister<'a>, -} - -const fn hpet_next_wrap(cur_tick: u64) -> u64 { - (cur_tick | 0xffffffff) + 1 -} - -const fn hpet_time_after(a: u64, b: u64) -> bool { - ((b - a) as i64) < 0 -} - -const fn ticks_to_ns(value: u64) -> u64 { - value * HPET_CLK_PERIOD -} - -const fn ns_to_ticks(value: u64) -> u64 { - value / HPET_CLK_PERIOD -} - -// Avoid touching the bits that cannot be written. -const fn hpet_fixup_reg(new: u64, old: u64, mask: u64) -> u64 { - (new & mask) | (old & !mask) -} - -const fn activating_bit(old: u64, new: u64, shift: usize) -> bool { - let mask: u64 = 1 << shift; - (old & mask == 0) && (new & mask != 0) -} - -const fn deactivating_bit(old: u64, new: u64, shift: usize) -> bool { - let mask: u64 = 1 << shift; - (old & mask != 0) && (new & mask == 0) -} - -fn timer_handler(timer_cell: &BqlRefCell) { - timer_cell.borrow_mut().callback() -} - -/// HPET Timer Abstraction -#[repr(C)] -#[derive(Debug)] -pub struct HPETTimer { - /// timer N index within the timer block (`HPETState`) - #[doc(alias = "tn")] - index: u8, - qemu_timer: Timer, - /// timer block abstraction containing this timer - state: NonNull, - - // Memory-mapped, software visible timer registers - /// Timer N Configuration and Capability Register - config: u64, - /// Timer N Comparator Value Register - cmp: u64, - /// Timer N FSB Interrupt Route Register - fsb: u64, - - // Hidden register state - /// comparator (extended to counter width) - cmp64: u64, - /// Last value written to comparator - period: u64, - /// timer pop will indicate wrap for one-shot 32-bit - /// mode. Next pop will be actual timer expiration. - wrap_flag: u8, - /// last value armed, to avoid timer storms - last: u64, -} - -// SAFETY: Sync is not automatically derived due to the `state` field, -// which is always dereferenced to a shared reference. -unsafe impl Sync for HPETTimer {} - -impl HPETTimer { - fn new(index: u8, state: *const HPETState) -> HPETTimer { - HPETTimer { - index, - // SAFETY: the HPETTimer will only be used after the timer - // is initialized below. - qemu_timer: unsafe { Timer::new() }, - state: NonNull::new(state.cast_mut()).unwrap(), - config: 0, - cmp: 0, - fsb: 0, - cmp64: 0, - period: 0, - wrap_flag: 0, - last: 0, - } - } - - fn init_timer_with_cell(cell: &BqlRefCell) { - let mut timer = cell.borrow_mut(); - // SAFETY: HPETTimer is only used as part of HPETState, which is - // always pinned. - let qemu_timer = unsafe { Pin::new_unchecked(&mut timer.qemu_timer) }; - qemu_timer.init_full(None, CLOCK_VIRTUAL, Timer::NS, 0, timer_handler, cell); - } - - fn get_state(&self) -> &HPETState { - // SAFETY: - // the pointer is convertible to a reference - unsafe { self.state.as_ref() } - } - - fn is_int_active(&self) -> bool { - self.get_state().is_timer_int_active(self.index.into()) - } - - const fn is_fsb_route_enabled(&self) -> bool { - self.config & (1 << HPET_TN_CFG_FSB_ENABLE_SHIFT) != 0 - } - - const fn is_periodic(&self) -> bool { - self.config & (1 << HPET_TN_CFG_PERIODIC_SHIFT) != 0 - } - - const fn is_int_enabled(&self) -> bool { - self.config & (1 << HPET_TN_CFG_INT_ENABLE_SHIFT) != 0 - } - - const fn is_32bit_mod(&self) -> bool { - self.config & (1 << HPET_TN_CFG_32BIT_SHIFT) != 0 - } - - const fn is_valset_enabled(&self) -> bool { - self.config & (1 << HPET_TN_CFG_SETVAL_SHIFT) != 0 - } - - fn clear_valset(&mut self) { - self.config &= !(1 << HPET_TN_CFG_SETVAL_SHIFT); - } - - /// True if timer interrupt is level triggered; otherwise, edge triggered. - const fn is_int_level_triggered(&self) -> bool { - self.config & (1 << HPET_TN_CFG_INT_TYPE_SHIFT) != 0 - } - - /// calculate next value of the general counter that matches the - /// target (either entirely, or the low 32-bit only depending on - /// the timer mode). - fn calculate_cmp64(&self, cur_tick: u64, target: u64) -> u64 { - if self.is_32bit_mod() { - let mut result: u64 = cur_tick.deposit(0, 32, target); - if result < cur_tick { - result += 0x100000000; - } - result - } else { - target - } - } - - const fn get_individual_route(&self) -> usize { - ((self.config & HPET_TN_CFG_INT_ROUTE_MASK) >> HPET_TN_CFG_INT_ROUTE_SHIFT) as usize - } - - fn get_int_route(&self) -> usize { - if self.index <= 1 && self.get_state().is_legacy_mode() { - // If LegacyReplacement Route bit is set, HPET specification requires - // timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, - // timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. - // - // If the LegacyReplacement Route bit is set, the individual routing - // bits for timers 0 and 1 (APIC or FSB) will have no impact. - // - // FIXME: Consider I/O APIC case. - if self.index == 0 { - 0 - } else { - RTC_ISA_IRQ - } - } else { - // (If the LegacyReplacement Route bit is set) Timer 2-n will be - // routed as per the routing in the timer n config registers. - // ... - // If the LegacyReplacement Route bit is not set, the individual - // routing bits for each of the timers are used. - self.get_individual_route() - } - } - - fn set_irq(&mut self, set: bool) { - let route = self.get_int_route(); - - if set && self.is_int_enabled() && self.get_state().is_hpet_enabled() { - if self.is_fsb_route_enabled() { - // SAFETY: - // the parameters are valid. - unsafe { - address_space_stl_le( - addr_of_mut!(address_space_memory), - self.fsb >> 32, // Timer N FSB int addr - self.fsb as u32, // Timer N FSB int value, truncate! - MEMTXATTRS_UNSPECIFIED, - null_mut(), - ); - } - } else if self.is_int_level_triggered() { - self.get_state().irqs[route].raise(); - } else { - self.get_state().irqs[route].pulse(); - } - } else if !self.is_fsb_route_enabled() { - self.get_state().irqs[route].lower(); - } - } - - fn update_irq(&mut self, set: bool) { - // If Timer N Interrupt Enable bit is 0, "the timer will - // still operate and generate appropriate status bits, but - // will not cause an interrupt" - self.get_state() - .update_int_status(self.index.into(), set && self.is_int_level_triggered()); - self.set_irq(set); - } - - fn arm_timer(&mut self, tick: u64) { - let mut ns = self.get_state().get_ns(tick); - - // Clamp period to reasonable min value (1 us) - if self.is_periodic() && ns - self.last < 1000 { - ns = self.last + 1000; - } - - self.last = ns; - self.qemu_timer.modify(self.last); - } - - fn set_timer(&mut self) { - let cur_tick: u64 = self.get_state().get_ticks(); - - self.wrap_flag = 0; - self.cmp64 = self.calculate_cmp64(cur_tick, self.cmp); - if self.is_32bit_mod() { - // HPET spec says in one-shot 32-bit mode, generate an interrupt when - // counter wraps in addition to an interrupt with comparator match. - if !self.is_periodic() && self.cmp64 > hpet_next_wrap(cur_tick) { - self.wrap_flag = 1; - self.arm_timer(hpet_next_wrap(cur_tick)); - return; - } - } - self.arm_timer(self.cmp64); - } - - fn del_timer(&mut self) { - // Just remove the timer from the timer_list without destroying - // this timer instance. - self.qemu_timer.delete(); - - if self.is_int_active() { - // For level-triggered interrupt, this leaves interrupt status - // register set but lowers irq. - self.update_irq(true); - } - } - - /// Configuration and Capability Register - fn set_tn_cfg_reg(&mut self, shift: u32, len: u32, val: u64) { - // TODO: Add trace point - trace_hpet_ram_write_tn_cfg(addr & 4) - let old_val: u64 = self.config; - let mut new_val: u64 = old_val.deposit(shift, len, val); - new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - - // Switch level-type interrupt to edge-type. - if deactivating_bit(old_val, new_val, HPET_TN_CFG_INT_TYPE_SHIFT) { - // Do this before changing timer.config; otherwise, if - // HPET_TN_FSB is set, update_irq will not lower the qemu_irq. - self.update_irq(false); - } - - self.config = new_val; - - if activating_bit(old_val, new_val, HPET_TN_CFG_INT_ENABLE_SHIFT) && self.is_int_active() { - self.update_irq(true); - } - - if self.is_32bit_mod() { - self.cmp = u64::from(self.cmp as u32); // truncate! - self.period = u64::from(self.period as u32); // truncate! - } - - if self.get_state().is_hpet_enabled() { - self.set_timer(); - } - } - - /// Comparator Value Register - fn set_tn_cmp_reg(&mut self, shift: u32, len: u32, val: u64) { - let mut length = len; - let mut value = val; - - // TODO: Add trace point - trace_hpet_ram_write_tn_cmp(addr & 4) - if self.is_32bit_mod() { - // High 32-bits are zero, leave them untouched. - if shift != 0 { - // TODO: Add trace point - trace_hpet_ram_write_invalid_tn_cmp() - return; - } - length = 64; - value = u64::from(value as u32); // truncate! - } - - if !self.is_periodic() || self.is_valset_enabled() { - self.cmp = self.cmp.deposit(shift, length, value); - } - - if self.is_periodic() { - self.period = self.period.deposit(shift, length, value); - } - - self.clear_valset(); - if self.get_state().is_hpet_enabled() { - self.set_timer(); - } - } - - /// FSB Interrupt Route Register - fn set_tn_fsb_route_reg(&mut self, shift: u32, len: u32, val: u64) { - self.fsb = self.fsb.deposit(shift, len, val); - } - - fn reset(&mut self) { - self.del_timer(); - self.cmp = u64::MAX; // Comparator Match Registers reset to all 1's. - self.config = (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << HPET_TN_CFG_SIZE_CAP_SHIFT); - if self.get_state().has_msi_flag() { - self.config |= 1 << HPET_TN_CFG_FSB_CAP_SHIFT; - } - // advertise availability of ioapic int - self.config |= - (u64::from(self.get_state().int_route_cap)) << HPET_TN_CFG_INT_ROUTE_CAP_SHIFT; - self.period = 0; - self.wrap_flag = 0; - } - - /// timer expiration callback - fn callback(&mut self) { - let period: u64 = self.period; - let cur_tick: u64 = self.get_state().get_ticks(); - - if self.is_periodic() && period != 0 { - while hpet_time_after(cur_tick, self.cmp64) { - self.cmp64 += period; - } - if self.is_32bit_mod() { - self.cmp = u64::from(self.cmp64 as u32); // truncate! - } else { - self.cmp = self.cmp64; - } - self.arm_timer(self.cmp64); - } else if self.wrap_flag != 0 { - self.wrap_flag = 0; - self.arm_timer(self.cmp64); - } - self.update_irq(true); - } - - const fn read(&self, reg: TimerRegister) -> u64 { - use TimerRegister::*; - match reg { - CFG => self.config, // including interrupt capabilities - CMP => self.cmp, // comparator register - ROUTE => self.fsb, - } - } - - fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) { - use TimerRegister::*; - match reg { - CFG => self.set_tn_cfg_reg(shift, len, value), - CMP => self.set_tn_cmp_reg(shift, len, value), - ROUTE => self.set_tn_fsb_route_reg(shift, len, value), - } - } -} - -/// HPET Event Timer Block Abstraction -#[repr(C)] -#[derive(qom::Object)] -pub struct HPETState { - parent_obj: ParentField, - iomem: MemoryRegion, - - // HPET block Registers: Memory-mapped, software visible registers - /// General Capabilities and ID Register - capability: BqlCell, - /// General Configuration Register - config: BqlCell, - /// General Interrupt Status Register - #[doc(alias = "isr")] - int_status: BqlCell, - /// Main Counter Value Register - #[doc(alias = "hpet_counter")] - counter: BqlCell, - - // Internal state - /// Capabilities that QEMU HPET supports. - /// bit 0: MSI (or FSB) support. - flags: u32, - - /// Offset of main counter relative to qemu clock. - hpet_offset: BqlCell, - hpet_offset_saved: bool, - - irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES], - rtc_irq_level: BqlCell, - pit_enabled: InterruptSource, - - /// Interrupt Routing Capability. - /// This field indicates to which interrupts in the I/O (x) APIC - /// the timers' interrupt can be routed, and is encoded in the - /// bits 32:64 of timer N's config register: - #[doc(alias = "intcap")] - int_route_cap: u32, - - /// HPET timer array managed by this timer block. - #[doc(alias = "timer")] - timers: [BqlRefCell; HPET_MAX_TIMERS], - num_timers: usize, - num_timers_save: BqlCell, - - /// Instance id (HPET timer block ID). - hpet_id: BqlCell, -} - -impl HPETState { - const fn has_msi_flag(&self) -> bool { - self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0 - } - - fn is_legacy_mode(&self) -> bool { - self.config.get() & (1 << HPET_CFG_LEG_RT_SHIFT) != 0 - } - - fn is_hpet_enabled(&self) -> bool { - self.config.get() & (1 << HPET_CFG_ENABLE_SHIFT) != 0 - } - - fn is_timer_int_active(&self, index: usize) -> bool { - self.int_status.get() & (1 << index) != 0 - } - - fn get_ticks(&self) -> u64 { - ns_to_ticks(CLOCK_VIRTUAL.get_ns() + self.hpet_offset.get()) - } - - fn get_ns(&self, tick: u64) -> u64 { - ticks_to_ns(tick) - self.hpet_offset.get() - } - - fn handle_legacy_irq(&self, irq: u32, level: u32) { - if irq == HPET_LEGACY_PIT_INT { - if !self.is_legacy_mode() { - self.irqs[0].set(level != 0); - } - } else { - self.rtc_irq_level.set(level); - if !self.is_legacy_mode() { - self.irqs[RTC_ISA_IRQ].set(level != 0); - } - } - } - - fn init_timers(this: &mut MaybeUninit) { - let state = this.as_ptr(); - for index in 0..HPET_MAX_TIMERS { - let mut timer = uninit_field_mut!(*this, timers[index]); - - // Initialize in two steps, to avoid calling Timer::init_full on a - // temporary that can be moved. - let timer = timer.write(BqlRefCell::new(HPETTimer::new( - index.try_into().unwrap(), - state, - ))); - HPETTimer::init_timer_with_cell(timer); - } - } - - fn update_int_status(&self, index: u32, level: bool) { - self.int_status - .set(self.int_status.get().deposit(index, 1, u64::from(level))); - } - - /// General Configuration Register - fn set_cfg_reg(&self, shift: u32, len: u32, val: u64) { - let old_val = self.config.get(); - let mut new_val = old_val.deposit(shift, len, val); - - new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - self.config.set(new_val); - - if activating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { - // Enable main counter and interrupt generation. - self.hpet_offset - .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); - - for timer in self.timers.iter().take(self.num_timers) { - let mut t = timer.borrow_mut(); - - if t.is_int_enabled() && t.is_int_active() { - t.update_irq(true); - } - t.set_timer(); - } - } else if deactivating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { - // Halt main counter and disable interrupt generation. - self.counter.set(self.get_ticks()); - - for timer in self.timers.iter().take(self.num_timers) { - timer.borrow_mut().del_timer(); - } - } - - // i8254 and RTC output pins are disabled when HPET is in legacy mode - if activating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { - self.pit_enabled.set(false); - self.irqs[0].lower(); - self.irqs[RTC_ISA_IRQ].lower(); - } else if deactivating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { - // TODO: Add irq binding: qemu_irq_lower(s->irqs[0]) - self.irqs[0].lower(); - self.pit_enabled.set(true); - self.irqs[RTC_ISA_IRQ].set(self.rtc_irq_level.get() != 0); - } - } - - /// General Interrupt Status Register: Read/Write Clear - fn set_int_status_reg(&self, shift: u32, _len: u32, val: u64) { - let new_val = val << shift; - let cleared = new_val & self.int_status.get(); - - for (index, timer) in self.timers.iter().take(self.num_timers).enumerate() { - if cleared & (1 << index) != 0 { - timer.borrow_mut().update_irq(false); - } - } - } - - /// Main Counter Value Register - fn set_counter_reg(&self, shift: u32, len: u32, val: u64) { - if self.is_hpet_enabled() { - // TODO: Add trace point - - // trace_hpet_ram_write_counter_write_while_enabled() - // - // HPET spec says that writes to this register should only be - // done while the counter is halted. So this is an undefined - // behavior. There's no need to forbid it, but when HPET is - // enabled, the changed counter value will not affect the - // tick count (i.e., the previously calculated offset will - // not be changed as well). - } - self.counter - .set(self.counter.get().deposit(shift, len, val)); - } - - unsafe fn init(mut this: ParentInit) { - static HPET_RAM_OPS: MemoryRegionOps = - MemoryRegionOpsBuilder::::new() - .read(&HPETState::read) - .write(&HPETState::write) - .native_endian() - .valid_sizes(4, 8) - .impl_sizes(4, 8) - .build(); - - MemoryRegion::init_io( - &mut uninit_field_mut!(*this, iomem), - &HPET_RAM_OPS, - "hpet", - HPET_REG_SPACE_LEN, - ); - - Self::init_timers(&mut this); - } - - fn post_init(&self) { - self.init_mmio(&self.iomem); - for irq in self.irqs.iter() { - self.init_irq(irq); - } - } - - fn realize(&self) -> util::Result<()> { - if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS { - Err(format!( - "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" - ))?; - } - if self.int_route_cap == 0 { - Err("hpet.hpet-intcap property not initialized")?; - } - - self.hpet_id.set(HPETFwConfig::assign_hpet_id()?); - - // 64-bit General Capabilities and ID Register; LegacyReplacementRoute. - self.capability.set( - HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT | - 1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT | - 1 << HPET_CAP_LEG_RT_CAP_SHIFT | - HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT | - ((self.num_timers - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer - (HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns - ); - - self.init_gpio_in(2, HPETState::handle_legacy_irq); - self.init_gpio_out(from_ref(&self.pit_enabled)); - Ok(()) - } - - fn reset_hold(&self, _type: ResetType) { - for timer in self.timers.iter().take(self.num_timers) { - timer.borrow_mut().reset(); - } - - self.counter.set(0); - self.config.set(0); - self.pit_enabled.set(true); - self.hpet_offset.set(0); - - HPETFwConfig::update_hpet_cfg( - self.hpet_id.get(), - self.capability.get() as u32, - self.mmio_addr(0).unwrap(), - ); - - // to document that the RTC lowers its output on reset as well - self.rtc_irq_level.set(0); - } - - fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode<'_> { - let shift = ((addr & 4) * 8) as u32; - let len = std::cmp::min(size * 8, 64 - shift); - - addr &= !4; - let reg = if (0..=0xff).contains(&addr) { - GlobalRegister::try_from(addr).map(HPETRegister::Global) - } else { - let timer_id: usize = ((addr - 0x100) / 0x20) as usize; - if timer_id < self.num_timers { - // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) - TimerRegister::try_from(addr & 0x18) - .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) - } else { - // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) - Err(addr) - } - }; - - // reg is now a Result - // convert the Err case into HPETRegister as well - let reg = reg.unwrap_or_else(HPETRegister::Unknown); - HPETAddrDecode { shift, len, reg } - } - - fn read(&self, addr: hwaddr, size: u32) -> u64 { - // TODO: Add trace point - trace_hpet_ram_read(addr) - let HPETAddrDecode { shift, reg, .. } = self.decode(addr, size); - - use GlobalRegister::*; - use HPETRegister::*; - (match reg { - Timer(timer, tn_reg) => timer.borrow_mut().read(tn_reg), - Global(CAP) => self.capability.get(), /* including HPET_PERIOD 0x004 */ - Global(CFG) => self.config.get(), - Global(INT_STATUS) => self.int_status.get(), - Global(COUNTER) => { - // TODO: Add trace point - // trace_hpet_ram_read_reading_counter(addr & 4, cur_tick) - if self.is_hpet_enabled() { - self.get_ticks() - } else { - self.counter.get() - } - } - Unknown(_) => { - // TODO: Add trace point- trace_hpet_ram_read_invalid() - 0 - } - }) >> shift - } - - fn write(&self, addr: hwaddr, value: u64, size: u32) { - let HPETAddrDecode { shift, len, reg } = self.decode(addr, size); - - // TODO: Add trace point - trace_hpet_ram_write(addr, value) - use GlobalRegister::*; - use HPETRegister::*; - match reg { - Timer(timer, tn_reg) => timer.borrow_mut().write(tn_reg, value, shift, len), - Global(CAP) => {} // General Capabilities and ID Register: Read Only - Global(CFG) => self.set_cfg_reg(shift, len, value), - Global(INT_STATUS) => self.set_int_status_reg(shift, len, value), - Global(COUNTER) => self.set_counter_reg(shift, len, value), - Unknown(_) => { - // TODO: Add trace point - trace_hpet_ram_write_invalid() - } - } - } - - fn pre_save(&self) -> Result<(), migration::Infallible> { - if self.is_hpet_enabled() { - self.counter.set(self.get_ticks()); - } - - /* - * The number of timers must match on source and destination, but it was - * also added to the migration stream. Check that it matches the value - * that was configured. - */ - self.num_timers_save.set(self.num_timers as u8); - Ok(()) - } - - fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> { - for timer in self.timers.iter().take(self.num_timers) { - let mut t = timer.borrow_mut(); - - t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp); - t.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND; - } - - // Recalculate the offset between the main counter and guest time - if !self.hpet_offset_saved { - self.hpet_offset - .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); - } - - Ok(()) - } - - fn is_rtc_irq_level_needed(&self) -> bool { - self.rtc_irq_level.get() != 0 - } - - fn is_offset_needed(&self) -> bool { - self.is_hpet_enabled() && self.hpet_offset_saved - } - - fn validate_num_timers(&self, _version_id: u8) -> bool { - self.num_timers == self.num_timers_save.get().into() - } -} - -qom_isa!(HPETState: SysBusDevice, DeviceState, Object); - -unsafe impl ObjectType for HPETState { - // No need for HPETClass. Just like OBJECT_DECLARE_SIMPLE_TYPE in C. - type Class = ::Class; - const TYPE_NAME: &'static CStr = crate::TYPE_HPET; -} - -impl ObjectImpl for HPETState { - type ParentType = SysBusDevice; - - const INSTANCE_INIT: Option)> = Some(Self::init); - const INSTANCE_POST_INIT: Option = Some(Self::post_init); - const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; -} - -// TODO: Make these properties user-configurable! -declare_properties! { - HPET_PROPERTIES, - define_property!( - c"timers", - HPETState, - num_timers, - unsafe { &qdev_prop_usize }, - u8, - default = HPET_MIN_TIMERS - ), - define_property!( - c"msi", - HPETState, - flags, - unsafe { &qdev_prop_bit }, - u32, - bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, - default = false, - ), - define_property!( - c"hpet-intcap", - HPETState, - int_route_cap, - unsafe { &qdev_prop_uint32 }, - u32, - default = 0 - ), - define_property!( - c"hpet-offset-saved", - HPETState, - hpet_offset_saved, - unsafe { &qdev_prop_bool }, - bool, - default = true - ), -} - -static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"hpet/rtc_irq_level") - .version_id(1) - .minimum_version_id(1) - .needed(&HPETState::is_rtc_irq_level_needed) - .fields(vmstate_fields! { - vmstate_of!(HPETState, rtc_irq_level), - }) - .build(); - -static VMSTATE_HPET_OFFSET: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"hpet/offset") - .version_id(1) - .minimum_version_id(1) - .needed(&HPETState::is_offset_needed) - .fields(vmstate_fields! { - vmstate_of!(HPETState, hpet_offset), - }) - .build(); - -const VMSTATE_HPET_TIMER: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"hpet_timer") - .version_id(1) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(HPETTimer, index), - vmstate_of!(HPETTimer, config), - vmstate_of!(HPETTimer, cmp), - vmstate_of!(HPETTimer, fsb), - vmstate_of!(HPETTimer, period), - vmstate_of!(HPETTimer, wrap_flag), - vmstate_of!(HPETTimer, qemu_timer), - }) - .build(); -impl_vmstate_struct!(HPETTimer, VMSTATE_HPET_TIMER); - -const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match"; - -const VMSTATE_HPET: VMStateDescription = - VMStateDescriptionBuilder::::new() - .name(c"hpet") - .version_id(2) - .minimum_version_id(2) - .pre_save(&HPETState::pre_save) - .post_load(&HPETState::post_load) - .fields(vmstate_fields! { - vmstate_of!(HPETState, config), - vmstate_of!(HPETState, int_status), - vmstate_of!(HPETState, counter), - vmstate_of!(HPETState, num_timers_save), - vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), - vmstate_of!(HPETState, timers[0 .. num_timers_save], HPETState::validate_num_timers).with_version_id(0), - }) - .subsections(vmstate_subsections!( - VMSTATE_HPET_RTC_IRQ_LEVEL, - VMSTATE_HPET_OFFSET, - )) - .build(); - -// SAFETY: HPET_PROPERTIES is a valid Property array constructed with the -// hwcore::declare_properties macro. -unsafe impl hwcore::DevicePropertiesImpl for HPETState { - const PROPERTIES: &'static [Property] = &HPET_PROPERTIES; -} - -impl DeviceImpl for HPETState { - const VMSTATE: Option> = Some(VMSTATE_HPET); - const REALIZE: Option util::Result<()>> = Some(Self::realize); -} - -impl ResettablePhasesImpl for HPETState { - const HOLD: Option = Some(Self::reset_hold); -} - -impl SysBusDeviceImpl for HPETState {} diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs deleted file mode 100644 index e569b57b9..000000000 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::ptr::addr_of_mut; - -use common::Zeroable; - -/// Each `HPETState` represents a Event Timer Block. The v1 spec supports -/// up to 8 blocks. QEMU only uses 1 block (in PC machine). -const HPET_MAX_NUM_EVENT_TIMER_BLOCK: usize = 8; - -#[repr(C, packed)] -#[derive(Copy, Clone, Default)] -pub struct HPETFwEntry { - pub event_timer_block_id: u32, - pub address: u64, - pub min_tick: u16, - pub page_prot: u8, -} -unsafe impl Zeroable for HPETFwEntry {} - -#[repr(C, packed)] -#[derive(Copy, Clone, Default)] -pub struct HPETFwConfig { - pub count: u8, - pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK], -} -unsafe impl Zeroable for HPETFwConfig {} - -#[allow(non_upper_case_globals)] -#[no_mangle] -pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { - count: u8::MAX, - ..Zeroable::ZERO -}; - -impl HPETFwConfig { - pub(crate) fn assign_hpet_id() -> Result { - assert!(bql::is_locked()); - // SAFETY: all accesses go through these methods, which guarantee - // that the accesses are protected by the BQL. - let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; - - if fw_cfg.count == u8::MAX { - // first instance - fw_cfg.count = 0; - } - - if fw_cfg.count == 8 { - Err("Only 8 instances of HPET are allowed")?; - } - - let id: usize = fw_cfg.count.into(); - fw_cfg.count += 1; - Ok(id) - } - - pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) { - assert!(bql::is_locked()); - // SAFETY: all accesses go through these methods, which guarantee - // that the accesses are protected by the BQL. - let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; - - fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id; - fw_cfg.hpet[hpet_id].address = address; - } -} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs deleted file mode 100644 index a95cf14ac..000000000 --- a/rust/hw/timer/hpet/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -//! # HPET QEMU Device Model -//! -//! This library implements a device model for the IA-PC HPET (High -//! Precision Event Timers) device in QEMU. - -pub mod device; -pub mod fw_cfg; - -pub const TYPE_HPET: &::std::ffi::CStr = c"hpet"; diff --git a/rust/hw/timer/meson.build b/rust/hw/timer/meson.build deleted file mode 100644 index 22a84f155..000000000 --- a/rust/hw/timer/meson.build +++ /dev/null @@ -1 +0,0 @@ -subdir('hpet') diff --git a/rust/meson.build b/rust/meson.build deleted file mode 100644 index c7bd6aba4..000000000 --- a/rust/meson.build +++ /dev/null @@ -1,46 +0,0 @@ -subproject('anyhow-1-rs', required: true) -subproject('bilge-0.2-rs', required: true) -subproject('bilge-impl-0.2-rs', required: true) -subproject('foreign-0.3-rs', required: true) -subproject('libc-0.2-rs', required: true) - -anyhow_rs = dependency('anyhow-1-rs') -bilge_rs = dependency('bilge-0.2-rs') -bilge_impl_rs = dependency('bilge-impl-0.2-rs') -foreign_rs = dependency('foreign-0.3-rs') -libc_rs = dependency('libc-0.2-rs') - -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) - -quote_rs_native = dependency('quote-1-rs', native: true) -syn_rs_native = dependency('syn-2-rs', native: true) -proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) - -genrs = [] - -subdir('qemu-macros') - -subdir('common') -subdir('bits') -subdir('util') -subdir('migration') -subdir('bql') -subdir('qom') -subdir('system') -subdir('chardev') -subdir('hw/core') -subdir('tests') - -subdir('hw') - -cargo = find_program('cargo', required: false) - -if cargo.found() - run_target('rustfmt', - command: [config_host['MESON'], 'devenv', - '--workdir', '@CURRENT_SOURCE_DIR@', - cargo, 'fmt'], - depends: genrs) -endif diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml deleted file mode 100644 index 708bfaaa6..000000000 --- a/rust/migration/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "migration" -version = "0.1.0" -description = "Rust bindings for QEMU/migration" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../common" } -util = { path = "../util" } - -[lints] -workspace = true diff --git a/rust/migration/build.rs b/rust/migration/build.rs deleted file mode 120000 index 71a316788..000000000 --- a/rust/migration/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/migration/meson.build b/rust/migration/meson.build deleted file mode 100644 index 5e820d43f..000000000 --- a/rust/migration/meson.build +++ /dev/null @@ -1,53 +0,0 @@ -_migration_bindgen_args = [] -c_bitfields = [ - 'MigrationPolicy', - 'MigrationPriority', - 'VMStateFlags', -] -foreach enum : c_bitfields - _migration_bindgen_args += ['--bitfield-enum', enum] -endforeach -# -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_migration_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _migration_bindgen_args, - ) - -_migration_rs = static_library( - 'migration', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/vmstate.rs', - ], - {'.' : _migration_bindings_inc_rs}, - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_util_rs], - dependencies: [common_rs], -) - -migration_rs = declare_dependency(link_with: [_migration_rs], - dependencies: [migration, qemuutil]) - -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-migration-rs-doctests', - _migration_rs, - protocol: 'rust', - dependencies: migration_rs, - suite: ['doc', 'rust']) diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs deleted file mode 100644 index 8ce13a900..000000000 --- a/rust/migration/src/bindings.rs +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -use common::Zeroable; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -unsafe impl Send for VMStateDescription {} -unsafe impl Sync for VMStateDescription {} - -unsafe impl Send for VMStateField {} -unsafe impl Sync for VMStateField {} - -unsafe impl Send for VMStateInfo {} -unsafe impl Sync for VMStateInfo {} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for VMStateFlags { - fn default() -> Self { - Self(0) - } -} - -unsafe impl Zeroable for VMStateFlags {} -unsafe impl Zeroable for VMStateField {} -unsafe impl Zeroable for VMStateDescription {} diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs deleted file mode 100644 index 5f51dde44..000000000 --- a/rust/migration/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub mod bindings; - -pub mod vmstate; -pub use vmstate::*; diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs deleted file mode 100644 index c05c4a1fd..000000000 --- a/rust/migration/src/vmstate.rs +++ /dev/null @@ -1,715 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Helper macros to declare migration state for device models. -//! -//! This module includes four families of macros: -//! -//! * [`vmstate_unused!`](crate::vmstate_unused) and -//! [`vmstate_of!`](crate::vmstate_of), which are used to express the -//! migration format for a struct. This is based on the [`VMState`] trait, -//! which is defined by all migratable types. -//! -//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward), -//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and -//! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the -//! definition of the [`VMState`] trait (respectively for transparent structs, -//! nested structs and `bilge`-defined types) -//! -//! * helper macros to declare a device model state struct, in particular -//! [`vmstate_subsections`](crate::vmstate_subsections) and -//! [`vmstate_fields`](crate::vmstate_fields). -//! -//! * direct equivalents to the C macros declared in -//! `include/migration/vmstate.h`. These are not type-safe and only provide -//! functionality that is missing from `vmstate_of!`. - -pub use std::convert::Infallible; -use std::{ - error::Error, - ffi::{c_int, c_void, CStr}, - fmt, io, - marker::PhantomData, - mem, - ptr::{addr_of, NonNull}, -}; - -use common::{ - callbacks::FnCall, - errno::{into_neg_errno, Errno}, - Zeroable, -}; - -use crate::bindings::{self, VMStateFlags}; -pub use crate::bindings::{MigrationPriority, VMStateField}; - -/// This macro is used to call a function with a generic argument bound -/// to the type of a field. The function must take a -/// [`PhantomData`]`` argument; `T` is the type of -/// field `$field` in the `$typ` type. -/// -/// # Examples -/// -/// ``` -/// # use migration::call_func_with_field; -/// # use core::marker::PhantomData; -/// const fn size_of_field(_: PhantomData) -> usize { -/// std::mem::size_of::() -/// } -/// -/// struct Foo { -/// x: u16, -/// }; -/// // calls size_of_field::() -/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); -/// ``` -#[macro_export] -macro_rules! call_func_with_field { - // Based on the answer by user steffahn (Frank Steffahn) at - // https://users.rust-lang.org/t/inferring-type-of-field/122857 - // and used under MIT license - ($func:expr, $typ:ty, $($field:tt).+) => { - $func(loop { - #![allow(unreachable_code)] - const fn phantom__(_: &T) -> ::core::marker::PhantomData { ::core::marker::PhantomData } - // Unreachable code is exempt from checks on uninitialized values. - // Use that trick to infer the type of this PhantomData. - break ::core::marker::PhantomData; - break phantom__(&{ let value__: $typ; value__.$($field).+ }); - }) - }; -} - -/// A trait for types that can be included in a device's migration stream. It -/// provides the base contents of a `VMStateField` (minus the name and offset). -/// -/// # Safety -/// -/// The contents of this trait go straight into structs that are parsed by C -/// code and used to introspect into other structs. Generally, you don't need -/// to implement it except via macros that do it for you, such as -/// `impl_vmstate_bitsized!`. -pub unsafe trait VMState { - /// The base contents of a `VMStateField` (minus the name and offset) for - /// the type that is implementing the trait. - const BASE: VMStateField; - - /// A flag that is added to another field's `VMStateField` to specify the - /// length's type in a variable-sized array. If this is not a supported - /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it - /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a - /// compile-time error. - const VARRAY_FLAG: VMStateFlags = { - panic!("invalid type for variable-sized array"); - }; -} - -/// Internal utility function to retrieve a type's `VMStateField`; -/// used by [`vmstate_of!`](crate::vmstate_of). -pub const fn vmstate_base(_: PhantomData) -> VMStateField { - T::BASE -} - -/// Internal utility function to retrieve a type's `VMStateFlags` when it -/// is used as the element count of a `VMSTATE_VARRAY`; used by -/// [`vmstate_of!`](crate::vmstate_of). -pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags { - T::VARRAY_FLAG -} - -/// Return the `VMStateField` for a field of a struct. The field must be -/// visible in the current scope. -/// -/// Only a limited set of types is supported out of the box: -/// * scalar types (integer and `bool`) -/// * the C struct `QEMUTimer` -/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, -/// [`BqlCell`], [`BqlRefCell`]) -/// * a raw pointer to any of the above -/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above -/// * an array of any of the above -/// -/// In order to support other types, the trait `VMState` must be implemented -/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward), -/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and -/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. -/// -/// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html -/// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html -/// [`Owned`]: ../../qom/qom/struct.Owned.html -#[macro_export] -macro_rules! vmstate_of { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { - $crate::bindings::VMStateField { - name: ::core::concat!(::core::stringify!($field_name), "\0") - .as_bytes() - .as_ptr() as *const ::std::os::raw::c_char, - offset: ::std::mem::offset_of!($struct_name, $field_name), - $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? - $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? - // The calls to `call_func_with_field!` are the magic that - // computes most of the VMStateField from the type of the field. - ..$crate::call_func_with_field!( - $crate::vmstate::vmstate_base, - $struct_name, - $field_name - )$(.with_varray_flag($crate::call_func_with_field!( - $crate::vmstate::vmstate_varray_flag, - $struct_name, - $num)) - $(.with_varray_multiply($factor))?)? - } - }; -} - -pub trait VMStateFlagsExt { - const VMS_VARRAY_FLAGS: VMStateFlags; -} - -impl VMStateFlagsExt for VMStateFlags { - const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( - VMStateFlags::VMS_VARRAY_INT32.0 - | VMStateFlags::VMS_VARRAY_UINT8.0 - | VMStateFlags::VMS_VARRAY_UINT16.0 - | VMStateFlags::VMS_VARRAY_UINT32.0, - ); -} - -// Add a couple builder-style methods to VMStateField, allowing -// easy derivation of VMStateField constants from other types. -impl VMStateField { - #[must_use] - pub const fn with_version_id(mut self, version_id: i32) -> Self { - assert!(version_id >= 0); - self.version_id = version_id; - self - } - - #[must_use] - pub const fn with_array_flag(mut self, num: usize) -> Self { - assert!(num <= 0x7FFF_FFFFusize); - assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); - assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); - if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { - self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); - // VMS_ARRAY_OF_POINTER flag stores the size of pointer. - // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. - // Resize if more smart pointers are supported. - self.size = std::mem::size_of::(); - } - self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); - self.num = num as i32; - self - } - - #[must_use] - pub const fn with_pointer_flag(mut self) -> Self { - assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); - self - } - - #[must_use] - pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self { - self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); - self.flags = VMStateFlags(self.flags.0 | flag.0); - self.num = 0; // varray uses num_offset instead of num. - self - } - - #[must_use] - #[allow(unused_mut)] - pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self { - assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); - self.with_varray_flag_unchecked(flag) - } - - #[must_use] - pub const fn with_varray_multiply(mut self, num: u32) -> Self { - assert!(num <= 0x7FFF_FFFFu32); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); - self.num = num as i32; - self - } -} - -/// This macro can be used (by just passing it a type) to forward the `VMState` -/// trait to the first field of a tuple. This is a workaround for lack of -/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. -/// -/// # Examples -/// -/// ``` -/// # use migration::impl_vmstate_forward; -/// pub struct Fifo([u8; 16]); -/// impl_vmstate_forward!(Fifo); -/// ``` -#[macro_export] -macro_rules! impl_vmstate_forward { - // This is similar to impl_vmstate_transparent below, but it - // uses the same trick as vmstate_of! to obtain the type of - // the first field of the tuple - ($tuple:ty) => { - unsafe impl $crate::vmstate::VMState for $tuple { - const BASE: $crate::bindings::VMStateField = - $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); - } - }; -} - -// Transparent wrappers: just use the internal type - -#[macro_export] -macro_rules! impl_vmstate_transparent { - ($type:ty where $base:tt: VMState $($where:tt)*) => { - unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { - const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { - size: mem::size_of::<$type>(), - ..<$base as $crate::vmstate::VMState>::BASE - }; - const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG; - } - }; -} - -impl_vmstate_transparent!(std::cell::Cell where T: VMState); -impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); -impl_vmstate_transparent!(std::pin::Pin where T: VMState); -impl_vmstate_transparent!(common::Opaque where T: VMState); - -#[macro_export] -macro_rules! impl_vmstate_bitsized { - ($type:ty) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::BASE; - const VARRAY_FLAG: $crate::bindings::VMStateFlags = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::VARRAY_FLAG; - } - }; -} - -// Scalar types using predefined VMStateInfos - -macro_rules! impl_vmstate_scalar { - ($info:ident, $type:ty$(, $varray_flag:ident)?) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField { - info: addr_of!(bindings::$info), - size: mem::size_of::<$type>(), - flags: $crate::vmstate::VMStateFlags::VMS_SINGLE, - ..::common::zeroable::Zeroable::ZERO - }; - $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? - } - }; -} - -impl_vmstate_scalar!(vmstate_info_bool, bool); -impl_vmstate_scalar!(vmstate_info_int8, i8); -impl_vmstate_scalar!(vmstate_info_int16, i16); -impl_vmstate_scalar!(vmstate_info_int32, i32); -impl_vmstate_scalar!(vmstate_info_int64, i64); -impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); -impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); -impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); -impl_vmstate_scalar!(vmstate_info_uint64, u64); -impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer); - -#[macro_export] -macro_rules! impl_vmstate_c_struct { - ($type:ty, $vmsd:expr) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField { - vmsd: ::std::ptr::addr_of!($vmsd), - size: ::std::mem::size_of::<$type>(), - flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..::common::zeroable::Zeroable::ZERO - }; - } - }; -} - -// Pointer types using the underlying type's VMState plus VMS_POINTER -// Note that references are not supported, though references to cells -// could be allowed. - -#[macro_export] -macro_rules! impl_vmstate_pointer { - ($type:ty where $base:tt: VMState $($where:tt)*) => { - unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* { - const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag(); - } - }; -} - -impl_vmstate_pointer!(*const T where T: VMState); -impl_vmstate_pointer!(*mut T where T: VMState); -impl_vmstate_pointer!(NonNull where T: VMState); - -// Unlike C pointers, Box is always non-null therefore there is no need -// to specify VMS_ALLOC. -impl_vmstate_pointer!(Box where T: VMState); - -// Arrays using the underlying type's VMState plus -// VMS_ARRAY/VMS_ARRAY_OF_POINTER - -unsafe impl VMState for [T; N] { - const BASE: VMStateField = ::BASE.with_array_flag(N); -} - -#[doc(alias = "VMSTATE_UNUSED")] -#[macro_export] -macro_rules! vmstate_unused { - ($size:expr) => {{ - $crate::bindings::VMStateField { - name: c"unused".as_ptr(), - size: $size, - info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, - flags: $crate::bindings::VMStateFlags::VMS_BUFFER, - ..::common::Zeroable::ZERO - } - }}; -} - -pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( - opaque: *mut c_void, - version_id: c_int, -) -> bool { - // SAFETY: the function is used in T's implementation of VMState - let owner: &T = unsafe { &*(opaque.cast::()) }; - let version: u8 = version_id.try_into().unwrap(); - F::call((owner, version)) -} - -pub type VMSFieldExistCb = unsafe extern "C" fn( - opaque: *mut std::os::raw::c_void, - version_id: std::os::raw::c_int, -) -> bool; - -#[macro_export] -macro_rules! vmstate_exist_fn { - ($struct_name:ty, $test_fn:expr) => {{ - const fn test_cb_builder__ ::common::FnCall<(&'a T, u8), bool>>( - _phantom: ::core::marker::PhantomData, - ) -> $crate::vmstate::VMSFieldExistCb { - const { assert!(F::IS_SOME) }; - $crate::vmstate::rust_vms_test_field_exists:: - } - - const fn phantom__(_: &T) -> ::core::marker::PhantomData { - ::core::marker::PhantomData - } - Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) - }}; -} - -/// Helper macro to declare a list of -/// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return -/// a pointer to the array of values it created. -#[macro_export] -macro_rules! vmstate_fields { - ($($field:expr),*$(,)*) => {{ - static _FIELDS: &[$crate::bindings::VMStateField] = &[ - $($field),*, - $crate::bindings::VMStateField { - flags: $crate::bindings::VMStateFlags::VMS_END, - ..::common::zeroable::Zeroable::ZERO - } - ]; - _FIELDS.as_ptr() - }} -} - -#[doc(alias = "VMSTATE_VALIDATE")] -#[macro_export] -macro_rules! vmstate_validate { - ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { - $crate::bindings::VMStateField { - name: ::std::ffi::CStr::as_ptr($test_name), - field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn), - flags: $crate::bindings::VMStateFlags( - $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 - | $crate::bindings::VMStateFlags::VMS_ARRAY.0, - ), - num: 0, // 0 elements: no data, only run test_fn callback - ..::common::zeroable::Zeroable::ZERO - } - }; -} - -/// Helper macro to allow using a struct in [`vmstate_of!`] -/// -/// # Safety -/// -/// The [`VMStateDescription`] constant `$vmsd` must be an accurate -/// description of the struct. -#[macro_export] -macro_rules! impl_vmstate_struct { - ($type:ty, $vmsd:expr) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = { - static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref(); - - $crate::bindings::VMStateField { - vmsd: ::core::ptr::addr_of!(*VMSD), - size: ::core::mem::size_of::<$type>(), - flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - ..common::Zeroable::ZERO - } - }; - } - }; -} - -/// A transparent wrapper type for the `subsections` field of -/// [`VMStateDescription`]. -/// -/// This is necessary to be able to declare subsection descriptions as statics, -/// because the only way to implement `Sync` for a foreign type (and `*const` -/// pointers are foreign types in Rust) is to create a wrapper struct and -/// `unsafe impl Sync` for it. -/// -/// This struct is used in the -/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation. -#[repr(transparent)] -pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); - -unsafe impl Sync for VMStateSubsectionsWrapper {} - -/// Helper macro to declare a list of subsections ([`VMStateDescription`]) -/// into a static and return a pointer to the array of pointers it created. -#[macro_export] -macro_rules! vmstate_subsections { - ($($subsection:expr),*$(,)*) => {{ - static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ - $({ - static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get(); - ::core::ptr::addr_of!(_SUBSECTION) - }),*, - ::core::ptr::null() - ]); - &_SUBSECTIONS - }} -} - -pub struct VMStateDescription(bindings::VMStateDescription, PhantomData); - -// SAFETY: When a *const T is passed to the callbacks, the call itself -// is done in a thread-safe manner. The invocation is okay as long as -// T itself is `Sync`. -unsafe impl Sync for VMStateDescription {} - -#[derive(Clone)] -pub struct VMStateDescriptionBuilder(bindings::VMStateDescription, PhantomData); - -#[derive(Debug)] -pub struct InvalidError; - -impl Error for InvalidError {} - -impl std::fmt::Display for InvalidError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid migration data") - } -} - -impl From for Errno { - fn from(_value: InvalidError) -> Errno { - io::ErrorKind::InvalidInput.into() - } -} - -unsafe extern "C" fn vmstate_no_version_cb< - T, - F: for<'a> FnCall<(&'a T,), Result<(), impl Into>>, ->( - opaque: *mut c_void, -) -> c_int { - // SAFETY: the function is used in T's implementation of VMState - let result = F::call((unsafe { &*(opaque.cast::()) },)); - into_neg_errno(result) -} - -unsafe extern "C" fn vmstate_post_load_cb< - T, - F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into>>, ->( - opaque: *mut c_void, - version_id: c_int, -) -> c_int { - // SAFETY: the function is used in T's implementation of VMState - let owner: &T = unsafe { &*(opaque.cast::()) }; - let version: u8 = version_id.try_into().unwrap(); - let result = F::call((owner, version)); - into_neg_errno(result) -} - -unsafe extern "C" fn vmstate_needed_cb FnCall<(&'a T,), bool>>( - opaque: *mut c_void, -) -> bool { - // SAFETY: the function is used in T's implementation of VMState - F::call((unsafe { &*(opaque.cast::()) },)) -} - -unsafe extern "C" fn vmstate_dev_unplug_pending_cb FnCall<(&'a T,), bool>>( - opaque: *mut c_void, -) -> bool { - // SAFETY: the function is used in T's implementation of VMState - F::call((unsafe { &*(opaque.cast::()) },)) -} - -impl VMStateDescriptionBuilder { - #[must_use] - pub const fn name(mut self, name_str: &CStr) -> Self { - self.0.name = ::std::ffi::CStr::as_ptr(name_str); - self - } - - #[must_use] - pub const fn unmigratable(mut self) -> Self { - self.0.unmigratable = true; - self - } - - #[must_use] - pub const fn early_setup(mut self) -> Self { - self.0.early_setup = true; - self - } - - #[must_use] - pub const fn version_id(mut self, version: u8) -> Self { - self.0.version_id = version as c_int; - self - } - - #[must_use] - pub const fn minimum_version_id(mut self, min_version: u8) -> Self { - self.0.minimum_version_id = min_version as c_int; - self - } - - #[must_use] - pub const fn priority(mut self, priority: MigrationPriority) -> Self { - self.0.priority = priority; - self - } - - #[must_use] - pub const fn pre_load FnCall<(&'a T,), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.pre_load = if F::IS_SOME { - Some(vmstate_no_version_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn post_load FnCall<(&'a T, u8), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.post_load = if F::IS_SOME { - Some(vmstate_post_load_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn pre_save FnCall<(&'a T,), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.pre_save = if F::IS_SOME { - Some(vmstate_no_version_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn post_save FnCall<(&'a T,), Result<(), impl Into>>>( - mut self, - _f: &F, - ) -> Self { - self.0.post_save = if F::IS_SOME { - Some(vmstate_no_version_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn needed FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { - self.0.needed = if F::IS_SOME { - Some(vmstate_needed_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn unplug_pending FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self { - self.0.dev_unplug_pending = if F::IS_SOME { - Some(vmstate_dev_unplug_pending_cb::) - } else { - None - }; - self - } - - #[must_use] - pub const fn fields(mut self, fields: *const VMStateField) -> Self { - self.0.fields = fields; - self - } - - #[must_use] - pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self { - self.0.subsections = subs.0.as_ptr(); - self - } - - #[must_use] - pub const fn build(self) -> VMStateDescription { - VMStateDescription::(self.0, PhantomData) - } - - #[must_use] - pub const fn new() -> Self { - Self(bindings::VMStateDescription::ZERO, PhantomData) - } -} - -impl Default for VMStateDescriptionBuilder { - fn default() -> Self { - Self::new() - } -} - -impl VMStateDescription { - pub const fn get(&self) -> bindings::VMStateDescription { - self.0 - } - - pub const fn as_ref(&self) -> &bindings::VMStateDescription { - &self.0 - } -} diff --git a/rust/migration/wrapper.h b/rust/migration/wrapper.h deleted file mode 100644 index daf316aed..000000000 --- a/rust/migration/wrapper.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2024 Linaro Ltd. - * - * Authors: Manos Pitsidianakis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" -#include "migration/vmstate.h" diff --git a/rust/qemu-macros/Cargo.toml b/rust/qemu-macros/Cargo.toml deleted file mode 100644 index 3b6f1d337..000000000 --- a/rust/qemu-macros/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "qemu_macros" -version = "0.1.0" -authors = ["Manos Pitsidianakis "] -description = "Rust bindings for QEMU - Utility macros" -resolver = "2" -publish = false - -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1" -quote = "1" -syn = { version = "2", features = ["extra-traits"] } - -[lints] -workspace = true diff --git a/rust/qemu-macros/meson.build b/rust/qemu-macros/meson.build deleted file mode 100644 index d0b2992e2..000000000 --- a/rust/qemu-macros/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -_qemu_macros_rs = rust.proc_macro( - 'qemu_macros', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: [ - '--cfg', 'use_fallback', - '--cfg', 'feature="syn-error"', - '--cfg', 'feature="proc-macro"', - ], - dependencies: [ - proc_macro2_rs_native, - quote_rs_native, - syn_rs_native, - ], -) - -qemu_macros = declare_dependency( - link_with: _qemu_macros_rs, -) - -rust.test('rust-qemu-macros-tests', _qemu_macros_rs, - suite: ['unit', 'rust']) diff --git a/rust/qemu-macros/src/bits.rs b/rust/qemu-macros/src/bits.rs deleted file mode 100644 index a80a3b9fe..000000000 --- a/rust/qemu-macros/src/bits.rs +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later - -// shadowing is useful together with "if let" -#![allow(clippy::shadow_unrelated)] - -use proc_macro2::{ - Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, -}; -use syn::Error; - -pub struct BitsConstInternal { - typ: TokenTree, -} - -fn paren(ts: TokenStream) -> TokenTree { - TT::Group(Group::new(Delimiter::Parenthesis, ts)) -} - -fn ident(s: &'static str) -> TokenTree { - TT::Ident(Ident::new(s, Span::call_site())) -} - -fn punct(ch: char) -> TokenTree { - TT::Punct(Punct::new(ch, Spacing::Alone)) -} - -/// Implements a recursive-descent parser that translates Boolean expressions on -/// bitmasks to invocations of `const` functions defined by the `bits!` macro. -impl BitsConstInternal { - // primary ::= '(' or ')' - // | ident - // | '!' ident - fn parse_primary( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - let next = match tok { - TT::Group(ref g) => { - if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { - return Err(Error::new(g.span(), "expected parenthesis")); - } - let mut stream = g.stream().into_iter(); - let Some(first_tok) = stream.next() else { - return Err(Error::new(g.span(), "expected operand, found ')'")); - }; - let mut output = TokenStream::new(); - // start from the lowest precedence - let next = self.parse_or(first_tok, &mut stream, &mut output)?; - if let Some(tok) = next { - return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); - } - out.extend(Some(paren(output))); - it.next() - } - TT::Ident(_) => { - let mut output = TokenStream::new(); - output.extend([ - self.typ.clone(), - TT::Punct(Punct::new(':', Spacing::Joint)), - TT::Punct(Punct::new(':', Spacing::Joint)), - tok, - ]); - out.extend(Some(paren(output))); - it.next() - } - TT::Punct(ref p) => { - if p.as_char() != '!' { - return Err(Error::new(p.span(), "expected operand")); - } - let Some(rhs_tok) = it.next() else { - return Err(Error::new(p.span(), "expected operand at end of input")); - }; - let next = self.parse_primary(rhs_tok, it, out)?; - out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); - next - } - _ => { - return Err(Error::new(tok.span(), "unexpected literal")); - } - }; - Ok(next) - } - - fn parse_binop< - F: Fn( - &Self, - TokenTree, - &mut dyn Iterator, - &mut TokenStream, - ) -> Result, Error>, - >( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ch: char, - f: F, - method: &'static str, - ) -> Result, Error> { - let mut next = f(self, tok, it, out)?; - while next.is_some() { - let op = next.as_ref().unwrap(); - let TT::Punct(ref p) = op else { break }; - if p.as_char() != ch { - break; - } - - let Some(rhs_tok) = it.next() else { - return Err(Error::new(p.span(), "expected operand at end of input")); - }; - let mut rhs = TokenStream::new(); - next = f(self, rhs_tok, it, &mut rhs)?; - out.extend([punct('.'), ident(method), paren(rhs)]); - } - Ok(next) - } - - // sub ::= primary ('-' primary)* - pub fn parse_sub( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") - } - - // and ::= sub ('&' sub)* - fn parse_and( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") - } - - // xor ::= and ('&' and)* - fn parse_xor( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") - } - - // or ::= xor ('|' xor)* - pub fn parse_or( - &self, - tok: TokenTree, - it: &mut dyn Iterator, - out: &mut TokenStream, - ) -> Result, Error> { - self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") - } - - pub fn parse( - it: &mut dyn Iterator, - ) -> Result { - let mut pos = Span::call_site(); - let mut typ = proc_macro2::TokenStream::new(); - - // Gobble everything up to an `@` sign, which is followed by a - // parenthesized expression; that is, all token trees except the - // last two form the type. - let next = loop { - let tok = it.next(); - if let Some(ref t) = tok { - pos = t.span(); - } - match tok { - None => break None, - Some(TT::Punct(ref p)) if p.as_char() == '@' => { - let tok = it.next(); - if let Some(ref t) = tok { - pos = t.span(); - } - break tok; - } - Some(x) => typ.extend(Some(x)), - } - }; - - let Some(tok) = next else { - return Err(Error::new( - pos, - "expected expression, do not call this macro directly", - )); - }; - let TT::Group(ref _group) = tok else { - return Err(Error::new( - tok.span(), - "expected parenthesis, do not call this macro directly", - )); - }; - let mut out = TokenStream::new(); - let state = Self { - typ: TT::Group(Group::new(Delimiter::None, typ)), - }; - - let next = state.parse_primary(tok, it, &mut out)?; - - // A parenthesized expression is a single production of the grammar, - // so the input must have reached the last token. - if let Some(tok) = next { - return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); - } - Ok(out) - } -} diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs deleted file mode 100644 index 830b43269..000000000 --- a/rust/qemu-macros/src/lib.rs +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use proc_macro::TokenStream; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - parse::Parse, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, - token::Comma, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, - Variant, -}; -mod bits; -use bits::BitsConstInternal; - -#[cfg(test)] -mod tests; - -fn get_fields<'a>( - input: &'a DeriveInput, - msg: &str, -) -> Result<&'a Punctuated, Error> { - let Data::Struct(ref s) = &input.data else { - return Err(Error::new( - input.ident.span(), - format!("Struct required for {msg}"), - )); - }; - let Fields::Named(ref fs) = &s.fields else { - return Err(Error::new( - input.ident.span(), - format!("Named fields required for {msg}"), - )); - }; - Ok(&fs.named) -} - -fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> { - let Data::Struct(ref s) = &input.data else { - return Err(Error::new( - input.ident.span(), - format!("Struct required for {msg}"), - )); - }; - let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else { - return Err(Error::new( - s.fields.span(), - format!("Tuple struct required for {msg}"), - )); - }; - if unnamed.len() != 1 { - return Err(Error::new( - s.fields.span(), - format!("A single field is required for {msg}"), - )); - } - Ok(&unnamed[0]) -} - -fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { - let expected = parse_quote! { #[repr(C)] }; - - if input.attrs.iter().any(|attr| attr == &expected) { - Ok(()) - } else { - Err(Error::new( - input.ident.span(), - format!("#[repr(C)] required for {msg}"), - )) - } -} - -fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { - let expected = parse_quote! { #[repr(transparent)] }; - - if input.attrs.iter().any(|attr| attr == &expected) { - Ok(()) - } else { - Err(Error::new( - input.ident.span(), - format!("#[repr(transparent)] required for {msg}"), - )) - } -} - -fn derive_object_or_error(input: DeriveInput) -> Result { - is_c_repr(&input, "#[derive(Object)]")?; - - let name = &input.ident; - let parent = &get_fields(&input, "#[derive(Object)]")? - .get(0) - .ok_or_else(|| { - Error::new( - input.ident.span(), - "#[derive(Object)] requires a parent field", - ) - })? - .ident; - - Ok(quote! { - ::common::assert_field_type!(#name, #parent, - ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>); - - ::util::module_init! { - MODULE_INIT_QOM => unsafe { - ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO); - } - } - }) -} - -#[proc_macro_derive(Object)] -pub fn derive_object(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_object_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -fn derive_opaque_or_error(input: DeriveInput) -> Result { - is_transparent_repr(&input, "#[derive(Wrapper)]")?; - - let name = &input.ident; - let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?; - let typ = &field.ty; - - Ok(quote! { - unsafe impl ::common::opaque::Wrapper for #name { - type Wrapped = <#typ as ::common::opaque::Wrapper>::Wrapped; - } - impl #name { - pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { - let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); - unsafe { ptr.as_ref() } - } - - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { - self.0.as_mut_ptr() - } - - pub const fn as_ptr(&self) -> *const ::Wrapped { - self.0.as_ptr() - } - - pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void { - self.0.as_void_ptr() - } - - pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { - slot.cast() - } - } - }) -} - -#[derive(Debug)] -enum DevicePropertyName { - CStr(syn::LitCStr), - Str(syn::LitStr), -} - -#[derive(Debug)] -struct DeviceProperty { - rename: Option, - defval: Option, -} - -impl Parse for DeviceProperty { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _: syn::Token![#] = input.parse()?; - let bracketed; - _ = syn::bracketed!(bracketed in input); - let attribute = bracketed.parse::()?; - debug_assert_eq!(&attribute.to_string(), "property"); - let mut retval = Self { - rename: None, - defval: None, - }; - let content; - _ = syn::parenthesized!(content in bracketed); - while !content.is_empty() { - let value: syn::Ident = content.parse()?; - if value == "rename" { - let _: syn::Token![=] = content.parse()?; - if retval.rename.is_some() { - return Err(syn::Error::new( - value.span(), - "`rename` can only be used at most once", - )); - } - if content.peek(syn::LitStr) { - retval.rename = Some(DevicePropertyName::Str(content.parse::()?)); - } else { - retval.rename = - Some(DevicePropertyName::CStr(content.parse::()?)); - } - } else if value == "default" { - let _: syn::Token![=] = content.parse()?; - if retval.defval.is_some() { - return Err(syn::Error::new( - value.span(), - "`default` can only be used at most once", - )); - } - retval.defval = Some(content.parse()?); - } else { - return Err(syn::Error::new( - value.span(), - format!("unrecognized field `{value}`"), - )); - } - - if !content.is_empty() { - let _: syn::Token![,] = content.parse()?; - } - } - Ok(retval) - } -} - -#[proc_macro_derive(Device, attributes(property))] -pub fn derive_device(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_device_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -fn derive_device_or_error(input: DeriveInput) -> Result { - is_c_repr(&input, "#[derive(Device)]")?; - let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")? - .iter() - .flat_map(|f| { - f.attrs - .iter() - .filter(|a| a.path().is_ident("property")) - .map(|a| Ok((f.clone(), syn::parse2(a.to_token_stream())?))) - }) - .collect::, Error>>()?; - let name = &input.ident; - let mut properties_expanded = vec![]; - - for (field, prop) in properties { - let DeviceProperty { rename, defval } = prop; - let field_name = field.ident.unwrap(); - macro_rules! str_to_c_str { - ($value:expr, $span:expr) => {{ - let (value, span) = ($value, $span); - let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| { - Error::new( - span, - format!( - "Property name `{value}` cannot be represented as a C string: {err}" - ), - ) - })?; - let cstr_lit = syn::LitCStr::new(&cstr, span); - Ok(quote! { #cstr_lit }) - }}; - } - - let prop_name = rename.map_or_else( - || str_to_c_str!(field_name.to_string(), field_name.span()), - |rename| -> Result { - match rename { - DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }), - DevicePropertyName::Str(str_lit) => { - str_to_c_str!(str_lit.value(), str_lit.span()) - } - } - }, - )?; - let field_ty = field.ty.clone(); - let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE }; - let set_default = defval.is_some(); - let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 })); - properties_expanded.push(quote! { - ::hwcore::bindings::Property { - name: ::std::ffi::CStr::as_ptr(#prop_name), - info: #qdev_prop , - offset: ::core::mem::offset_of!(#name, #field_name) as isize, - set_default: #set_default, - defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 }, - ..::common::Zeroable::ZERO - } - }); - } - - Ok(quote_spanned! {input.span() => - unsafe impl ::hwcore::DevicePropertiesImpl for #name { - const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ - #(#properties_expanded),* - ]; - } - }) -} - -#[proc_macro_derive(Wrapper)] -pub fn derive_opaque(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_opaque_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -#[allow(non_snake_case)] -fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { - let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); - if let Some(repr) = repr { - let nested = repr.parse_args_with(Punctuated::::parse_terminated)?; - for meta in nested { - match meta { - Meta::Path(path) if path.is_ident("u8") => return Ok(path), - Meta::Path(path) if path.is_ident("u16") => return Ok(path), - Meta::Path(path) if path.is_ident("u32") => return Ok(path), - Meta::Path(path) if path.is_ident("u64") => return Ok(path), - _ => {} - } - } - } - - Err(Error::new( - input.ident.span(), - format!("#[repr(u8/u16/u32/u64) required for {msg}"), - )) -} - -fn get_variants(input: &DeriveInput) -> Result<&Punctuated, Error> { - let Data::Enum(ref e) = &input.data else { - return Err(Error::new( - input.ident.span(), - "Cannot derive TryInto for union or struct.", - )); - }; - if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { - return Err(Error::new( - v.fields.span(), - "Cannot derive TryInto for enum with non-unit variants.", - )); - } - Ok(&e.variants) -} - -#[rustfmt::skip::macros(quote)] -fn derive_tryinto_body( - name: &Ident, - variants: &Punctuated, - repr: &Path, -) -> Result { - let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); - - Ok(quote! { - #(const #discriminants: #repr = #name::#discriminants as #repr;)* - match value { - #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* - _ => core::result::Result::Err(value), - } - }) -} - -#[rustfmt::skip::macros(quote)] -fn derive_tryinto_or_error(input: DeriveInput) -> Result { - let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; - let name = &input.ident; - let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; - let errmsg = format!("invalid value for {name}"); - - Ok(quote! { - impl #name { - #[allow(dead_code)] - pub const fn into_bits(self) -> #repr { - self as #repr - } - - #[allow(dead_code)] - pub const fn from_bits(value: #repr) -> Self { - match ({ - #body - }) { - Ok(x) => x, - Err(_) => panic!(#errmsg), - } - } - } - impl core::convert::TryFrom<#repr> for #name { - type Error = #repr; - - #[allow(ambiguous_associated_items)] - fn try_from(value: #repr) -> Result { - #body - } - } - }) -} - -#[proc_macro_derive(TryInto)] -pub fn derive_tryinto(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - derive_tryinto_or_error(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -#[proc_macro] -pub fn bits_const_internal(ts: TokenStream) -> TokenStream { - let ts = proc_macro2::TokenStream::from(ts); - let mut it = ts.into_iter(); - - BitsConstInternal::parse(&mut it) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs deleted file mode 100644 index 9ab7eab7f..000000000 --- a/rust/qemu-macros/src/tests.rs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2025, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use quote::quote; - -use super::*; - -macro_rules! derive_compile_fail { - ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ - let input: proc_macro2::TokenStream = $input; - let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; - let derive_fn: fn(input: syn::DeriveInput) -> Result = - $derive_fn; - - let input: syn::DeriveInput = syn::parse2(input).unwrap(); - let result = derive_fn(input); - let err = result.unwrap_err().into_compile_error(); - assert_eq!( - err.to_string(), - quote! { #(#error_msg)* }.to_string() - ); - }}; -} - -macro_rules! derive_compile { - ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ - let input: proc_macro2::TokenStream = $input; - let expected: proc_macro2::TokenStream = $($expected)*; - let derive_fn: fn(input: syn::DeriveInput) -> Result = - $derive_fn; - - let input: syn::DeriveInput = syn::parse2(input).unwrap(); - let result = derive_fn(input).unwrap(); - assert_eq!(result.to_string(), expected.to_string()); - }}; -} - -#[test] -fn test_derive_device() { - // Check that repr(C) is used - derive_compile_fail!( - derive_device_or_error, - quote! { - #[derive(Device)] - struct Foo { - _unused: [u8; 0], - } - }, - "#[repr(C)] required for #[derive(Device)]" - ); - // Check that invalid/misspelled attributes raise an error - derive_compile_fail!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - struct DummyState { - #[property(defalt = true)] - migrate_clock: bool, - } - }, - "unrecognized field `defalt`" - ); - // Check that repeated attributes are not allowed: - derive_compile_fail!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - struct DummyState { - #[property(rename = "migrate-clk", rename = "migrate-clk", default = true)] - migrate_clock: bool, - } - }, - "`rename` can only be used at most once" - ); - derive_compile_fail!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - struct DummyState { - #[property(default = true, default = true)] - migrate_clock: bool, - } - }, - "`default` can only be used at most once" - ); - // Check that the field name is preserved when `rename` isn't used: - derive_compile!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - pub struct DummyState { - parent: ParentField, - #[property(default = true)] - migrate_clock: bool, - } - }, - quote! { - unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { - const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ - ::hwcore::bindings::Property { - name: ::std::ffi::CStr::as_ptr(c"migrate_clock"), - info: ::VALUE, - offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, - set_default: true, - defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, - ..::common::Zeroable::ZERO - } - ]; - } - } - ); - // Check that `rename` value is used for the property name when used: - derive_compile!( - derive_device_or_error, - quote! { - #[repr(C)] - #[derive(Device)] - pub struct DummyState { - parent: ParentField, - #[property(rename = "migrate-clk", default = true)] - migrate_clock: bool, - } - }, - quote! { - unsafe impl ::hwcore::DevicePropertiesImpl for DummyState { - const PROPERTIES: &'static [::hwcore::bindings::Property] = &[ - ::hwcore::bindings::Property { - name: ::std::ffi::CStr::as_ptr(c"migrate-clk"), - info: ::VALUE, - offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize, - set_default: true, - defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 }, - ..::common::Zeroable::ZERO - } - ]; - } - } - ); -} - -#[test] -fn test_derive_object() { - derive_compile_fail!( - derive_object_or_error, - quote! { - #[derive(Object)] - struct Foo { - _unused: [u8; 0], - } - }, - "#[repr(C)] required for #[derive(Object)]" - ); - derive_compile!( - derive_object_or_error, - quote! { - #[derive(Object)] - #[repr(C)] - struct Foo { - _unused: [u8; 0], - } - }, - quote! { - ::common::assert_field_type!( - Foo, - _unused, - ::qom::ParentField<::ParentType> - ); - ::util::module_init! { - MODULE_INIT_QOM => unsafe { - ::qom::type_register_static(&::TYPE_INFO); - } - } - } - ); -} - -#[test] -fn test_derive_tryinto() { - derive_compile_fail!( - derive_tryinto_or_error, - quote! { - #[derive(TryInto)] - struct Foo { - _unused: [u8; 0], - } - }, - "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" - ); - derive_compile!( - derive_tryinto_or_error, - quote! { - #[derive(TryInto)] - #[repr(u8)] - enum Foo { - First = 0, - Second, - } - }, - quote! { - impl Foo { - #[allow(dead_code)] - pub const fn into_bits(self) -> u8 { - self as u8 - } - - #[allow(dead_code)] - pub const fn from_bits(value: u8) -> Self { - match ({ - const First: u8 = Foo::First as u8; - const Second: u8 = Foo::Second as u8; - match value { - First => core::result::Result::Ok(Foo::First), - Second => core::result::Result::Ok(Foo::Second), - _ => core::result::Result::Err(value), - } - }) { - Ok(x) => x, - Err(_) => panic!("invalid value for Foo"), - } - } - } - - impl core::convert::TryFrom for Foo { - type Error = u8; - - #[allow(ambiguous_associated_items)] - fn try_from(value: u8) -> Result { - const First: u8 = Foo::First as u8; - const Second: u8 = Foo::Second as u8; - match value { - First => core::result::Result::Ok(Foo::First), - Second => core::result::Result::Ok(Foo::Second), - _ => core::result::Result::Err(value), - } - } - } - } - ); -} diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml deleted file mode 100644 index 060ad2ec3..000000000 --- a/rust/qom/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "qom" -version = "0.1.0" -description = "Rust bindings for QEMU/QOM" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../common" } -bql = { path = "../bql" } -migration = { path = "../migration" } -qemu_macros = { path = "../qemu-macros" } -util = { path = "../util" } - -[lints] -workspace = true diff --git a/rust/qom/build.rs b/rust/qom/build.rs deleted file mode 120000 index 71a316788..000000000 --- a/rust/qom/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/qom/meson.build b/rust/qom/meson.build deleted file mode 100644 index 40c51b71b..000000000 --- a/rust/qom/meson.build +++ /dev/null @@ -1,43 +0,0 @@ -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_qom_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common, -) - -_qom_rs = static_library( - 'qom', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/prelude.rs', - 'src/qom.rs', - ], - {'.': _qom_bindings_inc_rs} - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, qemu_macros], -) - -qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom]) - -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-qom-rs-doctests', - _qom_rs, - protocol: 'rust', - dependencies: qom_rs, - suite: ['doc', 'rust']) diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs deleted file mode 100644 index 9ffff12cd..000000000 --- a/rust/qom/src/bindings.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs deleted file mode 100644 index 24c44fc2a..000000000 --- a/rust/qom/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub use qemu_macros::Object; - -pub mod bindings; - -// preserve one-item-per-"use" syntax, it is clearer -// for prelude-like modules -#[rustfmt::skip] -pub mod prelude; - -mod qom; -pub use qom::*; diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs deleted file mode 100644 index 00a609597..000000000 --- a/rust/qom/src/prelude.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Traits and essential types intended for blanket imports. - -pub use crate::qom::InterfaceType; -pub use crate::qom::IsA; -pub use crate::qom::Object; -pub use crate::qom::ObjectCast; -pub use crate::qom::ObjectClassMethods; -pub use crate::qom::ObjectDeref; -pub use crate::qom::ObjectMethods; -pub use crate::qom::ObjectType; - -pub use crate::qom_isa; diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs deleted file mode 100644 index 5808051cd..000000000 --- a/rust/qom/src/qom.rs +++ /dev/null @@ -1,951 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to access QOM functionality from Rust. -//! -//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU -//! devices. This module makes QOM's features available in Rust through three -//! main mechanisms: -//! -//! * Automatic creation and registration of `TypeInfo` for classes that are -//! written in Rust, as well as mapping between Rust traits and QOM vtables. -//! -//! * Type-safe casting between parent and child classes, through the [`IsA`] -//! trait and methods such as [`upcast`](ObjectCast::upcast) and -//! [`downcast`](ObjectCast::downcast). -//! -//! * Automatic delegation of parent class methods to child classes. When a -//! trait uses [`IsA`] as a bound, its contents become available to all child -//! classes through blanket implementations. This works both for class methods -//! and for instance methods accessed through references or smart pointers. -//! -//! # Structure of a class -//! -//! A leaf class only needs a struct holding instance state. The struct must -//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` -//! traits that exist for its superclasses. -//! -//! If a class has subclasses, it will also provide a struct for instance data, -//! with the same characteristics as for concrete classes, but it also needs -//! additional components to support virtual methods: -//! -//! * a struct for class data, for example `DeviceClass`. This corresponds to -//! the C "class struct" and holds the vtable that is used by instances of the -//! class and its subclasses. It must start with its parent's class struct. -//! -//! * a trait for virtual method implementations, for example `DeviceImpl`. -//! Child classes implement this trait to provide their own behavior for -//! virtual methods. The trait's methods take `&self` to access instance data. -//! The traits have the appropriate specialization of `IsA<>` as a supertrait, -//! for example `IsA` for `DeviceImpl`. -//! -//! * a trait for instance methods, for example `DeviceMethods`. This trait is -//! automatically implemented for any reference or smart pointer to a device -//! instance. It calls into the vtable provides access across all subclasses -//! to methods defined for the class. -//! -//! * optionally, a trait for class methods, for example `DeviceClassMethods`. -//! This provides access to class-wide functionality that doesn't depend on -//! instance data. Like instance methods, these are automatically inherited by -//! child classes. -//! -//! # Class structures -//! -//! Each QOM class that has virtual methods describes them in a -//! _class struct_. Class structs include a parent field corresponding -//! to the vtable of the parent class, all the way up to [`ObjectClass`]. -//! -//! As mentioned above, virtual methods are defined via traits such as -//! `DeviceImpl`. Class structs do not define any trait but, conventionally, -//! all of them have a `class_init` method to initialize the virtual methods -//! based on the trait and then call the same method on the superclass. -//! -//! ```ignore -//! impl YourSubclassClass -//! { -//! pub fn class_init(&mut self) { -//! ... -//! klass.parent_class::class_init(); -//! } -//! } -//! ``` -//! -//! If a class implements a QOM interface. In that case, the function must -//! contain, for each interface, an extra forwarding call as follows: -//! -//! ```ignore -//! ResettableClass::cast::(self).class_init::(); -//! ``` -//! -//! These `class_init` functions are methods on the class rather than a trait, -//! because the bound on `T` (`DeviceImpl` in this case), will change for every -//! class struct. The functions are pointed to by the -//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default -//! implementation, in most cases it will be enough to write it as follows: -//! -//! ```ignore -//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; -//! ``` -//! -//! This design incurs a small amount of code duplication but, by not using -//! traits, it allows the flexibility of implementing bindings in any crate, -//! without incurring into violations of orphan rules for traits. - -use std::{ - ffi::{c_void, CStr}, - fmt, - marker::PhantomData, - mem::{ManuallyDrop, MaybeUninit}, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; - -use common::Opaque; -use migration::impl_vmstate_pointer; - -use crate::bindings::{ - self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, - object_new, object_ref, object_unref, TypeInfo, -}; -pub use crate::bindings::{type_register_static, ObjectClass}; - -/// A safe wrapper around [`bindings::Object`]. -#[repr(transparent)] -#[derive(Debug, common::Wrapper)] -pub struct Object(Opaque); - -unsafe impl Send for Object {} -unsafe impl Sync for Object {} - -/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct -/// or indirect parent of `Self`). -/// -/// # Safety -/// -/// The struct `Self` must be `#[repr(C)]` and must begin, directly or -/// indirectly, with a field of type `P`. This ensures that invalid casts, -/// which rely on `IsA<>` for static checking, are rejected at compile time. -pub unsafe trait IsA: ObjectType {} - -// SAFETY: it is always safe to cast to your own type -unsafe impl IsA for T {} - -/// Macro to mark superclasses of QOM classes. This enables type-safe -/// up- and downcasting. -/// -/// # Safety -/// -/// This macro is a thin wrapper around the [`IsA`] trait and performs -/// no checking whatsoever of what is declared. It is the caller's -/// responsibility to have $struct begin, directly or indirectly, with -/// a field of type `$parent`. -#[macro_export] -macro_rules! qom_isa { - ($struct:ty : $($parent:ty),* ) => { - $( - // SAFETY: it is the caller responsibility to have $parent as the - // first field - unsafe impl $crate::IsA<$parent> for $struct {} - - impl AsRef<$parent> for $struct { - fn as_ref(&self) -> &$parent { - // SAFETY: follows the same rules as for IsA, which is - // declared above. - let ptr: *const Self = self; - unsafe { &*ptr.cast::<$parent>() } - } - } - )* - }; -} - -/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though -/// it hides the standard methods of `ManuallyDrop`. -/// -/// The first field of an `ObjectType` must be of type `ParentField`. -/// (Technically, this is only necessary if there is at least one Rust -/// superclass in the hierarchy). This is to ensure that the parent field is -/// dropped after the subclass; this drop order is enforced by the C -/// `object_deinit` function. -/// -/// # Examples -/// -/// ```ignore -/// #[repr(C)] -/// #[derive(qom::Object)] -/// pub struct MyDevice { -/// parent: ParentField, -/// ... -/// } -/// ``` -#[derive(Debug)] -#[repr(transparent)] -pub struct ParentField(std::mem::ManuallyDrop); - -impl Deref for ParentField { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for ParentField { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl fmt::Display for ParentField { - #[inline(always)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0.fmt(f) - } -} - -/// This struct knows that the superclasses of the object have already been -/// initialized. -/// -/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a -/// technique that is found in several crates, the main ones probably being -/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) -/// and `generativity`. -/// -/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to -/// the lifetime argument `'init`. This, together with the `for<'...>` in -/// `[ParentInit::with]`, block any attempt of the compiler to be creative when -/// operating on types of type `ParentInit` and to extend their lifetimes. In -/// particular, it ensures that the `ParentInit` cannot be made to outlive the -/// `rust_instance_init()` function that creates it, and therefore that the -/// `&'init T` reference is valid. -/// -/// This implementation of the same concept, without the QOM baggage, can help -/// understanding the effect: -/// -/// ``` -/// use std::marker::PhantomData; -/// -/// #[derive(PartialEq, Eq)] -/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); -/// -/// impl<'closure, T: Copy> Jail<'closure, T> { -/// fn get(&self) -> T { -/// *self.0 -/// } -/// -/// #[inline] -/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { -/// let parent_init = Jail(&v, PhantomData); -/// f(parent_init) -/// } -/// } -/// ``` -/// -/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the -/// closure: -/// -/// ```ignore -/// let x = 42; -/// let escape = Jail::with(&x, |token1| { -/// println!("{}", token1.get()); -/// // fails to compile... -/// token1 -/// }); -/// // ... so you cannot do this: -/// println!("{}", escape.get()); -/// ``` -/// -/// Likewise, in the QOM case the `ParentInit` cannot be moved out of -/// `instance_init()`. Without this trick it would be possible to stash a -/// `ParentInit` and use it later to access uninitialized memory. -/// -/// Here is another example, showing how separately-created "identities" stay -/// isolated: -/// -/// ```ignore -/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { -/// fn clone(&self) -> Jail<'closure, T> { -/// Jail(self.0, PhantomData) -/// } -/// } -/// -/// fn main() { -/// Jail::with(42, |token1| { -/// // this works and returns true: the clone has the same "identity" -/// println!("{}", token1 == token1.clone()); -/// Jail::with(42, |token2| { -/// // here the outer token remains accessible... -/// println!("{}", token1.get()); -/// // ... but the two are separate: this fails to compile: -/// println!("{}", token1 == token2); -/// }); -/// }); -/// } -/// ``` -pub struct ParentInit<'init, T>( - &'init mut MaybeUninit, - PhantomData &'init ()>, -); - -impl<'init, T> ParentInit<'init, T> { - #[inline] - pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { - let parent_init = ParentInit(obj, PhantomData); - f(parent_init) - } -} - -impl ParentInit<'_, T> { - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// Fields beyond `Object` could be uninitialized and it's your - /// responsibility to avoid that they're used when the pointer is - /// dereferenced, either directly or through a cast. - pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object { - self.as_object_ptr().cast_mut() - } - - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// Fields beyond `Object` could be uninitialized and it's your - /// responsibility to avoid that they're used when the pointer is - /// dereferenced, either directly or through a cast. - pub const fn as_object_ptr(&self) -> *const bindings::Object { - self.0.as_ptr().cast() - } -} - -impl<'a, T: ObjectImpl> ParentInit<'a, T> { - /// Convert from a derived type to one of its parent types, which - /// have already been initialized. - /// - /// # Safety - /// - /// Structurally this is always a safe operation; the [`IsA`] trait - /// provides static verification trait that `Self` dereferences to `U` or - /// a child of `U`, and only parent types of `T` are allowed. - /// - /// However, while the fields of the resulting reference are initialized, - /// calls might use uninitialized fields of the subclass. It is your - /// responsibility to avoid this. - pub const unsafe fn upcast(&self) -> &'a U - where - T::ParentType: IsA, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait; - // the parent has been initialized before `instance_init `is called - unsafe { &*(self.0.as_ptr().cast::()) } - } - - /// Convert from a derived type to one of its parent types, which - /// have already been initialized. - /// - /// # Safety - /// - /// Structurally this is always a safe operation; the [`IsA`] trait - /// provides static verification trait that `Self` dereferences to `U` or - /// a child of `U`, and only parent types of `T` are allowed. - /// - /// However, while the fields of the resulting reference are initialized, - /// calls might use uninitialized fields of the subclass. It is your - /// responsibility to avoid this. - pub unsafe fn upcast_mut(&mut self) -> &'a mut U - where - T::ParentType: IsA, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait; - // the parent has been initialized before `instance_init `is called - unsafe { &mut *(self.0.as_mut_ptr().cast::()) } - } -} - -impl Deref for ParentInit<'_, T> { - type Target = MaybeUninit; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl DerefMut for ParentInit<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - -unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { - let mut state = NonNull::new(obj).unwrap().cast::>(); - - // SAFETY: obj is an instance of T, since rust_instance_init - // is called from QOM core as the instance_init function - // for class T - unsafe { - ParentInit::with(state.as_mut(), |parent_init| { - T::INSTANCE_INIT.unwrap()(parent_init); - }); - } -} - -unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { - let state = NonNull::new(obj).unwrap().cast::(); - // SAFETY: obj is an instance of T, since rust_instance_post_init - // is called from QOM core as the instance_post_init function - // for class T - T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); -} - -unsafe extern "C" fn rust_class_init( - klass: *mut ObjectClass, - _data: *const c_void, -) { - let mut klass = NonNull::new(klass) - .unwrap() - .cast::<::Class>(); - // SAFETY: klass is a T::Class, since rust_class_init - // is called from QOM core as the class_init function - // for class T - ::CLASS_INIT(unsafe { klass.as_mut() }) -} - -unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { - // SAFETY: obj is an instance of T, since drop_object is called - // from the QOM core function object_deinit() as the instance_finalize - // function for class T. Note that while object_deinit() will drop the - // superclass field separately after this function returns, `T` must - // implement the unsafe trait ObjectType; the safety rules for the - // trait mandate that the parent field is manually dropped. - unsafe { std::ptr::drop_in_place(obj.cast::()) } -} - -/// Trait exposed by all structs corresponding to QOM objects. -/// -/// # Safety -/// -/// For classes declared in C: -/// -/// - `Class` and `TYPE` must match the data in the `TypeInfo`; -/// -/// - the first field of the struct must be of the instance type corresponding -/// to the superclass, as declared in the `TypeInfo` -/// -/// - likewise, the first field of the `Class` struct must be of the class type -/// corresponding to the superclass -/// -/// For classes declared in Rust and implementing [`ObjectImpl`]: -/// -/// - the struct must be `#[repr(C)]`; -/// -/// - the first field of the struct must be of type -/// [`ParentField`](ParentField), where `T` is the parent type -/// [`ObjectImpl::ParentType`] -/// -/// - the first field of the `Class` must be of the class struct corresponding -/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` -/// is not needed here. -/// -/// In both cases, having a separate class type is not necessary if the subclass -/// does not add any field. -pub unsafe trait ObjectType: Sized { - /// The QOM class object corresponding to this struct. This is used - /// to automatically generate a `class_init` method. - type Class; - - /// The name of the type, which can be passed to `object_new()` to - /// generate an instance of this type. - const TYPE_NAME: &'static CStr; - - /// Return the receiver as an Object. This is always safe, even - /// if this type represents an interface. - fn as_object(&self) -> &Object { - unsafe { &*self.as_ptr().cast() } - } - - /// Return the receiver as a const raw pointer to Object. - /// This is preferable to `as_object_mut_ptr()` if a C - /// function only needs a `const Object *`. - fn as_object_ptr(&self) -> *const bindings::Object { - self.as_object().as_ptr() - } - - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// This cast is always safe, but because the result is mutable - /// and the incoming reference is not, this should only be used - /// for calls to C functions, and only if needed. - unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { - self.as_object().as_mut_ptr() - } -} - -/// Trait exposed by all structs corresponding to QOM interfaces. -/// Unlike `ObjectType`, it is implemented on the class type (which provides -/// the vtable for the interfaces). -/// -/// # Safety -/// -/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; -/// right now, interfaces can only be declared in C. -pub unsafe trait InterfaceType: Sized { - /// The name of the type, which can be passed to - /// `object_class_dynamic_cast()` to obtain the pointer to the vtable - /// for this interface. - const TYPE_NAME: &'static CStr; - - /// Return the vtable for the interface; `U` is the type that - /// lists the interface in its `TypeInfo`. - /// - /// # Examples - /// - /// This function is usually called by a `class_init` method in `U::Class`. - /// For example, `DeviceClass::class_init` initializes its `Resettable` - /// interface as follows: - /// - /// ```ignore - /// ResettableClass::cast::(self).class_init::(); - /// ``` - /// - /// where `T` is the concrete subclass that is being initialized. - /// - /// # Panics - /// - /// Panic if the incoming argument if `T` does not implement the interface. - fn cast(klass: &mut U::Class) -> &mut Self { - unsafe { - // SAFETY: upcasting to ObjectClass is always valid, and the - // return type is either NULL or the argument itself - let result: *mut Self = object_class_dynamic_cast( - (klass as *mut U::Class).cast(), - Self::TYPE_NAME.as_ptr(), - ) - .cast(); - result.as_mut().unwrap() - } - } -} - -/// This trait provides safe casting operations for QOM objects to raw pointers, -/// to be used for example for FFI. The trait can be applied to any kind of -/// reference or smart pointers, and enforces correctness through the [`IsA`] -/// trait. -pub trait ObjectDeref: Deref -where - Self::Target: ObjectType, -{ - /// Convert to a const Rust pointer, to be used for example for FFI. - /// The target pointer type must be the type of `self` or a superclass - fn as_ptr(&self) -> *const U - where - Self::Target: IsA, - { - let ptr: *const Self::Target = self.deref(); - ptr.cast::() - } - - /// Convert to a mutable Rust pointer, to be used for example for FFI. - /// The target pointer type must be the type of `self` or a superclass. - /// Used to implement interior mutability for objects. - /// - /// # Safety - /// - /// This method is safe because only the actual dereference of the pointer - /// has to be unsafe. Bindings to C APIs will use it a lot, but care has - /// to be taken because it overrides the const-ness of `&self`. - fn as_mut_ptr(&self) -> *mut U - where - Self::Target: IsA, - { - #[allow(clippy::as_ptr_cast_mut)] - { - self.as_ptr::().cast_mut() - } - } -} - -/// Trait that adds extra functionality for `&T` where `T` is a QOM -/// object type. Allows conversion to/from C objects in generic code. -pub trait ObjectCast: ObjectDeref + Copy -where - Self::Target: ObjectType, -{ - /// Safely convert from a derived type to one of its parent types. - /// - /// This is always safe; the [`IsA`] trait provides static verification - /// trait that `Self` dereferences to `U` or a child of `U`. - fn upcast<'a, U: ObjectType>(self) -> &'a U - where - Self::Target: IsA, - Self: 'a, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait - unsafe { self.unsafe_cast::() } - } - - /// Attempt to convert to a derived type. - /// - /// Returns `None` if the object is not actually of type `U`. This is - /// verified at runtime by checking the object's type information. - fn downcast<'a, U: IsA>(self) -> Option<&'a U> - where - Self: 'a, - { - self.dynamic_cast::() - } - - /// Attempt to convert between any two types in the QOM hierarchy. - /// - /// Returns `None` if the object is not actually of type `U`. This is - /// verified at runtime by checking the object's type information. - fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> - where - Self: 'a, - { - unsafe { - // SAFETY: upcasting to Object is always valid, and the - // return type is either NULL or the argument itself - let result: *const U = - object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); - - result.as_ref() - } - } - - /// Convert to any QOM type without verification. - /// - /// # Safety - /// - /// What safety? You need to know yourself that the cast is correct; only - /// use when performance is paramount. It is still better than a raw - /// pointer `cast()`, which does not even check that you remain in the - /// realm of QOM `ObjectType`s. - /// - /// `unsafe_cast::()` is always safe. - unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U - where - Self: 'a, - { - unsafe { &*(self.as_ptr::().cast::()) } - } -} - -impl ObjectDeref for &T {} -impl ObjectCast for &T {} - -impl ObjectDeref for &mut T {} - -/// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + IsA { - /// The parent of the type. This should match the first field of the - /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. - type ParentType: ObjectType; - - /// Whether the object can be instantiated - const ABSTRACT: bool = false; - - /// Function that is called to initialize an object. The parent class will - /// have already been initialized so the type is only responsible for - /// initializing its own members. - /// - /// FIXME: The argument is not really a valid reference. `&mut - /// MaybeUninit` would be a better description. - const INSTANCE_INIT: Option)> = None; - - /// Function that is called to finish initialization of an object, once - /// `INSTANCE_INIT` functions have been called. - const INSTANCE_POST_INIT: Option = None; - - /// Called on descendant classes after all parent class initialization - /// has occurred, but before the class itself is initialized. This - /// is only useful if a class is not a leaf, and can be used to undo - /// the effects of copying the contents of the parent's class struct - /// to the descendants. - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), - > = None; - - const TYPE_INFO: TypeInfo = TypeInfo { - name: Self::TYPE_NAME.as_ptr(), - parent: Self::ParentType::TYPE_NAME.as_ptr(), - instance_size: core::mem::size_of::(), - instance_align: core::mem::align_of::(), - instance_init: match Self::INSTANCE_INIT { - None => None, - Some(_) => Some(rust_instance_init::), - }, - instance_post_init: match Self::INSTANCE_POST_INIT { - None => None, - Some(_) => Some(rust_instance_post_init::), - }, - instance_finalize: Some(drop_object::), - abstract_: Self::ABSTRACT, - class_size: core::mem::size_of::(), - class_init: Some(rust_class_init::), - class_base_init: Self::CLASS_BASE_INIT, - class_data: core::ptr::null(), - interfaces: core::ptr::null(), - }; - - // methods on ObjectClass - const UNPARENT: Option = None; - - /// Store into the argument the virtual method implementations - /// for `Self`. On entry, the virtual method pointers are set to - /// the default values coming from the parent classes; the function - /// can change them to override virtual methods of a parent class. - /// - /// Usually defined simply as `Self::Class::class_init::`; - /// however a default implementation cannot be included here, because the - /// bounds that the `Self::Class::class_init` method places on `Self` are - /// not known in advance. - /// - /// # Safety - /// - /// 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 - /// zeroed, otherwise it would not be possible to provide the class as a - /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks - /// that all fields *after the parent class* (but not the parent class - /// itself) are Zeroable. This unsafe trait can be added via a derive - /// macro. - const CLASS_INIT: fn(&mut Self::Class); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { - let state = NonNull::new(dev).unwrap().cast::(); - T::UNPARENT.unwrap()(unsafe { state.as_ref() }); -} - -impl ObjectClass { - /// Fill in the virtual methods of `ObjectClass` based on the definitions in - /// the `ObjectImpl` trait. - pub fn class_init(&mut self) { - if ::UNPARENT.is_some() { - self.unparent = Some(rust_unparent_fn::); - } - } -} - -unsafe impl ObjectType for Object { - type Class = ObjectClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; -} - -/// A reference-counted pointer to a QOM object. -/// -/// `Owned` wraps `T` with automatic reference counting. It increases the -/// reference count when created via [`Owned::from`] or cloned, and decreases -/// it when dropped. This ensures that the reference count remains elevated -/// as long as any `Owned` references to it exist. -/// -/// `Owned` can be used for two reasons: -/// * because the lifetime of the QOM object is unknown and someone else could -/// take a reference (similar to `Arc`, for example): in this case, the -/// object can escape and outlive the Rust struct that contains the `Owned` -/// field; -/// -/// * to ensure that the object stays alive until after `Drop::drop` is called -/// on the Rust struct: in this case, the object will always die together with -/// the Rust struct that contains the `Owned` field. -/// -/// Child properties are an example of the second case: in C, an object that -/// is created with `object_initialize_child` will die *before* -/// `instance_finalize` is called, whereas Rust expects the struct to have valid -/// contents when `Drop::drop` is called. Therefore Rust structs that have -/// child properties need to keep a reference to the child object. Right now -/// this can be done with `Owned`; in the future one might have a separate -/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like -/// `Owned`, but does not allow cloning. -/// -/// Note that dropping an `Owned` requires the big QEMU lock to be taken. -#[repr(transparent)] -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Owned(NonNull); - -// The following rationale for safety is taken from Linux's kernel::sync::Arc. - -// SAFETY: It is safe to send `Owned` to another thread when the underlying -// `T` is `Sync` because it effectively means sharing `&T` (which is safe -// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any -// thread that has an `Owned` may ultimately access `T` using a -// mutable reference when the reference count reaches zero and `T` is dropped. -unsafe impl Send for Owned {} - -// SAFETY: It is safe to send `&Owned` to another thread when the underlying -// `T` is `Sync` because it effectively means sharing `&T` (which is safe -// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any -// thread that has a `&Owned` may clone it and get an `Owned` on that -// thread, so the thread may ultimately access `T` using a mutable reference -// when the reference count reaches zero and `T` is dropped. -unsafe impl Sync for Owned {} - -impl Owned { - /// Convert a raw C pointer into an owned reference to the QOM - /// object it points to. The object's reference count will be - /// decreased when the `Owned` is dropped. - /// - /// # Panics - /// - /// Panics if `ptr` is NULL. - /// - /// # Safety - /// - /// The caller must indeed own a reference to the QOM object. - /// The object must not be embedded in another unless the outer - /// object is guaranteed to have a longer lifetime. - /// - /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed - /// back to `from_raw()` (assuming the original `Owned` was valid!), - /// since the owned reference remains there between the calls to - /// `into_raw()` and `from_raw()`. - pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY NOTE: while NonNull requires a mutable pointer, only - // Deref is implemented so the pointer passed to from_raw - // remains const - Owned(NonNull::new(ptr.cast_mut()).unwrap()) - } - - /// Obtain a raw C pointer from a reference. `src` is consumed - /// and the reference is leaked. - #[allow(clippy::missing_const_for_fn)] - pub fn into_raw(src: Owned) -> *mut T { - let src = ManuallyDrop::new(src); - src.0.as_ptr() - } - - /// Increase the reference count of a QOM object and return - /// a new owned reference to it. - /// - /// # Safety - /// - /// The object must not be embedded in another, unless the outer - /// object is guaranteed to have a longer lifetime. - pub unsafe fn from(obj: &T) -> Self { - unsafe { - object_ref(obj.as_object_mut_ptr().cast::()); - - // SAFETY NOTE: while NonNull requires a mutable pointer, only - // Deref is implemented so the reference passed to from_raw - // remains shared - Owned(NonNull::new_unchecked(obj.as_mut_ptr())) - } - } -} - -impl Clone for Owned { - fn clone(&self) -> Self { - // SAFETY: creation method is unsafe; whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - unsafe { Owned::from(self.deref()) } - } -} - -impl Deref for Owned { - type Target = T; - - fn deref(&self) -> &Self::Target { - // SAFETY: creation method is unsafe; whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - // With that guarantee, reference counting ensures that - // the object remains alive. - unsafe { &*self.0.as_ptr() } - } -} -impl ObjectDeref for Owned {} - -impl Drop for Owned { - fn drop(&mut self) { - assert!(bql::is_locked()); - // SAFETY: creation method is unsafe, and whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - unsafe { - object_unref(self.as_object_mut_ptr().cast::()); - } - } -} - -impl> fmt::Debug for Owned { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.deref().debug_fmt(f) - } -} - -/// Trait for class methods exposed by the Object class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA` -pub trait ObjectClassMethods: IsA { - /// Return a new reference counted instance of this class - fn new() -> Owned { - assert!(bql::is_locked()); - // SAFETY: the object created by object_new is allocated on - // the heap and has a reference count of 1 - unsafe { - let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); - let obj = Object::from_raw(raw_obj).unsafe_cast::(); - Owned::from_raw(obj) - } - } -} - -/// Trait for methods exposed by the Object class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA` -pub trait ObjectMethods: ObjectDeref -where - Self::Target: IsA, -{ - /// Return the name of the type of `self` - fn typename(&self) -> std::borrow::Cow<'_, str> { - let obj = self.upcast::(); - // SAFETY: safety of this is the requirement for implementing IsA - // The result of the C API has static lifetime - unsafe { - let p = object_get_typename(obj.as_mut_ptr()); - CStr::from_ptr(p).to_string_lossy() - } - } - - fn get_class(&self) -> &'static ::Class { - let obj = self.upcast::(); - - // SAFETY: all objects can call object_get_class; the actual class - // type is guaranteed by the implementation of `ObjectType` and - // `ObjectImpl`. - let klass: &'static ::Class = - unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; - - klass - } - - /// Convenience function for implementing the Debug trait - fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple(&self.typename()) - .field(&(self as *const Self)) - .finish() - } -} - -impl ObjectClassMethods for T where T: IsA {} -impl ObjectMethods for R where R::Target: IsA {} - -impl_vmstate_pointer!(Owned where T: VMState + ObjectType); diff --git a/rust/qom/wrapper.h b/rust/qom/wrapper.h deleted file mode 100644 index 3b71bcd3f..000000000 --- a/rust/qom/wrapper.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" - -#include "qom/object.h" diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml deleted file mode 100644 index ebecb99fe..000000000 --- a/rust/rustfmt.toml +++ /dev/null @@ -1,7 +0,0 @@ -edition = "2021" -format_generated_files = false -format_code_in_doc_comments = true -format_strings = true -imports_granularity = "Crate" -group_imports = "StdExternalCrate" -wrap_comments = true diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml deleted file mode 100644 index 7fd369b9e..000000000 --- a/rust/system/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "system" -version = "0.1.0" -description = "Rust bindings for QEMU/system" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../common" } -qom = { path = "../qom" } -util = { path = "../util" } - -[lints] -workspace = true diff --git a/rust/system/build.rs b/rust/system/build.rs deleted file mode 120000 index 71a316788..000000000 --- a/rust/system/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/system/meson.build b/rust/system/meson.build deleted file mode 100644 index 3ec140de0..000000000 --- a/rust/system/meson.build +++ /dev/null @@ -1,42 +0,0 @@ -c_enums = [ - 'device_endian', -] -_system_bindgen_args = [] -foreach enum : c_enums - _system_bindgen_args += ['--rustified-enum', enum] -endforeach - -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_system_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _system_bindgen_args, -) - -_system_rs = static_library( - 'system', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/memory.rs', - ], - {'.': _system_bindings_inc_rs} - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [common_rs, qemu_macros], -) - -system_rs = declare_dependency(link_with: [_system_rs], - dependencies: [hwcore]) diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs deleted file mode 100644 index 43edd9880..000000000 --- a/rust/system/src/bindings.rs +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -use common::Zeroable; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -// SAFETY: these are constants and vtables; the Send and Sync requirements -// are deferred to the unsafe callbacks that they contain -unsafe impl Send for MemoryRegionOps {} -unsafe impl Sync for MemoryRegionOps {} - -// SAFETY: this is a pure data struct -unsafe impl Send for CoalescedMemoryRange {} -unsafe impl Sync for CoalescedMemoryRange {} - -unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_1 {} -unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_2 {} -unsafe impl Zeroable for MemoryRegionOps {} -unsafe impl Zeroable for MemTxAttrs {} diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs deleted file mode 100644 index aafe9a866..000000000 --- a/rust/system/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub mod bindings; - -mod memory; -pub use memory::*; diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs deleted file mode 100644 index 4b3316bf7..000000000 --- a/rust/system/src/memory.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` - -use std::{ - ffi::{c_uint, c_void, CStr, CString}, - marker::PhantomData, -}; - -use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; -use qom::prelude::*; - -use crate::bindings::{self, device_endian, memory_region_init_io}; -pub use crate::bindings::{hwaddr, MemTxAttrs}; - -pub struct MemoryRegionOps( - bindings::MemoryRegionOps, - // Note: quite often you'll see PhantomData mentioned when discussing - // covariance and contravariance; you don't need any of those to understand - // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* - // holds callbacks that take an argument of type &T, except the type is erased - // before the callback is stored in the bindings::MemoryRegionOps field. - // The argument of PhantomData is a function pointer in order to represent - // that relationship; while that will also provide desirable and safe variance - // for T, variance is not the point but just a consequence. - PhantomData, -); - -// SAFETY: When a *const T is passed to the callbacks, the call itself -// is done in a thread-safe manner. The invocation is okay as long as -// T itself is `Sync`. -unsafe impl Sync for MemoryRegionOps {} - -#[derive(Clone)] -pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); - -unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( - opaque: *mut c_void, - addr: hwaddr, - size: c_uint, -) -> u64 { - F::call((unsafe { &*(opaque.cast::()) }, addr, size)) -} - -unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( - opaque: *mut c_void, - addr: hwaddr, - data: u64, - size: c_uint, -) { - F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) -} - -impl MemoryRegionOpsBuilder { - #[must_use] - pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { - self.0.read = Some(memory_region_ops_read_cb::); - self - } - - #[must_use] - pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { - self.0.write = Some(memory_region_ops_write_cb::); - self - } - - #[must_use] - pub const fn big_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; - self - } - - #[must_use] - pub const fn little_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; - self - } - - #[must_use] - pub const fn native_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; - self - } - - #[must_use] - pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { - self.0.valid.min_access_size = min; - self.0.valid.max_access_size = max; - self - } - - #[must_use] - pub const fn valid_unaligned(mut self) -> Self { - self.0.valid.unaligned = true; - self - } - - #[must_use] - pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { - self.0.impl_.min_access_size = min; - self.0.impl_.max_access_size = max; - self - } - - #[must_use] - pub const fn impl_unaligned(mut self) -> Self { - self.0.impl_.unaligned = true; - self - } - - #[must_use] - pub const fn build(self) -> MemoryRegionOps { - MemoryRegionOps::(self.0, PhantomData) - } - - #[must_use] - pub const fn new() -> Self { - Self(bindings::MemoryRegionOps::ZERO, PhantomData) - } -} - -impl Default for MemoryRegionOpsBuilder { - fn default() -> Self { - Self::new() - } -} - -/// A safe wrapper around [`bindings::MemoryRegion`]. -#[repr(transparent)] -#[derive(common::Wrapper)] -pub struct MemoryRegion(Opaque); - -unsafe impl Send for MemoryRegion {} -unsafe impl Sync for MemoryRegion {} - -impl MemoryRegion { - unsafe fn do_init_io( - slot: *mut bindings::MemoryRegion, - owner: *mut bindings::Object, - ops: &'static bindings::MemoryRegionOps, - name: &'static str, - size: u64, - ) { - unsafe { - let cstr = CString::new(name).unwrap(); - memory_region_init_io( - slot, - owner, - ops, - owner.cast::(), - cstr.as_ptr(), - size, - ); - } - } - - pub fn init_io>( - this: &mut MaybeUninitField<'_, T, Self>, - ops: &'static MemoryRegionOps, - name: &'static str, - size: u64, - ) { - unsafe { - Self::do_init_io( - this.as_mut_ptr().cast(), - MaybeUninitField::parent_mut(this).cast(), - &ops.0, - name, - size, - ); - } - } -} - -unsafe impl ObjectType for MemoryRegion { - type Class = bindings::MemoryRegionClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; -} - -qom_isa!(MemoryRegion: Object); - -/// A special `MemTxAttrs` constant, used to indicate that no memory -/// attributes are specified. -/// -/// Bus masters which don't specify any attributes will get this, -/// which has all attribute bits clear except the topmost one -/// (so that we can distinguish "all attributes deliberately clear" -/// from "didn't specify" if necessary). -pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { - unspecified: true, - ..Zeroable::ZERO -}; diff --git a/rust/system/wrapper.h b/rust/system/wrapper.h deleted file mode 100644 index 48abde850..000000000 --- a/rust/system/wrapper.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" - -#include "system/system.h" -#include "system/memory.h" -#include "system/address-spaces.h" diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml deleted file mode 100644 index d47dc3314..000000000 --- a/rust/tests/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "tests" -version = "0.1.0" -description = "Rust integration tests for QEMU" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -common = { path = "../common" } -chardev = { path = "../chardev" } -hwcore = { path = "../hw/core" } -migration = { path = "../migration" } -util = { path = "../util" } -bql = { path = "../bql" } -qom = { path = "../qom" } -system = { path = "../system" } - -[lints] -workspace = true diff --git a/rust/tests/meson.build b/rust/tests/meson.build deleted file mode 100644 index 00688c66f..000000000 --- a/rust/tests/meson.build +++ /dev/null @@ -1,14 +0,0 @@ -test('rust-integration', - executable( - 'rust-integration', - files('tests/vmstate_tests.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: ['--test'], - install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs]), - args: [ - '--test', '--test-threads', '1', - '--format', 'pretty', - ], - protocol: 'rust', - suite: ['unit', 'rust']) diff --git a/rust/tests/tests/vmstate_tests.rs b/rust/tests/tests/vmstate_tests.rs deleted file mode 100644 index fa9bbd6a1..000000000 --- a/rust/tests/tests/vmstate_tests.rs +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright (C) 2025 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::{c_void, CStr}, - mem::size_of, - ptr::NonNull, - slice, -}; - -use bql::BqlCell; -use common::Opaque; -use migration::{ - bindings::{ - vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, - vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, - }, - impl_vmstate_forward, impl_vmstate_struct, - vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField}, - vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate, -}; - -const FOO_ARRAY_MAX: usize = 3; - -// =========================== Test VMSTATE_FOOA =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOA: -// - VMSTATE_U16 -// - VMSTATE_UNUSED -// - VMSTATE_VARRAY_UINT16_UNSAFE -// - VMSTATE_VARRAY_MULTIPLY -#[repr(C)] -#[derive(Default)] -struct FooA { - arr: [u8; FOO_ARRAY_MAX], - num: u16, - arr_mul: [i8; FOO_ARRAY_MAX], - num_mul: u32, - elem: i8, -} - -static VMSTATE_FOOA: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_a") - .version_id(1) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooA, elem), - vmstate_unused!(size_of::()), - vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), - vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), - }) - .build(); - -impl_vmstate_struct!(FooA, VMSTATE_FOOA); - -#[test] -fn test_vmstate_uint16() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"elem\0" - ); - assert_eq!(foo_fields[0].offset, 16); - assert_eq!(foo_fields[0].num_offset, 0); - assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); - assert_eq!(foo_fields[0].version_id, 0); - assert_eq!(foo_fields[0].size, 1); - assert_eq!(foo_fields[0].num, 0); - assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); - assert!(foo_fields[0].vmsd.is_null()); - assert!(foo_fields[0].field_exists.is_none()); -} - -#[test] -fn test_vmstate_unused() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"unused\0" - ); - assert_eq!(foo_fields[1].offset, 0); - assert_eq!(foo_fields[1].num_offset, 0); - assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); - assert_eq!(foo_fields[1].version_id, 0); - assert_eq!(foo_fields[1].size, 8); - assert_eq!(foo_fields[1].num, 0); - assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); - assert!(foo_fields[1].vmsd.is_null()); - assert!(foo_fields[1].field_exists.is_none()); -} - -#[test] -fn test_vmstate_varray_uint16_unsafe() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to - // VMSTATE_VARRAY_UINT16_UNSAFE) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"arr\0" - ); - assert_eq!(foo_fields[2].offset, 0); - assert_eq!(foo_fields[2].num_offset, 4); - assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); - assert_eq!(foo_fields[2].version_id, 0); - assert_eq!(foo_fields[2].size, 1); - assert_eq!(foo_fields[2].num, 0); - assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); - assert!(foo_fields[2].vmsd.is_null()); - assert!(foo_fields[2].field_exists.is_none()); -} - -#[test] -fn test_vmstate_varray_multiply() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) }; - - // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to - // VMSTATE_VARRAY_MULTIPLY) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), - b"arr_mul\0" - ); - assert_eq!(foo_fields[3].offset, 6); - assert_eq!(foo_fields[3].num_offset, 12); - assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); - assert_eq!(foo_fields[3].version_id, 0); - assert_eq!(foo_fields[3].size, 1); - assert_eq!(foo_fields[3].num, 16); - assert_eq!( - foo_fields[3].flags.0, - VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 - ); - assert!(foo_fields[3].vmsd.is_null()); - assert!(foo_fields[3].field_exists.is_none()); - - // The last VMStateField in VMSTATE_FOOA. - assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); -} - -// =========================== Test VMSTATE_FOOB =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOB: -// - VMSTATE_BOOL_V -// - VMSTATE_U64 -// - VMSTATE_STRUCT_VARRAY_UINT8 -// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 -// - VMSTATE_ARRAY -// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn -#[repr(C)] -#[derive(Default)] -struct FooB { - arr_a: [FooA; FOO_ARRAY_MAX], - num_a: u8, - arr_a_mul: [FooA; FOO_ARRAY_MAX], - num_a_mul: u32, - wrap: BqlCell, - val: bool, - // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. - arr_i64: [i64; FOO_ARRAY_MAX], - arr_a_wrap: [FooA; FOO_ARRAY_MAX], - num_a_wrap: BqlCell, -} - -fn validate_foob(_state: &FooB, _version_id: u8) -> bool { - true -} - -static VMSTATE_FOOB: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_b") - .version_id(2) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooB, val).with_version_id(2), - vmstate_of!(FooB, wrap), - vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1), - vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2), - vmstate_of!(FooB, arr_i64), - vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob), - }) - .build(); - -#[test] -fn test_vmstate_bool_v() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"val\0" - ); - assert_eq!(foo_fields[0].offset, 136); - assert_eq!(foo_fields[0].num_offset, 0); - assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); - assert_eq!(foo_fields[0].version_id, 2); - assert_eq!(foo_fields[0].size, 1); - assert_eq!(foo_fields[0].num, 0); - assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); - assert!(foo_fields[0].vmsd.is_null()); - assert!(foo_fields[0].field_exists.is_none()); -} - -#[test] -fn test_vmstate_uint64() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"wrap\0" - ); - assert_eq!(foo_fields[1].offset, 128); - assert_eq!(foo_fields[1].num_offset, 0); - assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); - assert_eq!(foo_fields[1].version_id, 0); - assert_eq!(foo_fields[1].size, 8); - assert_eq!(foo_fields[1].num, 0); - assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); - assert!(foo_fields[1].vmsd.is_null()); - assert!(foo_fields[1].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_varray_uint8() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to - // VMSTATE_STRUCT_VARRAY_UINT8) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"arr_a\0" - ); - assert_eq!(foo_fields[2].offset, 0); - assert_eq!(foo_fields[2].num_offset, 60); - assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. - assert_eq!(foo_fields[2].version_id, 1); - assert_eq!(foo_fields[2].size, 20); - assert_eq!(foo_fields[2].num, 0); - assert_eq!( - foo_fields[2].flags.0, - VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 - ); - assert_eq!(foo_fields[2].vmsd, VMSTATE_FOOA.as_ref()); - assert!(foo_fields[2].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_varray_uint32_multiply() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to - // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), - b"arr_a_mul\0" - ); - assert_eq!(foo_fields[3].offset, 64); - assert_eq!(foo_fields[3].num_offset, 124); - assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. - assert_eq!(foo_fields[3].version_id, 2); - assert_eq!(foo_fields[3].size, 20); - assert_eq!(foo_fields[3].num, 32); - assert_eq!( - foo_fields[3].flags.0, - VMStateFlags::VMS_STRUCT.0 - | VMStateFlags::VMS_VARRAY_UINT32.0 - | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 - ); - assert_eq!(foo_fields[3].vmsd, VMSTATE_FOOA.as_ref()); - assert!(foo_fields[3].field_exists.is_none()); -} - -#[test] -fn test_vmstate_macro_array() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - - // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to - // VMSTATE_ARRAY) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), - b"arr_i64\0" - ); - assert_eq!(foo_fields[4].offset, 144); - assert_eq!(foo_fields[4].num_offset, 0); - assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); - assert_eq!(foo_fields[4].version_id, 0); - assert_eq!(foo_fields[4].size, 8); - assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); - assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); - assert!(foo_fields[4].vmsd.is_null()); - assert!(foo_fields[4].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_varray_uint8_wrapper() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) }; - let mut foo_b: FooB = Default::default(); - let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::(); - - // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to - // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in - // test_vmstate_struct_varray_uint8. - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), - b"arr_a_wrap\0" - ); - assert_eq!(foo_fields[5].num_offset, 228); - assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); - - // The last VMStateField in VMSTATE_FOOB. - assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); -} - -// =========================== Test VMSTATE_FOOC =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOC: -// - VMSTATE_POINTER -// - VMSTATE_ARRAY_OF_POINTER -struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. - -impl_vmstate_forward!(FooCWrapper); - -#[repr(C)] -struct FooC { - ptr: *const i32, - ptr_a: NonNull, - arr_ptr: [Box; FOO_ARRAY_MAX], - arr_ptr_wrap: FooCWrapper, -} - -unsafe impl Sync for FooC {} - -static VMSTATE_FOOC: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_c") - .version_id(3) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_of!(FooC, ptr).with_version_id(2), - vmstate_of!(FooC, ptr_a), - vmstate_of!(FooC, arr_ptr), - vmstate_of!(FooC, arr_ptr_wrap), - }) - .build(); - -const PTR_SIZE: usize = size_of::<*mut ()>(); - -#[test] -fn test_vmstate_pointer() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"ptr\0" - ); - assert_eq!(foo_fields[0].offset, 0); - assert_eq!(foo_fields[0].num_offset, 0); - assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); - assert_eq!(foo_fields[0].version_id, 2); - assert_eq!(foo_fields[0].size, 4); - assert_eq!(foo_fields[0].num, 0); - assert_eq!( - foo_fields[0].flags.0, - VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 - ); - assert!(foo_fields[0].vmsd.is_null()); - assert!(foo_fields[0].field_exists.is_none()); -} - -#[test] -fn test_vmstate_struct_pointer() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to - // VMSTATE_STRUCT_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"ptr_a\0" - ); - assert_eq!(foo_fields[1].offset, PTR_SIZE); - assert_eq!(foo_fields[1].num_offset, 0); - assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref()); - assert_eq!(foo_fields[1].version_id, 0); - assert_eq!(foo_fields[1].size, size_of::()); - assert_eq!(foo_fields[1].num, 0); - assert_eq!( - foo_fields[1].flags.0, - VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0 - ); - assert!(foo_fields[1].info.is_null()); - assert!(foo_fields[1].field_exists.is_none()); -} - -#[test] -fn test_vmstate_macro_array_of_pointer() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to - // VMSTATE_ARRAY_OF_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"arr_ptr\0" - ); - assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); - assert_eq!(foo_fields[2].num_offset, 0); - assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); - assert_eq!(foo_fields[2].version_id, 0); - assert_eq!(foo_fields[2].size, PTR_SIZE); - assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); - assert_eq!( - foo_fields[2].flags.0, - VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 - ); - assert!(foo_fields[2].vmsd.is_null()); - assert!(foo_fields[2].field_exists.is_none()); -} - -#[test] -fn test_vmstate_macro_array_of_pointer_wrapped() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) }; - - // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to - // VMSTATE_ARRAY_OF_POINTER) - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), - b"arr_ptr_wrap\0" - ); - assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); - assert_eq!(foo_fields[3].num_offset, 0); - assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 }); - assert_eq!(foo_fields[3].version_id, 0); - assert_eq!(foo_fields[3].size, PTR_SIZE); - assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); - assert_eq!( - foo_fields[3].flags.0, - VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 - ); - assert!(foo_fields[3].vmsd.is_null()); - assert!(foo_fields[3].field_exists.is_none()); - - // The last VMStateField in VMSTATE_FOOC. - assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); -} - -// =========================== Test VMSTATE_FOOD =========================== -// Test the use cases of the vmstate macro, corresponding to the following C -// macro variants: -// * VMSTATE_FOOD: -// - VMSTATE_VALIDATE - -// Add more member fields when vmstate_of support "test" parameter. -struct FooD; - -impl FooD { - fn validate_food_0(&self, _version_id: u8) -> bool { - true - } - - fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { - false - } -} - -fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { - true -} - -static VMSTATE_FOOD: VMStateDescription = VMStateDescriptionBuilder::::new() - .name(c"foo_d") - .version_id(3) - .minimum_version_id(1) - .fields(vmstate_fields! { - vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0), - vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1), - vmstate_validate!(FooD, c"foo_d_2", validate_food_2), - }) - .build(); - -#[test] -fn test_vmstate_validate() { - let foo_fields: &[VMStateField] = - unsafe { slice::from_raw_parts(VMSTATE_FOOD.as_ref().fields, 4) }; - let mut foo_d = FooD; - let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::(); - - // 1st VMStateField in VMSTATE_FOOD - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), - b"foo_d_0\0" - ); - assert_eq!(foo_fields[0].offset, 0); - assert_eq!(foo_fields[0].num_offset, 0); - assert!(foo_fields[0].info.is_null()); - assert_eq!(foo_fields[0].version_id, 0); - assert_eq!(foo_fields[0].size, 0); - assert_eq!(foo_fields[0].num, 0); - assert_eq!( - foo_fields[0].flags.0, - VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 - ); - assert!(foo_fields[0].vmsd.is_null()); - assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); - - // 2nd VMStateField in VMSTATE_FOOD - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), - b"foo_d_1\0" - ); - assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); - - // 3rd VMStateField in VMSTATE_FOOD - assert_eq!( - unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), - b"foo_d_2\0" - ); - assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); - - // The last VMStateField in VMSTATE_FOOD. - assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); -} diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml deleted file mode 100644 index 1f6767ed9..000000000 --- a/rust/util/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "util" -version = "0.1.0" -description = "Rust bindings for QEMU/util" -resolver = "2" -publish = false - -authors.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -anyhow = { workspace = true } -foreign = { workspace = true } -libc = { workspace = true } -common = { path = "../common" } - -[lints] -workspace = true diff --git a/rust/util/build.rs b/rust/util/build.rs deleted file mode 100644 index 5654d1d56..000000000 --- a/rust/util/build.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_file; -#[cfg(windows)] -use std::os::windows::fs::symlink_file; -use std::{env, fs::remove_file, io::Result, path::Path}; - -fn main() -> Result<()> { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { - let sub = get_rust_subdir(manifest_dir).unwrap(); - format!("{root}/{sub}/bindings.inc.rs") - } else { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - format!("{manifest_dir}/src/bindings.inc.rs") - }; - - let file = Path::new(&file); - if !Path::new(&file).exists() { - panic!(concat!( - "\n", - " No generated C bindings found! Maybe you wanted one of\n", - " `make clippy`, `make rustfmt`, `make rustdoc`?\n", - "\n", - " For other uses of `cargo`, start a subshell with\n", - " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", - " the top of the build tree." - )); - } - - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = format!("{out_dir}/bindings.inc.rs"); - let dest_path = Path::new(&dest_path); - if dest_path.symlink_metadata().is_ok() { - remove_file(dest_path)?; - } - symlink_file(file, dest_path)?; - - println!("cargo:rerun-if-changed=build.rs"); - Ok(()) -} - -fn get_rust_subdir(path: &str) -> Option<&str> { - path.find("/rust").map(|index| &path[index + 1..]) -} diff --git a/rust/util/meson.build b/rust/util/meson.build deleted file mode 100644 index 87a893673..000000000 --- a/rust/util/meson.build +++ /dev/null @@ -1,55 +0,0 @@ -_util_bindgen_args = [] -c_enums = [ - 'module_init_type', - 'QEMUClockType', -] -foreach enum : c_enums - _util_bindgen_args += ['--rustified-enum', enum] -endforeach - -# -# TODO: Remove this comment when the clang/libclang mismatch issue is solved. -# -# Rust bindings generation with `bindgen` might fail in some cases where the -# detected `libclang` does not match the expected `clang` version/target. In -# this case you must pass the path to `clang` and `libclang` to your build -# command invocation using the environment variables CLANG_PATH and -# LIBCLANG_PATH -_util_bindings_inc_rs = rust.bindgen( - input: 'wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + _util_bindgen_args, -) - -_util_rs = static_library( - 'util', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/error.rs', - 'src/log.rs', - 'src/module.rs', - 'src/timer.rs', - ], - {'.': _util_bindings_inc_rs} - ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, common_rs, qom, qemuutil], -) - -util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) - -# Doctests are essentially integration tests, so they need the same dependencies. -# Note that running them requires the object files for C code, so place them -# in a separate suite that is run by the "build" CI jobs rather than "check". -rust.doctest('rust-util-rs-doctests', - _util_rs, - protocol: 'rust', - dependencies: util_rs, - suite: ['doc', 'rust'] -) diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs deleted file mode 100644 index 9ffff12cd..000000000 --- a/rust/util/src/bindings.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#![allow( - dead_code, - improper_ctypes_definitions, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unnecessary_transmutes, - unsafe_op_in_unsafe_fn, - clippy::pedantic, - clippy::restriction, - clippy::style, - clippy::missing_const_for_fn, - clippy::ptr_offset_with_cast, - clippy::useless_transmute, - clippy::missing_safety_doc, - clippy::too_many_arguments -)] - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs deleted file mode 100644 index bfa5a8685..000000000 --- a/rust/util/src/error.rs +++ /dev/null @@ -1,416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Error propagation for QEMU Rust code -//! -//! This module contains [`Error`], the bridge between Rust errors and -//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) -//! struct. -//! -//! For FFI code, [`Error`] provides functions to simplify conversion between -//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: -//! -//! * [`ok_or_propagate`](crate::Error::ok_or_propagate), -//! [`bool_or_propagate`](crate::Error::bool_or_propagate), -//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build -//! a C return value while also propagating an error condition -//! -//! * [`err_or_else`](crate::Error::err_or_else) and -//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` -//! -//! This module is most commonly used at the boundary between C and Rust code; -//! other code will usually access it through the -//! [`utils::Result`](crate::Result) type alias, and will use the -//! [`std::error::Error`] interface to let C errors participate in Rust's error -//! handling functionality. -//! -//! Rust code can also create use this module to create an error object that -//! will be passed up to C code, though in most cases this will be done -//! transparently through the `?` operator. Errors can be constructed from a -//! simple error string, from an [`anyhow::Error`] to pass any other Rust error -//! type up to C code, or from a combination of the two. -//! -//! The third case, corresponding to [`Error::with_error`], is the only one that -//! requires mentioning [`utils::Error`](crate::Error) explicitly. Similar -//! to how QEMU's C code handles errno values, the string and the -//! `anyhow::Error` object will be concatenated with `:` as the separator. - -use std::{ - borrow::Cow, - ffi::{c_char, c_int, c_void, CStr}, - fmt::{self, Display}, - panic, ptr, -}; - -use foreign::{prelude::*, OwnedPointer}; - -use crate::bindings; - -pub type Result = std::result::Result; - -#[derive(Debug)] -pub struct Error { - msg: Option>, - /// Appends the print string of the error to the msg if not None - cause: Option, - file: &'static str, - line: u32, -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.cause.as_ref().map(AsRef::as_ref) - } - - #[allow(deprecated)] - fn description(&self) -> &str { - self.msg - .as_deref() - .or_else(|| self.cause.as_deref().map(std::error::Error::description)) - .expect("no message nor cause?") - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut prefix = ""; - if let Some(ref msg) = self.msg { - write!(f, "{msg}")?; - prefix = ": "; - } - if let Some(ref cause) = self.cause { - write!(f, "{prefix}{cause}")?; - } else if prefix.is_empty() { - panic!("no message nor cause?"); - } - Ok(()) - } -} - -impl From for Error { - #[track_caller] - fn from(msg: String) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(Cow::Owned(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From<&'static str> for Error { - #[track_caller] - fn from(msg: &'static str) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(Cow::Borrowed(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From for Error { - #[track_caller] - fn from(error: anyhow::Error) -> Self { - let location = panic::Location::caller(); - Error { - msg: None, - cause: Some(error), - file: location.file(), - line: location.line(), - } - } -} - -impl Error { - /// Create a new error, prepending `msg` to the - /// description of `cause` - #[track_caller] - pub fn with_error(msg: impl Into>, cause: impl Into) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(msg.into()), - cause: Some(cause.into()), - file: location.file(), - line: location.line(), - } - } - - /// Consume a result, returning `false` if it is an error and - /// `true` if it is successful. The error is propagated into - /// `errp` like the C API `error_propagate` would do. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { - // SAFETY: caller guarantees errp is valid - unsafe { Self::ok_or_propagate(result, errp) }.is_some() - } - - /// Consume a result, returning a `NULL` pointer if it is an error and - /// a C representation of the contents if it is successful. This is - /// similar to the C API `error_propagate`, but it panics if `*errp` - /// is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - /// - /// See [`propagate`](Error::propagate) for more information. - #[must_use] - pub unsafe fn ptr_or_propagate( - result: Result, - errp: *mut *mut bindings::Error, - ) -> *mut T::Foreign { - // SAFETY: caller guarantees errp is valid - unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() - } - - /// Consume a result in the same way as `self.ok()`, but also propagate - /// a possible error into `errp`. This is similar to the C API - /// `error_propagate`, but it panics if `*errp` is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - /// - /// See [`propagate`](Error::propagate) for more information. - pub unsafe fn ok_or_propagate( - result: Result, - errp: *mut *mut bindings::Error, - ) -> Option { - result.map_err(|err| unsafe { err.propagate(errp) }).ok() - } - - /// Equivalent of the C function `error_propagate`. Fill `*errp` - /// with the information container in `self` if `errp` is not NULL; - /// then consume it. - /// - /// This is similar to the C API `error_propagate`, but it panics if - /// `*errp` is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; it can be - /// `NULL` or it can point to any of: - /// * `error_abort` - /// * `error_fatal` - /// * a local variable of (C) type `Error *` - /// - /// Typically `errp` is received from C code and need not be - /// checked further at the Rust↔C boundary. - pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { - if errp.is_null() { - return; - } - - // SAFETY: caller guarantees that errp and *errp are valid - unsafe { - assert_eq!(*errp, ptr::null_mut()); - bindings::error_propagate(errp, self.clone_to_foreign_ptr()); - } - } - - /// Convert a C `Error*` into a Rust `Result`, using - /// `Ok(())` if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or valid; typically it was initialized - /// with `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { - // SAFETY: caller guarantees c_error is valid - unsafe { Self::err_or_else(c_error, || ()) } - } - - /// Convert a C `Error*` into a Rust `Result`, calling `f()` to - /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or point to a valid C [`struct - /// Error`](bindings::Error); typically it was initialized with - /// `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_else T>( - c_error: *mut bindings::Error, - f: F, - ) -> Result { - // SAFETY: caller guarantees c_error is valid - let err = unsafe { Option::::from_foreign(c_error) }; - match err { - None => Ok(f()), - Some(err) => Err(err), - } - } -} - -impl FreeForeign for Error { - type Foreign = bindings::Error; - - unsafe fn free_foreign(p: *mut bindings::Error) { - // SAFETY: caller guarantees p is valid - unsafe { - bindings::error_free(p); - } - } -} - -impl CloneToForeign for Error { - fn clone_to_foreign(&self) -> OwnedPointer { - // SAFETY: all arguments are controlled by this function - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::()); - let err: &mut bindings::Error = &mut *err.cast(); - *err = bindings::Error { - msg: format!("{self}").clone_to_foreign_ptr(), - err_class: bindings::ERROR_CLASS_GENERIC_ERROR, - src_len: self.file.len() as c_int, - src: self.file.as_ptr().cast::(), - line: self.line as c_int, - func: ptr::null_mut(), - hint: ptr::null_mut(), - }; - OwnedPointer::new(err) - } - } -} - -impl FromForeign for Error { - unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { - // SAFETY: caller guarantees c_error is valid - unsafe { - let error = &*c_error; - let file = if error.src_len < 0 { - // NUL-terminated - CStr::from_ptr(error.src).to_str() - } else { - // Can become str::from_utf8 with Rust 1.87.0 - std::str::from_utf8(std::slice::from_raw_parts( - &*error.src.cast::(), - error.src_len as usize, - )) - }; - - Error { - msg: FromForeign::cloned_from_foreign(error.msg), - cause: None, - file: file.unwrap(), - line: error.line as u32, - } - } - } -} - -#[cfg(test)] -mod tests { - use std::ffi::CStr; - - use anyhow::anyhow; - use common::assert_match; - use foreign::OwnedPointer; - - use super::*; - - #[track_caller] - fn error_for_test(msg: &CStr) -> OwnedPointer { - // SAFETY: all arguments are controlled by this function - let location = panic::Location::caller(); - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::()); - let err: &mut bindings::Error = &mut *err.cast(); - *err = bindings::Error { - msg: msg.clone_to_foreign_ptr(), - err_class: bindings::ERROR_CLASS_GENERIC_ERROR, - src_len: location.file().len() as c_int, - src: location.file().as_ptr().cast::(), - line: location.line() as c_int, - func: ptr::null_mut(), - hint: ptr::null_mut(), - }; - OwnedPointer::new(err) - } - } - - unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { - unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } - } - - #[test] - #[allow(deprecated)] - fn test_description() { - use std::error::Error; - - assert_eq!(super::Error::from("msg").description(), "msg"); - assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); - } - - #[test] - fn test_display() { - assert_eq!(&*format!("{}", Error::from("msg")), "msg"); - assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); - assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); - - assert_eq!( - &*format!("{}", Error::with_error("msg", anyhow!("cause"))), - "msg: cause" - ); - } - - #[test] - fn test_bool_or_propagate() { - unsafe { - let mut local_err: *mut bindings::Error = ptr::null_mut(); - - assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); - assert_eq!(local_err, ptr::null_mut()); - - let my_err = Error::from("msg"); - assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); - assert_ne!(local_err, ptr::null_mut()); - assert_eq!(error_get_pretty(local_err), c"msg"); - bindings::error_free(local_err); - } - } - - #[test] - fn test_ptr_or_propagate() { - unsafe { - let mut local_err: *mut bindings::Error = ptr::null_mut(); - - let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); - assert_eq!(String::from_foreign(ret), "abc"); - assert_eq!(local_err, ptr::null_mut()); - - let my_err = Error::from("msg"); - assert_eq!( - Error::ptr_or_propagate(Err::(my_err), &mut local_err), - ptr::null_mut() - ); - assert_ne!(local_err, ptr::null_mut()); - assert_eq!(error_get_pretty(local_err), c"msg"); - bindings::error_free(local_err); - } - } - - #[test] - fn test_err_or_unit() { - unsafe { - let result = Error::err_or_unit(ptr::null_mut()); - assert_match!(result, Ok(())); - - let err = error_for_test(c"msg"); - let err = Error::err_or_unit(err.into_inner()).unwrap_err(); - assert_eq!(&*format!("{err}"), "msg"); - } - } -} diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs deleted file mode 100644 index 16c89b951..000000000 --- a/rust/util/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pub mod bindings; -pub mod error; -pub mod log; -pub mod module; -pub mod timer; - -pub use error::{Error, Result}; diff --git a/rust/util/src/log.rs b/rust/util/src/log.rs deleted file mode 100644 index af9a3e912..000000000 --- a/rust/util/src/log.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2025 Bernhard Beschow -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for QEMU's logging infrastructure - -use std::{ - io::{self, Write}, - ptr::NonNull, -}; - -use common::errno; - -use crate::bindings; - -#[repr(u32)] -/// Represents specific error categories within QEMU's logging system. -/// -/// The `Log` enum provides a Rust abstraction for logging errors, corresponding -/// to a subset of the error categories defined in the C implementation. -pub enum Log { - /// Log invalid access caused by the guest. - /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. - GuestError = bindings::LOG_GUEST_ERROR, - - /// Log guest access of unimplemented functionality. - /// Corresponds to `LOG_UNIMP` in the C implementation. - Unimp = bindings::LOG_UNIMP, -} - -/// A RAII guard for QEMU's logging infrastructure. Creating the guard -/// locks the log file, and dropping it (letting it go out of scope) unlocks -/// the file. -/// -/// As long as the guard lives, it can be written to using [`std::io::Write`]. -/// -/// The locking is recursive, therefore owning a guard does not prevent -/// using [`log_mask_ln!()`](crate::log_mask_ln). -pub struct LogGuard(NonNull); - -impl LogGuard { - /// Return a RAII guard that writes to QEMU's logging infrastructure. - /// The log file is locked while the guard exists, ensuring that there - /// is no tearing of the messages. - /// - /// Return `None` if the log file is closed and could not be opened. - /// Do *not* use `unwrap()` on the result; failure can be handled simply - /// by not logging anything. - /// - /// # Examples - /// - /// ``` - /// # use util::log::LogGuard; - /// # use std::io::Write; - /// if let Some(mut log) = LogGuard::new() { - /// writeln!(log, "test"); - /// } - /// ``` - pub fn new() -> Option { - let f = unsafe { bindings::qemu_log_trylock() }.cast(); - NonNull::new(f).map(Self) - } - - /// Writes a formatted string into the log, returning any error encountered. - /// - /// This method is primarily used by the - /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it - /// to be called explicitly. It is public because it is the only way to - /// examine the error, which `log_mask_ln!()` ignores - /// - /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end. - pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> { - if let Some(mut log) = Self::new() { - log.write_fmt(args)?; - } - Ok(()) - } -} - -impl Write for LogGuard { - fn write(&mut self, bytes: &[u8]) -> io::Result { - let ret = unsafe { - bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr()) - }; - errno::into_io_result(ret) - } - - fn flush(&mut self) -> io::Result<()> { - // Do nothing, dropping the guard takes care of flushing - Ok(()) - } -} - -impl Drop for LogGuard { - fn drop(&mut self) { - unsafe { - bindings::qemu_log_unlock(self.0.as_ptr()); - } - } -} - -/// A macro to log messages conditionally based on a provided mask. -/// -/// The `log_mask_ln` macro checks whether the given mask matches the current -/// log level and, if so, formats and logs the message. It is the Rust -/// counterpart of the `qemu_log_mask()` macro in the C implementation. -/// -/// Errors from writing to the log are ignored. -/// -/// # Parameters -/// -/// - `$mask`: A log level mask. This should be a variant of the `Log` enum. -/// - `$fmt`: A format string following the syntax and rules of the `format!` -/// macro. It specifies the structure of the log message. -/// - `$args`: Optional arguments to be interpolated into the format string. -/// -/// # Example -/// -/// ``` -/// use util::{log::Log, log_mask_ln}; -/// -/// let error_address = 0xbad; -/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); -/// ``` -/// -/// It is also possible to use printf-style formatting, as well as having a -/// trailing `,`: -/// -/// ``` -/// use util::{log::Log, log_mask_ln}; -/// -/// let error_address = 0xbad; -/// log_mask_ln!( -/// Log::GuestError, -/// "Address 0x{:x} out of range", -/// error_address, -/// ); -/// ``` -#[macro_export] -macro_rules! log_mask_ln { - ($mask:expr, $fmt:tt $($args:tt)*) => {{ - // Type assertion to enforce type `Log` for $mask - let _: $crate::log::Log = $mask; - - if unsafe { - ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 - } { - _ = $crate::log::LogGuard::log_fmt( - format_args!("{}\n", format_args!($fmt $($args)*))); - } - }}; -} diff --git a/rust/util/src/module.rs b/rust/util/src/module.rs deleted file mode 100644 index 06c45fc14..000000000 --- a/rust/util/src/module.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Macro to register blocks of code that run as QEMU starts up. - -#[macro_export] -macro_rules! module_init { - ($type:ident => $body:block) => { - const _: () = { - #[used] - #[cfg_attr( - not(any(target_vendor = "apple", target_os = "windows")), - link_section = ".init_array" - )] - #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static LOAD_MODULE: extern "C" fn() = { - extern "C" fn init_fn() { - $body - } - - extern "C" fn ctor_fn() { - unsafe { - $crate::bindings::register_module_init( - Some(init_fn), - $crate::bindings::module_init_type::$type, - ); - } - } - - ctor_fn - }; - }; - }; - - // shortcut because it's quite common that $body needs unsafe {} - ($type:ident => unsafe $body:block) => { - ::util::module_init! { - $type => { unsafe { $body } } - } - }; -} diff --git a/rust/util/src/timer.rs b/rust/util/src/timer.rs deleted file mode 100644 index c6b3e4088..000000000 --- a/rust/util/src/timer.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::{c_int, c_void}, - pin::Pin, -}; - -use common::{callbacks::FnCall, Opaque}; - -use crate::bindings::{ - self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType, -}; - -/// A safe wrapper around [`bindings::QEMUTimer`]. -#[repr(transparent)] -#[derive(Debug, common::Wrapper)] -pub struct Timer(Opaque); - -unsafe impl Send for Timer {} -unsafe impl Sync for Timer {} - -#[repr(transparent)] -#[derive(common::Wrapper)] -pub struct TimerListGroup(Opaque); - -unsafe impl Send for TimerListGroup {} -unsafe impl Sync for TimerListGroup {} - -impl Timer { - pub const MS: u32 = bindings::SCALE_MS; - pub const US: u32 = bindings::SCALE_US; - pub const NS: u32 = bindings::SCALE_NS; - - /// Create a `Timer` struct without initializing it. - /// - /// # Safety - /// - /// The timer must be initialized before it is armed with - /// [`modify`](Self::modify). - pub const unsafe fn new() -> Self { - // SAFETY: requirements relayed to callers of Timer::new - Self(unsafe { Opaque::zeroed() }) - } - - /// Create a new timer with the given attributes. - pub fn init_full<'timer, 'opaque: 'timer, T, F>( - self: Pin<&'timer mut Self>, - timer_list_group: Option<&TimerListGroup>, - clk_type: ClockType, - scale: u32, - attributes: u32, - _cb: F, - opaque: &'opaque T, - ) where - F: for<'a> FnCall<(&'a T,)>, - { - const { assert!(F::IS_SOME) }; - - /// timer expiration callback - unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( - opaque: *mut c_void, - ) { - // SAFETY: the opaque was passed as a reference to `T`. - F::call((unsafe { &*(opaque.cast::()) },)) - } - - let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; - - // SAFETY: the opaque outlives the timer - unsafe { - timer_init_full( - self.as_mut_ptr(), - if let Some(g) = timer_list_group { - g as *const TimerListGroup as *mut _ - } else { - ::core::ptr::null_mut() - }, - clk_type.id, - scale as c_int, - attributes as c_int, - Some(timer_cb), - (opaque as *const T).cast::().cast_mut(), - ) - } - } - - pub fn modify(&self, expire_time: u64) { - // SAFETY: the only way to obtain a Timer safely is via methods that - // take a Pin<&mut Self>, therefore the timer is pinned - unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } - } - - pub fn delete(&self) { - // SAFETY: the only way to obtain a Timer safely is via methods that - // take a Pin<&mut Self>, therefore the timer is pinned - unsafe { timer_del(self.as_mut_ptr()) } - } -} - -// FIXME: use something like PinnedDrop from the pinned_init crate -impl Drop for Timer { - fn drop(&mut self) { - self.delete() - } -} - -pub struct ClockType { - id: QEMUClockType, -} - -impl ClockType { - pub fn get_ns(&self) -> u64 { - // SAFETY: cannot be created outside this module, therefore id - // is valid - (unsafe { qemu_clock_get_ns(self.id) }) as u64 - } -} - -pub const CLOCK_VIRTUAL: ClockType = ClockType { - id: QEMUClockType::QEMU_CLOCK_VIRTUAL, -}; - -pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h deleted file mode 100644 index b9ed68a01..000000000 --- a/rust/util/wrapper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* - * This header file is meant to be used as input to the `bindgen` application - * in order to generate C FFI compatible Rust bindings. - */ - -#ifndef __CLANG_STDATOMIC_H -#define __CLANG_STDATOMIC_H -/* - * Fix potential missing stdatomic.h error in case bindgen does not insert the - * correct libclang header paths on its own. We do not use stdatomic.h symbols - * in QEMU code, so it's fine to declare dummy types instead. - */ -typedef enum memory_order { - memory_order_relaxed, - memory_order_consume, - memory_order_acquire, - memory_order_release, - memory_order_acq_rel, - memory_order_seq_cst, -} memory_order; -#endif /* __CLANG_STDATOMIC_H */ - -#include "qemu/osdep.h" - -#include "qapi/error.h" -#include "qapi/error-internal.h" -#include "qemu/log-for-trace.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qemu/timer.h" diff --git a/scripts/get-wraps-from-cargo-registry.py b/scripts/get-wraps-from-cargo-registry.py deleted file mode 100755 index 31eed5c2d..000000000 --- a/scripts/get-wraps-from-cargo-registry.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-License-Identifier: GPL-2.0-or-later - -""" -get-wraps-from-cargo-registry.py - Update Meson subprojects from a global registry -""" - -# Copyright (C) 2025 Red Hat, Inc. -# -# Author: Paolo Bonzini - -import argparse -import configparser -import filecmp -import glob -import os -import subprocess -import sys - - -def get_name_and_semver(namever: str) -> tuple[str, str]: - """Split a subproject name into its name and semantic version parts""" - parts = namever.rsplit("-", 1) - if len(parts) != 2: - return namever, "" - - return parts[0], parts[1] - - -class UpdateSubprojects: - cargo_registry: str - top_srcdir: str - dry_run: bool - changes: int = 0 - - def find_installed_crate(self, namever: str) -> str | None: - """Find installed crate matching name and semver prefix""" - name, semver = get_name_and_semver(namever) - - # exact version match - path = os.path.join(self.cargo_registry, f"{name}-{semver}") - if os.path.exists(path): - return f"{name}-{semver}" - - # semver match - matches = sorted(glob.glob(f"{path}.*")) - return os.path.basename(matches[0]) if matches else None - - def compare_build_rs(self, orig_dir: str, registry_namever: str) -> None: - """Warn if the build.rs in the original directory differs from the registry version.""" - orig_build_rs = os.path.join(orig_dir, "build.rs") - new_build_rs = os.path.join(self.cargo_registry, registry_namever, "build.rs") - - msg = None - if os.path.isfile(orig_build_rs) != os.path.isfile(new_build_rs): - if os.path.isfile(orig_build_rs): - msg = f"build.rs removed in {registry_namever}" - if os.path.isfile(new_build_rs): - msg = f"build.rs added in {registry_namever}" - - elif os.path.isfile(orig_build_rs) and not filecmp.cmp(orig_build_rs, new_build_rs): - msg = f"build.rs changed from {orig_dir} to {registry_namever}" - - if msg: - print(f"⚠️ Warning: {msg}") - print(" This may affect the build process - please review the differences.") - - def update_subproject(self, wrap_file: str, registry_namever: str) -> None: - """Modify [wrap-file] section to point to self.cargo_registry.""" - assert wrap_file.endswith("-rs.wrap") - wrap_name = wrap_file[:-5] - - env = os.environ.copy() - env["MESON_PACKAGE_CACHE_DIR"] = self.cargo_registry - - config = configparser.ConfigParser() - config.read(wrap_file) - if "wrap-file" not in config: - return - - # do not download the wrap, always use the local copy - orig_dir = config["wrap-file"]["directory"] - if os.path.exists(orig_dir) and orig_dir != registry_namever: - self.compare_build_rs(orig_dir, registry_namever) - - if self.dry_run: - if orig_dir == registry_namever: - print(f"Will install {orig_dir} from registry.") - else: - print(f"Will replace {orig_dir} with {registry_namever}.") - self.changes += 1 - return - - config["wrap-file"]["directory"] = registry_namever - for key in list(config["wrap-file"].keys()): - if key.startswith("source"): - del config["wrap-file"][key] - - # replace existing directory with installed version - if os.path.exists(orig_dir): - subprocess.run( - ["meson", "subprojects", "purge", "--confirm", wrap_name], - cwd=self.top_srcdir, - env=env, - check=True, - ) - - with open(wrap_file, "w") as f: - config.write(f) - - if orig_dir == registry_namever: - print(f"Installing {orig_dir} from registry.") - else: - print(f"Replacing {orig_dir} with {registry_namever}.") - patch_dir = config["wrap-file"]["patch_directory"] - patch_dir = os.path.join("packagefiles", patch_dir) - _, ver = registry_namever.rsplit("-", 1) - subprocess.run( - ["meson", "rewrite", "kwargs", "set", "project", "/", "version", ver], - cwd=patch_dir, - env=env, - check=True, - ) - - subprocess.run( - ["meson", "subprojects", "download", wrap_name], - cwd=self.top_srcdir, - env=env, - check=True, - ) - self.changes += 1 - - @staticmethod - def parse_cmdline() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Replace Meson subprojects with packages in a Cargo registry" - ) - parser.add_argument( - "--cargo-registry", - default=os.environ.get("CARGO_REGISTRY"), - help="Path to Cargo registry (default: CARGO_REGISTRY env var)", - ) - parser.add_argument( - "--dry-run", - action="store_true", - default=False, - help="Do not actually replace anything", - ) - - args = parser.parse_args() - if not args.cargo_registry: - print("error: CARGO_REGISTRY environment variable not set and --cargo-registry not provided") - sys.exit(1) - - return args - - def __init__(self, args: argparse.Namespace): - self.cargo_registry = args.cargo_registry - self.dry_run = args.dry_run - self.top_srcdir = os.getcwd() - - def main(self) -> None: - if not os.path.exists("subprojects"): - print("'subprojects' directory not found, nothing to do.") - return - - os.chdir("subprojects") - for wrap_file in sorted(glob.glob("*-rs.wrap")): - namever = wrap_file[:-8] # Remove '-rs.wrap' - - registry_namever = self.find_installed_crate(namever) - if not registry_namever: - print(f"No installed crate found for {wrap_file}") - continue - - self.update_subproject(wrap_file, registry_namever) - - if self.changes: - if self.dry_run: - print("Rerun without --dry-run to apply changes.") - else: - print(f"✨ {self.changes} subproject(s) updated!") - else: - print("No changes.") - - -if __name__ == "__main__": - args = UpdateSubprojects.parse_cmdline() - UpdateSubprojects(args).main() diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 46bf73be7..33973223f 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -44,8 +44,6 @@ meson_options_help() { printf "%s\n" ' getrandom()' printf "%s\n" ' --enable-safe-stack SafeStack Stack Smash Protection (requires' printf "%s\n" ' clang/llvm and coroutine backend ucontext)' - printf "%s\n" ' --enable-strict-rust-lints' - printf "%s\n" ' Enable stricter set of Rust warnings' printf "%s\n" ' --enable-strip Strip targets on install' printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (slow)' printf "%s\n" ' --enable-trace-backends=CHOICES' @@ -167,7 +165,6 @@ meson_options_help() { printf "%s\n" ' rbd Ceph block device driver' printf "%s\n" ' rdma Enable RDMA-based migration' printf "%s\n" ' replication replication support' - printf "%s\n" ' rust Rust support' printf "%s\n" ' rutabaga-gfx rutabaga_gfx support' printf "%s\n" ' sdl SDL user interface' printf "%s\n" ' sdl-image SDL Image support for icons' @@ -444,8 +441,6 @@ _meson_option_parse() { --enable-rng-none) printf "%s" -Drng_none=true ;; --disable-rng-none) printf "%s" -Drng_none=false ;; --rtsig-map=*) quote_sh "-Drtsig_map=$2" ;; - --enable-rust) printf "%s" -Drust=enabled ;; - --disable-rust) printf "%s" -Drust=disabled ;; --enable-rutabaga-gfx) printf "%s" -Drutabaga_gfx=enabled ;; --disable-rutabaga-gfx) printf "%s" -Drutabaga_gfx=disabled ;; --enable-safe-stack) printf "%s" -Dsafe_stack=true ;; @@ -475,8 +470,6 @@ _meson_option_parse() { --disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;; --enable-stack-protector) printf "%s" -Dstack_protector=enabled ;; --disable-stack-protector) printf "%s" -Dstack_protector=disabled ;; - --enable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=true ;; - --disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;; --enable-strip) printf "%s" -Dstrip=true ;; --disable-strip) printf "%s" -Dstrip=false ;; --sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;; diff --git a/scripts/rust/rust_root_crate.sh b/scripts/rust/rust_root_crate.sh deleted file mode 100755 index 975bddf7f..000000000 --- a/scripts/rust/rust_root_crate.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -set -eu - -cat < - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -import argparse -from dataclasses import dataclass -import logging -from pathlib import Path -from typing import Any, Iterable, List, Mapping, Optional, Set - -try: - import tomllib -except ImportError: - import tomli as tomllib - -STRICT_LINTS = {"unknown_lints", "warnings"} - - -class CargoTOML: - tomldata: Mapping[Any, Any] - workspace_data: Mapping[Any, Any] - check_cfg: Set[str] - - def __init__(self, path: Optional[str], workspace: Optional[str]): - if path is not None: - with open(path, 'rb') as f: - self.tomldata = tomllib.load(f) - else: - self.tomldata = {"lints": {"workspace": True}} - - if workspace is not None: - with open(workspace, 'rb') as f: - self.workspace_data = tomllib.load(f) - if "workspace" not in self.workspace_data: - self.workspace_data["workspace"] = {} - - self.check_cfg = set(self.find_check_cfg()) - - def find_check_cfg(self) -> Iterable[str]: - toml_lints = self.lints - rust_lints = toml_lints.get("rust", {}) - cfg_lint = rust_lints.get("unexpected_cfgs", {}) - return cfg_lint.get("check-cfg", []) - - @property - def lints(self) -> Mapping[Any, Any]: - return self.get_table("lints", True) - - def get_table(self, key: str, can_be_workspace: bool = False) -> Mapping[Any, Any]: - table = self.tomldata.get(key, {}) - if can_be_workspace and table.get("workspace", False) is True: - table = self.workspace_data["workspace"].get(key, {}) - - return table - - -@dataclass -class LintFlag: - flags: List[str] - priority: int - - -def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[str]: - """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" - - toml_lints = cargo_toml.lints - - lint_list = [] - for k, v in toml_lints.items(): - prefix = "" if k == "rust" else k + "::" - for lint, data in v.items(): - level = data if isinstance(data, str) else data["level"] - priority = 0 if isinstance(data, str) else data.get("priority", 0) - if level == "deny": - flag = "-D" - elif level == "allow": - flag = "-A" - elif level == "warn": - flag = "-W" - elif level == "forbid": - flag = "-F" - else: - raise Exception(f"invalid level {level} for {prefix}{lint}") - - if not (strict_lints and lint in STRICT_LINTS): - lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) - - if strict_lints: - for lint in STRICT_LINTS: - lint_list.append(LintFlag(flags=["-D", lint], priority=1000000)) - - lint_list.sort(key=lambda x: x.priority) - for lint in lint_list: - yield from lint.flags - - -def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: - """Converts defines from config[..].h headers to rustc --cfg flags.""" - - with open(header, encoding="utf-8") as cfg: - config = [l.split()[1:] for l in cfg if l.startswith("#define")] - - cfg_list = [] - for cfg in config: - name = cfg[0] - if f'cfg({name})' not in cargo_toml.check_cfg: - continue - if len(cfg) >= 2 and cfg[1] != "1": - continue - cfg_list.append("--cfg") - cfg_list.append(name) - return cfg_list - - -def main() -> None: - parser = argparse.ArgumentParser() - parser.add_argument("-v", "--verbose", action="store_true") - parser.add_argument( - "--config-headers", - metavar="CONFIG_HEADER", - action="append", - dest="config_headers", - help="paths to any configuration C headers (*.h files), if any", - required=False, - default=[], - ) - parser.add_argument( - metavar="TOML_FILE", - action="store", - dest="cargo_toml", - help="path to Cargo.toml file", - nargs='?', - ) - parser.add_argument( - "--workspace", - metavar="DIR", - action="store", - dest="workspace", - help="path to root of the workspace", - required=False, - default=None, - ) - parser.add_argument( - "--features", - action="store_true", - dest="features", - help="generate --check-cfg arguments for features", - required=False, - default=None, - ) - parser.add_argument( - "--lints", - action="store_true", - dest="lints", - help="generate arguments from [lints] table", - required=False, - default=None, - ) - parser.add_argument( - "--rustc-version", - metavar="VERSION", - dest="rustc_version", - action="store", - help="version of rustc", - required=False, - default="1.0.0", - ) - parser.add_argument( - "--strict-lints", - action="store_true", - dest="strict_lints", - help="apply stricter checks (for nightly Rust)", - default=False, - ) - args = parser.parse_args() - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - logging.debug("args: %s", args) - - rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) - if args.workspace: - workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve() - cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml)) - else: - cargo_toml = CargoTOML(args.cargo_toml, None) - - if args.lints: - for tok in generate_lint_flags(cargo_toml, args.strict_lints): - print(tok) - - if rustc_version >= (1, 80): - if args.lints: - print("--check-cfg") - print("cfg(test)") - for cfg in sorted(cargo_toml.check_cfg): - print("--check-cfg") - print(cfg) - if args.features: - for feature in cargo_toml.get_table("features"): - if feature != "default": - print("--check-cfg") - print(f'cfg(feature,values("{feature}"))') - - for header in args.config_headers: - for tok in generate_cfg_flags(header, cargo_toml): - print(tok) - - -if __name__ == "__main__": - main() diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 6050c081f..9317cc9d5 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -9,11 +9,6 @@ bool bql_locked(void) return bql_is_locked; } -void rust_bql_mock_lock(void) -{ - bql_is_locked = true; -} - void bql_lock_impl(const char *file, int line) { } diff --git a/system/cpus.c b/system/cpus.c index 7f199c7ec..d27fc8b33 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -554,12 +554,6 @@ bool qemu_in_main_thread(void) return bql_locked(); } -void rust_bql_mock_lock(void) -{ - error_report("This function should be used only from tests"); - abort(); -} - /* * The BQL is taken from so many places that it is worth profiling the * callers directly, instead of funneling them all through a single function. diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index bf3bd5a30..100f87641 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -92,7 +92,7 @@ RUN apk update && \ python3 \ rpm2cpio \ rust \ - rust-bindgen \ + samurai \ sdl2-dev \ sdl2_image-dev \ diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index a942835a1..bf917b17d 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -16,7 +16,7 @@ RUN dnf distro-sync -y && \ alsa-lib-devel \ bash \ bc \ - bindgen-cli \ + bison \ brlapi-devel \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 081f3e00f..0ced8b667 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 91c555a36..f8cddafba 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index f0e2efcda..72179716b 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 025beb1ce..d28c7aa8f 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 4a941dd87..678bb4fa2 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 4d3e5d711..3f82f9b4b 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 22b4457ba..8f0014f27 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 13ec52c8a..4058dcc94 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 0a57c1a1d..63fc0040d 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -13,7 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ - bindgen \ + bison \ bsdextrautils \ bzip2 \ diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker deleted file mode 100644 index 4a033309b..000000000 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ /dev/null @@ -1,183 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all fedora-40 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM registry.fedoraproject.org/fedora:40 - -RUN dnf install -y nosync && \ - printf '#!/bin/sh\n\ -if test -d /usr/lib64\n\ -then\n\ - export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\ -else\n\ - export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\ -fi\n\ -exec "$@"\n' > /usr/bin/nosync && \ - chmod +x /usr/bin/nosync && \ - nosync dnf update -y && \ - nosync dnf install -y \ - SDL2-devel \ - SDL2_image-devel \ - alsa-lib-devel \ - bash \ - bc \ - bindgen-cli \ - bison \ - brlapi-devel \ - bzip2 \ - bzip2-devel \ - ca-certificates \ - capstone-devel \ - ccache \ - clang \ - ctags \ - cyrus-sasl-devel \ - daxctl-devel \ - dbus-daemon \ - device-mapper-multipath-devel \ - diffutils \ - findutils \ - flex \ - fuse3-devel \ - gcc \ - gcovr \ - gettext \ - git \ - glib2-devel \ - glib2-static \ - glibc-langpack-en \ - glibc-static \ - glusterfs-api-devel \ - gnutls-devel \ - gtk-vnc2-devel \ - gtk3-devel \ - hostname \ - jemalloc-devel \ - json-c-devel \ - libaio-devel \ - libasan \ - libattr-devel \ - libbpf-devel \ - libcacard-devel \ - libcap-ng-devel \ - libcbor-devel \ - libcmocka-devel \ - libcurl-devel \ - libdrm-devel \ - libepoxy-devel \ - libfdt-devel \ - libffi-devel \ - libgcrypt-devel \ - libiscsi-devel \ - libjpeg-devel \ - libnfs-devel \ - libpmem-devel \ - libpng-devel \ - librbd-devel \ - libseccomp-devel \ - libselinux-devel \ - libslirp-devel \ - libssh-devel \ - libtasn1-devel \ - libubsan \ - liburing-devel \ - libusbx-devel \ - libxdp-devel \ - libzstd-devel \ - llvm \ - lttng-ust-devel \ - lzo-devel \ - make \ - mesa-libgbm-devel \ - meson \ - mtools \ - ncurses-devel \ - nettle-devel \ - ninja-build \ - nmap-ncat \ - numactl-devel \ - openssh-clients \ - pam-devel \ - pcre-static \ - pipewire-devel \ - pixman-devel \ - pkgconfig \ - pulseaudio-libs-devel \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx_rtd_theme \ - python3-zombie-imp \ - rdma-core-devel \ - rust \ - sed \ - snappy-devel \ - socat \ - sparse \ - spice-protocol \ - spice-server-devel \ - swtpm \ - systemd-devel \ - systemtap-sdt-devel \ - tar \ - tesseract \ - tesseract-langpack-eng \ - usbredir-devel \ - util-linux \ - virglrenderer-devel \ - vte291-devel \ - vulkan-tools \ - which \ - xen-devel \ - xorriso \ - zlib-devel \ - zlib-static \ - zstd && \ - nosync dnf autoremove -y && \ - nosync dnf clean all -y && \ - rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ - rpm -qa | sort > /packages.txt && \ - mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -RUN dnf install -y wget -ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo -ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc -ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc -ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo -RUN set -eux && \ - rustArch='x86_64-unknown-linux-gnu' && \ - rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \ - url="https://static.rust-lang.org/rustup/archive/1.27.1/${rustArch}/rustup-init" && \ - wget "$url" && \ - echo "${rustupSha256} *rustup-init" | sha256sum -c - && \ - chmod +x rustup-init && \ - ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \ - chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \ - /usr/local/cargo/bin/rustup --version && \ - /usr/local/cargo/bin/rustup run nightly cargo --version && \ - /usr/local/cargo/bin/rustup run nightly rustc --version && \ - test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \ - test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \ - test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)" -ENV PATH=$CARGO_HOME/bin:$PATH -RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli -RUN $CARGO --list -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index a95034440..cfe4c94ec 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -20,7 +20,7 @@ exec "$@"\n' > /usr/bin/nosync && \ nosync dnf install -y \ bash \ bc \ - bindgen-cli \ + bison \ bzip2 \ ca-certificates \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 014e3ccf1..c152003dd 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -23,7 +23,7 @@ exec "$@"\n' > /usr/bin/nosync && \ alsa-lib-devel \ bash \ bc \ - bindgen-cli \ + bison \ brlapi-devel \ bzip2 \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index e90225dc2..a51cb24f6 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -98,7 +98,7 @@ RUN zypper update -y && \ python311-setuptools \ rdma-core-devel \ rust \ - rust-bindgen \ + sed \ snappy-devel \ sndio-devel \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 28a6f9324..08a5ca145 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -150,13 +150,6 @@ ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV RUSTC=/usr/bin/rustc-1.77 -ENV RUSTDOC=/usr/bin/rustdoc-1.77 -ENV CARGO_HOME=/usr/local/cargo -ENV PATH=$CARGO_HOME/bin:$PATH -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends cargo -RUN cargo install bindgen-cli # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/test-rust b/tests/docker/test-rust deleted file mode 100755 index e7e3e94a5..000000000 --- a/tests/docker/test-rust +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -e -# -# Run the rust code checks (a.k.a. check-rust-tools-nightly) -# -# Copyright (c) 2025 Linaro Ltd -# -# Authors: -# Alex Bennée -# -# This work is licensed under the terms of the GNU GPL, version 2 -# or (at your option) any later version. See the COPYING file in -# the top-level directory. - -. common.rc - -cd "$BUILD_DIR" - -configure_qemu --disable-user --disable-docs --enable-rust -pyvenv/bin/meson devenv -w $QEMU_SRC/rust ${CARGO-cargo} fmt --check -make clippy -make rustdoc diff --git a/tests/lcitool/libvirt-ci/lcitool/facts/mappings.yml b/tests/lcitool/libvirt-ci/lcitool/facts/mappings.yml index be43ef64d..29a3875ba 100644 --- a/tests/lcitool/libvirt-ci/lcitool/facts/mappings.yml +++ b/tests/lcitool/libvirt-ci/lcitool/facts/mappings.yml @@ -166,14 +166,6 @@ mappings: default: bc pkg: - # currently OpenSUSE only packages bindgen in Tumbleweed - bindgen: - default: bindgen - apk: rust-bindgen - pkg: rust-bindgen-cli - rpm: bindgen-cli - OpenSUSE: rust-bindgen - bison: default: bison diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/in/packages.yml b/tests/lcitool/libvirt-ci/tests/data/packages/in/packages.yml index 8fef8b930..e98d40579 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/in/packages.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/in/packages.yml @@ -12,7 +12,6 @@ packages: - bash - bash-completion - bc -- bindgen - bison - black - brlapi diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/almalinux-9.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/almalinux-9.yml index 6597a6e48..e849c468f 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/almalinux-9.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/almalinux-9.yml @@ -16,7 +16,6 @@ native: - bash - bash-completion - bc -- bindgen-cli - bison - brlapi-devel - bzip2 diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-321.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-321.yml index 009a464a2..982d2a67d 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-321.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-321.yml @@ -217,7 +217,6 @@ native: - ruby-dev - ruby-rake - rust -- rust-bindgen - rust-clippy - samurai - screen diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-322.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-322.yml index 009a464a2..982d2a67d 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-322.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-322.yml @@ -217,7 +217,6 @@ native: - ruby-dev - ruby-rake - rust -- rust-bindgen - rust-clippy - samurai - screen diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-edge.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-edge.yml index 009a464a2..982d2a67d 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-edge.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/alpine-edge.yml @@ -217,7 +217,6 @@ native: - ruby-dev - ruby-rake - rust -- rust-bindgen - rust-clippy - samurai - screen diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/centos-stream-9.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/centos-stream-9.yml index 6597a6e48..e849c468f 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/centos-stream-9.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/centos-stream-9.yml @@ -16,7 +16,6 @@ native: - bash - bash-completion - bc -- bindgen-cli - bison - brlapi-devel - bzip2 diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12-cross-s390x.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12-cross-s390x.yml index 490df3fe5..db912c33b 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12-cross-s390x.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12-cross-s390x.yml @@ -120,7 +120,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bsdextrautils diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12.yml index c0edd6e5b..b09a49376 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-12.yml @@ -9,7 +9,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bsdextrautils diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-13.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-13.yml index dc0547021..70fc9430d 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-13.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-13.yml @@ -9,7 +9,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bsdextrautils diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-sid.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-sid.yml index dc0547021..70fc9430d 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-sid.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/debian-sid.yml @@ -9,7 +9,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bsdextrautils diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-41.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-41.yml index dd84aca9c..ff830a845 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-41.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-41.yml @@ -11,7 +11,6 @@ native: - bash - bash-completion-devel - bc -- bindgen-cli - bison - brlapi-devel - bzip2 diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-42.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-42.yml index 6a68fe3a0..6912ca369 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-42.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-42.yml @@ -10,7 +10,6 @@ native: - bash - bash-completion-devel - bc -- bindgen-cli - bison - brlapi-devel - bzip2 diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide-cross-mingw64.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide-cross-mingw64.yml index 7384ba762..202402863 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide-cross-mingw64.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide-cross-mingw64.yml @@ -48,7 +48,6 @@ native: - bash - bash-completion-devel - bc -- bindgen-cli - bison - bzip2 - ca-certificates diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide.yml index 6a68fe3a0..6912ca369 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/fedora-rawhide.yml @@ -10,7 +10,6 @@ native: - bash - bash-completion-devel - bc -- bindgen-cli - bison - brlapi-devel - bzip2 diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-13.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-13.yml index 100da1a9d..18f3a3fdf 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-13.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-13.yml @@ -167,7 +167,6 @@ native: - ruby - rubygem-rake - rust -- rust-bindgen-cli - screen - sdl2 - sdl2_image diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-14.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-14.yml index 100da1a9d..18f3a3fdf 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-14.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-14.yml @@ -167,7 +167,6 @@ native: - ruby - rubygem-rake - rust -- rust-bindgen-cli - screen - sdl2 - sdl2_image diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-current.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-current.yml index 100da1a9d..18f3a3fdf 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-current.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/freebsd-current.yml @@ -167,7 +167,6 @@ native: - ruby - rubygem-rake - rust -- rust-bindgen-cli - screen - sdl2 - sdl2_image diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/macos-14.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/macos-14.yml index c7613897d..d3f5d50f4 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/macos-14.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/macos-14.yml @@ -36,7 +36,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bzip2 diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-leap-15.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-leap-15.yml index 368453e5e..a55a4f27b 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-leap-15.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-leap-15.yml @@ -247,7 +247,6 @@ native: - ruby - ruby-devel - rust -- rust-bindgen - sanlock-devel - screen - scrub diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-tumbleweed.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-tumbleweed.yml index 4aebc2586..bec039060 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-tumbleweed.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/opensuse-tumbleweed.yml @@ -246,7 +246,6 @@ native: - ruby - ruby-devel - rust -- rust-bindgen - sanlock-devel - screen - scrub diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2204.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2204.yml index 5e666b5f1..a3b356627 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2204.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2204.yml @@ -9,7 +9,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bsdextrautils diff --git a/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2404.yml b/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2404.yml index 4756ddb8f..8f1e47d90 100644 --- a/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2404.yml +++ b/tests/lcitool/libvirt-ci/tests/data/packages/out/ubuntu-2404.yml @@ -9,7 +9,6 @@ native: - bash - bash-completion - bc -- bindgen - bison - black - bsdextrautils diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 8f0e95e1c..b34ad2e92 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -1,14 +1,10 @@ mappings: - # Too old on Ubuntu 22.04; we install it from cargo instead - bindgen: - Ubuntu2204: - flake8: OpenSUSELeap15: meson: OpenSUSELeap15: - # Use Meson from PyPI wherever Rust is enabled + # Use Meson from PyPI for consistent toolchain versions Debian: Fedora: Ubuntu: diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index c07242f27..b50f5af05 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -3,7 +3,6 @@ packages: - alsa - bash - bc - - bindgen - bison - brlapi - bzip2 diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index d3488b267..6f4fcd0cb 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -116,43 +116,6 @@ debian12_extras = [ "ENV QEMU_CONFIGURE_OPTS --enable-netmap\n" ] -# Based on the hub.docker.com/library/rust Dockerfiles -fedora_rustup_nightly_extras = [ - "RUN dnf install -y wget\n", - "ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n", - "ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n", - "ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc\n", - "ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\n", - "RUN set -eux && \\\n", - " rustArch='x86_64-unknown-linux-gnu' && \\\n", - " rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \\\n", - ' url="https://static.rust-lang.org/rustup/archive/1.27.1/${rustArch}/rustup-init" && \\\n', - ' wget "$url" && \\\n', - ' echo "${rustupSha256} *rustup-init" | sha256sum -c - && \\\n', - " chmod +x rustup-init && \\\n", - " ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \\\n", - " chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \\\n", - " /usr/local/cargo/bin/rustup --version && \\\n", - " /usr/local/cargo/bin/rustup run nightly cargo --version && \\\n", - " /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n", - ' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n', - ' test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \\\n', - ' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n', - 'ENV PATH=$CARGO_HOME/bin:$PATH\n', - 'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n', - 'RUN $CARGO --list\n', -] - -ubuntu2204_rust_extras = [ - "ENV RUSTC=/usr/bin/rustc-1.77\n", - "ENV RUSTDOC=/usr/bin/rustdoc-1.77\n", - "ENV CARGO_HOME=/usr/local/cargo\n", - 'ENV PATH=$CARGO_HOME/bin:$PATH\n', - "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", - " apt install -y --no-install-recommends cargo\n", - 'RUN cargo install bindgen-cli\n', -] - def cross_build(prefix, targets): conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix) targets = "ENV DEF_TARGET_LIST %s\n" % (targets) @@ -173,14 +136,7 @@ try: trailer="".join(debian12_extras)) generate_dockerfile("fedora", "fedora-40") generate_dockerfile("opensuse-leap", "opensuse-leap-15") - generate_dockerfile("ubuntu2204", "ubuntu-2204", - trailer="".join(ubuntu2204_rust_extras)) - - # - # Non-fatal Rust-enabled build - # - generate_dockerfile("fedora-rust-nightly", "fedora-40", - trailer="".join(fedora_rustup_nightly_extras)) + generate_dockerfile("ubuntu2204", "ubuntu-2204") # # Cross compiling builds diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index c03e1cd58..0c50f29bf 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -61,8 +61,6 @@ "py311-tomli", "python3", "rpm2cpio", - "rust", - "rust-bindgen-cli", "sdl2", "sdl2_image", "snappy", @@ -78,5 +76,6 @@ "zstd" ], "pypi_pkgs": [], - "python": "/usr/local/bin/python3" + "python": "/usr/local/bin/python3", + "pkg_pkgs": [] } diff --git a/util/log.c b/util/log.c index abdcb6b31..19c87cddc 100644 --- a/util/log.c +++ b/util/log.c @@ -576,15 +576,3 @@ void qemu_print_log_usage(FILE *f) fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n"); #endif } - -#ifdef CONFIG_HAVE_RUST -ssize_t rust_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) -{ - /* - * Same as fwrite, but return -errno because Rust libc does not provide - * portable access to errno. :( - */ - int ret = fwrite(ptr, size, nmemb, stream); - return ret < 0 ? -errno : 0; -} -#endif