From patchwork Thu Dec 5 06:07:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894727 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 62438E7716D for ; Thu, 5 Dec 2024 05:50:53 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4jt-0006Ae-37; Thu, 05 Dec 2024 00:49:25 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4js-0006AP-5d; Thu, 05 Dec 2024 00:49:24 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jo-00077P-My; Thu, 05 Dec 2024 00:49:23 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377761; x=1764913761; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=u4ULxLbBoMZS1PFeI3YxwZ6tEeXAKH8bdHoJ0zJEbDo=; b=NY/CRAKGVTzb9ofv5qTZlv7DhPY6Qd01G89G6HZIbThX1BUqWqrs2Hp4 F/Fatw0PK+BIoLxTWZqp6DLqRY1SscyC/Ua1ZSR0Ueqx1Ns8xghgdUzbo ZTfpRR/1B2nGxaJxtEB6FBhZUGTctkt5McAugVkOre+XlZK2si94zRTss KHjEKpyKoTdT9qYAu47AI0nAME8s9xKf8+IP19DdbyPrb8okDwheYmHCa BTNQ1RRLXhpifnl5o3k3bnyZEYPClJxanifz9eVxQIQ5IkTDjPKFpuJHV 46dcRbkqw1ElEOw9G+uuTfYilJFb279RxUn/18ar0aC7j4FKUXngawBwW A==; X-CSE-ConnectionGUID: AcLTKBurROyFsFvBO8jIag== X-CSE-MsgGUID: P8+K/3U1Su6dg64iijPVPw== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815604" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815604" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:17 -0800 X-CSE-ConnectionGUID: bAZlyB9+Tn67qaLo02lmMQ== X-CSE-MsgGUID: rvtGDoXxTwqC9yftYwTyHw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94454950" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:14 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 01/13] bql: check that the BQL is not dropped within marked sections Date: Thu, 5 Dec 2024 14:07:02 +0800 Message-Id: <20241205060714.256270-2-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Paolo Bonzini The Big QEMU Lock (BQL) is used to provide interior mutability to Rust code. While BqlCell performs indivisible accesses, an equivalent of RefCell will allow the borrower to hold to the interior content for a long time. If the BQL is dropped, another thread could come and mutate the data from C code (Rust code would panic on borrow_mut() instead). In order to prevent this, add a new BQL primitive that can mark BQL-atomic sections and aborts if the BQL is dropped within them. Signed-off-by: Paolo Bonzini Signed-off-by: Zhao Liu --- include/qemu/main-loop.h | 15 +++++++++++++++ stubs/iothread-lock.c | 15 +++++++++++++++ system/cpus.c | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 5764db157c97..646306c272f7 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -262,6 +262,21 @@ AioContext *iohandler_get_aio_context(void); */ bool bql_locked(void); +/** + * bql_block: Allow/deny releasing the BQL + * + * The Big QEMU Lock (BQL) is used to provide interior mutability to + * Rust code, but this only works if other threads cannot run while + * the Rust code has an active borrow. This is because C code in + * other threads could come in and mutate data under the Rust code's + * feet. + * + * @increase: Whether to increase or decrease the blocking counter. + * Releasing the BQL while the counter is nonzero triggers + * an assertion failure. + */ +void bql_block_unlock(bool increase); + /** * qemu_in_main_thread: return whether it's possible to safely access * the global state of the block layer. diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index d7890e5581c5..54676598950f 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -1,6 +1,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +static uint32_t bql_unlock_blocked; + bool bql_locked(void) { return false; @@ -12,4 +14,17 @@ void bql_lock_impl(const char *file, int line) void bql_unlock(void) { + assert(!bql_unlock_blocked); +} + +void bql_block_unlock(bool increase) +{ + uint32_t new_value; + + assert(bql_locked()); + + /* check for overflow! */ + new_value = bql_unlock_blocked + increase - !increase; + assert((new_value > bql_unlock_blocked) == increase); + bql_unlock_blocked = new_value; } diff --git a/system/cpus.c b/system/cpus.c index a1b46f68476a..793c4698c7ad 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -514,6 +514,20 @@ bool qemu_in_vcpu_thread(void) QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) +static uint32_t bql_unlock_blocked; + +void bql_block_unlock(bool increase) +{ + uint32_t new_value; + + assert(bql_locked()); + + /* check for overflow! */ + new_value = bql_unlock_blocked + increase - !increase; + assert((new_value > bql_unlock_blocked) == increase); + bql_unlock_blocked = new_value; +} + bool bql_locked(void) { return get_bql_locked(); @@ -540,6 +554,7 @@ void bql_lock_impl(const char *file, int line) void bql_unlock(void) { g_assert(bql_locked()); + g_assert(!bql_unlock_blocked); set_bql_locked(false); qemu_mutex_unlock(&bql); } From patchwork Thu Dec 5 06:07:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894729 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 39F66E7716C for ; Thu, 5 Dec 2024 05:51:00 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4k0-0006DL-PP; Thu, 05 Dec 2024 00:49:32 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jz-0006CK-5E; Thu, 05 Dec 2024 00:49:31 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jw-00079W-B9; Thu, 05 Dec 2024 00:49:30 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377769; x=1764913769; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=5GfGup5DLVmiUBqW9hhr7aRRGVgIdFi7ux9lu3YbrqI=; b=mcot60Sk3d0cTc8DoT4HOvg/ZU+cXmYZgvmkWaJX9ulTam5SxyQH8zOQ iW+trS6NRKOVbQwTbqTesPWnsbXeCaUqhQaEHlA86DzvnAOaIY1eSbd6a MXShIF2VbymC8qD86popWxrPWSywQFTz0WLlmUg28Tz4iM4O6+5Z5ANhr crOCmnzX5gHyw05wEu/0JIxNwTKFLKcmhXdNpzyf8hTtgfIhQXCb7vd1l IqcHGNjz7pn1NUx5wN5qL7Rwsy25sexDxVF6YazVfCapQ1GTb5d+gn0Eu xa6wbW3RzzxQKLdZngxgy2aqZkoA5O2MnuBGJPSXf4bZTal5+pFWZ22ad g==; X-CSE-ConnectionGUID: YwGM9lLbTrOgG7DGip3mfA== X-CSE-MsgGUID: 0QnMFm4ITpy8YOjNFMcodg== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815619" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815619" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:24 -0800 X-CSE-ConnectionGUID: /QiqOQxaQMCVpV0QFAy7HA== X-CSE-MsgGUID: CAeHZVKvSsOgmkKo34CkgA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94454955" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:17 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 02/13] rust: cell: add BQL-enforcing RefCell variant Date: Thu, 5 Dec 2024 14:07:03 +0800 Message-Id: <20241205060714.256270-3-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Paolo Bonzini Similar to the existing BqlCell, introduce a custom interior mutability primitive that resembles RefCell but accounts for QEMU's threading model. Borrowing the RefCell requires proving that the BQL is held, and attempting to access without the BQL is a runtime panic. Almost all of the code was taken from Rust's standard library, while removing unstable features and probably-unnecessary functionality that amounts to 60% of the original code. A lot of what's left is documentation, as well as unit tests in the form of doctests. These are not yet integrated in "make check" but can be run with "cargo test --doc". Signed-off-by: Paolo Bonzini Signed-off-by: Zhao Liu --- Changes Before RFC v1: * Changed debug_assert to assert like what Paolo did for BqlCell. * Added #[derive(Debug)] for BqlRefCell since it looks like BqlRefCell will often be embedded in the structure with Debug, e.g., HPETState. --- rust/qemu-api/Cargo.toml | 3 +- rust/qemu-api/meson.build | 3 + rust/qemu-api/src/cell.rs | 546 +++++++++++++++++++++++++++++++++++++- 3 files changed, 541 insertions(+), 11 deletions(-) diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 669f288d1cb5..4aa22f319860 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,8 +20,9 @@ qemu_api_macros = { path = "../qemu-api-macros" } version_check = "~0.9" [features] -default = [] +default = ["debug_cell"] allocator = [] +debug_cell = [] [lints] workspace = true diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index d727ccf18354..3ac69cbc76c4 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -6,6 +6,9 @@ _qemu_api_cfg = run_command(rustc_args, if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] endif +if get_option('debug_mutex') + _qemu_api_cfg += ['--feature', 'debug_cell'] +endif _qemu_api_rs = static_library( 'qemu_api', diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 2e4ea8d590d5..07b636f26266 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -46,20 +46,30 @@ //! parts of a device must be made mutable in a controlled manner through the //! use of cell types. //! -//! This module provides a way to do so via the Big QEMU Lock. While -//! [`BqlCell`] is essentially the same single-threaded primitive that is -//! available in `std::cell`, the BQL allows it to be used from a multi-threaded -//! context and to share references across threads, while maintaining Rust's -//! safety guarantees. For this reason, unlike its `std::cell` counterpart, -//! `BqlCell` implements the `Sync` trait. +//! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. +//! While they are essentially the same single-threaded primitives that are +//! available in `std::cell`, the BQL allows them to be used from a +//! multi-threaded context and to share references across threads, while +//! maintaining Rust's safety guarantees. For this reason, unlike +//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the +//! `Sync` trait. //! //! BQL checks are performed in debug builds but can be optimized away in //! release builds, providing runtime safety during development with no overhead //! in production. //! -//! Warning: While `BqlCell` is similar to its `std::cell` counterpart, the two -//! are not interchangeable. Using `std::cell` types in QEMU device -//! implementations is usually incorrect and can lead to thread-safety issues. +//! 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 safety issues. //! //! ## `BqlCell` //! @@ -80,8 +90,37 @@ //! returns the replaced value. //! - [`set`](BqlCell::set): this method replaces the interior value, //! dropping the replaced value. +//! +//! ## `BqlRefCell` +//! +//! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a +//! process whereby one can claim temporary, exclusive, mutable access to the +//! inner value: +//! +//! ```ignore +//! fn clear_interrupts(&self, val: u32) { +//! // A mutable borrow gives read-write access to the registers +//! let mut regs = self.registers.borrow_mut(); +//! let old = regs.interrupt_status(); +//! regs.update_interrupt_status(old & !val); +//! } +//! ``` +//! +//! Borrows for `BqlRefCell`s are tracked at _runtime_, unlike Rust's native +//! reference types which are entirely tracked statically, at compile time. +//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), +//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The +//! thread will panic if these rules are violated or if the BQL is not held. -use std::{cell::UnsafeCell, cmp::Ordering, fmt, mem}; +use std::{ + cell::{Cell, UnsafeCell}, + cmp::Ordering, + fmt, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; use crate::bindings; @@ -93,6 +132,15 @@ pub fn bql_locked() -> bool { !cfg!(MESON) || unsafe { bindings::bql_locked() } } +fn bql_block_unlock(increase: bool) { + if cfg!(MESON) { + // SAFETY: this only adjusts a counter + unsafe { + bindings::bql_block_unlock(increase); + } + } +} + /// A mutable memory location that is protected by the Big QEMU Lock. /// /// # Memory layout @@ -296,3 +344,481 @@ 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` starts with the same in-memory representation as its +/// inner type `T`. +#[repr(C)] +#[derive(Debug)] +pub struct BqlRefCell { + // It is important that this is the first field (which is not the case + // for std::cell::BqlRefCell), so that we can use offset_of! on it. + // UnsafeCell and repr(C) both prevent usage of niches. + value: UnsafeCell, + borrow: Cell, + // Stores the location of the earliest currently active borrow. + // This gets updated whenever we go from having zero borrows + // to having a single borrow. When a borrow occurs, this gets included + // in the panic message + #[cfg(feature = "debug_cell")] + borrowed_at: Cell>>, +} + +// Positive values represent the number of `BqlRef` active. Negative values +// represent the number of `BqlRefMut` active. Right now QEMU's implementation +// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping +// components of a `BqlRefCell` (e.g., different ranges of a slice). +// +// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely +// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of +// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or +// underflow. However, this is not a guarantee, as a pathological program could +// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all +// code must explicitly check for overflow and underflow in order to avoid +// unsafety, or at least behave correctly in the event that overflow or +// underflow happens (e.g., see BorrowRef::new). +type BorrowFlag = isize; +const UNUSED: BorrowFlag = 0; + +#[inline(always)] +const fn is_writing(x: BorrowFlag) -> bool { + x < UNUSED +} + +#[inline(always)] +const fn is_reading(x: BorrowFlag) -> bool { + x > UNUSED +} + +impl BqlRefCell { + /// Creates a new `BqlRefCell` containing `value`. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlRefCell { + BqlRefCell { + value: UnsafeCell::new(value), + borrow: Cell::new(UNUSED), + #[cfg(feature = "debug_cell")] + borrowed_at: Cell::new(None), + } + } +} + +// This ensures the panicking code is outlined from `borrow_mut` for +// `BqlRefCell`. +#[inline(never)] +#[cold] +#[cfg(feature = "debug_cell")] +fn panic_already_borrowed(source: &Cell>>) -> ! { + // If a borrow occurred, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + panic!("already borrowed at {:?}", source.take().unwrap()) +} + +#[inline(never)] +#[cold] +#[cfg(not(feature = "debug_cell"))] +fn panic_already_borrowed() -> ! { + panic!("already borrowed") +} + +impl BqlRefCell { + #[inline] + #[allow(clippy::unused_self)] + fn panic_already_borrowed(&self) -> ! { + #[cfg(feature = "debug_cell")] + { + panic_already_borrowed(&self.borrowed_at) + } + #[cfg(not(feature = "debug_cell"))] + { + panic_already_borrowed() + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRef` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let borrowed_five = c.borrow(); + /// let borrowed_five2 = c.borrow(); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use qemu_api::cell::BqlRefCell; + /// + /// 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> { + assert!(bql_locked()); + 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())); + } + + bql_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 qemu_api::cell::BqlRefCell; + /// + /// 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 qemu_api::cell::BqlRefCell; + /// + /// 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 + bql_block_unlock(true); + + // SAFETY: `BorrowRefMut` guarantees unique access. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRefMut { + value, + _borrow: b, + marker: PhantomData, + } + } else { + self.panic_already_borrowed() + } + } + + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is +// stored out-of-line. Even though BqlRefCell includes Cells, they are +// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU +// Lock cannot be released while any borrows is active. +unsafe impl Send for BqlRefCell where T: Send {} +unsafe impl Sync for BqlRefCell {} + +impl Clone for BqlRefCell { + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone(&self) -> BqlRefCell { + BqlRefCell::new(self.borrow().clone()) + } + + /// # Panics + /// + /// Panics if `source` is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone_from(&mut self, source: &Self) { + self.value.get_mut().clone_from(&source.borrow()) + } +} + +impl Default for BqlRefCell { + /// Creates a `BqlRefCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlRefCell { + BqlRefCell::new(Default::default()) + } +} + +impl PartialEq for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn eq(&self, other: &BqlRefCell) -> bool { + *self.borrow() == *other.borrow() + } +} + +impl Eq for BqlRefCell {} + +impl PartialOrd for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn partial_cmp(&self, other: &BqlRefCell) -> Option { + self.borrow().partial_cmp(&*other.borrow()) + } +} + +impl Ord for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn cmp(&self, other: &BqlRefCell) -> Ordering { + self.borrow().cmp(&*other.borrow()) + } +} + +impl From for BqlRefCell { + /// Creates a new `BqlRefCell` containing the given value. + fn from(t: T) -> BqlRefCell { + BqlRefCell::new(t) + } +} + +struct BorrowRef<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRef<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + let b = borrow.get().wrapping_add(1); + if !is_reading(b) { + // Incrementing borrow can result in a non-reading value (<= 0) in these cases: + // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read + // borrow due to Rust's reference aliasing rules + // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed + // into isize::MIN (the max amount of writing borrows) so we can't allow an + // additional read borrow because isize can't represent so many read borrows + // (this can only happen if you mem::forget more than a small constant amount + // of `BqlRef`s, which is not good practice) + None + } else { + // Incrementing borrow can result in a reading value (> 0) in these cases: + // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read + // borrow + // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is + // large enough to represent having one more read borrow + borrow.set(b); + Some(BorrowRef { borrow }) + } + } +} + +impl Drop for BorrowRef<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + assert!(is_reading(borrow)); + self.borrow.set(borrow - 1); + bql_block_unlock(false) + } +} + +impl Clone for BorrowRef<'_> { + #[inline] + fn clone(&self) -> Self { + BorrowRef::new(self.borrow).unwrap() + } +} + +/// Wraps a borrowed reference to a value in a `BqlRefCell` box. +/// A wrapper type for an immutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRef<'b, T: 'b> { + // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a + // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops. + // `NonNull` is also covariant over `T`, just like we would have with `&T`. + value: NonNull, + borrow: BorrowRef<'b>, +} + +impl Deref for BqlRef<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl<'b, T> BqlRef<'b, T> { + /// Copies a `BqlRef`. + /// + /// The `BqlRefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `BqlRef::clone(...)`. A `Clone` implementation or a method would + /// interfere with the widespread use of `r.borrow().clone()` to clone + /// the contents of a `BqlRefCell`. + #[must_use] + #[inline] + #[allow(clippy::should_implement_trait)] + pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> { + BqlRef { + value: orig.value, + borrow: orig.borrow.clone(), + } + } +} + +impl fmt::Debug for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +struct BorrowRefMut<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRefMut<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + // There must currently be no existing references when borrow_mut() is + // called, so we explicitly only allow going from UNUSED to UNUSED - 1. + match borrow.get() { + UNUSED => { + borrow.set(UNUSED - 1); + Some(BorrowRefMut { borrow }) + } + _ => None, + } + } +} + +impl Drop for BorrowRefMut<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + assert!(is_writing(borrow)); + self.borrow.set(borrow + 1); + bql_block_unlock(false) + } +} + +/// A wrapper type for a mutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRefMut<'b, T: 'b> { + // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a + // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. + value: NonNull, + _borrow: BorrowRefMut<'b>, + // `NonNull` is covariant over `T`, so we need to reintroduce invariance. + marker: PhantomData<&'b mut T>, +} + +impl Deref for BqlRefMut<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl DerefMut for BqlRefMut<'_, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_mut() } + } +} + +impl fmt::Debug for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} From patchwork Thu Dec 5 06:07:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894723 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5AFD4E77173 for ; Thu, 5 Dec 2024 05:49:58 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4jw-0006BY-SY; Thu, 05 Dec 2024 00:49:28 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jw-0006BM-0L; Thu, 05 Dec 2024 00:49:28 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4ju-00079A-F5; Thu, 05 Dec 2024 00:49:27 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377767; x=1764913767; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Sx8nPxJJRfYhQt7D0F4clWV/fbFfMbPN7go3+4J/5Rc=; b=k9XC99M/lx1XFVaz2A4xoDvLqKocn6Eo9qv8T4oeYxh+3UullTfn1Dim Cnty98ONsKmaASjVR/Rqw1jodll8N3WYWXucnsKxj+biWDJsRPFFg6wsi HTMLG3L4QHhh1yKfs3oLP83ckCniy4qPY0IqOoJUx3xTCfYzFLGBwFaCR 8Gvaeea8WChMKVYz2I26SNlRVnFsWtJh0y7+h35Bh7NHh6lrOYak3Zmab OZX1/hrRVKrtb9tkAEVzGUkWqy/CtPFDLRKwwRYTuSVjsMUfeOMxRG1Z3 KnTSarQWWqrlWi4BD2212nw8tBOaOFOMoESjxHHJor5Z0LsecpkWDWQSM A==; X-CSE-ConnectionGUID: VoNbXnOFQIiTwfPMdimSQQ== X-CSE-MsgGUID: iFQ5Azm8QC+MpqjCEOvXOw== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815623" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815623" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:24 -0800 X-CSE-ConnectionGUID: TRWGioeHT7257PaafcEfDA== X-CSE-MsgGUID: sxO6YOnFTVqTlepySetzDw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94454972" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:21 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 03/13] rust/cell: add get_mut() method for BqlCell Date: Thu, 5 Dec 2024 14:07:04 +0800 Message-Id: <20241205060714.256270-4-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org The get_mut() is useful when doing compound assignment operations, e.g., *c.get_mut() += 1. Implement get_mut() for BqlCell by referring to Cell. Signed-off-by: Zhao Liu --- rust/qemu-api/src/cell.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 07b636f26266..95f1cc0b3eb5 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -324,6 +324,31 @@ impl BqlCell { pub const fn as_ptr(&self) -> *mut T { self.value.get() } + + /// Returns a mutable reference to the underlying data. + /// + /// This call borrows `BqlCell` mutably (at compile-time) which guarantees + /// that we possess the only reference. + /// + /// However be cautious: this method expects `self` to be mutable, which is + /// generally not the case when using a `BqlCell`. If you require interior + /// mutability by reference, consider using `BqlRefCell` which provides + /// run-time checked mutable borrows through its [`borrow_mut`] method. + /// + /// [`borrow_mut`]: BqlRefCell::borrow_mut() + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell;; + /// + /// let mut c = BqlCell::new(5); + /// *c.get_mut() += 1; + /// + /// assert_eq!(c.get(), 6); + pub fn get_mut(&mut self) -> &mut T { + self.value.get_mut() + } } impl BqlCell { From patchwork Thu Dec 5 06:07:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894724 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 97061E77172 for ; Thu, 5 Dec 2024 05:49:58 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4jz-0006Cf-Jb; Thu, 05 Dec 2024 00:49:31 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jy-0006C8-DF; Thu, 05 Dec 2024 00:49:30 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jw-00079A-OJ; Thu, 05 Dec 2024 00:49:30 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377769; x=1764913769; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=voF79vowTR6fMRnGLbc8fHjowgWUVNqNwTCb10ZYV1o=; b=bio/VIQZu/EGqyXiJIGlmcAJVq6gJ6jy7vKvZh1ImyKfHyBH0IU8BMZf g9S3hdWTvuVMEdoNrOq40DFVsnbF80Dcr2Yy8YOJu86+FY2VJYyeH/WqF vKLJJnCSnw4DvwnFwXLIt6p28jATIQIdTI98v6mzO0TaEqztzx8CyZzc7 q5DyGKKSiz15txFsWXKzaM258oVNt21ymfDXG5r6yL1ymjEmXuicY8APn 2b5VNNEnP7OkhZGpw2uGRqrmUkP7piMXMKY7QI7gjxQCj6A5lSptA2/Sx q20aWHZDtauyfl5OOqzBlX1357M3+TUhfJpQElNRFIq0ObkXS8dTjsBCV Q==; X-CSE-ConnectionGUID: nz2aGToOTGOFIo7URgGWAQ== X-CSE-MsgGUID: 6DvI+7iBSVuBAt4HiRs4gg== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815634" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815634" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:27 -0800 X-CSE-ConnectionGUID: 1fB97pmNSHW/arkgG+nckQ== X-CSE-MsgGUID: K3zGE1hdTcGll9X0qPA74A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94454985" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:24 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 04/13] rust: add bindings for gpio_{in|out} initialization Date: Thu, 5 Dec 2024 14:07:05 +0800 Message-Id: <20241205060714.256270-5-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org The qdev_init_gpio_{in|out} are qdev interfaces, so that it's natural to wrap them as DeviceState's methods in Rust API, which could eliminate unsafe cases in the device lib. Wrap qdev_init_gpio_{in|out} as methods in a new trait DeviceGPIOImpl. In addition, for qdev_init_gpio_in(), to convert the idiomatic Rust callback into a C-style callback qemu_irq_handler, add a handler pointer member in DeviceGPIOImpl. For any device needs to initialize GPIO in, it needs to define a handler. And for device which just wants to initialize GPIO out, it can leave the GPIO_IRQ_HANDLER as None. Then device could use init_gpio_in() and init_gpio_out() to initialize GPIO in and out, like C code. Note, for qemu_irq_handler, assume the opaque parameter refers to the self DeviceState, and this is enough as for now, as it's the most common case in QEMU. Signed-off-by: Zhao Liu --- rust/qemu-api/src/qdev.rs | 55 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 23a06b377b2c..5e6580b6f261 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -4,12 +4,17 @@ //! Bindings to create devices and access device functionality from Rust. -use std::ffi::CStr; +use std::{ + ffi::CStr, + os::raw::{c_int, c_void}, + ptr::{addr_of, NonNull}, +}; pub use bindings::{DeviceClass, DeviceState, Property}; use crate::{ - bindings::{self, Error}, + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error}, + irq::InterruptSource, qom::{ClassInitImpl, Object, ObjectClass, ObjectType}, qom_isa, vmstate::VMStateDescription, @@ -144,3 +149,49 @@ unsafe impl ObjectType for DeviceState { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } qom_isa!(DeviceState: Object); + +/// # 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. +/// +/// Note: Always assume opaque is referred to the self DeviceState, and +/// this is also the most common case in QEMU. +unsafe extern "C" fn rust_irq_handler( + opaque: *mut c_void, + lines_num: c_int, + level: c_int, +) { + // SAFETY: + // the pointer is convertible to a reference + let state = unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + + T::GPIO_IRQ_HANDLER.unwrap()(state, lines_num as u32, level as u32); +} + +/// Trait that defines the irq handler for GPIO in. +pub trait DeviceGPIOImpl { + const GPIO_IRQ_HANDLER: Option = None; + + fn init_gpio_in(&self, lines_num: u32) + where + Self: Sized, + { + assert!(Self::GPIO_IRQ_HANDLER.is_some()); + + unsafe { + qdev_init_gpio_in( + addr_of!(*self) as *mut _, + Some(rust_irq_handler::), + lines_num as c_int, + ); + } + } + + fn init_gpio_out(&self, pins: &InterruptSource, lines_num: u32) { + unsafe { + qdev_init_gpio_out(addr_of!(*self) as *mut _, pins.as_ptr(), lines_num as c_int); + } + } +} From patchwork Thu Dec 5 06:07:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894732 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 8ECE7E7716C for ; Thu, 5 Dec 2024 05:51:41 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4k3-0006Et-6f; Thu, 05 Dec 2024 00:49:35 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k0-0006DQ-Qx; Thu, 05 Dec 2024 00:49:33 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4jz-00079o-4S; Thu, 05 Dec 2024 00:49:32 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377771; x=1764913771; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=VCKCuXFNdAEeiFHD7bZV0Fz0m6rEj3nJ8quOxmRRxzg=; b=JwBbuuzhPvnD7lqDEGm41o3OhvOpO7cFp/RDnNyQ+HsNkynXa0z2OqwG X8F6w4y0f2N+JtrixhcqrEnB7CjfFe4mAbkeqy6vXgQn/M2neq6bASm5R 4ls6a62kbVxOCBxzvBl+n1LO4r6QXsknZ/gtYG4q0uSXPC+PMtwna2Y4u jNcdknLkcu+B94pX2FjKSXs6VJtpSRSKdBCLxOI10IAb7btvgOCXcJs4B s1/HEFpyUEj4tZ2beIaIDBWLIdHBhbUcE09EPDTWwGBYepVcCA6jTl+zb GN0RgS8VOyWJIMFBvjLrVolrcAyRwyzxa08UP+YwzMJmhtj8/H1rXI3Rv A==; X-CSE-ConnectionGUID: EEEcpT4VRYuFMyKZi1fljg== X-CSE-MsgGUID: /kG7pm4GSFWHsM0ZED7JpQ== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815638" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815638" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:29 -0800 X-CSE-ConnectionGUID: TYQryDhBQkqY1/OhFauqSA== X-CSE-MsgGUID: PTQz4QFMSeak1YBlHB0Nsw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94454991" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:27 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 05/13] rust: add a bit operation binding for deposit64 Date: Thu, 5 Dec 2024 14:07:06 +0800 Message-Id: <20241205060714.256270-6-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org The bindgen supports `static inline` function binding since v0.64.0 as an experimental feature (`--wrap-static-fns`), and stabilizes it after v0.70.0. But the oldest version of bindgen supported by QEMU is v0.60.1, so there's no way to generate the binding for deposit64() which is `static inline` (in include/qemu/bitops.h). Manually implement a binding. Since it only involves bit operations, fortunately, the Rust version of deposit64() is almost identical to the original C version. Signed-off-by: Zhao Liu --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/bitops.rs | 11 +++++++++++ rust/qemu-api/src/lib.rs | 1 + 3 files changed, 13 insertions(+) create mode 100644 rust/qemu-api/src/bitops.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 3ac69cbc76c4..00e86a679d8a 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -16,6 +16,7 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/bindings.rs', + 'src/bitops.rs', 'src/cell.rs', 'src/c_str.rs', 'src/irq.rs', diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs new file mode 100644 index 000000000000..a11a07fb8830 --- /dev/null +++ b/rust/qemu-api/src/bitops.rs @@ -0,0 +1,11 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +pub fn deposit64(value: u64, start: usize, length: usize, fieldval: u64) -> u64 { + /* FIXME: Implement a more elegant check with error handling support? */ + assert!(length > 0 && length <= 64 - start); + + let mask = (u64::MAX >> (64 - length)) << start; + (value & !mask) | ((fieldval << start) & mask) +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 0d46b372c6bb..009906c907e7 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -7,6 +7,7 @@ #[rustfmt::skip] pub mod bindings; +pub mod bitops; pub mod c_str; pub mod cell; pub mod irq; From patchwork Thu Dec 5 06:07:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894733 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3F0EDE7716C for ; Thu, 5 Dec 2024 05:51:45 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4k4-0006FM-P8; Thu, 05 Dec 2024 00:49:36 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k3-0006Ev-Nx; Thu, 05 Dec 2024 00:49:35 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k1-00079o-HE; Thu, 05 Dec 2024 00:49:35 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377774; x=1764913774; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=+UM44gN8vs+EoSbyXpVOo3DcigJbEd34STQ9qH+3m8A=; b=TvHd3OTY/ZvSO9ZohiAnhY9lelyaPKe7E404BYj7cIQTv21Jjg4ooQzG AK3zN4f4XTzbtnkpK5NMppuAW/kSglx6iOoXNSfe/Gv7D92OrnXiSpwRc xSqHfNPTTJES+V5VJRxktA7MGZI6j7J5hhzVHtkP5TztINSH+oU4SFB4J 6Qp4JMTam7TO40K+Z+OG8H40lbVmHGSR3kdiMMd5rK8msYGf7KwwM2UdN lxJyM1cci5yuOiaN7XYagfzFCbJ7wkXcVEH5vg+bIPudmLzJz8SIg9t7q 6SKa3kZFYPQ2AAdLhWOOWqdY5ia3PBUSwejDcN6B8cgjrnrg7dDx/kA2j A==; X-CSE-ConnectionGUID: GLHQQMJMSlOiqPAfFojnKg== X-CSE-MsgGUID: jIVc0b7MRluCfR2+T62X6g== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815646" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815646" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:32 -0800 X-CSE-ConnectionGUID: RvzMCpbrQcCozAZUsKbHdw== X-CSE-MsgGUID: yl2iBmPNTs2lapUgSlNJ3w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455001" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:29 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 06/13] rust: add bindings for memattrs Date: Thu, 5 Dec 2024 14:07:07 +0800 Message-Id: <20241205060714.256270-7-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org The MemTxAttrs structure is composed of bitfield members, and bindgen is unable to generate an equivalent macro definition for MEMTXATTRS_UNSPECIFIED. Therefore, we have to manually define a global constant variable MEMTXATTRS_UNSPECIFIED to support calls from Rust code. However, the binding methods of MemTxAttrs are non-const, so we cannot directly use them when defining MEMTXATTRS_UNSPECIFIED. As a result, add the third-party crate once_cell to use its Lazy to help define MEMTXATTRS_UNSPECIFIED. Note, lazy_static has been deprecated and LazyCell (in std) became stable since v1.80. When the minimum supported rustc version is bumped to v1.80 in the future, LazyCell can be used to replace the current once_cell. Signed-off-by: Zhao Liu --- rust/Cargo.lock | 7 ++++++ rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 9 ++++++-- rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/memattrs.rs | 21 +++++++++++++++++ rust/wrapper.h | 1 + scripts/archive-source.sh | 2 +- scripts/make-release | 2 +- subprojects/.gitignore | 1 + subprojects/once_cell-1.20-rs.wrap | 7 ++++++ .../once_cell-1.20-rs/meson.build | 23 +++++++++++++++++++ 11 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 rust/qemu-api/src/memattrs.rs create mode 100644 subprojects/once_cell-1.20-rs.wrap create mode 100644 subprojects/packagefiles/once_cell-1.20-rs/meson.build diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c0c6069247a8..6b19553b6d10 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -46,6 +46,12 @@ dependencies = [ "either", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "pl011" version = "0.1.0" @@ -92,6 +98,7 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "once_cell", "qemu_api_macros", "version_check", ] diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 4aa22f319860..265e00f97176 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -14,6 +14,7 @@ keywords = [] categories = [] [dependencies] +once_cell = { version = "1.20.2" } qemu_api_macros = { path = "../qemu-api-macros" } [build-dependencies] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 00e86a679d8a..508986948883 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -10,6 +10,9 @@ if get_option('debug_mutex') _qemu_api_cfg += ['--feature', 'debug_cell'] endif +subproject('once_cell-1.20-rs', required: true) +once_cell_dep = dependency('once_cell-1.20-rs') + _qemu_api_rs = static_library( 'qemu_api', structured_sources( @@ -20,6 +23,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/c_str.rs', 'src/irq.rs', + 'src/memattrs.rs', 'src/module.rs', 'src/offset_of.rs', 'src/qdev.rs', @@ -33,6 +37,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, + dependencies: once_cell_dep, ) rust.test('rust-qemu-api-tests', _qemu_api_rs, @@ -40,7 +45,7 @@ rust.test('rust-qemu-api-tests', _qemu_api_rs, qemu_api = declare_dependency( link_with: _qemu_api_rs, - dependencies: qemu_api_macros, + dependencies: [qemu_api_macros, once_cell_dep], ) # Rust executables do not support objects, so add an intermediate step. @@ -56,7 +61,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [qemu_api, qemu_api_macros], + dependencies: [qemu_api, qemu_api_macros, once_cell_dep], link_whole: [rust_qemu_api_objs, libqemuutil]), args: [ '--test', diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 009906c907e7..e60c9ac16409 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -11,6 +11,7 @@ pub mod c_str; pub mod cell; pub mod irq; +pub mod memattrs; pub mod module; pub mod offset_of; pub mod qdev; diff --git a/rust/qemu-api/src/memattrs.rs b/rust/qemu-api/src/memattrs.rs new file mode 100644 index 000000000000..7cc8aea4b7b7 --- /dev/null +++ b/rust/qemu-api/src/memattrs.rs @@ -0,0 +1,21 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use once_cell::sync::Lazy; + +use crate::bindings::MemTxAttrs; + +impl MemTxAttrs { + fn memtxattrs_unspecified() -> Self { + let mut attrs = MemTxAttrs::default(); + attrs.set_unspecified(1); + attrs + } +} + +/// Bus masters which don't specify any attributes will get this, +/// which has all attribute bits clear except the topmost one +/// (so that we can distinguish "all attributes deliberately clear" +/// from "didn't specify" if necessary). +pub static MEMTXATTRS_UNSPECIFIED: Lazy = Lazy::new(MemTxAttrs::memtxattrs_unspecified); diff --git a/rust/wrapper.h b/rust/wrapper.h index 285d0eb6ad01..033f3e9cf32c 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -62,3 +62,4 @@ typedef enum memory_order { #include "qapi/error.h" #include "migration/vmstate.h" #include "chardev/char-serial.h" +#include "exec/memattrs.h" diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 30677c3ec903..59e72d92498a 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -30,7 +30,7 @@ subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs - syn-2-rs unicode-ident-1-rs" + syn-2-rs unicode-ident-1-rs once_cell-1.20-rs" sub_deinit="" function cleanup() { diff --git a/scripts/make-release b/scripts/make-release index 8dc939124c4f..b6b48bdf6f08 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -21,7 +21,7 @@ SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs - syn-2-rs unicode-ident-1-rs" + syn-2-rs unicode-ident-1-rs once_cell-1.20-rs" src="$1" version="$2" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 50f173f90dbe..dba8ea74b823 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -11,6 +11,7 @@ /bilge-impl-0.2.0 /either-1.12.0 /itertools-0.11.0 +/once_cell-1.20.2 /proc-macro-error-1.0.4 /proc-macro-error-attr-1.0.4 /proc-macro2-1.0.84 diff --git a/subprojects/once_cell-1.20-rs.wrap b/subprojects/once_cell-1.20-rs.wrap new file mode 100644 index 000000000000..b13ba81a22b1 --- /dev/null +++ b/subprojects/once_cell-1.20-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = once_cell-1.20.2 +source_url = https://crates.io/api/v1/crates/once_cell/1.20.2/download +source_filename = once_cell-1.20.2.tar.gz +source_hash = 1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775 +#method = cargo +patch_directory = once_cell-1.20-rs diff --git a/subprojects/packagefiles/once_cell-1.20-rs/meson.build b/subprojects/packagefiles/once_cell-1.20-rs/meson.build new file mode 100644 index 000000000000..0b81f8e76250 --- /dev/null +++ b/subprojects/packagefiles/once_cell-1.20-rs/meson.build @@ -0,0 +1,23 @@ +project('once_cell-1.20-rs', 'rust', + version: '1.20.2', + license: 'MIT OR Apache-2.0', + default_options: []) + +_once_cell_rs = static_library( + 'once_cell', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'feature="std"' + ], + dependencies: [], + native: true, +) + +once_cell_dep = declare_dependency( + link_with: _once_cell_rs, +) + +meson.override_dependency('once_cell-1.20-rs', once_cell_dep, native: true) From patchwork Thu Dec 5 06:07:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894725 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 16D3AE7716D for ; Thu, 5 Dec 2024 05:50:02 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4k8-0006Gl-Bo; Thu, 05 Dec 2024 00:49:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k5-0006Fz-Pd; Thu, 05 Dec 2024 00:49:37 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k3-0007AB-Sl; Thu, 05 Dec 2024 00:49:37 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377776; x=1764913776; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=HOiFDozy8NrNAjDI+/O7a547R/8wQv3FfO4ASH2GLSE=; b=JithTF0rdaGoTb2SZBU+gk0DrhA219wupSQzQLqB2ogmqaZNeAC73SZx xarWjtY6122Z5ScSuvu4WibeQ+Wyz8uOBksypXYiI1yZ1PuVT3Pip88Ga 8NEDKkUYV8Iu3zqPNJnKLayyxdw4/MaU5UQ9LXSRgmOvIGxVNOJOzIBAu gSOFXOasaBrh+FLVbgSG+1H0nffvxzj1IqkudW2dXABH5Q6ouvX/sS5s/ fHrVLMz2vgqMVq5jSWRvW/+6ln+7+Jyf0ZLnZCxj/7Ut7zgebKmgJ12GI DU6l0JGxaOFMgAx+J/KC6tNebryeqtdvJqQcSsP3zrK2jODvh90eXQzi3 g==; X-CSE-ConnectionGUID: 3T8qcSEIRj+mjNsPHF/F2A== X-CSE-MsgGUID: 4pZ5nhRbSXe16HCUVcN8qA== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815652" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815652" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:35 -0800 X-CSE-ConnectionGUID: ku2QwvUMQhS8uiHTQDpqVQ== X-CSE-MsgGUID: DGQj/cYjQkG1BtHFNzXpqQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455026" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:32 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 07/13] rust: add bindings for timer Date: Thu, 5 Dec 2024 14:07:08 +0800 Message-Id: <20241205060714.256270-8-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org The bindgen supports `static inline` function binding since v0.64.0 as an experimental feature (`--wrap-static-fns`), and stabilizes it after v0.70.0. But the oldest version of bindgen supported by QEMU is v0.60.1, so there's no way to generate the bindings for timer_new() and its variants which are `static inline` (in include/qemu/timer.h). Manually implement bindings to help create new timers in Rust. Additionally, wrap timer_mod(), timer_del() and qemu_clock_get_virtual_ns() as safe functions to make timer interfaces more Rust-idiomatic. In addition, for timer_new() and its variants, to convert the idiomatic Rust callback into a C-style callback QEMUTimerCB, introduce a trait QEMUTimerImpl. For any object needs to initialize a new timer, it needs to implement QEMUTimerImpl trait and define a handler. Signed-off-by: Zhao Liu --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/timer.rs | 123 +++++++++++++++++++++++++++++++++++++ rust/wrapper.h | 1 + 4 files changed, 126 insertions(+) create mode 100644 rust/qemu-api/src/timer.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 508986948883..5bf3c3dfab67 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -29,6 +29,7 @@ _qemu_api_rs = static_library( 'src/qdev.rs', 'src/qom.rs', 'src/sysbus.rs', + 'src/timer.rs', 'src/vmstate.rs', 'src/zeroable.rs', ], diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index e60c9ac16409..495261976dbc 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -17,6 +17,7 @@ pub mod qdev; pub mod qom; pub mod sysbus; +pub mod timer; pub mod vmstate; pub mod zeroable; diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs new file mode 100644 index 000000000000..4f9e8c9277c6 --- /dev/null +++ b/rust/qemu-api/src/timer.rs @@ -0,0 +1,123 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + borrow::BorrowMut, + boxed::Box, + os::raw::{c_int, c_void}, + ptr::NonNull, +}; + +pub use bindings::QEMUTimer; + +use crate::bindings::{self, *}; + +impl QEMUTimer { + fn new() -> Self { + QEMUTimer { + expire_time: 0, + timer_list: ::core::ptr::null_mut(), + cb: None, + opaque: ::core::ptr::null_mut(), + next: ::core::ptr::null_mut(), + attributes: 0, + scale: 0, + } + } + + // TODO: Consider how to avoid passing in C style callbacks directly. + fn timer_new_full( + timer_list_group: Option<&mut QEMUTimerListGroup>, + clk_type: QEMUClockType, + scale: u32, + attributes: u32, + opaque: &mut T::Opaque, + ) -> Self { + let mut ts: Box = Box::new(QEMUTimer::new()); + let group_ptr = if let Some(g) = timer_list_group { + g + } else { + ::core::ptr::null_mut() + }; + + // Safety: + // ts is a valid Box object which can borrow a valid mutable + // pointer, and opaque is converted from a reference so it's + // also valid. + unsafe { + timer_init_full( + ts.borrow_mut(), + group_ptr, + clk_type, + scale as c_int, + attributes as c_int, + Some(rust_timer_handler::), + (opaque as *mut T::Opaque).cast::(), + ) + }; + + *ts + } + + pub fn timer_mod(&mut self, expire_time: u64) { + unsafe { timer_mod(self as *mut QEMUTimer, expire_time as i64) } + } + + pub fn timer_del(&mut self) { + unsafe { timer_del(self as *mut QEMUTimer) }; + } +} + +/// timer expiration callback +unsafe extern "C" fn rust_timer_handler(opaque: *mut c_void) { + // SAFETY: + // the pointer is convertible to a reference + let para = unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + + T::QEMU_TIMER_CB.unwrap()(para); +} + +pub trait QEMUTimerImpl { + type Opaque; + + // To be more general, opaque is mutable here. But it still should + // be protected by BqlCell/BqlRefCell. + // + // FIXME: limit opaque to immutable? + const QEMU_TIMER_CB: Option = None; + + fn timer_new(clk_type: QEMUClockType, scale: u32, opaque: &mut Self::Opaque) -> QEMUTimer + where + Self: Sized, + { + QEMUTimer::timer_new_full::(None, clk_type, scale, 0, opaque) + } + + fn timer_new_ns(clk_type: QEMUClockType, opaque: &mut Self::Opaque) -> QEMUTimer + where + Self: Sized, + { + Self::timer_new(clk_type, SCALE_NS, opaque) + } + + fn timer_new_us(clk_type: QEMUClockType, opaque: &mut Self::Opaque) -> QEMUTimer + where + Self: Sized, + { + Self::timer_new(clk_type, SCALE_US, opaque) + } + + fn timer_new_ms(clk_type: QEMUClockType, opaque: &mut Self::Opaque) -> QEMUTimer + where + Self: Sized, + { + Self::timer_new(clk_type, SCALE_MS, opaque) + } +} + +pub fn qemu_clock_get_virtual_ns() -> u64 { + // SAFETY: + // Valid parameter. + (unsafe { qemu_clock_get_ns(QEMUClockType::QEMU_CLOCK_VIRTUAL) }) as u64 +} diff --git a/rust/wrapper.h b/rust/wrapper.h index 033f3e9cf32c..0da42e84933a 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -63,3 +63,4 @@ typedef enum memory_order { #include "migration/vmstate.h" #include "chardev/char-serial.h" #include "exec/memattrs.h" +#include "qemu/timer.h" From patchwork Thu Dec 5 06:07:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894734 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5C25FE7716C for ; Thu, 5 Dec 2024 05:52:11 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4kA-0006HP-BX; Thu, 05 Dec 2024 00:49:42 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k8-0006Gq-Rx; Thu, 05 Dec 2024 00:49:40 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k7-0007B2-8i; Thu, 05 Dec 2024 00:49:40 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377779; x=1764913779; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=IJWVRmzDJbwLIewLn+CtruGga6G97lX0CrTx7B/D3+Y=; b=JFoXl8P+62lRYZ7A7KSO6teEYKWCJ6135aHN4O6oaBWm78vtY+QIq5eP 34s3G129f5o36QjYMhRQ0YxfVAgZMe6chj6E/O492MScQ7i76nvaewH+l OMnEZZAchCbDQMoGOkvBqFFcRqvyT3ZisTsxQoeYrmpKAqiJI61Z88Muy JwB9l9U6o7wKBAfRZnSb6SbllG6jj5TEAATxlt5GH04gJw9Kf4GUAd0Os Okxq8YEhcPXH45TxKx7CGtkBkCygT2LUcd+PY8qEY4q7ouLi5qy/14Xuw fiPXQant3Mj6ch2v+qWCDwW7CLb5saBWAAH6F8XVHOPZqGSQSORpfoWww Q==; X-CSE-ConnectionGUID: E16kPp3iTbWRjADTenzmhA== X-CSE-MsgGUID: ZIQ9ZfDCQcGggArrJ8p1Lg== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815661" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815661" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:38 -0800 X-CSE-ConnectionGUID: fjnL5WZoSYmotje8jHaYog== X-CSE-MsgGUID: tlA4ynduSDCkWUaLwRA7BA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455043" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:35 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 08/13] rust/qdev: add the macro to define bit property Date: Thu, 5 Dec 2024 14:07:09 +0800 Message-Id: <20241205060714.256270-9-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org HPET device (Rust device) needs to define the bit type property. Add a variant of define_property macro to define bit type property. Signed-off-by: Zhao Liu --- rust/qemu-api/src/qdev.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 5e6580b6f261..66810803ec9d 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -103,6 +103,18 @@ fn class_init(dc: &mut DeviceClass) { #[macro_export] macro_rules! define_property { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, bit = $bitnr:expr, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: $crate::offset_of!($state, $field) as isize, + bitnr: $bitnr, + set_default: true, + defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, + ..$crate::zeroable::Zeroable::ZERO + } + }; ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => { $crate::bindings::Property { // use associated function syntax for type checking From patchwork Thu Dec 5 06:07:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894721 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2CE38E7716D for ; Thu, 5 Dec 2024 05:49:58 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4kC-0006Ht-CP; Thu, 05 Dec 2024 00:49:44 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kA-0006HU-VT; Thu, 05 Dec 2024 00:49:42 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4k9-0007B4-4K; Thu, 05 Dec 2024 00:49:42 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377781; x=1764913781; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=MqPtIjiKuUsW3EQ/VPF+u6Z8ovdVcse78lSqSTpfpsA=; b=lpdQeVwRTT9s17r2ZVgoEfzovix1mtTrlXXF5rdwpAM/bG9EJUH3U417 GiExcQZIOTmTwgxnzH8NDXsRAZirszzAfN8RxWEbP4ilmam+kLkzMASy0 ml5KvYmHX9NVv0gypD83DcKsTU7Oq8M5DX3k0TS48gabtUgiy/F3XGFlz RZ54qTQiYn8zNdvOyIAAfBXxeDJsPLVu5QC2itH41RP6oRSwCBHbzOLnG IYb1SbBQuIJq+FlQLcMD+ih/QGgF9XjGHlwcDUaPHv887lKV2cjXr6lr4 EDI4y1UHiEEad7SJjKSGFVCutuxzdViWj7dmVNskXJym8bQaBe7KOgXkt Q==; X-CSE-ConnectionGUID: 5jtPkJzwTSK71luZiyaWSg== X-CSE-MsgGUID: MOWHeT8AR1y2h9EzC5r1HA== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815669" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815669" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:40 -0800 X-CSE-ConnectionGUID: a5XZ/26IR3yt+nQKjD4Zng== X-CSE-MsgGUID: Bp3GlixeSHuiq6x1DnhnHw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455046" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:37 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 09/13] i386/fw_cfg: move hpet_cfg definition to hpet.c Date: Thu, 5 Dec 2024 14:07:10 +0800 Message-Id: <20241205060714.256270-10-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org HPET device needs to access and update hpet_cfg variable, but now it is defined in hw/i386/fw_cfg.c and Rust code can't access it. Move hpet_cfg definition to hpet.c (and rename it to hpet_fw_cfg). This allows Rust HPET device implements its own global hpet_fw_cfg variable, and will further reduce the use of unsafe C code access and calls in the Rust HPET implementation. Signed-off-by: Zhao Liu --- hw/i386/fw_cfg.c | 4 +--- hw/timer/hpet.c | 16 +++++++++------- include/hw/timer/hpet.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 0e4494627c21..965e6306838a 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -26,8 +26,6 @@ #include CONFIG_DEVICES #include "target/i386/cpu.h" -struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; - const char *fw_cfg_arch_key_name(uint16_t key) { static const struct { @@ -149,7 +147,7 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, #endif fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_fw_cfg, sizeof(hpet_fw_cfg)); /* allocate memory for the NUMA channel: one (64bit) word for the number * of nodes, one word for each VCPU->node and one word for each node to * hold the amount of memory. diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 5399f1b2a3f7..d8bd51b7e202 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -40,6 +40,8 @@ #include "qom/object.h" #include "trace.h" +struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; + #define HPET_MSI_SUPPORT 0 OBJECT_DECLARE_SIMPLE_TYPE(HPETState, HPET) @@ -278,7 +280,7 @@ static int hpet_post_load(void *opaque, int version_id) /* Push number of timers into capability returned via HPET_ID */ s->capability &= ~HPET_ID_NUM_TIM_MASK; s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ s->flags &= ~(1 << HPET_MSI_SUPPORT); @@ -665,8 +667,8 @@ static void hpet_reset(DeviceState *d) s->hpet_counter = 0ULL; s->hpet_offset = 0ULL; s->config = 0ULL; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; + hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + hpet_fw_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; /* to document that the RTC lowers its output on reset as well */ s->rtc_irq_level = 0; @@ -708,17 +710,17 @@ static void hpet_realize(DeviceState *dev, Error **errp) if (!s->intcap) { warn_report("Hpet's intcap not initialized"); } - if (hpet_cfg.count == UINT8_MAX) { + if (hpet_fw_cfg.count == UINT8_MAX) { /* first instance */ - hpet_cfg.count = 0; + hpet_fw_cfg.count = 0; } - if (hpet_cfg.count == 8) { + if (hpet_fw_cfg.count == 8) { error_setg(errp, "Only 8 instances of HPET is allowed"); return; } - s->hpet_id = hpet_cfg.count++; + s->hpet_id = hpet_fw_cfg.count++; for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) { sysbus_init_irq(sbd, &s->irqs[i]); diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h index d17a8d43199e..dbf709251a8f 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -74,7 +74,7 @@ struct hpet_fw_config struct hpet_fw_entry hpet[8]; } QEMU_PACKED; -extern struct hpet_fw_config hpet_cfg; +extern struct hpet_fw_config hpet_fw_cfg; #define TYPE_HPET "hpet" From patchwork Thu Dec 5 06:07:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894728 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6C355E7716C for ; Thu, 5 Dec 2024 05:50:55 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4kF-0006Iq-55; Thu, 05 Dec 2024 00:49:47 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kD-0006IR-Fl; Thu, 05 Dec 2024 00:49:45 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kB-0007B2-Jf; Thu, 05 Dec 2024 00:49:45 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377784; x=1764913784; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ku9jHA4GvDjfowVdl0cDhGNs9WRaPIDchgZut6j+nUI=; b=fK99LW7KsQTEmYtQ1Q6QdUrdKukOH6CPZekfKKdnjzSVXMAXf3Ot9BkN Naj98JfyeYo72YSI75c9MA7CFrI5fzXK70ICsDgcrsaUqDVwUDesodC0A hRcJ9dXOpCVxpBrhZfHbJ5RvWO0zZunbuGUuPGTtqbhjjoa3dKP1sVGji KZ58CYni4eT7OoBnUNxgYwQ7RcEHF94O9sa4knOeVFNd16uRbGYEmrUcV DsQEjBWY4c0CZzE6xtvf7ohLsbLO9JeCkEwXrucAtX1XfQlmU3HeA//gI cpReoWlliKhLgxReP9CtjKjF5ryDjBjekRigquQCOb7Mqz4UF7teL94/a Q==; X-CSE-ConnectionGUID: 1YFj52kGRZyn3f8zLe+VRw== X-CSE-MsgGUID: KXgHlihgR4CM+KxFavc89Q== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815674" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815674" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:43 -0800 X-CSE-ConnectionGUID: KsnE5wCOR3y45eadn9Z8Jg== X-CSE-MsgGUID: lb7XB3GZTzqmwA13mkfQHg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455050" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:40 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 10/13] rust/timer/hpet: define hpet_fw_cfg Date: Thu, 5 Dec 2024 14:07:11 +0800 Message-Id: <20241205060714.256270-11-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Define HPETFwEntry structure with the same memory layout as hpet_fw_entry in C. Further, define the global hpet_fw_cfg variable in Rust which is the same as the C version. This hpet_fw_cfg variable in Rust will replace the C version one and allows both Rust code and C code to access it. The Rust version of hpet_fw_cfg is self-contained, avoiding unsafe access to C code. Signed-off-by: Zhao Liu --- rust/Cargo.lock | 8 +++ rust/Cargo.toml | 1 + rust/hw/meson.build | 1 + rust/hw/timer/hpet/Cargo.toml | 14 +++++ rust/hw/timer/hpet/meson.build | 18 +++++++ rust/hw/timer/hpet/src/fw_cfg.rs | 88 ++++++++++++++++++++++++++++++++ rust/hw/timer/hpet/src/lib.rs | 15 ++++++ rust/hw/timer/meson.build | 1 + 8 files changed, 146 insertions(+) create mode 100644 rust/hw/timer/hpet/Cargo.toml create mode 100644 rust/hw/timer/hpet/meson.build create mode 100644 rust/hw/timer/hpet/src/fw_cfg.rs create mode 100644 rust/hw/timer/hpet/src/lib.rs create mode 100644 rust/hw/timer/meson.build diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 6b19553b6d10..996454af03cf 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -37,6 +37,14 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "hpet" +version = "0.1.0" +dependencies = [ + "qemu_api", + "qemu_api_macros", +] + [[package]] name = "itertools" version = "0.11.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index de0835bf5b5c..fc620bcaac00 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -4,6 +4,7 @@ members = [ "qemu-api-macros", "qemu-api", "hw/char/pl011", + "hw/timer/hpet", ] [workspace.lints.rust] diff --git a/rust/hw/meson.build b/rust/hw/meson.build index 860196645e71..9749d4adfc96 100644 --- a/rust/hw/meson.build +++ b/rust/hw/meson.build @@ -1 +1,2 @@ subdir('char') +subdir('timer') diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml new file mode 100644 index 000000000000..db2ef4642b4f --- /dev/null +++ b/rust/hw/timer/hpet/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "hpet" +version = "0.1.0" +edition = "2021" +authors = ["Zhao Liu "] +license = "GPL-2.0-or-later" +description = "IA-PC High Precision Event Timer emulation in Rust" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +qemu_api = { path = "../../../qemu-api" } +qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build new file mode 100644 index 000000000000..c2d7c0532ca4 --- /dev/null +++ b/rust/hw/timer/hpet/meson.build @@ -0,0 +1,18 @@ +_libhpet_rs = static_library( + 'hpet', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [ + qemu_api, + qemu_api_macros, + ], +) + +rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( + link_whole: [_libhpet_rs], + # Putting proc macro crates in `dependencies` is necessary for Meson to find + # them when compiling the root per-target static rust lib. + dependencies: [qemu_api_macros], + variables: {'crate': 'hpet'}, +)]) diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs new file mode 100644 index 000000000000..a057c2778be4 --- /dev/null +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -0,0 +1,88 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +#![allow(dead_code)] + +use qemu_api::{cell::BqlCell, zeroable::Zeroable}; + +// Each HPETState represents a Event Timer Block. The v1 spec supports +// up to 8 blocks. QEMU only uses 1 block (in PC machine). +const HPET_MAX_NUM_EVENT_TIMER_BLOCK: usize = 8; + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct HPETFwEntry { + pub event_timer_block_id: u32, + pub address: u64, + pub min_tick: u16, + pub page_prot: u8, +} + +unsafe impl Zeroable for HPETFwEntry { + const ZERO: Self = Self { + event_timer_block_id: 0, + address: 0, + min_tick: 0, + page_prot: 0, + }; +} + +#[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 { + const ZERO: Self = Self { + count: 0, + hpet: [Zeroable::ZERO; HPET_MAX_NUM_EVENT_TIMER_BLOCK], + }; +} + +// Expose to C code to configure firmware. +// BqlCell is picked since it has the same memory layout +// as HPETFwConfig (just like Cell/UnsafeCell/T). +pub struct HPETFwConfigCell(BqlCell); + +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut hpet_fw_cfg: HPETFwConfigCell = HPETFwConfigCell(BqlCell::new(HPETFwConfig { + count: u8::MAX, + ..Zeroable::ZERO +})); + +impl HPETFwConfigCell { + pub(crate) fn assign_hpet_id(&mut self) -> usize { + if self.0.get().count == u8::MAX { + // first instance + self.0.get_mut().count = 0; + } + + if self.0.get().count == 8 { + // TODO: Add error binding: error_setg() + panic!("Only 8 instances of HPET is allowed"); + } + + let id: usize = self.0.get().count.into(); + self.0.get_mut().count += 1; + id + } + + pub(crate) fn update_hpet_cfg( + &mut self, + hpet_id: usize, + event_timer_block_id: Option, + address: Option, + ) { + if let Some(e) = event_timer_block_id { + self.0.get_mut().hpet[hpet_id].event_timer_block_id = e; + } + + if let Some(a) = address { + self.0.get_mut().hpet[hpet_id].address = a; + } + } +} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs new file mode 100644 index 000000000000..04cb41372b94 --- /dev/null +++ b/rust/hw/timer/hpet/src/lib.rs @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later +// +// HPET QEMU Device Model +// +// This library implements a device model for the IA-PC HPET (High +// Precision Event Timers) device in QEMU. +// + +#![deny(unsafe_op_in_unsafe_fn)] + +extern crate qemu_api; + +pub mod fw_cfg; diff --git a/rust/hw/timer/meson.build b/rust/hw/timer/meson.build new file mode 100644 index 000000000000..22a84f15536b --- /dev/null +++ b/rust/hw/timer/meson.build @@ -0,0 +1 @@ +subdir('hpet') From patchwork Thu Dec 5 06:07:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894726 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 93442E7716C for ; Thu, 5 Dec 2024 05:50:24 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4kK-0006Jc-44; Thu, 05 Dec 2024 00:49:52 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kI-0006JL-Fn; Thu, 05 Dec 2024 00:49:50 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kF-0007Br-Pb; Thu, 05 Dec 2024 00:49:50 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377788; x=1764913788; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zl7v7OjX1AjzOt0ThZ3p6xLnRkfwi1U8Qu7vMDZYnMw=; b=Uyep76V1X1wphCEv+vv1/32g5steo2WbBlC7tWQNf++BT0Uz2SN4AmTn fRmn/QTHWWDmDMlgve72h6o1FH2wk4f05ux1UDOkZjZQo7/C7ZpuFpYdv bpRxILz3I5OH7Obnzmxa1pQTpzD6QoWjRxOLNaKsg6rCYA41/D/MHONzY Q9hDpXL6yk9ZtH3y1TgFmtDTMzy4Ebs0pGkj60wIGrJ0aj42AEXMXbmKR ojO2qCvTW3XOges5zG4ppKV76MbLw/kDbN5BHfb1R61ogK1fzrwEdGXiK 7hWs+seFsRpg2Bt9545R6fCprLMFl8TJ7ovKno2pBfaDxDAe4Ks9vRdkJ g==; X-CSE-ConnectionGUID: ZufM0SDES8SZgKJfwgFfTg== X-CSE-MsgGUID: 1s8ZXaWCRDyGJoBC9BBvBQ== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815679" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815679" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:46 -0800 X-CSE-ConnectionGUID: xnuBWuUCSSS277sACLIXwA== X-CSE-MsgGUID: rVFbJY3kSviYF0ydeBnq2A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455067" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:43 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 11/13] rust/timer/hpet: add basic HPET timer & state Date: Thu, 5 Dec 2024 14:07:12 +0800 Message-Id: <20241205060714.256270-12-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Add the HPETTimer and HPETState (HPET timer block), along with their basic methods and register definitions. This is in preparation for supporting the QAPI interfaces. Note, wrap all items in HPETState that may be changed in the callback called by C code into the BqlCell/BqlRefCell. Signed-off-by: Zhao Liu --- rust/hw/timer/hpet/src/hpet.rs | 638 +++++++++++++++++++++++++++++++++ rust/hw/timer/hpet/src/lib.rs | 1 + rust/wrapper.h | 1 + 3 files changed, 640 insertions(+) create mode 100644 rust/hw/timer/hpet/src/hpet.rs diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs new file mode 100644 index 000000000000..9550d8fe438a --- /dev/null +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -0,0 +1,638 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +#![allow(dead_code)] + +use core::ptr::{null_mut, NonNull}; +use std::os::raw::c_int; + +use qemu_api::{ + bindings::*, + bitops::deposit64, + cell::{BqlCell, BqlRefCell}, + irq::InterruptSource, + memattrs::MEMTXATTRS_UNSPECIFIED, + timer::{qemu_clock_get_virtual_ns, QEMUTimerImpl}, +}; + +// Register space for each timer block. (HPET_BASE isn't defined here.) +const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes + +pub(crate) const HPET_MIN_TIMERS: usize = 3; // Miniumum recommended hardware implementation. +const HPET_MAX_TIMERS: usize = 32; // Maximum timers in each timer block. + +// Flags that HPETState.flags supports. +pub(crate) const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0; + +const HPET_NUM_IRQ_ROUTES: usize = 32; +const HPET_LEGACY_PIT_INT: u32 = 0; // HPET_LEGACY_RTC_INT isn't defined here. +const RTC_ISA_IRQ: usize = 8; + +const HPET_CLK_PERIOD: u64 = 10; // 10 ns +const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns + +// General Capabilities and ID Register +const HPET_CAP_REG: u64 = 0x000; +// Revision ID (bits 0:7) +const HPET_CAP_REV_ID_VALUE: u64 = 0x1; // Revision 1 is implemented (refer to v1.0a spec). +const HPET_CAP_REV_ID_SHIFT: usize = 0; +// Number of Timers (bits 8:12) +const HPET_CAP_NUM_TIM_SHIFT: usize = 8; +// Counter Size (bit 13) +const HPET_CAP_COUNT_SIZE_CAP_SHIFT: usize = 13; +// LegacyReplacement Route Capable (bit 15) +const HPET_CAP_LEG_RT_CAP_SHIFT: usize = 15; +// Vendor ID (bits 16:31) +const HPET_CAP_VENDER_ID_VALUE: u64 = 0x8086; +const HPET_CAP_VENDER_ID_SHIFT: usize = 16; +// Main Counter Tick Period (bits 32:63) +const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32; + +// General Configuration Register +const HPET_CFG_REG: u64 = 0x010; +// Overall Enable (bit 0) +const HPET_CFG_ENABLE_SHIFT: usize = 0; +// LegacyReplacement Route (bit 1) +const HPET_CFG_LEG_RT_SHIFT: usize = 1; +// Other bits are reserved. +const HPET_CFG_WRITE_MASK: u64 = 0x003; + +// General Interrupt Status Register +const HPET_INT_STATUS_REG: u64 = 0x020; + +// Main Counter Value Register +const HPET_COUNTER_REG: u64 = 0x0f0; + +// Timer N Configuration and Capability Register (masked by 0x18) +const HPET_TN_CFG_REG: u64 = 0x000; +// bit 0, 7, and bits 16:31 are reserved. +// bit 4, 5, 15, and bits 32:64 are read-only. +const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e; +// Timer N Interrupt Type (bit 1) +const HPET_TN_CFG_INT_TYPE_SHIFT: usize = 1; +// Timer N Interrupt Enable (bit 2) +const HPET_TN_CFG_INT_ENABLE_SHIFT: usize = 2; +// Timer N Type (Periodic enabled or not, bit 3) +const HPET_TN_CFG_PERIODIC_SHIFT: usize = 3; +// Timer N Periodic Interrupt Capable (support Periodic or not, bit 4) +const HPET_TN_CFG_PERIODIC_CAP_SHIFT: usize = 4; +// Timer N Size (timer size is 64-bits or 32 bits, bit 5) +const HPET_TN_CFG_SIZE_CAP_SHIFT: usize = 5; +// Timer N Value Set (bit 6) +const HPET_TN_CFG_SETVAL_SHIFT: usize = 6; +// Timer N 32-bit Mode (bit 8) +const HPET_TN_CFG_32BIT_SHIFT: usize = 8; +// Timer N Interrupt Rout (bits 9:13) +const HPET_TN_CFG_INT_ROUTE_MASK: u64 = 0x3e00; +const HPET_TN_CFG_INT_ROUTE_SHIFT: usize = 9; +// Timer N FSB Interrupt Enable (bit 14) +const HPET_TN_CFG_FSB_ENABLE_SHIFT: usize = 14; +// Timer N FSB Interrupt Delivery (bit 15) +const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; +// Timer N Interrupt Routing Capability (bits 32:63) +const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; + +// Timer N Comparator Value Register (masked by 0x18) +const HPET_TN_CMP_REG: u64 = 0x008; + +// Timer N FSB Interrupt Route Register (masked by 0x18) +const HPET_TN_FSB_ROUTE_REG: u64 = 0x010; + +fn hpet_next_wrap(cur_tick: u64) -> u64 { + (cur_tick | 0xffffffff) + 1 +} + +fn hpet_time_after(a: u64, b: u64) -> bool { + ((b - a) as i64) < 0 +} + +fn ticks_to_ns(value: u64) -> u64 { + value * HPET_CLK_PERIOD +} + +fn ns_to_ticks(value: u64) -> u64 { + value / HPET_CLK_PERIOD +} + +// Avoid touching the bits that cannot be written. +fn hpet_fixup_reg(new: u64, old: u64, mask: u64) -> u64 { + (new & mask) | (old & !mask) +} + +fn activating_bit(old: u64, new: u64, shift: usize) -> bool { + let mask: u64 = 1 << shift; + (old & mask == 0) && (new & mask != 0) +} + +fn deactivating_bit(old: u64, new: u64, shift: usize) -> bool { + let mask: u64 = 1 << shift; + (old & mask != 0) && (new & mask == 0) +} + +/// HPET Timer Abstraction +#[repr(C)] +#[derive(Debug, Default, qemu_api_macros::offsets)] +pub struct HPETTimer { + /// timer N index within the timer block (HPETState) + index: usize, + qemu_timer: Box, + /// timer block abstraction containing this timer + state: Option>, + + /// Memory-mapped, software visible timer registers + + /// Timer N Configuration and Capability Register + config: u64, + /// Timer N Comparator Value Register + cmp: u64, + /// Timer N FSB Interrupt Route Register + fsb: u64, + + /// Hidden register state + + /// comparator (extended to counter width) + cmp64: u64, + /// Last value written to comparator + period: u64, + /// timer pop will indicate wrap for one-shot 32-bit + /// mode. Next pop will be actual timer expiration. + wrap_flag: u8, + /// last value armed, to avoid timer storms + last: u64, +} + +impl HPETTimer { + fn init(&mut self, index: usize, state_ptr: *mut HPETState) -> &mut Self { + *self = HPETTimer::default(); + self.index = index; + self.state = NonNull::new(state_ptr); + self + } + + fn init_timer_with_state(&mut self) { + let index = self.index; + + self.qemu_timer = Box::new(HPETState::timer_new_ns( + QEMUClockType::QEMU_CLOCK_VIRTUAL, + &mut self.get_state_mut().timer[index], + )); + } + + fn get_state_ref(&self) -> &HPETState { + // SAFETY: + // the pointer is convertible to a reference + unsafe { self.state.unwrap().as_ref() } + } + + fn get_state_mut(&mut self) -> &mut HPETState { + // SAFETY: + // the pointer is convertible to a reference + unsafe { self.state.unwrap().as_mut() } + } + + fn is_int_active(&self) -> bool { + self.get_state_ref().int_status.get() & (1 << self.index) != 0 + } + + fn is_fsb_route_enabled(&self) -> bool { + self.config & 1 << HPET_TN_CFG_FSB_ENABLE_SHIFT != 0 + } + + fn is_periodic(&self) -> bool { + self.config & 1 << HPET_TN_CFG_PERIODIC_SHIFT != 0 + } + + fn is_int_enabled(&self) -> bool { + self.config & 1 << HPET_TN_CFG_INT_ENABLE_SHIFT != 0 + } + + fn is_32bit_mod(&self) -> bool { + self.config & 1 << HPET_TN_CFG_32BIT_SHIFT != 0 + } + + fn is_valset_enabled(&self) -> bool { + self.config & 1 << HPET_TN_CFG_SETVAL_SHIFT != 0 + } + + fn clear_valset(&mut self) { + self.config &= !(1 << HPET_TN_CFG_SETVAL_SHIFT); + } + + /// True if timer interrupt is level triggered; otherwise, edge triggered. + fn is_int_level_triggered(&self) -> bool { + self.config & 1 << HPET_TN_CFG_INT_TYPE_SHIFT != 0 + } + + /// calculate next value of the general counter that matches the + /// target (either entirely, or the low 32-bit only depending on + /// the timer mode). + fn calculate_cmp64(&self, cur_tick: u64, target: u64) -> u64 { + if self.is_32bit_mod() { + let mut result: u64 = deposit64(cur_tick, 0, 32, target); + if result < cur_tick { + result += 0x100000000; + } + result + } else { + target + } + } + + fn update_int_status(&mut self, set: bool) -> &mut Self { + let mask: u64 = 1 << self.index; + + if set && self.is_int_level_triggered() { + // If Timer N Interrupt Enable bit is 0, "the timer will + // still operate and generate appropriate status bits, but + // will not cause an interrupt" + *self.get_state_mut().int_status.get_mut() |= mask; + } else { + *self.get_state_mut().int_status.get_mut() &= !mask; + } + self + } + + fn get_individual_route(&self) -> usize { + ((self.config & HPET_TN_CFG_INT_ROUTE_MASK) >> HPET_TN_CFG_INT_ROUTE_SHIFT) as usize + } + + fn get_int_route(&self) -> usize { + if self.index <= 1 && self.get_state_ref().is_legacy_mode() { + // If LegacyReplacement Route bit is set, HPET specification requires + // timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, + // timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. + // + // If the LegacyReplacement Route bit is set, the individual routing + // bits for timers 0 and 1 (APIC or FSB) will have no impact. + // + // FIXME: Consider I/O APIC case. + if self.index == 0 { + 0 + } else { + RTC_ISA_IRQ + } + } else { + // (If the LegacyReplacement Route bit is set) Timer 2-n will be + // routed as per the routing in the timer n config registers. + // ... + // If the LegacyReplacement Route bit is not set, the individual + // routing bits for each of the timers are used. + self.get_individual_route() + } + } + + fn set_irq(&mut self, set: bool) { + let route = self.get_int_route(); + + if set && self.is_int_enabled() && self.get_state_ref().is_hpet_enabled() { + if self.is_fsb_route_enabled() { + // SAFETY: + // the parameters are valid. + unsafe { + address_space_stl_le( + &mut address_space_memory, + self.fsb >> 32, // Timer N FSB int addr + self.fsb as u32, // Timer N FSB int value, truncate! + *MEMTXATTRS_UNSPECIFIED, + null_mut(), + ); + } + } else if self.is_int_level_triggered() { + self.get_state_ref().irqs[route].raise(); + } else { + self.get_state_ref().irqs[route].pulse(); + } + } else if !self.is_fsb_route_enabled() { + self.get_state_ref().irqs[route].lower(); + } + } + + fn update_irq(&mut self, set: bool) { + self.update_int_status(set).set_irq(set); + } + + fn arm_timer(&mut self, tick: u64) { + let mut ns = self.get_state_ref().get_ns(tick); + + // Clamp period to reasonable min value (1 us) + if self.is_periodic() && ns - self.last < 1000 { + ns = self.last + 1000; + } + + self.last = ns; + self.qemu_timer.as_mut().timer_mod(self.last); + } + + fn set_timer(&mut self) { + let cur_tick: u64 = self.get_state_ref().get_ticks(); + + self.wrap_flag = 0; + self.cmp64 = self.calculate_cmp64(cur_tick, self.cmp); + if self.is_32bit_mod() { + // HPET spec says in one-shot 32-bit mode, generate an interrupt when + // counter wraps in addition to an interrupt with comparator match. + if !self.is_periodic() && self.cmp64 > hpet_next_wrap(cur_tick) { + self.wrap_flag = 1; + self.arm_timer(hpet_next_wrap(cur_tick)); + return; + } + } + self.arm_timer(self.cmp64); + } + + fn del_timer(&mut self) { + self.qemu_timer.as_mut().timer_del(); + + if self.is_int_active() { + // For level-triggered interrupt, this leaves interrupt status + // register set but lowers irq. + self.update_irq(true); + } + } + + // Configuration and Capability Register + fn set_tn_cfg_reg(&mut self, shift: usize, len: usize, val: u64) { + // TODO: Add trace point - trace_hpet_ram_write_tn_cfg(addr & 4) + let old_val: u64 = self.config; + let mut new_val: u64 = deposit64(old_val, shift, len, val); + new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + + // Switch level-type interrupt to edge-type. + if deactivating_bit(old_val, new_val, HPET_TN_CFG_INT_TYPE_SHIFT) { + // Do this before changing timer.config; otherwise, if + // HPET_TN_FSB is set, update_irq will not lower the qemu_irq. + self.update_irq(false); + } + + self.config = new_val; + + if activating_bit(old_val, new_val, HPET_TN_CFG_INT_ENABLE_SHIFT) && self.is_int_active() { + self.update_irq(true); + } + + if self.is_32bit_mod() { + self.cmp = self.cmp as u32 as u64; // truncate! + self.period = self.period as u32 as u64; // truncate! + } + + if self.get_state_ref().is_hpet_enabled() { + self.set_timer(); + } + } + + /// Comparator Value Register + fn set_tn_cmp_reg(&mut self, shift: usize, len: usize, val: u64) { + let mut length = len; + let mut value = val; + + // TODO: Add trace point - trace_hpet_ram_write_tn_cmp(addr & 4) + if self.is_32bit_mod() { + // High 32-bits are zero, leave them untouched. + if shift != 0 { + // TODO: Add trace point - trace_hpet_ram_write_invalid_tn_cmp() + return; + } + length = 64; + value = value as u32 as u64; // truncate! + } + + if !self.is_periodic() || self.is_valset_enabled() { + self.cmp = deposit64(self.cmp, shift, length, value); + } + + if self.is_periodic() { + self.period = deposit64(self.period, shift, length, value); + } + + self.clear_valset(); + if self.get_state_ref().is_hpet_enabled() { + self.set_timer(); + } + } + + /// FSB Interrupt Route Register + fn set_tn_fsb_route_reg(&mut self, shift: usize, len: usize, val: u64) { + self.fsb = deposit64(self.fsb, shift, len, val); + } + + fn reset(&mut self) { + self.del_timer(); + self.cmp = u64::MAX; // Comparator Match Registers reset to all 1's. + self.config = 1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT | 1 << HPET_TN_CFG_SIZE_CAP_SHIFT; + if self.get_state_ref().has_msi_flag() { + self.config |= 1 << HPET_TN_CFG_FSB_CAP_SHIFT; + } + // advertise availability of ioapic int + self.config |= + (self.get_state_ref().int_route_cap as u64) << HPET_TN_CFG_INT_ROUTE_CAP_SHIFT; + self.period = 0; + self.wrap_flag = 0; + } + + /// timer expiration callback + fn callback(&mut self) { + let period: u64 = self.period; + let cur_tick: u64 = self.get_state_ref().get_ticks(); + + if self.is_periodic() && period != 0 { + while hpet_time_after(cur_tick, self.cmp64) { + self.cmp64 += period; + } + if self.is_32bit_mod() { + self.cmp = self.cmp64 as u32 as u64; + } else { + self.cmp = self.cmp64; + } + self.arm_timer(self.cmp64); + } else if self.wrap_flag != 0 { + self.wrap_flag = 0; + self.arm_timer(self.cmp64); + } + self.update_irq(true); + } +} + +#[derive(Debug)] +pub struct HPETTimerInstance(BqlRefCell); + +impl HPETTimerInstance { + fn timer_handler(timer: &mut HPETTimerInstance) { + timer.0.borrow_mut().callback() + } +} + +/// HPET Event Timer Block Abstraction +/// Note: Wrap all items that may be changed in the callback called by C +/// into the BqlCell/BqlRefCell. +#[repr(C)] +#[derive(Debug, qemu_api_macros::offsets)] +pub struct HPETState { + parent_obj: SysBusDevice, + iomem: MemoryRegion, + + /// Registers: Memory-mapped, software visible registers + + /// General Capabilities and ID Register + capability: BqlCell, + /// General Configuration Register + config: BqlCell, + /// General Interrupt Status Register + int_status: BqlCell, + /// Main Counter Value Register + counter: BqlCell, + + /// Internal state + + /// Capabilities that QEMU HPET supports. + /// bit 0: MSI (or FSB) support. + pub(crate) flags: BqlCell, + + /// Offset of main counter relative to qemu clock. + hpet_offset: BqlCell, + pub(crate) hpet_offset_saved: bool, + + irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES], + rtc_irq_level: BqlCell, + pit_enabled: InterruptSource, + + /// Interrupt Routing Capability. + /// This field indicates to which interrupts in the I/O (x) APIC + /// the timers' interrupt can be routed, and is encoded in the + /// bits 32:64 of timer N's config register: + pub(crate) int_route_cap: u32, + + /// HPET timer array managed by this timer block. + timer: [HPETTimerInstance; HPET_MAX_TIMERS], + pub(crate) num_timers: BqlCell, + + /// Instance id (HPET timer block ID). + hpet_id: BqlCell, +} + +impl HPETState { + fn has_msi_flag(&self) -> bool { + self.flags.get() & 1 << HPET_FLAG_MSI_SUPPORT_SHIFT != 0 + } + + fn is_legacy_mode(&self) -> bool { + self.config.get() & 1 << HPET_CFG_LEG_RT_SHIFT != 0 + } + + fn is_hpet_enabled(&self) -> bool { + self.config.get() & 1 << HPET_CFG_ENABLE_SHIFT != 0 + } + + fn get_ticks(&self) -> u64 { + ns_to_ticks(qemu_clock_get_virtual_ns() + self.hpet_offset.get()) + } + + fn get_ns(&self, tick: u64) -> u64 { + ticks_to_ns(tick) - self.hpet_offset.get() + } + + fn handle_legacy_irq(&mut self, irq: u32, level: u32) { + if irq == HPET_LEGACY_PIT_INT { + if !self.is_legacy_mode() { + self.irqs[0].set(level != 0); + } + } else { + self.rtc_irq_level.set(level as u8); + if !self.is_legacy_mode() { + self.irqs[RTC_ISA_IRQ].set(level != 0); + } + } + } + + fn get_timer(&self, timer_id: usize) -> &BqlRefCell { + &self.timer[timer_id].0 + } + + fn init_timer(&mut self) { + let raw_ptr: *mut HPETState = self; + + for i in 0..HPET_MAX_TIMERS { + let mut timer = self.get_timer(i).borrow_mut(); + timer.init(i, raw_ptr).init_timer_with_state(); + } + } + + /// General Configuration Register + fn set_cfg_reg(&mut self, shift: usize, len: usize, val: u64) { + let old_val = self.config.get(); + let mut new_val = deposit64(old_val, shift, len, val); + + new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + self.config.set(new_val); + + if activating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { + // Enable main counter and interrupt generation. + self.hpet_offset + .set(ticks_to_ns(self.counter.get()) - qemu_clock_get_virtual_ns()); + + for i in 0..self.num_timers.get() { + let mut timer = self.get_timer(i).borrow_mut(); + + if timer.is_int_enabled() && timer.is_int_active() { + timer.update_irq(true); + } + timer.set_timer(); + } + } else if deactivating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { + // Halt main counter and disable interrupt generation. + self.counter.set(self.get_ticks()); + + for i in 0..self.num_timers.get() { + self.get_timer(i).borrow_mut().del_timer(); + } + } + + // i8254 and RTC output pins are disabled when HPET is in legacy mode + if activating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { + self.pit_enabled.set(false); + self.irqs[0].lower(); + self.irqs[RTC_ISA_IRQ].lower(); + } else if deactivating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { + // TODO: Add irq binding: qemu_irq_lower(s->irqs[0]) + self.irqs[0].lower(); + self.pit_enabled.set(true); + self.irqs[RTC_ISA_IRQ].set(self.rtc_irq_level.get() != 0); + } + } + + /// General Interrupt Status Register: Read/Write Clear + fn set_int_status_reg(&mut self, shift: usize, _len: usize, val: u64) { + let new_val = val << shift; + let cleared = new_val & self.int_status.get(); + + for i in 0..self.num_timers.get() { + if cleared & (1 << i) != 0 { + self.get_timer(i).borrow_mut().update_irq(false); + } + } + } + + /// Main Counter Value Register + fn set_counter_reg(&mut self, shift: usize, len: usize, val: u64) { + if self.is_hpet_enabled() { + // TODO: Add trace point - + // trace_hpet_ram_write_counter_write_while_enabled() + // + // HPET spec says that writes to this register should only be + // done while the counter is halted. So this is an undefined + // behavior. There's no need to forbid it, but when HPET is + // enabled, the changed counter value will not affect the + // tick count (i.e., the previously calculated offset will + // not be changed as well). + } + self.counter + .set(deposit64(self.counter.get(), shift, len, val)); + } +} + +impl QEMUTimerImpl for HPETState { + type Opaque = HPETTimerInstance; + + const QEMU_TIMER_CB: Option = + Some(HPETTimerInstance::timer_handler); +} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index 04cb41372b94..387913bbdfb9 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -13,3 +13,4 @@ extern crate qemu_api; pub mod fw_cfg; +pub mod hpet; diff --git a/rust/wrapper.h b/rust/wrapper.h index 0da42e84933a..f44aba63d2ed 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -64,3 +64,4 @@ typedef enum memory_order { #include "chardev/char-serial.h" #include "exec/memattrs.h" #include "qemu/timer.h" +#include "exec/address-spaces.h" From patchwork Thu Dec 5 06:07:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894730 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id AFAB9E7716D for ; Thu, 5 Dec 2024 05:51:20 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4kM-0006Kn-NQ; Thu, 05 Dec 2024 00:49:54 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kK-0006K7-MJ; Thu, 05 Dec 2024 00:49:52 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kI-0007By-89; Thu, 05 Dec 2024 00:49:52 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377790; x=1764913790; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=LDckMvXhJNI3rW7CA66Qk7TmJwbv6cFDKjeds0sFlc4=; b=AWeLB4gYZZmR+cqopAHEAlnMK5YcKUDW7rbT9uWMekqn/FmK9+5PR4l2 PYsc5PjPmVnIHW08xXDgJvDhFNBBAwqGj/N32FlemgJ6HUg0fq4ziOuez F+t1T5Mtx+giFPlZj47OJ1eaMvXhgnHBIAVmVShNC3JfpL0rYKcOv1g2P fZbuiSmSXs9lNkxKbXFty6Gb04nsdlA61WL1SowCZQwvJdPPTjMvbi/bO qcVYb5+4r1TvyrdHlmrgV2E0zLgCK/jP+g7z5+774tbnRUop0XWsVszWd QwAftNq521aid+RGEz1EGj9qZoXjji/KYjVnsmubHqWcFsm3aG1XUwyAK w==; X-CSE-ConnectionGUID: 9BysKVV1T2OztSQRvfLMeQ== X-CSE-MsgGUID: m22BqopKSWSx/fuTphi7Gw== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815685" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815685" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:49 -0800 X-CSE-ConnectionGUID: FPGRTD/oSd6+Vs04Q2AR0w== X-CSE-MsgGUID: ulAAKIxcTHOHyGHhgZGiUQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455077" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:46 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 12/13] rust/timer/hpet: add qdev APIs support Date: Thu, 5 Dec 2024 14:07:13 +0800 Message-Id: <20241205060714.256270-13-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Implement QAPI support for HPET device in qdev.rs. Additionally, wrap the handling of HPET internal details as traits to be specifically implemented in hpet.rs. Signed-off-by: Zhao Liu --- rust/hw/timer/hpet/src/fw_cfg.rs | 2 - rust/hw/timer/hpet/src/hpet.rs | 232 ++++++++++++++++++++++++++++++- rust/hw/timer/hpet/src/lib.rs | 5 + rust/hw/timer/hpet/src/qdev.rs | 133 ++++++++++++++++++ 4 files changed, 365 insertions(+), 7 deletions(-) create mode 100644 rust/hw/timer/hpet/src/qdev.rs diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index a057c2778be4..6515a428cebb 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -2,8 +2,6 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -#![allow(dead_code)] - use qemu_api::{cell::BqlCell, zeroable::Zeroable}; // Each HPETState represents a Event Timer Block. The v1 spec supports diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 9550d8fe438a..9480633a77dd 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -2,10 +2,8 @@ // Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later -#![allow(dead_code)] - -use core::ptr::{null_mut, NonNull}; -use std::os::raw::c_int; +use core::ptr::{addr_of_mut, null_mut, NonNull}; +use std::os::raw::{c_uint, c_void}; use qemu_api::{ bindings::*, @@ -13,9 +11,14 @@ cell::{BqlCell, BqlRefCell}, irq::InterruptSource, memattrs::MEMTXATTRS_UNSPECIFIED, + qdev::DeviceGPIOImpl, + qom::ObjectType, timer::{qemu_clock_get_virtual_ns, QEMUTimerImpl}, + zeroable::Zeroable, }; +use crate::{fw_cfg::*, qdev::*}; + // Register space for each timer block. (HPET_BASE isn't defined here.) const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes @@ -453,6 +456,38 @@ fn callback(&mut self) { } } +impl RamOps for HPETTimer { + fn read(&mut self, addr: hwaddr, _size: c_uint) -> u64 { + let shift: u64 = (addr & 4) * 8; + + match addr & 0x18 { + HPET_TN_CFG_REG => self.config >> shift, // including interrupt capabilities + HPET_TN_CMP_REG => self.cmp >> shift, // comparator register + HPET_TN_FSB_ROUTE_REG => self.fsb >> shift, + _ => { + // TODO: Add trace point - trace_hpet_ram_read_invalid() + // Reserved. + 0 + } + } + } + + fn write(&mut self, addr: hwaddr, value: u64, size: u64) { + let shift = ((addr & 4) * 8) as usize; + let len = std::cmp::min(size * 8, 64 - shift as u64) as usize; + + match addr & 0x18 { + HPET_TN_CFG_REG => self.set_tn_cfg_reg(shift, len, value), + HPET_TN_CMP_REG => self.set_tn_cmp_reg(shift, len, value), + HPET_TN_FSB_ROUTE_REG => self.set_tn_fsb_route_reg(shift, len, value), + _ => { + // TODO: Add trace point - trace_hpet_ram_write_invalid() + // Reserved. + } + } + } +} + #[derive(Debug)] pub struct HPETTimerInstance(BqlRefCell); @@ -466,7 +501,7 @@ fn timer_handler(timer: &mut HPETTimerInstance) { /// Note: Wrap all items that may be changed in the callback called by C /// into the BqlCell/BqlRefCell. #[repr(C)] -#[derive(Debug, qemu_api_macros::offsets)] +#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] pub struct HPETState { parent_obj: SysBusDevice, iomem: MemoryRegion, @@ -636,3 +671,190 @@ impl QEMUTimerImpl for HPETState { const QEMU_TIMER_CB: Option = Some(HPETTimerInstance::timer_handler); } + +impl ObjOps for HPETState { + // TODO: Add binding to register idiomatic Rust callback. + const HPET_RAM_OPS: MemoryRegionOps = MemoryRegionOps { + read: Some(hpet_ram_read), + write: Some(hpet_ram_write), + read_with_attrs: None, + write_with_attrs: None, + valid: MemoryRegionOps__bindgen_ty_1 { + min_access_size: 4, + max_access_size: 8, + ..Zeroable::ZERO + }, + impl_: MemoryRegionOps__bindgen_ty_2 { + min_access_size: 4, + max_access_size: 8, + ..Zeroable::ZERO + }, + endianness: device_endian::DEVICE_NATIVE_ENDIAN, + }; + + unsafe fn init(&mut self) { + // SAFETY: + // self and self.iomem are guaranteed to be valid at this point since callers + // must make sure the `self` reference is valid. + unsafe { + memory_region_init_io( + addr_of_mut!(self.iomem), + addr_of_mut!(*self).cast::(), + &Self::HPET_RAM_OPS, + addr_of_mut!(*self).cast::(), + Self::TYPE_NAME.as_ptr(), + HPET_REG_SPACE_LEN, + ); + let sbd = addr_of_mut!(*self).cast::(); + sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); + } + } +} + +impl DeviceGPIOImpl for HPETState { + const GPIO_IRQ_HANDLER: Option = + Some(HPETState::handle_legacy_irq); +} + +impl QDevOps for HPETState { + fn realize(&mut self) { + // SAFETY: + // caller of C Qdev guarantees that the "self" passed in is a + // valid HPETState reference, so it is able to cast as SysBusDevice. + let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::()) }; + + if self.int_route_cap == 0 { + // TODO: Add error binding: warn_report() + println!("Hpet's hpet-intcap property not initialized"); + } + + // SAFETY: + // hpet_fw_cfg is contained in a BqlCell and is protected by BQL, + // so it's safe to modify it. + self.hpet_id.set(unsafe { hpet_fw_cfg.assign_hpet_id() }); + + for irq in self.irqs.iter() { + sbd.init_irq(irq); + } + + if self.num_timers.get() < HPET_MIN_TIMERS { + self.num_timers.set(HPET_MIN_TIMERS); + } else if self.num_timers.get() > HPET_MAX_TIMERS { + self.num_timers.set(HPET_MAX_TIMERS); + } + + self.init_timer(); + // 64-bit General Capabilities and ID Register; LegacyReplacementRoute. + self.capability.set( + HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT | + 1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT | + 1 << HPET_CAP_LEG_RT_CAP_SHIFT | + HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT | + ((self.num_timers.get() - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer + (HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns + ); + + self.init_gpio_in(2); + self.init_gpio_out(&self.pit_enabled, 1); + } + + fn reset(&mut self) { + let sbd: *mut SysBusDevice = addr_of_mut!(*self).cast::(); + + for i in 0..self.num_timers.get() { + self.get_timer(i).borrow_mut().reset(); + } + + self.pit_enabled.set(true); + self.counter.set(0); + self.hpet_offset.set(0); + self.config.set(0); + // SAFETY: + // hpet_fw_cfg is contained in a BqlCell and is protected by BQL, + // so it's safe to modify it. + unsafe { + hpet_fw_cfg.update_hpet_cfg( + self.hpet_id.get(), + Some(self.capability.get() as u32), + Some((*sbd).mmio[0].addr), + ) + }; + + // to document that the RTC lowers its output on reset as well + self.rtc_irq_level.set(0); + } +} + +impl RamOps for HPETState { + fn read(&mut self, addr: hwaddr, size: c_uint) -> u64 { + let shift: u64 = (addr & 4) * 8; + + // address range of all TN regs + if (0x100..=0x3ff).contains(&addr) { + let timer_id: usize = ((addr - 0x100) / 0x20) as usize; + + // TODO: Add trace point - trace_hpet_ram_read(addr) + if timer_id > self.num_timers.get() { + // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) + // Reserved. + return 0; + } + + self.get_timer(timer_id).borrow_mut().read(addr, size) + } else { + match addr & !4 { + HPET_CAP_REG => self.capability.get() >> shift, /* including HPET_PERIOD 0x004 */ + // (CNT_CLK_PERIOD field) + HPET_CFG_REG => self.config.get() >> shift, + HPET_COUNTER_REG => { + let cur_tick: u64 = if self.is_hpet_enabled() { + self.get_ticks() + } else { + self.counter.get() + }; + + // TODO: Add trace point - trace_hpet_ram_read_reading_counter(addr & 4, + // cur_tick) + cur_tick >> shift + } + HPET_INT_STATUS_REG => self.int_status.get() >> shift, + _ => { + // TODO: Add trace point- trace_hpet_ram_read_invalid() + // Reserved. + 0 + } + } + } + } + + fn write(&mut self, addr: hwaddr, value: u64, size: u64) { + let shift = ((addr & 4) * 8) as usize; + let len = std::cmp::min(size * 8, 64 - shift as u64) as usize; + + // TODO: Add trace point - trace_hpet_ram_write(addr, value) + if (0x100..=0x3ff).contains(&addr) { + let timer_id: usize = ((addr - 0x100) / 0x20) as usize; + + // TODO: Add trace point - trace_hpet_ram_write_timer_id(timer_id) + if timer_id > self.num_timers.get() { + // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) + return; + } + + self.get_timer(timer_id) + .borrow_mut() + .write(addr, value, size) + } else { + match addr & !0x4 { + HPET_CAP_REG => {} // General Capabilities and ID Register: Read Only + HPET_CFG_REG => self.set_cfg_reg(shift, len, value), + HPET_INT_STATUS_REG => self.set_int_status_reg(shift, len, value), + HPET_COUNTER_REG => self.set_counter_reg(shift, len, value), + _ => { + // TODO: Add trace point - trace_hpet_ram_write_invalid() + // Reserved. + } + } + } + } +} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index 387913bbdfb9..1692dbf19a85 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -10,7 +10,12 @@ #![deny(unsafe_op_in_unsafe_fn)] +use qemu_api::c_str; + extern crate qemu_api; pub mod fw_cfg; pub mod hpet; +pub mod qdev; + +pub const TYPE_HPET: &::std::ffi::CStr = c_str!("hpet"); diff --git a/rust/hw/timer/hpet/src/qdev.rs b/rust/hw/timer/hpet/src/qdev.rs new file mode 100644 index 000000000000..6ddfc9422d78 --- /dev/null +++ b/rust/hw/timer/hpet/src/qdev.rs @@ -0,0 +1,133 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::ptr::NonNull; +use std::{ + ffi::CStr, + os::raw::{c_uint, c_void}, +}; + +use qemu_api::{ + bindings::*, + c_str, + qdev::DeviceImpl, + qom::{ClassInitImpl, ObjectImpl, ObjectType}, + qom_isa, +}; + +use crate::hpet::*; + +qom_isa!(HPETState : SysBusDevice, DeviceState, Object); + +// TODO: add OBJECT_DECLARE_SIMPLE_TYPE. +#[repr(C)] +pub struct HPETClass { + parent_class: ::Class, +} + +unsafe impl ObjectType for HPETState { + type Class = HPETClass; + const TYPE_NAME: &'static CStr = crate::TYPE_HPET; +} + +impl ClassInitImpl for HPETState { + fn class_init(klass: &mut HPETClass) { + >::class_init(&mut klass.parent_class); + } +} + +pub(crate) trait ObjOps { + const HPET_RAM_OPS: MemoryRegionOps; + unsafe fn init(&mut self); +} + +impl ObjectImpl for HPETState { + type ParentType = SysBusDevice; + + const INSTANCE_INIT: Option = Some(Self::init); +} + +pub(crate) trait RamOps { + fn read(&mut self, addr: hwaddr, _size: c_uint) -> u64; + fn write(&mut self, addr: hwaddr, value: u64, size: u64); +} + +pub(crate) unsafe extern "C" fn hpet_ram_read( + opaque: *mut c_void, + addr: hwaddr, + size: c_uint, +) -> u64 { + // SAFETY: + // the pointer is convertible to a reference + let state: &mut HPETState = + unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + + state.read(addr, size) +} + +pub(crate) unsafe extern "C" fn hpet_ram_write( + opaque: *mut c_void, + addr: hwaddr, + data: u64, + size: c_uint, +) { + // SAFETY: + // the pointer is convertible to a reference + let state: &mut HPETState = + unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + + state.write(addr, data, size as u64); +} + +// TODO: Make these properties user-configurable! +qemu_api::declare_properties! { + HPET_PROPERTIES, + qemu_api::define_property!( + c_str!("timers"), + HPETState, + num_timers, + unsafe { &qdev_prop_uint8 }, + u8, + default = HPET_MIN_TIMERS + ), + qemu_api::define_property!( + c_str!("msi"), + HPETState, + flags, + unsafe { &qdev_prop_bit }, + u32, + bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, + default = false, + ), + qemu_api::define_property!( + c_str!("hpet-intcap"), + HPETState, + int_route_cap, + unsafe { &qdev_prop_uint32 }, + u32, + default = 0 + ), + qemu_api::define_property!( + c_str!("hpet-offset-saved"), + HPETState, + hpet_offset_saved, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} + +pub(crate) trait QDevOps { + fn realize(&mut self); + fn reset(&mut self); +} + +impl DeviceImpl for HPETState { + fn properties() -> &'static [Property] { + &HPET_PROPERTIES + } + + const REALIZE: Option = Some(Self::realize); + const RESET: Option = Some(Self::reset); +} From patchwork Thu Dec 5 06:07:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhao Liu X-Patchwork-Id: 13894731 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3EF3CE77172 for ; Thu, 5 Dec 2024 05:51:23 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJ4ki-0006hR-Jo; Thu, 05 Dec 2024 00:50:16 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kf-0006hB-IG; Thu, 05 Dec 2024 00:50:13 -0500 Received: from mgamail.intel.com ([198.175.65.18]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJ4kd-0007Br-Vx; Thu, 05 Dec 2024 00:50:13 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1733377812; x=1764913812; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=163co5V0L2579CPnezHoIN6M6o+8Ub2IEuDw5h91upg=; b=NQ6uXEqbVLoAUbOjEUPGdnNctckrpBFjWhdDctDYWW4qPU125T3n128y JA5TdGaVp5Hf/dOIzdS2xOKrE9QLunz6ykVBHu0mnlEH8JavL3BNaSNpB AcS10vOYuzcPoT0P6BvGJa3TRClXEVv4f34u5z3AWqrv8SiT3dURXDbOk CvyMqoHRMdo3VcwvGsXdwMETTTBiGW5JDkmnrNopKbBEW8Rt9FWiKGSLJ 8TKaPfCQnFf9GPQZo/ddhNN3+XEyaxDxh6jlwAUIPw2Qawgda17rNUXVq b3tFXH5hJnzPA7PzbncVB2CzK2B2MV97BM+3TEju0tzb6EQWlS6gGZyqp g==; X-CSE-ConnectionGUID: +hsGLYkXQ2OL0aFkY3ii9w== X-CSE-MsgGUID: o+Ln+NEkTQSyKDySWTCzHQ== X-IronPort-AV: E=McAfee;i="6700,10204,11276"; a="33815692" X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="33815692" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Dec 2024 21:49:51 -0800 X-CSE-ConnectionGUID: 8xZHD59IRnCk5pAw4SIMyA== X-CSE-MsgGUID: aBSIfJ1iRxC2OFMTQQoN+w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,209,1728975600"; d="scan'208";a="94455084" Received: from liuzhao-optiplex-7080.sh.intel.com ([10.239.160.36]) by fmviesa009.fm.intel.com with ESMTP; 04 Dec 2024 21:49:49 -0800 From: Zhao Liu To: Paolo Bonzini , "Michael S . Tsirkin" , Manos Pitsidianakis , Junjie Mao , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Philippe_M?= =?utf-8?q?athieu-Daud=C3=A9?= , Peter Maydell Cc: qemu-devel@nongnu.org, qemu-rust@nongnu.org, Zhao Liu Subject: [RFC 13/13] i386: enable rust hpet for pc when rust is enabled Date: Thu, 5 Dec 2024 14:07:14 +0800 Message-Id: <20241205060714.256270-14-zhao1.liu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241205060714.256270-1-zhao1.liu@intel.com> References: <20241205060714.256270-1-zhao1.liu@intel.com> MIME-Version: 1.0 Received-SPF: pass client-ip=198.175.65.18; envelope-from=zhao1.liu@intel.com; helo=mgamail.intel.com X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-2.999, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Add HPET configuration in PC's Kconfig options, and select HPET device (Rust version) if Rust is supported. Signed-off-by: Zhao Liu --- hw/i386/Kconfig | 2 ++ hw/timer/Kconfig | 1 - rust/hw/Kconfig | 1 + rust/hw/timer/Kconfig | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 rust/hw/timer/Kconfig diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 32818480d263..83ab3222c4f0 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -39,6 +39,8 @@ config PC select PCSPK select I8257 select MC146818RTC + select HPET if !HAVE_RUST + select X_HPET_RUST if HAVE_RUST # For ACPI builder: select SERIAL_ISA select ACPI_PCI diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index c96fd5d97ae8..645d7531f40e 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -11,7 +11,6 @@ config A9_GTIMER config HPET bool - default y if PC config I8254 bool diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig index 4d934f30afe1..36f92ec02874 100644 --- a/rust/hw/Kconfig +++ b/rust/hw/Kconfig @@ -1,2 +1,3 @@ # devices Kconfig source char/Kconfig +source timer/Kconfig diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig new file mode 100644 index 000000000000..afd980335037 --- /dev/null +++ b/rust/hw/timer/Kconfig @@ -0,0 +1,2 @@ +config X_HPET_RUST + bool