From patchwork Mon Jul 1 14:58:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718240 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 61D4EC41513 for ; Mon, 1 Jul 2024 15:00:22 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIUm-0003M7-Nv; Mon, 01 Jul 2024 10:59:08 -0400 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 1sOIUl-0003Ki-3J for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:07 -0400 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 1sOIUj-00052e-8O for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:06 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845943; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RHpEuKlw8QJPXOpD78MMN+7/lIJQeedbl+21kFAu6RE=; b=V3/8weawR4JSvZ2ANh/dLkipo1X3Qg8pxIXyR0vPBt9KQ1ZUjFizv6PsU1wTiG+vgppCZs qE3dP9Mu+U3RCYrefNlNK3xXnZgK21fgmp46phi9FVnmlxgsL75Pj/ghKe22AfiWnbHodP nDCAJ1KmASi3KK3GHbKx0YPHnVlrwOU= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-422-sWmV8dvEPlWZrndKNfmUyQ-1; Mon, 01 Jul 2024 10:59:02 -0400 X-MC-Unique: sWmV8dvEPlWZrndKNfmUyQ-1 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4258675a6easo5779695e9.3 for ; Mon, 01 Jul 2024 07:59:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845941; x=1720450741; 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=RHpEuKlw8QJPXOpD78MMN+7/lIJQeedbl+21kFAu6RE=; b=Rx5hcVpbkSMm0IHNs+OKZ9te6y552X9zhSQkS3iosStxkcp/spr6V5+09HIWwl2g/U ZA5DPDtGv4n8UMBxhM/Gdu8OFNFJiaJyUpAmcH2wzbozqeJF4/1OO0THDjsSLLqoJzOq IseyEljVQS6ESljWVm4ef3Q8JbSIdkyWyiCoOPsC9e2nlPkXb4tJqoKj5aj2SVMutN/t eQu+I+VOX80spLhGz44iHPWCpZW0iPZSH1YaXshH/a7oBVwtZ7LHN0Eu9vJ/TYg1RFoT 11pgrDkpTfYsfU6zrEyM7bWgSojxcAYaN6UmI4Xa8lsfqjSHdl1dzcwYdZia0xIeCk4/ NoEQ== X-Gm-Message-State: AOJu0YytxnSHI81xcs2J89P5PAT/SoK4hZs9PnL4zkKmO2BMzmh2+rM3 3du/Kfw89m843HOVsbuLG1HjJNJbJ4dCSrERan2tf/n0z6BzFE4VKvmg4IbkD1FtPEq/ByNl7IL dt/kMap2gOLX3ccwyYd2xs/Q1QtYzqt4h/6C7XE8cYERgVQpMRgFaioCzsVVDRv72AW/Gaw/g54 8EmyRw0qdQx1y5ehQ0e0vddFGogsK9QAh79bJ3 X-Received: by 2002:a05:6000:2a7:b0:366:e9f9:99c1 with SMTP id ffacd0b85a97d-36775724482mr3998914f8f.53.1719845940936; Mon, 01 Jul 2024 07:59:00 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEpHuqT08prYZNxP+XKtb0LrSfZbWdvY+x4yOUMzPNbG+HRAJihzeEYXDUjNvYEPueTbtXkYA== X-Received: by 2002:a05:6000:2a7:b0:366:e9f9:99c1 with SMTP id ffacd0b85a97d-36775724482mr3998888f8f.53.1719845940525; Mon, 01 Jul 2024 07:59:00 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a0e169dsm10218914f8f.65.2024.07.01.07.58.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:58:58 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 01/14] add skeleton Date: Mon, 1 Jul 2024 16:58:33 +0200 Message-ID: <20240701145853.1394967-2-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 qemu/ is where target-independent code goes. This code should not use constructors and will be brought in as needed. qemu-hw/ is where the target-dependent code goes, which is going to be built depending on Kconfig symbols. Signed-off-by: Paolo Bonzini --- .gitignore | 2 + Cargo.toml | 3 + qemu-hw/Cargo.toml | 6 ++ qemu-hw/src/lib.rs | 0 qemu-hw/src/main.rs | 3 + qemu/Cargo.toml | 7 ++ qemu/src/bindings/mod.rs | 88 +++++++++++++++++++++++++ qemu/src/lib.rs | 4 ++ 9 files changed, 249 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 qemu-hw/Cargo.toml create mode 100644 qemu-hw/src/lib.rs create mode 100644 qemu-hw/src/main.rs create mode 100644 qemu/Cargo.toml create mode 100644 qemu/src/bindings/mod.rs create mode 100644 qemu/src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fbfc34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/.git-old diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f66a80e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["qemu"] +resolver = "2" diff --git a/qemu-hw/Cargo.toml b/qemu-hw/Cargo.toml new file mode 100644 index 0000000..9bd6930 --- /dev/null +++ b/qemu-hw/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "qemu-hw" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/qemu-hw/src/lib.rs b/qemu-hw/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/qemu-hw/src/main.rs b/qemu-hw/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/qemu-hw/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml new file mode 100644 index 0000000..18d0fa4 --- /dev/null +++ b/qemu/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "qemu" +version = "0.1.0" +edition = "2021" + +[dependencies] +const-default = { version = "~1", features = ["derive"] } diff --git a/qemu/src/bindings/mod.rs b/qemu/src/bindings/mod.rs new file mode 100644 index 0000000..a49447b --- /dev/null +++ b/qemu/src/bindings/mod.rs @@ -0,0 +1,88 @@ +use std::ffi::{c_char, c_void}; + +#[repr(C)] +pub struct Object { + pub klass: *mut c_void, + pub free: extern "C" fn(c: *mut c_void), + pub properties: *mut c_void, + pub r#ref: u32, + pub parent: *mut Object, +} + +#[repr(C)] +pub struct ObjectClass { + pub unparent: Option, +} + +#[repr(C)] +pub struct DeviceState { + pub base: Object, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +pub struct PropertyInfo { + pub name: *const c_char, + pub description: *const c_char, + // ... +} +#[repr(C)] +pub struct Property { + pub name: *const c_char, + pub offset: usize, + pub default: u64, + pub info: *const PropertyInfo, +} + +pub struct DeviceClass { + pub oc: ObjectClass, + + pub realize: Option, + pub unrealize: Option, + pub cold_reset: Option, + pub properties: *const Property, +} + +#[repr(C)] +pub struct TypeInfo { + pub name: *const c_char, + pub parent: *const c_char, + pub instance_mem_init: Option, + pub instance_init: Option, + pub instance_finalize: Option, + pub class_init: Option, + pub instance_size: usize, +} + +#[repr(C)] +pub struct Error { + _unused: c_char, +} + +extern "C" { + pub fn error_setg_internal( + errp: *mut *mut Error, + src: *mut c_char, + line: u32, + func: *mut c_char, + fmt: *const c_char, + ... + ); + pub fn error_get_pretty(errp: *const Error) -> *mut c_char; + pub fn error_free(errp: *mut Error); + + pub fn object_dynamic_cast(obj: *mut Object, typ: *const c_char) -> *mut c_void; + pub fn object_property_add_child(obj: *mut Object, typ: *const c_char, + child: *mut Object); + pub fn object_get_typename(obj: *const Object) -> *const c_char; + pub fn object_ref(obj: *mut Object); + pub fn object_new(typ: *const c_char) -> *const Object; + pub fn object_unref(obj: *mut Object); + pub fn object_unparent(obj: *mut Object); + + pub fn device_cold_reset(obj: *mut DeviceState); + pub fn device_realize(obj: *mut DeviceState, err: *mut *mut Error) -> bool; + pub fn type_register(obj: *const TypeInfo); + + pub static qdev_prop_bool: PropertyInfo; +} diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs new file mode 100644 index 0000000..fce21d7 --- /dev/null +++ b/qemu/src/lib.rs @@ -0,0 +1,4 @@ +#![allow(unused_macros)] +#![allow(dead_code)] + +pub mod bindings; From patchwork Mon Jul 1 14:58:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718241 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 8A815C2BD09 for ; Mon, 1 Jul 2024 15:00:25 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIUq-0003YY-Gv; Mon, 01 Jul 2024 10:59:12 -0400 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 1sOIUo-0003TH-H0 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:10 -0400 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 1sOIUm-00057H-RU for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:10 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845947; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/AQLjBJD+DdaeIr+SrmTNPomxj+lI0aBHtLoGiVChLk=; b=XmYYOcxzqJTEtmQH++Cj0utjV7XtktjbzilLmyCR0Xi0+RHC71hl0FsVH2cJaUIv6kChtl Ur9s+mPa33YLGwnL1HCW6xfeGGp2sUPqgSC4y+oRR/Vh9pi9AUVCMU2KoP03O+CawL3RIg qUo+gQU5f4Ta2QW/Rza2YKAjypSmwEI= Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-489-mePTAs07PyO14i9bB1Qomg-1; Mon, 01 Jul 2024 10:59:05 -0400 X-MC-Unique: mePTAs07PyO14i9bB1Qomg-1 Received: by mail-lf1-f70.google.com with SMTP id 2adb3069b0e04-52ce324c204so3707239e87.1 for ; Mon, 01 Jul 2024 07:59:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845943; x=1720450743; 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=/AQLjBJD+DdaeIr+SrmTNPomxj+lI0aBHtLoGiVChLk=; b=XZBArWiTxdH+5OGYZJOMReg4oOyf5t4BOqMcilpJGS9ZIDK3PK6ANjAwli0EIdpwF+ I1EX2orwl5jc9KjgdipszV7slF3MHDfasE9azBMQOU1IY3BuUaGiqw3z/jrGhBF3zX1D MwEMSTVNPzBwt7XLdD76iBzz5+C/pT1Jub/e3YP/V6LZeNwIyDz/zHYPw2a+gZIwmU/x mp48E85jdQ5RLpwG675eXzKDW7KCtf/016U7gYlbW6r3qUfntlhj4V6IOr+h1PhRJKph Ww0lOtHk0jWNKnUr3JoD0TdUcgUdGyPVU3WMI38O8n6O5PCFi1kbgaYPFdAVX951wk+3 xwbA== X-Gm-Message-State: AOJu0YwnW3dxuYsMy8CK7ZZxfnH5QtApyJdnJ9cvPN37KBHi8urj02zL Am3jX4KpWZzur+1XieiYlwYS4/EKsE6R38Xl4AOGxCLiEtmFlipCDrN9szlZMhSXzUr3WO4e4R9 Z2+B27nlKlo13nb4xlag1ibpmyorbf1pOJn0fGJ8zJjdzfagvca3U/E/zwiuFax5nkVIr4bHD81 +wA7z34/QNus1YO0BlTpJ66Aj2bqZWDIzIeisJ X-Received: by 2002:a19:7503:0:b0:52c:e07d:229c with SMTP id 2adb3069b0e04-52e826664d2mr3250984e87.22.1719845943668; Mon, 01 Jul 2024 07:59:03 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE5p8ijT5qhqXZTDOkO+cIGKrbcGDOCq0CVzpKFgEeQ98pu11elnT9puovdOVNkAzN99/lZ4g== X-Received: by 2002:a19:7503:0:b0:52c:e07d:229c with SMTP id 2adb3069b0e04-52e826664d2mr3250970e87.22.1719845943319; Mon, 01 Jul 2024 07:59:03 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4257a4d4cd1sm94550475e9.28.2024.07.01.07.59.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:02 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 02/14] set expectations Date: Mon, 1 Jul 2024 16:58:34 +0200 Message-ID: <20240701145853.1394967-3-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ef6f0d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +This is very experimental and barely compiles From patchwork Mon Jul 1 14:58:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718259 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 CBBA4C3065B for ; Mon, 1 Jul 2024 15:02:28 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIUz-0003xR-31; Mon, 01 Jul 2024 10:59:21 -0400 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 1sOIUx-0003um-6n for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:19 -0400 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 1sOIUs-0005Ap-1G for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:18 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845953; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dOb7lPpCZJz3JV8h1vR7Nm7ptbAZTCJyioTDRRfbz+U=; b=SG7YenCXIv9adhCTuAOznvomUhQFrV0nafvSnN+cDK8bOh3s6e9TXf3NjmmHxljDFHwnzR tBOXAv0s2kAZglxaBRYfFdoIYMafmrXwRAhm1O5ughcA7V/tSbyZgAMW/6Se8uk2VW5pCJ sWPxISPQQHJLgwxy8zDioo4YvDF1fdA= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-27-kJmnEonkN86oPV_C0k4w2g-1; Mon, 01 Jul 2024 10:59:08 -0400 X-MC-Unique: kJmnEonkN86oPV_C0k4w2g-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-4255f94080bso22338945e9.2 for ; Mon, 01 Jul 2024 07:59:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845946; x=1720450746; 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=dOb7lPpCZJz3JV8h1vR7Nm7ptbAZTCJyioTDRRfbz+U=; b=A+O1uWXIzTMs6sp9yq8espKfcHn+Z77JQQd7BBpLti8akCjJiAZKQNvXa72Mb6kMIc Q/G3FI34UtwP/bTKpDo+opCB2p7LPCzJyBo1VhXmI9zUU+YJ9SobIqJVp6dv4R/dbkp4 86Oyu17f44zySD0WqySgSCrRYTT/04mQeM2LInVGAEkXaMEOjtrZ4oOu7V99cTpUZ29C GKZbLsBAIMaX3yS9tJBptOHoITMesPvOmqzUh5VCWuzsqp1hj0S70+Zja8pIlCzcoDne KWWWGGXQsaGZWarzvqYC7Zaknfy181loisTSEWDh5CF4ZDdAUUag81hM/CncVkiHFMUC Pwmw== X-Gm-Message-State: AOJu0YzNmJ4Y9uY0ZsmS43s8aTDT5PjSAlBK+m72Dc5bBErLFb9DjnPY eHx+Gxx0Hn5ukURtQLGkJE5hDCG7Ria9C0BzIyvWUj/PvqQOtZi/deQlRiGJK/uIY4WIbtpyHZw u2aor6N2WL84bliGHzLeQROXJs24okhw6MOjs/+ok5b7vQovsnjxXgXcfisY9K/2mzhtxuBlanC DsPkTMx0wTxS9JKTih9bS4AN/PUuvhotmPMHVU X-Received: by 2002:a05:600c:3b09:b0:424:7bcf:2486 with SMTP id 5b1f17b1804b1-4257a00aadamr34948445e9.4.1719845946398; Mon, 01 Jul 2024 07:59:06 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGxYzDZk7aajFO2Hj5/wZoBbox9nvdOZOlzMaYILjTLvsNdMm0i2/Axrh27hLn+vIFESdNz9g== X-Received: by 2002:a05:600c:3b09:b0:424:7bcf:2486 with SMTP id 5b1f17b1804b1-4257a00aadamr34948265e9.4.1719845945943; Mon, 01 Jul 2024 07:59:05 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4256af3cf90sm156221475e9.5.2024.07.01.07.59.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:05 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 03/14] rust: define traits and pointer wrappers to convert from/to C representations Date: Mon, 1 Jul 2024 16:58:35 +0200 Message-ID: <20240701145853.1394967-4-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 qemu::util::foreign module provides: - A trait for structs that can be converted to a C ("foreign") representation (CloneToForeign) - A trait for structs that can be built from a C ("foreign") representation (FromForeign), and the utility IntoNative that can be used with less typing (similar to the standard library's From and Into pair) - Automatic implementations of the above traits for Option<>, supporting NULL pointers - A wrapper for a pointer that automatically frees the contained data. If a struct XYZ implements CloneToForeign, you can build an OwnedPointer and it will free the contents automatically unless you retrieve it with owned_ptr.into_inner() Signed-off-by: Paolo Bonzini --- qemu/src/lib.rs | 6 + qemu/src/util/foreign.rs | 247 +++++++++++++++++++++++++++++++++++++++ qemu/src/util/mod.rs | 1 + 3 files changed, 254 insertions(+) create mode 100644 qemu/src/util/foreign.rs create mode 100644 qemu/src/util/mod.rs diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index fce21d7..c48edcf 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -2,3 +2,9 @@ #![allow(dead_code)] pub mod bindings; + +pub mod util; +pub use util::foreign::CloneToForeign; +pub use util::foreign::FromForeign; +pub use util::foreign::IntoNative; +pub use util::foreign::OwnedPointer; diff --git a/qemu/src/util/foreign.rs b/qemu/src/util/foreign.rs new file mode 100644 index 0000000..a591925 --- /dev/null +++ b/qemu/src/util/foreign.rs @@ -0,0 +1,247 @@ +// TODO: change to use .cast() etc. +#![allow(clippy::ptr_as_ptr)] + +/// Traits to map between C structs and native Rust types. +/// Similar to glib-rs but a bit simpler and possibly more +/// idiomatic. +use std::borrow::Cow; +use std::fmt; +use std::fmt::Debug; +use std::mem; +use std::ptr; + +/// A type for which there is a canonical representation as a C datum. +pub trait CloneToForeign { + /// The representation of `Self` as a C datum. Typically a + /// `struct`, though there are exceptions for example `c_char` + /// for strings, since C strings are of `char *` type). + type Foreign; + + /// Free the C datum pointed to by `p`. + /// + /// # Safety + /// + /// `p` must be `NULL` or point to valid data. + unsafe fn free_foreign(p: *mut Self::Foreign); + + /// Convert a native Rust object to a foreign C struct, copying + /// everything pointed to by `self` (same as `to_glib_full` in `glib-rs`) + fn clone_to_foreign(&self) -> OwnedPointer; + + /// Convert a native Rust object to a foreign C pointer, copying + /// everything pointed to by `self`. The returned pointer must + /// be freed with the `free_foreign` associated function. + fn clone_to_foreign_ptr(&self) -> *mut Self::Foreign { + self.clone_to_foreign().into_inner() + } +} + +impl CloneToForeign for Option +where + T: CloneToForeign, +{ + type Foreign = ::Foreign; + + unsafe fn free_foreign(x: *mut Self::Foreign) { + T::free_foreign(x) + } + + fn clone_to_foreign(&self) -> OwnedPointer { + // Same as the underlying implementation, but also convert `None` + // to a `NULL` pointer. + self.as_ref() + .map(CloneToForeign::clone_to_foreign) + .map(OwnedPointer::into) + .unwrap_or_default() + } +} + +impl FromForeign for Option +where + T: FromForeign, +{ + unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self { + // Same as the underlying implementation, but also accept a `NULL` pointer. + if p.is_null() { + None + } else { + Some(T::cloned_from_foreign(p)) + } + } +} + +impl CloneToForeign for Box +where + T: CloneToForeign, +{ + type Foreign = ::Foreign; + + unsafe fn free_foreign(x: *mut Self::Foreign) { + T::free_foreign(x) + } + + fn clone_to_foreign(&self) -> OwnedPointer { + self.as_ref().clone_to_foreign().into() + } +} + +impl FromForeign for Box +where + T: FromForeign, +{ + unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self { + Box::new(T::cloned_from_foreign(p)) + } +} + +impl<'a, B> CloneToForeign for Cow<'a, B> + where B: 'a + ToOwned + ?Sized + CloneToForeign, +{ + type Foreign = B::Foreign; + + unsafe fn free_foreign(ptr: *mut B::Foreign) { + B::free_foreign(ptr); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + self.as_ref().clone_to_foreign().into() + } +} + +/// Convert a C datum into a native Rust object, taking ownership of +/// the C datum. You should not need to implement this trait +/// as long as Rust types implement `FromForeign`. +pub trait IntoNative { + /// Convert a C datum to a native Rust object, taking ownership of + /// the pointer or Rust object (same as `from_glib_full` in `glib-rs`) + /// + /// # Safety + /// + /// `p` must point to valid data, or can be `NULL` if Self is an + /// `Option` type. It becomes invalid after the function returns. + unsafe fn into_native(self) -> T; +} + +impl IntoNative for *mut T +where + U: FromForeign, +{ + unsafe fn into_native(self) -> U { + U::from_foreign(self) + } +} + +/// A type which can be constructed from a canonical representation as a +/// C datum. +pub trait FromForeign: CloneToForeign + Sized { + /// Convert a C datum to a native Rust object, copying everything + /// pointed to by `p` (same as `from_glib_none` in `glib-rs`) + /// + /// # Safety + /// + /// `p` must point to valid data, or can be `NULL` is `Self` is an + /// `Option` type. + unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self; + + /// Convert a C datum to a native Rust object, taking ownership of + /// the pointer or Rust object (same as `from_glib_full` in `glib-rs`) + /// + /// The default implementation calls `cloned_from_foreign` and frees `p`. + /// + /// # Safety + /// + /// `p` must point to valid data, or can be `NULL` is `Self` is an + /// `Option` type. `p` becomes invalid after the function returns. + unsafe fn from_foreign(p: *mut Self::Foreign) -> Self { + let result = Self::cloned_from_foreign(p); + Self::free_foreign(p); + result + } +} + +pub struct OwnedPointer { + ptr: *mut ::Foreign, +} + +impl OwnedPointer { + /// Return a new `OwnedPointer` that wraps the pointer `ptr`. + /// + /// # Safety + /// + /// The pointer must be valid and live until the returned `OwnedPointer` + /// is dropped. + pub unsafe fn new(ptr: *mut ::Foreign) -> Self { + OwnedPointer { ptr } + } + + /// Safely create an `OwnedPointer` from one that has the same + /// freeing function. + pub fn from(x: OwnedPointer) -> Self + where + U: CloneToForeign::Foreign> + ?Sized, + { + unsafe { + // SAFETY: the pointer type and free function are the same, + // only the type changes + OwnedPointer::new(x.into_inner()) + } + } + + /// Safely convert an `OwnedPointer` into one that has the same + /// freeing function. + pub fn into(self) -> OwnedPointer + where + U: CloneToForeign::Foreign>, + { + OwnedPointer::from(self) + } + + /// Return the pointer that is stored in the `OwnedPointer`. The + /// pointer is valid for as long as the `OwnedPointer` itself. + pub fn as_ptr(&self) -> *const ::Foreign { + self.ptr + } + + pub fn as_mut_ptr(&self) -> *mut ::Foreign { + self.ptr + } + + /// Return the pointer that is stored in the `OwnedPointer`, + /// consuming the `OwnedPointer` but not freeing the pointer. + pub fn into_inner(mut self) -> *mut ::Foreign { + let result = mem::replace(&mut self.ptr, ptr::null_mut()); + mem::forget(self); + result + } +} + +impl OwnedPointer { + /// Convert a C datum to a native Rust object, taking ownership of + /// the pointer or Rust object (same as `from_glib_full` in `glib-rs`) + pub fn into_native(self) -> T { + // SAFETY: the pointer was passed to the unsafe constructor OwnedPointer::new + unsafe { T::from_foreign(self.into_inner()) } + } +} + +impl Debug for OwnedPointer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = std::any::type_name::(); + let name = format!("OwnedPointer<{}>", name); + f.debug_tuple(&name).field(&self.as_ptr()).finish() + } +} + +impl Default for OwnedPointer { + fn default() -> Self { + ::default().clone_to_foreign() + } +} + +impl Drop for OwnedPointer { + fn drop(&mut self) { + let p = mem::replace(&mut self.ptr, ptr::null_mut()); + // SAFETY: the pointer was passed to the unsafe constructor OwnedPointer::new + unsafe { T::free_foreign(p) } + } +} diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs new file mode 100644 index 0000000..be00d0c --- /dev/null +++ b/qemu/src/util/mod.rs @@ -0,0 +1 @@ +pub mod foreign; From patchwork Mon Jul 1 14:58:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718239 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 61D9EC3065A for ; Mon, 1 Jul 2024 15:00:22 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIUx-0003up-PT; Mon, 01 Jul 2024 10:59:19 -0400 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 1sOIUv-0003p3-3E for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:17 -0400 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 1sOIUs-0005Ah-0X for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:16 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845952; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4FB4lTjJ2jdkYkhNKjtSbR2CEvqDwGBPr2HOuzmXi+Q=; b=Cu4ubbs9phv437Vxcn/SDP9+iXVsa+mAKkB61RJSNR7pvkGSY7ZbzGH3wR9gteGo5tv5y4 Sonc0cu2C4y78Yh90nqblul6yDJOkvwOSWjtW3mB4iJYf49qw0al4C+WdCM8VsbyDmfZ8m 97YHv0uLg1xmBMnfZ1r9+7NlDzdYyEQ= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-620-WgpDa7wbNZ61bhssyeVNAg-1; Mon, 01 Jul 2024 10:59:11 -0400 X-MC-Unique: WgpDa7wbNZ61bhssyeVNAg-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-363edcf12a3so2052780f8f.3 for ; Mon, 01 Jul 2024 07:59:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845949; x=1720450749; 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=4FB4lTjJ2jdkYkhNKjtSbR2CEvqDwGBPr2HOuzmXi+Q=; b=OVoPFI6z4C+XrJ1LQm4dAMMz6CqDZub87m426OphPmBJV3eH9L8F1CIWTT2CoxvYLZ k6MckvvBDWlbaDZkrpnuWw+9X9m8X24l7Wp9hZo6BvEJ1Vr4f/OViVUMk0kD+/H++F7j qwVQwAh2e79cZLdxV+NNbQQUVr1lN3nFN/oNl0e6nfnw69+lZ7EW9Vvtqzx7+6SWsLSI mG9OTvaR3ZVngVPG+UUx+rjAP+1VkgJtxskYzsBmp0KdmKoABz+KqGF9P11liZ+I+Jd9 COKSYrzZtETovDfYyXwopT+NkY+Hru1zcHhEe1rFqZ4TIEkdot12J+CcvwjWjHyR+Bym Skow== X-Gm-Message-State: AOJu0YyEZMcnLulSfBoMkimj2YuqbxL/NgkdYRXPuAt1fbMGnenKKFmY LighrUIADIQpRn9RBKxTf9TtqC84g7WNKmbRAkCC8FEqX66RNs0SbqJissYsvVfCQFPMhEN6It/ AWKB7WYraR42eVLLDyFqWVoqdBQ/RmBnXiGMM2f63vRyoViYxb+0OyhKDvXBUN17mHG+VF+B6Rr S9Vp9GOucQTmpDTNl3s2MfcoJHUu4LK38PdevI X-Received: by 2002:a05:6000:1447:b0:367:4dbb:ed4e with SMTP id ffacd0b85a97d-3677561da1bmr4170415f8f.0.1719845949201; Mon, 01 Jul 2024 07:59:09 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEma+4/l+Pn62ItPvedLeZpIOdZx7y2TYZTPNuebsrDVIm7QiIfX7fwQ3nyIkvoKCnsvAYgew== X-Received: by 2002:a05:6000:1447:b0:367:4dbb:ed4e with SMTP id ffacd0b85a97d-3677561da1bmr4170391f8f.0.1719845948723; Mon, 01 Jul 2024 07:59:08 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a0fba5asm10250857f8f.71.2024.07.01.07.59.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:08 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 04/14] rust: add tests for util::foreign Date: Mon, 1 Jul 2024 16:58:36 +0200 Message-ID: <20240701145853.1394967-5-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 Provide sample implementations in util::foreign for strings and elementary integer types, and use them to test the code. Signed-off-by: Paolo Bonzini --- qemu/Cargo.toml | 4 + qemu/src/util/foreign.rs | 456 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 474 insertions(+) diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml index 18d0fa4..1100725 100644 --- a/qemu/Cargo.toml +++ b/qemu/Cargo.toml @@ -5,3 +5,7 @@ edition = "2021" [dependencies] const-default = { version = "~1", features = ["derive"] } +libc = "^0" + +[dev-dependencies] +matches = ">=0" diff --git a/qemu/src/util/foreign.rs b/qemu/src/util/foreign.rs index a591925..0b8b708 100644 --- a/qemu/src/util/foreign.rs +++ b/qemu/src/util/foreign.rs @@ -5,6 +5,7 @@ /// Similar to glib-rs but a bit simpler and possibly more /// idiomatic. use std::borrow::Cow; +use std::ffi::{c_char, c_void, CStr, CString}; use std::fmt; use std::fmt::Debug; use std::mem; @@ -22,6 +23,14 @@ pub trait CloneToForeign { /// # Safety /// /// `p` must be `NULL` or point to valid data. + /// + /// ``` + /// # use qemu::CloneToForeign; + /// let foreign = "Hello, world!".clone_to_foreign(); + /// unsafe { + /// String::free_foreign(foreign.into_inner()); + /// } + /// ``` unsafe fn free_foreign(p: *mut Self::Foreign); /// Convert a native Rust object to a foreign C struct, copying @@ -119,6 +128,17 @@ pub trait IntoNative { /// /// `p` must point to valid data, or can be `NULL` if Self is an /// `Option` type. It becomes invalid after the function returns. + /// + /// ``` + /// # use qemu::{CloneToForeign, IntoNative}; + /// let s = "Hello, world!".to_string(); + /// let foreign = s.clone_to_foreign(); + /// let native: String = unsafe { + /// foreign.into_native() + /// // foreign is not leaked + /// }; + /// assert_eq!(s, native); + /// ``` unsafe fn into_native(self) -> T; } @@ -141,6 +161,15 @@ pub trait FromForeign: CloneToForeign + Sized { /// /// `p` must point to valid data, or can be `NULL` is `Self` is an /// `Option` type. + /// + /// ``` + /// # use qemu::FromForeign; + /// let p = c"Hello, world!".as_ptr(); + /// let s = unsafe { + /// String::cloned_from_foreign(p as *const std::ffi::c_char) + /// }; + /// assert_eq!(s, "Hello, world!"); + /// ``` unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self; /// Convert a C datum to a native Rust object, taking ownership of @@ -152,6 +181,16 @@ pub trait FromForeign: CloneToForeign + Sized { /// /// `p` must point to valid data, or can be `NULL` is `Self` is an /// `Option` type. `p` becomes invalid after the function returns. + /// + /// ``` + /// # use qemu::{CloneToForeign, FromForeign}; + /// let s = "Hello, world!"; + /// let foreign = s.clone_to_foreign(); + /// unsafe { + /// assert_eq!(String::from_foreign(foreign.into_inner()), s); + /// } + /// // foreign is not leaked + /// ``` unsafe fn from_foreign(p: *mut Self::Foreign) -> Self { let result = Self::cloned_from_foreign(p); Self::free_foreign(p); @@ -176,6 +215,12 @@ impl OwnedPointer { /// Safely create an `OwnedPointer` from one that has the same /// freeing function. + /// ``` + /// # use qemu::{CloneToForeign, OwnedPointer}; + /// let s = "Hello, world!"; + /// let foreign_str = s.clone_to_foreign(); + /// let foreign_string = OwnedPointer::::from(foreign_str); + /// # assert_eq!(foreign_string.into_native(), s); pub fn from(x: OwnedPointer) -> Self where U: CloneToForeign::Foreign> + ?Sized, @@ -189,6 +234,12 @@ impl OwnedPointer { /// Safely convert an `OwnedPointer` into one that has the same /// freeing function. + /// ``` + /// # use qemu::{CloneToForeign, OwnedPointer}; + /// let s = "Hello, world!"; + /// let foreign_str = s.clone_to_foreign(); + /// let foreign_string: OwnedPointer = foreign_str.into(); + /// # assert_eq!(foreign_string.into_native(), s); pub fn into(self) -> OwnedPointer where U: CloneToForeign::Foreign>, @@ -198,6 +249,16 @@ impl OwnedPointer { /// Return the pointer that is stored in the `OwnedPointer`. The /// pointer is valid for as long as the `OwnedPointer` itself. + /// + /// ``` + /// # use qemu::CloneToForeign; + /// let s = "Hello, world!"; + /// let foreign = s.clone_to_foreign(); + /// let p = foreign.as_ptr(); + /// let len = unsafe { libc::strlen(p) }; + /// drop(foreign); + /// # assert_eq!(len, 13); + /// ``` pub fn as_ptr(&self) -> *const ::Foreign { self.ptr } @@ -208,6 +269,15 @@ impl OwnedPointer { /// Return the pointer that is stored in the `OwnedPointer`, /// consuming the `OwnedPointer` but not freeing the pointer. + /// + /// ``` + /// # use qemu::CloneToForeign; + /// let s = "Hello, world!"; + /// let p = s.clone_to_foreign().into_inner(); + /// let len = unsafe { libc::strlen(p) }; + /// // p needs to be freed manually + /// # assert_eq!(len, 13); + /// ``` pub fn into_inner(mut self) -> *mut ::Foreign { let result = mem::replace(&mut self.ptr, ptr::null_mut()); mem::forget(self); @@ -218,6 +288,17 @@ impl OwnedPointer { impl OwnedPointer { /// Convert a C datum to a native Rust object, taking ownership of /// the pointer or Rust object (same as `from_glib_full` in `glib-rs`) + /// + /// ``` + /// # use qemu::{CloneToForeign, IntoNative}; + /// let s = "Hello, world!".to_string(); + /// let foreign = s.clone_to_foreign(); + /// let native: String = unsafe { + /// foreign.into_native() + /// // foreign is not leaked + /// }; + /// assert_eq!(s, native); + /// ``` pub fn into_native(self) -> T { // SAFETY: the pointer was passed to the unsafe constructor OwnedPointer::new unsafe { T::from_foreign(self.into_inner()) } @@ -245,3 +326,378 @@ impl Drop for OwnedPointer { unsafe { T::free_foreign(p) } } } + +impl CloneToForeign for str { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + // SAFETY: self.as_ptr() is guaranteed to point to self.len() bytes; + // the destination is freshly allocated + unsafe { + let p = libc::malloc(self.len() + 1) as *mut c_char; + ptr::copy_nonoverlapping(self.as_ptr() as *const c_char, p, self.len()); + *p.add(self.len()) = 0; + OwnedPointer::new(p) + } + } +} + +impl CloneToForeign for CStr { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + // SAFETY: self.as_ptr() is guaranteed to point to self.len() bytes; + // the destination is freshly allocated + unsafe { + let slice = self.to_bytes_with_nul(); + let p = libc::malloc(slice.len()) as *mut c_char; + ptr::copy_nonoverlapping(self.as_ptr() as *const c_char, p, slice.len()); + OwnedPointer::new(p) + } + } +} + +impl CloneToForeign for String { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + self.as_str().clone_to_foreign().into() + } +} + +impl FromForeign for String { + unsafe fn cloned_from_foreign(p: *const c_char) -> Self { + let cstr = CStr::from_ptr(p); + String::from_utf8_lossy(cstr.to_bytes()).into_owned() + } +} + +impl CloneToForeign for CString { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + self.as_c_str().clone_to_foreign().into() + } +} + +impl FromForeign for CString { + unsafe fn cloned_from_foreign(p: *const c_char) -> Self { + CStr::from_ptr(p).to_owned() + } +} + +impl FromForeign for Cow<'_, str> +{ + unsafe fn cloned_from_foreign(p: *const c_char) -> Self { + let cstr = CStr::from_ptr(p); + cstr.to_string_lossy() + } +} + +macro_rules! foreign_copy_type { + ($rust_type:ty, $foreign_type:ty) => { + impl CloneToForeign for $rust_type { + type Foreign = $foreign_type; + + unsafe fn free_foreign(ptr: *mut Self::Foreign) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + // Safety: we are copying into a freshly-allocated block + unsafe { + let p = libc::malloc(mem::size_of::()) as *mut Self::Foreign; + *p = *self as Self::Foreign; + OwnedPointer::new(p) + } + } + } + + impl FromForeign for $rust_type { + unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self { + *p + } + } + + impl CloneToForeign for [$rust_type] { + type Foreign = $foreign_type; + + unsafe fn free_foreign(ptr: *mut Self::Foreign) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer { + // SAFETY: self.as_ptr() is guaranteed to point to the same number of bytes + // as the freshly allocated destination + unsafe { + let size = mem::size_of::(); + let p = libc::malloc(self.len() * size) as *mut Self::Foreign; + ptr::copy_nonoverlapping(self.as_ptr() as *const Self::Foreign, p, self.len()); + OwnedPointer::new(p) + } + } + } + }; +} +foreign_copy_type!(i8, i8); +foreign_copy_type!(u8, u8); +foreign_copy_type!(i16, i16); +foreign_copy_type!(u16, u16); +foreign_copy_type!(i32, i32); +foreign_copy_type!(u32, u32); +foreign_copy_type!(i64, i64); +foreign_copy_type!(u64, u64); +foreign_copy_type!(isize, libc::ptrdiff_t); +foreign_copy_type!(usize, libc::size_t); +foreign_copy_type!(f32, f32); +foreign_copy_type!(f64, f64); + +#[cfg(test)] +mod tests { + #![allow(clippy::shadow_unrelated)] + + use super::*; + use matches::assert_matches; + use std::ffi::c_void; + + #[test] + fn test_foreign_int_convert() { + let i = 123i8; + let p = i.clone_to_foreign(); + unsafe { + assert_eq!(i, *p.as_ptr()); + assert_eq!(i, i8::cloned_from_foreign(p.as_ptr())); + } + assert_eq!(i, p.into_native()); + + let p = i.clone_to_foreign(); + unsafe { + assert_eq!(i, i8::from_foreign(p.into_inner())); + } + } + + #[test] + fn test_cloned_from_foreign_string_cow() { + let s = "Hello, world!".to_string(); + let cstr = c"Hello, world!"; + let cloned = unsafe { Cow::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + } + + #[test] + fn test_cloned_from_foreign_string() { + let s = "Hello, world!".to_string(); + let cstr = c"Hello, world!"; + let cloned = unsafe { String::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + } + + #[test] + fn test_cloned_from_foreign_cstring() { + let s = CString::new("Hello, world!").unwrap(); + let cloned = s.clone_to_foreign(); + let copy = unsafe { CString::cloned_from_foreign(cloned.as_ptr()) }; + assert_ne!(copy.as_ptr(), cloned.as_ptr()); + assert_ne!(copy.as_ptr(), s.as_ptr()); + assert_eq!(copy, s); + } + + #[test] + fn test_from_foreign_string() { + let s = "Hello, world!".to_string(); + let cloned = s.clone_to_foreign_ptr(); + let copy = unsafe { String::from_foreign(cloned) }; + assert_eq!(s, copy); + } + + #[test] + fn test_owned_pointer_default() { + let s: String = Default::default(); + let foreign: OwnedPointer = Default::default(); + let native = foreign.into_native(); + assert_eq!(s, native); + } + + #[test] + fn test_owned_pointer_into() { + let s = "Hello, world!".to_string(); + let cloned: OwnedPointer = s.clone_to_foreign().into(); + let copy = cloned.into_native(); + assert_eq!(s, copy); + } + + #[test] + fn test_owned_pointer_into_native() { + let s = "Hello, world!".to_string(); + let cloned = s.clone_to_foreign(); + let copy = cloned.into_native(); + assert_eq!(s, copy); + } + + #[test] + fn test_ptr_into_native() { + let s = "Hello, world!".to_string(); + let cloned = s.clone_to_foreign_ptr(); + let copy: String = unsafe { cloned.into_native() }; + assert_eq!(s, copy); + + // This is why type bounds are needed... they aren't for + // OwnedPointer::into_native + let cloned = s.clone_to_foreign_ptr(); + let copy: c_char = unsafe { cloned.into_native() }; + assert_eq!(s.as_bytes()[0], copy as u8); + } + + #[test] + fn test_clone_to_foreign_str() { + let s = "Hello, world!"; + let p = c"Hello, world!".as_ptr(); + let cloned = s.clone_to_foreign(); + unsafe { + let len = libc::strlen(cloned.as_ptr()); + assert_eq!(len, s.len()); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + p as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_cstr() { + let s: &CStr = c"Hello, world!"; + let cloned = s.clone_to_foreign(); + unsafe { + let len = libc::strlen(cloned.as_ptr()); + assert_eq!(len, s.count_bytes()); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + s.as_ptr() as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_string_cow() { + let p = c"Hello, world!".as_ptr(); + for s in vec![ + Into::>::into("Hello, world!"), + Into::>::into("Hello, world!".to_string())] { + let cloned = s.clone_to_foreign(); + unsafe { + let len = libc::strlen(cloned.as_ptr()); + assert_eq!(len, s.len()); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + p as *const c_void, + len + 1 + ), + 0 + ); + } + } + } + + #[test] + fn test_clone_to_foreign_bytes() { + let s = b"Hello, world!\0"; + let cloned = s.clone_to_foreign(); + unsafe { + let len = libc::strlen(cloned.as_ptr() as *const c_char); + assert_eq!(len, s.len() - 1); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + s.as_ptr() as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_cstring() { + let s = CString::new("Hello, world!").unwrap(); + let cloned = s.clone_to_foreign(); + unsafe { + assert_ne!(s.as_ptr(), cloned.as_ptr()); + assert_eq!( + libc::strcmp( + cloned.as_ptr() as *const c_char, + s.as_ptr() as *const c_char, + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_string() { + let s = "Hello, world!".to_string(); + let cstr = c"Hello, world!"; + let cloned = s.clone_to_foreign(); + unsafe { + let len = libc::strlen(cloned.as_ptr()); + assert_eq!(len, s.len()); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + cstr.as_ptr() as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_option() { + // An Option can be used to produce or convert NULL pointers + let s = Some("Hello, world!".to_string()); + let cstr = c"Hello, world!"; + unsafe { + assert_eq!(Option::::cloned_from_foreign(cstr.as_ptr()), s); + assert_matches!(Option::::cloned_from_foreign(ptr::null()), None); + assert_matches!(Option::::from_foreign(ptr::null_mut()), None); + } + } + + #[test] + fn test_box() { + // A box can be produced if the inner type has the capability. + let s = Box::new("Hello, world!".to_string()); + let cstr = c"Hello, world!"; + let cloned = unsafe { Box::::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + + let s = Some(Box::new("Hello, world!".to_string())); + let cloned = unsafe { Option::>::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + } +} From patchwork Mon Jul 1 14:58:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718263 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 77AF3C2BD09 for ; Mon, 1 Jul 2024 15:04:14 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIUy-0003wD-5a; Mon, 01 Jul 2024 10:59:20 -0400 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 1sOIUw-0003t3-FQ for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:18 -0400 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 1sOIUt-0005Ea-Tw for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:18 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845955; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZZE6DVkDuQcxnlYAIOtl14LqCPg3/XUa+gT7zaaHeP4=; b=gs75Wxy7fnFHMJJlWyGhIUQOnbBC4+t6ZX8k6wTEnlZVLkKvo/fiz+/hAQeF14qm8YdCTo wvcbihFGCxGPgTdeGSwHCB26GarE3Yo4QT55kmnSg1SO7Pjo/i5MkVSLyu+0X+cpwyIx/4 uqC+/j1Rv0Kq9+Xpdx0I7RAYl7XQm70= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-73-5Z3Vlh5VNW20Q9C7n4Jwvw-1; Mon, 01 Jul 2024 10:59:13 -0400 X-MC-Unique: 5Z3Vlh5VNW20Q9C7n4Jwvw-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-42490ae735dso29810615e9.0 for ; Mon, 01 Jul 2024 07:59:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845952; x=1720450752; 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=ZZE6DVkDuQcxnlYAIOtl14LqCPg3/XUa+gT7zaaHeP4=; b=PNqmJ45qGwe4k6XvIyytHTOXif0Mgj/4WJTeHk9l3gFf7AO4BgyQWgzdL/OFAKxOKi F20LNxMfPC2GOJoaKL0VTou/Uah/p4iysMXhoLi3pkcv9MBSD8I0ytBR4RQBGyWohDWA qpWBw6F/8tszFi1sPLS3nvh44f8qRAcos2VHS6cWyMSE7k1Xh6kx9FotjrJAucvqjXS0 d6Te7v5TB106AVzNgrJRsTXSDibu/5g1Exal6zJcmPnYuNUpMZY9HHehiEeFvF3N9QZg ZnG9AlrNCf7iDBPseGICCOZl1VwtsUULFOysPpwisGD5rwctAakDCn5gHyXrAj9z6jP2 ZxNA== X-Gm-Message-State: AOJu0YxIZZdkRaNlahaoAVCHhIlISAyPJy70s836jIGz8w3ziQINb54D EwvpjPQfDAkqRfeNWfaof/Vs0VN49CT1TZFpICOb/Z65Z/3LgTCHoTKARm441zckPbhy2tEBxkR 5iPJkxPJ5RRPaIgC7ipOgms6RZEqxW1fGOye63YPIo3a4I5hhsbYbGGmEpLUHH9NWaoi1+PKTks IbC9T22pvxYqgfoeZ6g/+R6Bgn4MC6sxC+rGCj X-Received: by 2002:a05:600c:5129:b0:425:63b9:ae2c with SMTP id 5b1f17b1804b1-4257a02008fmr51352905e9.27.1719845951773; Mon, 01 Jul 2024 07:59:11 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEyfqBCnafqIIFZ+4imKiXRlHo8ocrMrnhO9bcec/S2pttQQ3WOh4sF1xTFfPsNuQ4Dn/Nldw== X-Received: by 2002:a05:600c:5129:b0:425:63b9:ae2c with SMTP id 5b1f17b1804b1-4257a02008fmr51352685e9.27.1719845951384; Mon, 01 Jul 2024 07:59:11 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42577a0c286sm76291075e9.0.2024.07.01.07.59.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:10 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 05/14] rust: define wrappers for Error Date: Mon, 1 Jul 2024 16:58:37 +0200 Message-ID: <20240701145853.1394967-6-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 wrappers for Error provide the following functionality: - a struct that implements std::error::Error and can be filled with information similar to what would go into a C Error* (location, message). - functionality similar to error_prepend() via the "cause()" member of std::error::Error. - converting a Result<> into a value that can be returned to C, filling in an Error** (like error_propagate() would do) if the Result<> contains an error; useful for callbacks written in Rust - converting a C Error* into a Result that can be returned to Rust, useful for Rust wrappers of C functions Signed-off-by: Paolo Bonzini --- qemu/src/lib.rs | 2 + qemu/src/util/error.rs | 241 +++++++++++++++++++++++++++++++++++++++++ qemu/src/util/mod.rs | 1 + 3 files changed, 244 insertions(+) create mode 100644 qemu/src/util/error.rs diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index c48edcf..5f926b8 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -4,7 +4,9 @@ pub mod bindings; pub mod util; +pub use util::error::Error; pub use util::foreign::CloneToForeign; pub use util::foreign::FromForeign; pub use util::foreign::IntoNative; pub use util::foreign::OwnedPointer; +pub type Result = std::result::Result; diff --git a/qemu/src/util/error.rs b/qemu/src/util/error.rs new file mode 100644 index 0000000..e7e6f2e --- /dev/null +++ b/qemu/src/util/error.rs @@ -0,0 +1,241 @@ +//! Error class for QEMU Rust code +//! +//! @author Paolo Bonzini + +use crate::bindings; +use crate::bindings::error_free; +use crate::bindings::error_get_pretty; +use crate::bindings::error_setg_internal; + +use std::ffi::CStr; +use std::fmt::{self, Display}; +use std::ptr; + +use crate::util::foreign::{CloneToForeign, FromForeign, OwnedPointer}; + +#[derive(Debug, Default)] +pub struct Error { + msg: Option, + /// Appends the print string of the error to the msg if not None + cause: Option>, + location: Option<(String, u32)>, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.cause.as_deref() + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.msg + .as_deref() + .or_else(|| self.cause.as_deref().map(std::error::Error::description)) + .unwrap_or("error") + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut prefix = ""; + if let Some((ref file, line)) = self.location { + write!(f, "{}:{}", file, line)?; + prefix = ": "; + } + if let Some(ref msg) = self.msg { + write!(f, "{}{}", prefix, msg)?; + prefix = ": "; + } + if let Some(ref cause) = self.cause { + write!(f, "{}{}", prefix, cause)?; + } else if prefix.is_empty() { + f.write_str("unknown error")?; + } + Ok(()) + } +} + +impl From<&str> for Error { + fn from(msg: &str) -> Self { + Error { + msg: Some(String::from(msg)), + cause: None, + location: None, + } + } +} + +impl From for Error { + fn from(error: std::io::Error) -> Self { + Error { + msg: None, + cause: Some(Box::new(error)), + location: None, + } + } +} + +impl Error { + /// Create a new error, prepending `msg` to the + /// description of `cause` + pub fn with_error(msg: &str, cause: E) -> Self { + Error { + msg: Some(String::from(msg)), + cause: Some(Box::new(cause)), + location: None, + } + } + + /// Create a new error, prepending `file:line: msg` to the + /// description of `cause` + pub fn with_error_file_line( + msg: &str, + cause: E, + file: &str, + line: u32, + ) -> Self { + Error { + msg: Some(String::from(msg)), + cause: Some(Box::new(cause)), + location: Some((String::from(file), line)), + } + } + + /// Create a new error with format `file:line: msg` + pub fn with_file_line(msg: &str, file: &str, line: u32) -> Self { + Error { + msg: Some(String::from(msg)), + cause: None, + location: Some((String::from(file), line)), + } + } + + /// Consume a result, returning false if it is an error and + /// true if it is successful. The error is propagated into + /// `errp` like the C API `error_propagate` would do. + /// + /// # Safety + /// + /// `errp` must be valid; typically it is received from C code + pub unsafe fn bool_or_propagate( + result: Result<(), Self>, + errp: *mut *mut bindings::Error, + ) -> bool { + Self::ok_or_propagate(result, errp).is_some() + } + + /// Consume a result, returning a `NULL` pointer if it is an + /// error and a C representation of the contents if it is + /// successful. The error is propagated into `errp` like + /// the C API `error_propagate` would do. + /// + /// # Safety + /// + /// `errp` must be valid; typically it is received from C code + pub unsafe fn ptr_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> *mut T::Foreign { + Self::ok_or_propagate(result, errp).map_or(ptr::null_mut(), |ref x| { + CloneToForeign::clone_to_foreign_ptr(x) + }) + } + + /// Consume a result and return `self.ok()`, but also propagate a + /// possible error into `errp`, like the C API `error_propagate` + /// would do. + /// + /// # Safety + /// + /// `errp` must be valid; typically it is received from C code + pub unsafe fn ok_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> Option { + match result { + Ok(ok) => Some(ok), + Err(err) => { + err.propagate(errp); + None + } + } + } + + /// Equivalent of the C function `error_propagate`. Fill `*errp` + /// with the information container in `self` if `errp` is not NULL; + /// then consume it. + /// + /// # Safety + /// + /// `errp` must be valid; typically it is received from C code + pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { + if errp.is_null() { + return; + } + errp.write(self.clone_to_foreign_ptr()); + } + + /// Convert a C `Error*` into a Rust `Result`, using `Ok(Default::default())` + /// if `c_error` is NULL. + /// + /// # Safety + /// + /// `c_error` must be valid; typically it has been filled by a C + /// function. + pub unsafe fn err_or_default(c_error: *mut bindings::Error) -> Result { + Self::err_or_else(c_error, Default::default) + } + + /// Convert a C `Error*` into a Rust `Result`, calling `f()` to + /// obtain an `Ok` value if `c_error` is NULL. + /// + /// # Safety + /// + /// `c_error` must be valid; typically it has been filled by a C + /// function. + pub unsafe fn err_or_else T>( + c_error: *mut bindings::Error, + f: F, + ) -> Result { + match Option::::from_foreign(c_error) { + None => Ok(f()), + Some(err) => Err(err), + } + } +} + +impl CloneToForeign for Error { + type Foreign = bindings::Error; + + fn clone_to_foreign(&self) -> OwnedPointer { + let mut x: *mut bindings::Error = ptr::null_mut(); + unsafe { + error_setg_internal( + &mut x, + ptr::null_mut(), // FIXME + 0, + ptr::null_mut(), // FIXME + c"%s".as_ptr(), + format!("{}", self), + ); + OwnedPointer::new(x) + } + } + + unsafe fn free_foreign(p: *mut bindings::Error) { + unsafe { + error_free(p); + } + } +} + +impl FromForeign for Error { + unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { + let c_str = unsafe { CStr::from_ptr(error_get_pretty(c_error)) }; + Error { + msg: Some(c_str.to_string_lossy().into_owned()), + cause: None, + location: None, + } + } +} diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs index be00d0c..e6078ac 100644 --- a/qemu/src/util/mod.rs +++ b/qemu/src/util/mod.rs @@ -1 +1,2 @@ +pub mod error; pub mod foreign; From patchwork Mon Jul 1 14:58:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718244 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 5E003C3065A for ; Mon, 1 Jul 2024 15:01:24 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIV6-000420-If; Mon, 01 Jul 2024 10:59:28 -0400 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 1sOIV4-00040k-T9 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:26 -0400 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 1sOIV2-0005JE-E0 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845963; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=um1cHYKDVA60dq1DdcbcXL9qPvVb59yG3OyrA7YkkNg=; b=CORQGbyLO1WO/FsL6DvBr29lhDt0wJubXL5yTWSjxKIPB8ha/7meeN05uEnnqSyiIF6d6G nH4ylm1u1ozb/XfBBqyVZcEzvqAkJPvVgxSQDwnlzimjfF/KYHDiBb1seHl4r9oYzxITyO uZ4aITFu5lWd5W6wEWXxckl38QKdesU= Received: from mail-lj1-f199.google.com (mail-lj1-f199.google.com [209.85.208.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-329-rvlHLLYjO1qQ3_GqQzlrrQ-1; Mon, 01 Jul 2024 10:59:17 -0400 X-MC-Unique: rvlHLLYjO1qQ3_GqQzlrrQ-1 Received: by mail-lj1-f199.google.com with SMTP id 38308e7fff4ca-2ee4c666856so35551471fa.0 for ; Mon, 01 Jul 2024 07:59:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845955; x=1720450755; 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=um1cHYKDVA60dq1DdcbcXL9qPvVb59yG3OyrA7YkkNg=; b=pk3MdeU/jHk3bQFeXaS3AeOino5ERtqntv/Dy9r8mU/6AHA7lQXbzGbKRL+ZYAfY/v WntL2I8T0tiUBl7Pt5Sxm8FzpMZ0RX3nuDVp0d6VOS+Xhy8efVUJtRx89WinsnZ8u17L 4NMqSXxzJ7Pv/pr2SMc0ExBMnfGRlHquqhyF2YwvqznaXFU5wYNFfcbEmk/KhzYs/DYb /+n9/nGydvMcbn/jf4E1EIQA/xA12O5GD7n82USyMK93MABStesznny8AWKqPUcEPwo0 H/0xw0IBXsw68bXvbPqXC84A85dHTSiaCPrq++XlBFCgyeShdKyiagvmSsddf4ih6cJo qI4g== X-Gm-Message-State: AOJu0YwmVXDMurNAHxd/ShFUKC/Tud20KGyZhqRumAO+vP0KQpoIKybO UIZYvJFia0CQtnPpY6hipyFLUUVheMWUF+GVrzc8m3x46PMeeQ0fzFB/yiIRBJ7Sxoa9A0t/U38 RnpcwsU6gBuK0XX9Vc5tsw/HNh+Ugy9i1ULBFgDnKX47s0+WqPh2xtKKGm+iOc2H/CU+Hqb0vKv 4/lVUew9BirtV/wsRCKN29gz0ewJH0E1fNc/Jh X-Received: by 2002:a2e:3018:0:b0:2ec:5f85:61c0 with SMTP id 38308e7fff4ca-2ee5e707c21mr47302921fa.48.1719845954929; Mon, 01 Jul 2024 07:59:14 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF7LkxnzvJNVXy+mD5cbiiqW3PV7qLAI1xQqINPEE8KtDPkKHNMSCCcvIpZYx6R577FF0DtKQ== X-Received: by 2002:a2e:3018:0:b0:2ec:5f85:61c0 with SMTP id 38308e7fff4ca-2ee5e707c21mr47302731fa.48.1719845954359; Mon, 01 Jul 2024 07:59:14 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4256b061095sm149668235e9.23.2024.07.01.07.59.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:13 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 06/14] rust: define wrappers for basic QOM concepts Date: Mon, 1 Jul 2024 16:58:38 +0200 Message-ID: <20240701145853.1394967-7-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 This provides type-safe object casts, and automatic reference counting. Signed-off-by: Paolo Bonzini --- qemu/qom-rust.txt | 82 ++++++++++++ qemu/src/lib.rs | 6 + qemu/src/qom/mod.rs | 2 + qemu/src/qom/object.rs | 34 +++++ qemu/src/qom/refs.rs | 274 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 398 insertions(+) create mode 100644 qemu/qom-rust.txt create mode 100644 qemu/src/qom/mod.rs create mode 100644 qemu/src/qom/object.rs create mode 100644 qemu/src/qom/refs.rs diff --git a/qemu/qom-rust.txt b/qemu/qom-rust.txt new file mode 100644 index 0000000..1588445 --- /dev/null +++ b/qemu/qom-rust.txt @@ -0,0 +1,82 @@ +Rust QOM interoperability design +-------------------------------- + +Passing objects around +---------------------- + +ObjectRef: +-> trait for performing casts on objects +-> upcasts safe at compile time, downcasts safe at runtime +-> implemented by &T and qom::Owned +-> casting &T produces &U, casting qom::Owned produces qom::Owned + +qom::Owned +-> T is a struct for a QOM object +-> cloning qom::Owned calls object_ref, dropping qom::Owned calls object_unref + + +Calling methods +--------------- + +- all methods &self (interior mutability) + - Rust implementation needs to wrap state with Cell<>, RefCell<> or Mutex<> + +one struct per class; one trait per non-final class; one trait per interface +struct: Object, Device, ... +- defines constructors + example: PL011::new() (technically defined on ObjectType) + +- defines methods of final classes + +trait: ObjectMethods, DeviceMethods, UserCreatableMethods, ... +- defines methods of non-final classes and interfaces + example: obj.typename() + +- automatically implemented by &T where T is a subclass + + +all methods expect interior mutability +- structs not Send/Sync by default since they contain C pointers + - hence &T and Owned also not thread-safe +- good: QOM tree (e.g. object_unparent) not thread-safe +- what if objects _are_ thread-safe? + - possibly another trait ObjectSyncMethods? + +Bindings for C classes +---------------------- + +struct must implement ObjectType + + unsafe impl ObjectType for Object { + const TYPE: &'static CStr = c"object"; + } + +struct must implement IsA for all superclasses T + + unsafe impl IsA for Object {} + + +Defining QOM classes in Rust +---------------------------- + +struct must be #[repr(C)] + +one traits per class + one more if it has virtual functions + +trait #1: ObjectTypeImpl, DeviceTypeImpl, ... +- metadata + type Super: ObjectType; +- functions: + unsafe fn instance_init(obj: *mut Self); + ... + +trait #2: ObjectImpl, DeviceImpl, ... +- functions: + fn unrealize(&self) + +Rust implementation split in configuration (Default + ConstDefault) and +state (Default) + +instance_init implemented automatically via Default/ConstDefault trait + maybe: pre_init hook that replaces memset(obj, 0, type->instance_size)? +instance_finalize implemented automatically via Drop trait diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index 5f926b8..0d91623 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -2,6 +2,12 @@ #![allow(dead_code)] pub mod bindings; +pub use bindings::Object; + +pub mod qom; +pub use qom::object::ObjectType; +pub use qom::refs::ObjectCast; +pub use qom::refs::Owned; pub mod util; pub use util::error::Error; diff --git a/qemu/src/qom/mod.rs b/qemu/src/qom/mod.rs new file mode 100644 index 0000000..95489c5 --- /dev/null +++ b/qemu/src/qom/mod.rs @@ -0,0 +1,2 @@ +pub mod object; +pub mod refs; diff --git a/qemu/src/qom/object.rs b/qemu/src/qom/object.rs new file mode 100644 index 0000000..bd6b957 --- /dev/null +++ b/qemu/src/qom/object.rs @@ -0,0 +1,34 @@ +//! Bindings for the QOM Object class +//! +//! @author Paolo Bonzini + +use std::ffi::CStr; + +use crate::bindings::Object; + +use crate::qom_isa; + +/// Trait exposed by all structs corresponding to QOM objects. +/// Defines "class methods" for the class. Usually these can be +/// implemented on the class itself; here, using a trait allows +/// each class to define `TYPE`, and it also lets `new()` return the +/// right type. +/// +/// # Safety +/// +/// - the first field of the struct must be of `Object` type, +/// or derived from it +/// +/// - `TYPE` must match the type name used in the `TypeInfo` (no matter +/// if it is defined in C or Rust). +/// +/// - the struct must be `#[repr(C)]` +pub unsafe trait ObjectType: Sized { + const TYPE: &'static CStr; +} + +unsafe impl ObjectType for Object { + const TYPE: &'static CStr = c"object"; +} + +qom_isa!(Object); diff --git a/qemu/src/qom/refs.rs b/qemu/src/qom/refs.rs new file mode 100644 index 0000000..a319bde --- /dev/null +++ b/qemu/src/qom/refs.rs @@ -0,0 +1,274 @@ +//! Casting and reference counting traits for QOM objects +//! +//! @author Paolo Bonzini + +use crate::bindings::object_dynamic_cast; +use crate::bindings::Object; +use crate::bindings::{object_ref, object_unref}; + +use crate::qom::object::ObjectType; + +use std::borrow::Borrow; +use std::mem::ManuallyDrop; +use std::ops::Deref; +use std::ptr::NonNull; + +/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct +/// or indirect parent of `Self`). +/// +/// # Safety +/// +/// The struct `Self` must begin, directly or indirectly, with a field of type +/// `P`. This ensures that invalid casts, which rely on `IsA<>` for static +/// checking, are rejected at compile time. +pub unsafe trait IsA: ObjectType {} + +// SAFETY: it is always safe to cast to your own type +unsafe impl IsA for T {} + +#[macro_export] +macro_rules! qom_isa { + ($struct:ty $(,$parent:ty)* ) => { + $( + impl AsRef<$parent> for $struct { + fn as_ref(&self) -> &$parent { + use $crate::ObjectCast; + self.upcast::<$parent>() + } + } + + // SAFETY: it is the caller responsibility to have $parent as the + // first field + unsafe impl $crate::qom::refs::IsA<$parent> for $struct {} + )* + }; +} + +/// Trait for a reference to a QOM object. Allows conversion to/from +/// C objects in generic code. +pub trait ObjectCast: Copy + Deref +where + Self::Target: ObjectType, +{ + /// Convert this (possibly smart) reference to a basic Rust reference. + fn as_ref(&self) -> &Self::Target { + self.deref() + } + + /// Convert to a const Rust pointer, to be used for example for FFI. + fn as_ptr(&self) -> *const Self::Target { + self.as_ref() + } + + /// Convert to a mutable Rust pointer, to be used for example for FFI. + /// Used to implement interior mutability for objects. + /// + /// # Safety + /// + /// This method is unsafe because it overrides const-ness of `&self`. + /// Bindings to C APIs will use it a lot, but otherwise it should not + /// be necessary. + unsafe fn as_mut_ptr(&self) -> *mut Self::Target { + #[allow(clippy::as_ptr_cast_mut)] + { + self.as_ptr().cast_mut() + } + } + + /// Perform a cast to a superclass + fn upcast<'a, U: ObjectType>(self) -> &'a U + where + Self::Target: IsA, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast::() } + } + + /// Perform a cast to a subclass. Checks at compile time that the + /// cast can succeed, but the final verification will happen at + /// runtime only. + fn downcast<'a, U: IsA>(self) -> Option<&'a U> + where + Self: 'a, + { + self.dynamic_cast::() + } + + /// Perform a cast between QOM types. The check that U is indeed + /// the dynamic type of `self` happens at runtime. + fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> + where + Self: 'a, + { + unsafe { + // SAFETY: upcasting to Object is always valid, and the + // return type is either NULL or the argument itself + let result: *const U = + object_dynamic_cast(self.unsafe_cast::().as_mut_ptr(), U::TYPE.as_ptr()) + .cast(); + + result.as_ref() + } + } + + /// Unconditional cast to an arbitrary QOM type. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct; only use + /// when performance is paramount. It is still better than a raw pointer + /// `cast()`, which does not even check that you remain in the realm of + /// QOM `ObjectType`s. + /// + /// `unsafe_cast::()` can also be used, and is always safe, if all + /// you have is an `ObjectType` (as opposed to an `IsA`). + unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U + where + Self: 'a, + { + &*(self.as_ptr().cast::()) + } +} + +impl ObjectCast for &T {} + +/// An owned reference to a QOM object. +/// +/// Like [`std::sync::Arc`], references are added with [`Clone::clone`] and removed +/// by dropping the `Owned`. +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Owned(NonNull); + +// QOM knows how to handle reference counting across threads, but sending +// the Owned to another thread requires the implementation itself to be +// thread-safe (aka Sync). But I'm not entirely sure that this is enough +// (see for example ARef in rust/kernel/types.rs, which is very similar +// to this type). +// +//unsafe impl Send for Owned {} +//unsafe impl Sync for Owned {} + +impl Owned { + /// Obtain a reference from a raw C pointer + /// + /// # Safety + /// + /// Typically this function will only be used by low level bindings + /// to C APIs. + 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_unchecked(ptr.cast_mut())) + } + + /// Obtain a raw C pointer from a reference. `src` is consumed + /// and the reference is leaked. + pub fn into_raw(src: Owned) -> *const T { + let src = ManuallyDrop::new(src); + src.0.as_ptr() + } + + + /// Increase the reference count of a QOM object and return + /// + /// # Safety + /// + /// Unsafe because the object could be embedded in another. To + /// obtain an `Owned` safely, use `ObjectType::new()`. + pub unsafe fn from(obj: &T) -> Self { + object_ref(obj.unsafe_cast::().as_mut_ptr()); + + // SAFETY NOTE: while NonNull requires a mutable pointer, + // only Deref is implemented so the pointer passed to from_raw + // remains const + Owned(NonNull::new_unchecked(obj.as_mut_ptr())) + } + + /// Perform a cast to a superclass + pub fn upcast(src: Owned) -> Owned + where + T: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { Owned::unsafe_cast::(src) } + } + + /// Perform a cast to a subclass. Checks at compile time that the + /// cast can succeed, but the final verification will happen at + /// runtime only. + pub fn downcast>(src: Owned) -> Result, Owned> { + Owned::dynamic_cast::(src) + } + + /// Perform a cast between QOM types. The check that U is indeed + /// the dynamic type of `self` happens at runtime. + pub fn dynamic_cast(src: Owned) -> Result, Owned> { + // override automatic drop to skip the unref/ref + let src = ManuallyDrop::new(src); + match src.dynamic_cast::() { + // get the ownership back from the ManuallyDrop<> + None => Err(ManuallyDrop::into_inner(src)), + + // SAFETY: the ref is moved (thanks to ManuallyDrop) from + // self to casted_ref + Some(casted_ref) => Ok(unsafe { Owned::::from_raw(casted_ref) }), + } + } + + /// Unconditional cast to an arbitrary QOM type. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct. Only use + /// when performance is paramount + pub unsafe fn unsafe_cast(src: Owned) -> Owned { + // override automatic drop to skip the unref/ref + let src = ManuallyDrop::new(src); + let casted_ref = src.unsafe_cast::(); + Owned::::from_raw(casted_ref) + } +} + +impl AsRef for Owned { + fn as_ref(&self) -> &T { + self.deref() + } +} + +impl Borrow for Owned { + fn borrow(&self) -> &T { + self.deref() + } +} + +impl Clone for Owned { + fn clone(&self) -> Self { + // SAFETY: creation method is unsafe, and whoever calls it + // has responsibility that the pointer is valid + unsafe { Owned::from(self.deref()) } + } +} + +impl Deref for Owned { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: creation method is unsafe, and whoever calls it + // has responsibility that the pointer has a static lifetime. + // Once that is guaranteed, reference counting ensures that + // the object remains alive. + unsafe { &*self.0.as_ptr() } + } +} + +impl Drop for Owned { + fn drop(&mut self) { + // SAFETY: creation method is unsafe, and whoever calls it + // has responsibility that the pointer is valid + unsafe { + object_unref(self.unsafe_cast::().as_mut_ptr()); + } + } +} From patchwork Mon Jul 1 14:58:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718242 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 6271DC2BD09 for ; Mon, 1 Jul 2024 15:00:36 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIV4-00040W-QE; Mon, 01 Jul 2024 10:59:26 -0400 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 1sOIV3-00040F-6y for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:25 -0400 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 1sOIV1-0005J7-BK for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:24 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845962; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ItGWoIXbmG8BtrqUqVyvFsub+0df4Uw051wHIRwFpYQ=; b=Vz5P1xNJFfocxSWg6BFpuV3hLzFDTq4S1NL4yOyeQCUdz6SMhNr9jrT2RNp8oh67pM3Dgs uT5JmbhZkxkGmoEEIs18+otWCg7PsJWQS3wiXQNI0y8hhY0VME/s66B2NtieJXvL/CZ9hl DquiQ6fXX2Lj+MtgENxbZkjMcGPIFRA= Received: from mail-lj1-f197.google.com (mail-lj1-f197.google.com [209.85.208.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-637-C33Z-AxVO5--EY7C0qhI0A-1; Mon, 01 Jul 2024 10:59:21 -0400 X-MC-Unique: C33Z-AxVO5--EY7C0qhI0A-1 Received: by mail-lj1-f197.google.com with SMTP id 38308e7fff4ca-2ee4a63e95cso31683741fa.1 for ; Mon, 01 Jul 2024 07:59:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845958; x=1720450758; 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=ItGWoIXbmG8BtrqUqVyvFsub+0df4Uw051wHIRwFpYQ=; b=D6mx4rjIokBajljUj3RUD4dM/3OAJmWgmQilMNjQHT/EAyiEWFjWmWQR+84VGCs6rE l/ieSwrmpeq107Y2rvk1ptmKMkEG8FoaAvFc4NfzyBPgOq3s+9eMhwvCLvbGSqa3SRGx tZcfoWo8YSbcd5n1SmxNal3AuWXdG/6k4QBf2egPhnR2Kbma5Cka1QkqAWOioJ3XVLr3 h9Z3ipXTSxfeFQj/w1fHwmaF9i6umg/oQVwz+51EgU1ug+hY2w2uhSQ7JiVT2MkihdNW 1LTle3SHnlJntF7Ip5G0atPBnpfz8+kRaXSnvmTIXW5eN1646NIjXvAEI2HbhGfXVw6D uimw== X-Gm-Message-State: AOJu0YyfAR+NxErlAOI1Jcw0V47eM5ip1QoRJuM/AzxUx4Rk9tlWDGWp UrI/NIlSzfqb1TkqshTjcpGJHnu0bwuHo1QNF26Wex48TEEGlTGsbDkKIFWVyUGqKqdQAofX7ga X+hhVm9yCpfBR9d1HtdeCF/piHaig8+n5gLq1hKty5SJ2AqTRQQW30XA6mcp6/zmOpq2M11AH8v h6V77Xw3x5xGAo0kEqTz4iNWFoQ/aU8W6yZols X-Received: by 2002:a05:6512:23a5:b0:52c:c9e9:7dc1 with SMTP id 2adb3069b0e04-52e82685af7mr4560246e87.32.1719845958302; Mon, 01 Jul 2024 07:59:18 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHrq4CQpqRSQh7VeAd2458ighSxU+mbTpCqs5YqpXlLzux0ViL0UX3tC1CM1gF0qK7GU+tlgA== X-Received: by 2002:a05:6512:23a5:b0:52c:c9e9:7dc1 with SMTP id 2adb3069b0e04-52e82685af7mr4560217e87.32.1719845957878; Mon, 01 Jul 2024 07:59:17 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4256b064f16sm157833445e9.27.2024.07.01.07.59.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:16 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 07/14] rust: define wrappers for methods of the QOM Object class Date: Mon, 1 Jul 2024 16:58:39 +0200 Message-ID: <20240701145853.1394967-8-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 Provide a trait that can be used to invoke methods of the QOM object class. The trait extends Deref and has a blanket implementation for any type that dereferences to IsA. This way, it can be used on any struct that dereferences to Object or a subclass. Signed-off-by: Paolo Bonzini --- qemu/src/lib.rs | 2 + qemu/src/qom/object.rs | 92 ++++++++++++++++++++++++++++++++++++++++++ qemu/src/qom/refs.rs | 8 ++++ 3 files changed, 102 insertions(+) diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index 0d91623..a6e7b17 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -5,6 +5,8 @@ pub mod bindings; pub use bindings::Object; pub mod qom; +pub use qom::object::ObjectClassMethods; +pub use qom::object::ObjectMethods; pub use qom::object::ObjectType; pub use qom::refs::ObjectCast; pub use qom::refs::Owned; diff --git a/qemu/src/qom/object.rs b/qemu/src/qom/object.rs index bd6b957..4e84e29 100644 --- a/qemu/src/qom/object.rs +++ b/qemu/src/qom/object.rs @@ -2,12 +2,26 @@ //! //! @author Paolo Bonzini +use std::borrow::Cow; use std::ffi::CStr; +use std::fmt; +use std::ops::Deref; +use crate::bindings::object_get_typename; +use crate::bindings::object_property_add_child; +use crate::bindings::object_new; +use crate::bindings::object_unparent; use crate::bindings::Object; use crate::qom_isa; +use crate::qom::refs::IsA; +use crate::qom::refs::ObjectCast; +use crate::qom::refs::Owned; + +use crate::util::foreign::CloneToForeign; +use crate::util::foreign::FromForeign; + /// Trait exposed by all structs corresponding to QOM objects. /// Defines "class methods" for the class. Usually these can be /// implemented on the class itself; here, using a trait allows @@ -31,4 +45,82 @@ unsafe impl ObjectType for Object { const TYPE: &'static CStr = c"object"; } +// ------------------------------ +// Object class + qom_isa!(Object); + +/// 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 { + // 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.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`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectMethods: Deref +where + Self::Target: IsA, +{ + /// Return the name of the type of `self` + fn typename(&self) -> Cow<'_, str> { + let obj = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + // The result of the C API has static lifetime + unsafe { + Cow::cloned_from_foreign(object_get_typename(obj.as_mut_ptr())) + } + } + + /// Add an object as a child of the receiver. + fn property_add_child(&self, name: &str, child: Owned) + { + let obj = self.upcast::(); + let name = name.clone_to_foreign(); + unsafe { + // SAFETY: casting to object is always safe even if `child`'s + // target type is an interface type + let child = child.unsafe_cast::(); + object_property_add_child(obj.as_mut_ptr(), + name.as_ptr(), + child.as_mut_ptr()); + + // object_property_add_child() added a reference of its own; + // dropping the one in `child` is the common case. + } + } + + /// Remove the object from the QOM tree + fn unparent(&self) { + let obj = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + unsafe { + object_unparent(obj.as_mut_ptr()); + } + } + + /// 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 ObjectClassMethods for R where R: IsA {} +impl ObjectMethods for R where R::Target: IsA {} diff --git a/qemu/src/qom/refs.rs b/qemu/src/qom/refs.rs index a319bde..431ef0a 100644 --- a/qemu/src/qom/refs.rs +++ b/qemu/src/qom/refs.rs @@ -6,9 +6,11 @@ use crate::bindings::object_dynamic_cast; use crate::bindings::Object; use crate::bindings::{object_ref, object_unref}; +use crate::qom::object::ObjectMethods; use crate::qom::object::ObjectType; use std::borrow::Borrow; +use std::fmt::{self, Debug}; use std::mem::ManuallyDrop; use std::ops::Deref; use std::ptr::NonNull; @@ -272,3 +274,9 @@ impl Drop for Owned { } } } + +impl> Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().debug_fmt(f) + } +} From patchwork Mon Jul 1 14:58:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718264 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 5063CC2BD09 for ; Mon, 1 Jul 2024 15:04:31 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIV6-00041c-9U; Mon, 01 Jul 2024 10:59:28 -0400 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 1sOIV4-00040Y-Q1 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:26 -0400 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 1sOIV3-0005JP-1E for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845964; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2fDHzvolcHSyEF2PMhkfCLwQi+naT9yNZR1yGvTHNhQ=; b=GmYTnDEwpZ4yqh6izq9MWGXXu0qbOsqDXKTQmN4B3MOJgWa7kflGxdm6g5INpOP5SaZpgi v/y1B0HmHSBSE6h035YY/JBMwbD6M6Yr1oPAt17eY5ADCxfgmPtCpG6+/34nu0N1OWJyek Cn0l7CRQHBnv4L4dDyO4lvdu61lqa2g= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-381-vrFeTYTVM_uV3EiCgik-9A-1; Mon, 01 Jul 2024 10:59:23 -0400 X-MC-Unique: vrFeTYTVM_uV3EiCgik-9A-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-424a5a5f024so20968345e9.3 for ; Mon, 01 Jul 2024 07:59:23 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845961; x=1720450761; 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=2fDHzvolcHSyEF2PMhkfCLwQi+naT9yNZR1yGvTHNhQ=; b=B8JM2sMw0t+1qCjcjP9JJfPCbLQ9KJZdz5jj7puIixENp/Sbb1vr+gq3qJm9hfaEhp mU3a63EdrDSQJMXSHyRvmlA+HEaqw5Mhe0wuwtw3EhZSGgT2gxZ8mARotXkfeuUnOsPv uo8YY+WMzQlo5TDpzgQffcU5d4juhebq1p7f8id8CIrqm08wmUivfMraHV2v3MD8x/hu ek6ExQWTQz6EnuDJrlSb20GJUtgUWQzGC4hjQrq2hA58wcQde/WgjI6LOa4195cIOpVK alTVD6B4MTBcbD8jhUT3IQiSG6KyZmmLcDj7ChZiNvkXELVNZjAP/0FTXwOpICUmOMEY 5F4A== X-Gm-Message-State: AOJu0Ywb5GsV26yNn4zQPeIz7IH0LTXT7Nzd23+wAFJZ186fuOJP7JGF OwpgikdFVYd8qm2RGDl8JXu7oFXB6bJ11PAz3N2dNSbEIEtknqMl0fany7rCqRYsNYqOpA24Swa Di86T4gdcxgWelZJvlvGqB2etHyFMN+ETksV6IjbtJKYm9jRP9oVUDsuwTeMHLsXEm3Uu+l2rLU /cQqNOpfWwlFPCWi1tNYUIX1XFNb/GyLb5MY4Z X-Received: by 2002:a05:600c:4213:b0:424:8fe1:41f9 with SMTP id 5b1f17b1804b1-4257a02b772mr40209665e9.11.1719845961095; Mon, 01 Jul 2024 07:59:21 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE3E0S5vdT8n7O2aIMFqSXbXsqL/8LiVmGLXQLHirZr3Ks/sXxuBRDzCKbPLFLyHa0ERNzeQg== X-Received: by 2002:a05:600c:4213:b0:424:8fe1:41f9 with SMTP id 5b1f17b1804b1-4257a02b772mr40209425e9.11.1719845960731; Mon, 01 Jul 2024 07:59:20 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a0d9385sm10201466f8f.39.2024.07.01.07.59.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:20 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 08/14] rust: define wrappers for methods of the QOM Device class Date: Mon, 1 Jul 2024 16:58:40 +0200 Message-ID: <20240701145853.1394967-9-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 Provide a trait that can be used to invoke methods of the QOM Device class. The trait extends Deref and has a blanket implementation for any type that dereferences to IsA. This way, it can be used on any struct that dereferences to Object or a subclass. Signed-off-by: Paolo Bonzini --- qemu/src/hw/core/device.rs | 56 ++++++++++++++++++++++++++++++++++++++ qemu/src/hw/core/mod.rs | 1 + qemu/src/hw/mod.rs | 1 + qemu/src/lib.rs | 4 +++ 4 files changed, 62 insertions(+) create mode 100644 qemu/src/hw/core/device.rs create mode 100644 qemu/src/hw/core/mod.rs create mode 100644 qemu/src/hw/mod.rs diff --git a/qemu/src/hw/core/device.rs b/qemu/src/hw/core/device.rs new file mode 100644 index 0000000..294251e --- /dev/null +++ b/qemu/src/hw/core/device.rs @@ -0,0 +1,56 @@ +//! Bindings for the QOM Device class +//! +//! @author Paolo Bonzini + +use crate::qom::object::ObjectType; + +use crate::qom::refs::IsA; +use crate::qom::refs::ObjectCast; + +use crate::bindings; +use crate::bindings::device_cold_reset; +use crate::bindings::device_realize; +use crate::bindings::DeviceState; +use crate::bindings::Object; + +use crate::qom_isa; + +use crate::Result; + +use std::ffi::CStr; +use std::ops::Deref; +use std::ptr::null_mut; + +unsafe impl ObjectType for DeviceState { + const TYPE: &'static CStr = c"device"; +} + +qom_isa!(DeviceState, Object); + +/// Trait for 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 DeviceMethods: Deref +where + Self::Target: IsA, +{ + fn realize(&self) -> Result<()> { + let device = self.upcast::(); + let mut err: *mut bindings::Error = null_mut(); + // SAFETY: safety of this is the requirement for implementing IsA + unsafe { + device_realize(device.as_mut_ptr(), &mut err); + crate::Error::err_or_default(err) + } + } + + fn cold_reset(&self) { + let device = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + unsafe { device_cold_reset(device.as_mut_ptr()) } + } +} + +impl DeviceMethods for R where R::Target: IsA {} diff --git a/qemu/src/hw/core/mod.rs b/qemu/src/hw/core/mod.rs new file mode 100644 index 0000000..5458924 --- /dev/null +++ b/qemu/src/hw/core/mod.rs @@ -0,0 +1 @@ +pub mod device; diff --git a/qemu/src/hw/mod.rs b/qemu/src/hw/mod.rs new file mode 100644 index 0000000..5a7ca06 --- /dev/null +++ b/qemu/src/hw/mod.rs @@ -0,0 +1 @@ +pub mod core; diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index a6e7b17..b0dcce1 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -2,8 +2,12 @@ #![allow(dead_code)] pub mod bindings; +pub use bindings::DeviceState; pub use bindings::Object; +pub mod hw; +pub use hw::core::device::DeviceMethods; + pub mod qom; pub use qom::object::ObjectClassMethods; pub use qom::object::ObjectMethods; From patchwork Mon Jul 1 14:58:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718243 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 72B9FC3065A for ; Mon, 1 Jul 2024 15:01:15 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIVF-00044F-1e; Mon, 01 Jul 2024 10:59:37 -0400 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 1sOIVC-00043X-UY for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:34 -0400 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 1sOIV8-0005Ke-Qa for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:33 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845969; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DEGKZAsNCxRe4msj+coj0klBr3yJvhJ3TzEiM0nG3fs=; b=PqakJVlniZd0wIUqnEaE/9c+WHDnXwKv6P63rnWncWgUvMwhnD6uNfpYwv7MGoq/bb4Ajt RHOicJRuehBuG7r3biG4jZcqqaaxQ63lD4P15yIFSI+d3woXqtkmDv/Q0GRt9ItxkAyvIi v/x+elHaPIGhKyLVoiGpA1yon4nJijA= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-157-RICNNUntO2qGYW7gBhQLkg-1; Mon, 01 Jul 2024 10:59:26 -0400 X-MC-Unique: RICNNUntO2qGYW7gBhQLkg-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-4256667ebf9so22789025e9.1 for ; Mon, 01 Jul 2024 07:59:25 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845964; x=1720450764; 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=DEGKZAsNCxRe4msj+coj0klBr3yJvhJ3TzEiM0nG3fs=; b=gLuFlV8Bn2HZ8aIqIqZlMqyrPTbktgAtZG/LkFnv+8BiiZd9ZB8ZyQTDITAr37WSET WdSWlPuJfE3abZM0JZKiRubVmJVnIuBsGh5zAZSpKtajZ5cHYRxq+P1XGVplAADvaj3z 337BW2t097SQUKF9bUClCMjdFsqLZ+onFi7KP78dy3quZwtXbj57+wvvHLxpb5wInH9k gKHzxeI75NjWgt5d1iEeJzM0nB340AWRTibBKBh1V3p+8mwLWLpygDHra4P6ZE9NFd89 aWpoOxm+YvvSqhN2yHAUey+5DpFxrmAGSKaYWAc2eG3X2Ttg6/A31LcxVFJa5+K75H0f TdgQ== X-Gm-Message-State: AOJu0YxDcLaJolIhSppp99R438pQZGhXdeUbFAC5HXV5+yGeQauV3Ct8 /nfpGXEoRqYjNtIiFBVFIFrs7r8056qK03+O/ADoL+TysIExlBFB9AGuddIB0xfNi9ol1uh5t8C CehJfFaAjw96CxhZRjwXH6DG2eVabgNdX8P7svz6VUH2hwt08ea8lOvAc6j4jpfhVu30rayW6oF BPQSYMrawBPZ2xFD/9RhydAMqXZwSpWhfp0Y9i X-Received: by 2002:a7b:ce15:0:b0:425:5fa2:60a8 with SMTP id 5b1f17b1804b1-4257a02fc72mr42828075e9.24.1719845963929; Mon, 01 Jul 2024 07:59:23 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE6AimDK+r53PynBwCen4/l1vp8ax4I7+MnkLb+V8YDarFrzgyg/jpKhdbK7n9bEJPpWZWjrw== X-Received: by 2002:a7b:ce15:0:b0:425:5fa2:60a8 with SMTP id 5b1f17b1804b1-4257a02fc72mr42827815e9.24.1719845963521; Mon, 01 Jul 2024 07:59:23 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4256b0c0f1bsm154656005e9.44.2024.07.01.07.59.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:23 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 09/14] rust: add idiomatic bindings to define Object subclasses Date: Mon, 1 Jul 2024 16:58:41 +0200 Message-ID: <20240701145853.1394967-10-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 Provide a macro to register a type and automatically define instance_init (actually instance_mem_init) and instance_finalize functions. Subclasses of Object must define a trait ObjectImpl, to point the type definition machinery to the implementation of virtual functions in Object. Signed-off-by: Paolo Bonzini --- qemu/src/lib.rs | 4 + qemu/src/qom/mod.rs | 1 + qemu/src/qom/object_impl.rs | 146 ++++++++++++++++++++++++++++++++++++ qemu/src/util/mod.rs | 1 + qemu/src/util/zeroed.rs | 21 ++++++ qemu/tests/main.rs | 32 ++++++++ 6 files changed, 205 insertions(+) create mode 100644 qemu/src/qom/object_impl.rs create mode 100644 qemu/src/util/zeroed.rs create mode 100644 qemu/tests/main.rs diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index b0dcce1..81abf9c 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -4,6 +4,7 @@ pub mod bindings; pub use bindings::DeviceState; pub use bindings::Object; +pub use bindings::TypeInfo; pub mod hw; pub use hw::core::device::DeviceMethods; @@ -12,6 +13,8 @@ pub mod qom; pub use qom::object::ObjectClassMethods; pub use qom::object::ObjectMethods; pub use qom::object::ObjectType; +pub use qom::object_impl::ObjectImpl; +pub use qom::object_impl::TypeImpl; pub use qom::refs::ObjectCast; pub use qom::refs::Owned; @@ -21,4 +24,5 @@ pub use util::foreign::CloneToForeign; pub use util::foreign::FromForeign; pub use util::foreign::IntoNative; pub use util::foreign::OwnedPointer; +pub use util::zeroed::Zeroed; pub type Result = std::result::Result; diff --git a/qemu/src/qom/mod.rs b/qemu/src/qom/mod.rs index 95489c5..3f8ee6e 100644 --- a/qemu/src/qom/mod.rs +++ b/qemu/src/qom/mod.rs @@ -1,2 +1,3 @@ pub mod object; +pub mod object_impl; pub mod refs; diff --git a/qemu/src/qom/object_impl.rs b/qemu/src/qom/object_impl.rs new file mode 100644 index 0000000..61546b6 --- /dev/null +++ b/qemu/src/qom/object_impl.rs @@ -0,0 +1,146 @@ +//! Macros and traits to implement subclasses of Object in Rust +//! +//! @author Paolo Bonzini + +#![allow(clippy::missing_safety_doc)] + +use const_default::ConstDefault; + +use std::ffi::c_void; +use std::mem; +use std::mem::MaybeUninit; +use std::ptr::drop_in_place; + +use crate::qom::object::ObjectType; + +use crate::qom::refs::ObjectCast; + +use crate::bindings::type_register; +use crate::bindings::Object; +use crate::bindings::ObjectClass; +use crate::bindings::TypeInfo; + +use crate::util::zeroed::Zeroed; + +/// Information on which superclass methods are overridden +/// by a Rust-implemented subclass of Object. +pub trait ObjectImpl: ObjectType { + /// If not `None`, a function that implements the `unparent` member + /// of the QOM `ObjectClass`. + const UNPARENT: Option = None; +} + +impl ObjectClass { + /// Initialize an `ObjectClass` from an `ObjectImpl`. + pub fn class_init(&mut self) { + unsafe extern "C" fn rust_unparent(obj: *mut Object) { + let f = T::UNPARENT.unwrap(); + f((&*obj).unsafe_cast::()) + } + self.unparent = T::UNPARENT.map(|_| rust_unparent:: as _); + } +} + +impl Object { + pub unsafe extern "C" fn rust_class_init( + klass: *mut c_void, + _data: *mut c_void, + ) { + let oc: &mut ObjectClass = &mut *(klass.cast()); + oc.class_init::(); + } +} + +/// Internal information on a Rust-implemented subclass of Object. +/// Only public because it is used by macros. +pub unsafe trait TypeImpl: ObjectType + ObjectImpl { + type Super: ObjectType; + type Conf: ConstDefault; + type State: Default; + + const CLASS_INIT: unsafe extern "C" fn(klass: *mut c_void, data: *mut c_void); + + fn uninit_conf(obj: &mut MaybeUninit) -> &mut MaybeUninit; + fn uninit_state(obj: &mut MaybeUninit) -> &mut MaybeUninit; +} + +unsafe fn rust_type_register() { + unsafe extern "C" fn rust_instance_mem_init(obj: *mut c_void) { + let obj: &mut std::mem::MaybeUninit = &mut *(obj.cast()); + + T::uninit_conf(obj).write(ConstDefault::DEFAULT); + T::uninit_state(obj).write(Default::default()); + } + + unsafe extern "C" fn rust_instance_finalize(obj: *mut c_void) { + let obj: *mut T = obj.cast(); + drop_in_place(obj); + } + + let ti = TypeInfo { + name: T::TYPE.as_ptr(), + parent: T::Super::TYPE.as_ptr(), + instance_size: mem::size_of::(), + instance_mem_init: Some(rust_instance_mem_init::), + instance_finalize: Some(rust_instance_finalize::), + class_init: Some(T::CLASS_INIT), + + // SAFETY: TypeInfo is defined in C and all fields are okay to be zeroed + ..Zeroed::zeroed() + }; + + type_register(&ti) +} + +#[macro_export] +macro_rules! qom_define_type { + ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty; @extends $super:ty $(,$supers:ty)*) => { + struct $struct { + // self.base dropped by call to superclass instance_finalize + base: std::mem::ManuallyDrop<$super>, + conf: $conf_ty, + state: $state_ty, + } + + // Define IsA markers for the struct itself and all the superclasses + $crate::qom_isa!($struct, $super $(,$supers)*); + + unsafe impl $crate::qom::object::ObjectType for $struct { + const TYPE: &'static std::ffi::CStr = $name; + } + + unsafe impl $crate::qom::object_impl::TypeImpl for $struct { + type Super = $super; + type Conf = $conf_ty; + type State = $state_ty; + + const CLASS_INIT: unsafe extern "C" fn(klass: *mut std::ffi::c_void, data: *mut std::ffi::c_void) + = <$super>::rust_class_init::; + + fn uninit_conf(obj: &mut std::mem::MaybeUninit::) -> &mut std::mem::MaybeUninit<$conf_ty> { + use std::ptr::addr_of_mut; + + // Projecting the incoming reference to a single field is safe, + // because the return value is also MaybeUnit + unsafe { &mut *(addr_of_mut!((*obj.as_mut_ptr()).conf).cast()) } + } + + fn uninit_state(obj: &mut std::mem::MaybeUninit::) -> &mut std::mem::MaybeUninit<$state_ty> { + use std::ptr::addr_of_mut; + + // Projecting the incoming reference to a single field is safe, + // because the return value is also MaybeUnit + unsafe { &mut *(addr_of_mut!((*obj.as_mut_ptr()).state).cast()) } + } + } + + // TODO: call rust_type_register + }; +} + +#[macro_export] +macro_rules! conf_type { + ($type:ty) => { + <$type as $crate::qom::object_impl::TypeImpl>::Conf + }; +} diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs index e6078ac..9c081b6 100644 --- a/qemu/src/util/mod.rs +++ b/qemu/src/util/mod.rs @@ -1,2 +1,3 @@ pub mod error; pub mod foreign; +pub mod zeroed; diff --git a/qemu/src/util/zeroed.rs b/qemu/src/util/zeroed.rs new file mode 100644 index 0000000..e656834 --- /dev/null +++ b/qemu/src/util/zeroed.rs @@ -0,0 +1,21 @@ +#![allow(clippy::undocumented_unsafe_blocks)] + +use std::mem::MaybeUninit; + +/// Trait providing an easy way to obtain an all-zero +/// value for a struct +/// +/// # Safety +/// +/// Only add this to a type if `MaybeUninit::zeroed().assume_init()` +/// is valid for that type. +pub unsafe trait Zeroed: Sized { + fn zeroed() -> Self { + // SAFETY: If this weren't safe, just do not add the + // trait to a type. + unsafe { MaybeUninit::zeroed().assume_init() } + } +} + +// Put here all the impls that you need for the bindgen-provided types. +unsafe impl Zeroed for crate::bindings::TypeInfo {} diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs new file mode 100644 index 0000000..a7cbeed --- /dev/null +++ b/qemu/tests/main.rs @@ -0,0 +1,32 @@ +use const_default::ConstDefault; + +use qemu::qom_define_type; +use qemu::Object; +use qemu::ObjectClassMethods; +use qemu::ObjectImpl; + +#[derive(Default, ConstDefault)] +struct TestConf { + #[allow(dead_code)] + foo: bool, +} + +#[derive(Default)] +struct TestState { + #[allow(dead_code)] + bar: i32, +} + +qom_define_type!( + c"test-object", + TestObject, + TestConf, + (); + @extends Object +); + +impl ObjectImpl for TestObject {} + +fn main() { + drop(TestObject::new()); +} From patchwork Mon Jul 1 14:58:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718261 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 F3643C2BD09 for ; Mon, 1 Jul 2024 15:03:14 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIVL-00045D-Ip; Mon, 01 Jul 2024 10:59:43 -0400 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 1sOIVK-00044q-19 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:42 -0400 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 1sOIVB-0005L8-I3 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845972; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kFn8a5fReLo+S4SarZhzaU3e4BbwOPj+DmW1DUwZMXo=; b=E1vI6ayGcZtcsRjzVouLeXS5LmXgS0hrbWLtblBdeFZedjHq+tDM3owGKGQ6Qp1liRT+/9 3VpljY2bXvVoFR2l0upGsvZAfHoTSWcf5NgG+8xkyTvpIfvJ0wRtoE1gd9fY5pqJ9hoXPC BwUQYKlKiBYglmuaw7CjUjyw8aMgfHk= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-60-pp4OJrtGNNWnSZzbcDuyKg-1; Mon, 01 Jul 2024 10:59:28 -0400 X-MC-Unique: pp4OJrtGNNWnSZzbcDuyKg-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-424a775ee7fso22872765e9.0 for ; Mon, 01 Jul 2024 07:59:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845967; x=1720450767; 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=kFn8a5fReLo+S4SarZhzaU3e4BbwOPj+DmW1DUwZMXo=; b=d+pFcr4dAH+WNzK6KihdhZcBzTttX7DScrqh15Q612Dp88osLHv/OV3qapDiCw3mTX 5VN+ZNyefdZ+ZYiwQ2UZXZn4QyHup2veioBl2mcVLrzdAAgHFzuofNGeHQGWqkFpfyP+ 4sfM9EltNH6jF4wM/0CyedbEHRyoQrMD80Hnfj59p5Jzdh+dXW+nAQa1U9XuCbjDCaNt XMr0EcSvrzm72AYoo+6Qofd05b8ARzTCXndlrDzVshgKVmyVtu/cyJ0Nir1ctbR5coxG vi6BkoMF+WYSLaWHbwe/nMHS354BWR3T7aGAkQSQhqrUDXYfoLpZzxqIubKHB9d8QxkU fQiw== X-Gm-Message-State: AOJu0YyeJXLO8Qd3Hpca/z3g9EWg4bonW5gJqzmvk105UQa+CNFE97ef hHzaMdh4ioO8E1nMjC5QoGdggWRhpjDf+49FYADF5Rvz9IX3+ogPhnscJaGjTu8pa77swIjcLmL XWozflv7sJweRvnOXGladxUlQ5KgMVlREbA6Axv6RnRwDN7RiG0LO+YgwGJf+uDzl/zCl9FIb4m NNc0SXla3/NYaYUIYT6NJkTxKZRDTITxI310Uy X-Received: by 2002:a5d:5f8e:0:b0:363:7788:b975 with SMTP id ffacd0b85a97d-36775721529mr4047303f8f.52.1719845966872; Mon, 01 Jul 2024 07:59:26 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEy1qi0DnHKrmHTxoOgaGg/kZjQppCGzPd4Wb/whzeKfCyDi6+eAC45fD2XzhbQUkYllkUWZQ== X-Received: by 2002:a5d:5f8e:0:b0:363:7788:b975 with SMTP id ffacd0b85a97d-36775721529mr4047279f8f.52.1719845966483; Mon, 01 Jul 2024 07:59:26 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a1030dfsm10236997f8f.100.2024.07.01.07.59.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:25 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 10/14] rust: add idiomatic bindings to define Device subclasses Date: Mon, 1 Jul 2024 16:58:42 +0200 Message-ID: <20240701145853.1394967-11-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, T_SPF_TEMPERROR=0.01 autolearn=no 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 Provide a macro to register a type and automatically define qdev properties. Subclasses of DeviceState must define a trait DeviceImpl, to point the type definition machinery to the implementation of virtual functions in DeviceState. Signed-off-by: Paolo Bonzini --- qemu/src/hw/core/device_impl.rs | 140 ++++++++++++++++++++++++++++++++ qemu/src/hw/core/mod.rs | 1 + qemu/src/lib.rs | 5 ++ qemu/tests/main.rs | 52 +++++++++++- 4 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 qemu/src/hw/core/device_impl.rs diff --git a/qemu/src/hw/core/device_impl.rs b/qemu/src/hw/core/device_impl.rs new file mode 100644 index 0000000..80b0e5e --- /dev/null +++ b/qemu/src/hw/core/device_impl.rs @@ -0,0 +1,140 @@ +//! Macros and traits to implement subclasses of Device in Rust +//! +//! @author Paolo Bonzini + +#![allow(clippy::missing_safety_doc)] + +use std::ffi::c_void; + +use crate::bindings; +use crate::bindings::DeviceClass; +use crate::bindings::DeviceState; +use crate::bindings::Property; + +use crate::qom::object_impl::ObjectImpl; +use crate::qom::object_impl::TypeImpl; + +use crate::qom::refs::ObjectCast; + +use crate::util::error::Error; + +/// Information on which superclass methods are overridden +/// by a Rust-implemented subclass of Device. +pub trait DeviceImpl: ObjectImpl + DeviceTypeImpl { + /// If not `None`, a function that implements the `realize` member + /// of the QOM `DeviceClass`. + const REALIZE: Option crate::Result<()>> = None; + + /// If not `None`, a function that implements the `unrealize` member + /// of the QOM `DeviceClass`. + const UNREALIZE: Option = None; + + /// If not `None`, a function that implements the `cold_reset` member + /// of the QOM `DeviceClass`. + const COLD_RESET: Option = None; +} + +impl DeviceClass { + pub fn class_init(&mut self) { + unsafe extern "C" fn rust_cold_reset(obj: *mut DeviceState) { + let f = T::COLD_RESET.unwrap(); + f((&*obj).unsafe_cast::()) + } + self.cold_reset = T::COLD_RESET.map(|_| rust_cold_reset:: as _); + + unsafe extern "C" fn rust_realize( + obj: *mut DeviceState, + errp: *mut *mut bindings::Error, + ) { + let f = T::REALIZE.unwrap(); + let result = f((&*obj).unsafe_cast::()); + Error::ok_or_propagate(result, errp); + } + self.realize = T::REALIZE.map(|_| rust_realize:: as _); + + unsafe extern "C" fn rust_unrealize(obj: *mut DeviceState) { + let f = T::UNREALIZE.unwrap(); + f((&*obj).unsafe_cast::()) + } + self.unrealize = T::UNREALIZE.map(|_| rust_unrealize:: as _); + + self.properties = ::properties(); + + // Now initialize the ObjectClass from the ObjectImpl. + self.oc.class_init::(); + } +} + +impl DeviceState { + pub unsafe extern "C" fn rust_class_init( + klass: *mut c_void, + _data: *mut c_void, + ) { + let dc: &mut DeviceClass = &mut *(klass.cast()); + dc.class_init::(); + } +} + +/// Internal information on a Rust-implemented subclass of Device. +/// Only public because it is used by macros. +pub unsafe trait DeviceTypeImpl: TypeImpl { + const CONF_OFFSET: usize; + + // This needs to be here, and not in DeviceImpl, because properties + // reference statics (for globals defined in C, e.g. qdev_prop_bool) + // which is unstable (see https://github.com/rust-lang/rust/issues/119618, + // feature const_refs_to_static) + fn properties() -> *const Property; +} + +pub struct QdevPropBool; +impl QdevPropBool { + pub const fn convert(value: &bool) -> u64 { + *value as u64 + } +} + +#[macro_export] +macro_rules! qdev_prop { + (@internal bool, $name:expr, $default:expr, $offset:expr) => { + $crate::Property { + name: $name.as_ptr(), + offset: $offset, + default: $crate::hw::core::device_impl::QdevPropBool::convert(&($default)), + info: unsafe { &$crate::bindings::qdev_prop_bool }, + } + }; + + // Replace field with typechecking expression and offset + ($kind:tt, $name:expr, $type:ty, $default:expr, $field:ident) => { + qdev_prop!(@internal + $kind, + $name, + (<$crate::conf_type!($type) as ConstDefault>::DEFAULT).$field, + <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + std::mem::offset_of!($crate::conf_type!($type), $field) + ) + }; +} + +#[macro_export] +macro_rules! qdev_define_type { + ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty; + @extends $super:ty $(,$supers:ty)*; + @properties [$($props: expr),+]) => { + $crate::qom_define_type!( + $name, $struct, $conf_ty, $state_ty; + @extends $super $(,$supers)*, $crate::Object); + + unsafe impl $crate::DeviceTypeImpl for $struct { + const CONF_OFFSET: usize = std::mem::offset_of!($struct, conf); + + fn properties() -> *const $crate::Property { + static mut PROPERTIES: &'static [$crate::Property] = &[$($props),+]; + + // SAFETY: The only reference is created here; mut is needed to refer to + // &qdev_prop_xxx. + unsafe { PROPERTIES.as_ptr() } + } + } + } +} diff --git a/qemu/src/hw/core/mod.rs b/qemu/src/hw/core/mod.rs index 5458924..6cd9197 100644 --- a/qemu/src/hw/core/mod.rs +++ b/qemu/src/hw/core/mod.rs @@ -1 +1,2 @@ pub mod device; +pub mod device_impl; diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index 81abf9c..3f0491c 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -2,12 +2,17 @@ #![allow(dead_code)] pub mod bindings; +pub use bindings::DeviceClass; pub use bindings::DeviceState; pub use bindings::Object; +pub use bindings::Property; +pub use bindings::PropertyInfo; pub use bindings::TypeInfo; pub mod hw; pub use hw::core::device::DeviceMethods; +pub use hw::core::device_impl::DeviceImpl; +pub use hw::core::device_impl::DeviceTypeImpl; pub mod qom; pub use qom::object::ObjectClassMethods; diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs index a7cbeed..e499c14 100644 --- a/qemu/tests/main.rs +++ b/qemu/tests/main.rs @@ -5,9 +5,18 @@ use qemu::Object; use qemu::ObjectClassMethods; use qemu::ObjectImpl; +use qemu::qdev_define_type; +use qemu::qdev_prop; +use qemu::DeviceImpl; +use qemu::DeviceMethods; +use qemu::DeviceState; + +use qemu::Result; + +use std::cell::RefCell; + #[derive(Default, ConstDefault)] struct TestConf { - #[allow(dead_code)] foo: bool, } @@ -27,6 +36,47 @@ qom_define_type!( impl ObjectImpl for TestObject {} +qdev_define_type!( + c"test-device", + TestDevice, + TestConf, + RefCell; + @extends DeviceState; + @properties [qdev_prop!(bool, c"foo", TestDevice, true, foo)] +); + +impl TestDevice { + #[allow(clippy::unused_self)] + fn unparent(&self) { + println!("unparent"); + } + + #[allow(clippy::unused_self)] + fn realize(&self) -> Result<()> { + println!("realize"); + Ok(()) + } + + #[allow(clippy::unused_self)] + fn unrealize(&self) { + println!("unrealize"); + } +} + +impl ObjectImpl for TestDevice { + const UNPARENT: Option = Some(TestDevice::unparent); +} + +impl DeviceImpl for TestDevice { + const REALIZE: Option Result<()>> = Some(TestDevice::realize); + const UNREALIZE: Option = Some(TestDevice::unrealize); +} + fn main() { drop(TestObject::new()); + + let d = TestDevice::new(); + d.realize().unwrap(); + d.cold_reset(); + d.unparent(); } From patchwork Mon Jul 1 14:58:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718257 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 495CFC2BD09 for ; Mon, 1 Jul 2024 15:01:48 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIVS-0004Pk-DN; Mon, 01 Jul 2024 10:59:50 -0400 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 1sOIVQ-0004M2-El for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:48 -0400 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 1sOIVB-0005LJ-Nd for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:48 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845973; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ATkDSIn1AD4iloFEBRdTrGDcFCBr2wUtEchDeQI6v+Q=; b=ErwXH893U6f3EPXmS9wr5FVZu/nCulC2NsNmAfRooQGdf+CHeJANgk8x8UapRA2wZB3Fm+ h8rJntb/nZajO+aeDBQe6n7Qo5tg8ZK8zSVjbgJxo2hC11SzeEPQ7fOTpGfnM2Cwiui5U3 p2Z/NFx12AyCMceFJdnkxkH/bb3aJWE= Received: from mail-lf1-f71.google.com (mail-lf1-f71.google.com [209.85.167.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-655-SwrYpLGaMSm5psLRhGgHaw-1; Mon, 01 Jul 2024 10:59:31 -0400 X-MC-Unique: SwrYpLGaMSm5psLRhGgHaw-1 Received: by mail-lf1-f71.google.com with SMTP id 2adb3069b0e04-52d174e26baso4072166e87.2 for ; Mon, 01 Jul 2024 07:59:31 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845969; x=1720450769; 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=ATkDSIn1AD4iloFEBRdTrGDcFCBr2wUtEchDeQI6v+Q=; b=rUgyIgwcb09Fn7SxTBECaatROdXsoS7+bipkU6dMcmoTDtUOiZB4tS+Rs2wMkfn1oU MVwwfvrLMtZ2nnPQHtQZ/tum/zy+Mhaxpp2iOEIWFEx7fY+4k4JIkI/1BVfa4pw2ahUi 0/KGxU5ay/S3k84Vjirxucdfm1HDWQWBnA0acAkFVXJN1lW5dQD+xWAJ6Tj+GBfZNuQ3 nA9f3HTajc3dasaOJbQXo4UFHCX55C3dAxo+gXSnOhNOq3CSqtJImeYbAO+wC/d1cPIr OGBjQtBOz+WwS9gHPS13CYjmcLQglZvR+plYadc6+29ZDoYb6agNsRidY/iXgJmwNYmQ tPYw== X-Gm-Message-State: AOJu0YyA5mLWtZVHMb6M8szIz3QUlgaHQvj6PQZfNEh4aRluaKO4bae0 b+H0mh6cNtmVXgecKf9+PfVo9ju+7+XZtoWrdXWRDtlnEeHk7ibIi6aamSIYsH1rFlIu/cT7K/8 oKOQXHiAx+Hi0i5/UL3RppHN54jUdbewvWnosnRDEljqf+HSTWUI/ik+tumkI202Z6glCCSNngc rkcNBKEbrrhIqaUldR4oi9ZtpMRz+3niIJGBMv X-Received: by 2002:a05:6512:2807:b0:52e:764b:b20d with SMTP id 2adb3069b0e04-52e8266df15mr4109348e87.28.1719845969613; Mon, 01 Jul 2024 07:59:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGRUwwyRrlhMLiBz8bYjke97OF81aIw/lI/5KWh8bhZEqd1yOGGLzXZGVgy1sfMMY1nE4sg2A== X-Received: by 2002:a05:6512:2807:b0:52e:764b:b20d with SMTP id 2adb3069b0e04-52e8266df15mr4109332e87.28.1719845969238; Mon, 01 Jul 2024 07:59:29 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4256af3cf9bsm159885515e9.8.2024.07.01.07.59.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:28 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 11/14] rust: replace std::ffi::c_char with libc::c_char Date: Mon, 1 Jul 2024 16:58:43 +0200 Message-ID: <20240701145853.1394967-12-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, 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 Allow working with Rust versions prior to 1.64. Signed-off-by: Paolo Bonzini --- qemu/src/bindings/mod.rs | 3 ++- qemu/src/util/foreign.rs | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/qemu/src/bindings/mod.rs b/qemu/src/bindings/mod.rs index a49447b..0ad3828 100644 --- a/qemu/src/bindings/mod.rs +++ b/qemu/src/bindings/mod.rs @@ -1,4 +1,5 @@ -use std::ffi::{c_char, c_void}; +use libc::c_char; +use std::ffi::c_void; #[repr(C)] pub struct Object { diff --git a/qemu/src/util/foreign.rs b/qemu/src/util/foreign.rs index 0b8b708..464400a 100644 --- a/qemu/src/util/foreign.rs +++ b/qemu/src/util/foreign.rs @@ -4,8 +4,11 @@ /// Traits to map between C structs and native Rust types. /// Similar to glib-rs but a bit simpler and possibly more /// idiomatic. + +use libc::c_char; + use std::borrow::Cow; -use std::ffi::{c_char, c_void, CStr, CString}; +use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::fmt::Debug; use std::mem; @@ -166,7 +169,7 @@ pub trait FromForeign: CloneToForeign + Sized { /// # use qemu::FromForeign; /// let p = c"Hello, world!".as_ptr(); /// let s = unsafe { - /// String::cloned_from_foreign(p as *const std::ffi::c_char) + /// String::cloned_from_foreign(p as *const libc::c_char) /// }; /// assert_eq!(s, "Hello, world!"); /// ``` From patchwork Mon Jul 1 14:58:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718260 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 D3D4AC3065A for ; Mon, 1 Jul 2024 15:02:39 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIVN-00046Z-Fl; Mon, 01 Jul 2024 10:59:45 -0400 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 1sOIVL-00045A-ER for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:43 -0400 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 1sOIVF-0005NO-P6 for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:43 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845976; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=S6tUQLMOUhCwh40f/djXK9f9MtwQXr/0BnjScvVQiDg=; b=BZP9CGBNzbz2UhvtSwaNrkKRnyxJuGzBHPd1LbXfC2ACOP7l9xzQtxgx7OAfiZXLOdbeM7 eJKtgK+LaVypiu7sHRz4ahb6osFH+zMQ6UzmCWiik/X5oHItIjHBIQGwlxxNVVdSi1wPnB RURMK7MEp1lAgqmXPxcEu9lBXJBs94Q= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-516-pPjkiIKAOgKSLHsSNLI_5A-1; Mon, 01 Jul 2024 10:59:34 -0400 X-MC-Unique: pPjkiIKAOgKSLHsSNLI_5A-1 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-36725ec0f7dso1789636f8f.0 for ; Mon, 01 Jul 2024 07:59:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845972; x=1720450772; 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=S6tUQLMOUhCwh40f/djXK9f9MtwQXr/0BnjScvVQiDg=; b=aF+tJXeLA/vCbGeePaSJ6jm9aqyJG32b+aL1DFTJWABcPA6p1bgSFX5TwhgpePiEJM ywYecaqmH4HDqIvzyUV/lgifoOWUlsBCi3yLCT9mSwMP2q2ey/1/tGPp+Ai/yJqBgw1G iMdrMHt1eR9fhKg9dTcMnlEyWjgTZz3sd/8/JUft2XxBeBZCes4Piafm2f78yzx9u4mQ MLRXZIwE4B/BVqvS4XjBjvYGg4aqH4wkMYyzxTwmxVq+ZCHvuKTcuyq9Gy2nEwf92jnq NF2VuXkQkY697ttSeUh1R7AxjZD+pFsf04yBj1PlX1jiwyxaF9aO902neQBnFRFDN3i6 Y5hA== X-Gm-Message-State: AOJu0Yw9hnKw2QeTTr7BLgWRpn1CpjB/HShhiHvLsfT5PTJdc7wXEDe0 bcUxKx0FZZSh1ngOdG3I8q0dl2C0rIMRSDDuiFaZdYvtDhA0hYgW6QR/3mIbdZDXqagtb+2y13v WSyzEeWQFxeZx6u3xfRJtIvtuDopuMVUJN0xdSeLQAQNDZqmj9Z9nss01NME1ndLsi1MN5lDDlM OGAucJLWanXV86RL68E5g0QCSiGoU1A98lYebe X-Received: by 2002:a05:6000:cc1:b0:35f:1f28:2ec9 with SMTP id ffacd0b85a97d-367757309b3mr4591235f8f.70.1719845972471; Mon, 01 Jul 2024 07:59:32 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHUs8iJiZlS9gIYM6LIQ8vGya0LH7I8sevS5ByHUq2hKrw+Xtip4wuC3ch865IK6PTZOxjwVQ== X-Received: by 2002:a05:6000:cc1:b0:35f:1f28:2ec9 with SMTP id ffacd0b85a97d-367757309b3mr4591205f8f.70.1719845972000; Mon, 01 Jul 2024 07:59:32 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a0e1412sm10232820f8f.53.2024.07.01.07.59.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:31 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 12/14] rust: replace c"" literals with cstr crate Date: Mon, 1 Jul 2024 16:58:44 +0200 Message-ID: <20240701145853.1394967-13-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 Part of what's needed to work with Rust versions prior to 1.77. Signed-off-by: Paolo Bonzini --- qemu/Cargo.toml | 3 +++ qemu/qom-rust.txt | 2 +- qemu/src/hw/core/device.rs | 4 +++- qemu/src/qom/object.rs | 4 +++- qemu/src/util/error.rs | 4 +++- qemu/src/util/foreign.rs | 20 +++++++++++--------- qemu/tests/main.rs | 7 ++++--- 8 files changed, 39 insertions(+), 16 deletions(-) diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml index 1100725..a07a449 100644 --- a/qemu/Cargo.toml +++ b/qemu/Cargo.toml @@ -7,5 +7,8 @@ edition = "2021" const-default = { version = "~1", features = ["derive"] } libc = "^0" +# pick older version in order to support Rust 1.63 +cstr = { version = "=0.2.10" } + [dev-dependencies] matches = ">=0" diff --git a/qemu/qom-rust.txt b/qemu/qom-rust.txt index 1588445..ef4bd06 100644 --- a/qemu/qom-rust.txt +++ b/qemu/qom-rust.txt @@ -48,7 +48,7 @@ Bindings for C classes struct must implement ObjectType unsafe impl ObjectType for Object { - const TYPE: &'static CStr = c"object"; + const TYPE: &'static CStr = cstr!("object"); } struct must implement IsA for all superclasses T diff --git a/qemu/src/hw/core/device.rs b/qemu/src/hw/core/device.rs index 294251e..4edf61d 100644 --- a/qemu/src/hw/core/device.rs +++ b/qemu/src/hw/core/device.rs @@ -17,12 +17,14 @@ use crate::qom_isa; use crate::Result; +use cstr::cstr; + use std::ffi::CStr; use std::ops::Deref; use std::ptr::null_mut; unsafe impl ObjectType for DeviceState { - const TYPE: &'static CStr = c"device"; + const TYPE: &'static CStr = cstr!("device"); } qom_isa!(DeviceState, Object); diff --git a/qemu/src/qom/object.rs b/qemu/src/qom/object.rs index 4e84e29..9f6c078 100644 --- a/qemu/src/qom/object.rs +++ b/qemu/src/qom/object.rs @@ -7,6 +7,8 @@ use std::ffi::CStr; use std::fmt; use std::ops::Deref; +use cstr::cstr; + use crate::bindings::object_get_typename; use crate::bindings::object_property_add_child; use crate::bindings::object_new; @@ -42,7 +44,7 @@ pub unsafe trait ObjectType: Sized { } unsafe impl ObjectType for Object { - const TYPE: &'static CStr = c"object"; + const TYPE: &'static CStr = cstr!("object"); } // ------------------------------ diff --git a/qemu/src/util/error.rs b/qemu/src/util/error.rs index e7e6f2e..79c3c81 100644 --- a/qemu/src/util/error.rs +++ b/qemu/src/util/error.rs @@ -7,6 +7,8 @@ use crate::bindings::error_free; use crate::bindings::error_get_pretty; use crate::bindings::error_setg_internal; +use cstr::cstr; + use std::ffi::CStr; use std::fmt::{self, Display}; use std::ptr; @@ -215,7 +217,7 @@ impl CloneToForeign for Error { ptr::null_mut(), // FIXME 0, ptr::null_mut(), // FIXME - c"%s".as_ptr(), + cstr!("%s").as_ptr(), format!("{}", self), ); OwnedPointer::new(x) diff --git a/qemu/src/util/foreign.rs b/qemu/src/util/foreign.rs index 464400a..7a663cc 100644 --- a/qemu/src/util/foreign.rs +++ b/qemu/src/util/foreign.rs @@ -167,7 +167,8 @@ pub trait FromForeign: CloneToForeign + Sized { /// /// ``` /// # use qemu::FromForeign; - /// let p = c"Hello, world!".as_ptr(); + /// # use cstr::cstr; + /// let p = cstr!("Hello, world!").as_ptr(); /// let s = unsafe { /// String::cloned_from_foreign(p as *const libc::c_char) /// }; @@ -476,6 +477,7 @@ mod tests { #![allow(clippy::shadow_unrelated)] use super::*; + use cstr::cstr; use matches::assert_matches; use std::ffi::c_void; @@ -498,7 +500,7 @@ mod tests { #[test] fn test_cloned_from_foreign_string_cow() { let s = "Hello, world!".to_string(); - let cstr = c"Hello, world!"; + let cstr = cstr!("Hello, world!"); let cloned = unsafe { Cow::cloned_from_foreign(cstr.as_ptr()) }; assert_eq!(s, cloned); } @@ -506,7 +508,7 @@ mod tests { #[test] fn test_cloned_from_foreign_string() { let s = "Hello, world!".to_string(); - let cstr = c"Hello, world!"; + let cstr = cstr!("Hello, world!"); let cloned = unsafe { String::cloned_from_foreign(cstr.as_ptr()) }; assert_eq!(s, cloned); } @@ -570,7 +572,7 @@ mod tests { #[test] fn test_clone_to_foreign_str() { let s = "Hello, world!"; - let p = c"Hello, world!".as_ptr(); + let p = cstr!("Hello, world!").as_ptr(); let cloned = s.clone_to_foreign(); unsafe { let len = libc::strlen(cloned.as_ptr()); @@ -588,7 +590,7 @@ mod tests { #[test] fn test_clone_to_foreign_cstr() { - let s: &CStr = c"Hello, world!"; + let s: &CStr = cstr!("Hello, world!"); let cloned = s.clone_to_foreign(); unsafe { let len = libc::strlen(cloned.as_ptr()); @@ -606,7 +608,7 @@ mod tests { #[test] fn test_clone_to_foreign_string_cow() { - let p = c"Hello, world!".as_ptr(); + let p = cstr!("Hello, world!").as_ptr(); for s in vec![ Into::>::into("Hello, world!"), Into::>::into("Hello, world!".to_string())] { @@ -663,7 +665,7 @@ mod tests { #[test] fn test_clone_to_foreign_string() { let s = "Hello, world!".to_string(); - let cstr = c"Hello, world!"; + let cstr = cstr!("Hello, world!"); let cloned = s.clone_to_foreign(); unsafe { let len = libc::strlen(cloned.as_ptr()); @@ -683,7 +685,7 @@ mod tests { fn test_option() { // An Option can be used to produce or convert NULL pointers let s = Some("Hello, world!".to_string()); - let cstr = c"Hello, world!"; + let cstr = cstr!("Hello, world!"); unsafe { assert_eq!(Option::::cloned_from_foreign(cstr.as_ptr()), s); assert_matches!(Option::::cloned_from_foreign(ptr::null()), None); @@ -695,7 +697,7 @@ mod tests { fn test_box() { // A box can be produced if the inner type has the capability. let s = Box::new("Hello, world!".to_string()); - let cstr = c"Hello, world!"; + let cstr = cstr!("Hello, world!"); let cloned = unsafe { Box::::cloned_from_foreign(cstr.as_ptr()) }; assert_eq!(s, cloned); diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs index e499c14..601e92b 100644 --- a/qemu/tests/main.rs +++ b/qemu/tests/main.rs @@ -1,4 +1,5 @@ use const_default::ConstDefault; +use cstr::cstr; use qemu::qom_define_type; use qemu::Object; @@ -27,7 +28,7 @@ struct TestState { } qom_define_type!( - c"test-object", + cstr!("test-object"), TestObject, TestConf, (); @@ -37,12 +38,12 @@ qom_define_type!( impl ObjectImpl for TestObject {} qdev_define_type!( - c"test-device", + cstr!("test-device"), TestDevice, TestConf, RefCell; @extends DeviceState; - @properties [qdev_prop!(bool, c"foo", TestDevice, true, foo)] + @properties [qdev_prop!(bool, cstr!("foo"), TestDevice, true, foo)] ); impl TestDevice { From patchwork Mon Jul 1 14:58:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718262 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 8B593C3065A for ; Mon, 1 Jul 2024 15:04:06 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIVP-0004GD-9t; Mon, 01 Jul 2024 10:59:47 -0400 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 1sOIVN-00047L-Kv for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:45 -0400 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 1sOIVH-0005Uz-9G for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:45 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845978; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=L1+NBzK4iA++gxlMtSNv140MQlgSKu48htjyD75ufEs=; b=Se2ibSAeyu2xxxxKMR1oikZg66mpj+dMsc11RAqDwQZTEZFfIihyr7uiDOkG4O1W3ho5mh HR1u5hYjTOjVKg4ZZcadGJdO6Uj8PsP4zHe1gzyQu81sPDNcVzRbsEZO6UWKFDhioae1s8 RdY8YZNKfW3sHvI05I48+ghYj76kfWA= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-634-p9dS2SUYOtWrIOMcy1owPA-1; Mon, 01 Jul 2024 10:59:37 -0400 X-MC-Unique: p9dS2SUYOtWrIOMcy1owPA-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-4258675a531so4696995e9.3 for ; Mon, 01 Jul 2024 07:59:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845975; x=1720450775; 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=L1+NBzK4iA++gxlMtSNv140MQlgSKu48htjyD75ufEs=; b=Vhuhg3vQ94rrPLrbkRn0xLUTiMRaC7Q5KCozUgTPyWV1aRTqkfw0yPNr0P6JakQmyu of6cc2zNKNVErpImfdFr9rOi36PkPaN86VccysUJ22cOHLEbcbpuYfzEitAPQYrLWPqU LB/aJDXIocvZTTePG1ICN8n69ffO8igMCcE/g5v5FEajcCd9sJqbbESxUVlLtYVIpzxy TkLb+PYYUR+pxkMjScdYQBq0kHVjBexguCcUs1me++T4Xk2//O1Op84fllDKEWmVUQQL 5DeEPmy5TusYPH63lWPeKOTTi7gNkFwy9DzRK/63SWIYgElSQq3eKhSsMGFkDo0r5OCO I8cA== X-Gm-Message-State: AOJu0YyfG0JwIyHILbXZAjEXGvdkr/CrvEHPJe7+9LzM3By7VW0IsBU8 IvfBuC24cuxb9+GK/7Me3cU7sYfOsegVZTG82o8orsWK3j5LFJ/+10AAw2fGqaFMfE7Bozo3k1o /hUIc/DTkxvMjC17U0zy8YT0IF1+WhJK83mU2PkrlDk+FWCXGo3+dhA74vUxGNXGG2SVPHP12Dw cRZrq5i93NaCCS4WpJbd+zKIfHeGGZwFdFyM+2 X-Received: by 2002:a05:6000:136f:b0:367:1da6:e419 with SMTP id ffacd0b85a97d-3677571c211mr5077508f8f.46.1719845975210; Mon, 01 Jul 2024 07:59:35 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFOqL7Nm7rykTZQM85jvkNjOHlSRggswDFFctiJZAELgEEO0Z37teVQhYg2YQBb0MCbP0LWLA== X-Received: by 2002:a05:6000:136f:b0:367:1da6:e419 with SMTP id ffacd0b85a97d-3677571c211mr5077485f8f.46.1719845974774; Mon, 01 Jul 2024 07:59:34 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a0d8ed0sm10291911f8f.28.2024.07.01.07.59.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:34 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 13/14] rust: introduce alternative to offset_of! Date: Mon, 1 Jul 2024 16:58:45 +0200 Message-ID: <20240701145853.1394967-14-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 Allow working with Rust versions prior to 1.77. The code was taken from Rust's Discourse platform and is used with permission of the author. Signed-off-by: Paolo Bonzini --- qemu/Cargo.toml | 3 + qemu/build.rs | 5 ++ qemu/src/hw/core/device_impl.rs | 4 +- qemu/src/lib.rs | 4 ++ qemu/src/qom/object_impl.rs | 13 +++-- qemu/src/util/mod.rs | 1 + qemu/src/util/offset_of.rs | 99 +++++++++++++++++++++++++++++++++ qemu/tests/main.rs | 11 +++- 9 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 qemu/build.rs create mode 100644 qemu/src/util/offset_of.rs diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml index a07a449..93808a5 100644 --- a/qemu/Cargo.toml +++ b/qemu/Cargo.toml @@ -12,3 +12,6 @@ cstr = { version = "=0.2.10" } [dev-dependencies] matches = ">=0" + +[build-dependencies] +version_check = { version = "~0.9" } diff --git a/qemu/build.rs b/qemu/build.rs new file mode 100644 index 0000000..34f7b49 --- /dev/null +++ b/qemu/build.rs @@ -0,0 +1,5 @@ +fn main() { + if let Some(true) = version_check::is_min_version("1.77.0") { + println!("cargo:rustc-cfg=has_offset_of"); + } +} diff --git a/qemu/src/hw/core/device_impl.rs b/qemu/src/hw/core/device_impl.rs index 80b0e5e..b1d2f04 100644 --- a/qemu/src/hw/core/device_impl.rs +++ b/qemu/src/hw/core/device_impl.rs @@ -111,7 +111,7 @@ macro_rules! qdev_prop { $kind, $name, (<$crate::conf_type!($type) as ConstDefault>::DEFAULT).$field, - <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + std::mem::offset_of!($crate::conf_type!($type), $field) + <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + $crate::offset_of!($crate::conf_type!($type), $field) ) }; } @@ -126,7 +126,7 @@ macro_rules! qdev_define_type { @extends $super $(,$supers)*, $crate::Object); unsafe impl $crate::DeviceTypeImpl for $struct { - const CONF_OFFSET: usize = std::mem::offset_of!($struct, conf); + const CONF_OFFSET: usize = $crate::offset_of!($struct, conf); fn properties() -> *const $crate::Property { static mut PROPERTIES: &'static [$crate::Property] = &[$($props),+]; diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs index 3f0491c..2d43a25 100644 --- a/qemu/src/lib.rs +++ b/qemu/src/lib.rs @@ -31,3 +31,7 @@ pub use util::foreign::IntoNative; pub use util::foreign::OwnedPointer; pub use util::zeroed::Zeroed; pub type Result = std::result::Result; + +// with_offsets is exported directly from util::offset_of +#[cfg(has_offset_of)] +pub use std::mem::offset_of; diff --git a/qemu/src/qom/object_impl.rs b/qemu/src/qom/object_impl.rs index 61546b6..b1768b9 100644 --- a/qemu/src/qom/object_impl.rs +++ b/qemu/src/qom/object_impl.rs @@ -95,11 +95,14 @@ unsafe fn rust_type_register() { #[macro_export] macro_rules! qom_define_type { ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty; @extends $super:ty $(,$supers:ty)*) => { - struct $struct { - // self.base dropped by call to superclass instance_finalize - base: std::mem::ManuallyDrop<$super>, - conf: $conf_ty, - state: $state_ty, + $crate::with_offsets! { + #[repr(C)] + struct $struct { + // self.base dropped by call to superclass instance_finalize + base: std::mem::ManuallyDrop<$super>, + conf: $conf_ty, + state: $state_ty, + } } // Define IsA markers for the struct itself and all the superclasses diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs index 9c081b6..e4df7c9 100644 --- a/qemu/src/util/mod.rs +++ b/qemu/src/util/mod.rs @@ -1,3 +1,4 @@ pub mod error; pub mod foreign; +pub mod offset_of; pub mod zeroed; diff --git a/qemu/src/util/offset_of.rs b/qemu/src/util/offset_of.rs new file mode 100644 index 0000000..4ce5188 --- /dev/null +++ b/qemu/src/util/offset_of.rs @@ -0,0 +1,99 @@ +#[cfg(not(has_offset_of))] +#[macro_export] +macro_rules! offset_of { + ($Container:ty, $field:ident) => { + <$Container>::offset_to.$field + }; +} + +/// A wrapper for struct declarations, that allows using `offset_of!` in +/// versions of Rust prior to 1.77 +#[macro_export] +macro_rules! with_offsets { + // source: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df + // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla + ( + #[repr(C)] + $(#[$struct_meta:meta])* + $struct_vis:vis + struct $StructName:ident { + $( + $(#[$field_meta:meta])* + $field_vis:vis + $field_name:ident : $field_ty:ty + ),* + $(,)? + } + ) => ( + #[repr(C)] + $(#[$struct_meta])* + $struct_vis + struct $StructName { + $( + $(#[$field_meta])* + $field_vis + $field_name : $field_ty , + )* + } + + #[cfg(not(has_offset_of))] + #[allow(nonstandard_style)] + const _: () = { + pub + struct StructOffsets { + $( + $field_vis + $field_name: usize, + )* + } + struct Helper; + impl $StructName { + pub + const offset_to: StructOffsets = StructOffsets { + $( + $field_name: Helper::$field_name, + )* + }; + } + const END_OF_PREV_FIELD: usize = 0; + $crate::with_offsets! { + @names [ $($field_name)* ] + @tys [ $($field_ty ,)*] + } + }; + ); + + ( + @names [] + @tys [] + ) => (); + + ( + @names [$field_name:ident $($other_names:tt)*] + @tys [$field_ty:ty , $($other_tys:tt)*] + ) => ( + impl Helper { + const $field_name: usize = { + let align = + std::mem::align_of::<$field_ty>() + ; + let trail = + END_OF_PREV_FIELD % align + ; + 0 + END_OF_PREV_FIELD + + (align - trail) + * [1, 0][(trail == 0) as usize] + }; + } + const _: () = { + const END_OF_PREV_FIELD: usize = + Helper::$field_name + + std::mem::size_of::<$field_ty>() + ; + $crate::with_offsets! { + @names [$($other_names)*] + @tys [$($other_tys)*] + } + }; + ); +} diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs index 601e92b..854c626 100644 --- a/qemu/tests/main.rs +++ b/qemu/tests/main.rs @@ -14,11 +14,16 @@ use qemu::DeviceState; use qemu::Result; +use qemu::with_offsets; + use std::cell::RefCell; -#[derive(Default, ConstDefault)] -struct TestConf { - foo: bool, +with_offsets! { + #[repr(C)] + #[derive(Default, ConstDefault)] + struct TestConf { + foo: bool, + } } #[derive(Default)] From patchwork Mon Jul 1 14:58:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 13718265 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 C8F78C3065B for ; Mon, 1 Jul 2024 15:05:22 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sOIVO-0004CW-54; Mon, 01 Jul 2024 10:59:46 -0400 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 1sOIVM-00045Z-KK for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:44 -0400 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 1sOIVK-0005pK-3H for qemu-devel@nongnu.org; Mon, 01 Jul 2024 10:59:44 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1719845981; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=luGqbPijLsIyeJwfJwi+l/hOQ+ghfxnFRgaepulV208=; b=T/zdvewbLcrr/DIWvVf/LHCHCpAqA2MmkixWV9jajdP6fhQLc/jocRIbXeALD5DTP2zwtr 7rDqNr43bEIeqGb9AjhDDumiix0gEJzGMFk4uq/oMWkcw2w5/7LPyEkQeV0kH0BSzttmof fi07csMxIOjia1qn8YWUupxWvbZX31w= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-658-yIct2ODCNCuRp60fWHdyGQ-1; Mon, 01 Jul 2024 10:59:40 -0400 X-MC-Unique: yIct2ODCNCuRp60fWHdyGQ-1 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-36536118656so1875030f8f.1 for ; Mon, 01 Jul 2024 07:59:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719845977; x=1720450777; 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=luGqbPijLsIyeJwfJwi+l/hOQ+ghfxnFRgaepulV208=; b=mbNXi+npS+6TI/gIEyjobwbIkshPgerssM3ZYpL8SGaJhk498TL2Eg3uTHtSghkfBk fMuhtphYX9V/n0XAfNDFIR1p78H50R2YjXpYpDbJ/i4qJ6IXj1qX67zzuOSdCRx3102V YT+XSmsBabFV3JkI0fbDyjV0ljJ/pXpXtcTy10P6p5tRGLOI7yWxNaU5PbsXUXOwG9JK TN5hFaoi9k/06Nc77A58X0AcyhX3XOG9P/FXgB3ge40pRzaaF75vRCHKqpyhJAUx+UhJ /rAKQeJcoIdnRu97nvzHJFRpNj0o5HoDog2aP6FvlnGYuIOpHd91imeJ9cFhs3H6vV5W 3/Aw== X-Gm-Message-State: AOJu0YzmrRuVRJIwyagW27jvwq0fDdLwun7lIl44jWenMXPgHwfsCYpb BXeh88eAfcoVhilE6ZuD/VHQa9RAzE3HtIijU/eWnPkoLI2Yr6EjZCfRZQUI13xo/oxZ4O7J+a6 9GNkDycpwlMo7E4v1B9bYIyYr0zQ1az9UK3C7MgGxL6S/L0YL8v/SxjVaBM0ZtUauCnCZn5WZeM dTzf5t/01jGDVq5zapRuc+DTt5HjJyVhSZiNOH X-Received: by 2002:a5d:6205:0:b0:367:4dc9:efcb with SMTP id ffacd0b85a97d-367757305c6mr4185680f8f.70.1719845977783; Mon, 01 Jul 2024 07:59:37 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFYpktNCTaKAuU/apTVDe8ZFC76gPb3FORDsTr4ThlRulNc0TDB9C7Ge/UmV5o7AbbXsyj7vA== X-Received: by 2002:a5d:6205:0:b0:367:4dc9:efcb with SMTP id ffacd0b85a97d-367757305c6mr4185660f8f.70.1719845977449; Mon, 01 Jul 2024 07:59:37 -0700 (PDT) Received: from avogadro.local ([151.95.101.29]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4256b0c1018sm155820705e9.40.2024.07.01.07.59.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 07:59:36 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , Manos Pitsidianakis , Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Hanna Czenczek , Stefan Hajnoczi Subject: [PATCH 14/14] rust: use version of toml_edit that does not require new Rust Date: Mon, 1 Jul 2024 16:58:46 +0200 Message-ID: <20240701145853.1394967-15-pbonzini@redhat.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240701145853.1394967-1-pbonzini@redhat.com> References: <20240701145853.1394967-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: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, 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_H4=0.001, RCVD_IN_MSPIKE_WL=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 toml_edit is quite aggressive in bumping the minimum required version of Rust. Force usage of an old version that runs with 1.63.0. Signed-off-by: Paolo Bonzini --- qemu/Cargo.toml | 3 +++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml index 93808a5..3ce5dba 100644 --- a/qemu/Cargo.toml +++ b/qemu/Cargo.toml @@ -15,3 +15,6 @@ matches = ">=0" [build-dependencies] version_check = { version = "~0.9" } + +# pick older version in order to support Rust 1.63 +toml_edit = { version = "~0.14" }