From patchwork Fri Jan 17 19:39:54 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943940 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 81214C02188 for ; Fri, 17 Jan 2025 19:41:28 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCY-0001WD-JY; Fri, 17 Jan 2025 14:40:18 -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 1tYsCW-0001Un-ST for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:16 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCV-0002BS-A6 for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:16 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142814; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vaboZ+TQyEKNVXlrPQA7ag9T+Mayf0OWrnAczy+Hp9Q=; b=d3Vmz48bnYP+Qw1+uHf3DQRcrcv9z22eIb4YuNGxtt5NyRQBAb56fYv6BVu3qFmwRoM0AK yLAjbXaBRbcuFdEbGrFJaPpYv33sVGqxu6aBHIstSxXZxesRTLs/wFaZShGDyvA/3LkuCx VJM3+NRbzp0hn9JvKxAEi5JUYJecaTU= Received: from mail-ed1-f70.google.com (mail-ed1-f70.google.com [209.85.208.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-451-GozuQNAlMNil04DBgb2Rrw-1; Fri, 17 Jan 2025 14:40:13 -0500 X-MC-Unique: GozuQNAlMNil04DBgb2Rrw-1 X-Mimecast-MFC-AGG-ID: GozuQNAlMNil04DBgb2Rrw Received: by mail-ed1-f70.google.com with SMTP id 4fb4d7f45d1cf-5d40c0c728aso2307609a12.2 for ; Fri, 17 Jan 2025 11:40:12 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142810; x=1737747610; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vaboZ+TQyEKNVXlrPQA7ag9T+Mayf0OWrnAczy+Hp9Q=; b=mEZ/tuClcEZ9NUKCqzKGMJiOdl2owGMeeqvDpHUXMbChUIcDpX7vt+LlTYx0uwZ8n2 3etDK1sNBxAsbjVLF5cRY9aHQ4NT0BAgLwcVykmwQiADASSbiJw8jmRb4AmtniMOrSOy FmPRNgp0zpFvF5BJKxzGRjTSdn+hxyNk4DHs4b5oSKKGNXEcj+t3rXvJhw+rLRCzCmy1 I9QsDpaJGgFsSDzdg6WRV8EVCIIwqhGIO/bjzZZQO6UaU3n1ZVWamPd17uBK4zHAWGcm Y6UcjGZfKY5M0qXWcx5SMpDTJu/zWHOXsTU/WU+gD9ukgaQN70Y3oEG3D3Nj82tUob3S mG9w== X-Gm-Message-State: AOJu0Yx8SNpNwDLtEFUxdC85JoHFULeX+ofCY3syh13T7sVtw77QDH8t nYC2JQy5oGOIdL2LfDGj6YwNJRAd///8Zr21DBTuc2IjrXMH3oeYvVFV2TMj+/jNW9pqnwHM7fp bBxMt0p50wgcPVFyfqhRuFsqS+oLCj22p0RDGsmCX8gPV0nZu1EeVEkbzGj8d6DnNsi8joo8wOv iBIQDfwy/to3n4aKlOr0DtuPSjJ+9dYoffdcs12A0= X-Gm-Gg: ASbGnctViUnbf7f88otu7UgzzDjqGPKzcdFhcjsiJXHQfPYo3FGDdzhvUt8+9zeLITa KiipTTcJyZCaS0Ty7XrLzKxXioDFUfVDRa3ZyXXYWvoxLcu+PGnSSELrkRqjkhiOK1Be6csPCrU Li/1ZggqapFaMtYsNo5SPQC6t1wN8W+WvQZJOVXuRTwAZ3tHLJJLiQOpHzalpchkrFFiHuemsRF 7fsculqwv7h32LvGqKSQJNPz2Gu4ZvL9+Qrk/Py7xUdyrEsZ+6ZRkr6solH4g== X-Received: by 2002:a05:6402:524b:b0:5d0:9054:b119 with SMTP id 4fb4d7f45d1cf-5db7db07787mr8615037a12.21.1737142810512; Fri, 17 Jan 2025 11:40:10 -0800 (PST) X-Google-Smtp-Source: AGHT+IHDQN3/+dlRteAovEglF5VL6vxcKS7afffhvsgRaXODoQ26l5oelB3KyewP+DUvzPrRvfyxEA== X-Received: by 2002:a05:6402:524b:b0:5d0:9054:b119 with SMTP id 4fb4d7f45d1cf-5db7db07787mr8614735a12.21.1737142807492; Fri, 17 Jan 2025 11:40:07 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5db73642247sm1949804a12.2.2025.01.17.11.40.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:05 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH 01/10] rust: qemu-api: add sub-subclass to the integration tests Date: Fri, 17 Jan 2025 20:39:54 +0100 Message-ID: <20250117194003.1173231-2-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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: Zhao Liu missing signed-off-by from zhao --- rust/qemu-api/tests/tests.rs | 56 ++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 526c3f4f8ea..5c3e75ed3d5 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -14,8 +14,8 @@ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceImpl, DeviceState, Property}, - qom::{ObjectImpl, ParentField}, + qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, + qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -37,6 +37,10 @@ pub struct DummyState { qom_isa!(DummyState: Object, DeviceState); +pub struct DummyClass { + parent_class: ::Class, +} + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -49,7 +53,7 @@ pub struct DummyState { } unsafe impl ObjectType for DummyState { - type Class = ::Class; + type Class = DummyClass; const TYPE_NAME: &'static CStr = c_str!("dummy"); } @@ -67,6 +71,51 @@ fn vmsd() -> Option<&'static VMStateDescription> { } } +// `impl ClassInitImpl for T` doesn't work since it violates +// orphan rule. +impl ClassInitImpl for DummyState { + fn class_init(klass: &mut DummyClass) { + >::class_init(&mut klass.parent_class); + } +} + +#[derive(qemu_api_macros::offsets)] +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct DummyChildState { + parent: ParentField, +} + +qom_isa!(DummyChildState: Object, DeviceState, DummyState); + +pub struct DummyChildClass { + parent_class: ::Class, +} + +unsafe impl ObjectType for DummyChildState { + type Class = DummyChildClass; + const TYPE_NAME: &'static CStr = c_str!("dummy_child"); +} + +impl ObjectImpl for DummyChildState { + type ParentType = DummyState; + const ABSTRACT: bool = false; +} + +impl DeviceImpl for DummyChildState {} + +impl ClassInitImpl for DummyChildState { + fn class_init(klass: &mut DummyClass) { + >::class_init(&mut klass.parent_class); + } +} + +impl ClassInitImpl for DummyChildState { + fn class_init(klass: &mut DummyChildClass) { + >::class_init(&mut klass.parent_class); + } +} + fn init_qom() { static ONCE: BqlCell = BqlCell::new(false); @@ -85,6 +134,7 @@ fn test_object_new() { init_qom(); unsafe { object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); + object_unref(object_new(DummyChildState::TYPE_NAME.as_ptr()).cast()); } } From patchwork Fri Jan 17 19:39:55 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943946 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 A5AC8C02183 for ; Fri, 17 Jan 2025 19:42:05 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCZ-0001XA-Mx; Fri, 17 Jan 2025 14:40:19 -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 1tYsCY-0001WG-Qv for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:18 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCW-0002Br-V0 for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:18 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142816; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=D0ARwjCk/01hXKDGIyxRI2rwcuyqDQN//xQ3CzCZhAQ=; b=iWbXtjHKfBdMPxdWLGtvC2TjtVOjjIQ1uqo3+9VRlfjbmAgmWAfVowRBszWZASyBPLme7q utp3+5MNSVWvfj1FTeoH6WhSxiKoTqivEYbOUb7e6jU5BCPZfjdlQed8etZ4l9MIi1FYCy hdqPjOxd2BWqfHV6FsGmsWGFXsxGYeU= Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-154-vaWV2lSqN2-wBwHycmbBZg-1; Fri, 17 Jan 2025 14:40:13 -0500 X-MC-Unique: vaWV2lSqN2-wBwHycmbBZg-1 X-Mimecast-MFC-AGG-ID: vaWV2lSqN2-wBwHycmbBZg Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-aaf901a0ef9so213269666b.0 for ; Fri, 17 Jan 2025 11:40:13 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142809; x=1737747609; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=D0ARwjCk/01hXKDGIyxRI2rwcuyqDQN//xQ3CzCZhAQ=; b=RiRIK2+kh+hAubrmuqpvUnq439yHNFAc9h8CB6+nBy4+OcdVlYBrUFpl6suSP6s8OL goSOIMVzqt41/YNNk0f3NOTGi4oNAt9EkzzaLLa8f+0zbtjq0duWtTlMIjh70KUS19zT OhflGvXkhmnVbZERtlr+n25ft1MQ8c05pUbb+9Ghvmsq69XhLkoUZHZdNzB9wIVn64IP rzBcIX3oEHTQ5vFebHaivsGVsGaIvxlOxdhS1/Lqqfvlor6xLXPu/YTEWa1EvAMSO9Uh fmp36uB/Ow1MyDS+9BnwCVo1MBPhrCvFk03S5m5Yzrzz3lOEMihKmXdpuNjHLk5Pavs0 Lt/Q== X-Gm-Message-State: AOJu0YzBST4Il307XNOE295A1sb+DdCGT146NRSZEuri6zcBAsTNF1kh 7DVBPIc05/j+Blmt407YxNrw6qyLHF8sGLig8g01HCZHpU5CuO+25zZkPaLWzkn9bRpby437qmM w701Ha1Ze+qq1Uw9aCBPccGXFifFdSRxbftbJHyYCB8vN1qvYZDcJK0wZ5Ze1l6rEiwwsDHuK35 XNe5J2UP1u5MXQvrMY8C6ZUP7WgDvlshjSsTQj3G8= X-Gm-Gg: ASbGncsYZcHefKZhTSEeEs1wJYk5ZH3+76yIHgPJEaAYBBn/HgI4mKVOVMfT3NW+tXh kavshq5w6bC9xSQKQ90YL1h1Kla2ZbTEff371VOVgI7m9BDmqiMdgd9kKc5eimLT464vndD06OY WfSgAg2g/DMPwH8XXmJ5MY70q1ct3lsK7jfT7gCMAOM6fi9bNU6cwOXtowjHBSGuUkHsSuWTfz3 fed224VTSnGDQye5Hqd6lULWVxA3TSeQEYN3nhNH6rNWyJ4J6e5vCAjpd8o+w== X-Received: by 2002:a17:907:1b15:b0:aae:b259:ef5e with SMTP id a640c23a62f3a-ab38aedb10amr439699366b.0.1737142809427; Fri, 17 Jan 2025 11:40:09 -0800 (PST) X-Google-Smtp-Source: AGHT+IFMDRdERqU2cfUiTl47s86HjchaelOmwLr6bdW4pbj5I3PpuiERcQKQgI69m80YCaGYdfXq3w== X-Received: by 2002:a17:907:1b15:b0:aae:b259:ef5e with SMTP id a640c23a62f3a-ab38aedb10amr439696366b.0.1737142808843; Fri, 17 Jan 2025 11:40:08 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab384c61388sm217563366b.20.2025.01.17.11.40.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:08 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 02/10] rust: qom: add reference counting functionality Date: Fri, 17 Jan 2025 20:39:55 +0100 Message-ID: <20250117194003.1173231-3-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -39 X-Spam_score: -4.0 X-Spam_bar: ---- X-Spam_report: (-4.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-1.79, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 a smart pointer that allows to add and remove references from QOM objects. It's important to note that while all QOM objects have a reference count, in practice not all of them have their lifetime guarded by it. Embedded objects, specifically, are confined to the lifetime of the owner. When writing Rust bindings this is important, because embedded objects are *never* used through the "Owned<>" smart pointer that is introduced here. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 121 ++++++++++++++++++++++++++++++++++- rust/qemu-api/src/vmstate.rs | 6 +- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 97901fb9084..c404f8a1aa7 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -56,13 +56,21 @@ use std::{ ffi::CStr, fmt, + mem::ManuallyDrop, ops::{Deref, DerefMut}, os::raw::c_void, + ptr::NonNull, }; pub use bindings::{Object, ObjectClass}; -use crate::bindings::{self, object_dynamic_cast, object_get_class, object_get_typename, TypeInfo}; +use crate::{ + bindings::{ + self, object_dynamic_cast, object_get_class, object_get_typename, object_ref, object_unref, + TypeInfo, + }, + cell::bql_locked, +}; /// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct /// or indirect parent of `Self`). @@ -605,6 +613,110 @@ unsafe impl ObjectType for Object { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; } +/// A reference-counted pointer to a QOM object. +/// +/// `Owned` wraps `T` with automatic reference counting. It increases the +/// reference count when created via [`Owned::from`] and decreases it when +/// dropped. This ensures that the reference count remains elevated as long as +/// any `Owned` references to it exist. Note that however the object may +/// disappear if it is *embedded* within a larger object. For more information +/// see the [`Owned::from`]. +/// +/// It is *not* safe to send `Owned` to another thread, because currently +/// `object_unref` requires the Big QEMU Lock. +#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Owned(NonNull); + +// SAFETY: the implementation asserts via `bql_locked` that the BQL is taken +unsafe impl Sync for Owned {} + +impl Owned { + /// Convert a raw C pointer into an owned reference to the QOM + /// object it points to. The object's reference count will be + /// decreased when the `Owned` is dropped. + /// + /// # Panics + /// + /// Panics if `ptr` is NULL. + /// + /// # Safety + /// + /// The caller must indeed own a reference to the QOM object. + /// The object must not be embedded in another unless the outer + /// object is guaranteed to have a longer lifetime. + /// + /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed + /// back to `from_raw()` (assuming the original `Owned` was valid!), + /// since the owned reference remains there between the calls to + /// `into_raw()` and `from_raw()`. + #[allow(clippy::missing_const_for_fn)] + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the pointer passed to from_raw + // remains const + Owned(NonNull::new(ptr as *mut T).unwrap()) + } + + /// Obtain a raw C pointer from a reference. `src` is consumed + /// and the reference is leaked. + #[allow(clippy::missing_const_for_fn)] + pub fn into_raw(src: Owned) -> *mut T { + let src = ManuallyDrop::new(src); + src.0.as_ptr() + } + + /// Increase the reference count of a QOM object and return + /// a new owned reference to it. + /// + /// # Safety + /// + /// The object must not be embedded in another, unless the outer + /// object is guaranteed to have a longer lifetime. + pub unsafe fn from(obj: &T) -> Self { + unsafe { + object_ref(obj.as_object_mut_ptr().cast::()); + + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the reference passed to from_raw + // remains shared + Owned(NonNull::new_unchecked(obj.as_mut_ptr())) + } + } +} + +impl Deref for Owned { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned`. + // With that guarantee, reference counting ensures that + // the object remains alive. + unsafe { &*self.0.as_ptr() } + } +} +impl ObjectDeref for Owned {} + +impl Drop for Owned { + fn drop(&mut self) { + assert!(bql_locked()); + // SAFETY: creation method is unsafe, and whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned`. + unsafe { + object_unref(self.as_object_mut_ptr().cast::()); + } + } +} + +impl> fmt::Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().debug_fmt(f) + } +} + /// Trait for methods exposed by the Object class. The methods can be /// called on all objects that have the trait `IsA`. /// @@ -636,6 +748,13 @@ fn get_class(&self) -> &'static ::Class { klass } + + /// Convenience function for implementing the Debug trait + fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple(&self.typename()) + .field(&(self as *const Self)) + .finish() + } } impl ObjectMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 6ac432cf52f..11d21b8791c 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -29,6 +29,8 @@ pub use crate::bindings::{VMStateDescription, VMStateField}; use crate::{ bindings::{self, VMStateFlags}, + prelude::*, + qom::Owned, zeroable::Zeroable, }; @@ -191,7 +193,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, /// [`BqlCell`](crate::cell::BqlCell), [`BqlRefCell`](crate::cell::BqlRefCell) /// * a raw pointer to any of the above -/// * a `NonNull` pointer or a `Box` for any of the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`](crate::qom::Owned) for any of +/// the above /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented @@ -398,6 +401,7 @@ unsafe impl<$base> VMState for $type where $base: VMState $($where)* { // Unlike C pointers, Box is always non-null therefore there is no need // to specify VMS_ALLOC. impl_vmstate_pointer!(Box where T: VMState); +impl_vmstate_pointer!(Owned where T: VMState + ObjectType); // Arrays using the underlying type's VMState plus // VMS_ARRAY/VMS_ARRAY_OF_POINTER From patchwork Fri Jan 17 19:39:56 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943939 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 519FEC02183 for ; Fri, 17 Jan 2025 19:41:18 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCo-0001Xz-64; Fri, 17 Jan 2025 14:40:34 -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 1tYsCY-0001WI-U1 for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:18 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCX-0002By-7N for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:18 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142816; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZfIMapRdAVK5cr8WH2cqZZ26cbMwLQZzS74QVu30oqU=; b=Mvu9r7f/GQPBUiOCvXWDC/5kZBuSYOFR2YAotJjtwNLQbLGk8TPv6d2nBdU9SrHdL08x1R SLpLjqq8NDSEliYnPZY+ffajd4dbkzcGLPpH9aLTLUx7CMEym8E1zDflOqzmeToZh9H4Bx LlyPkICmedGEL6JMY5W0FVscdEonPek= Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-378-sEnvASvLNtqCzVie-0-fxQ-1; Fri, 17 Jan 2025 14:40:15 -0500 X-MC-Unique: sEnvASvLNtqCzVie-0-fxQ-1 X-Mimecast-MFC-AGG-ID: sEnvASvLNtqCzVie-0-fxQ Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-aa68203d288so195006666b.1 for ; Fri, 17 Jan 2025 11:40:15 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142813; x=1737747613; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZfIMapRdAVK5cr8WH2cqZZ26cbMwLQZzS74QVu30oqU=; b=NH3rYEHlYyZqlTpQpGjKlb/tDw/lmFmi7VONhC2OqiqKNX7DS8EZ7coH3oaVx5Qi81 hpUkXQnF82aPoQczL07Rf/jCxkmCoSyQ2z2slsJ7yFrrv6oWtV0zjcvAG+vMJ/VxQkPk uwlnY89/CbrnLtznt+jJfoNrhd5wht81zQaa7TZf92Q7oHucf03UPgyoVkuETY7o9es6 nHRCYOTlUgW4xpaEsUCQ/fdJADrYKBgx1G7fc7ezqr/gZshjtcgZgHZIEpSVst+Ez4hM 52xR8nuu0oQDE8LPNCCp8k2r7nI/fnFGsZEhdL2QZjQfhOGDhRKsVCLkyCpqfb+KQXVW AOvg== X-Gm-Message-State: AOJu0YxyZwbA+lYXlLI0bMnKcOuBj7xDdMtYZ07AOzSNmhQolBEkfk9P tVrVWherVib5nue3m6fFu0g5OSAjES3atihEhnZKDlfWYioogOXHhSpTlT4nfyNQlyx5fjE/WqL Hjw1bPIO9S1HxP2mUESVZbTlB0z4n4H/eQmPn5GzniSMSKusA2EIrqAEnaT0KU5KMPNxQb56WGt g0s0ZjIf+sEjQzzsY2GM2rCsFgqKWCPcoExshNRBs= X-Gm-Gg: ASbGnct9EIUzEW4/PymXIdpwFx7fmEzMObRcOBU7EFvAmB15tS9BBlx0Z9uu0wmdGNt cR8av+uwuHCQH09exyyiX9MgLfKUnjHFcdrwOcSuTj72GamGzF+OBbg8HjfeVdInS7fIYfBcFwu 9NS1R281+Ak8mpirTXLRjXKf0szFmpU8PGT6mGZqGGrrjhTWKn6y5lTgXwQxMbQfcGqdQsQMX34 gb/ZwSF55KQn4RoxIUQ52Dt/NCNVySMPaWYph/GWQ/mNtuCV9uBaNzdLEA0SA== X-Received: by 2002:a17:907:2d2c:b0:ab2:faed:f180 with SMTP id a640c23a62f3a-ab38b18bf29mr409514666b.33.1737142813561; Fri, 17 Jan 2025 11:40:13 -0800 (PST) X-Google-Smtp-Source: AGHT+IH6cJ0/MWso21JFMAHR/UQKmDuyGQq4mE3BUGlnLZ6Bn+aB2bkitJngAEw5J+sh3QwdPAQFAg== X-Received: by 2002:a17:907:2d2c:b0:ab2:faed:f180 with SMTP id a640c23a62f3a-ab38b18bf29mr409512266b.33.1737142813111; Fri, 17 Jan 2025 11:40:13 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab384ce1bacsm220439066b.53.2025.01.17.11.40.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:09 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 03/10] rust: qom: add object creation functionality Date: Fri, 17 Jan 2025 20:39:56 +0100 Message-ID: <20250117194003.1173231-4-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -39 X-Spam_score: -4.0 X-Spam_bar: ---- X-Spam_report: (-4.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-1.79, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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 basic object lifecycle test can now be implemented using safe code! Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 13 ++++++++----- rust/qemu-api/src/prelude.rs | 1 + rust/qemu-api/src/qom.rs | 23 +++++++++++++++++++++-- rust/qemu-api/tests/tests.rs | 30 +++++++++++------------------- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 27563700665..d8409f3d310 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -690,15 +690,18 @@ pub fn post_load(&self, _version_id: u32) -> Result<(), ()> { irq: qemu_irq, chr: *mut Chardev, ) -> *mut DeviceState { + let pl011 = PL011State::new(); unsafe { - let dev: *mut DeviceState = qdev_new(PL011State::TYPE_NAME.as_ptr()); - let sysbus: *mut SysBusDevice = dev.cast::(); - + let dev = pl011.as_mut_ptr::(); qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); - sysbus_realize_and_unref(sysbus, addr_of_mut!(error_fatal)); + + let sysbus = pl011.as_mut_ptr::(); + sysbus_realize(sysbus, addr_of_mut!(error_fatal)); sysbus_mmio_map(sysbus, 0, addr); sysbus_connect_irq(sysbus, 0, irq); - dev + + // return the pointer, which is kept alive by the QOM tree; drop owned ref + pl011.as_mut_ptr() } } diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 2dc86e19b29..3df6a5c21ec 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -12,6 +12,7 @@ pub use crate::qom::ObjectCast; pub use crate::qom::ObjectCastMut; pub use crate::qom::ObjectDeref; +pub use crate::qom::ObjectClassMethods; pub use crate::qom::ObjectMethods; pub use crate::qom::ObjectType; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index c404f8a1aa7..6f7db3eabcb 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -66,8 +66,8 @@ use crate::{ bindings::{ - self, object_dynamic_cast, object_get_class, object_get_typename, object_ref, object_unref, - TypeInfo, + self, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, + object_unref, TypeInfo, }, cell::bql_locked, }; @@ -717,6 +717,24 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } +/// Trait for class methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectClassMethods: IsA { + /// Return a new reference counted instance of this class + fn new() -> Owned { + assert!(bql_locked()); + // SAFETY: the object created by object_new is allocated on + // the heap and has a reference count of 1 + unsafe { + let obj = &*object_new(Self::TYPE_NAME.as_ptr()); + Owned::from_raw(obj.unsafe_cast::()) + } + } +} + /// Trait for methods exposed by the Object class. The methods can be /// called on all objects that have the trait `IsA`. /// @@ -757,4 +775,5 @@ fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } +impl ObjectClassMethods for T where T: IsA {} impl ObjectMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 5c3e75ed3d5..1944e65c8f3 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -3,8 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use std::{ - ffi::CStr, - os::raw::c_void, + ffi::{c_void, CStr}, ptr::{addr_of, addr_of_mut}, }; @@ -132,22 +131,16 @@ fn init_qom() { /// Create and immediately drop an instance. fn test_object_new() { init_qom(); - unsafe { - object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast()); - object_unref(object_new(DummyChildState::TYPE_NAME.as_ptr()).cast()); - } + drop(DummyState::new()); + drop(DummyChildState::new()); } #[test] /// Try invoking a method on an object. fn test_typename() { init_qom(); - let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; - let p_ref: &DummyState = unsafe { &*p }; - assert_eq!(p_ref.typename(), "dummy"); - unsafe { - object_unref(p_ref.as_object_mut_ptr().cast::()); - } + let p = DummyState::new(); + assert_eq!(p.typename(), "dummy"); } // a note on all "cast" tests: usually, especially for downcasts the desired @@ -162,24 +155,23 @@ fn test_typename() { /// Test casts on shared references. fn test_cast() { init_qom(); - let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() }; + let p = DummyState::new(); + let p_ptr: *mut DummyState = unsafe { p.as_mut_ptr() }; + let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; - let p_ref: &DummyState = unsafe { &*p }; let obj_ref: &Object = p_ref.upcast(); - assert_eq!(addr_of!(*obj_ref), p.cast()); + assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); assert!(sbd_ref.is_none()); let dev_ref: Option<&DeviceState> = obj_ref.downcast(); - assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast()); + assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); // SAFETY: the cast is wrong, but the value is only used for comparison unsafe { let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); - assert_eq!(addr_of!(*sbd_ref), p.cast()); - - object_unref(p_ref.as_object_mut_ptr().cast::()); + assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); } } From patchwork Fri Jan 17 19:39:57 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943941 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 3767CC02183 for ; Fri, 17 Jan 2025 19:41:30 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCp-0001bA-D1; Fri, 17 Jan 2025 14:40: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 1tYsCe-0001Y7-4b for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:26 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCc-0002Cj-58 for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:23 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142821; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fRcbdCPUwOzkxRo3DppX4d9hILrOyDu40de8lR3wnkQ=; b=WQy6Qi4laX6We1/hBQv1geBzpM7h/IK1Fluk1GGfvzIM4bR0wY4UBRvOggoXhYF1Zcn3wV 4xTbpofWifSSVY86++JYsD8VK6NQJGSk7Tb+TzR2Rbmo97q4MX4s6S3A5Z7dwzRU4pSOze wX76L849x+9cCWGkTOipT1d0XPMjmgI= Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-295-AxvMnqo2NICMID8YQqmdTw-1; Fri, 17 Jan 2025 14:40:20 -0500 X-MC-Unique: AxvMnqo2NICMID8YQqmdTw-1 X-Mimecast-MFC-AGG-ID: AxvMnqo2NICMID8YQqmdTw Received: by mail-ed1-f71.google.com with SMTP id 4fb4d7f45d1cf-5d89a53fc46so3165137a12.2 for ; Fri, 17 Jan 2025 11:40:20 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142817; x=1737747617; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fRcbdCPUwOzkxRo3DppX4d9hILrOyDu40de8lR3wnkQ=; b=YPx9Oh/gZpyup/pYewF8hojm1uZtaBve1EIajbZn0R+CXl3F5AGsXS9zbQV38vZ4F4 50ULwtXyOOQZ/cDzzGh2V5x/UQtU6hLbEr8WJu1J170Tm4hDdNXF2KK81mT9HARGmpa8 9I8uaAzn9tE8sDzyE7xSiGh8Gxsc/HTNlJ6fHezzpUjgfoHEUGLwZXytq0SX0waO5WP3 uMKV6rP8C9PD+ytkW3cs4Hcu3tEkdxLCMRLizVXzB5xpZd7K4UyQBK6yESV3HB3n62+A 2xXaGMNsvRx4pX3bIzzP+dXZmADm3z9iLA14/5d3eDb4UbGkiuZkl9d7pUNXuClv7Mo5 o4+w== X-Gm-Message-State: AOJu0YzSepsvUFmH3aMQSTy5FkUCeNnkeiAAuQX5Su2B/j3hJZ5DlBX6 hU28PGfGatMNWhiuypuSMdcc1q1OnZSof68lNKqg8bH1iYWn+Rq5CzeG5iiANUqYIv3Gp0SZknw KmuoA2SyVF4EBPTZGxiBLhW9KhhQ6CBzoyUOz+n0CDB2+1FftR4i1XeVKisipcvr4m9f58CAzmL zk2mfnarKw/xxHBDZVCXXPYdYe3l/EM+ORlEI9QjI= X-Gm-Gg: ASbGncu0VxIxSJT3aJTmTzZ9VJM4O6O3TKpv4BAPoWmvio9tCin2PYMHr9aZekhGUCq dgx3ERGVNyz4vft62gp5AV/bhNtGis7l0TTPTRr6CGED+DN2MhHqU0oKcI9WaolKRd/0A0555Fi 6cdeo9TZVVCjYuUin84w2epywH4JTqtHYICnPVc+KfCxJt9GuUeIV2qZjc1wMofngOhiDZBZMyq YRxP7slCSL9bCmHbJisok8Qx/7ZnhXqIR8U+nVXNaa+VvtSsWg1kXqogZ1bsA== X-Received: by 2002:a17:907:3f11:b0:ab2:aea7:510f with SMTP id a640c23a62f3a-ab38b320310mr425608866b.30.1737142817151; Fri, 17 Jan 2025 11:40:17 -0800 (PST) X-Google-Smtp-Source: AGHT+IH6byZGf3zER2luuH6R2ebrcD5twTbc6HkYOOuFRATHSL5vwYA0+GcMdkji3gsjKP+xQTIxRg== X-Received: by 2002:a17:907:3f11:b0:ab2:aea7:510f with SMTP id a640c23a62f3a-ab38b320310mr425602266b.30.1737142815320; Fri, 17 Jan 2025 11:40:15 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab384fce6ddsm216656066b.180.2025.01.17.11.40.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:13 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 04/10] rust: callbacks: allow passing optional callbacks as () Date: Fri, 17 Jan 2025 20:39:57 +0100 Message-ID: <20250117194003.1173231-5-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 In some cases, callbacks are optional. Using "Some(function)" and "None" does not work well, because when someone writes "None" the compiler does not know what to use for "F" in "Option". Therefore, adopt () to mean a "null" callback. It is possible to enforce that a callback is valid by adding a "let _: () = F::ASSERT_IS_SOME" before the invocation of F::call. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/callbacks.rs | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs index 314f9dce962..a59878bfb74 100644 --- a/rust/qemu-api/src/callbacks.rs +++ b/rust/qemu-api/src/callbacks.rs @@ -79,6 +79,31 @@ /// call_it(&move |_| String::from(x), "hello workd"); /// ``` /// +/// `()` can be used to indicate "no function": +/// +/// ``` +/// # use qemu_api::callbacks::FnCall; +/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { +/// if F::IS_SOME { +/// Some(F::call((s,))) +/// } else { +/// None +/// } +/// } +/// +/// assert!(optional(&(), "hello world").is_none()); +/// ``` +/// +/// Invoking `F::call` will then be a run-time error. +/// +/// ```should_panic +/// # use qemu_api::callbacks::FnCall; +/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// # F::call((s,)) +/// # } +/// let s: String = call_it(&(), "hello world"); // panics +/// ``` +/// /// # Safety /// /// Because `Self` is a zero-sized type, all instances of the type are @@ -93,10 +118,70 @@ pub unsafe trait FnCall: 'static + Sync + Sized { /// Rust 1.79.0+. const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::() == 0) }; + /// Referring to this constant asserts that the `Self` type is an actual + /// function type, which can be used to catch incorrect use of `()` + /// at compile time. + /// + /// # Examples + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// let _: () = F::ASSERT_IS_SOME; + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + /// + /// Note that this use more simply `const { assert!(F::IS_SOME) }` in + /// Rust 1.79.0 or newer. + const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) }; + + /// `true` if `Self` is an actual function type and not `()`. + /// + /// # Examples + /// + /// You can use `IS_SOME` to catch this at compile time: + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// const { assert!(F::IS_SOME) } + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + const IS_SOME: bool; + + /// `false` if `Self` is an actual function type, `true` if it is `()`. + fn is_none() -> bool { + !Self::IS_SOME + } + + /// `true` if `Self` is an actual function type, `false` if it is `()`. + fn is_some() -> bool { + Self::IS_SOME + } + /// Call the function with the arguments in args. fn call(a: Args) -> R; } +/// `()` acts as a "null" callback. Using `()` and `function` is nicer +/// than `None` and `Some(function)`, because the compiler is unable to +/// infer the type of just `None`. Therefore, the trait itself acts as the +/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. +unsafe impl FnCall for () { + const IS_SOME: bool = false; + + /// Call the function with the arguments in args. + fn call(_a: Args) -> R { + panic!("callback not specified") + } +} + macro_rules! impl_call { ($($args:ident,)* ) => ( // SAFETY: because each function is treated as a separate type, @@ -106,6 +191,8 @@ unsafe impl FnCall<($($args,)*), R> for F where F: 'static + Sync + Sized + Fn($($args, )*) -> R, { + const IS_SOME: bool = true; + #[inline(always)] fn call(a: ($($args,)*)) -> R { let _: () = Self::ASSERT_ZERO_SIZED; @@ -141,4 +228,14 @@ fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { fn test_call() { assert_eq!(do_test_call(&str::to_owned), "hello world") } + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { + assert!(F::is_some()); + } + + #[test] + fn test_is_some() { + do_test_is_some(&str::to_owned); + } } From patchwork Fri Jan 17 19:39:58 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943948 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 DB830C02188 for ; Fri, 17 Jan 2025 19:42:08 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCr-0001cV-Da; Fri, 17 Jan 2025 14:40:37 -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 1tYsCp-0001aX-0m for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:35 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCe-0002Cz-13 for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:28 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142823; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oahqCqM04zU6SgnyH8PstMyxlymjpkN8kciJN6RaKAY=; b=QEBnwTi954YohlcETYViuBL4nZd/bl4WUvcltX7t8wwpFIvkqq+hxGMyTcsGHIv4hejJ6B mkzeNSWHuooQuIvKvgsInFAx0D/Zmrf430+wFFPBNb3lJ2C7nY+gUAev7XzzC0LLdmI2AI 7yesSfAWI74ifjawCCi2JXdM8aE2g6k= Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-34-mOd_VZWhOLqLd46FcKIjwA-1; Fri, 17 Jan 2025 14:40:20 -0500 X-MC-Unique: mOd_VZWhOLqLd46FcKIjwA-1 X-Mimecast-MFC-AGG-ID: mOd_VZWhOLqLd46FcKIjwA Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-aa68952272bso289572666b.2 for ; Fri, 17 Jan 2025 11:40:19 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142817; x=1737747617; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=oahqCqM04zU6SgnyH8PstMyxlymjpkN8kciJN6RaKAY=; b=IAr0PnI40GT9rrX16IxpPFml4MM10TEC3GlSylK4vHlJFJoxncvupQlAr1PwUHs5MX BJS3Yj/SpIeiZbSKVovAAkjKnoM93s+pBWI+vyCumuJRrApR9Kml4XtcLqjaf4SCOoej ExCd3blUYWkUGZ5FQ/WcgUMBveEiiMe79nWAAfPLswltYDg1OVJrsvYXSZsuNeMhJsFs mtck4cgylmpgsN+EF9HyiaZAL7BJw0a6vOLeiokfjmgcc4oBzbT2RJnpo7aljewsNYBe ZfV5H7yOSQ6unnjuFdrafnEx0cWNPsVqE1bCRq8wPFR3TrxTzDQvytNAYtGjvjQ/yBof OTPg== X-Gm-Message-State: AOJu0YzB7e/X2g3LBEhQHtKanIOtiji2eRqQe4ECwbdF+AreisUIfEeu Yd+kxBGzv7gfQ5FxeUXaw2iPWdxVnr2LCFCpDHBVy9CfVzBS7JoBAkg74BOBEcoC8xfVIzAZY4M MubBfmyb4OoajhsloxnNUZzjnsN5CrktgL8Ub0ROaFMWqS1MNZsAOIv7GZQvlen/HYbAuEBYoEm /aZMClgvPyKq7Eo+NvIP5gV8AeaQ9cWs7pP8IArtk= X-Gm-Gg: ASbGncsg8BmzzVNpi7hxWoeAsrp35FQezTgC62VsntnFwoprl/gVMZ7MEqX7UOZNFE5 WTdNsKN815XTP1DzQfSwyBXw2Ckuav0WvjEjuhHorFK577gP3UCApoYX4ZNlAfwyTI77NIcXZ6U VlDJ5XqgRboEpj3aMDijv/eOrDCbGOekUmtAzL6ArWXlf3DnaMGizNUS06g00HqmNWe37nnJU2V KeHsyb0023fwFh/b0ua9s0Avt0R0zOkbAHVjoSQbPq/mm40US6XNINhD089VQ== X-Received: by 2002:a17:907:971a:b0:aa6:66eb:9c06 with SMTP id a640c23a62f3a-ab38b1e207bmr357684566b.5.1737142817126; Fri, 17 Jan 2025 11:40:17 -0800 (PST) X-Google-Smtp-Source: AGHT+IFosYdYhimjMiswhnSJuuOa2F6TozlgWSH1f1nUqSoanZQZX5mubtd8TH4IUPY4KsN3mmKDPg== X-Received: by 2002:a17:907:971a:b0:aa6:66eb:9c06 with SMTP id a640c23a62f3a-ab38b1e207bmr357682166b.5.1737142816563; Fri, 17 Jan 2025 11:40:16 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab384ce1bacsm220451766b.53.2025.01.17.11.40.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:16 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 05/10] rust: qdev: add clock creation Date: Fri, 17 Jan 2025 20:39:58 +0100 Message-ID: <20250117194003.1173231-6-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -39 X-Spam_score: -4.0 X-Spam_bar: ---- X-Spam_report: (-4.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-1.79, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 a Rust version of qdev_init_clock_in, which can be used in instance_init. There are a couple differences with the C version: - in Rust the object keeps its own reference to the clock (in addition to the one embedded in the NamedClockList), and the reference is dropped automatically by instance_finalize(); this is encoded in the signature of DeviceClassMethods::init_clock_in, which makes the lifetime of the clock independent of that of the object it holds. This goes unnoticed in the C version and is due to the existence of aliases. - also, anything that happens during instance_init uses the pinned_init framework to operate on a partially initialized object, and is done through class methods (i.e. through DeviceClassMethods rather than DeviceMethods) because the device does not exist yet. Therefore, Rust code *must* create clocks from instance_init, which is stricter than C. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 28 +++++------- rust/qemu-api/src/prelude.rs | 2 + rust/qemu-api/src/qdev.rs | 76 ++++++++++++++++++++++++++++++-- rust/qemu-api/src/vmstate.rs | 4 +- 4 files changed, 87 insertions(+), 23 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index d8409f3d310..dfe199ad0ed 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,8 +13,8 @@ c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, - qdev::DeviceImpl, - qom::{ClassInitImpl, ObjectImpl, ParentField}, + qdev::{Clock, ClockEvent, DeviceImpl}, + qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, }; use crate::{ @@ -123,7 +123,7 @@ pub struct PL011State { #[doc(alias = "irq")] pub interrupts: [InterruptSource; IRQMASK.len()], #[doc(alias = "clk")] - pub clock: NonNull, + pub clock: Owned, #[doc(alias = "migrate_clk")] pub migrate_clock: bool, } @@ -487,8 +487,6 @@ impl PL011State { /// location/instance. All its fields are expected to hold unitialized /// values with the sole exception of `parent_obj`. unsafe fn init(&mut self) { - const CLK_NAME: &CStr = c_str!("clk"); - // SAFETY: // // self and self.iomem are guaranteed to be valid at this point since callers @@ -508,22 +506,16 @@ unsafe fn init(&mut self) { // SAFETY: // - // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, - // we can overwrite the undefined value without side effects. This is + // self.clock is not initialized at this point; but since `NonNull<_>` is + // not Drop, we can overwrite the undefined value without side effects. This is // safe since all PL011State instances are created by QOM code which // calls this function to initialize the fields; therefore no code is // able to access an invalid self.clock value. - unsafe { - let dev: &mut DeviceState = self.upcast_mut(); - self.clock = NonNull::new(qdev_init_clock_in( - dev, - CLK_NAME.as_ptr(), - None, /* pl011_clock_update */ - addr_of_mut!(*self).cast::(), - ClockEvent::ClockUpdate.0, - )) - .unwrap(); - } + self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate); + } + + const fn clock_update(&self, _event: ClockEvent) { + /* pl011_trace_baudrate_change(s); */ } fn post_init(&self) { diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 3df6a5c21ec..87e3ce90f26 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -7,6 +7,8 @@ pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; +pub use crate::qdev::DeviceMethods; + pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 4658409bebb..f573712550e 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -4,14 +4,19 @@ //! Bindings to create devices and access device functionality from Rust. -use std::ffi::CStr; +use std::{ + ffi::{CStr, CString}, + os::raw::c_void, +}; -pub use bindings::{DeviceClass, DeviceState, Property}; +pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property}; use crate::{ bindings::{self, Error}, + callbacks::FnCall, + cell::bql_locked, prelude::*, - qom::{ClassInitImpl, ObjectClass}, + qom::{ClassInitImpl, ObjectClass, Owned}, vmstate::VMStateDescription, }; @@ -145,3 +150,68 @@ unsafe impl ObjectType for DeviceState { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } qom_isa!(DeviceState: Object); + +pub trait DeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ + #[inline] + fn init_clock_in FnCall<(&'a Self::Target, ClockEvent)>>( + &self, + name: &str, + _cb: &F, + events: ClockEvent, + ) -> Owned { + fn do_init_clock_in( + dev: *mut DeviceState, + name: &str, + cb: Option, + events: ClockEvent, + ) -> Owned { + assert!(bql_locked()); + + // SAFETY: the clock is heap allocated and does not have a reference, so + // Owned::from adds one. the callback is disabled automatically + // when the clock is unparented, which happens before the device is + // finalized. + unsafe { + let cstr = CString::new(name).unwrap(); + let clk = bindings::qdev_init_clock_in( + dev, + cstr.as_ptr(), + cb, + dev.cast::(), + events.0, + ); + + Owned::from(&*clk) + } + } + + let cb: Option = if F::is_some() { + unsafe extern "C" fn rust_clock_cb FnCall<(&'a T, ClockEvent)>>( + opaque: *mut c_void, + event: ClockEvent, + ) { + // SAFETY: the opaque is "this", which is indeed a pointer to T + F::call((unsafe { &*(opaque.cast::()) }, event)) + } + Some(rust_clock_cb::) + } else { + None + }; + + // SAFETY: self can be cast to DeviceState because its type has an + // IsA bound. + do_init_clock_in(unsafe { self.as_mut_ptr() }, name, cb, events) + } +} + +impl DeviceMethods for R where R::Target: IsA {} + +unsafe impl ObjectType for Clock { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; +} +qom_isa!(Clock: Object); diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 11d21b8791c..164effc6553 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -470,11 +470,11 @@ macro_rules! vmstate_clock { $crate::assert_field_type!( $struct_name, $field_name, - core::ptr::NonNull<$crate::bindings::Clock> + $crate::qom::Owned<$crate::bindings::Clock> ); $crate::offset_of!($struct_name, $field_name) }, - size: ::core::mem::size_of::<*const $crate::bindings::Clock>(), + size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, ..$crate::zeroable::Zeroable::ZERO From patchwork Fri Jan 17 19:39:59 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943947 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 D3D4AC02185 for ; Fri, 17 Jan 2025 19:42:08 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCr-0001cK-57; Fri, 17 Jan 2025 14:40:37 -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 1tYsCp-0001ae-3n for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:35 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCg-0002DJ-JQ for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:29 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142825; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tB+0wLdGKfYjOO1CK7DKuiSRMJWw6EIiF2Kmen84UhQ=; b=Ud+5pwq8/f0pK9z3CJdpY8OtXhpTlnqb10gKE7WQuSyZmaney4YXZgaZzEcJLh54is2eun ikTrg/6m2Qou4X+rocviVQMo4/kAs+VcTVCBj32Coq1xHcCWw4H/put9B0pKiJVng0ARSW iFEtUiMxK2xlM/dokfzMTDqEjeCgAnM= Received: from mail-ed1-f69.google.com (mail-ed1-f69.google.com [209.85.208.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-338-OjsqiqEPPWKxEvtnav0Bjg-1; Fri, 17 Jan 2025 14:40:23 -0500 X-MC-Unique: OjsqiqEPPWKxEvtnav0Bjg-1 X-Mimecast-MFC-AGG-ID: OjsqiqEPPWKxEvtnav0Bjg Received: by mail-ed1-f69.google.com with SMTP id 4fb4d7f45d1cf-5d89a53fc46so3165208a12.2 for ; Fri, 17 Jan 2025 11:40:23 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142821; x=1737747621; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=tB+0wLdGKfYjOO1CK7DKuiSRMJWw6EIiF2Kmen84UhQ=; b=PjvZHXz/DT77Go0nsLDLJO09GR+qg4mG10EP8NQ69ate1A9OZroWPW9MnUg5I8S9Eh GtkMiIpfQJekHgq1InEwTwHj8IlAzbKXQD0PapXeCJj1huch5oKPXNIDenhAdYYeFe8E ONHjgsfM1YsgNokBYRo60eJ5jRhmPRCoi/fjeXzAXtWrDiSbLdsUxwvDaiF1YCBgLpvL XzQQOAbeEGpKe0Y+8BrUuzDLmcbg4tVxDy4BCH9ieqGi8NaczBSmAgW6k8hBxW+KOR8c 7NGuUnavWXpcVZtPo1OPL1eFkO6xS8Wq3wgjbio1TBbOVOD/agC7RyGAmxQPe/3aJopg mDYA== X-Gm-Message-State: AOJu0Ywo3tYW/PrqRHOiS8yTpXmo7hikbOgh92Hl4rN5BA96puOI2A5u OhMh8sIhNvo2MakrmDFJZQCAsh3caWs5N907m43vzSz8n/E38ZX+FRlGo4WJDegCRI7WmrPYl6K nlum+jhe4SM2FX3yMRgsxr+Eb7/W9bYYBxlkWHLwlRlovTz/slQcZg3uYguRkGYktycX2K0lu71 tC1qRrtfqGdFKo+H8LX9KYfFTtIsGZZrX7OsLPKw8= X-Gm-Gg: ASbGnctPtsAsTxriqlGE6So+Bzfk9uNzEKP2pQVMh+fDkMQXb9DEPQkkvCHCPnBhXUD gibR69KFwsZW//rEpUt+M2s/c021RQvmkY9C/C5YpybFuh3tR9l186jz4Nxry8OK0/PzLETxO21 McJp2WLYGaRgu0xu6opsA+m/GR++lmKnHmfLrfMdymOv0lnSbsTdiOnJh2OF0hM46REGietFUfq sypvLWXYl92PRiCGaGhfR9f7ugH0Is7qRpJaNPx/3lVxw2MXVUiVPBwJ5ZJEg== X-Received: by 2002:a05:6402:2342:b0:5db:67a7:e742 with SMTP id 4fb4d7f45d1cf-5db7d2f947dmr3591163a12.8.1737142821022; Fri, 17 Jan 2025 11:40:21 -0800 (PST) X-Google-Smtp-Source: AGHT+IHbHfDaPZvPVis45W8G4jtQRMfhXqK0bY0Sa1dU3kx3iXc5jJ4dYg7FVyd2EaIFIN225cnciQ== X-Received: by 2002:a05:6402:2342:b0:5db:67a7:e742 with SMTP id 4fb4d7f45d1cf-5db7d2f947dmr3591134a12.8.1737142820549; Fri, 17 Jan 2025 11:40:20 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5db73642580sm1910393a12.15.2025.01.17.11.40.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:17 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 06/10] rust: qom: allow initializing interface vtables Date: Fri, 17 Jan 2025 20:39:59 +0100 Message-ID: <20250117194003.1173231-7-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -39 X-Spam_score: -4.0 X-Spam_bar: ---- X-Spam_report: (-4.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-1.79, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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 Unlike regular classes, interface vtables can only be obtained via object_class_dynamic_cast. Provide a wrapper that allows accessing the vtable and pass it to a ClassInitImpl implementation, for example ClassInitImpl. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/prelude.rs | 1 + rust/qemu-api/src/qom.rs | 45 ++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 87e3ce90f26..254edb476dd 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -9,6 +9,7 @@ pub use crate::qdev::DeviceMethods; +pub use crate::qom::InterfaceType; pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 6f7db3eabcb..8b2dbb3919c 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -66,8 +66,8 @@ use crate::{ bindings::{ - self, object_dynamic_cast, object_get_class, object_get_typename, object_new, object_ref, - object_unref, TypeInfo, + self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, + object_get_typename, object_new, object_ref, object_unref, TypeInfo, }, cell::bql_locked, }; @@ -256,6 +256,47 @@ unsafe fn as_object_mut_ptr(&self) -> *mut Object { } } +/// Trait exposed by all structs corresponding to QOM interfaces. +/// Unlike `ObjectType`, it is implemented on the class type (which provides +/// the vtable for the interfaces). +/// +/// # Safety +/// +/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; +/// right now, interfaces can only be declared in C. +pub unsafe trait InterfaceType: Sized { + /// The name of the type, which can be passed to + /// `object_class_dynamic_cast()` to obtain the pointer to the vtable + /// for this interface. + const TYPE_NAME: &'static CStr; + + /// Initialize the vtable for the interface; the generic argument `T` is the + /// type being initialized, while the generic argument `U` is the type that + /// lists the interface in its `TypeInfo`. + /// + /// # Panics + /// + /// Panic if the incoming argument if `T` does not implement the interface. + fn interface_init< + T: ObjectType + ClassInitImpl + ClassInitImpl, + U: ObjectType, + >( + klass: &mut U::Class, + ) { + unsafe { + // SAFETY: upcasting to ObjectClass is always valid, and the + // return type is either NULL or the argument itself + let result: *mut Self = object_class_dynamic_cast( + (klass as *mut U::Class).cast(), + Self::TYPE_NAME.as_ptr(), + ) + .cast(); + + >::class_init(result.as_mut().unwrap()) + } + } +} + /// This trait provides safe casting operations for QOM objects to raw pointers, /// to be used for example for FFI. The trait can be applied to any kind of /// reference or smart pointers, and enforces correctness through the [`IsA`] From patchwork Fri Jan 17 19:40:00 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943945 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 C1695C02183 for ; Fri, 17 Jan 2025 19:42:01 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCq-0001bt-L7; Fri, 17 Jan 2025 14:40: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 1tYsCp-0001af-44 for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:35 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCh-0002DP-8R for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:29 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142826; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=A81qJn2EL6E/QyMc/gOAoSx6A9iJTPC6iOP+Z5fufcI=; b=QSLF7rqyjU2wKC1h3Fujf/HnzZrbJXwsjV4ogrJ5CjcHqEoHp5RwPfFjbELOFlSqCQP1k7 pU+PUMAHFhy+01swIz9FqbMYaQg7adw5g58AG8xOx2lisrOzmo3gZywJGm6AVTPUHwWYyq euifvoNqhJ9cUy2yrPVyv8gAsYn2wRM= Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-183-mU27WBnFNkSrN1kJjUUbuA-1; Fri, 17 Jan 2025 14:40:25 -0500 X-MC-Unique: mU27WBnFNkSrN1kJjUUbuA-1 X-Mimecast-MFC-AGG-ID: mU27WBnFNkSrN1kJjUUbuA Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-aa68952272bso289582766b.2 for ; Fri, 17 Jan 2025 11:40:25 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142823; x=1737747623; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=A81qJn2EL6E/QyMc/gOAoSx6A9iJTPC6iOP+Z5fufcI=; b=JhlgucIt2OaL288MfoT/1L476tISzPWd6B1ERTl+YIOeBPWpw62WtGWlhHw+mbnHmC PeXTNpat39VmZq0XpUxe+YDzY07iT7nk+nW226JKnGbSribDjLwWHy5bv1ATgQroacMc ypAz2uHdLyi4T6BKxUx+/5wLY4Nf/xSqJwFtONQKOF7v8oqLObVSkvGazl9WxKm+m4Q6 2HxifAg1HNyOqLPO1fm16IoczEV+Enx0OPDT8zLZqpcq74KC1iehKqEqG3+AonaQilii mNik0L9PDpfYTcPRUZ2tLwTj8WxxtCXZdLcq6JYBfSNmNSegg7xbHGWSEVOC6dtrSSYk 9AHg== X-Gm-Message-State: AOJu0Yy95z6/0JSEgHwP4VYdNgE+9HfCljmV/fW1qfxkwZqUAF6GS3c0 /L369aM9fPeEvH2kVET27kcOoYQuYVj+58ibfrrKv0HDsJ48Y4WS27GDkt9n6L3gU4r2zjGGOOi CyG57DhR42sK7OWXT3Q52F6vn+Pz6AleY4w5dYWJ6bFDg3MrG9EeWEqEaDivyRMlpPYUriZ+ksB v1WissMrTZWhR872lVpsTDkQQtXa1EXSDDD9SltRM= X-Gm-Gg: ASbGnctgtPqZu+qx3bPv1USIO9mj24m3UDujqb2lE3rXCA/UveYi8lsqeiCpn2uQhgU OFoglJAdTB5TMdv0DUi5o4mkQAodHQNrCFktYSqxXEU/trXatvP+DmirwVK5lxHxy877cJ1Y/Op MXIl13PkSr5RbGKmAYYc+andg1ToyEIKcv5l7/RaFUtL+ITznmtECWv+bKIYhccCUE+NdXzaTah 1u74Vq3aZGeZIGAvW2IUDVlLAI1CcHAN8/QniQ46S4s6wVAeodD2gInXASf6g== X-Received: by 2002:a17:907:9802:b0:aae:8491:bab5 with SMTP id a640c23a62f3a-ab38b30e1admr345573766b.26.1737142823242; Fri, 17 Jan 2025 11:40:23 -0800 (PST) X-Google-Smtp-Source: AGHT+IG2DRY5RAxoi0USi7TxLjP6LuLwhm7HLPfDLog2RFrIdUOus5b4CwuEYkSzedMlE9HVgpG5AA== X-Received: by 2002:a17:907:9802:b0:aae:8491:bab5 with SMTP id a640c23a62f3a-ab38b30e1admr345571766b.26.1737142822809; Fri, 17 Jan 2025 11:40:22 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab3848b2047sm218476466b.0.2025.01.17.11.40.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:22 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 07/10] rust: qdev: make ObjectImpl a supertrait of DeviceImpl Date: Fri, 17 Jan 2025 20:40:00 +0100 Message-ID: <20250117194003.1173231-8-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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 In practice it has to be implemented always in order to access an implementation of ClassInitImpl. Make the relationship explicit in the code. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qdev.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index f573712550e..ab883e2faef 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -21,7 +21,7 @@ }; /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl { +pub trait DeviceImpl: ObjectImpl { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). From patchwork Fri Jan 17 19:40:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943944 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 D1B09C02183 for ; Fri, 17 Jan 2025 19:41:49 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCu-0001fv-P0; Fri, 17 Jan 2025 14:40: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 1tYsCs-0001dF-8D for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:38 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCo-0002Du-QQ for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:37 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142830; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/bcokY2+woAr9GlmKaOrfVb07DGACuR0xqvBoQA4nFo=; b=PoCtytuXKUpXqJNnKuqX/AssSOt7MJtczyE5gGi5lrr+ERvhEFWgzG3Rg0CBXwvm6cthf8 1as6leDik7eY925JkQc5YYZ0IoZAgaXWumYhdrC6LbrH9tOej1HwiHUbT/Wjb+gxjh+KnK gfWSL1aONIbbQfo1K93jiE/j8+D1dfo= Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-595-h0RFQL9eMGyqfgjf9NSEwA-1; Fri, 17 Jan 2025 14:40:28 -0500 X-MC-Unique: h0RFQL9eMGyqfgjf9NSEwA-1 X-Mimecast-MFC-AGG-ID: h0RFQL9eMGyqfgjf9NSEwA Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-aa67855b3deso229131766b.1 for ; Fri, 17 Jan 2025 11:40:28 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142826; x=1737747626; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=/bcokY2+woAr9GlmKaOrfVb07DGACuR0xqvBoQA4nFo=; b=VKedgC8lbG5ii8AFgKcXxg2w/YENfIDr70I2B0dIfdXcNRynYqunlTgNhcyvlRAtFt /7VNsPVH+Rj/5KwVDCKMqQxgZ2sRS3IgRH1wG6cBqBLju8lqWTdqNzec13X+5Wh2z/0A dZq2PA8GTe9IwlYEUSsIiNWhuuRLdv1FM9/oWhjHrfoCWJgnF9O6JGa2EVwAR5O4FQMi 0VB2nq3IFYOHev/deu31IyuCMv7v/wUTxuAgV9qaQbkIw9GTUL6NEREvGC9wU/XKYYK/ juRipa1vZ90N55ZrLj5UQ6wuuoalMHrrhPQDGyTW021CxVR36F6mraMnUCEiTXpCj9ST +nHg== X-Gm-Message-State: AOJu0YySJwWBMtSw6JvifQVkXgaaoiAicjdY6IemI4VyHXMERJgDJf4C bv9jwsQAkPfBouvtdnQV+7r1k+C4VMzkCJck6DynQ8GmVZ8K+oIOEUsa71CRxw7x9oI3fazXKGL e/83yM4pOSAZYZSTerZgAMUAmJL8A/BBTWJqGt78HO15XpGQyCACqOxd+nHV03EyhSY6ZDCvZN2 jtO/sRoq0vFU9xTdpH8Ij3NdHtOzfVY1wOIGL+OjA= X-Gm-Gg: ASbGncsv0cZU323T6668DY+w8XQt6OpmUQMKRXI1jQrFIB8yjEOoAisRnpQDPoDMROb w7jK3KKcIdOtLxOo+IDEckJM7wlPxLwq+rAsI1HwkCV1yul+1CbB9hS4GXA9DDFlaGn/WuMHv0t smRJ8T4yZOw4qmtIkSQ8roLyLcBIkcJeVAH/cG93p0Ir/E0iHN8p3WI3i+9Y+1pWPNTSsN9RrCI ezrtXWrhFPvetIu/Iod362kPd6xvQlRcHCFcJaVEOHp1bG0OzIwcTzehanjbg== X-Received: by 2002:a17:907:6d17:b0:aab:f8e8:53ce with SMTP id a640c23a62f3a-ab38b191b7amr406421466b.11.1737142825834; Fri, 17 Jan 2025 11:40:25 -0800 (PST) X-Google-Smtp-Source: AGHT+IGDN8ziIeDECHBOQB5Iy4qolTVUmxdj1Q7ykud1FPn+USOIuSaz+o2LlCHThwP3Mgba5PVONg== X-Received: by 2002:a17:907:6d17:b0:aab:f8e8:53ce with SMTP id a640c23a62f3a-ab38b191b7amr406418166b.11.1737142825274; Fri, 17 Jan 2025 11:40:25 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab384c5ad98sm218413666b.28.2025.01.17.11.40.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:24 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 08/10] rust: qdev: switch from legacy reset to Resettable Date: Fri, 17 Jan 2025 20:40:01 +0100 Message-ID: <20250117194003.1173231-9-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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 Signed-off-by: Paolo Bonzini --- meson.build | 1 + rust/hw/char/pl011/src/device.rs | 10 ++- rust/qemu-api/src/qdev.rs | 116 ++++++++++++++++++++++++------- rust/qemu-api/tests/tests.rs | 5 +- 4 files changed, 102 insertions(+), 30 deletions(-) diff --git a/meson.build b/meson.build index d06f59095c6..2d9fa87af83 100644 --- a/meson.build +++ b/meson.build @@ -4065,6 +4065,7 @@ if have_rust 'MigrationPriority', 'QEMUChrEvent', 'QEMUClockType', + 'ResetType', 'device_endian', 'module_init_type', ] diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index dfe199ad0ed..259efacb046 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,7 +13,7 @@ c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, - qdev::{Clock, ClockEvent, DeviceImpl}, + qdev::{Clock, ClockEvent, DeviceImpl, ResettablePhasesImpl, ResetType}, qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, }; @@ -164,7 +164,10 @@ fn vmsd() -> Option<&'static VMStateDescription> { Some(&device_class::VMSTATE_PL011) } const REALIZE: Option = Some(Self::realize); - const RESET: Option = Some(Self::reset); +} + +impl ResettablePhasesImpl for PL011State { + const HOLD: Option = Some(Self::reset_hold); } impl PL011Registers { @@ -601,7 +604,7 @@ pub fn realize(&mut self) { } } - pub fn reset(&self) { + pub fn reset_hold(&self, _type: ResetType) { self.regs.borrow_mut().reset(); } @@ -723,3 +726,4 @@ impl ObjectImpl for PL011Luminary { } impl DeviceImpl for PL011Luminary {} +impl ResettablePhasesImpl for PL011Luminary {} diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index ab883e2faef..8f0b279003a 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -9,19 +9,83 @@ os::raw::c_void, }; -pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property}; +pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType}; use crate::{ - bindings::{self, Error}, + bindings::{self, Error, ResettableClass}, callbacks::FnCall, cell::bql_locked, prelude::*, - qom::{ClassInitImpl, ObjectClass, Owned}, + qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, }; +/// Trait providing the contents of [`ResettablePhases`]. +pub trait ResettablePhasesImpl { + /// If not None, this is called when the object enters reset. It + /// can reset local state of the object, but it must not do anything that + /// has a side-effect on other objects, such as raising or lowering a + /// [`qemu_irq`] line or reading or writing guest memory. It takes the + /// reset's type as argument. + const ENTER: Option = None; + + /// If not None, this is called when the object for entry into reset, once + /// every object in the system which is being reset has had its + /// @phases.enter method called. At this point devices can do actions + /// that affect other objects. + /// + /// If in doubt, implement this method. + const HOLD: Option = None; + + /// If not None, this phase is called when the object leaves the reset + /// state. Actions affecting other objects are permitted. + const EXIT: Option = None; +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_enter_fn( + obj: *mut Object, + typ: ResetType, +) { + assert!(!obj.is_null()); + let state = obj.cast::(); + T::ENTER.unwrap()(unsafe { &mut *state }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_hold_fn( + obj: *mut Object, + typ: ResetType, +) { + assert!(!obj.is_null()); + let state = obj.cast::(); + T::HOLD.unwrap()(unsafe { &mut *state }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_exit_fn( + obj: *mut Object, + typ: ResetType, +) { + assert!(!obj.is_null()); + let state = obj.cast::(); + T::EXIT.unwrap()(unsafe { &mut *state }, typ); +} + /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: ObjectImpl { +pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). @@ -30,13 +94,6 @@ pub trait DeviceImpl: ObjectImpl { /// with the function pointed to by `REALIZE`. const REALIZE: Option = None; - /// If not `None`, the parent class's `reset` method is overridden - /// with the function pointed to by `RESET`. - /// - /// Rust does not yet support the three-phase reset protocol; this is - /// usually okay for leaf classes. - const RESET: Option = None; - /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants /// is unstable until Rust 1.83.0. @@ -65,30 +122,36 @@ fn vmsd() -> Option<&'static VMStateDescription> { T::REALIZE.unwrap()(unsafe { &mut *state }); } -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_reset_fn(dev: *mut DeviceState) { - assert!(!dev.is_null()); - let state = dev.cast::(); - T::RESET.unwrap()(unsafe { &mut *state }); +unsafe impl InterfaceType for ResettableClass { + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; +} + +impl ClassInitImpl for T +where + T: ResettablePhasesImpl, +{ + fn class_init(rc: &mut ResettableClass) { + if ::ENTER.is_some() { + rc.phases.enter = Some(rust_resettable_enter_fn::); + } + if ::HOLD.is_some() { + rc.phases.hold = Some(rust_resettable_hold_fn::); + } + if ::EXIT.is_some() { + rc.phases.exit = Some(rust_resettable_exit_fn::); + } + } } impl ClassInitImpl for T where - T: ClassInitImpl + DeviceImpl, + T: ClassInitImpl + ClassInitImpl + DeviceImpl, { fn class_init(dc: &mut DeviceClass) { if ::REALIZE.is_some() { dc.realize = Some(rust_realize_fn::); } - if ::RESET.is_some() { - unsafe { - bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::)); - } - } if let Some(vmsd) = ::vmsd() { dc.vmsd = vmsd; } @@ -99,6 +162,7 @@ fn class_init(dc: &mut DeviceClass) { } } + ResettableClass::interface_init::(dc); >::class_init(&mut dc.parent_class); } } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 1944e65c8f3..178041ffaae 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -13,7 +13,7 @@ cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState, Property}, + qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, ParentField}, vmstate::VMStateDescription, zeroable::Zeroable, @@ -61,6 +61,8 @@ impl ObjectImpl for DummyState { const ABSTRACT: bool = false; } +impl ResettablePhasesImpl for DummyState {} + impl DeviceImpl for DummyState { fn properties() -> &'static [Property] { &DUMMY_PROPERTIES @@ -101,6 +103,7 @@ impl ObjectImpl for DummyChildState { const ABSTRACT: bool = false; } +impl ResettablePhasesImpl for DummyChildState {} impl DeviceImpl for DummyChildState {} impl ClassInitImpl for DummyChildState { From patchwork Fri Jan 17 19:40:02 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943949 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 24C7BC02183 for ; Fri, 17 Jan 2025 19:42:22 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCs-0001dV-GU; Fri, 17 Jan 2025 14:40:38 -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 1tYsCr-0001ca-Ix for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:37 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCo-0002Dy-Pu for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:37 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142831; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QsBnnC+UEglMSUQPR072ZGcFxRcTecgJCzaWCd7nUBI=; b=PGtV3oAR7sfEEURqhQLy2FJtu2YiWcLc6mKtaVn4AbQvfgdk2XB5CMU/jAmLSXufdEFhMw Tagrp2LlGqs9gFzCM3saOapoFypoy67g5pR2RfbhcLVXUKVvMjGigWex/ZT2KE+jUJQjQK r8gJyc3/gVR8KX5IdKpzHKCoq+bFg6I= Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-653-Z5s4_zLoOBaWtnYWIM4Xyw-1; Fri, 17 Jan 2025 14:40:29 -0500 X-MC-Unique: Z5s4_zLoOBaWtnYWIM4Xyw-1 X-Mimecast-MFC-AGG-ID: Z5s4_zLoOBaWtnYWIM4Xyw Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-aa68fd5393cso323188966b.0 for ; Fri, 17 Jan 2025 11:40:29 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142827; x=1737747627; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QsBnnC+UEglMSUQPR072ZGcFxRcTecgJCzaWCd7nUBI=; b=t3+gL5nWTvakN/6S3JntnqsI3Ojq9YjBW+/2U5CE2mTebZTgOSzy3+O+7f/ySMYEEy UYE0YikWXVcflqTS9s6j6Tkbpx2YF5cyHlHM27d4JIE5PY1KOndtSVNSOucT/o4hTgVG i4oXdkad9OpDLn8R1gkAMa+a3rodzR70pf+qNr5J6TLfBzGinEQLO8oVk0ZeUTRkIkpj PGYV76v5ilXWtsLKYbPMi4R1CDpGIJ5P41PwhSQhOwKb0AwJzaDmn1NcrOpsiIg6wPu8 5BMQPZ9qM0ieN9CjvhB1r67QU3ehfY/mYZdhVxA5Tqhtzg2KTITNR1g0P22BPN7lyClI +gNw== X-Gm-Message-State: AOJu0YyWKVlHq4WG5bVVyNLQjjr+5LEDMLhp0Mgkd2BsQoqCnC6uVfWK hTHAput6ph4htx+AkOtz4znFtJP36i39WhHg0Ds4EOUruC0JktZvjHC9cO9haSLATafRLxTmVvy uSo+740KDzt9JD15TQs5OyeJTdqIMR0aaIM6k6S7ZR06B0AJCfeSmqYH9bUnKasnZh3rwvu2ZcV vLgM+lWzQH5icVekRxtC7n3PtPfN+e1gJQk7zV+0k= X-Gm-Gg: ASbGncuVcj+wMN4mXi82zIK5l3uTrV5I0D5RQGZN+24vrQ03q2+TgWSR8OaaHbPzvmX IJzTsED/6kS2WjbayN+B+aBHFoBWDjcYAYD1ZdJIsVM38i+OfhQjl2+Fd+3UkB2UqJiOfxWPgGt e3zAit33MMWjzY6fqOTIs70YO3IK3ZwcI9EM9//Gx6JaiHhT/utJe53O5TyZijy8ovMTMNPMmww fJSp2P5DkNHS88bKK5vtkTUfTB0YyGooBpU0/PnHcvo4lM9kdxisEUAuBD7dA== X-Received: by 2002:a17:906:99c2:b0:ab2:faed:fad5 with SMTP id a640c23a62f3a-ab38cc8f4afmr295071966b.15.1737142827096; Fri, 17 Jan 2025 11:40:27 -0800 (PST) X-Google-Smtp-Source: AGHT+IHxdrZHqpDe6C9QrkUsgwubTEYk4OqEheVEYW7WoWLeoplqUR4CKOTcrdanHRbuMe6isWiOGg== X-Received: by 2002:a17:906:99c2:b0:ab2:faed:fad5 with SMTP id a640c23a62f3a-ab38cc8f4afmr295069966b.15.1737142826671; Fri, 17 Jan 2025 11:40:26 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab384c57034sm218647366b.34.2025.01.17.11.40.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:26 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 09/10] rust: bindings: add Sync markers to types referred to by MemoryRegionOps Date: Fri, 17 Jan 2025 20:40:02 +0100 Message-ID: <20250117194003.1173231-10-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 This is needed for the MemoryRegionOps to be declared as static; Rust requires static elements to be Sync. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 19 ++++++++++++++++++- rust/qemu-api/src/irq.rs | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 8a9b821bb91..3f61264ab80 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -21,7 +21,24 @@ #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); -unsafe impl Send for Property {} +// SAFETY: these are implemented in C; the bindings need to assert that the +// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. +unsafe impl Sync for BusState {} +unsafe impl Sync for CharBackend {} +unsafe impl Sync for Chardev {} +unsafe impl Sync for Clock {} +unsafe impl Sync for DeviceState {} +unsafe impl Sync for MemoryRegion {} +unsafe impl Sync for ObjectClass {} +unsafe impl Sync for Object {} +unsafe impl Sync for SysBusDevice {} + +// SAFETY: this is a pure data struct +unsafe impl Sync for CoalescedMemoryRange {} + +// SAFETY: these are constants and vtables; the Sync requirements are deferred +// to the unsafe callbacks that they contain +unsafe impl Sync for MemoryRegionOps {} unsafe impl Sync for Property {} unsafe impl Sync for TypeInfo {} unsafe impl Sync for VMStateDescription {} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 378e5202951..638545c3a64 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -43,6 +43,9 @@ pub struct InterruptSource _marker: PhantomData, } +// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken +unsafe impl Sync for InterruptSource where c_int: From {} + impl InterruptSource { /// Send a low (`false`) value to the interrupt sink. pub fn lower(&self) { From patchwork Fri Jan 17 19:40:03 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13943943 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 A8152C02183 for ; Fri, 17 Jan 2025 19:41:45 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tYsCu-0001fr-FK; Fri, 17 Jan 2025 14:40: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 1tYsCs-0001dM-Cz for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:38 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tYsCo-0002EC-Oe for qemu-devel@nongnu.org; Fri, 17 Jan 2025 14:40:38 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1737142833; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bkTyh20ofHIoBA2u9V9N3EE8+dgrUhUhrAuzIR0U41E=; b=fdqdSF69/ZPK65nFt4MS6kXrc9JCJwHoKnnDUYDJJMK/ywFeicZWx437YfwUjfTWuQR21z CKG2YLGaUZcWnoFUesrgDr1tGicwQC9Y9kA2+dy9f9Mu7zCD6gfTg6hzIFhEL0m8XZU8/I Erxtzmbs8XX9vB6c9rCNy5kGTeY07hc= Received: from mail-ed1-f72.google.com (mail-ed1-f72.google.com [209.85.208.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-65-9lxWprf-MKaU97StrL8t8A-1; Fri, 17 Jan 2025 14:40:32 -0500 X-MC-Unique: 9lxWprf-MKaU97StrL8t8A-1 X-Mimecast-MFC-AGG-ID: 9lxWprf-MKaU97StrL8t8A Received: by mail-ed1-f72.google.com with SMTP id 4fb4d7f45d1cf-5d0b5036394so3418031a12.3 for ; Fri, 17 Jan 2025 11:40:32 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737142830; x=1737747630; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bkTyh20ofHIoBA2u9V9N3EE8+dgrUhUhrAuzIR0U41E=; b=sSyLpiWD7JjubT74E+mZ/DA/C5wOLqhHnH9J9s8FnfsSr12hFx0LFq4lPlMmxjqUFN 5419qGLkcM9l3jB5u7DNDv+xtKiwlmXjmoqqkz/ADKv5o1tUSiYcIQhbsfjEC0/tnmeU 1DTrjb0if//g+OuYKyB38Lm2LLhyQmsGkNCmMlEJOTnDfTLwaX7swMOAcilHwHK3qedW s3MCS4eylV3KDqiFk5jItoIS9bEiJ52LeY4yhIXqpM2scB1nryaIQ941ynZi0H82RuDH QKlTdN8C6bw9/WnOfFZvSfFXpNrz2jVEj0KzgPV9zonaNwcucdsHb+Y66FCYdRnQTFLm YH3A== X-Gm-Message-State: AOJu0YxD766+CtxAaAZtFZFDJjBiaIXfsYGi0Mef6Vf/0QzljbV0fraR FJhzAnOQLGIaf5+CeYoHzRgQusK+oudw0gvTlJGek52U6B0oHqywnqjiSm3yM0w0Wfy7v9fvVck Z75FiCnjZ4p2wyZnvq7sQFVEskXT8tyJcgAf2k/jmW+e61NcaTtmT972DVkqMVR192rvkOqbpXf u7EQIGKTrwAeVlGxyCLt5FB0Hsgl0U87Hy7NgUzgA= X-Gm-Gg: ASbGncvjG/C8ZvU0xLbMVnDBbNP7pe9EpuQEfdPCXv295s3yq0+8t2fiBJTa8Iut71h VTPMg+rS4vTY1ej+KA6GyOpghmhaHkZIj2HlW7rQ2v3fgp3e6wU0wz7ogCXIwPrLBGQshbtQU9H eFk5gomPhkwe894YivJWiWLumPvNnLHsBcFphJ+SVffSLpA74z4eEY5VB+yRolHSKCv9sJergNm 2e7sBGMzIQ4L5lFoXXJ+KPYIEAOyy02m8+mpsCm8DX6rajo1y0cb3CGVn1oIQ== X-Received: by 2002:a05:6402:34ca:b0:5d9:82bc:ad06 with SMTP id 4fb4d7f45d1cf-5db7d2d9b24mr3890788a12.3.1737142830322; Fri, 17 Jan 2025 11:40:30 -0800 (PST) X-Google-Smtp-Source: AGHT+IE8v/v1+hpk7RH2ofAQecmmr9x1RCkbbmIftXAxpm+Y5fIks36690JZ34lNPYgohLmLKTOo8Q== X-Received: by 2002:a05:6402:34ca:b0:5d9:82bc:ad06 with SMTP id 4fb4d7f45d1cf-5db7d2d9b24mr3890775a12.3.1737142829811; Fri, 17 Jan 2025 11:40:29 -0800 (PST) Received: from [192.168.10.47] ([176.206.124.70]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5db73642379sm1951266a12.8.2025.01.17.11.40.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jan 2025 11:40:28 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org Subject: [PATCH 10/10] rust: bindings for MemoryRegionOps Date: Fri, 17 Jan 2025 20:40:03 +0100 Message-ID: <20250117194003.1173231-11-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250117194003.1173231-1-pbonzini@redhat.com> References: <20250117194003.1173231-1-pbonzini@redhat.com> MIME-Version: 1.0 Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.132, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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 Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 43 +++--- rust/hw/char/pl011/src/lib.rs | 1 - rust/hw/char/pl011/src/memory_ops.rs | 36 ----- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/memory.rs | 191 +++++++++++++++++++++++++++ rust/qemu-api/src/sysbus.rs | 7 +- rust/qemu-api/src/zeroable.rs | 12 ++ 8 files changed, 234 insertions(+), 58 deletions(-) delete mode 100644 rust/hw/char/pl011/src/memory_ops.rs create mode 100644 rust/qemu-api/src/memory.rs diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 259efacb046..294394c6e82 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,7 +2,7 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of_mut, NonNull}; +use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, os::raw::{c_int, c_void}, @@ -12,14 +12,14 @@ bindings::{self, *}, c_str, impl_vmstate_forward, irq::InterruptSource, + memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, - qdev::{Clock, ClockEvent, DeviceImpl, ResettablePhasesImpl, ResetType}, + qdev::{Clock, ClockEvent, DeviceImpl, ResetType, ResettablePhasesImpl}, qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, }; use crate::{ device_class, - memory_ops::PL011_OPS, registers::{self, Interrupt}, RegisterOffset, }; @@ -490,20 +490,24 @@ impl PL011State { /// location/instance. All its fields are expected to hold unitialized /// values with the sole exception of `parent_obj`. unsafe fn init(&mut self) { + static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() + .read(&PL011State::read) + .write(&PL011State::write) + .native_endian() + .impl_sizes(4, 4) + .build(); + // 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::(), - &PL011_OPS, - addr_of_mut!(*self).cast::(), - Self::TYPE_NAME.as_ptr(), - 0x1000, - ); - } + MemoryRegion::init_io( + unsafe { &mut *addr_of_mut!(self.iomem) }, + addr_of_mut!(*self), + &PL011_OPS, + "pl011", + 0x1000, + ); self.regs = Default::default(); @@ -528,8 +532,7 @@ fn post_init(&self) { } } - #[allow(clippy::needless_pass_by_ref_mut)] - pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { + pub fn read(&self, offset: hwaddr, _size: u32) -> u64 { let (update_irq, result) = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { let device_id = self.get_class().device_id; @@ -544,16 +547,20 @@ pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { if update_irq { self.update(); unsafe { - qemu_chr_fe_accept_input(&mut self.char_backend); + qemu_chr_fe_accept_input(addr_of!(self.char_backend) as *mut _); } } result.into() } - pub fn write(&mut self, offset: hwaddr, value: u64) { + pub fn write(&self, offset: hwaddr, value: u64, _size: u32) { let mut update_irq = false; if let Ok(field) = RegisterOffset::try_from(offset) { - update_irq = self.regs.borrow_mut().write(field, value as u32, &mut self.char_backend); + update_irq = self.regs.borrow_mut().write( + field, + value as u32, + addr_of!(self.char_backend) as *mut _, + ); } else { eprintln!("write bad offset {offset} value {value}"); } diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 300c732ae1d..5622e974cbc 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -29,7 +29,6 @@ mod device; mod device_class; -mod memory_ops; pub use device::pl011_create; diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs deleted file mode 100644 index 95b4df794e4..00000000000 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use core::ptr::NonNull; -use std::os::raw::{c_uint, c_void}; - -use qemu_api::{bindings::*, zeroable::Zeroable}; - -use crate::device::PL011State; - -pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { - read: Some(pl011_read), - write: Some(pl011_write), - read_with_attrs: None, - write_with_attrs: None, - endianness: device_endian::DEVICE_NATIVE_ENDIAN, - valid: Zeroable::ZERO, - impl_: MemoryRegionOps__bindgen_ty_2 { - min_access_size: 4, - max_access_size: 4, - ..Zeroable::ZERO - }, -}; - -unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { - assert!(!opaque.is_null()); - let mut state = unsafe { NonNull::new_unchecked(opaque.cast::()) }; - unsafe { state.as_mut() }.read(addr, size) -} - -unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { - assert!(!opaque.is_null()); - let mut state = unsafe { NonNull::new_unchecked(opaque.cast::()) }; - unsafe { state.as_mut() }.write(addr, data); -} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 60944a657de..80eafc7f6bd 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -22,6 +22,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/c_str.rs', 'src/irq.rs', + 'src/memory.rs', 'src/module.rs', 'src/offset_of.rs', 'src/prelude.rs', diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 83c6a987c05..8cc095b13f6 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -18,6 +18,7 @@ pub mod callbacks; pub mod cell; pub mod irq; +pub mod memory; pub mod module; pub mod offset_of; pub mod qdev; diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs new file mode 100644 index 00000000000..963d689c27d --- /dev/null +++ b/rust/qemu-api/src/memory.rs @@ -0,0 +1,191 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for `MemoryRegion` and `MemoryRegionOps` + +use std::{ + ffi::{CStr, CString}, + marker::{PhantomData, PhantomPinned}, + os::raw::{c_uint, c_void}, + ptr::addr_of, +}; + +pub use bindings::hwaddr; + +use crate::{ + bindings::{self, device_endian, memory_region_init_io}, + callbacks::FnCall, + prelude::*, + zeroable::Zeroable, +}; + +pub struct MemoryRegionOps( + bindings::MemoryRegionOps, + // Note: quite often you'll see PhantomData mentioned when discussing + // covariance and contravariance; you don't need any of those to understand + // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* + // holds callbacks that take an argument of type &T, except the type is erased + // before the callback is stored in the bindings::MemoryRegionOps field. + // The argument of PhantomData is a function pointer in order to represent + // that relationship; while that will also provide desirable and safe variance + // for T, variance is not the point but just a consequence. + PhantomData, +); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for MemoryRegionOps {} + +#[derive(Clone)] +pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); + +unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( + opaque: *mut c_void, + addr: hwaddr, + size: c_uint, +) -> u64 { + F::call((unsafe { &*(opaque.cast::()) }, addr, size)) +} + +unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( + opaque: *mut c_void, + addr: hwaddr, + data: u64, + size: c_uint, +) { + F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) +} + +impl MemoryRegionOpsBuilder { + #[must_use] + pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { + self.0.read = Some(memory_region_ops_read_cb::); + self + } + + #[must_use] + pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { + self.0.write = Some(memory_region_ops_write_cb::); + self + } + + #[must_use] + pub const fn big_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; + self + } + + #[must_use] + pub const fn little_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; + self + } + + #[must_use] + pub const fn native_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; + self + } + + #[must_use] + pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { + self.0.valid.min_access_size = min; + self.0.valid.max_access_size = max; + self + } + + #[must_use] + pub const fn valid_unaligned(mut self) -> Self { + self.0.valid.unaligned = true; + self + } + + #[must_use] + pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { + self.0.impl_.min_access_size = min; + self.0.impl_.max_access_size = max; + self + } + + #[must_use] + pub const fn impl_unaligned(mut self) -> Self { + self.0.impl_.unaligned = true; + self + } + + #[must_use] + pub const fn build(self) -> MemoryRegionOps { + MemoryRegionOps::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::MemoryRegionOps::ZERO, PhantomData) + } +} + +impl Default for MemoryRegionOpsBuilder { + fn default() -> Self { + Self::new() + } +} + +/// A safe wrapper around [`bindings::MemoryRegion`]. Compared to the +/// underlying C struct it is marked as pinned because the QOM tree +/// contains a pointer to it. +pub struct MemoryRegion { + inner: bindings::MemoryRegion, + _pin: PhantomPinned, +} + +impl MemoryRegion { + // inline to ensure that it is not included in tests, which only + // link to hwcore and qom. FIXME: inlining is actually the opposite + // of what we want, since this is the type-erased version of the + // init_io function below. Look into splitting the qemu_api crate. + #[inline(always)] + unsafe fn do_init_io( + slot: *mut bindings::MemoryRegion, + owner: *mut Object, + ops: &'static bindings::MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + let cstr = CString::new(name).unwrap(); + memory_region_init_io( + slot, + owner.cast::(), + ops, + owner.cast::(), + cstr.as_ptr(), + size, + ); + } + } + + pub fn init_io>( + &mut self, + owner: *mut T, + ops: &'static MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + Self::do_init_io(&mut self.inner, owner.cast::(), &ops.0, name, size); + } + } + + pub(crate) const fn as_mut_ptr(&self) -> *mut bindings::MemoryRegion { + addr_of!(self.inner) as *mut _ + } +} + +unsafe impl ObjectType for MemoryRegion { + type Class = bindings::MemoryRegionClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; +} +qom_isa!(MemoryRegion: Object); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index e6762b5c145..c27dbf79e43 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -2,7 +2,7 @@ // Author(s): Paolo Bonzini // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, ptr::addr_of}; +use std::ffi::CStr; pub use bindings::{SysBusDevice, SysBusDeviceClass}; @@ -10,6 +10,7 @@ bindings, cell::bql_locked, irq::InterruptSource, + memory::MemoryRegion, prelude::*, qdev::{DeviceClass, DeviceState}, qom::ClassInitImpl, @@ -42,10 +43,10 @@ pub trait SysBusDeviceMethods: ObjectDeref /// important, since whoever creates the sysbus device will refer to the /// region with a number that corresponds to the order of calls to /// `init_mmio`. - fn init_mmio(&self, iomem: &bindings::MemoryRegion) { + fn init_mmio(&self, iomem: &MemoryRegion) { assert!(bql_locked()); unsafe { - bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _); + bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr()); } } diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs index 57cac96de06..0208381ee38 100644 --- a/rust/qemu-api/src/zeroable.rs +++ b/rust/qemu-api/src/zeroable.rs @@ -99,6 +99,18 @@ unsafe impl Zeroable for crate::bindings::VMStateDescription { }; } +unsafe impl Zeroable for crate::bindings::MemoryRegionOps { + const ZERO: Self = Self { + read: None, + write: None, + read_with_attrs: None, + write_with_attrs: None, + endianness: crate::bindings::device_endian::DEVICE_NATIVE_ENDIAN, + valid: Zeroable::ZERO, + impl_: Zeroable::ZERO, + }; +} + unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 { const ZERO: Self = Self { min_access_size: 0,