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

* rust: use native Meson support for clippy and rustdoc
* rust: add "bits", a custom bitflags implementation
* target/i386: Remove FRED dependency on WRMSRNS
* target/i386: Add the immediate form MSR access instruction support
* TDX fixes

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmg/XrsUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroOPIwf/VXh98Wd+7BJLkNJVFpczSF7YhJ5J
# a5BcWLOdVrzEJoqvfc9lkubgpShgzYDYJH99F/FloHddkPvZ1NRB2JXtDB1O3sSC
# NGaI4YM8uA/k21pt1jQtDJkk3Az7GNIBIcvi4HR5GjTOvOKGOXLpYErK52lM4GNG
# Aa17/Rb9Ug+QzyuS1M+mDPFdY2X6Hore2jXsp3ZH+U8hs+khecHEPsZUZ/Nlr1Z7
# UoiYks4U29wtVJ/BCjNkgXoMJC6uqL/nOP5dLJBgboOodrtwdwpDMIUcyPLrOnjf
# ugJx0zYHIVdqpdft72EvLD92bzB8WoUiPsUA/dG45gGmhzuYWDmOqSdaKg==
# =l0gm
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 03 Jun 2025 16:44:43 EDT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu:
  rust: qemu-api-macros: add from_bits and into_bits to #[derive(TryInto)]
  rust: pl011: use the bits macro
  rust: add "bits", a custom bitflags implementation
  i386/tdvf: Fix build on 32-bit host
  i386/tdx: Fix build on 32-bit host
  meson: use config_base_arch for target libraries
  target/i386: Add the immediate form MSR access instruction support
  target/i386: Add a new CPU feature word for CPUID.7.1.ECX
  target/i386: Remove FRED dependency on WRMSRNS
  rust: use native Meson support for clippy and rustdoc
  rust: cell: remove support for running doctests with "cargo test --doc"
  rust: add qemu-api doctests to "meson test"
  build, dockerfiles: add support for detecting rustdoc
  rust: use "objects" for Rust executables as well
  meson: update to version 1.8.1
  rust: bindings: allow ptr_offset_with_cast

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2025-06-04 11:43:31 -04:00
34 changed files with 934 additions and 149 deletions
+2 -1
View File
@@ -76,7 +76,8 @@
fi
- section_end buildenv
- section_start test "Running tests"
- $MAKE NINJA=":" $MAKE_CHECK_ARGS
# doctests need all the compilation artifacts
- $MAKE NINJA=":" MTESTARGS="--no-suite doc" $MAKE_CHECK_ARGS
- section_end test
.native_test_job_template:
+3 -8
View File
@@ -41,7 +41,7 @@ build-system-ubuntu:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-docs --enable-rust
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
MAKE_CHECK_ARGS: check-build
MAKE_CHECK_ARGS: check-build check-doc
check-system-ubuntu:
extends: .native_test_job_template
@@ -115,7 +115,7 @@ build-system-fedora:
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
TARGETS: microblaze-softmmu mips-softmmu
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
MAKE_CHECK_ARGS: check-build
MAKE_CHECK_ARGS: check-build check-doc
build-system-fedora-rust-nightly:
extends:
@@ -127,12 +127,7 @@ build-system-fedora-rust-nightly:
IMAGE: fedora-rust-nightly
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
TARGETS: aarch64-softmmu
MAKE_CHECK_ARGS: check-build
after_script:
- source scripts/ci/gitlab-ci-section
- section_start test "Running Rust doctests"
- cd build
- pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api
MAKE_CHECK_ARGS: check-build check-doc
allow_failure: true
+1 -1
View File
@@ -1,3 +1,3 @@
doc-valid-idents = ["PrimeCell", ".."]
doc-valid-idents = ["IrDA", "PrimeCell", ".."]
allow-mixed-uninlined-format-args = false
msrv = "1.77.0"
Vendored
+16
View File
@@ -209,6 +209,8 @@ for opt do
;;
--rustc=*) RUSTC="$optarg"
;;
--rustdoc=*) RUSTDOC="$optarg"
;;
--cpu=*) cpu="$optarg"
;;
--extra-cflags=*)
@@ -323,6 +325,7 @@ 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 <<EOF
@@ -660,6 +663,8 @@ for opt do
;;
--rustc=*)
;;
--rustdoc=*)
;;
--make=*)
;;
--install=*)
@@ -890,6 +895,7 @@ Advanced options (experts only):
--cxx=CXX use C++ compiler CXX [$cxx]
--objcc=OBJCC use Objective-C compiler OBJCC [$objcc]
--rustc=RUSTC use Rust compiler RUSTC [$rustc]
--rustdoc=RUSTDOC use rustdoc binary RUSTDOC [$rustdoc]
--extra-cflags=CFLAGS append extra C compiler flags CFLAGS
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS
--extra-objcflags=OBJCFLAGS append extra Objective C compiler flags OBJCFLAGS
@@ -1178,6 +1184,14 @@ fi
##########################################
# detect rust triple
meson_version=$($meson --version)
if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then
if test "$rust" = enabled; then
error_exit "Rust support needs Meson 1.8.1 or newer"
fi
echo "Rust needs Meson 1.8.1, disabling" 2>&1
rust=disabled
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
@@ -1893,8 +1907,10 @@ if test "$skip_meson" = no; then
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
+8 -4
View File
@@ -37,12 +37,16 @@ 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 special shorthand targets in the QEMU build directory::
* by using Makefile targets, provided by Meson, that run ``clippy`` or
``rustdoc``:
make clippy
make rustfmt
make rustdoc
A target for ``rustfmt`` is also declared in ``rust/meson.build``:
make rustfmt
* by invoking ``cargo`` through the Meson `development environment`__
feature::
@@ -50,7 +54,7 @@ which can be fixed in different ways:
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 clippy`` just work.
will enter a shell where commands like ``cargo fmt`` just work.
__ https://mesonbuild.com/Commands.html#devenv
@@ -66,7 +70,7 @@ be run via ``meson test`` or ``make``::
make check-rust
Building Rust code with ``--enable-modules`` is not supported yet.
Note that doctests require all ``.o`` files from the build to be available.
Supported tools
'''''''''''''''
+3 -3
View File
@@ -101,16 +101,16 @@ static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src,
/* sanity check */
if (entry->size < entry->data_len) {
error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%lx",
error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%"PRIx64,
entry->data_len, entry->size);
return -1;
}
if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) {
error_report("MemoryAddress 0x%lx not page aligned", entry->address);
error_report("MemoryAddress 0x%"PRIx64" not page aligned", entry->address);
return -1;
}
if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) {
error_report("MemoryDataSize 0x%lx not page aligned", entry->size);
error_report("MemoryDataSize 0x%"PRIx64" not page aligned", entry->size);
return -1;
}
+6 -5
View File
@@ -106,6 +106,7 @@ if have_rust
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()
@@ -4134,13 +4135,12 @@ common_all = static_library('common',
target_common_arch_libs = {}
target_common_system_arch_libs = {}
foreach target_base_arch, config_base_arch : config_base_arch_mak
config_target = config_target_mak[target]
target_inc = [include_directories('target' / target_base_arch)]
inc = [common_user_inc + target_inc]
target_common = common_ss.apply(config_target, strict: false)
target_system = system_ss.apply(config_target, strict: false)
target_user = user_ss.apply(config_target, strict: false)
target_common = common_ss.apply(config_base_arch, strict: false)
target_system = system_ss.apply(config_base_arch, strict: false)
target_user = user_ss.apply(config_base_arch, strict: false)
common_deps = []
system_deps = []
user_deps = []
@@ -4403,7 +4403,7 @@ foreach target : target_dirs
build_by_default: true,
build_always_stale: true)
rlib = static_library('rust_' + target.underscorify(),
rlib_rs,
structured_sources([], {'.': rlib_rs}),
dependencies: target_rust.dependencies(),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'c')
@@ -4757,6 +4757,7 @@ 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
+2 -2
View File
@@ -41,8 +41,8 @@ def main() -> int:
parser.parse_args()
packages = {
"meson==1.5.0":
"52b34f4903b882df52ad0d533146d4b992c018ea77399f825579737672ae7b20",
"meson==1.8.1":
"374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da",
}
vendor_dir = Path(__file__, "..", "..", "wheels").resolve()
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -19,7 +19,7 @@
[meson]
# The install key should match the version in python/wheels/
meson = { accepted = ">=1.5.0", installed = "1.5.0", canary = "meson" }
meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" }
pycotap = { accepted = ">=1.1.0", installed = "1.3.1" }
[docs]
+8
View File
@@ -31,6 +31,13 @@ dependencies = [
"syn",
]
[[package]]
name = "bits"
version = "0.1.0"
dependencies = [
"qemu_api_macros",
]
[[package]]
name = "either"
version = "1.12.0"
@@ -66,6 +73,7 @@ version = "0.1.0"
dependencies = [
"bilge",
"bilge-impl",
"bits",
"qemu_api",
"qemu_api_macros",
]
+1 -1
View File
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
"bits",
"qemu-api-macros",
"qemu-api",
"hw/char/pl011",
@@ -63,7 +64,6 @@ ignored_unit_patterns = "deny"
implicit_clone = "deny"
macro_use_imports = "deny"
missing_safety_doc = "deny"
multiple_crate_versions = "deny"
mut_mut = "deny"
needless_bitwise_bool = "deny"
needless_pass_by_ref_mut = "deny"
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "bits"
version = "0.1.0"
authors = ["Paolo Bonzini <pbonzini@redhat.com>"]
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_api_macros = { path = "../qemu-api-macros" }
[lints]
workspace = true
+16
View File
@@ -0,0 +1,16 @@
_bits_rs = static_library(
'bits',
'src/lib.rs',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [qemu_api_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'])
+443
View File
@@ -0,0 +1,443 @@
// 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`]`<type>`/[`Into`]`<type>` 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 fn set(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
#[allow(dead_code)]
#[inline(always)]
pub fn clear(&mut self, rhs: Self) {
self.0 &= !rhs.0;
}
#[allow(dead_code)]
#[inline(always)]
pub 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 } => {
::qemu_api_macros::bits_const_internal! { $type @ ($expr) }
};
{ $type:ty as $int_type:ty: $expr:expr } => {
(::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
};
}
#[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
);
}
}
+1
View File
@@ -18,6 +18,7 @@ crate-type = ["staticlib"]
[dependencies]
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
bits = { path = "../../../bits" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
+1
View File
@@ -6,6 +6,7 @@ _libpl011_rs = static_library(
dependencies: [
bilge_rs,
bilge_impl_rs,
bits_rs,
qemu_api,
qemu_api_macros,
],
+25 -26
View File
@@ -85,8 +85,8 @@ pub struct PL011Registers {
#[doc(alias = "cr")]
pub control: registers::Control,
pub dmacr: u32,
pub int_enabled: u32,
pub int_level: u32,
pub int_enabled: Interrupt,
pub int_level: Interrupt,
pub read_fifo: Fifo,
pub ilpr: u32,
pub ibrd: u32,
@@ -199,9 +199,9 @@ impl PL011Registers {
LCR_H => u32::from(self.line_control),
CR => u32::from(self.control),
FLS => self.ifl,
IMSC => self.int_enabled,
RIS => self.int_level,
MIS => self.int_level & self.int_enabled,
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
@@ -263,13 +263,13 @@ impl PL011Registers {
self.set_read_trigger();
}
IMSC => {
self.int_enabled = value;
self.int_enabled = Interrupt::from(value);
return true;
}
RIS => {}
MIS => {}
ICR => {
self.int_level &= !value;
self.int_level &= !Interrupt::from(value);
return true;
}
DMACR => {
@@ -295,7 +295,7 @@ impl PL011Registers {
self.flags.set_receive_fifo_empty(true);
}
if self.read_count + 1 == self.read_trigger {
self.int_level &= !Interrupt::RX.0;
self.int_level &= !Interrupt::RX;
}
self.receive_status_error_clear.set_from_data(c);
*update = true;
@@ -305,7 +305,7 @@ impl PL011Registers {
fn write_data_register(&mut self, value: u32) -> bool {
// interrupts always checked
let _ = self.loopback_tx(value.into());
self.int_level |= Interrupt::TX.0;
self.int_level |= Interrupt::TX;
true
}
@@ -361,19 +361,19 @@ impl PL011Registers {
// Change interrupts based on updated FR
let mut il = self.int_level;
il &= !Interrupt::MS.0;
il &= !Interrupt::MS;
if self.flags.data_set_ready() {
il |= Interrupt::DSR.0;
il |= Interrupt::DSR;
}
if self.flags.data_carrier_detect() {
il |= Interrupt::DCD.0;
il |= Interrupt::DCD;
}
if self.flags.clear_to_send() {
il |= Interrupt::CTS.0;
il |= Interrupt::CTS;
}
if self.flags.ring_indicator() {
il |= Interrupt::RI.0;
il |= Interrupt::RI;
}
self.int_level = il;
true
@@ -391,8 +391,8 @@ impl PL011Registers {
self.line_control.reset();
self.receive_status_error_clear.reset();
self.dmacr = 0;
self.int_enabled = 0;
self.int_level = 0;
self.int_enabled = 0.into();
self.int_level = 0.into();
self.ilpr = 0;
self.ibrd = 0;
self.fbrd = 0;
@@ -451,7 +451,7 @@ impl PL011Registers {
}
if self.read_count == self.read_trigger {
self.int_level |= Interrupt::RX.0;
self.int_level |= Interrupt::RX;
return true;
}
false
@@ -632,7 +632,7 @@ impl PL011State {
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 & i != 0);
irq.set(flags.any_set(i));
}
}
@@ -642,14 +642,13 @@ impl PL011State {
}
/// Which bits in the interrupt status matter for each outbound IRQ line ?
const IRQMASK: [u32; 6] = [
/* combined IRQ */
Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0,
Interrupt::RX.0,
Interrupt::TX.0,
Interrupt::RT.0,
Interrupt::MS.0,
Interrupt::E.0,
const IRQMASK: [Interrupt; 6] = [
Interrupt::all(),
Interrupt::RX,
Interrupt::TX,
Interrupt::RT,
Interrupt::MS,
Interrupt::E,
];
/// # Safety
+21 -18
View File
@@ -9,7 +9,8 @@
// https://developer.arm.com/documentation/ddi0183/latest/
use bilge::prelude::*;
use qemu_api::impl_vmstate_bitsized;
use bits::bits;
use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward};
/// Offset of each register from the base memory address of the device.
#[doc(alias = "offset")]
@@ -326,22 +327,24 @@ impl Default for Control {
}
}
/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
pub struct Interrupt(pub u32);
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,
impl Interrupt {
pub const OE: Self = Self(1 << 10);
pub const BE: Self = Self(1 << 9);
pub const PE: Self = Self(1 << 8);
pub const FE: Self = Self(1 << 7);
pub const RT: Self = Self(1 << 6);
pub const TX: Self = Self(1 << 5);
pub const RX: Self = Self(1 << 4);
pub const DSR: Self = Self(1 << 3);
pub const DCD: Self = Self(1 << 2);
pub const CTS: Self = Self(1 << 1);
pub const RI: Self = Self(1 << 0);
pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0);
pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0);
E = bits!(Self as u32: OE | BE | PE | FE),
MS = bits!(Self as u32: RI | DSR | DCD | CTS),
}
}
impl_vmstate_forward!(Interrupt);

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