rust: add PL011 device model

This commit adds a re-implementation of hw/char/pl011.c in Rust.

How to build:

1. Configure a QEMU build with:
   --enable-system --target-list=aarch64-softmmu --enable-rust
2. Launching a VM with qemu-system-aarch64 should use the Rust version
   of the pl011 device

Co-authored-by: Junjie Mao <junjie.mao@intel.com>
Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Link: https://lore.kernel.org/r/20241024-rust-round-2-v1-2-051e7a25b978@linaro.org
This commit is contained in:
Manos Pitsidianakis
2024-11-05 14:18:15 +01:00
committed by Paolo Bonzini
co-authored by Junjie Mao Paolo Bonzini
parent ca5aa28e24
commit 37fdb2f56a
37 changed files with 1906 additions and 12 deletions
+5
View File
@@ -1146,6 +1146,11 @@ F: include/hw/*/microbit*.h
F: tests/qtest/microbit-test.c
F: docs/system/arm/nrf.rst
ARM PL011 Rust device
M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
S: Maintained
F: rust/hw/char/pl011/
AVR Machines
-------------
+20 -10
View File
@@ -20,7 +20,8 @@ config ARM_VIRT
select PCI_EXPRESS
select PCI_EXPRESS_GENERIC_BRIDGE
select PFLASH_CFI01
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL031 # RTC
select PL061 # GPIO
select GPIO_PWR
@@ -73,7 +74,8 @@ config HIGHBANK
select AHCI
select ARM_TIMER # sp804
select ARM_V7M
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL022 # SPI
select PL031 # RTC
select PL061 # GPIO
@@ -86,7 +88,8 @@ config INTEGRATOR
depends on TCG && ARM
select ARM_TIMER
select INTEGRATOR_DEBUG
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL031 # RTC
select PL041 # audio
select PL050 # keyboard/mouse
@@ -104,7 +107,8 @@ config MUSCA
default y
depends on TCG && ARM
select ARMSSE
select PL011
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL031
select SPLIT_IRQ
select UNIMP
@@ -168,7 +172,8 @@ config REALVIEW
select WM8750 # audio codec
select LSI_SCSI_PCI
select PCI
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL031 # RTC
select PL041 # audio codec
select PL050 # keyboard/mouse
@@ -193,7 +198,8 @@ config SBSA_REF
select PCI_EXPRESS
select PCI_EXPRESS_GENERIC_BRIDGE
select PFLASH_CFI01
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL031 # RTC
select PL061 # GPIO
select USB_XHCI_SYSBUS
@@ -217,7 +223,8 @@ config STELLARIS
select ARM_V7M
select CMSDK_APB_WATCHDOG
select I2C
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL022 # SPI
select PL061 # GPIO
select SSD0303 # OLED display
@@ -277,7 +284,8 @@ config VEXPRESS
select ARM_TIMER # sp804
select LAN9118
select PFLASH_CFI01
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select PL041 # audio codec
select PL181 # display
select REALVIEW
@@ -362,7 +370,8 @@ config RASPI
default y
depends on TCG && ARM
select FRAMEBUFFER
select PL011 # UART
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select SDHCI
select USB_DWC2
select BCM2835_SPI
@@ -438,7 +447,8 @@ config XLNX_VERSAL
select ARM_GIC
select CPU_CLUSTER
select DEVICE_TREE
select PL011
select PL011 if !HAVE_RUST # UART
select X_PL011_RUST if HAVE_RUST # UART
select CADENCE
select VIRTIO_MMIO
select UNIMP
+24
View File
@@ -3540,6 +3540,7 @@ 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()
@@ -4087,6 +4088,29 @@ foreach target : target_dirs
arch_srcs += target_specific.sources()
arch_deps += target_specific.dependencies()
if have_rust and have_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: [find_program('scripts/rust/rust_root_crate.sh')] + crates,
capture: true,
build_by_default: true,
build_always_stale: true)
rlib = static_library('rust_' + target.underscorify(),
rlib_rs,
dependencies: target_rust.dependencies(),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: rustc_args,
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.
+1
View File
@@ -0,0 +1 @@
source hw/Kconfig
+2
View File
@@ -0,0 +1,2 @@
# devices Kconfig
source char/Kconfig
+3
View File
@@ -0,0 +1,3 @@
config X_PL011_RUST
bool
default y if HAVE_RUST
+1
View File
@@ -0,0 +1 @@
subdir('pl011')
+2
View File
@@ -0,0 +1,2 @@
# Ignore generated bindings file overrides.
src/bindings.rs.inc
+134
View File
@@ -0,0 +1,134 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[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 = "either"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "pl011"
version = "0.1.0"
dependencies = [
"bilge",
"bilge-impl",
"qemu_api",
"qemu_api_macros",
]
[[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.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "qemu_api"
version = "0.1.0"
[[package]]
name = "qemu_api_macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[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.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "pl011"
version = "0.1.0"
edition = "2021"
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
license = "GPL-2.0-or-later"
readme = "README.md"
homepage = "https://www.qemu.org"
description = "pl011 device model for QEMU"
repository = "https://gitlab.com/epilys/rust-for-qemu"
resolver = "2"
publish = false
keywords = []
categories = []
[lib]
crate-type = ["staticlib"]
[dependencies]
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
# Do not include in any global workspace
[workspace]
+31
View File
@@ -0,0 +1,31 @@
# PL011 QEMU Device Model
This library implements a device model for the PrimeCell® UART (PL011)
device in QEMU.
## Build static lib
Host build target must be explicitly specified:
```sh
cargo build --target x86_64-unknown-linux-gnu
```
Replace host target triplet if necessary.
## Generate Rust documentation
To generate docs for this crate, including private items:
```sh
cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu
```
To include direct dependencies like `bilge` (bitmaps for register types):
```sh
cargo tree --depth 1 -e normal --prefix none \
| cut -d' ' -f1 \
| xargs printf -- '-p %s\n' \
| xargs cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu
```
+26
View File
@@ -0,0 +1,26 @@
subproject('bilge-0.2-rs', required: true)
subproject('bilge-impl-0.2-rs', required: true)
bilge_dep = dependency('bilge-0.2-rs')
bilge_impl_dep = dependency('bilge-impl-0.2-rs')
_libpl011_rs = static_library(
'pl011',
files('src/lib.rs'),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [
bilge_dep,
bilge_impl_dep,
qemu_api,
qemu_api_macros,
],
)
rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
link_whole: [_libpl011_rs],
# Putting proc macro crates in `dependencies` is necessary for Meson to find
# them when compiling the root per-target static rust lib.
dependencies: [bilge_impl_dep, qemu_api_macros],
variables: {'crate': 'pl011'},
)])
File diff suppressed because it is too large Load Diff
+70
View File
@@ -0,0 +1,70 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
use core::ptr::NonNull;
use qemu_api::{bindings::*, definitions::ObjectImpl};
use crate::device::PL011State;
#[used]
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
name: PL011State::TYPE_INFO.name,
unmigratable: true,
..unsafe { ::core::mem::MaybeUninit::<VMStateDescription>::zeroed().assume_init() }
};
qemu_api::declare_properties! {
PL011_PROPERTIES,
qemu_api::define_property!(
c"chardev",
PL011State,
char_backend,
unsafe { &qdev_prop_chr },
CharBackend
),
qemu_api::define_property!(
c"migrate-clk",
PL011State,
migrate_clock,
unsafe { &qdev_prop_bool },
bool
),
}
qemu_api::device_class_init! {
pl011_class_init,
props => PL011_PROPERTIES,
realize_fn => Some(pl011_realize),
legacy_reset_fn => Some(pl011_reset),
vmsd => VMSTATE_PL011,
}
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer, that has
/// the same size as [`PL011State`]. We also expect the device is
/// readable/writeable from one thread at any time.
#[no_mangle]
pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
unsafe {
assert!(!dev.is_null());
let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
state.as_mut().realize();
}
}
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer, that has
/// the same size as [`PL011State`]. We also expect the device is
/// readable/writeable from one thread at any time.
#[no_mangle]
pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
unsafe {
assert!(!dev.is_null());
let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
state.as_mut().reset();
}
}
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
use core::{mem::MaybeUninit, ptr::NonNull};
use qemu_api::bindings::*;
use crate::device::PL011State;
pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps {
read: Some(pl011_read),
write: Some(pl011_write),
read_with_attrs: None,
write_with_attrs: None,
endianness: device_endian::DEVICE_NATIVE_ENDIAN,
valid: unsafe { MaybeUninit::<MemoryRegionOps__bindgen_ty_1>::zeroed().assume_init() },
impl_: MemoryRegionOps__bindgen_ty_2 {
min_access_size: 4,
max_access_size: 4,
..unsafe { MaybeUninit::<MemoryRegionOps__bindgen_ty_2>::zeroed().assume_init() }
},
};
#[no_mangle]
unsafe extern "C" fn pl011_read(
opaque: *mut core::ffi::c_void,
addr: hwaddr,
size: core::ffi::c_uint,
) -> u64 {
assert!(!opaque.is_null());
let mut state = unsafe { NonNull::new_unchecked(opaque.cast::<PL011State>()) };
let val = unsafe { state.as_mut().read(addr, size) };
match val {
std::ops::ControlFlow::Break(val) => val,
std::ops::ControlFlow::Continue(val) => {
// SAFETY: self.char_backend is a valid CharBackend instance after it's been
// initialized in realize().
let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) };
unsafe { qemu_chr_fe_accept_input(cb_ptr) };
val
}
}
}
#[no_mangle]
unsafe extern "C" fn pl011_write(
opaque: *mut core::ffi::c_void,
addr: hwaddr,
data: u64,
_size: core::ffi::c_uint,
) {
unsafe {
assert!(!opaque.is_null());
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
state.as_mut().write(addr, data)
}
}
+1
View File
@@ -0,0 +1 @@
subdir('char')
+2
View File
@@ -1,2 +1,4 @@
subdir('qemu-api-macros')
subdir('qemu-api')
subdir('hw')
+3 -1
View File
@@ -27,7 +27,9 @@ sub_file="${sub_tdir}/submodule.tar"
# in their checkout, because the build environment is completely
# different to the host OS.
subprojects="keycodemapdb libvfio-user berkeley-softfloat-3
berkeley-testfloat-3 proc-macro2-1-rs quote-1-rs
berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs
bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs
proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs
syn-2-rs unicode-ident-1-rs"
sub_deinit=""
+3 -1
View File
@@ -18,7 +18,9 @@ fi
# Only include wraps that are invoked with subproject()
SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3
berkeley-testfloat-3 proc-macro2-1-rs quote-1-rs
berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs
bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs
proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs
syn-2-rs unicode-ident-1-rs"
src="$1"

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