Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# Ignore any cargo development build artifacts; for qemu-wide builds, all build
# artifacts will go to the meson build directory.
target
+1
View File
@@ -0,0 +1 @@
source hw/Kconfig
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// 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 root = env::var("MESON_BUILD_ROOT").expect(concat!(
"\n",
" MESON_BUILD_ROOT not 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 sub = get_rust_subdir(manifest_dir).unwrap();
let file = format!("{root}/{sub}/bindings.inc.rs");
let file = Path::new(&file);
if !file.exists() {
panic!(concat!(
"\n",
" No generated C bindings found! Run `make` first; or maybe you\n",
" wanted one of `make clippy`, `make rustfmt`, `make rustdoc`?\n",
));
}
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..])
}
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "chardev-sys"
version = "0.1.0"
description = "Rust sys bindings for QEMU/chardev"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
glib-sys = { workspace = true }
common = { path = "../../common" }
qom-sys = { path = "../qom-sys" }
util-sys = { path = "../util-sys" }
[lints]
workspace = true
[package.metadata.bindgen]
header = "wrapper.h"
rustified-enum = ["QEMUChrEvent"]
+1
View File
@@ -0,0 +1 @@
../build.rs
+39
View File
@@ -0,0 +1,39 @@
// 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;
use glib_sys::{gboolean, guint, GIOCondition, GMainContext, GSource, GSourceFunc};
use qom_sys::{Object, ObjectClass};
use util_sys::{Error, IOCanReadHandler, IOReadHandler, QemuOpts};
#[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 CharFrontend {}
unsafe impl Sync for CharFrontend {}
unsafe impl Zeroable for CharFrontend {}
+9
View File
@@ -0,0 +1,9 @@
_bindgen_chardev_rs = rust.bindgen(
args: bindgen_args_common + bindgen_args_data['chardev-sys'].split(),
kwargs: bindgen_kwargs)
_chardev_sys_rs = cargo_ws.package('chardev-sys').library(
structured_sources(['lib.rs', _bindgen_chardev_rs]))
cargo_ws.package('chardev-sys').override_dependency(declare_dependency(link_with: _chardev_sys_rs))
chardev_sys_rs = declare_dependency(link_with: [_chardev_sys_rs])
+12
View File
@@ -0,0 +1,12 @@
/* 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.
*/
#include "qemu/osdep.h"
#include "chardev/char.h"
#include "chardev/char-fe.h"
#include "chardev/char-serial.h"
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
"""
Generate bindgen arguments from Cargo.toml metadata for QEMU's Rust FFI bindings.
Author: Paolo Bonzini <pbonzini@redhat.com>
Copyright (C) 2025 Red Hat, Inc.
This script processes Cargo.toml file for QEMU's bindings crates (util-sys,
chardev-sys, qom-sys, etc.); it generates bindgen command lines that allow
easy customization and that export the right headers in each bindings crate.
For detailed information, see docs/devel/rust.rst.
"""
import os
import re
import sys
import argparse
from pathlib import Path
from dataclasses import dataclass
from typing import Iterable, List, Dict, Any
try:
import tomllib
except ImportError:
import tomli as tomllib # type: ignore
INCLUDE_RE = re.compile(r'^#include\s+"([^"]+)"')
OPTIONS = [
"bitfield-enum",
"newtype-enum",
"newtype-global-enum",
"rustified-enum",
"rustified-non-exhaustive-enum",
"constified-enum",
"constified-enum-module",
"normal-alias",
"new-type-alias",
"new-type-alias-deref",
"bindgen-wrapper-union",
"manually-drop-union",
"blocklist-type",
"blocklist-function",
"blocklist-item",
"blocklist-file",
"blocklist-var",
"opaque-type",
"no-partialeq",
"no-copy",
"no-debug",
"no-default",
"no-hash",
"must-use-type",
"with-derive-custom",
"with-derive-custom-struct",
"with-derive-custom-enum",
"with-derive-custom-union",
"with-attribute-custom",
"with-attribute-custom-struct",
"with-attribute-custom-enum",
"with-attribute-custom-union",
]
@dataclass
class BindgenInfo:
cmd_args: List[str]
inputs: List[str]
def extract_includes(lines: Iterable[str]) -> List[str]:
"""Extract #include directives from a file."""
includes: List[str] = []
for line in lines:
match = INCLUDE_RE.match(line.strip())
if match:
includes.append(match.group(1))
return includes
def build_bindgen_args(metadata: Dict[str, Any]) -> List[str]:
"""Build command line arguments from [package.metadata.bindgen]."""
args: List[str] = []
for key, values in metadata.items():
if key in OPTIONS:
flag = f"--{key}"
assert isinstance(values, list)
for value in values:
args.append(flag)
args.append(value)
return args
def main() -> int:
parser = argparse.ArgumentParser(
description="Generate bindgen arguments from Cargo.toml metadata"
)
parser.add_argument(
"directories", nargs="+", help="Directories containing Cargo.toml files"
)
parser.add_argument(
"-I",
"--include-root",
default=None,
help="Base path for --allowlist-file/--blocklist-file",
)
parser.add_argument("--source-dir", default=os.getcwd(), help="Source directory")
parser.add_argument("-o", "--output", required=True, help="Output file")
parser.add_argument("--dep-file", help="Dependency file to write")
args = parser.parse_args()
prev_allowlist_files: Dict[str, object] = {}
bindgen_infos: Dict[str, BindgenInfo] = {}
os.chdir(args.source_dir)
include_root = args.include_root or args.source_dir
for directory in args.directories:
cargo_path = Path(directory) / "Cargo.toml"
inputs = [str(Path(args.source_dir) / cargo_path)]
with open(cargo_path, "rb") as f:
cargo_toml = tomllib.load(f)
metadata = cargo_toml.get("package", {}).get("metadata", {}).get("bindgen", {})
input_file = Path(directory) / metadata["header"]
inputs.append(str(Path(args.source_dir) / input_file))
cmd_args = build_bindgen_args(metadata)
# Each include file is allowed for this file and blocked in the
# next ones
for blocklist_path in prev_allowlist_files:
cmd_args.extend(["--blocklist-file", blocklist_path])
with open(input_file, "r", encoding="utf-8", errors="ignore") as f:
includes = extract_includes(f)
for allowlist_file in includes + metadata.get("additional-files", []):
allowlist_path = Path(include_root) / allowlist_file
cmd_args.extend(["--allowlist-file", str(allowlist_path)])
prev_allowlist_files.setdefault(str(allowlist_path), True)
bindgen_infos[directory] = BindgenInfo(cmd_args=cmd_args, inputs=inputs)
# now write the output
with open(args.output, "w") as f:
for directory, info in bindgen_infos.items():
args_sh = " ".join(info.cmd_args)
f.write(f"{directory}={args_sh}\n")
if args.dep_file:
with open(args.dep_file, "w") as f:
deps: List[str] = []
for info in bindgen_infos.values():
deps += info.inputs
f.write(f"{os.path.basename(args.output)}: {' '.join(deps)}\n")
return 0
if __name__ == "__main__":
sys.exit(main())
+31
View File
@@ -0,0 +1,31 @@
[package]
name = "hwcore-sys"
version = "0.1.0"
description = "Rust sys bindings for QEMU/hwcore"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
glib-sys = { workspace = true }
common = { path = "../../common" }
chardev-sys = { path = "../chardev-sys" }
qom-sys = { path = "../qom-sys" }
migration-sys = { path = "../migration-sys" }
util-sys = { path = "../util-sys" }
[lints]
workspace = true
[package.metadata.bindgen]
header = "wrapper.h"
rustified-enum = ["DeviceCategory", "GpioPolarity", "MachineInitPhase", "ResetType"]
bitfield-enum = ["ClockEvent"]
+1
View File
@@ -0,0 +1 @@
../build.rs
+41
View File
@@ -0,0 +1,41 @@
// 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_sys::Chardev;
use common::Zeroable;
use glib_sys::GSList;
use migration_sys::VMStateDescription;
use qom_sys::{
InterfaceClass, Object, ObjectClass, ObjectProperty, ObjectPropertyAccessor,
ObjectPropertyRelease,
};
use util_sys::{Error, QDict, QList};
#[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 Zeroable for Property__bindgen_ty_1 {}
unsafe impl Zeroable for Property {}
+9
View File
@@ -0,0 +1,9 @@
_bindgen_hwcore_rs = rust.bindgen(
args: bindgen_args_common + bindgen_args_data['hwcore-sys'].split(),
kwargs: bindgen_kwargs)
_hwcore_sys_rs = cargo_ws.package('hwcore-sys').library(
structured_sources(['lib.rs', _bindgen_hwcore_rs]))
cargo_ws.package('hwcore-sys').override_dependency(declare_dependency(link_with: _hwcore_sys_rs))
hwcore_sys_rs = declare_dependency(link_with: [_hwcore_sys_rs])
+30
View File
@@ -0,0 +1,30 @@
/* 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.
*/
/*
* We block include/qemu/typedefs.h from bindgen, add here symbols
* that are needed as opaque types by other functions.
*/
typedef struct Clock Clock;
typedef struct DeviceState DeviceState;
typedef struct IRQState *qemu_irq;
typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
/* Once bindings exist, these could move to a different *-sys crate. */
typedef struct BlockBackend BlockBackend;
typedef struct Monitor Monitor;
typedef struct NetClientState NetClientState;
#include "qemu/osdep.h"
#include "hw/core/clock.h"
#include "hw/core/irq.h"
#include "hw/core/qdev-clock.h"
#include "hw/core/qdev.h"
#include "hw/core/qdev-properties-system.h"
#include "hw/core/qdev-properties.h"
#include "hw/core/resettable.h"
+37
View File
@@ -0,0 +1,37 @@
# Generate bindgen arguments from Cargo.toml metadata
# Sort these in dependency order, same as the subdir()
# invocations below.
bindgen_dirs = [
'util-sys',
'migration-sys',
'qom-sys',
'chardev-sys',
'hwcore-sys',
'system-sys',
]
bindgen_args_file = configure_file(
command: [files('generate_bindgen_args.py'),
'-I', meson.project_source_root() / 'include',
'--source-dir', meson.current_source_dir(),
'-o', '@OUTPUT@', '--dep-file', '@DEPFILE@'] + bindgen_dirs,
output: 'bindgen_args.mak',
depfile: 'bindgen_args.d'
)
# now generate all bindgen files
bindgen_args_data = keyval.load(bindgen_args_file)
bindgen_kwargs = {
'input': 'wrapper.h',
'dependencies': common_ss.all_dependencies(),
'output': 'bindings.inc.rs',
'include_directories': bindings_incdir,
'bindgen_version': ['>=0.60.0'],
'c_args': bindgen_c_args,
}
subdir('util-sys')
subdir('migration-sys')
subdir('qom-sys')
subdir('chardev-sys')
subdir('hwcore-sys')
subdir('system-sys')
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "migration-sys"
version = "0.1.0"
description = "Rust sys bindings for QEMU/migration"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
glib-sys = { workspace = true }
common = { path = "../../common" }
util-sys = { path = "../util-sys" }
[lints]
workspace = true
[package.metadata.bindgen]
header = "wrapper.h"
bitfield-enum = ["MigrationPolicy", "MigrationPriority", "VMStateFlags"]
blocklist-function = ["vmstate_register_ram", "vmstate_register_ram_global", "vmstate_unregister_ram"]
+1
View File
@@ -0,0 +1 @@
../build.rs
+122
View File
@@ -0,0 +1,122 @@
// 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;
use util_sys::{Error, JSONWriter, QEMUFile};
#[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 {}
unsafe impl Zeroable for VMStateStructMember {}
// The following higher-level helpers could be in "migration"
// crate when Rust has const trait impl.
pub trait VMStateFlagsExt {
const VMS_VARRAY_FLAGS: VMStateFlags;
}
impl VMStateFlagsExt for VMStateFlags {
const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags(VMStateFlags::VMS_VARRAY.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::<usize>();
}
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)
}
}
impl VMStateStructMember {
pub const fn new(off: usize, size: usize) -> Self {
Self {
offset: off as u32,
size: size as u8,
}
}
}
+9
View File
@@ -0,0 +1,9 @@
_bindgen_migration_rs = rust.bindgen(
args: bindgen_args_common + bindgen_args_data['migration-sys'].split(),
kwargs: bindgen_kwargs)
_migration_sys_rs = cargo_ws.package('migration-sys').library(
structured_sources(['lib.rs', _bindgen_migration_rs]))
cargo_ws.package('migration-sys').override_dependency(declare_dependency(link_with: _migration_sys_rs))
migration_sys_rs = declare_dependency(link_with: [_migration_sys_rs])
+10
View File
@@ -0,0 +1,10 @@
/* 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.
*/
#include "qemu/osdep.h"
#include "migration/vmstate.h"
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "qom-sys"
version = "0.1.0"
description = "Rust sys bindings for QEMU/qom"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
glib-sys = { workspace = true }
util-sys = { path = "../util-sys" }
[lints]
workspace = true
[package.metadata.bindgen]
header = "wrapper.h"
+1
View File
@@ -0,0 +1 @@
../build.rs
+31
View File
@@ -0,0 +1,31 @@
// 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 glib_sys::{GHashTable, GHashTableIter, GSList};
use util_sys::{Error, QDict, QObject, Visitor};
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
unsafe impl Send for TypeInfo {}
unsafe impl Sync for TypeInfo {}
+9
View File
@@ -0,0 +1,9 @@
_bindgen_qom_rs = rust.bindgen(
args: bindgen_args_common + bindgen_args_data['qom-sys'].split(),
kwargs: bindgen_kwargs)
_qom_sys_rs = cargo_ws.package('qom-sys').library(
structured_sources(['lib.rs', _bindgen_qom_rs]))
cargo_ws.package('qom-sys').override_dependency(declare_dependency(link_with: _qom_sys_rs))
qom_sys_rs = declare_dependency(link_with: [_qom_sys_rs])
+17
View File
@@ -0,0 +1,17 @@
/* 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.
*/
/*
* We block include/qemu/typedefs.h from bindgen, add here symbols
* that are needed as opaque types by other functions.
*/
typedef struct Object Object;
typedef struct ObjectClass ObjectClass;
#include "qemu/osdep.h"
#include "qom/object.h"
+31
View File
@@ -0,0 +1,31 @@
[package]
name = "system-sys"
version = "0.1.0"
description = "Rust sys bindings for QEMU/system"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
glib-sys = { workspace = true }
common = { path = "../../common" }
migration-sys = { path = "../migration-sys" }
util-sys = { path = "../util-sys" }
qom-sys = { path = "../qom-sys" }
hwcore-sys = { path = "../hwcore-sys" }
[lints]
workspace = true
[package.metadata.bindgen]
header = "wrapper.h"
rustified-enum = ["device_endian"]
additional-files = ["system/memory.*"]
+1
View File
@@ -0,0 +1 @@
../build.rs
+44
View File
@@ -0,0 +1,44 @@
// 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;
use hwcore_sys::{qemu_irq, DeviceClass, DeviceState};
use qom_sys::{Object, ObjectClass};
use util_sys::{Error, EventNotifier, QEMUBH};
#[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 {}
+9
View File
@@ -0,0 +1,9 @@
_bindgen_system_rs = rust.bindgen(
args: bindgen_args_common + bindgen_args_data['system-sys'].split(),
kwargs: bindgen_kwargs)
_system_sys_rs = cargo_ws.package('system-sys').library(
structured_sources(['lib.rs', _bindgen_system_rs]))
cargo_ws.package('system-sys').override_dependency(declare_dependency(link_with: _system_sys_rs))
system_sys_rs = declare_dependency(link_with: [_system_sys_rs])
+21
View File
@@ -0,0 +1,21 @@
/* 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.
*/
/*
* We block include/qemu/typedefs.h from bindgen, add here symbols
* that are needed as opaque types by other functions.
*/
typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot;
typedef struct MemoryRegion MemoryRegion;
typedef struct RAMBlock RAMBlock;
#include "qemu/osdep.h"
#include "exec/hwaddr.h"
#include "system/address-spaces.h"
#include "system/memory.h"
#include "hw/core/sysbus.h"
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "util-sys"
version = "0.1.0"
description = "Rust sys bindings for QEMU/util"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
path = "lib.rs"
[dependencies]
glib-sys = { workspace = true }
[lints]
workspace = true
[package.metadata.bindgen]
header = "wrapper.h"
rustified-enum = ["module_init_type", "QEMUClockType"]
+1
View File
@@ -0,0 +1 @@
../build.rs
+27
View File
@@ -0,0 +1,27 @@
// 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 glib_sys::{guint, GArray, GHashTable, GPollFD, GSList, GSource, GString};
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+9
View File
@@ -0,0 +1,9 @@
_bindgen_util_rs = rust.bindgen(
args: bindgen_args_common + bindgen_args_data['util-sys'].split(),
kwargs: bindgen_kwargs)
_util_sys_rs = cargo_ws.package('util-sys').library(
structured_sources(['lib.rs', _bindgen_util_rs]))
cargo_ws.package('util-sys').override_dependency(declare_dependency(link_with: _util_sys_rs))
util_sys_rs = declare_dependency(link_with: [_util_sys_rs])
+39
View File
@@ -0,0 +1,39 @@
/* 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.
*/
/*
* We block include/qemu/typedefs.h from bindgen, add here symbols
* that are needed as opaque types by other functions.
*/
typedef struct QEMUBH QEMUBH;
typedef struct QEMUFile QEMUFile;
typedef struct QemuOpts QemuOpts;
typedef struct JSONWriter JSONWriter;
typedef struct Visitor Visitor;
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/error-internal.h"
#include "qemu/event_notifier.h"
#include "qemu/main-loop.h"
#include "qemu/aio.h"
#include "qemu/log-for-trace.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/timer.h"
#include "qapi/visitor.h"
#include "qobject/qbool.h"
#include "qobject/qdict.h"
#include "qobject/qjson.h"
#include "qobject/qlist.h"
#include "qobject/qnull.h"
#include "qobject/qnum.h"
#include "qobject/qobject.h"
#include "qobject/qstring.h"
#include "qobject/json-writer.h"
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "bits"
version = "0.1.0"
authors = ["Paolo Bonzini <[email protected]>"]
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
+11
View File
@@ -0,0 +1,11 @@
_bits_rs = cargo_ws.package('bits').library()
cargo_ws.package('bits').override_dependency(declare_dependency(link_with: _bits_rs))
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'])
+466
View File
@@ -0,0 +1,466 @@
// 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 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__.checked_ilog2().map_or(1, |bit| (bit + 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 &= !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),
}
}
bits! {
pub struct EmptyMask(u32) {
NONE = 0,
}
}
#[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
);
}
#[test]
pub fn test_sub_assign() {
let mut op1 = InterruptMask::E;
op1 -= InterruptMask::RI;
assert_eq!(op1, InterruptMask::E - InterruptMask::RI);
}
#[test]
pub fn test_bit_display_empty() {
assert_eq!(format!("{:b}", EmptyMask::NONE), "0");
}
}
+24
View File
@@ -0,0 +1,24 @@
[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]
glib-sys.workspace = true
util-sys = { path = "../bindings/util-sys" }
[features]
default = ["debug_cell"]
debug_cell = []
[lints]
workspace = true
+18
View File
@@ -0,0 +1,18 @@
_bql_cfg = []
if get_option('debug_mutex')
_bql_cfg += ['--cfg', 'feature="debug_cell"']
endif
_bql_rs = cargo_ws.package('bql').library(rust_args: _bql_cfg)
cargo_ws.package('bql').override_dependency(declare_dependency(link_with: _bql_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,
dependencies: bql_rs,
suite: ['doc', 'rust'])
+863
View File
@@ -0,0 +1,863 @@
// 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].
//! Therefore, individual parts of a device must be made mutable in a
//! controlled manner; this module provides the tools to do so.
//!
//! [`MemoryRegion`s]: ../../system/memory/struct.MemoryRegion.html
//!
//! ## Cell types
//!
//! [`BqlCell<T>`] and [`BqlRefCell<T>`] 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 = <SysBusDevice as ObjectType>::Class;
//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061";
//! # }
//! struct PL061State {
//! parent_obj: ParentField<SysBusDevice>,
//!
//! // 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<PL061Registers>,
//! }
//! ```
//!
//! ### `BqlCell<T>`
//!
//! [`BqlCell<T>`] 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<T>`
//!
//! [`BqlRefCell<T>`] 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<T>`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.
#[cfg(feature = "debug_cell")]
use std::cell::Cell;
use std::{
cell::UnsafeCell,
cmp::Ordering,
fmt,
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
ptr::NonNull,
};
/// A mutable memory location that is protected by the Big QEMU Lock.
///
/// # Memory layout
///
/// `BqlCell<T>` has the same in-memory representation as its inner type `T`.
#[repr(transparent)]
pub struct BqlCell<T> {
value: UnsafeCell<T>,
}
// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex,
// except it is stored out-of-line
unsafe impl<T: Send> Send for BqlCell<T> {}
unsafe impl<T: Send> Sync for BqlCell<T> {}
impl<T: Copy> Clone for BqlCell<T> {
#[inline]
fn clone(&self) -> BqlCell<T> {
BqlCell::new(self.get())
}
}
impl<T: Default> Default for BqlCell<T> {
/// Creates a `BqlCell<T>`, with the `Default` value for T.
#[inline]
fn default() -> BqlCell<T> {
BqlCell::new(Default::default())
}
}
impl<T: PartialEq + Copy> PartialEq for BqlCell<T> {
#[inline]
fn eq(&self, other: &BqlCell<T>) -> bool {
self.get() == other.get()
}
}
impl<T: Eq + Copy> Eq for BqlCell<T> {}
impl<T: PartialOrd + Copy> PartialOrd for BqlCell<T> {
#[inline]
fn partial_cmp(&self, other: &BqlCell<T>) -> Option<Ordering> {
self.get().partial_cmp(&other.get())
}
}
impl<T: Ord + Copy> Ord for BqlCell<T> {
#[inline]
fn cmp(&self, other: &BqlCell<T>) -> Ordering {
self.get().cmp(&other.get())
}
}
impl<T> From<T> for BqlCell<T> {
/// Creates a new `BqlCell<T>` containing the given value.
fn from(t: T) -> BqlCell<T> {
BqlCell::new(t)
}
}
impl<T: fmt::Debug + Copy> fmt::Debug for BqlCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get().fmt(f)
}
}
impl<T: fmt::Display + Copy> fmt::Display for BqlCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get().fmt(f)
}
}
impl<T> BqlCell<T> {
/// 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<T> {
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<T: Copy> BqlCell<T> {
/// 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<T> BqlCell<T> {
/// 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<T: Default> BqlCell<T> {
/// 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())
}
}
/// 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<T>` starts with the same in-memory representation as its
/// inner type `T`.
#[repr(C)]
pub struct BqlRefCell<T> {
// 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<T>,
borrow: BqlCell<BorrowFlag>,
// 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<Option<&'static std::panic::Location<'static>>>,
}
// 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<T> BqlRefCell<T> {
/// Creates a new `BqlRefCell` containing `value`.
///
/// # Examples
///
/// ```
/// use bql::BqlRefCell;
///
/// let c = BqlRefCell::new(5);
/// ```
#[inline]
pub const fn new(value: T) -> BqlRefCell<T> {
BqlRefCell {
value: UnsafeCell::new(value),
borrow: BqlCell::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<Option<&'static std::panic::Location<'static>>>) -> ! {
// 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<T> BqlRefCell<T> {
#[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 mutable reference to the underlying data in this cell,
/// while the owner already has a mutable reference to the cell.
///
/// # Examples
///
/// ```
/// use bql::BqlRefCell;
///
/// let mut c = BqlRefCell::new(5);
///
/// *c.get_mut() = 10;
/// ```
#[inline]
pub const fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
/// 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<T> Send for BqlRefCell<T> where T: Send {}
unsafe impl<T> Sync for BqlRefCell<T> {}
impl<T: Clone> Clone for BqlRefCell<T> {
/// # Panics
///
/// Panics if the value is currently mutably borrowed.
#[inline]
#[track_caller]
fn clone(&self) -> BqlRefCell<T> {
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<T: Default> Default for BqlRefCell<T> {
/// Creates a `BqlRefCell<T>`, with the `Default` value for T.
#[inline]
fn default() -> BqlRefCell<T> {
BqlRefCell::new(Default::default())
}
}
impl<T: PartialEq> PartialEq for BqlRefCell<T> {
/// # Panics
///
/// Panics if the value in either `BqlRefCell` is currently mutably
/// borrowed.
#[inline]
fn eq(&self, other: &BqlRefCell<T>) -> bool {
*self.borrow() == *other.borrow()
}
}
impl<T: Eq> Eq for BqlRefCell<T> {}
impl<T: PartialOrd> PartialOrd for BqlRefCell<T> {
/// # Panics
///
/// Panics if the value in either `BqlRefCell` is currently mutably
/// borrowed.
#[inline]
fn partial_cmp(&self, other: &BqlRefCell<T>) -> Option<Ordering> {
self.borrow().partial_cmp(&*other.borrow())
}
}
impl<T: Ord> Ord for BqlRefCell<T> {
/// # Panics
///
/// Panics if the value in either `BqlRefCell` is currently mutably
/// borrowed.
#[inline]
fn cmp(&self, other: &BqlRefCell<T>) -> Ordering {
self.borrow().cmp(&*other.borrow())
}
}
impl<T> From<T> for BqlRefCell<T> {
/// Creates a new `BqlRefCell<T>` containing the given value.
fn from(t: T) -> BqlRefCell<T> {
BqlRefCell::new(t)
}
}
struct BorrowRef<'b> {
borrow: &'b BqlCell<BorrowFlag>,
}
impl<'b> BorrowRef<'b> {
#[inline]
fn new(borrow: &'b BqlCell<BorrowFlag>) -> Option<BorrowRef<'b>> {
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<T>`.
///
/// 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<T>,
borrow: BorrowRef<'b>,
}
impl<T> 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<T: fmt::Debug> fmt::Debug for BqlRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for BqlRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
struct BorrowRefMut<'b> {
borrow: &'b BqlCell<BorrowFlag>,
}
impl<'b> BorrowRefMut<'b> {
#[inline]
fn new(borrow: &'b BqlCell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
// 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<T>`.
///
/// 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<T>,
_borrow: BorrowRefMut<'b>,
// `NonNull` is covariant over `T`, so we need to reintroduce invariance.
marker: PhantomData<&'b mut T>,
}
impl<T> 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<T> 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<T: fmt::Debug> fmt::Debug for BqlRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-or-later
use util_sys::{bql_block_unlock, bql_locked, rust_bql_mock_lock};
mod cell;
pub use cell::*;
// preserve one-item-per-"use" syntax, it is clearer
// for prelude-like modules
#[rustfmt::skip]
pub mod prelude;
/// 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);
}
}
+4
View File
@@ -0,0 +1,4 @@
//! Essential types and traits intended for blanket imports.
pub use crate::cell::BqlCell;
pub use crate::cell::BqlRefCell;
+25
View File
@@ -0,0 +1,25 @@
[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]
glib-sys = { workspace = true }
chardev-sys = { path = "../bindings/chardev-sys" }
common = { path = "../common" }
bql = { path = "../bql" }
migration = { path = "../migration" }
qom = { path = "../qom" }
util = { path = "../util" }
[lints]
workspace = true
+4
View File
@@ -0,0 +1,4 @@
_chardev_rs = cargo_ws.package('chardev').library()
cargo_ws.package('chardev').override_dependency(declare_dependency(link_with: _chardev_rs))
chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil])
+261
View File
@@ -0,0 +1,261 @@
// Copyright 2024 Red Hat, Inc.
// Author(s): Paolo Bonzini <[email protected]>
// 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 [`CharFrontend`] 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::{prelude::*, 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<bindings::Chardev>);
pub type ChardevClass = bindings::ChardevClass;
pub type Event = bindings::QEMUChrEvent;
/// A safe wrapper around [`bindings::CharFrontend`], 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 `CharFrontend`.
pub struct CharFrontend {
inner: BqlRefCell<bindings::CharFrontend>,
_pin: PhantomPinned,
}
pub struct CharFrontendMut<'a>(BqlRefMut<'a, bindings::CharFrontend>);
impl Write for CharFrontendMut<'_> {
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let chr: &mut bindings::CharFrontend = &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::CharFrontend = &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 CharFrontend {
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 CharFrontend {
fn drop(&mut self) {
self.disable_handlers();
}
}
impl CharFrontend {
/// 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<T, F: for<'a> 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::<T>()) };
let r = F::call((owner,));
r.try_into().unwrap()
}
unsafe extern "C" fn rust_receive_cb<T, F: for<'a, 'b> 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::<T>()) };
let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) };
F::call((owner, buf))
}
unsafe extern "C" fn rust_event_cb<T, F: for<'a> 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::<T>()) };
F::call((owner, event))
}
const { assert!(CanReceiveFn::IS_SOME) };
let receive_cb: Option<unsafe extern "C" fn(*mut c_void, *const u8, c_int)> =
if ReceiveFn::is_some() {
Some(rust_receive_cb::<T, ReceiveFn>)
} else {
None
};
let event_cb: Option<unsafe extern "C" fn(*mut c_void, Event)> = if EventFn::is_some() {
Some(rust_event_cb::<T, EventFn>)
} 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::<T, CanReceiveFn>),
receive_cb,
event_cb,
None,
(owner as *const T).cast_mut().cast::<c_void>(),
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 + '_ {
CharFrontendMut(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::<c_void>(),
)
};
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<usize> {
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);
+11
View File
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pub use chardev_sys as bindings;
mod chardev;
pub use chardev::*;
// preserve one-item-per-"use" syntax, it is clearer
// for prelude-like modules
#[rustfmt::skip]
pub mod prelude;
+5
View File
@@ -0,0 +1,5 @@
//! Essential types and traits intended for blanket imports.
pub use crate::chardev::Chardev;
pub use crate::chardev::CharFrontend;
pub use crate::chardev::Event;
+20
View File
@@ -0,0 +1,20 @@
[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
+19
View File
@@ -0,0 +1,19 @@
_common_cfg = run_command(rustc_args,
'--config-headers', config_host_h, files('Cargo.toml'),
capture: true, check: true).stdout().strip().splitlines()
_common_rs = cargo_ws.package('common').library(rust_args: _common_cfg)
cargo_ws.package('common').override_dependency(declare_dependency(link_with: _common_rs))
common_rs = declare_dependency(link_with: [_common_rs])
rust.test('rust-common-tests', _common_rs,
suite: ['unit', 'rust'])
# 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,
dependencies: common_rs,
suite: ['doc', 'rust'])
+148
View File
@@ -0,0 +1,148 @@
// Copyright 2024, Red Hat Inc.
// Author(s): Paolo Bonzini <[email protected]>
// 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<T> 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!(<Box<u32> as Deref>::Target, u32);
/// ```
///
/// Different types will cause a compile failure
///
/// ```compile_fail
/// # use common::assert_same_type;
/// assert_same_type!(&Box<u32>, &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, U>(_: T)
where
T: $crate::assertions::EqType<Itself = U>,
{
}
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, U>(_: &T)
where
T: $crate::assertions::EqType<Itself = U>,
{
}
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<std::thread::JoinHandle<()>, 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);
};
}
+118
View File
@@ -0,0 +1,118 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <[email protected]>
// 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<Self, Output = Self> + AddAssign<Self> +
BitAnd<Self, Output = Self> + BitAndAssign<Self> +
BitOr<Self, Output = Self> + BitOrAssign<Self> +
BitXor<Self, Output = Self> + BitXorAssign<Self> +
Copy +
Div<Self, Output = Self> + DivAssign<Self> +
Eq +
Mul<Self, Output = Self> + MulAssign<Self> +
Not<Output = Self> + Ord + PartialOrd +
Rem<Self, Output = Self> + RemAssign<Self> +
Shl<Self, Output = Self> + ShlAssign<Self> +
Shl<u32, Output = Self> + ShlAssign<u32> + // add more as needed
Shr<Self, Output = Self> + ShrAssign<Self> +
Shr<u32, Output = Self> + ShrAssign<u32> // 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<U: IntegerExt>(self, start: u32, length: u32,
fieldval: U) -> Self
where Self: From<U>
{
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);
}
}
+216
View File
@@ -0,0 +1,216 @@
// 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::<F>`.
///
/// 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<T, F: for<'a> FnCall<(&'a T,)>>(
/// opaque: *mut c_void,
/// ) {
/// // SAFETY: the opaque was passed as a reference to `T`.
/// F::call((unsafe { &*(opaque.cast::<T>()) }, ))
/// }
///
/// // 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<T, F: for<'a> FnCall<(&'a T,)>>(_f: &F, opaque: &T) {
/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::<T, F>;
/// 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::<Self, F>`.
///
/// `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<F: for<'a> 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<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
/// 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<F: for<'a> 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<Args, R = ()>: '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<F: for<'a> 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<Args, R> FnCall<Args, R> 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<F, $($args,)* R> 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::<Self>() == 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::<Self>::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);
}
}
+354
View File
@@ -0,0 +1,354 @@
// 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<Errno> 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<io::ErrorKind> 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<Errno> 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<io::Error> 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<convert::Infallible> 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<Self::Out, Errno>;
}
/// 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<u16> + std::ops::Neg<Output = Self::Out>;
/// 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<Self::Out, Errno> {
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<T: GetErrno>(value: T) -> io::Result<T::Out> {
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<u32> = Ok(0x8899_AABB);
/// into_neg_errno(err) // panic
/// # ;
/// ```
pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> 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<u8> = Err(io::ErrorKind::PermissionDenied.into());
assert_eq!(into_neg_errno(err), -1);
if cfg!(unix) {
let os_err: io::Result<u8> = 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<u16> = Err(io::ErrorKind::PermissionDenied.into());
assert_eq!(into_neg_errno(err), -1);
if cfg!(unix) {
let os_err: io::Result<u16> = 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<u32> = Err(io::ErrorKind::PermissionDenied.into());
assert_eq!(into_neg_errno(err), -1);
if cfg!(unix) {
let os_err: io::Result<u32> = 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<u64> = Err(io::ErrorKind::InvalidInput.into());
assert_eq!(into_neg_errno(err), -22);
if cfg!(unix) {
let os_err: io::Result<u64> = 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);
}
}
}
+27
View File
@@ -0,0 +1,27 @@
// 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};
// preserve one-item-per-"use" syntax, it is clearer
// for prelude-like modules
#[rustfmt::skip]
pub mod prelude;
pub mod uninit;
pub use uninit::MaybeUninitField;
pub mod zeroable;
pub use zeroable::Zeroable;
+236
View File
@@ -0,0 +1,236 @@
// 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<T>`] 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::<MyStruct>::uninit();
//! qemu_struct_init(state.as_mut_ptr());
//! }
//! ```
//!
//! [`Opaque<T>`] will usually be wrapped one level further, so that
//! bridge methods can be added to the wrapper:
//!
//! ```ignore
//! pub struct MyStruct(Opaque<bindings::MyStruct>);
//!
//! impl MyStruct {
//! fn new() -> Pin<Box<MyStruct>> {
//! 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<T>`] 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<T>`] 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<T>`] 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<T>` 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<T>` is `#[repr(transparent)]`, so that it matches the memory layout
/// of `T`.
#[repr(transparent)]
pub struct Opaque<T> {
value: UnsafeCell<MaybeUninit<T>>,
// PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
// one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
// see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
_pin: PhantomPinned,
}
impl<T> Opaque<T> {
/// 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::<Self>();
// 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<T> fmt::Debug for Opaque<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut name: String = "Opaque<".to_string();
name += std::any::type_name::<T>();
name += ">";
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
impl<T: Default> Opaque<T> {
/// 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<String>,
/// }
///
/// 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<T> Wrapper for Opaque<T> {
type Wrapped = T;
}
+9
View File
@@ -0,0 +1,9 @@
//! Essential types and traits intended for blanket imports.
pub use crate::bitops::IntegerExt;
pub use crate::uninit::MaybeUninitField;
// Re-export commonly used macros
pub use crate::static_assert;
pub use crate::uninit_field_mut;
pub use qemu_macros::TryInto;
+85
View File
@@ -0,0 +1,85 @@
//! Access fields of a [`MaybeUninit`]
use std::{
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
pub struct MaybeUninitField<'a, T, U> {
parent: &'a mut MaybeUninit<T>,
child: *mut U,
}
impl<'a, T, U> MaybeUninitField<'a, T, U> {
#[doc(hidden)]
pub const fn new(parent: &'a mut MaybeUninit<T>, 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<T, U> Deref for MaybeUninitField<'_, T, U> {
type Target = MaybeUninit<U>;
fn deref(&self) -> &MaybeUninit<U> {
// 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<T, U> DerefMut for MaybeUninitField<'_, T, U> {
fn deref_mut(&mut self) -> &mut MaybeUninit<U> {
// 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<S> = 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__)
}};
}
+18
View File
@@ -0,0 +1,18 @@
// 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::<Self>::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<T>` 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::<Self>::zeroed().assume_init() };
}
+3
View File
@@ -0,0 +1,3 @@
# devices Kconfig
source char/Kconfig
source timer/Kconfig
+2
View File
@@ -0,0 +1,2 @@
config X_PL011_RUST
bool
+1
View File
@@ -0,0 +1 @@
subdir('pl011')
+30
View File
@@ -0,0 +1,30 @@
[package]
name = "pl011"
version = "0.1.0"
authors = ["Manos Pitsidianakis <[email protected]>"]
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]
glib-sys.workspace = true
bitfield-struct = { version = "0.13" }
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" }
trace = { path = "../../../trace" }
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
../../../bindings/build.rs
+26
View File
@@ -0,0 +1,26 @@
_libpl011_bindings_inc_rs = rust.bindgen(
args: bindgen_args_common + [
'--allowlist-file', meson.project_source_root() / 'include/hw/char/pl011.h',
'--blocklist-file',
meson.project_source_root() /
'include/(block\|chardev/|exec/|hw/core/|qemu/|qom/|system/).*',
],
kwargs: bindgen_kwargs,
)
_libpl011_rs = cargo_ws.package('pl011').library(
structured_sources(
[
'src/lib.rs',
'src/bindings.rs',
'src/device.rs',
'src/registers.rs',
],
{'.' : _libpl011_bindings_inc_rs},
),
)
rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
link_whole: [_libpl011_rs],
variables: {'crate': 'pl011'},
)])
+31
View File
@@ -0,0 +1,31 @@
// 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.
use chardev::bindings::{CharFrontend, Chardev};
use hwcore::bindings::{qemu_irq, Clock, DeviceState};
use system::bindings::{hwaddr, MemoryRegion, SysBusDevice};
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+781
View File
@@ -0,0 +1,781 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{ffi::CStr, mem::size_of};
use bql::prelude::*;
use chardev::prelude::*;
use common::prelude::*;
use hwcore::{prelude::*, ClockEvent, IRQState};
use migration::{self, prelude::*};
use qom::prelude::*;
use system::prelude::*;
use util::prelude::*;
use crate::registers::{self, Interrupt, RegisterOffset};
::trace::include_trace!("hw_char");
// 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<hwaddr> 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<u32> for Fifo {
fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
&mut self.0[idx as usize]
}
}
impl std::ops::Index<u32> 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<SysBusDevice>,
pub iomem: MemoryRegion,
#[doc(alias = "chr")]
#[property(rename = "chardev")]
pub char_frontend: CharFrontend,
pub regs: BqlRefCell<PL011Registers>,
/// 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<Clock>,
#[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::<PL011State>() <= size_of::<crate::bindings::PL011State>());
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
#[repr(C)]
pub struct PL011Class {
parent_class: <SysBusDevice as ObjectType>::Class,
/// The byte string that identifies the device.
device_id: DeviceId,
}
trait PL011Impl: SysBusDeviceImpl + IsA<PL011State> {
const DEVICE_ID: DeviceId;
}
impl PL011Class {
fn class_init<T: PL011Impl>(&mut self) {
self.device_id = T::DEVICE_ID;
self.parent_class.class_init::<T>();
}
}
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<unsafe fn(ParentInit<Self>)> = Some(Self::init);
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
}
impl DeviceImpl for PL011State {
const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_PL011);
const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for PL011State {
const HOLD: Option<fn(&Self, ResetType)> = 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, device: &PL011State) -> bool {
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;
device.trace_baudrate_change(self.ibrd, self.fbrd);
}
FBRD => {
self.fbrd = value;
device.trace_baudrate_change(self.ibrd, self.fbrd);
}
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 _ = device.char_frontend.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 {
let depth = self.fifo_depth();
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) & (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;
}
trace::trace_pl011_read_fifo(self.read_count, depth);
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);
trace::trace_pl011_fifo_rx_put(value.into(), self.read_count, depth);
if self.read_count == depth {
trace::trace_pl011_fifo_rx_full();
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<Self>) {
static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
.read(&PL011State::read)
.write(&PL011State::write)
.little_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);
}
pub fn trace_baudrate_change(&self, ibrd: u32, fbrd: u32) {
let divider = 4.0 / f64::from(ibrd * (FBRD_MASK + 1) + fbrd);
let hz = self.clock.hz();
let rate = if ibrd == 0 {
0
} else {
((hz as f64) * divider) as u32
};
trace::trace_pl011_baudrate_change(rate, hz, ibrd, fbrd);
}
fn clock_update(&self, _event: ClockEvent) {
let regs = self.regs.borrow();
let (ibrd, fbrd) = (regs.ibrd, regs.fbrd);
self.trace_baudrate_change(ibrd, fbrd)
}
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);
trace::trace_pl011_read(offset, result, c"");
if update_irq {
self.update();
self.char_frontend.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.
trace::trace_pl011_write(offset, value as u32, c"");
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_frontend.write_all(&ch);
}
update_irq = self.regs.borrow_mut().write(field, value as u32, self);
} 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();
let fifo_available = regs.fifo_depth() - regs.read_count;
trace::trace_pl011_can_receive(
regs.line_control.into(),
regs.read_count,
regs.fifo_depth(),
fifo_available,
);
fifo_available
}
fn receive(&self, buf: &[u8]) {
trace::trace_pl011_receive(buf.len());
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_frontend
.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;
trace::trace_pl011_irq_state(flags != 0);
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::<IRQState>::from(&*irq) };
let dev = PL011State::new();
if !chr.is_null() {
let chr = unsafe { Owned::<Chardev>::from(&*chr) };
dev.prop_set_chr("chardev", &chr);
}
dev.sysbus_realize().unwrap_fatal();
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<PL011State>,
}
qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object);
unsafe impl ObjectType for PL011Luminary {
type Class = <PL011State as ObjectType>::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::<Self>;
}
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<PL011State> =
VMStateDescriptionBuilder::<PL011State>::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::<PL011Registers>::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<PL011State> =
VMStateDescriptionBuilder::<PL011State>::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::<u32>()),
vmstate_of!(PL011State, regs),
})
.subsections(vmstate_subsections! {
VMSTATE_PL011_CLOCK
})
.build();
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// 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";
+347
View File
@@ -0,0 +1,347 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// 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.
// rustc prefers "constant-like" enums to use upper case names, but that
// is inconsistent in its own way.
#![allow(non_upper_case_globals)]
// For more detail see the PL011 Technical Reference Manual DDI0183:
// https://developer.arm.com/documentation/ddi0183/latest/
use bitfield_struct::bitfield;
use bits::bits;
use migration::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
#[bitfield(u8)]
pub struct Errors {
pub framing_error: bool,
pub parity_error: bool,
pub break_error: bool,
pub overrun_error: bool,
#[bits(4)]
_reserved_unpredictable: u8,
}
impl Errors {
pub const BREAK: Self = Errors::new().with_break_error(true);
}
/// 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.
#[bitfield(u32)]
#[doc(alias = "UARTDR")]
pub struct Data {
pub data: u8,
#[bits(8)]
pub errors: Errors,
_reserved: u16,
}
impl_vmstate_forward!(Data);
impl Data {
pub const BREAK: Self = Self::new().with_errors(Errors::BREAK);
}
/// 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.
#[bitfield(u32)]
pub struct ReceiveStatusErrorClear {
#[bits(8)]
pub errors: Errors,
#[bits(24)]
_reserved_unpredictable: u32,
}
impl_vmstate_forward!(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();
}
}
#[bitfield(u32, default = false)]
/// 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,
#[bits(23)]
_reserved_zero_no_modify: u32,
}
impl_vmstate_forward!(Flags);
impl Flags {
pub fn reset(&mut self) {
*self = Self::default();
}
}
impl Default for Flags {
fn default() -> Self {
// After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1
Self::from(0)
.with_receive_fifo_empty(true)
.with_transmit_fifo_empty(true)
}
}
#[bitfield(u32)]
/// 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
#[bits(1)]
pub parity: Parity,
/// STP2: Two stop bits select
pub two_stops_bits: bool,
/// FEN: Enable FIFOs
#[bits(1)]
pub fifos_enabled: Mode,
/// WLEN: Word length in bits
/// b11 = 8 bits
/// b10 = 7 bits
/// b01 = 6 bits
/// b00 = 5 bits.
#[bits(2)]
pub word_length: WordLength,
/// SPS Stick parity select
pub sticky_parity: bool,
/// 31:8 - Reserved, do not modify, read as zero.
#[bits(24)]
_reserved_zero_no_modify: u32,
}
impl_vmstate_forward!(LineControl);
impl LineControl {
pub fn reset(&mut self) {
// All the bits are cleared to 0 when reset.
*self = Self::default();
}
}
/// `EPS` "Even parity select", field of [Line Control
/// register](LineControl).
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)]
pub enum Parity {
Odd = 0,
Even = 1,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)]
/// `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,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)]
#[allow(clippy::enum_variant_names)]
/// `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.
#[bitfield(u32, default = false)]
#[doc(alias = "UARTCR")]
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: bool,
/// Reserved, do not modify, read as zero.
#[bits(4)]
_reserved_zero_no_modify: u8,
/// `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_forward!(Control);
impl Control {
pub fn reset(&mut self) {
*self = Self::default();
}
}
impl Default for Control {
fn default() -> Self {
Self::from(0)
.with_enable_receive(true)
.with_enable_transmit(true)
}
}
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);
+51
View File
@@ -0,0 +1,51 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2024 Linaro Ltd.
*
* Authors: Manos Pitsidianakis <[email protected]>
*
* 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"
+27
View File
@@ -0,0 +1,27 @@
[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]
glib-sys.workspace = true
hwcore-sys = { path = "../../bindings/hwcore-sys" }
qemu_macros = { path = "../../qemu-macros" }
common = { path = "../../common" }
bql = { path = "../../bql" }
qom = { path = "../../qom" }
chardev = { path = "../../chardev" }
migration = { path = "../../migration" }
util = { path = "../../util" }
[lints]
workspace = true
+20
View File
@@ -0,0 +1,20 @@
_hwcore_rs = cargo_ws.package('hwcore').library()
cargo_ws.package('hwcore').override_dependency(declare_dependency(link_with: _hwcore_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: [chardev_rs, common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]),
args: [
'--test', '--test-threads', '1',
'--format', 'pretty',
],
protocol: 'rust',
suite: ['unit', 'rust'])
+115
View File
@@ -0,0 +1,115 @@
// Copyright 2024 Red Hat, Inc.
// Author(s): Paolo Bonzini <[email protected]>
// 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<bindings::IRQState>);
/// 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<bool>`'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::DeviceMethods::init_gpio_out`], 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 `qdev_connect_gpio_out`. 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<T = bool>
where
c_int: From<T>,
{
cell: BqlCell<*mut bindings::IRQState>,
_marker: PhantomData<T>,
}
// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken
unsafe impl<T> Sync for InterruptSource<T> where c_int: From<T> {}
impl InterruptSource<bool> {
/// 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<T> InterruptSource<T>
where
c_int: From<T>,
{
/// 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 const fn as_ptr(&self) -> *mut *mut bindings::IRQState {
self.cell.as_ptr()
}
pub 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);
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pub use hwcore_sys as bindings;
pub use qemu_macros::Device;
pub use qom;
mod irq;
pub use irq::*;
// preserve one-item-per-"use" syntax, it is clearer
// for prelude-like modules
#[rustfmt::skip]
pub mod prelude;
mod qdev;
pub use qdev::*;
+12
View File
@@ -0,0 +1,12 @@
//! Essential types and traits intended for blanket imports.
pub use crate::qdev::Clock;
pub use crate::qdev::DeviceClassExt;
pub use crate::qdev::DeviceState;
pub use crate::qdev::DeviceImpl;
pub use crate::qdev::DeviceMethods;
pub use crate::qdev::ResettablePhasesImpl;
pub use crate::qdev::ResetType;
pub use crate::irq::InterruptSource;
+459
View File
@@ -0,0 +1,459 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// 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::{addr_of, NonNull},
};
use chardev::Chardev;
use common::{callbacks::FnCall, Opaque};
use migration::{impl_vmstate_c_struct, VMStateDescription};
use qom::{prelude::*, ObjectClass};
use util::{Error, Result};
pub use crate::bindings::{ClockEvent, ResetType};
use crate::{
bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, DeviceClass, Property},
irq::InterruptSource,
};
/// A safe wrapper around [`bindings::Clock`].
#[repr(transparent)]
#[derive(Debug, common::Wrapper)]
pub struct Clock(Opaque<bindings::Clock>);
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<bindings::DeviceState>);
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<fn(&Self, ResetType)> = 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<fn(&Self, ResetType)> = None;
/// If not None, this phase is called when the object leaves the reset
/// state. Actions affecting other objects are permitted.
const EXIT: Option<fn(&Self, ResetType)> = 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<T: ResettablePhasesImpl>(
obj: *mut qom::bindings::Object,
typ: ResetType,
) {
let state = NonNull::new(obj).unwrap().cast::<T>();
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<T: ResettablePhasesImpl>(
obj: *mut qom::bindings::Object,
typ: ResetType,
) {
let state = NonNull::new(obj).unwrap().cast::<T>();
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<T: ResettablePhasesImpl>(
obj: *mut qom::bindings::Object,
typ: ResetType,
) {
let state = NonNull::new(obj).unwrap().cast::<T>();
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 `BASE_INFO` and `BIT_INFO` must be
/// valid raw references to [`bindings::PropertyInfo`].
///
/// Note we could not use a regular reference:
///
/// ```text
/// const VALUE: &bindings::PropertyInfo = ...
/// ```
///
/// because this results in the following 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 using a `*const` would also 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 BASE_INFO: *const bindings::PropertyInfo;
#[doc(hidden)] // https://github.com/rust-lang/rust/issues/149635
const BIT_INFO: *const bindings::PropertyInfo = {
panic!("invalid type for bit property");
};
}
macro_rules! impl_qdev_prop {
($type:ty,$info:ident$(, $bit_info:ident)?) => {
unsafe impl $crate::qdev::QDevProp for $type {
const BASE_INFO: *const $crate::bindings::PropertyInfo =
addr_of!($crate::bindings::$info);
$(const BIT_INFO: *const $crate::bindings::PropertyInfo =
addr_of!($crate::bindings::$bit_info);)?
}
};
}
impl_qdev_prop!(bool, qdev_prop_bool);
impl_qdev_prop!(u8, qdev_prop_uint8);
impl_qdev_prop!(u16, qdev_prop_uint16);
impl_qdev_prop!(u32, qdev_prop_uint32, qdev_prop_bit);
impl_qdev_prop!(u64, qdev_prop_uint64, qdev_prop_bit64);
impl_qdev_prop!(usize, qdev_prop_usize);
impl_qdev_prop!(i32, qdev_prop_int32);
impl_qdev_prop!(i64, qdev_prop_int64);
impl_qdev_prop!(chardev::CharFrontend, 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<DeviceState>
{
/// _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<fn(&Self) -> 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<VMStateDescription<Self>> = 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<T: DeviceImpl>(
dev: *mut bindings::DeviceState,
errp: *mut *mut util::bindings::Error,
) {
let state = NonNull::new(dev).unwrap().cast::<T>();
let result = T::REALIZE.unwrap()(unsafe { state.as_ref() });
unsafe {
Error::ok_or_propagate(result, errp);
}
}
#[repr(transparent)]
pub struct ResettableClass(bindings::ResettableClass);
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.
fn class_init<T: ResettablePhasesImpl>(&mut self) {
if <T as ResettablePhasesImpl>::ENTER.is_some() {
self.0.phases.enter = Some(rust_resettable_enter_fn::<T>);
}
if <T as ResettablePhasesImpl>::HOLD.is_some() {
self.0.phases.hold = Some(rust_resettable_hold_fn::<T>);
}
if <T as ResettablePhasesImpl>::EXIT.is_some() {
self.0.phases.exit = Some(rust_resettable_exit_fn::<T>);
}
}
}
pub trait DeviceClassExt {
fn class_init<T: DeviceImpl>(&mut self);
}
impl DeviceClassExt for DeviceClass {
fn class_init<T: DeviceImpl>(&mut self) {
if <T as DeviceImpl>::REALIZE.is_some() {
self.realize = Some(rust_realize_fn::<T>);
}
if let Some(ref vmsd) = <T as DeviceImpl>::VMSTATE {
self.vmsd = vmsd.as_ref();
}
let prop = <T as DevicePropertiesImpl>::PROPERTIES;
if !prop.is_empty() {
unsafe {
bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len());
}
}
ResettableClass::cast::<DeviceState>(self).class_init::<T>();
self.parent_class.class_init::<T>();
}
}
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<T: DeviceImpl, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
this: &mut ParentInit<T>,
name: &str,
_cb: &F,
events: ClockEvent,
) -> Owned<Clock>
where
T::ParentType: IsA<DeviceState>,
{
fn do_init_clock_in(
dev: &DeviceState,
name: &str,
cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
events: ClockEvent,
) -> Owned<Clock> {
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<unsafe extern "C" fn(*mut c_void, ClockEvent)> = if F::is_some() {
unsafe extern "C" fn rust_clock_cb<T, F: for<'a> 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::<T>()) }, event))
}
Some(rust_clock_cb::<T, F>)
} 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<T: DeviceImpl>(this: &mut ParentInit<T>, name: &str) -> Owned<Clock>
where
T::ParentType: IsA<DeviceState>,
{
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<DeviceState>`.
///
/// The trait should only be used through the blanket implementation,
/// which guarantees safety via `IsA`.
pub trait DeviceMethods: ObjectDeref
where
Self::Target: IsA<DeviceState>,
{
fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
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<F: for<'a> 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<T, F: for<'a> 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::<T>()) }, line as u32, level as u32))
}
let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) =
rust_irq_handler::<Self::Target, F>;
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<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {}
impl Clock {
pub const PERIOD_1SEC: u64 = bindings::CLOCK_PERIOD_1SEC;
pub const fn period_from_ns(ns: u64) -> u64 {
ns * Self::PERIOD_1SEC / 1_000_000_000
}
pub const fn period_from_hz(hz: u64) -> u64 {
match Self::PERIOD_1SEC.checked_div(hz) {
Some(value) => value,
None => 0,
}
}
pub const fn period_to_hz(period: u64) -> u64 {
match Self::PERIOD_1SEC.checked_div(period) {
Some(value) => value,
None => 0,
}
}
pub const fn period(&self) -> u64 {
// SAFETY: Clock is returned by init_clock_in with zero value for period
unsafe { &*self.0.as_ptr() }.period
}
pub const fn hz(&self) -> u64 {
Self::period_to_hz(self.period())
}
}
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);
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{ffi::CStr, ptr::addr_of};
use bql::prelude::*;
use hwcore::prelude::*;
use migration::prelude::*;
use qom::prelude::*;
use util::bindings::{module_call_init, module_init_type};
// Test that macros can compile.
pub const VMSTATE: VMStateDescription<DummyState> = VMStateDescriptionBuilder::<DummyState>::new()
.name(c"name")
.unmigratable()
.build();
#[repr(C)]
#[derive(qom::Object, hwcore::Device)]
pub struct DummyState {
parent: ParentField<DeviceState>,
#[property(rename = "migrate-clk", default = true)]
migrate_clock: bool,
}
qom_isa!(DummyState: Object, DeviceState);
pub struct DummyClass {
parent_class: <DeviceState as ObjectType>::Class,
}
impl DummyClass {
pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) {
self.parent_class.class_init::<T>();
}
}
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::<Self>;
}
impl ResettablePhasesImpl for DummyState {}
impl DeviceImpl for DummyState {
const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE);
}
#[repr(C)]
#[derive(qom::Object, hwcore::Device)]
pub struct DummyChildState {
parent: ParentField<DummyState>,
}
qom_isa!(DummyChildState: Object, DeviceState, DummyState);
pub struct DummyChildClass {
parent_class: <DummyState as ObjectType>::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::<Self>;
}
impl ResettablePhasesImpl for DummyChildState {}
impl DeviceImpl for DummyChildState {}
impl DummyChildClass {
pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) {
self.parent_class.class_init::<T>();
}
}
fn init_qom() {
static ONCE: BqlCell<bool> = 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::<SysBusDevice>();
//
// 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<&DummyChildState> = 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: &DummyChildState = obj_ref.unsafe_cast();
assert_eq!(addr_of!(*sbd_ref), p_ptr.cast());
}
}
+2
View File
@@ -0,0 +1,2 @@
subdir('char')
subdir('timer')
+2
View File
@@ -0,0 +1,2 @@
config X_HPET_RUST
bool
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "hpet"
version = "0.1.0"
authors = ["Zhao Liu <[email protected]>"]
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" }
trace = { path = "../../../trace" }
[lints]
workspace = true
+6
View File
@@ -0,0 +1,6 @@
_libhpet_rs = cargo_ws.package('hpet').library()
rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
link_whole: [_libhpet_rs],
variables: {'crate': 'hpet'},
)])
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::ptr::addr_of_mut;
use common::Zeroable;
use util::{self, prelude::*};
/// 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() -> util::Result<usize> {
assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
let fw_cfg = unsafe { &mut *addr_of_mut!(hpet_fw_cfg) };
if fw_cfg.count == u8::MAX {
// first instance
fw_cfg.count = 0;
}
ensure!(fw_cfg.count != 8, "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 fw_cfg = unsafe { &mut *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;
}
}
+13
View File
@@ -0,0 +1,13 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <[email protected]>
// 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";
+1
View File
@@ -0,0 +1 @@
subdir('hpet')
+36
View File
@@ -0,0 +1,36 @@
if not have_system
subdir_done()
else
message('Rust enabled but it is only used by system emulators.')
endif
cargo_ws = import('rust').workspace()
genrs = []
subdir('qemu-macros')
subdir('common')
subdir('bindings')
subdir('bits')
subdir('util')
subdir('bql')
subdir('migration')
subdir('qom')
subdir('chardev')
subdir('hw/core')
subdir('system')
subdir('tests')
subdir('trace')
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
+24
View File
@@ -0,0 +1,24 @@
[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]
bql = { path = "../bql" }
common = { path = "../common" }
qemu_macros = { path = "../qemu-macros" }
util = { path = "../util" }
migration-sys = { path = "../bindings/migration-sys" }
glib-sys.workspace = true
[lints]
workspace = true
+13
View File
@@ -0,0 +1,13 @@
_migration_rs = cargo_ws.package('migration').library()
cargo_ws.package('migration').override_dependency(declare_dependency(link_with: _migration_rs))
migration_rs = declare_dependency(link_with: [_migration_rs],
dependencies: [bql_rs, 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,
dependencies: migration_rs,
suite: ['doc', 'rust'])
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pub use migration_sys as bindings;
pub use qemu_macros::ToMigrationState;
pub mod migratable;
pub use migratable::*;
// preserve one-item-per-"use" syntax, it is clearer
// for prelude-like modules
#[rustfmt::skip]
pub mod prelude;
pub mod vmstate;
pub use vmstate::*;
+478
View File
@@ -0,0 +1,478 @@
// Copyright 2025 Red Hat, Inc.
// Author(s): Paolo Bonzini <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
fmt,
mem::size_of,
ptr::{self, addr_of, NonNull},
sync::{Arc, Mutex},
};
use bql::prelude::*;
use common::Zeroable;
use crate::{
bindings, vmstate_fields_ref, vmstate_of, InvalidError, VMState, VMStateDescriptionBuilder,
};
/// Enables QEMU migration support even when a type is wrapped with
/// synchronization primitives (like `Mutex`) that the C migration
/// code cannot directly handle. The trait provides methods to
/// extract essential state for migration and restore it after
/// migration completes.
///
/// On top of extracting data from synchronization wrappers during save
/// and restoring it during load, it's also possible to use `ToMigrationState`
/// to convert runtime representations to migration-safe formats.
///
/// # Examples
///
/// ```
/// use bql::BqlCell;
/// use migration::{InvalidError, ToMigrationState, VMState};
/// # use migration::VMStateField;
///
/// # #[derive(Debug, PartialEq, Eq)]
/// struct DeviceState {
/// counter: BqlCell<u32>,
/// enabled: bool,
/// }
///
/// # #[derive(Debug)]
/// #[derive(Default)]
/// struct DeviceMigrationState {
/// counter: u32,
/// enabled: bool,
/// }
///
/// # unsafe impl VMState for DeviceMigrationState {
/// # const BASE: VMStateField = ::common::Zeroable::ZERO;
/// # }
/// impl ToMigrationState for DeviceState {
/// type Migrated = DeviceMigrationState;
///
/// fn snapshot_migration_state(
/// &self,
/// target: &mut Self::Migrated,
/// ) -> Result<(), InvalidError> {
/// target.counter = self.counter.get();
/// target.enabled = self.enabled;
/// Ok(())
/// }
///
/// fn restore_migrated_state_mut(
/// &mut self,
/// source: Self::Migrated,
/// _version_id: u8,
/// ) -> Result<(), InvalidError> {
/// self.counter.set(source.counter);
/// self.enabled = source.enabled;
/// Ok(())
/// }
/// }
/// # bql::start_test();
/// # let dev = DeviceState { counter: 10.into(), enabled: true };
/// # let mig = dev.to_migration_state().unwrap();
/// # assert!(matches!(*mig, DeviceMigrationState { counter: 10, enabled: true }));
/// # let mut dev2 = DeviceState { counter: 42.into(), enabled: false };
/// # dev2.restore_migrated_state_mut(*mig, 1).unwrap();
/// # assert_eq!(dev2, dev);
/// ```
///
/// More commonly, the trait is derived through the
/// [`derive(ToMigrationState)`](qemu_macros::ToMigrationState) procedural
/// macro.
pub trait ToMigrationState {
/// The type used to represent the migrated state.
type Migrated: Default + VMState;
/// Capture the current state into a migration-safe format, failing
/// if the state cannot be migrated.
fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError>;
/// Restores state from a migrated representation, failing if the
/// state cannot be restored.
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError>;
/// Convenience method to combine allocation and state capture
/// into a single operation.
fn to_migration_state(&self) -> Result<Box<Self::Migrated>, InvalidError> {
let mut migrated = Box::<Self::Migrated>::default();
self.snapshot_migration_state(&mut migrated)?;
Ok(migrated)
}
}
// Implementations for primitive types. Do not use a blanket implementation
// for all Copy types, because [T; N] is Copy if T is Copy; that would conflict
// with the below implementation for arrays.
macro_rules! impl_for_primitive {
($($t:ty),*) => {
$(
impl ToMigrationState for $t {
type Migrated = Self;
fn snapshot_migration_state(
&self,
target: &mut Self::Migrated,
) -> Result<(), InvalidError> {
*target = *self;
Ok(())
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
_version_id: u8,
) -> Result<(), InvalidError> {
*self = source;
Ok(())
}
}
)*
};
}
impl_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, bool);
impl ToMigrationState for util::timer::Timer {
type Migrated = i64;
fn snapshot_migration_state(&self, target: &mut i64) -> Result<(), InvalidError> {
// SAFETY: as_ptr() is unsafe to ensure that the caller reasons about
// the pinning of the data inside the Opaque<>. Here all we do is
// access a field.
*target = self.expire_time_ns().unwrap_or(-1);
Ok(())
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
self.restore_migrated_state(source, version_id)
}
}
impl<T: ToMigrationState, const N: usize> ToMigrationState for [T; N]
where
[T::Migrated; N]: Default,
{
type Migrated = [T::Migrated; N];
fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
for (item, target_item) in self.iter().zip(target.iter_mut()) {
item.snapshot_migration_state(target_item)?;
}
Ok(())
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
for (item, source_item) in self.iter_mut().zip(source) {
item.restore_migrated_state_mut(source_item, version_id)?;
}
Ok(())
}
}
impl<T: ToMigrationState> ToMigrationState for Mutex<T> {
type Migrated = T::Migrated;
fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
self.lock().unwrap().snapshot_migration_state(target)
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
self.get_mut()
.unwrap()
.restore_migrated_state_mut(source, version_id)
}
}
impl<T: ToMigrationState> ToMigrationState for BqlRefCell<T> {
type Migrated = T::Migrated;
fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
self.borrow().snapshot_migration_state(target)
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
self.get_mut()
.restore_migrated_state_mut(source, version_id)
}
}
/// Extension trait for types that support migration state restoration
/// through interior mutability.
///
/// This trait extends [`ToMigrationState`] for types that can restore
/// their state without requiring mutable access. While user structs
/// will generally use `ToMigrationState`, the device will have multiple
/// references and therefore the device struct has to employ an interior
/// mutability wrapper like [`Mutex`] or [`BqlRefCell`].
///
/// Anything that implements this trait can in turn be used within
/// [`Migratable<T>`], which makes no assumptions on how to achieve mutable
/// access to the runtime state.
///
/// # Examples
///
/// ```
/// use std::sync::Mutex;
///
/// use migration::ToMigrationStateShared;
///
/// let device_state = Mutex::new(42);
/// // Can restore without &mut access
/// device_state.restore_migrated_state(100, 1).unwrap();
/// assert_eq!(*device_state.lock().unwrap(), 100);
/// ```
pub trait ToMigrationStateShared: ToMigrationState {
/// Restores state from a migrated representation to an interior-mutable
/// object. Similar to `restore_migrated_state_mut`, but requires a
/// shared reference; therefore it can be used to restore a device's
/// state even though devices have multiple references to them.
fn restore_migrated_state(
&self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError>;
}
impl ToMigrationStateShared for util::timer::Timer {
fn restore_migrated_state(&self, source: i64, _version_id: u8) -> Result<(), InvalidError> {
if source >= 0 {
self.modify_ns(source as u64);
} else {
self.delete();
}
Ok(())
}
}
impl<T: ToMigrationStateShared, const N: usize> ToMigrationStateShared for [T; N]
where
[T::Migrated; N]: Default,
{
fn restore_migrated_state(
&self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
for (item, source_item) in self.iter().zip(source) {
item.restore_migrated_state(source_item, version_id)?;
}
Ok(())
}
}
// Arc requires the contained object to be interior-mutable
impl<T: ToMigrationStateShared> ToMigrationState for Arc<T> {
type Migrated = T::Migrated;
fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
(**self).snapshot_migration_state(target)
}
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
(**self).restore_migrated_state(source, version_id)
}
}
impl<T: ToMigrationStateShared> ToMigrationStateShared for Arc<T> {
fn restore_migrated_state(
&self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
(**self).restore_migrated_state(source, version_id)
}
}
// Interior-mutable types. Note how they only require ToMigrationState for
// the inner type!
impl<T: ToMigrationState> ToMigrationStateShared for Mutex<T> {
fn restore_migrated_state(
&self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
self.lock()
.unwrap()
.restore_migrated_state_mut(source, version_id)
}
}
impl<T: ToMigrationState> ToMigrationStateShared for BqlRefCell<T> {
fn restore_migrated_state(
&self,
source: Self::Migrated,
version_id: u8,
) -> Result<(), InvalidError> {
self.borrow_mut()
.restore_migrated_state_mut(source, version_id)
}
}
/// A wrapper that enables QEMU migration for types with shared state.
///
/// `Migratable<T>` provides a bridge between Rust types that use interior
/// mutability (like `Mutex<T>`) and QEMU's C-based migration infrastructure.
/// It manages the lifecycle of migration state and provides automatic
/// conversion between runtime and migration representations.
///
/// ```
/// # use std::sync::Mutex;
/// # use migration::{Migratable, ToMigrationState, VMState, VMStateField};
///
/// #[derive(ToMigrationState)]
/// pub struct DeviceRegs {
/// status: u32,
/// }
/// # unsafe impl VMState for DeviceRegsMigration {
/// # const BASE: VMStateField = ::common::Zeroable::ZERO;
/// # }
///
/// pub struct SomeDevice {
/// // ...
/// registers: Migratable<Mutex<DeviceRegs>>,
/// }
/// ```
#[repr(C)]
pub struct Migratable<T: ToMigrationStateShared> {
/// Pointer to migration state, valid only during migration operations.
/// C vmstate does not support NULL pointers, so no `Option<Box<>>`.
migration_state: BqlCell<*mut T::Migrated>,
/// The runtime state that can be accessed during normal operation
runtime_state: T,
}
// SAFETY: the migration_state asserts via `BqlCell` that the BQL is taken.
unsafe impl<T: ToMigrationStateShared + Sync> Sync for Migratable<T> {}
impl<T: ToMigrationStateShared> std::ops::Deref for Migratable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.runtime_state
}
}
impl<T: ToMigrationStateShared> std::ops::DerefMut for Migratable<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.runtime_state
}
}
impl<T: ToMigrationStateShared> Migratable<T> {
/// Creates a new `Migratable` wrapper around the given runtime state.
///
/// # Returns
/// A new `Migratable` instance ready for use and migration
pub fn new(runtime_state: T) -> Self {
Self {
migration_state: BqlCell::new(ptr::null_mut()),
runtime_state,
}
}
fn pre_save(&self) -> Result<(), InvalidError> {
let state = self.runtime_state.to_migration_state()?;
self.migration_state.set(Box::into_raw(state));
Ok(())
}
fn post_save(&self) {
let _ = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) };
}
fn pre_load(&self) -> Result<(), InvalidError> {
self.migration_state
.set(Box::into_raw(Box::<T::Migrated>::default()));
Ok(())
}
fn post_load(&self, version_id: u8) -> Result<(), InvalidError> {
let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) };
self.runtime_state
.restore_migrated_state(*state, version_id)
}
}
impl<T: ToMigrationStateShared + fmt::Debug> fmt::Debug for Migratable<T>
where
T::Migrated: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut struct_f = f.debug_struct("Migratable");
struct_f.field("runtime_state", &self.runtime_state);
let state = NonNull::new(self.migration_state.get()).map(|x| unsafe { x.as_ref() });
struct_f.field("migration_state", &state);
struct_f.finish()
}
}
impl<T: ToMigrationStateShared + Default> Default for Migratable<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: 'static + ToMigrationStateShared> Migratable<T> {
const FIELD: bindings::VMStateField = vmstate_of!(Self, migration_state);
const FIELDS: &[bindings::VMStateField] = vmstate_fields_ref! {
Migratable::<T>::FIELD
};
// All Migratable<T> instances share the same name. This is fine because
// Migratable<T> is always a field within a VMSD. The parent VMSD has the
// different name to distinguish child Migratable<T>.
const VMSD: &'static bindings::VMStateDescription = VMStateDescriptionBuilder::<Self>::new()
.name(c"migratable-wrapper")
.version_id(1)
.minimum_version_id(1)
.pre_save(&Self::pre_save)
.pre_load(&Self::pre_load)
.post_save(&Self::post_save)
.post_load(&Self::post_load)
.fields(Self::FIELDS)
.build()
.as_ref();
}
unsafe impl<T: 'static + ToMigrationStateShared> VMState for Migratable<T> {
const BASE: bindings::VMStateField = {
bindings::VMStateField {
vmsd: addr_of!(*Self::VMSD),
size: size_of::<Self>(),
flags: bindings::VMStateFlags::VMS_STRUCT,
..Zeroable::ZERO
}
};
}
+19
View File
@@ -0,0 +1,19 @@
//! Essential types and traits intended for blanket imports.
// Core migration traits and types
pub use crate::vmstate::VMState;
pub use crate::vmstate::VMStateDescription;
pub use crate::vmstate::VMStateDescriptionBuilder;
// Migratable wrappers
pub use crate::migratable::Migratable;
pub use crate::ToMigrationState;
// Commonly used macros
pub use crate::impl_vmstate_forward;
pub use crate::impl_vmstate_struct;
pub use crate::vmstate_fields;
pub use crate::vmstate_of;
pub use crate::vmstate_subsections;
pub use crate::vmstate_unused;
pub use crate::vmstate_validate;
+663
View File
@@ -0,0 +1,663 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// 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) 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, VMStateStructMember};
/// 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`]`<T>` 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<T>(_: PhantomData<T>) -> usize {
/// std::mem::size_of::<T>()
/// }
///
/// struct Foo {
/// x: u16,
/// };
/// // calls size_of_field::<u16>()
/// 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)]
#![allow(unused_variables)]
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::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_forward!`.
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.
#[doc(hidden)] // https://github.com/rust-lang/rust/issues/149635
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<T: VMState>(_: PhantomData<T>) -> 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<T: VMState>(_: PhantomData<T>) -> VMStateFlags {
T::VARRAY_FLAG
}
pub const OPAQUE: &[u8; 1048576] = &[0; 1048576];
pub const fn size_of_ptr_type<T>(_: *const T) -> usize {
::core::mem::size_of::<T>()
}
#[macro_export]
macro_rules! size_of_field_type {
($struct_name:ty, $($field_name:ident).+) => {
$crate::vmstate::size_of_ptr_type(unsafe {
::core::ptr::addr_of!(
(*$crate::vmstate::OPAQUE.as_ptr().cast::<$struct_name>()).$($field_name).+
)
})
};
}
/// 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)
/// 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().cast::<::std::os::raw::c_char>(),
offset: ::std::mem::offset_of!($struct_name, $($field_name).+),
$(num_indirect: $crate::vmstate::VMStateStructMember {
offset: ::std::mem::offset_of!($struct_name, $($num).+) as u32,
size: $crate::size_of_field_type!($struct_name, $($num).+) as u8,
},)?
$(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).+)))?
}
};
}
/// 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: ::core::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!(bql::BqlCell<T> where T: VMState);
impl_vmstate_transparent!(bql::BqlRefCell<T> where T: VMState);
impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
impl_vmstate_transparent!(common::Opaque<T> where T: VMState);
impl_vmstate_transparent!(std::mem::ManuallyDrop<T> where T: VMState);
// 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);
impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY);
impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY);
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<T> where T: VMState);
// Unlike C pointers, Box is always non-null therefore there is no need
// to specify VMS_ALLOC.
impl_vmstate_pointer!(Box<T> where T: VMState);
// Arrays using the underlying type's VMState plus
// VMS_ARRAY/VMS_ARRAY_OF_POINTER
unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
const BASE: VMStateField = <T as VMState>::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<T, F: for<'a> 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::<T>()) };
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__<T, F: for<'a> ::common::FnCall<(&'a T, u8), bool>>(
_phantom: ::core::marker::PhantomData<F>,
) -> $crate::vmstate::VMSFieldExistCb {
const { assert!(F::IS_SOME) };
$crate::vmstate::rust_vms_test_field_exists::<T, F>
}
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
::core::marker::PhantomData
}
Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn)))
}};
}
/// Add a terminator to the fields in the arguments, and return
/// a reference to the resulting array of values.
#[macro_export]
macro_rules! vmstate_fields_ref {
($($field:expr),*$(,)*) => {
&[
$($field),*,
$crate::bindings::VMStateField {
flags: $crate::bindings::VMStateFlags::VMS_END,
..::common::zeroable::Zeroable::ZERO
}
]
}
}
/// 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] = $crate::vmstate_fields_ref!(
$($field),*,
);
_FIELDS
}}
}
#[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_NO_STATE.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
}
};
}
};
}
/// The type returned by [`vmstate_subsections!`](crate::vmstate_subsections).
pub type VMStateSubsections = &'static [Option<&'static crate::bindings::VMStateDescription>];
/// 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::VMStateSubsections = &[
$({
static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get();
Some(&_SUBSECTION)
}),*,
None,
];
&_SUBSECTIONS
}}
}
pub struct VMStateDescription<T>(bindings::VMStateDescription, PhantomData<fn(&T)>);
// 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<T: Sync> Sync for VMStateDescription<T> {}
#[derive(Clone)]
pub struct VMStateDescriptionBuilder<T>(
bindings::VMStateDescription,
Option<*const std::os::raw::c_char>, // the name of VMStateDescription
PhantomData<fn(&T)>,
);
#[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<InvalidError> 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<Errno>>>,
>(
opaque: *mut c_void,
) -> c_int {
// SAFETY: the function is used in T's implementation of VMState
let result = F::call((unsafe { &*(opaque.cast::<T>()) },));
into_neg_errno(result)
}
unsafe extern "C" fn vmstate_post_save_cb<T, F: for<'a> FnCall<(&'a T,), ()>>(opaque: *mut c_void) {
// SAFETY: the function is used in T's implementation of VMState.
F::call((unsafe { &*(opaque.cast::<T>()) },));
}
unsafe extern "C" fn vmstate_post_load_cb<
T,
F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>,
>(
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::<T>()) };
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<T, F: for<'a> 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::<T>()) },))
}
unsafe extern "C" fn vmstate_dev_unplug_pending_cb<T, F: for<'a> 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::<T>()) },))
}
impl<T> VMStateDescriptionBuilder<T> {
#[must_use]
pub const fn name(mut self, name_str: &CStr) -> Self {
self.1 = Some(::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<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
mut self,
_f: &F,
) -> Self {
self.0.pre_load = if F::IS_SOME {
Some(vmstate_no_version_cb::<T, F>)
} else {
None
};
self
}
#[must_use]
pub const fn post_load<F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>>(
mut self,
_f: &F,
) -> Self {
self.0.post_load = if F::IS_SOME {
Some(vmstate_post_load_cb::<T, F>)
} else {
None
};
self
}
#[must_use]
pub const fn pre_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
mut self,
_f: &F,
) -> Self {
self.0.pre_save = if F::IS_SOME {
Some(vmstate_no_version_cb::<T, F>)
} else {
None
};
self
}
#[must_use]
pub const fn post_save<F: for<'a> FnCall<(&'a T,), ()>>(mut self, _f: &F) -> Self {
self.0.post_save = if F::IS_SOME {
Some(vmstate_post_save_cb::<T, F>)
} else {
None
};
self
}
#[must_use]
pub const fn needed<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
self.0.needed = if F::IS_SOME {
Some(vmstate_needed_cb::<T, F>)
} else {
None
};
self
}
#[must_use]
pub const fn unplug_pending<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
self.0.dev_unplug_pending = if F::IS_SOME {
Some(vmstate_dev_unplug_pending_cb::<T, F>)
} else {
None
};
self
}
#[must_use]
pub const fn fields(mut self, fields: &'static [VMStateField]) -> Self {
if fields[fields.len() - 1].flags.0 != VMStateFlags::VMS_END.0 {
panic!("fields are not terminated, use vmstate_fields!");
}
self.0.fields = fields.as_ptr();
self
}
#[must_use]
pub const fn subsections(mut self, subs: &'static VMStateSubsections) -> Self {
if subs[subs.len() - 1].is_some() {
panic!("subsections are not terminated, use vmstate_subsections!");
}
let subs: *const Option<&bindings::VMStateDescription> = subs.as_ptr();
self.0.subsections = subs.cast::<*const bindings::VMStateDescription>();
self
}
#[must_use]
pub const fn build(mut self) -> VMStateDescription<T> {
// FIXME: is_null()/as_ref() become const since v1.84.
assert!(self.1.is_some(), "VMStateDescription requires name field!");
self.0.name = self.1.unwrap();
VMStateDescription::<T>(self.0, PhantomData)
}
#[must_use]
pub const fn new() -> Self {
Self(bindings::VMStateDescription::ZERO, None, PhantomData)
}
}
impl<T> Default for VMStateDescriptionBuilder<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> VMStateDescription<T> {
pub const fn get(&self) -> bindings::VMStateDescription {
self.0
}
pub const fn as_ref(&self) -> &bindings::VMStateDescription {
&self.0
}
}
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "qemu_macros"
version = "0.1.0"
authors = ["Manos Pitsidianakis <[email protected]>"]
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]
attrs = "0.2.9"
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["extra-traits"] }
[lints]
workspace = true
+9
View File
@@ -0,0 +1,9 @@
_qemu_macros_rs = cargo_ws.package('qemu_macros').proc_macro()
cargo_ws.package('qemu_macros').override_dependency(declare_dependency(link_with: _qemu_macros_rs))
qemu_macros = declare_dependency(
link_with: _qemu_macros_rs,
)
rust.test('rust-qemu-macros-tests', _qemu_macros_rs,
suite: ['unit', 'rust'])
+213
View File
@@ -0,0 +1,213 @@
// 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<Item = TokenTree>,
out: &mut TokenStream,
) -> Result<Option<TokenTree>, 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<Item = TokenTree>,
&mut TokenStream,
) -> Result<Option<TokenTree>, Error>,
>(
&self,
tok: TokenTree,
it: &mut dyn Iterator<Item = TokenTree>,
out: &mut TokenStream,
ch: char,
f: F,
method: &'static str,
) -> Result<Option<TokenTree>, 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<Item = TokenTree>,
out: &mut TokenStream,
) -> Result<Option<TokenTree>, Error> {
self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference")
}
// and ::= sub ('&' sub)*
fn parse_and(
&self,
tok: TokenTree,
it: &mut dyn Iterator<Item = TokenTree>,
out: &mut TokenStream,
) -> Result<Option<TokenTree>, Error> {
self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection")
}
// xor ::= and ('&' and)*
fn parse_xor(
&self,
tok: TokenTree,
it: &mut dyn Iterator<Item = TokenTree>,
out: &mut TokenStream,
) -> Result<Option<TokenTree>, 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<Item = TokenTree>,
out: &mut TokenStream,
) -> Result<Option<TokenTree>, Error> {
self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
}
pub fn parse(
it: &mut dyn Iterator<Item = TokenTree>,
) -> Result<proc_macro2::TokenStream, Error> {
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)
}
}
+515
View File
@@ -0,0 +1,515 @@
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later
use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input, parse_quote,
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
Variant,
};
mod bits;
use bits::BitsConstInternal;
mod migration_state;
use migration_state::MigrationStateDerive;
#[cfg(test)]
mod tests;
fn get_fields<'a>(
input: &'a DeriveInput,
msg: &str,
) -> Result<&'a Punctuated<Field, Comma>, 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<proc_macro2::TokenStream, Error> {
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<proc_macro2::TokenStream, Error> {
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 <Self as ::common::opaque::Wrapper>::Wrapped) -> &'a Self {
let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>();
unsafe { ptr.as_ref() }
}
pub const fn as_mut_ptr(&self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
self.0.as_mut_ptr()
}
pub const fn as_ptr(&self) -> *const <Self as ::common::opaque::Wrapper>::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 <Self as ::common::opaque::Wrapper>::Wrapped {
slot.cast()
}
}
})
}
#[derive(Debug)]
enum DevicePropertyName {
CStr(syn::LitCStr),
Str(syn::LitStr),
}
impl Parse for DevicePropertyName {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lo = input.lookahead1();
if lo.peek(syn::LitStr) {
Ok(Self::Str(input.parse()?))
} else if lo.peek(syn::LitCStr) {
Ok(Self::CStr(input.parse()?))
} else {
Err(lo.error())
}
}
}
#[derive(Default, Debug)]
struct DeviceProperty {
rename: Option<DevicePropertyName>,
bitnr: Option<syn::Expr>,
defval: Option<syn::Expr>,
}
impl DeviceProperty {
fn parse_from(&mut self, a: &Attribute) -> syn::Result<()> {
use attrs::{set, with, Attrs};
let mut parser = Attrs::new();
parser.once("rename", with::eq(set::parse(&mut self.rename)));
parser.once("bit", with::eq(set::parse(&mut self.bitnr)));
parser.once("default", with::eq(set::parse(&mut self.defval)));
a.parse_args_with(&mut parser)
}
fn parse(a: &Attribute) -> syn::Result<Self> {
let mut retval = Self::default();
retval.parse_from(a)?;
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<proc_macro2::TokenStream, Error> {
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(), DeviceProperty::parse(a)?)))
})
.collect::<Result<Vec<_>, Error>>()?;
let name = &input.ident;
let mut properties_expanded = vec![];
for (field, prop) in properties {
let DeviceProperty {
rename,
bitnr,
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()),
|prop_rename| -> Result<proc_macro2::TokenStream, Error> {
match prop_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, bitval) = if let Some(bitval) = bitnr {
(
quote! { <#field_ty as ::hwcore::QDevProp>::BIT_INFO },
quote! {
{
const {
assert!(#bitval >= 0 && #bitval < #field_ty::BITS as _,
"bit number exceeds type bits range");
}
#bitval as u8
}
},
)
} else {
(
quote! { <#field_ty as ::hwcore::QDevProp>::BASE_INFO },
quote! { 0 },
)
};
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,
bitnr: #bitval,
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<Path, Error> {
let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
if let Some(repr) = repr {
let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::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<Variant, Comma>, 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<Variant, Comma>,
repr: &Path,
) -> Result<proc_macro2::TokenStream, Error> {
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<proc_macro2::TokenStream, Error> {
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<Self, #repr> {
#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();
let out = BitsConstInternal::parse(&mut it).unwrap_or_else(syn::Error::into_compile_error);
// https://github.com/rust-lang/rust-clippy/issues/15852
quote! {
{
#[allow(clippy::double_parens)]
#out
}
}
.into()
}
/// Derive macro for generating migration state structures and trait
/// implementations.
///
/// This macro generates a migration state struct and implements the
/// `ToMigrationState` trait for the annotated struct, enabling state
/// serialization and restoration. Note that defining a `VMStateDescription`
/// for the migration state struct is left to the user.
///
/// # Container attributes
///
/// The following attributes can be applied to the struct:
///
/// - `#[migration_state(rename = CustomName)]` - Customizes the name of the
/// generated migration struct. By default, the generated struct is named
/// `{OriginalName}Migration`.
///
/// # Field attributes
///
/// The following attributes can be applied to individual fields:
///
/// - `#[migration_state(omit)]` - Excludes the field from the migration state
/// entirely.
///
/// - `#[migration_state(into(Type))]` - Converts the field using `.into()`
/// during both serialization and restoration.
///
/// - `#[migration_state(try_into(Type))]` - Converts the field using
/// `.try_into()` during both serialization and restoration. Returns
/// `InvalidError` on conversion failure.
///
/// - `#[migration_state(clone)]` - Clones the field value.
///
/// Fields without any attributes use `ToMigrationState` recursively; note that
/// this is a simple copy for types that implement `Copy`.
///
/// # Attribute compatibility
///
/// - `omit` cannot be used with any other attributes
/// - only one of `into(Type)`, `try_into(Type)` can be used, but they can be
/// coupled with `clone`.
///
/// # Examples
///
/// Basic usage:
/// ```ignore
/// #[derive(ToMigrationState)]
/// struct MyStruct {
/// field1: u32,
/// field2: Timer,
/// }
/// ```
///
/// With attributes:
/// ```ignore
/// #[derive(ToMigrationState)]
/// #[migration_state(rename = CustomMigration)]
/// struct MyStruct {
/// #[migration_state(omit)]
/// runtime_field: u32,
///
/// #[migration_state(clone)]
/// shared_data: String,
///
/// #[migration_state(into(Cow<'static, str>), clone)]
/// converted_field: String,
///
/// #[migration_state(try_into(i8))]
/// fallible_field: u32,
///
/// // Default: use ToMigrationState trait recursively
/// nested_field: NestedStruct,
///
/// // Primitive types have a default implementation of ToMigrationState
/// simple_field: u32,
/// }
/// ```
#[proc_macro_derive(ToMigrationState, attributes(migration_state))]
pub fn derive_to_migration_state(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
MigrationStateDerive::expand(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
+298
View File
@@ -0,0 +1,298 @@
use std::borrow::Cow;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{spanned::Spanned, DeriveInput, Error, Field, Ident, Result, Type};
use crate::get_fields;
#[derive(Debug, Default)]
enum ConversionMode {
#[default]
None,
Omit,
Into(Type),
TryInto(Type),
ToMigrationState,
}
impl ConversionMode {
fn target_type(&self, original_type: &Type) -> TokenStream {
match self {
ConversionMode::Into(ty) | ConversionMode::TryInto(ty) => ty.to_token_stream(),
ConversionMode::ToMigrationState => {
quote! { <#original_type as ToMigrationState>::Migrated }
}
_ => original_type.to_token_stream(),
}
}
}
#[derive(Debug, Default)]
struct ContainerAttrs {
rename: Option<Ident>,
}
impl ContainerAttrs {
fn parse_from(&mut self, attrs: &[syn::Attribute]) -> Result<()> {
use attrs::{set, with, Attrs};
Attrs::new()
.once("rename", with::eq(set::parse(&mut self.rename)))
.parse_attrs("migration_state", attrs)?;
Ok(())
}
fn parse(attrs: &[syn::Attribute]) -> Result<Self> {
let mut container_attrs = Self::default();
container_attrs.parse_from(attrs)?;
Ok(container_attrs)
}
}
#[derive(Debug, Default)]
struct FieldAttrs {
conversion: ConversionMode,
clone: bool,
}
impl FieldAttrs {
fn parse_from(&mut self, attrs: &[syn::Attribute]) -> Result<()> {
let mut omit_flag = false;
let mut into_type: Option<Type> = None;
let mut try_into_type: Option<Type> = None;
use attrs::{set, with, Attrs};
Attrs::new()
.once("omit", set::flag(&mut omit_flag))
.once("into", with::paren(set::parse(&mut into_type)))
.once("try_into", with::paren(set::parse(&mut try_into_type)))
.once("clone", set::flag(&mut self.clone))
.parse_attrs("migration_state", attrs)?;
self.conversion = match (omit_flag, into_type, try_into_type, self.clone) {
// Valid combinations of attributes first...
(true, None, None, false) => ConversionMode::Omit,
(false, Some(ty), None, _) => ConversionMode::Into(ty),
(false, None, Some(ty), _) => ConversionMode::TryInto(ty),
(false, None, None, true) => ConversionMode::None, // clone without conversion
(false, None, None, false) => ConversionMode::ToMigrationState, // default behavior
// ... then the error cases
(true, _, _, _) => {
return Err(Error::new(
attrs[0].span(),
"ToMigrationState: omit cannot be used with other attributes",
));
}
(_, Some(_), Some(_), _) => {
return Err(Error::new(
attrs[0].span(),
"ToMigrationState: into and try_into attributes cannot be used together",
));
}
};
Ok(())
}
fn parse(attrs: &[syn::Attribute]) -> Result<Self> {
let mut field_attrs = Self::default();
field_attrs.parse_from(attrs)?;
Ok(field_attrs)
}
}
#[derive(Debug)]
struct MigrationStateField {
name: Ident,
original_type: Type,
attrs: FieldAttrs,
}
impl MigrationStateField {
fn maybe_clone(&self, mut value: TokenStream) -> TokenStream {
if self.attrs.clone {
value = quote! { #value.clone() };
}
value
}
fn generate_migration_state_field(&self) -> TokenStream {
let name = &self.name;
let field_type = self.attrs.conversion.target_type(&self.original_type);
quote! {
pub #name: #field_type,
}
}
fn generate_snapshot_field(&self) -> TokenStream {
let name = &self.name;
let value = self.maybe_clone(quote! { self.#name });
match &self.attrs.conversion {
ConversionMode::Omit => {
unreachable!("Omitted fields are filtered out during processing")
}
ConversionMode::None => quote! {
target.#name = #value;
},
ConversionMode::Into(_) => quote! {
target.#name = #value.into();
},
ConversionMode::TryInto(_) => quote! {
target.#name = #value.try_into().map_err(|_| migration::InvalidError)?;
},
ConversionMode::ToMigrationState => quote! {
self.#name.snapshot_migration_state(&mut target.#name)?;
},
}
}
fn generate_restore_field(&self) -> TokenStream {
let name = &self.name;
match &self.attrs.conversion {
ConversionMode::Omit => {
unreachable!("Omitted fields are filtered out during processing")
}
ConversionMode::None => quote! {
self.#name = #name;
},
ConversionMode::Into(_) => quote! {
self.#name = #name.into();
},
ConversionMode::TryInto(_) => quote! {
self.#name = #name.try_into().map_err(|_| migration::InvalidError)?;
},
ConversionMode::ToMigrationState => quote! {
self.#name.restore_migrated_state_mut(#name, _version_id)?;
},
}
}
}
#[derive(Debug)]
pub struct MigrationStateDerive {
input: DeriveInput,
fields: Vec<MigrationStateField>,
container_attrs: ContainerAttrs,
}
impl MigrationStateDerive {
fn parse(input: DeriveInput) -> Result<Self> {
let container_attrs = ContainerAttrs::parse(&input.attrs)?;
let fields = get_fields(&input, "ToMigrationState")?;
let fields = Self::process_fields(fields)?;
Ok(Self {
input,
fields,
container_attrs,
})
}
fn process_fields(
fields: &syn::punctuated::Punctuated<Field, syn::token::Comma>,
) -> Result<Vec<MigrationStateField>> {
let processed = fields
.iter()
.map(|field| {
let attrs = FieldAttrs::parse(&field.attrs)?;
Ok((field, attrs))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.filter(|(_, attrs)| !matches!(attrs.conversion, ConversionMode::Omit))
.map(|(field, attrs)| MigrationStateField {
name: field.ident.as_ref().unwrap().clone(),
original_type: field.ty.clone(),
attrs,
})
.collect();
Ok(processed)
}
fn migration_state_name(&self) -> Cow<'_, Ident> {
match &self.container_attrs.rename {
Some(rename) => Cow::Borrowed(rename),
None => Cow::Owned(format_ident!("{}Migration", &self.input.ident)),
}
}
fn generate_migration_state_struct(&self) -> TokenStream {
let name = self.migration_state_name();
let fields = self
.fields
.iter()
.map(MigrationStateField::generate_migration_state_field);
quote! {
#[derive(Default)]
pub struct #name {
#(#fields)*
}
}
}
fn generate_snapshot_migration_state(&self) -> TokenStream {
let fields = self
.fields
.iter()
.map(MigrationStateField::generate_snapshot_field);
quote! {
fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), migration::InvalidError> {
#(#fields)*
Ok(())
}
}
}
fn generate_restore_migrated_state(&self) -> TokenStream {
let names: Vec<_> = self.fields.iter().map(|f| &f.name).collect();
let fields = self
.fields
.iter()
.map(MigrationStateField::generate_restore_field);
// version_id could be used or not depending on conversion attributes
quote! {
#[allow(clippy::used_underscore_binding)]
fn restore_migrated_state_mut(&mut self, source: Self::Migrated, _version_id: u8) -> Result<(), migration::InvalidError> {
let Self::Migrated { #(#names),* } = source;
#(#fields)*
Ok(())
}
}
}
fn generate(&self) -> TokenStream {
let struct_name = &self.input.ident;
let generics = &self.input.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let name = self.migration_state_name();
let migration_state_struct = self.generate_migration_state_struct();
let snapshot_impl = self.generate_snapshot_migration_state();
let restore_impl = self.generate_restore_migrated_state();
quote! {
#migration_state_struct
impl #impl_generics ToMigrationState for #struct_name #ty_generics #where_clause {
type Migrated = #name;
#snapshot_impl
#restore_impl
}
}
}
pub fn expand(input: DeriveInput) -> Result<TokenStream> {
let tokens = Self::parse(input)?.generate();
Ok(tokens)
}
}
+465
View File
@@ -0,0 +1,465 @@
// Copyright 2025, Linaro Limited
// Author(s): Manos Pitsidianakis <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later
use quote::quote;
use super::*;
macro_rules! derive_compile_fail {
($derive_fn:path, $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<proc_macro2::TokenStream, syn::Error> =
$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:path, $input:expr, $($expected:tt)*) => {{
let input: proc_macro2::TokenStream = $input;
let expected: proc_macro2::TokenStream = $($expected)*;
let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
$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,
}
},
"Expected one of `bit`, `default` or `rename`"
);
// 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,
}
},
"Duplicate argument",
"Already used here",
);
derive_compile_fail!(
derive_device_or_error,
quote! {
#[repr(C)]
#[derive(Device)]
struct DummyState {
#[property(default = true, default = true)]
migrate_clock: bool,
}
},
"Duplicate argument",
"Already used here",
);
derive_compile_fail!(
derive_device_or_error,
quote! {
#[repr(C)]
#[derive(Device)]
struct DummyState {
#[property(bit = 0, bit = 1)]
flags: u32,
}
},
"Duplicate argument",
"Already used here",
);
// 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<DeviceState>,
#[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: <bool as ::hwcore::QDevProp>::BASE_INFO,
offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
bitnr: 0,
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<DeviceState>,
#[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: <bool as ::hwcore::QDevProp>::BASE_INFO,
offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
bitnr: 0,
set_default: true,
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
..::common::Zeroable::ZERO
}
];
}
}
);
// Check that `bit` value is used for the bit property without default
// value (note: though C macro (e.g., DEFINE_PROP_BIT) always requires
// default value, Rust side allows to default this field to "0"):
derive_compile!(
derive_device_or_error,
quote! {
#[repr(C)]
#[derive(Device)]
pub struct DummyState {
parent: ParentField<DeviceState>,
#[property(bit = 3)]
flags: u32,
}
},
quote! {
unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
::hwcore::bindings::Property {
name: ::std::ffi::CStr::as_ptr(c"flags"),
info: <u32 as ::hwcore::QDevProp>::BIT_INFO,
offset: ::core::mem::offset_of!(DummyState, flags) as isize,
bitnr : {
const { assert!(3 >= 0 && 3 < u32::BITS as _ , "bit number exceeds type bits range"); }
3 as u8
},
set_default: false,
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: 0 as u64 },
..::common::Zeroable::ZERO
}
];
}
}
);
// Check that `bit` value is used for the bit property when used:
derive_compile!(
derive_device_or_error,
quote! {
#[repr(C)]
#[derive(Device)]
pub struct DummyState {
parent: ParentField<DeviceState>,
#[property(bit = 3, default = true)]
flags: u32,
}
},
quote! {
unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
::hwcore::bindings::Property {
name: ::std::ffi::CStr::as_ptr(c"flags"),
info: <u32 as ::hwcore::QDevProp>::BIT_INFO,
offset: ::core::mem::offset_of!(DummyState, flags) as isize,
bitnr : {
const { assert!(3 >= 0 && 3 < u32::BITS as _ , "bit number exceeds type bits range"); }
3 as u8
},
set_default: true,
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
..::common::Zeroable::ZERO
}
];
}
}
);
// Check that `bit` value is used for the bit property with rename when used:
derive_compile!(
derive_device_or_error,
quote! {
#[repr(C)]
#[derive(Device)]
pub struct DummyState {
parent: ParentField<DeviceState>,
#[property(rename = "msi", bit = 3, default = false)]
flags: u64,
}
},
quote! {
unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
::hwcore::bindings::Property {
name: ::std::ffi::CStr::as_ptr(c"msi"),
info: <u64 as ::hwcore::QDevProp>::BIT_INFO,
offset: ::core::mem::offset_of!(DummyState, flags) as isize,
bitnr : {
const { assert!(3 >= 0 && 3 < u64::BITS as _ , "bit number exceeds type bits range"); }
3 as u8
},
set_default: true,
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: false 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<<Foo as ::qom::ObjectImpl>::ParentType>
);
::util::module_init! {
MODULE_INIT_QOM => unsafe {
::qom::type_register_static(&<Foo as ::qom::ObjectImpl>::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<u8> for Foo {
type Error = u8;
#[allow(ambiguous_associated_items)]
fn try_from(value: u8) -> Result<Self, u8> {
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),
}
}
}
}
);
}
#[test]
fn test_derive_to_migration_state() {
derive_compile_fail!(
MigrationStateDerive::expand,
quote! {
struct MyStruct {
#[migration_state(omit, clone)]
bad: u32,
}
},
"ToMigrationState: omit cannot be used with other attributes"
);
derive_compile_fail!(
MigrationStateDerive::expand,
quote! {
struct MyStruct {
#[migration_state(into)]
bad: u32,
}
},
"unexpected end of input, expected parentheses"
);
derive_compile_fail!(
MigrationStateDerive::expand,
quote! {
struct MyStruct {
#[migration_state(into(String), try_into(String))]
bad: &'static str,
}
},
"ToMigrationState: into and try_into attributes cannot be used together"
);
derive_compile!(
MigrationStateDerive::expand,
quote! {
#[migration_state(rename = CustomMigration)]
struct MyStruct {
#[migration_state(omit)]
runtime_field: u32,
#[migration_state(clone)]
shared_data: String,
#[migration_state(into(Cow<'static, str>), clone)]
converted_field: String,
#[migration_state(try_into(i8))]
fallible_field: u32,
nested_field: NestedStruct,
simple_field: u32,
}
},
quote! {
#[derive(Default)]
pub struct CustomMigration {
pub shared_data: String,
pub converted_field: Cow<'static, str>,
pub fallible_field: i8,
pub nested_field: <NestedStruct as ToMigrationState>::Migrated,
pub simple_field: <u32 as ToMigrationState>::Migrated,
}
impl ToMigrationState for MyStruct {
type Migrated = CustomMigration;
fn snapshot_migration_state(
&self,
target: &mut Self::Migrated
) -> Result<(), migration::InvalidError> {
target.shared_data = self.shared_data.clone();
target.converted_field = self.converted_field.clone().into();
target.fallible_field = self
.fallible_field
.try_into()
.map_err(|_| migration::InvalidError)?;
self.nested_field
.snapshot_migration_state(&mut target.nested_field)?;
self.simple_field
.snapshot_migration_state(&mut target.simple_field)?;
Ok(())
}
#[allow(clippy::used_underscore_binding)]
fn restore_migrated_state_mut(
&mut self,
source: Self::Migrated,
_version_id: u8
) -> Result<(), migration::InvalidError> {
let Self::Migrated {
shared_data,
converted_field,
fallible_field,
nested_field,
simple_field
} = source;
self.shared_data = shared_data;
self.converted_field = converted_field.into();
self.fallible_field = fallible_field
.try_into()
.map_err(|_| migration::InvalidError)?;
self.nested_field
.restore_migrated_state_mut(nested_field, _version_id)?;
self.simple_field
.restore_migrated_state_mut(simple_field, _version_id)?;
Ok(())
}
}
}
);
}
+25
View File
@@ -0,0 +1,25 @@
[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" }
qom-sys = { path = "../bindings/qom-sys" }
glib-sys.workspace = true
[lints]
workspace = true
+12
View File
@@ -0,0 +1,12 @@
_qom_rs = cargo_ws.package('qom').library()
cargo_ws.package('qom').override_dependency(declare_dependency(link_with: _qom_rs))
qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom, 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-qom-rs-doctests',
_qom_rs,
dependencies: qom_rs,
suite: ['doc', 'rust'])

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