Import QEMU upstream snapshot d2e570c
Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
source hw/Kconfig
|
||||
@@ -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..])
|
||||
}
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
../build.rs
|
||||
@@ -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 {}
|
||||
@@ -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])
|
||||
@@ -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"
|
||||
@@ -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())
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
../build.rs
|
||||
@@ -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 {}
|
||||
@@ -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])
|
||||
@@ -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"
|
||||
@@ -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')
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
../build.rs
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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])
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../build.rs
|
||||
@@ -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 {}
|
||||
@@ -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])
|
||||
@@ -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"
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
../build.rs
|
||||
@@ -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 {}
|
||||
@@ -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])
|
||||
@@ -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"
|
||||
@@ -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"]
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../build.rs
|
||||
@@ -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"));
|
||||
@@ -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])
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! Essential types and traits intended for blanket imports.
|
||||
|
||||
pub use crate::cell::BqlCell;
|
||||
pub use crate::cell::BqlRefCell;
|
||||
@@ -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
|
||||
@@ -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])
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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__)
|
||||
}};
|
||||
}
|
||||
@@ -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() };
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# devices Kconfig
|
||||
source char/Kconfig
|
||||
source timer/Kconfig
|
||||
@@ -0,0 +1,2 @@
|
||||
config X_PL011_RUST
|
||||
bool
|
||||
@@ -0,0 +1 @@
|
||||
subdir('pl011')
|
||||
@@ -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
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../../bindings/build.rs
|
||||
@@ -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'},
|
||||
)])
|
||||
@@ -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"));
|
||||
@@ -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();
|
||||
@@ -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";
|
||||
@@ -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);
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
@@ -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);
|
||||
@@ -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::*;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
subdir('char')
|
||||
subdir('timer')
|
||||
@@ -0,0 +1,2 @@
|
||||
config X_HPET_RUST
|
||||
bool
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
@@ -0,0 +1 @@
|
||||
subdir('hpet')
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
@@ -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::*;
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user