From patchwork Thu Oct 24 14:02:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849131 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 77563CE8E7A for ; Thu, 24 Oct 2024 14:05:06 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yR3-00077h-DC; Thu, 24 Oct 2024 10:03:33 -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 1t3yR1-00077S-IK for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:31 -0400 Received: from mail-ej1-x631.google.com ([2a00:1450:4864:20::631]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yQw-0003ta-Oi for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:31 -0400 Received: by mail-ej1-x631.google.com with SMTP id a640c23a62f3a-a9a6acac4c3so138846866b.0 for ; Thu, 24 Oct 2024 07:03:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778604; x=1730383404; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=HPZlkGUuKhNgrlgtDwPVRTmR3FzHv6w6ni5BQKVW4Eo=; b=ggc4LzILeaevjDszboJ6LXuAtIkPdKnn9HAshFxf+usFg88nRF/oIFgEul1uYAXGWs AX+qgDGIzGM48TMB13YM6WDmkW86d5CDlSG04IkSRpURJNjJp5B88k3V/V7pV0EkG3t2 oVFAHqE/5OhvbnTLyAKUwwooK//wQmCQeGHx/dUaSYnuCNtMLnwftT76xABZ6IEcPG2B wIqrNDl/QVBzsqRIzjeXLeIykKkP+H3o0imNiXMVdSqCig+3SB4TydicnHb3/x+zpyoA Dvpa+CuzIpQk6fllQAh/akAIezsCOWNfC1brhtdoE5YkkVnHUB0rYKfGYbdoX+WhKI81 976w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778604; x=1730383404; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HPZlkGUuKhNgrlgtDwPVRTmR3FzHv6w6ni5BQKVW4Eo=; b=AuW9X4duEQmNGA6/n/Lob8ywR7Df9us6mzwkplYJX2nrgV6764BTZ+Od1wR2idT5A6 c0lbgDM8Hhao6XcjV8l3/z8V5/Sso2KZ2Y13evp+Z7Fo4QdyLzRFyMKWZ7K13SO83Fyk k+RfV4TdmXb1JWi3Auo5I4izULKppF9fbimgdq/YdAYL2q22xBhASbXlfTk8aIhMELG2 VKr8eJLhDQa6CgmfpcMSgxSJZa/4T6VLLPBdaDAy5llqsujaVQvRdo+25bXGP1J505B9 Asmw/1H7OmBSg/WzfM+yYKdUjpwXi8sV7wzvDRKkaTAPDzESw+lM6yTm68KuFinnizT+ qHXw== X-Gm-Message-State: AOJu0YzR/5V/rqR5R7+fSsb84cmuAyQRda2AtPR13ntBV03kKEhUu7xE v8ALCnTepWorfFFRLPDepZyoD1WmLx7owCP+bVFGFvkwtFiyhLWuddokVd1YBOg= X-Google-Smtp-Source: AGHT+IEyn0iRCkHaoWwSuxxZOwlzKawvb2U9lzYfag7HyT43DvUtPZxckVo9n9eh6337RRiCWbNv1w== X-Received: by 2002:a17:907:6d29:b0:a8a:58c5:78f1 with SMTP id a640c23a62f3a-a9ad2711791mr174482866b.11.1729778602864; Thu, 24 Oct 2024 07:03:22 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:22 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:02:59 +0300 Subject: [PATCH 01/11] Revert "rust: add PL011 device model" MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-1-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=84372; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=FGMhTFmzdJgWKFqcTqIrpxOXCBEYBfopxSuG8TgVq8I=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9paFF2R2prcDhWZTlRMzVBMVJkVUJYTWEyCmZRVFF0d3lOaFZpajQ4R1RB Tk9KQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG9nQUt DUkIzS2Nkd2YzNEowTEdHRC85SUludzhraXlsbUhYZ3p3MGc3YVJyU3gxWjVkV2V1am1HVVVEVw o4Mk5NOUxoVk43MzhVVmRWRHdGbTdzSkhHM3JyQjIrL3NnL1VwQXhoeTFCd00yRGRqbFdCV3B3T kxTa0ZTaityCng1LzlLZGxhd3FoeE9TVG5LRlZsQ0dxVnEwREdaejJhOXBjTjlDeDdvUXhrcmxH aTRyT0ZRcWFpalptNC84a1kKWG5lMDdGTmxBZkhvUHpUckcwcVNsalFjU3drZ0FnaG9yRkxRYmt xQm9iYmd3MW1peFBMTExlT3E5Uzk2Q3lDNwpPTno0QzdraXFIbnJCYlErQzhXNTU5Rm9DSVMvdU lpeWtMbkh4T0o3MTFNV1ZIQ3kyMVBoQmlNZXErWHV6Ny9JCmlyd29lRnZWMjdNeTlqSmxDNnN6L 01ETU1RVjdySmp2VzFzalBPSFRKcVFIRTVZZ29nQ1VtTS9TYnc4WkNLZ3MKbHZlZmEzSlhCY0tF TGc3OWZDNGNNNkxBM0kxcmR2YWVZMjBnWU4wdFZvSzhqOG5SVTkzeERKSVJSRFRESWM1RQpyL1l jM0VTeUdUcGYyZVEyRncvazd0RW5NYzAyMExWK0t4MlloQWN3ZjgwUWdldmVoMXJpRDBOTFRQRm ErZVQ1Cjkza1hpTHA1MGY0YmJXKy9zcmlkdlN1dkVXM1VCTFhBa3gveU9PSGNFeTNFUUEzRVlDN kkrUVFRYm1qZjhLYWIKZXk4Qys3Rk9heHhPbXRPSGRzb1pWWnRNaUFqUXIzVFBYeFhzTXRwMzBo VVMxTlp0YU9pT3QrSWxRY0wrOGRwRwo5SGRRSkk3Sk1aQVFBVkY5NWtvWXF6czdrd1hCaVNtZUU za3d0bFhudVVNeTZRQkVkcXZJYysvTmNzOVg0N1ZLCnNIR1RFdz09Cj1pTXF0Ci0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::631; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ej1-x631.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 Patch was applied with invalid authorship by accident, which confuses git tooling that look at git blame for contributors etc. Patch will be re-applied with correct authorship right after this commit. This reverts commit d0f0cd5b1f7e9780753344548e17ad4df9fcf5d8. Signed-off-by: Manos Pitsidianakis --- MAINTAINERS | 5 - meson.build | 24 - hw/arm/Kconfig | 30 +- rust/Kconfig | 1 - rust/hw/Kconfig | 2 - rust/hw/char/Kconfig | 3 - rust/hw/char/meson.build | 1 - rust/hw/char/pl011/.gitignore | 2 - rust/hw/char/pl011/Cargo.lock | 134 ----- rust/hw/char/pl011/Cargo.toml | 26 - rust/hw/char/pl011/README.md | 31 -- rust/hw/char/pl011/meson.build | 26 - rust/hw/char/pl011/src/device.rs | 599 --------------------- rust/hw/char/pl011/src/device_class.rs | 70 --- rust/hw/char/pl011/src/lib.rs | 586 -------------------- rust/hw/char/pl011/src/memory_ops.rs | 59 -- rust/hw/meson.build | 1 - rust/meson.build | 2 - scripts/archive-source.sh | 4 +- scripts/make-release | 4 +- scripts/rust/rust_root_crate.sh | 13 - subprojects/.gitignore | 7 - subprojects/arbitrary-int-1-rs.wrap | 7 - subprojects/bilge-0.2-rs.wrap | 7 - subprojects/bilge-impl-0.2-rs.wrap | 7 - subprojects/either-1-rs.wrap | 7 - subprojects/itertools-0.11-rs.wrap | 7 - .../packagefiles/arbitrary-int-1-rs/meson.build | 19 - subprojects/packagefiles/bilge-0.2-rs/meson.build | 29 - .../packagefiles/bilge-impl-0.2-rs/meson.build | 45 -- subprojects/packagefiles/either-1-rs/meson.build | 24 - .../packagefiles/itertools-0.11-rs/meson.build | 30 -- .../packagefiles/proc-macro-error-1-rs/meson.build | 40 -- .../proc-macro-error-attr-1-rs/meson.build | 32 -- .../packagefiles/unicode-ident-1-rs/meson.build | 20 - subprojects/proc-macro-error-1-rs.wrap | 7 - subprojects/proc-macro-error-attr-1-rs.wrap | 7 - 37 files changed, 12 insertions(+), 1906 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c3bfa132fd6ecd61dd733b760a5e1ccd39613455..793c683aa6cccafca9c98b74b9a20d84211f041b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1137,11 +1137,6 @@ F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c F: docs/system/arm/nrf.rst -ARM PL011 Rust device -M: Manos Pitsidianakis -S: Maintained -F: rust/hw/char/pl011/ - AVR Machines ------------- diff --git a/meson.build b/meson.build index c26c417de16ad9256a019a90fd89ea990bb3f948..0d617c551e61c90c7c357c62ff5da34437723947 100644 --- a/meson.build +++ b/meson.build @@ -3520,7 +3520,6 @@ qom_ss = ss.source_set() system_ss = ss.source_set() specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() -rust_devices_ss = ss.source_set() stub_ss = ss.source_set() trace_ss = ss.source_set() user_ss = ss.source_set() @@ -4068,29 +4067,6 @@ foreach target : target_dirs arch_srcs += target_specific.sources() arch_deps += target_specific.dependencies() - if have_rust and have_system - target_rust = rust_devices_ss.apply(config_target, strict: false) - crates = [] - foreach dep : target_rust.dependencies() - crates += dep.get_variable('crate') - endforeach - if crates.length() > 0 - rlib_rs = custom_target('rust_' + target.underscorify() + '.rs', - output: 'rust_' + target.underscorify() + '.rs', - command: [find_program('scripts/rust/rust_root_crate.sh')] + crates, - capture: true, - build_by_default: true, - build_always_stale: true) - rlib = static_library('rust_' + target.underscorify(), - rlib_rs, - dependencies: target_rust.dependencies(), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: rustc_args, - rust_abi: 'c') - arch_deps += declare_dependency(link_whole: [rlib]) - endif - endif - # allow using headers from the dependencies but do not include the sources, # because this emulator only needs those in "objects". For external # dependencies, the full dependency is included below in the executable. diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index e7fd9338d11dadb7a18032c674927db3d9887bdd..53eb7bb3d0157bb6e8e078fe73ec66015ae0fe13 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -20,8 +20,7 @@ config ARM_VIRT select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL061 # GPIO select GPIO_PWR @@ -74,8 +73,7 @@ config HIGHBANK select AHCI select ARM_TIMER # sp804 select ARM_V7M - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL022 # SPI select PL031 # RTC select PL061 # GPIO @@ -88,8 +86,7 @@ config INTEGRATOR depends on TCG && ARM select ARM_TIMER select INTEGRATOR_DEBUG - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL041 # audio select PL050 # keyboard/mouse @@ -107,8 +104,7 @@ config MUSCA default y depends on TCG && ARM select ARMSSE - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 select PL031 select SPLIT_IRQ select UNIMP @@ -172,8 +168,7 @@ config REALVIEW select WM8750 # audio codec select LSI_SCSI_PCI select PCI - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL041 # audio codec select PL050 # keyboard/mouse @@ -198,8 +193,7 @@ config SBSA_REF select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL061 # GPIO select USB_XHCI_SYSBUS @@ -223,8 +217,7 @@ config STELLARIS select ARM_V7M select CMSDK_APB_WATCHDOG select I2C - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL022 # SPI select PL061 # GPIO select SSD0303 # OLED display @@ -284,8 +277,7 @@ config VEXPRESS select ARM_TIMER # sp804 select LAN9118 select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL041 # audio codec select PL181 # display select REALVIEW @@ -370,8 +362,7 @@ config RASPI default y depends on TCG && ARM select FRAMEBUFFER - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select SDHCI select USB_DWC2 select BCM2835_SPI @@ -447,8 +438,7 @@ config XLNX_VERSAL select ARM_GIC select CPU_CLUSTER select DEVICE_TREE - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 select CADENCE select VIRTIO_MMIO select UNIMP diff --git a/rust/Kconfig b/rust/Kconfig index f9f5c3909887451f71360a7986d79e57fdb43c91..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/rust/Kconfig +++ b/rust/Kconfig @@ -1 +0,0 @@ -source hw/Kconfig diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig deleted file mode 100644 index 4d934f30afe13ddff418db8ec9e8b8eb25a9e8d0..0000000000000000000000000000000000000000 --- a/rust/hw/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -# devices Kconfig -source char/Kconfig diff --git a/rust/hw/char/Kconfig b/rust/hw/char/Kconfig deleted file mode 100644 index a1732a9e97fe3211547e30bc9319382e6394ed5b..0000000000000000000000000000000000000000 --- a/rust/hw/char/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -config X_PL011_RUST - bool - default y if HAVE_RUST diff --git a/rust/hw/char/meson.build b/rust/hw/char/meson.build deleted file mode 100644 index 5716dc43ef6facdcf1cc963108347bbf4d12cf0e..0000000000000000000000000000000000000000 --- a/rust/hw/char/meson.build +++ /dev/null @@ -1 +0,0 @@ -subdir('pl011') diff --git a/rust/hw/char/pl011/.gitignore b/rust/hw/char/pl011/.gitignore deleted file mode 100644 index 71eaff2035d5a65b57ae32dfeecf3d87bbc7b396..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore generated bindings file overrides. -src/bindings.rs.inc diff --git a/rust/hw/char/pl011/Cargo.lock b/rust/hw/char/pl011/Cargo.lock deleted file mode 100644 index b58cebb186e99efe184117bb931a341543d4466b..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/Cargo.lock +++ /dev/null @@ -1,134 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "arbitrary-int" -version = "1.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" - -[[package]] -name = "bilge" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" -dependencies = [ - "arbitrary-int", - "bilge-impl", -] - -[[package]] -name = "bilge-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" -dependencies = [ - "itertools", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "pl011" -version = "0.1.0" -dependencies = [ - "bilge", - "bilge-impl", - "qemu_api", - "qemu_api_macros", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "qemu_api" -version = "0.1.0" - -[[package]] -name = "qemu_api_macros" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml deleted file mode 100644 index b089e3dded623131ee13b4af8145b84388755df7..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "pl011" -version = "0.1.0" -edition = "2021" -authors = ["Manos Pitsidianakis "] -license = "GPL-2.0-or-later" -readme = "README.md" -homepage = "https://www.qemu.org" -description = "pl011 device model for QEMU" -repository = "https://gitlab.com/epilys/rust-for-qemu" -resolver = "2" -publish = false -keywords = [] -categories = [] - -[lib] -crate-type = ["staticlib"] - -[dependencies] -bilge = { version = "0.2.0" } -bilge-impl = { version = "0.2.0" } -qemu_api = { path = "../../../qemu-api" } -qemu_api_macros = { path = "../../../qemu-api-macros" } - -# Do not include in any global workspace -[workspace] diff --git a/rust/hw/char/pl011/README.md b/rust/hw/char/pl011/README.md deleted file mode 100644 index cd7dea31634241cbf96b0be13f21d52bbd8ae750..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# PL011 QEMU Device Model - -This library implements a device model for the PrimeCell® UART (PL011) -device in QEMU. - -## Build static lib - -Host build target must be explicitly specified: - -```sh -cargo build --target x86_64-unknown-linux-gnu -``` - -Replace host target triplet if necessary. - -## Generate Rust documentation - -To generate docs for this crate, including private items: - -```sh -cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu -``` - -To include direct dependencies like `bilge` (bitmaps for register types): - -```sh -cargo tree --depth 1 -e normal --prefix none \ - | cut -d' ' -f1 \ - | xargs printf -- '-p %s\n' \ - | xargs cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu -``` diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build deleted file mode 100644 index 547cca5a96f7eef284caf1949380b65f7d015d92..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -subproject('bilge-0.2-rs', required: true) -subproject('bilge-impl-0.2-rs', required: true) - -bilge_dep = dependency('bilge-0.2-rs') -bilge_impl_dep = dependency('bilge-impl-0.2-rs') - -_libpl011_rs = static_library( - 'pl011', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [ - bilge_dep, - bilge_impl_dep, - qemu_api, - qemu_api_macros, - ], -) - -rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( - link_whole: [_libpl011_rs], - # Putting proc macro crates in `dependencies` is necessary for Meson to find - # them when compiling the root per-target static rust lib. - dependencies: [bilge_impl_dep, qemu_api_macros], - variables: {'crate': 'pl011'}, -)]) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs deleted file mode 100644 index c7193b41beec0b177dbc75ac0e43fcfea4c82bfb..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/src/device.rs +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use core::{ - ffi::{c_int, c_uchar, c_uint, c_void, CStr}, - ptr::{addr_of, addr_of_mut, NonNull}, -}; - -use qemu_api::{ - bindings::{self, *}, - definitions::ObjectImpl, -}; - -use crate::{ - memory_ops::PL011_OPS, - registers::{self, Interrupt}, - RegisterOffset, -}; - -static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; - -const DATA_BREAK: u32 = 1 << 10; - -/// QEMU sourced constant. -pub const PL011_FIFO_DEPTH: usize = 16_usize; - -#[repr(C)] -#[derive(Debug, qemu_api_macros::Object)] -/// PL011 Device Model in QEMU -pub struct PL011State { - pub parent_obj: SysBusDevice, - pub iomem: MemoryRegion, - #[doc(alias = "fr")] - pub flags: registers::Flags, - #[doc(alias = "lcr")] - pub line_control: registers::LineControl, - #[doc(alias = "rsr")] - pub receive_status_error_clear: registers::ReceiveStatusErrorClear, - #[doc(alias = "cr")] - pub control: registers::Control, - pub dmacr: u32, - pub int_enabled: u32, - pub int_level: u32, - pub read_fifo: [u32; PL011_FIFO_DEPTH], - pub ilpr: u32, - pub ibrd: u32, - pub fbrd: u32, - pub ifl: u32, - pub read_pos: usize, - pub read_count: usize, - pub read_trigger: usize, - #[doc(alias = "chr")] - pub char_backend: CharBackend, - /// QEMU interrupts - /// - /// ```text - /// * sysbus MMIO region 0: device registers - /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line) - /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line) - /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line) - /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line) - /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line) - /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line) - /// ``` - #[doc(alias = "irq")] - pub interrupts: [qemu_irq; 6usize], - #[doc(alias = "clk")] - pub clock: NonNull, - #[doc(alias = "migrate_clk")] - pub migrate_clock: bool, -} - -impl ObjectImpl for PL011State { - type Class = PL011Class; - const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; - const TYPE_NAME: &'static CStr = crate::TYPE_PL011; - const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); - const ABSTRACT: bool = false; - const INSTANCE_INIT: Option = Some(pl011_init); - const INSTANCE_POST_INIT: Option = None; - const INSTANCE_FINALIZE: Option = None; -} - -#[repr(C)] -pub struct PL011Class { - _inner: [u8; 0], -} - -impl qemu_api::definitions::Class for PL011Class { - const CLASS_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), - > = Some(crate::device_class::pl011_class_init); - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), - > = None; -} - -#[used] -pub static CLK_NAME: &CStr = c"clk"; - -impl PL011State { - /// Initializes a pre-allocated, unitialized instance of `PL011State`. - /// - /// # Safety - /// - /// `self` must point to a correctly sized and aligned location for the - /// `PL011State` type. It must not be called more than once on the same - /// location/instance. All its fields are expected to hold unitialized - /// values with the sole exception of `parent_obj`. - pub unsafe fn init(&mut self) { - let dev = addr_of_mut!(*self).cast::(); - // SAFETY: - // - // self and self.iomem are guaranteed to be valid at this point since callers - // must make sure the `self` reference is valid. - unsafe { - memory_region_init_io( - addr_of_mut!(self.iomem), - addr_of_mut!(*self).cast::(), - &PL011_OPS, - addr_of_mut!(*self).cast::(), - Self::TYPE_INFO.name, - 0x1000, - ); - let sbd = addr_of_mut!(*self).cast::(); - sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); - for irq in self.interrupts.iter_mut() { - sysbus_init_irq(sbd, irq); - } - } - // SAFETY: - // - // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, - // we can overwrite the undefined value without side effects. This is - // safe since all PL011State instances are created by QOM code which - // calls this function to initialize the fields; therefore no code is - // able to access an invalid self.clock value. - unsafe { - self.clock = NonNull::new(qdev_init_clock_in( - dev, - CLK_NAME.as_ptr(), - None, /* pl011_clock_update */ - addr_of_mut!(*self).cast::(), - ClockEvent::ClockUpdate.0, - )) - .unwrap(); - } - } - - pub fn read( - &mut self, - offset: hwaddr, - _size: core::ffi::c_uint, - ) -> std::ops::ControlFlow { - use RegisterOffset::*; - - std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) { - Err(v) if (0x3f8..0x400).contains(&v) => { - u64::from(PL011_ID_ARM[((offset - 0xfe0) >> 2) as usize]) - } - Err(_) => { - // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); - 0 - } - Ok(DR) => { - // s->flags &= ~PL011_FLAG_RXFF; - self.flags.set_receive_fifo_full(false); - let c = self.read_fifo[self.read_pos]; - if self.read_count > 0 { - self.read_count -= 1; - self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); - } - if self.read_count == 0 { - // self.flags |= PL011_FLAG_RXFE; - self.flags.set_receive_fifo_empty(true); - } - if self.read_count + 1 == self.read_trigger { - //self.int_level &= ~ INT_RX; - self.int_level &= !registers::INT_RX; - } - // Update error bits. - self.receive_status_error_clear = c.to_be_bytes()[3].into(); - self.update(); - // Must call qemu_chr_fe_accept_input, so return Continue: - return std::ops::ControlFlow::Continue(c.into()); - } - Ok(RSR) => u8::from(self.receive_status_error_clear).into(), - Ok(FR) => u16::from(self.flags).into(), - Ok(FBRD) => self.fbrd.into(), - Ok(ILPR) => self.ilpr.into(), - Ok(IBRD) => self.ibrd.into(), - Ok(LCR_H) => u16::from(self.line_control).into(), - Ok(CR) => { - // We exercise our self-control. - u16::from(self.control).into() - } - Ok(FLS) => self.ifl.into(), - Ok(IMSC) => self.int_enabled.into(), - Ok(RIS) => self.int_level.into(), - Ok(MIS) => u64::from(self.int_level & self.int_enabled), - Ok(ICR) => { - // "The UARTICR Register is the interrupt clear register and is write-only" - // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR - 0 - } - Ok(DMACR) => self.dmacr.into(), - }) - } - - pub fn write(&mut self, offset: hwaddr, value: u64) { - // eprintln!("write offset {offset} value {value}"); - use RegisterOffset::*; - let value: u32 = value as u32; - match RegisterOffset::try_from(offset) { - Err(_bad_offset) => { - eprintln!("write bad offset {offset} value {value}"); - } - Ok(DR) => { - // ??? Check if transmitter is enabled. - let ch: u8 = value as u8; - // XXX this blocks entire thread. Rewrite to use - // qemu_chr_fe_write and background I/O callbacks - - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - unsafe { - qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1); - } - self.loopback_tx(value); - self.int_level |= registers::INT_TX; - self.update(); - } - Ok(RSR) => { - self.receive_status_error_clear = 0.into(); - } - Ok(FR) => { - // flag writes are ignored - } - Ok(ILPR) => { - self.ilpr = value; - } - Ok(IBRD) => { - self.ibrd = value; - } - Ok(FBRD) => { - self.fbrd = value; - } - Ok(LCR_H) => { - let value = value as u16; - let new_val: registers::LineControl = value.into(); - // Reset the FIFO state on FIFO enable or disable - if bool::from(self.line_control.fifos_enabled()) - ^ bool::from(new_val.fifos_enabled()) - { - self.reset_fifo(); - } - if self.line_control.send_break() ^ new_val.send_break() { - let mut break_enable: c_int = new_val.send_break().into(); - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - unsafe { - qemu_chr_fe_ioctl( - addr_of_mut!(self.char_backend), - CHR_IOCTL_SERIAL_SET_BREAK as i32, - addr_of_mut!(break_enable).cast::(), - ); - } - self.loopback_break(break_enable > 0); - } - self.line_control = new_val; - self.set_read_trigger(); - } - Ok(CR) => { - // ??? Need to implement the enable bit. - let value = value as u16; - self.control = value.into(); - self.loopback_mdmctrl(); - } - Ok(FLS) => { - self.ifl = value; - self.set_read_trigger(); - } - Ok(IMSC) => { - self.int_enabled = value; - self.update(); - } - Ok(RIS) => {} - Ok(MIS) => {} - Ok(ICR) => { - self.int_level &= !value; - self.update(); - } - Ok(DMACR) => { - self.dmacr = value; - if value & 3 > 0 { - // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); - eprintln!("pl011: DMA not implemented"); - } - } - } - } - - #[inline] - fn loopback_tx(&mut self, value: u32) { - if !self.loopback_enabled() { - return; - } - - // Caveat: - // - // In real hardware, TX loopback happens at the serial-bit level - // and then reassembled by the RX logics back into bytes and placed - // into the RX fifo. That is, loopback happens after TX fifo. - // - // Because the real hardware TX fifo is time-drained at the frame - // rate governed by the configured serial format, some loopback - // bytes in TX fifo may still be able to get into the RX fifo - // that could be full at times while being drained at software - // pace. - // - // In such scenario, the RX draining pace is the major factor - // deciding which loopback bytes get into the RX fifo, unless - // hardware flow-control is enabled. - // - // For simplicity, the above described is not emulated. - self.put_fifo(value); - } - - fn loopback_mdmctrl(&mut self) { - if !self.loopback_enabled() { - return; - } - - /* - * Loopback software-driven modem control outputs to modem status inputs: - * FR.RI <= CR.Out2 - * FR.DCD <= CR.Out1 - * FR.CTS <= CR.RTS - * FR.DSR <= CR.DTR - * - * The loopback happens immediately even if this call is triggered - * by setting only CR.LBE. - * - * CTS/RTS updates due to enabled hardware flow controls are not - * dealt with here. - */ - - //fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD | - // PL011_FLAG_DSR | PL011_FLAG_CTS); - //fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0; - //fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0; - //fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0; - //fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0; - // - self.flags.set_ring_indicator(self.control.out_2()); - self.flags.set_data_carrier_detect(self.control.out_1()); - self.flags.set_clear_to_send(self.control.request_to_send()); - self.flags - .set_data_set_ready(self.control.data_transmit_ready()); - - // Change interrupts based on updated FR - let mut il = self.int_level; - - il &= !Interrupt::MS; - //il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0; - //il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0; - //il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0; - //il |= (fr & PL011_FLAG_RI) ? INT_RI : 0; - - if self.flags.data_set_ready() { - il |= Interrupt::DSR as u32; - } - if self.flags.data_carrier_detect() { - il |= Interrupt::DCD as u32; - } - if self.flags.clear_to_send() { - il |= Interrupt::CTS as u32; - } - if self.flags.ring_indicator() { - il |= Interrupt::RI as u32; - } - self.int_level = il; - self.update(); - } - - fn loopback_break(&mut self, enable: bool) { - if enable { - self.loopback_tx(DATA_BREAK); - } - } - - fn set_read_trigger(&mut self) { - self.read_trigger = 1; - } - - pub fn realize(&mut self) { - // SAFETY: self.char_backend has the correct size and alignment for a - // CharBackend object, and its callbacks are of the correct types. - unsafe { - qemu_chr_fe_set_handlers( - addr_of_mut!(self.char_backend), - Some(pl011_can_receive), - Some(pl011_receive), - Some(pl011_event), - None, - addr_of_mut!(*self).cast::(), - core::ptr::null_mut(), - true, - ); - } - } - - pub fn reset(&mut self) { - self.line_control.reset(); - self.receive_status_error_clear.reset(); - self.dmacr = 0; - self.int_enabled = 0; - self.int_level = 0; - self.ilpr = 0; - self.ibrd = 0; - self.fbrd = 0; - self.read_trigger = 1; - self.ifl = 0x12; - self.control.reset(); - self.flags = 0.into(); - self.reset_fifo(); - } - - pub fn reset_fifo(&mut self) { - self.read_count = 0; - self.read_pos = 0; - - /* Reset FIFO flags */ - self.flags.reset(); - } - - pub fn can_receive(&self) -> bool { - // trace_pl011_can_receive(s->lcr, s->read_count, r); - self.read_count < self.fifo_depth() - } - - pub fn event(&mut self, event: QEMUChrEvent) { - if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.fifo_enabled() { - self.put_fifo(DATA_BREAK); - self.receive_status_error_clear.set_break_error(true); - } - } - - #[inline] - pub fn fifo_enabled(&self) -> bool { - matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO) - } - - #[inline] - pub fn loopback_enabled(&self) -> bool { - self.control.enable_loopback() - } - - #[inline] - pub fn fifo_depth(&self) -> usize { - // Note: FIFO depth is expected to be power-of-2 - if self.fifo_enabled() { - return PL011_FIFO_DEPTH; - } - 1 - } - - pub fn put_fifo(&mut self, value: c_uint) { - let depth = self.fifo_depth(); - assert!(depth > 0); - let slot = (self.read_pos + self.read_count) & (depth - 1); - self.read_fifo[slot] = value; - self.read_count += 1; - // s->flags &= ~PL011_FLAG_RXFE; - self.flags.set_receive_fifo_empty(false); - if self.read_count == depth { - //s->flags |= PL011_FLAG_RXFF; - self.flags.set_receive_fifo_full(true); - } - - if self.read_count == self.read_trigger { - self.int_level |= registers::INT_RX; - self.update(); - } - } - - pub fn update(&self) { - let flags = self.int_level & self.int_enabled; - for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - // SAFETY: self.interrupts have been initialized in init(). - unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) }; - } - } -} - -/// Which bits in the interrupt status matter for each outbound IRQ line ? -pub const IRQMASK: [u32; 6] = [ - /* combined IRQ */ - Interrupt::E - | Interrupt::MS - | Interrupt::RT as u32 - | Interrupt::TX as u32 - | Interrupt::RX as u32, - Interrupt::RX as u32, - Interrupt::TX as u32, - Interrupt::RT as u32, - Interrupt::MS, - Interrupt::E, -]; - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::()); - state.as_ref().can_receive().into() - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -/// -/// The buffer and size arguments must also be valid. -#[no_mangle] -pub unsafe extern "C" fn pl011_receive( - opaque: *mut core::ffi::c_void, - buf: *const u8, - size: core::ffi::c_int, -) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - if state.as_ref().loopback_enabled() { - return; - } - if size > 0 { - debug_assert!(!buf.is_null()); - state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) - } - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - state.as_mut().event(event) - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer for `chr`. -#[no_mangle] -pub unsafe extern "C" fn pl011_create( - addr: u64, - irq: qemu_irq, - chr: *mut Chardev, -) -> *mut DeviceState { - unsafe { - let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name); - let sysbus: *mut SysBusDevice = dev.cast::(); - - qdev_prop_set_chr(dev, bindings::TYPE_CHARDEV.as_ptr(), chr); - sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error); - sysbus_mmio_map(sysbus, 0, addr); - sysbus_connect_irq(sysbus, 0, irq); - dev - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_init(obj: *mut Object) { - unsafe { - debug_assert!(!obj.is_null()); - let mut state = NonNull::new_unchecked(obj.cast::()); - state.as_mut().init(); - } -} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs deleted file mode 100644 index b7ab31af02d7bb50ae94be0b153baafc7ccfa375..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/src/device_class.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use core::ptr::NonNull; - -use qemu_api::{bindings::*, definitions::ObjectImpl}; - -use crate::device::PL011State; - -#[used] -pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { - name: PL011State::TYPE_INFO.name, - unmigratable: true, - ..unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() } -}; - -qemu_api::declare_properties! { - PL011_PROPERTIES, - qemu_api::define_property!( - c"chardev", - PL011State, - char_backend, - unsafe { &qdev_prop_chr }, - CharBackend - ), - qemu_api::define_property!( - c"migrate-clk", - PL011State, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool - ), -} - -qemu_api::device_class_init! { - pl011_class_init, - props => PL011_PROPERTIES, - realize_fn => Some(pl011_realize), - legacy_reset_fn => Some(pl011_reset), - vmsd => VMSTATE_PL011, -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) { - unsafe { - assert!(!dev.is_null()); - let mut state = NonNull::new_unchecked(dev.cast::()); - state.as_mut().realize(); - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) { - unsafe { - assert!(!dev.is_null()); - let mut state = NonNull::new_unchecked(dev.cast::()); - state.as_mut().reset(); - } -} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs deleted file mode 100644 index 2939ee50c99ceaacf6ec68127272d58814e33679..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/src/lib.rs +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later -// -// PL011 QEMU Device Model -// -// This library implements a device model for the PrimeCell® UART (PL011) -// device in QEMU. -// -#![doc = include_str!("../README.md")] -//! # Library crate -//! -//! See [`PL011State`](crate::device::PL011State) for the device model type and -//! the [`registers`] module for register types. - -#![deny( - rustdoc::broken_intra_doc_links, - rustdoc::redundant_explicit_links, - clippy::correctness, - clippy::suspicious, - clippy::complexity, - clippy::perf, - clippy::cargo, - clippy::nursery, - clippy::style, - // restriction group - clippy::dbg_macro, - clippy::as_underscore, - clippy::assertions_on_result_states, - // pedantic group - clippy::doc_markdown, - clippy::borrow_as_ptr, - clippy::cast_lossless, - clippy::option_if_let_else, - clippy::missing_const_for_fn, - clippy::cognitive_complexity, - clippy::missing_safety_doc, - )] - -extern crate bilge; -extern crate bilge_impl; -extern crate qemu_api; - -pub mod device; -pub mod device_class; -pub mod memory_ops; - -pub const TYPE_PL011: &::core::ffi::CStr = c"pl011"; - -/// Offset of each register from the base memory address of the device. -/// -/// # Source -/// ARM DDI 0183G, Table 3-1 p.3-3 -#[doc(alias = "offset")] -#[allow(non_camel_case_types)] -#[repr(u64)] -#[derive(Debug)] -pub enum RegisterOffset { - /// Data Register - /// - /// A write to this register initiates the actual data transmission - #[doc(alias = "UARTDR")] - DR = 0x000, - /// Receive Status Register or Error Clear Register - #[doc(alias = "UARTRSR")] - #[doc(alias = "UARTECR")] - RSR = 0x004, - /// Flag Register - /// - /// A read of this register shows if transmission is complete - #[doc(alias = "UARTFR")] - FR = 0x018, - /// Fractional Baud Rate Register - /// - /// responsible for baud rate speed - #[doc(alias = "UARTFBRD")] - FBRD = 0x028, - /// `IrDA` Low-Power Counter Register - #[doc(alias = "UARTILPR")] - ILPR = 0x020, - /// Integer Baud Rate Register - /// - /// Responsible for baud rate speed - #[doc(alias = "UARTIBRD")] - IBRD = 0x024, - /// line control register (data frame format) - #[doc(alias = "UARTLCR_H")] - LCR_H = 0x02C, - /// Toggle UART, transmission or reception - #[doc(alias = "UARTCR")] - CR = 0x030, - /// Interrupt FIFO Level Select Register - #[doc(alias = "UARTIFLS")] - FLS = 0x034, - /// Interrupt Mask Set/Clear Register - #[doc(alias = "UARTIMSC")] - IMSC = 0x038, - /// Raw Interrupt Status Register - #[doc(alias = "UARTRIS")] - RIS = 0x03C, - /// Masked Interrupt Status Register - #[doc(alias = "UARTMIS")] - MIS = 0x040, - /// Interrupt Clear Register - #[doc(alias = "UARTICR")] - ICR = 0x044, - /// DMA control Register - #[doc(alias = "UARTDMACR")] - DMACR = 0x048, - ///// Reserved, offsets `0x04C` to `0x07C`. - //Reserved = 0x04C, -} - -impl core::convert::TryFrom for RegisterOffset { - type Error = u64; - - fn try_from(value: u64) -> Result { - macro_rules! case { - ($($discriminant:ident),*$(,)*) => { - /* check that matching on all macro arguments compiles, which means we are not - * missing any enum value; if the type definition ever changes this will stop - * compiling. - */ - const fn _assert_exhaustive(val: RegisterOffset) { - match val { - $(RegisterOffset::$discriminant => (),)* - } - } - - match value { - $(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)* - _ => Err(value), - } - } - } - case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR } - } -} - -pub mod registers { - //! Device registers exposed as typed structs which are backed by arbitrary - //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. - //! - //! All PL011 registers are essentially 32-bit wide, but are typed here as - //! bitmaps with only the necessary width. That is, if a struct bitmap - //! in this module is for example 16 bits long, it should be conceived - //! as a 32-bit register where the unmentioned higher bits are always - //! unused thus treated as zero when read or written. - use bilge::prelude::*; - - // TODO: FIFO Mode has different semantics - /// Data Register, `UARTDR` - /// - /// The `UARTDR` register is the data register. - /// - /// For words to be transmitted: - /// - /// - if the FIFOs are enabled, data written to this location is pushed onto - /// the transmit - /// FIFO - /// - if the FIFOs are not enabled, data is stored in the transmitter - /// holding register (the - /// bottom word of the transmit FIFO). - /// - /// The write operation initiates transmission from the UART. The data is - /// prefixed with a start bit, appended with the appropriate parity bit - /// (if parity is enabled), and a stop bit. The resultant word is then - /// transmitted. - /// - /// For received words: - /// - /// - if the FIFOs are enabled, the data byte and the 4-bit status (break, - /// frame, parity, - /// and overrun) is pushed onto the 12-bit wide receive FIFO - /// - if the FIFOs are not enabled, the data byte and status are stored in - /// the receiving - /// holding register (the bottom word of the receive FIFO). - /// - /// The received data byte is read by performing reads from the `UARTDR` - /// register along with the corresponding status information. The status - /// information can also be read by a read of the `UARTRSR/UARTECR` - /// register. - /// - /// # Note - /// - /// You must disable the UART before any of the control registers are - /// reprogrammed. When the UART is disabled in the middle of - /// transmission or reception, it completes the current character before - /// stopping. - /// - /// # Source - /// ARM DDI 0183G 3.3.1 Data Register, UARTDR - #[bitsize(16)] - #[derive(Clone, Copy, DebugBits, FromBits)] - #[doc(alias = "UARTDR")] - pub struct Data { - _reserved: u4, - pub data: u8, - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, - } - - // TODO: FIFO Mode has different semantics - /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` - /// - /// The UARTRSR/UARTECR register is the receive status register/error clear - /// register. Receive status can also be read from the `UARTRSR` - /// register. If the status is read from this register, then the status - /// information for break, framing and parity corresponds to the - /// data character read from the [Data register](Data), `UARTDR` prior to - /// reading the UARTRSR register. The status information for overrun is - /// set immediately when an overrun condition occurs. - /// - /// - /// # Note - /// The received data character must be read first from the [Data - /// Register](Data), `UARTDR` before reading the error status associated - /// with that data character from the `UARTRSR` register. This read - /// sequence cannot be reversed, because the `UARTRSR` register is - /// updated only when a read occurs from the `UARTDR` register. However, - /// the status information can also be obtained by reading the `UARTDR` - /// register - /// - /// # Source - /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, - /// UARTRSR/UARTECR - #[bitsize(8)] - #[derive(Clone, Copy, DebugBits, FromBits)] - pub struct ReceiveStatusErrorClear { - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, - _reserved_unpredictable: u4, - } - - impl ReceiveStatusErrorClear { - pub fn reset(&mut self) { - // All the bits are cleared to 0 on reset. - *self = 0.into(); - } - } - - impl Default for ReceiveStatusErrorClear { - fn default() -> Self { - 0.into() - } - } - - #[bitsize(16)] - #[derive(Clone, Copy, DebugBits, FromBits)] - /// Flag Register, `UARTFR` - #[doc(alias = "UARTFR")] - pub struct Flags { - /// CTS Clear to send. This bit is the complement of the UART clear to - /// send, `nUARTCTS`, modem status input. That is, the bit is 1 - /// when `nUARTCTS` is LOW. - pub clear_to_send: bool, - /// DSR Data set ready. This bit is the complement of the UART data set - /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when - /// `nUARTDSR` is LOW. - pub data_set_ready: bool, - /// DCD Data carrier detect. This bit is the complement of the UART data - /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is - /// 1 when `nUARTDCD` is LOW. - pub data_carrier_detect: bool, - /// BUSY UART busy. If this bit is set to 1, the UART is busy - /// transmitting data. This bit remains set until the complete - /// byte, including all the stop bits, has been sent from the - /// shift register. This bit is set as soon as the transmit FIFO - /// becomes non-empty, regardless of whether the UART is enabled - /// or not. - pub busy: bool, - /// RXFE Receive FIFO empty. The meaning of this bit depends on the - /// state of the FEN bit in the UARTLCR_H register. If the FIFO - /// is disabled, this bit is set when the receive holding - /// register is empty. If the FIFO is enabled, the RXFE bit is - /// set when the receive FIFO is empty. - pub receive_fifo_empty: bool, - /// TXFF Transmit FIFO full. The meaning of this bit depends on the - /// state of the FEN bit in the UARTLCR_H register. If the FIFO - /// is disabled, this bit is set when the transmit holding - /// register is full. If the FIFO is enabled, the TXFF bit is - /// set when the transmit FIFO is full. - pub transmit_fifo_full: bool, - /// RXFF Receive FIFO full. The meaning of this bit depends on the state - /// of the FEN bit in the UARTLCR_H register. If the FIFO is - /// disabled, this bit is set when the receive holding register - /// is full. If the FIFO is enabled, the RXFF bit is set when - /// the receive FIFO is full. - pub receive_fifo_full: bool, - /// Transmit FIFO empty. The meaning of this bit depends on the state of - /// the FEN bit in the [Line Control register](LineControl), - /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the - /// transmit holding register is empty. If the FIFO is enabled, - /// the TXFE bit is set when the transmit FIFO is empty. This - /// bit does not indicate if there is data in the transmit shift - /// register. - pub transmit_fifo_empty: bool, - /// `RI`, is `true` when `nUARTRI` is `LOW`. - pub ring_indicator: bool, - _reserved_zero_no_modify: u7, - } - - impl Flags { - pub fn reset(&mut self) { - // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - self.set_receive_fifo_full(false); - self.set_transmit_fifo_full(false); - self.set_busy(false); - self.set_receive_fifo_empty(true); - self.set_transmit_fifo_empty(true); - } - } - - impl Default for Flags { - fn default() -> Self { - let mut ret: Self = 0.into(); - ret.reset(); - ret - } - } - - #[bitsize(16)] - #[derive(Clone, Copy, DebugBits, FromBits)] - /// Line Control Register, `UARTLCR_H` - #[doc(alias = "UARTLCR_H")] - pub struct LineControl { - /// 15:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u8, - /// 7 SPS Stick parity select. - /// 0 = stick parity is disabled - /// 1 = either: - /// • if the EPS bit is 0 then the parity bit is transmitted and checked - /// as a 1 • if the EPS bit is 1 then the parity bit is - /// transmitted and checked as a 0. This bit has no effect when - /// the PEN bit disables parity checking and generation. See Table 3-11 - /// on page 3-14 for the parity truth table. - pub sticky_parity: bool, - /// WLEN Word length. These bits indicate the number of data bits - /// transmitted or received in a frame as follows: b11 = 8 bits - /// b10 = 7 bits - /// b01 = 6 bits - /// b00 = 5 bits. - pub word_length: WordLength, - /// FEN Enable FIFOs: - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers 1 = transmit and receive FIFO - /// buffers are enabled (FIFO mode). - pub fifos_enabled: Mode, - /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits - /// are transmitted at the end of the frame. The receive - /// logic does not check for two stop bits being received. - pub two_stops_bits: bool, - /// EPS Even parity select. Controls the type of parity the UART uses - /// during transmission and reception: - /// - 0 = odd parity. The UART generates or checks for an odd number of - /// 1s in the data and parity bits. - /// - 1 = even parity. The UART generates or checks for an even number - /// of 1s in the data and parity bits. - /// This bit has no effect when the `PEN` bit disables parity checking - /// and generation. See Table 3-11 on page 3-14 for the parity - /// truth table. - pub parity: Parity, - /// 1 PEN Parity enable: - /// - /// - 0 = parity is disabled and no parity bit added to the data frame - /// - 1 = parity checking and generation is enabled. - /// - /// See Table 3-11 on page 3-14 for the parity truth table. - pub parity_enabled: bool, - /// BRK Send break. - /// - /// If this bit is set to `1`, a low-level is continually output on the - /// `UARTTXD` output, after completing transmission of the - /// current character. For the proper execution of the break command, - /// the software must set this bit for at least two complete - /// frames. For normal use, this bit must be cleared to `0`. - pub send_break: bool, - } - - impl LineControl { - pub fn reset(&mut self) { - // All the bits are cleared to 0 when reset. - *self = 0.into(); - } - } - - impl Default for LineControl { - fn default() -> Self { - 0.into() - } - } - - #[bitsize(1)] - #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] - /// `EPS` "Even parity select", field of [Line Control - /// register](LineControl). - pub enum Parity { - /// - 0 = odd parity. The UART generates or checks for an odd number of - /// 1s in the data and parity bits. - Odd = 0, - /// - 1 = even parity. The UART generates or checks for an even number - /// of 1s in the data and parity bits. - Even = 1, - } - - #[bitsize(1)] - #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] - /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control - /// register](LineControl). - pub enum Mode { - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers - Character = 0, - /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). - FIFO = 1, - } - - impl From for bool { - fn from(val: Mode) -> Self { - matches!(val, Mode::FIFO) - } - } - - #[bitsize(2)] - #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] - /// `WLEN` Word length, field of [Line Control register](LineControl). - /// - /// These bits indicate the number of data bits transmitted or received in a - /// frame as follows: - pub enum WordLength { - /// b11 = 8 bits - _8Bits = 0b11, - /// b10 = 7 bits - _7Bits = 0b10, - /// b01 = 6 bits - _6Bits = 0b01, - /// b00 = 5 bits. - _5Bits = 0b00, - } - - /// Control Register, `UARTCR` - /// - /// The `UARTCR` register is the control register. All the bits are cleared - /// to `0` on reset except for bits `9` and `8` that are set to `1`. - /// - /// # Source - /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 - #[bitsize(16)] - #[doc(alias = "UARTCR")] - #[derive(Clone, Copy, DebugBits, FromBits)] - pub struct Control { - /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled - /// in the middle of transmission or reception, it completes the current - /// character before stopping. 1 = the UART is enabled. Data - /// transmission and reception occurs for either UART signals or SIR - /// signals depending on the setting of the SIREN bit. - pub enable_uart: bool, - /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT` - /// remains LOW (no light pulse generated), and signal transitions on - /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is - /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, - /// in the marking state. Signal transitions on UARTRXD or modem status - /// inputs have no effect. This bit has no effect if the UARTEN bit - /// disables the UART. - pub enable_sir: bool, - /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding - /// mode. If this bit is cleared to 0, low-level bits are transmitted as - /// an active high pulse with a width of 3/ 16th of the bit period. If - /// this bit is set to 1, low-level bits are transmitted with a pulse - /// width that is 3 times the period of the IrLPBaud16 input signal, - /// regardless of the selected bit rate. Setting this bit uses less - /// power, but might reduce transmission distances. - pub sir_lowpower_irda_mode: u1, - /// Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u4, - /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is - /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR - /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed - /// through to the SIRIN path. The SIRTEST bit in the test register must - /// be set to 1 to override the normal half-duplex SIR operation. This - /// must be the requirement for accessing the test registers during - /// normal operation, and SIRTEST must be cleared to 0 when loopback - /// testing is finished. This feature reduces the amount of external - /// coupling required during system test. If this bit is set to 1, and - /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the - /// UARTRXD path. In either SIR mode or UART mode, when this bit is set, - /// the modem outputs are also fed through to the modem inputs. This bit - /// is cleared to 0 on reset, to disable loopback. - pub enable_loopback: bool, - /// `TXE` Transmit enable. If this bit is set to 1, the transmit section - /// of the UART is enabled. Data transmission occurs for either UART - /// signals, or SIR signals depending on the setting of the SIREN bit. - /// When the UART is disabled in the middle of transmission, it - /// completes the current character before stopping. - pub enable_transmit: bool, - /// `RXE` Receive enable. If this bit is set to 1, the receive section - /// of the UART is enabled. Data reception occurs for either UART - /// signals or SIR signals depending on the setting of the SIREN bit. - /// When the UART is disabled in the middle of reception, it completes - /// the current character before stopping. - pub enable_receive: bool, - /// `DTR` Data transmit ready. This bit is the complement of the UART - /// data transmit ready, `nUARTDTR`, modem status output. That is, when - /// the bit is programmed to a 1 then `nUARTDTR` is LOW. - pub data_transmit_ready: bool, - /// `RTS` Request to send. This bit is the complement of the UART - /// request to send, `nUARTRTS`, modem status output. That is, when the - /// bit is programmed to a 1 then `nUARTRTS` is LOW. - pub request_to_send: bool, - /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`) - /// modem status output. That is, when the bit is programmed to a 1 the - /// output is 0. For DTE this can be used as Data Carrier Detect (DCD). - pub out_1: bool, - /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`) - /// modem status output. That is, when the bit is programmed to a 1, the - /// output is 0. For DTE this can be used as Ring Indicator (RI). - pub out_2: bool, - /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1, - /// RTS hardware flow control is enabled. Data is only requested when - /// there is space in the receive FIFO for it to be received. - pub rts_hardware_flow_control_enable: bool, - /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1, - /// CTS hardware flow control is enabled. Data is only transmitted when - /// the `nUARTCTS` signal is asserted. - pub cts_hardware_flow_control_enable: bool, - } - - impl Control { - pub fn reset(&mut self) { - *self = 0.into(); - self.set_enable_receive(true); - self.set_enable_transmit(true); - } - } - - impl Default for Control { - fn default() -> Self { - let mut ret: Self = 0.into(); - ret.reset(); - ret - } - } - - /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC - pub const INT_OE: u32 = 1 << 10; - pub const INT_BE: u32 = 1 << 9; - pub const INT_PE: u32 = 1 << 8; - pub const INT_FE: u32 = 1 << 7; - pub const INT_RT: u32 = 1 << 6; - pub const INT_TX: u32 = 1 << 5; - pub const INT_RX: u32 = 1 << 4; - pub const INT_DSR: u32 = 1 << 3; - pub const INT_DCD: u32 = 1 << 2; - pub const INT_CTS: u32 = 1 << 1; - pub const INT_RI: u32 = 1 << 0; - pub const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; - - #[repr(u32)] - pub enum Interrupt { - OE = 1 << 10, - BE = 1 << 9, - PE = 1 << 8, - FE = 1 << 7, - RT = 1 << 6, - TX = 1 << 5, - RX = 1 << 4, - DSR = 1 << 3, - DCD = 1 << 2, - CTS = 1 << 1, - RI = 1 << 0, - } - - impl Interrupt { - pub const E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; - } -} - -// TODO: You must disable the UART before any of the control registers are -// reprogrammed. When the UART is disabled in the middle of transmission or -// reception, it completes the current character before stopping diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs deleted file mode 100644 index 8d066ebf6d016fa30db004933751a854d7e59117..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use core::{mem::MaybeUninit, ptr::NonNull}; - -use qemu_api::bindings::*; - -use crate::device::PL011State; - -pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { - read: Some(pl011_read), - write: Some(pl011_write), - read_with_attrs: None, - write_with_attrs: None, - endianness: device_endian::DEVICE_NATIVE_ENDIAN, - valid: unsafe { MaybeUninit::::zeroed().assume_init() }, - impl_: MemoryRegionOps__bindgen_ty_2 { - min_access_size: 4, - max_access_size: 4, - ..unsafe { MaybeUninit::::zeroed().assume_init() } - }, -}; - -#[no_mangle] -unsafe extern "C" fn pl011_read( - opaque: *mut core::ffi::c_void, - addr: hwaddr, - size: core::ffi::c_uint, -) -> u64 { - assert!(!opaque.is_null()); - let mut state = unsafe { NonNull::new_unchecked(opaque.cast::()) }; - let val = unsafe { state.as_mut().read(addr, size) }; - match val { - std::ops::ControlFlow::Break(val) => val, - std::ops::ControlFlow::Continue(val) => { - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; - unsafe { qemu_chr_fe_accept_input(cb_ptr) }; - - val - } - } -} - -#[no_mangle] -unsafe extern "C" fn pl011_write( - opaque: *mut core::ffi::c_void, - addr: hwaddr, - data: u64, - _size: core::ffi::c_uint, -) { - unsafe { - assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - state.as_mut().write(addr, data) - } -} diff --git a/rust/hw/meson.build b/rust/hw/meson.build deleted file mode 100644 index 860196645e719624d2e2e6bc301b62b81ab2e19b..0000000000000000000000000000000000000000 --- a/rust/hw/meson.build +++ /dev/null @@ -1 +0,0 @@ -subdir('char') diff --git a/rust/meson.build b/rust/meson.build index def77389cddc52f5d4503840e9bdfb1207586fa2..7a32b1b195083571931ad589965c10ddaf6383b1 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -1,4 +1,2 @@ subdir('qemu-api-macros') subdir('qemu-api') - -subdir('hw') diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 30677c3ec9032ea01090f74602d839d1c571d012..62a2cf45d28ed5565076443b9f931a647d395542 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -27,9 +27,7 @@ sub_file="${sub_tdir}/submodule.tar" # in their checkout, because the build environment is completely # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 - berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs - proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs + berkeley-testfloat-3 proc-macro2-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" sub_deinit="" diff --git a/scripts/make-release b/scripts/make-release index 8dc939124c4fd4abf3509c3b64c0588bc8810962..cf7d694ef73ba1c6c5afad5d211a42f6a6fe1577 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -18,9 +18,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 - berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs - proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs + berkeley-testfloat-3 proc-macro2-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" src="$1" diff --git a/scripts/rust/rust_root_crate.sh b/scripts/rust/rust_root_crate.sh deleted file mode 100755 index 975bddf7f1a4c6ca7770f800bdc894cdff1f3ab1..0000000000000000000000000000000000000000 --- a/scripts/rust/rust_root_crate.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -set -eu - -cat < X-Patchwork-Id: 13849130 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 7F8C9CE8E7B for ; Thu, 24 Oct 2024 14:05:06 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yR7-00078d-0I; Thu, 24 Oct 2024 10:03:38 -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 1t3yR5-000780-B8 for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:35 -0400 Received: from mail-ed1-x530.google.com ([2a00:1450:4864:20::530]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yQz-0003uQ-Uw for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:34 -0400 Received: by mail-ed1-x530.google.com with SMTP id 4fb4d7f45d1cf-5c9634c9160so1045954a12.2 for ; Thu, 24 Oct 2024 07:03:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778608; x=1730383408; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=+SVgJkd9IbTDUuA8uJ6beJLQHAW4cKJ/d6U0ghRVnFM=; b=NzwFAW6doVm/aVU3/xo1B98cqajhp6aYZISC3nuqdDkDkCn8BL6KDSkNnf3JpxOFoc tHykRjr5jIbQQgqNP5cuLo0sLHE61Bbq4CzENLQ+1OLSmiO3Ul9DtpUL9KBmpC1XgFNM fJnadKUUYRskIaj2k4+uR6t4rJur/PL3GQfue5FvTduObxAYsSOnuEuNWByLmYMF9DF8 RwR6qlEjexn/ITp786whFjxRXSryVf5G2XH0QONkjQaqplpIUJdwBMYH6ylc1VfqxLKD Ux+uRD3X4azs7snd614+29Tm4wV2msu0qMHzlwuc05uuFetn/rx10jqDbsdsV1Bedn9B SP6w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778608; x=1730383408; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+SVgJkd9IbTDUuA8uJ6beJLQHAW4cKJ/d6U0ghRVnFM=; b=dJUx9Uq0Hc05vck1cuGlZZkk/BKUiaR0DZ2aJ3beoe6E/vgd9A4wFyWDYLw/cHOhD+ eV0wk98oChAop2Js4nWqEkmT9tduq8cNZRgOZXFSFX3exhrUj5+68RMharSnVjLKOAMP VqDi8yebuqdIpg3WBoWIQVpnDfhsrphnWRgS174R7jzo6mikuZbnXGLVd5F3LgaCqRpX qbyNm5/3Fse3yghxJphA8npB+/DJxluO3urrdeEo4OtjeRk0Chl/wNuSYdyrr+5LN6B1 yrqLNOSjOgpdWnMhdolhZwPoX3iLU+6uc5hOTENQrSIRBJuFuMTtYnv21nFB7zOfVtRt JFkw== X-Gm-Message-State: AOJu0YxAMy3pcUdS03y2KeoL8MXpCqrdguvl6I4fjViSTxMkC/zbT72j 6APJeBDchFgisfsxXq1UWGSrEs2upMwNX3ThLGJYdzzpCKWcXhsQW0Xu0LQOWUA= X-Google-Smtp-Source: AGHT+IFojY8Ck1w96BsUpnWgdTGC0RF9nOF/B6pnBvGm68yX+5eMRof0AOB3UQDoAf68PJ3dx2i++g== X-Received: by 2002:a17:907:9484:b0:a9a:1565:1051 with SMTP id a640c23a62f3a-a9ad27127c7mr195794766b.10.1729778606758; Thu, 24 Oct 2024 07:03:26 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:26 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:00 +0300 Subject: [PATCH 02/11] rust: add PL011 device model MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-2-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=84468; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=yXwI2OJKwlSx0i7yHHP76uQoqKGPlyD8Q0wIdVoTKCU=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9pc3lyZUNjZnRYWDEyOGhhcEZ1ZTczSW10ClQrZDRCS211Q1JWUEpjM2h5 YzZKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG9nQUt DUkIzS2Nkd2YzNEowSjk5RUFDUTgvSUxBVGVRampqdDZkN2pmMlk1NUoxZGpmSlIzRTI2RG5HNQ poQUpCRmpSd3NxZE53ZXdFbU1Ib3QwT1NVL3ZMRUVDeU9LdkJaWnpWU3F0UVp4Tk9FYmFqdyswS HRWL1VEWkt1CmpwT0FqR3I3WnBTV3ppWHJVcG8rOXYxU0JFbkc4YXZCNlI1azdyQU1vMFFNM2p4 TTVpMWw4VkRIdTVEdEE2cTQKWnFCYkZ5dEdGRWppN09ld05MYTZvQXNCTzBzaE4yNHNKeWt6Zzd rOXpaMnNzUnI5NjJBMWZIZHRRbDdsd3M2Ugp6TmM2eFd4MVRsc20zLzNOTjV5dkxXZlNDb2Fuby tCUklkWS9idDZjakd2WGhCUWxyY253Q2dSVkFESEh3Ulc2CkFhd3FGczZuUjM5a2dJdm9ScmcrY kVYWTduRzQraWpSRzR3TkhNWU1LYUZqanEwMERyUlVFUmNWTjVlZzJrK2QKeDlYdGxvdzc2N1g3 V1Mvd1JWc1AxNTVxNXhrMndDTDhwVHZ4Qmd6WVRJaFlzUXFSZW9PTVFLTVVsWEpaYUplRwo1cTB RQTloQllWajdSUitDL0ptZ3pHUmpObkQ1M1JZM2RnSGN5UDUzQ1dtaFVLYUFrQnBTUnk3MWorc0 x6U0N6Ck92a2RGR1lNQUJod1Brb2xtRnpubVc3VGFCRTRhSHhQd2t4b3lZaDBYZDRsL0lJOXdjU VUrMnMrRld6TUYzN1QKandCR25KUm1yOWZSV0k3ZlJGRDFJY1ZXVDBIVnlScis1RWgrZER2M3Vw WFhqM3V5S0szdWF5OHQzRXQ3M0g4ZQovTjMvUGR2cVFBZ0toYUVjdUh6VWdIL0Q4Q1BTSmNPS09 JTyt5ajNWakJSWWRENnYyandlbVNpM1E3c1lPUVpVCnZJWEErZz09Cj1hQmdYCi0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::530; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ed1-x530.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 commit adds a re-implementation of hw/char/pl011.c in Rust. How to build: 1. Configure a QEMU build with: --enable-system --target-list=aarch64-softmmu --enable-rust 2. Launching a VM with qemu-system-aarch64 should use the Rust version of the pl011 device Co-authored-by: Junjie Mao Co-authored-by: Paolo Bonzini Signed-off-by: Junjie Mao Signed-off-by: Paolo Bonzini Signed-off-by: Manos Pitsidianakis --- MAINTAINERS | 5 + meson.build | 24 + hw/arm/Kconfig | 30 +- rust/Kconfig | 1 + rust/hw/Kconfig | 2 + rust/hw/char/Kconfig | 3 + rust/hw/char/meson.build | 1 + rust/hw/char/pl011/.gitignore | 2 + rust/hw/char/pl011/Cargo.lock | 134 +++++ rust/hw/char/pl011/Cargo.toml | 26 + rust/hw/char/pl011/README.md | 31 ++ rust/hw/char/pl011/meson.build | 26 + rust/hw/char/pl011/src/device.rs | 599 +++++++++++++++++++++ rust/hw/char/pl011/src/device_class.rs | 70 +++ rust/hw/char/pl011/src/lib.rs | 586 ++++++++++++++++++++ rust/hw/char/pl011/src/memory_ops.rs | 59 ++ rust/hw/meson.build | 1 + rust/meson.build | 2 + scripts/archive-source.sh | 4 +- scripts/make-release | 4 +- scripts/rust/rust_root_crate.sh | 13 + subprojects/.gitignore | 7 + subprojects/arbitrary-int-1-rs.wrap | 7 + subprojects/bilge-0.2-rs.wrap | 7 + subprojects/bilge-impl-0.2-rs.wrap | 7 + subprojects/either-1-rs.wrap | 7 + subprojects/itertools-0.11-rs.wrap | 7 + .../packagefiles/arbitrary-int-1-rs/meson.build | 19 + subprojects/packagefiles/bilge-0.2-rs/meson.build | 29 + .../packagefiles/bilge-impl-0.2-rs/meson.build | 45 ++ subprojects/packagefiles/either-1-rs/meson.build | 24 + .../packagefiles/itertools-0.11-rs/meson.build | 30 ++ .../packagefiles/proc-macro-error-1-rs/meson.build | 40 ++ .../proc-macro-error-attr-1-rs/meson.build | 32 ++ .../packagefiles/unicode-ident-1-rs/meson.build | 20 + subprojects/proc-macro-error-1-rs.wrap | 7 + subprojects/proc-macro-error-attr-1-rs.wrap | 7 + 37 files changed, 1906 insertions(+), 12 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 793c683aa6cccafca9c98b74b9a20d84211f041b..c3bfa132fd6ecd61dd733b760a5e1ccd39613455 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1137,6 +1137,11 @@ F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c F: docs/system/arm/nrf.rst +ARM PL011 Rust device +M: Manos Pitsidianakis +S: Maintained +F: rust/hw/char/pl011/ + AVR Machines ------------- diff --git a/meson.build b/meson.build index 0d617c551e61c90c7c357c62ff5da34437723947..c26c417de16ad9256a019a90fd89ea990bb3f948 100644 --- a/meson.build +++ b/meson.build @@ -3520,6 +3520,7 @@ qom_ss = ss.source_set() system_ss = ss.source_set() specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() +rust_devices_ss = ss.source_set() stub_ss = ss.source_set() trace_ss = ss.source_set() user_ss = ss.source_set() @@ -4067,6 +4068,29 @@ foreach target : target_dirs arch_srcs += target_specific.sources() arch_deps += target_specific.dependencies() + if have_rust and have_system + target_rust = rust_devices_ss.apply(config_target, strict: false) + crates = [] + foreach dep : target_rust.dependencies() + crates += dep.get_variable('crate') + endforeach + if crates.length() > 0 + rlib_rs = custom_target('rust_' + target.underscorify() + '.rs', + output: 'rust_' + target.underscorify() + '.rs', + command: [find_program('scripts/rust/rust_root_crate.sh')] + crates, + capture: true, + build_by_default: true, + build_always_stale: true) + rlib = static_library('rust_' + target.underscorify(), + rlib_rs, + dependencies: target_rust.dependencies(), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: rustc_args, + rust_abi: 'c') + arch_deps += declare_dependency(link_whole: [rlib]) + endif + endif + # allow using headers from the dependencies but do not include the sources, # because this emulator only needs those in "objects". For external # dependencies, the full dependency is included below in the executable. diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 53eb7bb3d0157bb6e8e078fe73ec66015ae0fe13..e7fd9338d11dadb7a18032c674927db3d9887bdd 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -20,7 +20,8 @@ config ARM_VIRT select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL061 # GPIO select GPIO_PWR @@ -73,7 +74,8 @@ config HIGHBANK select AHCI select ARM_TIMER # sp804 select ARM_V7M - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL022 # SPI select PL031 # RTC select PL061 # GPIO @@ -86,7 +88,8 @@ config INTEGRATOR depends on TCG && ARM select ARM_TIMER select INTEGRATOR_DEBUG - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL041 # audio select PL050 # keyboard/mouse @@ -104,7 +107,8 @@ config MUSCA default y depends on TCG && ARM select ARMSSE - select PL011 + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 select SPLIT_IRQ select UNIMP @@ -168,7 +172,8 @@ config REALVIEW select WM8750 # audio codec select LSI_SCSI_PCI select PCI - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL041 # audio codec select PL050 # keyboard/mouse @@ -193,7 +198,8 @@ config SBSA_REF select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL061 # GPIO select USB_XHCI_SYSBUS @@ -217,7 +223,8 @@ config STELLARIS select ARM_V7M select CMSDK_APB_WATCHDOG select I2C - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL022 # SPI select PL061 # GPIO select SSD0303 # OLED display @@ -277,7 +284,8 @@ config VEXPRESS select ARM_TIMER # sp804 select LAN9118 select PFLASH_CFI01 - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL041 # audio codec select PL181 # display select REALVIEW @@ -362,7 +370,8 @@ config RASPI default y depends on TCG && ARM select FRAMEBUFFER - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select SDHCI select USB_DWC2 select BCM2835_SPI @@ -438,7 +447,8 @@ config XLNX_VERSAL select ARM_GIC select CPU_CLUSTER select DEVICE_TREE - select PL011 + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select CADENCE select VIRTIO_MMIO select UNIMP diff --git a/rust/Kconfig b/rust/Kconfig index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f9f5c3909887451f71360a7986d79e57fdb43c91 100644 --- a/rust/Kconfig +++ b/rust/Kconfig @@ -0,0 +1 @@ +source hw/Kconfig diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..4d934f30afe13ddff418db8ec9e8b8eb25a9e8d0 --- /dev/null +++ b/rust/hw/Kconfig @@ -0,0 +1,2 @@ +# devices Kconfig +source char/Kconfig diff --git a/rust/hw/char/Kconfig b/rust/hw/char/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..a1732a9e97fe3211547e30bc9319382e6394ed5b --- /dev/null +++ b/rust/hw/char/Kconfig @@ -0,0 +1,3 @@ +config X_PL011_RUST + bool + default y if HAVE_RUST diff --git a/rust/hw/char/meson.build b/rust/hw/char/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..5716dc43ef6facdcf1cc963108347bbf4d12cf0e --- /dev/null +++ b/rust/hw/char/meson.build @@ -0,0 +1 @@ +subdir('pl011') diff --git a/rust/hw/char/pl011/.gitignore b/rust/hw/char/pl011/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..71eaff2035d5a65b57ae32dfeecf3d87bbc7b396 --- /dev/null +++ b/rust/hw/char/pl011/.gitignore @@ -0,0 +1,2 @@ +# Ignore generated bindings file overrides. +src/bindings.rs.inc diff --git a/rust/hw/char/pl011/Cargo.lock b/rust/hw/char/pl011/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..b58cebb186e99efe184117bb931a341543d4466b --- /dev/null +++ b/rust/hw/char/pl011/Cargo.lock @@ -0,0 +1,134 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arbitrary-int" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" + +[[package]] +name = "bilge" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" +dependencies = [ + "arbitrary-int", + "bilge-impl", +] + +[[package]] +name = "bilge-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" +dependencies = [ + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "pl011" +version = "0.1.0" +dependencies = [ + "bilge", + "bilge-impl", + "qemu_api", + "qemu_api_macros", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qemu_api" +version = "0.1.0" + +[[package]] +name = "qemu_api_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b089e3dded623131ee13b4af8145b84388755df7 --- /dev/null +++ b/rust/hw/char/pl011/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "pl011" +version = "0.1.0" +edition = "2021" +authors = ["Manos Pitsidianakis "] +license = "GPL-2.0-or-later" +readme = "README.md" +homepage = "https://www.qemu.org" +description = "pl011 device model for QEMU" +repository = "https://gitlab.com/epilys/rust-for-qemu" +resolver = "2" +publish = false +keywords = [] +categories = [] + +[lib] +crate-type = ["staticlib"] + +[dependencies] +bilge = { version = "0.2.0" } +bilge-impl = { version = "0.2.0" } +qemu_api = { path = "../../../qemu-api" } +qemu_api_macros = { path = "../../../qemu-api-macros" } + +# Do not include in any global workspace +[workspace] diff --git a/rust/hw/char/pl011/README.md b/rust/hw/char/pl011/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cd7dea31634241cbf96b0be13f21d52bbd8ae750 --- /dev/null +++ b/rust/hw/char/pl011/README.md @@ -0,0 +1,31 @@ +# PL011 QEMU Device Model + +This library implements a device model for the PrimeCell® UART (PL011) +device in QEMU. + +## Build static lib + +Host build target must be explicitly specified: + +```sh +cargo build --target x86_64-unknown-linux-gnu +``` + +Replace host target triplet if necessary. + +## Generate Rust documentation + +To generate docs for this crate, including private items: + +```sh +cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu +``` + +To include direct dependencies like `bilge` (bitmaps for register types): + +```sh +cargo tree --depth 1 -e normal --prefix none \ + | cut -d' ' -f1 \ + | xargs printf -- '-p %s\n' \ + | xargs cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu +``` diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..547cca5a96f7eef284caf1949380b65f7d015d92 --- /dev/null +++ b/rust/hw/char/pl011/meson.build @@ -0,0 +1,26 @@ +subproject('bilge-0.2-rs', required: true) +subproject('bilge-impl-0.2-rs', required: true) + +bilge_dep = dependency('bilge-0.2-rs') +bilge_impl_dep = dependency('bilge-impl-0.2-rs') + +_libpl011_rs = static_library( + 'pl011', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [ + bilge_dep, + bilge_impl_dep, + qemu_api, + qemu_api_macros, + ], +) + +rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( + link_whole: [_libpl011_rs], + # Putting proc macro crates in `dependencies` is necessary for Meson to find + # them when compiling the root per-target static rust lib. + dependencies: [bilge_impl_dep, qemu_api_macros], + variables: {'crate': 'pl011'}, +)]) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs new file mode 100644 index 0000000000000000000000000000000000000000..c7193b41beec0b177dbc75ac0e43fcfea4c82bfb --- /dev/null +++ b/rust/hw/char/pl011/src/device.rs @@ -0,0 +1,599 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::{ + ffi::{c_int, c_uchar, c_uint, c_void, CStr}, + ptr::{addr_of, addr_of_mut, NonNull}, +}; + +use qemu_api::{ + bindings::{self, *}, + definitions::ObjectImpl, +}; + +use crate::{ + memory_ops::PL011_OPS, + registers::{self, Interrupt}, + RegisterOffset, +}; + +static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; + +const DATA_BREAK: u32 = 1 << 10; + +/// QEMU sourced constant. +pub const PL011_FIFO_DEPTH: usize = 16_usize; + +#[repr(C)] +#[derive(Debug, qemu_api_macros::Object)] +/// PL011 Device Model in QEMU +pub struct PL011State { + pub parent_obj: SysBusDevice, + pub iomem: MemoryRegion, + #[doc(alias = "fr")] + pub flags: registers::Flags, + #[doc(alias = "lcr")] + pub line_control: registers::LineControl, + #[doc(alias = "rsr")] + pub receive_status_error_clear: registers::ReceiveStatusErrorClear, + #[doc(alias = "cr")] + pub control: registers::Control, + pub dmacr: u32, + pub int_enabled: u32, + pub int_level: u32, + pub read_fifo: [u32; PL011_FIFO_DEPTH], + pub ilpr: u32, + pub ibrd: u32, + pub fbrd: u32, + pub ifl: u32, + pub read_pos: usize, + pub read_count: usize, + pub read_trigger: usize, + #[doc(alias = "chr")] + pub char_backend: CharBackend, + /// QEMU interrupts + /// + /// ```text + /// * sysbus MMIO region 0: device registers + /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line) + /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line) + /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line) + /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line) + /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line) + /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line) + /// ``` + #[doc(alias = "irq")] + pub interrupts: [qemu_irq; 6usize], + #[doc(alias = "clk")] + pub clock: NonNull, + #[doc(alias = "migrate_clk")] + pub migrate_clock: bool, +} + +impl ObjectImpl for PL011State { + type Class = PL011Class; + const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; + const TYPE_NAME: &'static CStr = crate::TYPE_PL011; + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); + const ABSTRACT: bool = false; + const INSTANCE_INIT: Option = Some(pl011_init); + const INSTANCE_POST_INIT: Option = None; + const INSTANCE_FINALIZE: Option = None; +} + +#[repr(C)] +pub struct PL011Class { + _inner: [u8; 0], +} + +impl qemu_api::definitions::Class for PL011Class { + const CLASS_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), + > = Some(crate::device_class::pl011_class_init); + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), + > = None; +} + +#[used] +pub static CLK_NAME: &CStr = c"clk"; + +impl PL011State { + /// Initializes a pre-allocated, unitialized instance of `PL011State`. + /// + /// # Safety + /// + /// `self` must point to a correctly sized and aligned location for the + /// `PL011State` type. It must not be called more than once on the same + /// location/instance. All its fields are expected to hold unitialized + /// values with the sole exception of `parent_obj`. + pub unsafe fn init(&mut self) { + let dev = addr_of_mut!(*self).cast::(); + // SAFETY: + // + // self and self.iomem are guaranteed to be valid at this point since callers + // must make sure the `self` reference is valid. + unsafe { + memory_region_init_io( + addr_of_mut!(self.iomem), + addr_of_mut!(*self).cast::(), + &PL011_OPS, + addr_of_mut!(*self).cast::(), + Self::TYPE_INFO.name, + 0x1000, + ); + let sbd = addr_of_mut!(*self).cast::(); + sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); + for irq in self.interrupts.iter_mut() { + sysbus_init_irq(sbd, irq); + } + } + // SAFETY: + // + // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, + // we can overwrite the undefined value without side effects. This is + // safe since all PL011State instances are created by QOM code which + // calls this function to initialize the fields; therefore no code is + // able to access an invalid self.clock value. + unsafe { + self.clock = NonNull::new(qdev_init_clock_in( + dev, + CLK_NAME.as_ptr(), + None, /* pl011_clock_update */ + addr_of_mut!(*self).cast::(), + ClockEvent::ClockUpdate.0, + )) + .unwrap(); + } + } + + pub fn read( + &mut self, + offset: hwaddr, + _size: core::ffi::c_uint, + ) -> std::ops::ControlFlow { + use RegisterOffset::*; + + std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&v) => { + u64::from(PL011_ID_ARM[((offset - 0xfe0) >> 2) as usize]) + } + Err(_) => { + // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + 0 + } + Ok(DR) => { + // s->flags &= ~PL011_FLAG_RXFF; + self.flags.set_receive_fifo_full(false); + let c = self.read_fifo[self.read_pos]; + if self.read_count > 0 { + self.read_count -= 1; + self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); + } + if self.read_count == 0 { + // self.flags |= PL011_FLAG_RXFE; + self.flags.set_receive_fifo_empty(true); + } + if self.read_count + 1 == self.read_trigger { + //self.int_level &= ~ INT_RX; + self.int_level &= !registers::INT_RX; + } + // Update error bits. + self.receive_status_error_clear = c.to_be_bytes()[3].into(); + self.update(); + // Must call qemu_chr_fe_accept_input, so return Continue: + return std::ops::ControlFlow::Continue(c.into()); + } + Ok(RSR) => u8::from(self.receive_status_error_clear).into(), + Ok(FR) => u16::from(self.flags).into(), + Ok(FBRD) => self.fbrd.into(), + Ok(ILPR) => self.ilpr.into(), + Ok(IBRD) => self.ibrd.into(), + Ok(LCR_H) => u16::from(self.line_control).into(), + Ok(CR) => { + // We exercise our self-control. + u16::from(self.control).into() + } + Ok(FLS) => self.ifl.into(), + Ok(IMSC) => self.int_enabled.into(), + Ok(RIS) => self.int_level.into(), + Ok(MIS) => u64::from(self.int_level & self.int_enabled), + Ok(ICR) => { + // "The UARTICR Register is the interrupt clear register and is write-only" + // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR + 0 + } + Ok(DMACR) => self.dmacr.into(), + }) + } + + pub fn write(&mut self, offset: hwaddr, value: u64) { + // eprintln!("write offset {offset} value {value}"); + use RegisterOffset::*; + let value: u32 = value as u32; + match RegisterOffset::try_from(offset) { + Err(_bad_offset) => { + eprintln!("write bad offset {offset} value {value}"); + } + Ok(DR) => { + // ??? Check if transmitter is enabled. + let ch: u8 = value as u8; + // XXX this blocks entire thread. Rewrite to use + // qemu_chr_fe_write and background I/O callbacks + + // SAFETY: self.char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + unsafe { + qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1); + } + self.loopback_tx(value); + self.int_level |= registers::INT_TX; + self.update(); + } + Ok(RSR) => { + self.receive_status_error_clear = 0.into(); + } + Ok(FR) => { + // flag writes are ignored + } + Ok(ILPR) => { + self.ilpr = value; + } + Ok(IBRD) => { + self.ibrd = value; + } + Ok(FBRD) => { + self.fbrd = value; + } + Ok(LCR_H) => { + let value = value as u16; + let new_val: registers::LineControl = value.into(); + // Reset the FIFO state on FIFO enable or disable + if bool::from(self.line_control.fifos_enabled()) + ^ bool::from(new_val.fifos_enabled()) + { + self.reset_fifo(); + } + if self.line_control.send_break() ^ new_val.send_break() { + let mut break_enable: c_int = new_val.send_break().into(); + // SAFETY: self.char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + unsafe { + qemu_chr_fe_ioctl( + addr_of_mut!(self.char_backend), + CHR_IOCTL_SERIAL_SET_BREAK as i32, + addr_of_mut!(break_enable).cast::(), + ); + } + self.loopback_break(break_enable > 0); + } + self.line_control = new_val; + self.set_read_trigger(); + } + Ok(CR) => { + // ??? Need to implement the enable bit. + let value = value as u16; + self.control = value.into(); + self.loopback_mdmctrl(); + } + Ok(FLS) => { + self.ifl = value; + self.set_read_trigger(); + } + Ok(IMSC) => { + self.int_enabled = value; + self.update(); + } + Ok(RIS) => {} + Ok(MIS) => {} + Ok(ICR) => { + self.int_level &= !value; + self.update(); + } + Ok(DMACR) => { + self.dmacr = value; + if value & 3 > 0 { + // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); + eprintln!("pl011: DMA not implemented"); + } + } + } + } + + #[inline] + fn loopback_tx(&mut self, value: u32) { + if !self.loopback_enabled() { + return; + } + + // Caveat: + // + // In real hardware, TX loopback happens at the serial-bit level + // and then reassembled by the RX logics back into bytes and placed + // into the RX fifo. That is, loopback happens after TX fifo. + // + // Because the real hardware TX fifo is time-drained at the frame + // rate governed by the configured serial format, some loopback + // bytes in TX fifo may still be able to get into the RX fifo + // that could be full at times while being drained at software + // pace. + // + // In such scenario, the RX draining pace is the major factor + // deciding which loopback bytes get into the RX fifo, unless + // hardware flow-control is enabled. + // + // For simplicity, the above described is not emulated. + self.put_fifo(value); + } + + fn loopback_mdmctrl(&mut self) { + if !self.loopback_enabled() { + return; + } + + /* + * Loopback software-driven modem control outputs to modem status inputs: + * FR.RI <= CR.Out2 + * FR.DCD <= CR.Out1 + * FR.CTS <= CR.RTS + * FR.DSR <= CR.DTR + * + * The loopback happens immediately even if this call is triggered + * by setting only CR.LBE. + * + * CTS/RTS updates due to enabled hardware flow controls are not + * dealt with here. + */ + + //fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD | + // PL011_FLAG_DSR | PL011_FLAG_CTS); + //fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0; + //fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0; + //fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0; + //fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0; + // + self.flags.set_ring_indicator(self.control.out_2()); + self.flags.set_data_carrier_detect(self.control.out_1()); + self.flags.set_clear_to_send(self.control.request_to_send()); + self.flags + .set_data_set_ready(self.control.data_transmit_ready()); + + // Change interrupts based on updated FR + let mut il = self.int_level; + + il &= !Interrupt::MS; + //il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0; + //il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0; + //il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0; + //il |= (fr & PL011_FLAG_RI) ? INT_RI : 0; + + if self.flags.data_set_ready() { + il |= Interrupt::DSR as u32; + } + if self.flags.data_carrier_detect() { + il |= Interrupt::DCD as u32; + } + if self.flags.clear_to_send() { + il |= Interrupt::CTS as u32; + } + if self.flags.ring_indicator() { + il |= Interrupt::RI as u32; + } + self.int_level = il; + self.update(); + } + + fn loopback_break(&mut self, enable: bool) { + if enable { + self.loopback_tx(DATA_BREAK); + } + } + + fn set_read_trigger(&mut self) { + self.read_trigger = 1; + } + + pub fn realize(&mut self) { + // SAFETY: self.char_backend has the correct size and alignment for a + // CharBackend object, and its callbacks are of the correct types. + unsafe { + qemu_chr_fe_set_handlers( + addr_of_mut!(self.char_backend), + Some(pl011_can_receive), + Some(pl011_receive), + Some(pl011_event), + None, + addr_of_mut!(*self).cast::(), + core::ptr::null_mut(), + true, + ); + } + } + + pub fn reset(&mut self) { + self.line_control.reset(); + self.receive_status_error_clear.reset(); + self.dmacr = 0; + self.int_enabled = 0; + self.int_level = 0; + self.ilpr = 0; + self.ibrd = 0; + self.fbrd = 0; + self.read_trigger = 1; + self.ifl = 0x12; + self.control.reset(); + self.flags = 0.into(); + self.reset_fifo(); + } + + pub fn reset_fifo(&mut self) { + self.read_count = 0; + self.read_pos = 0; + + /* Reset FIFO flags */ + self.flags.reset(); + } + + pub fn can_receive(&self) -> bool { + // trace_pl011_can_receive(s->lcr, s->read_count, r); + self.read_count < self.fifo_depth() + } + + pub fn event(&mut self, event: QEMUChrEvent) { + if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.fifo_enabled() { + self.put_fifo(DATA_BREAK); + self.receive_status_error_clear.set_break_error(true); + } + } + + #[inline] + pub fn fifo_enabled(&self) -> bool { + matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO) + } + + #[inline] + pub fn loopback_enabled(&self) -> bool { + self.control.enable_loopback() + } + + #[inline] + pub fn fifo_depth(&self) -> usize { + // Note: FIFO depth is expected to be power-of-2 + if self.fifo_enabled() { + return PL011_FIFO_DEPTH; + } + 1 + } + + pub fn put_fifo(&mut self, value: c_uint) { + let depth = self.fifo_depth(); + assert!(depth > 0); + let slot = (self.read_pos + self.read_count) & (depth - 1); + self.read_fifo[slot] = value; + self.read_count += 1; + // s->flags &= ~PL011_FLAG_RXFE; + self.flags.set_receive_fifo_empty(false); + if self.read_count == depth { + //s->flags |= PL011_FLAG_RXFF; + self.flags.set_receive_fifo_full(true); + } + + if self.read_count == self.read_trigger { + self.int_level |= registers::INT_RX; + self.update(); + } + } + + pub fn update(&self) { + let flags = self.int_level & self.int_enabled; + for (irq, i) in self.interrupts.iter().zip(IRQMASK) { + // SAFETY: self.interrupts have been initialized in init(). + unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) }; + } + } +} + +/// Which bits in the interrupt status matter for each outbound IRQ line ? +pub const IRQMASK: [u32; 6] = [ + /* combined IRQ */ + Interrupt::E + | Interrupt::MS + | Interrupt::RT as u32 + | Interrupt::TX as u32 + | Interrupt::RX as u32, + Interrupt::RX as u32, + Interrupt::TX as u32, + Interrupt::RT as u32, + Interrupt::MS, + Interrupt::E, +]; + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +#[no_mangle] +pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { + unsafe { + debug_assert!(!opaque.is_null()); + let state = NonNull::new_unchecked(opaque.cast::()); + state.as_ref().can_receive().into() + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +/// +/// The buffer and size arguments must also be valid. +#[no_mangle] +pub unsafe extern "C" fn pl011_receive( + opaque: *mut core::ffi::c_void, + buf: *const u8, + size: core::ffi::c_int, +) { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + if state.as_ref().loopback_enabled() { + return; + } + if size > 0 { + debug_assert!(!buf.is_null()); + state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) + } + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +#[no_mangle] +pub unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + state.as_mut().event(event) + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer for `chr`. +#[no_mangle] +pub unsafe extern "C" fn pl011_create( + addr: u64, + irq: qemu_irq, + chr: *mut Chardev, +) -> *mut DeviceState { + unsafe { + let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name); + let sysbus: *mut SysBusDevice = dev.cast::(); + + qdev_prop_set_chr(dev, bindings::TYPE_CHARDEV.as_ptr(), chr); + sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error); + sysbus_mmio_map(sysbus, 0, addr); + sysbus_connect_irq(sysbus, 0, irq); + dev + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +#[no_mangle] +pub unsafe extern "C" fn pl011_init(obj: *mut Object) { + unsafe { + debug_assert!(!obj.is_null()); + let mut state = NonNull::new_unchecked(obj.cast::()); + state.as_mut().init(); + } +} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs new file mode 100644 index 0000000000000000000000000000000000000000..b7ab31af02d7bb50ae94be0b153baafc7ccfa375 --- /dev/null +++ b/rust/hw/char/pl011/src/device_class.rs @@ -0,0 +1,70 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::ptr::NonNull; + +use qemu_api::{bindings::*, definitions::ObjectImpl}; + +use crate::device::PL011State; + +#[used] +pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { + name: PL011State::TYPE_INFO.name, + unmigratable: true, + ..unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() } +}; + +qemu_api::declare_properties! { + PL011_PROPERTIES, + qemu_api::define_property!( + c"chardev", + PL011State, + char_backend, + unsafe { &qdev_prop_chr }, + CharBackend + ), + qemu_api::define_property!( + c"migrate-clk", + PL011State, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool + ), +} + +qemu_api::device_class_init! { + pl011_class_init, + props => PL011_PROPERTIES, + realize_fn => Some(pl011_realize), + legacy_reset_fn => Some(pl011_reset), + vmsd => VMSTATE_PL011, +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +#[no_mangle] +pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) { + unsafe { + assert!(!dev.is_null()); + let mut state = NonNull::new_unchecked(dev.cast::()); + state.as_mut().realize(); + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +#[no_mangle] +pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) { + unsafe { + assert!(!dev.is_null()); + let mut state = NonNull::new_unchecked(dev.cast::()); + state.as_mut().reset(); + } +} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..2939ee50c99ceaacf6ec68127272d58814e33679 --- /dev/null +++ b/rust/hw/char/pl011/src/lib.rs @@ -0,0 +1,586 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later +// +// PL011 QEMU Device Model +// +// This library implements a device model for the PrimeCell® UART (PL011) +// device in QEMU. +// +#![doc = include_str!("../README.md")] +//! # Library crate +//! +//! See [`PL011State`](crate::device::PL011State) for the device model type and +//! the [`registers`] module for register types. + +#![deny( + rustdoc::broken_intra_doc_links, + rustdoc::redundant_explicit_links, + clippy::correctness, + clippy::suspicious, + clippy::complexity, + clippy::perf, + clippy::cargo, + clippy::nursery, + clippy::style, + // restriction group + clippy::dbg_macro, + clippy::as_underscore, + clippy::assertions_on_result_states, + // pedantic group + clippy::doc_markdown, + clippy::borrow_as_ptr, + clippy::cast_lossless, + clippy::option_if_let_else, + clippy::missing_const_for_fn, + clippy::cognitive_complexity, + clippy::missing_safety_doc, + )] + +extern crate bilge; +extern crate bilge_impl; +extern crate qemu_api; + +pub mod device; +pub mod device_class; +pub mod memory_ops; + +pub const TYPE_PL011: &::core::ffi::CStr = c"pl011"; + +/// Offset of each register from the base memory address of the device. +/// +/// # Source +/// ARM DDI 0183G, Table 3-1 p.3-3 +#[doc(alias = "offset")] +#[allow(non_camel_case_types)] +#[repr(u64)] +#[derive(Debug)] +pub enum RegisterOffset { + /// Data Register + /// + /// A write to this register initiates the actual data transmission + #[doc(alias = "UARTDR")] + DR = 0x000, + /// Receive Status Register or Error Clear Register + #[doc(alias = "UARTRSR")] + #[doc(alias = "UARTECR")] + RSR = 0x004, + /// Flag Register + /// + /// A read of this register shows if transmission is complete + #[doc(alias = "UARTFR")] + FR = 0x018, + /// Fractional Baud Rate Register + /// + /// responsible for baud rate speed + #[doc(alias = "UARTFBRD")] + FBRD = 0x028, + /// `IrDA` Low-Power Counter Register + #[doc(alias = "UARTILPR")] + ILPR = 0x020, + /// Integer Baud Rate Register + /// + /// Responsible for baud rate speed + #[doc(alias = "UARTIBRD")] + IBRD = 0x024, + /// line control register (data frame format) + #[doc(alias = "UARTLCR_H")] + LCR_H = 0x02C, + /// Toggle UART, transmission or reception + #[doc(alias = "UARTCR")] + CR = 0x030, + /// Interrupt FIFO Level Select Register + #[doc(alias = "UARTIFLS")] + FLS = 0x034, + /// Interrupt Mask Set/Clear Register + #[doc(alias = "UARTIMSC")] + IMSC = 0x038, + /// Raw Interrupt Status Register + #[doc(alias = "UARTRIS")] + RIS = 0x03C, + /// Masked Interrupt Status Register + #[doc(alias = "UARTMIS")] + MIS = 0x040, + /// Interrupt Clear Register + #[doc(alias = "UARTICR")] + ICR = 0x044, + /// DMA control Register + #[doc(alias = "UARTDMACR")] + DMACR = 0x048, + ///// Reserved, offsets `0x04C` to `0x07C`. + //Reserved = 0x04C, +} + +impl core::convert::TryFrom for RegisterOffset { + type Error = u64; + + fn try_from(value: u64) -> Result { + macro_rules! case { + ($($discriminant:ident),*$(,)*) => { + /* check that matching on all macro arguments compiles, which means we are not + * missing any enum value; if the type definition ever changes this will stop + * compiling. + */ + const fn _assert_exhaustive(val: RegisterOffset) { + match val { + $(RegisterOffset::$discriminant => (),)* + } + } + + match value { + $(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)* + _ => Err(value), + } + } + } + case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR } + } +} + +pub mod registers { + //! Device registers exposed as typed structs which are backed by arbitrary + //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. + //! + //! All PL011 registers are essentially 32-bit wide, but are typed here as + //! bitmaps with only the necessary width. That is, if a struct bitmap + //! in this module is for example 16 bits long, it should be conceived + //! as a 32-bit register where the unmentioned higher bits are always + //! unused thus treated as zero when read or written. + use bilge::prelude::*; + + // TODO: FIFO Mode has different semantics + /// Data Register, `UARTDR` + /// + /// The `UARTDR` register is the data register. + /// + /// For words to be transmitted: + /// + /// - if the FIFOs are enabled, data written to this location is pushed onto + /// the transmit + /// FIFO + /// - if the FIFOs are not enabled, data is stored in the transmitter + /// holding register (the + /// bottom word of the transmit FIFO). + /// + /// The write operation initiates transmission from the UART. The data is + /// prefixed with a start bit, appended with the appropriate parity bit + /// (if parity is enabled), and a stop bit. The resultant word is then + /// transmitted. + /// + /// For received words: + /// + /// - if the FIFOs are enabled, the data byte and the 4-bit status (break, + /// frame, parity, + /// and overrun) is pushed onto the 12-bit wide receive FIFO + /// - if the FIFOs are not enabled, the data byte and status are stored in + /// the receiving + /// holding register (the bottom word of the receive FIFO). + /// + /// The received data byte is read by performing reads from the `UARTDR` + /// register along with the corresponding status information. The status + /// information can also be read by a read of the `UARTRSR/UARTECR` + /// register. + /// + /// # Note + /// + /// You must disable the UART before any of the control registers are + /// reprogrammed. When the UART is disabled in the middle of + /// transmission or reception, it completes the current character before + /// stopping. + /// + /// # Source + /// ARM DDI 0183G 3.3.1 Data Register, UARTDR + #[bitsize(16)] + #[derive(Clone, Copy, DebugBits, FromBits)] + #[doc(alias = "UARTDR")] + pub struct Data { + _reserved: u4, + pub data: u8, + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + } + + // TODO: FIFO Mode has different semantics + /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` + /// + /// The UARTRSR/UARTECR register is the receive status register/error clear + /// register. Receive status can also be read from the `UARTRSR` + /// register. If the status is read from this register, then the status + /// information for break, framing and parity corresponds to the + /// data character read from the [Data register](Data), `UARTDR` prior to + /// reading the UARTRSR register. The status information for overrun is + /// set immediately when an overrun condition occurs. + /// + /// + /// # Note + /// The received data character must be read first from the [Data + /// Register](Data), `UARTDR` before reading the error status associated + /// with that data character from the `UARTRSR` register. This read + /// sequence cannot be reversed, because the `UARTRSR` register is + /// updated only when a read occurs from the `UARTDR` register. However, + /// the status information can also be obtained by reading the `UARTDR` + /// register + /// + /// # Source + /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, + /// UARTRSR/UARTECR + #[bitsize(8)] + #[derive(Clone, Copy, DebugBits, FromBits)] + pub struct ReceiveStatusErrorClear { + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + _reserved_unpredictable: u4, + } + + impl ReceiveStatusErrorClear { + pub fn reset(&mut self) { + // All the bits are cleared to 0 on reset. + *self = 0.into(); + } + } + + impl Default for ReceiveStatusErrorClear { + fn default() -> Self { + 0.into() + } + } + + #[bitsize(16)] + #[derive(Clone, Copy, DebugBits, FromBits)] + /// Flag Register, `UARTFR` + #[doc(alias = "UARTFR")] + pub struct Flags { + /// CTS Clear to send. This bit is the complement of the UART clear to + /// send, `nUARTCTS`, modem status input. That is, the bit is 1 + /// when `nUARTCTS` is LOW. + pub clear_to_send: bool, + /// DSR Data set ready. This bit is the complement of the UART data set + /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when + /// `nUARTDSR` is LOW. + pub data_set_ready: bool, + /// DCD Data carrier detect. This bit is the complement of the UART data + /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is + /// 1 when `nUARTDCD` is LOW. + pub data_carrier_detect: bool, + /// BUSY UART busy. If this bit is set to 1, the UART is busy + /// transmitting data. This bit remains set until the complete + /// byte, including all the stop bits, has been sent from the + /// shift register. This bit is set as soon as the transmit FIFO + /// becomes non-empty, regardless of whether the UART is enabled + /// or not. + pub busy: bool, + /// RXFE Receive FIFO empty. The meaning of this bit depends on the + /// state of the FEN bit in the UARTLCR_H register. If the FIFO + /// is disabled, this bit is set when the receive holding + /// register is empty. If the FIFO is enabled, the RXFE bit is + /// set when the receive FIFO is empty. + pub receive_fifo_empty: bool, + /// TXFF Transmit FIFO full. The meaning of this bit depends on the + /// state of the FEN bit in the UARTLCR_H register. If the FIFO + /// is disabled, this bit is set when the transmit holding + /// register is full. If the FIFO is enabled, the TXFF bit is + /// set when the transmit FIFO is full. + pub transmit_fifo_full: bool, + /// RXFF Receive FIFO full. The meaning of this bit depends on the state + /// of the FEN bit in the UARTLCR_H register. If the FIFO is + /// disabled, this bit is set when the receive holding register + /// is full. If the FIFO is enabled, the RXFF bit is set when + /// the receive FIFO is full. + pub receive_fifo_full: bool, + /// Transmit FIFO empty. The meaning of this bit depends on the state of + /// the FEN bit in the [Line Control register](LineControl), + /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the + /// transmit holding register is empty. If the FIFO is enabled, + /// the TXFE bit is set when the transmit FIFO is empty. This + /// bit does not indicate if there is data in the transmit shift + /// register. + pub transmit_fifo_empty: bool, + /// `RI`, is `true` when `nUARTRI` is `LOW`. + pub ring_indicator: bool, + _reserved_zero_no_modify: u7, + } + + impl Flags { + pub fn reset(&mut self) { + // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 + self.set_receive_fifo_full(false); + self.set_transmit_fifo_full(false); + self.set_busy(false); + self.set_receive_fifo_empty(true); + self.set_transmit_fifo_empty(true); + } + } + + impl Default for Flags { + fn default() -> Self { + let mut ret: Self = 0.into(); + ret.reset(); + ret + } + } + + #[bitsize(16)] + #[derive(Clone, Copy, DebugBits, FromBits)] + /// Line Control Register, `UARTLCR_H` + #[doc(alias = "UARTLCR_H")] + pub struct LineControl { + /// 15:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u8, + /// 7 SPS Stick parity select. + /// 0 = stick parity is disabled + /// 1 = either: + /// • if the EPS bit is 0 then the parity bit is transmitted and checked + /// as a 1 • if the EPS bit is 1 then the parity bit is + /// transmitted and checked as a 0. This bit has no effect when + /// the PEN bit disables parity checking and generation. See Table 3-11 + /// on page 3-14 for the parity truth table. + pub sticky_parity: bool, + /// WLEN Word length. These bits indicate the number of data bits + /// transmitted or received in a frame as follows: b11 = 8 bits + /// b10 = 7 bits + /// b01 = 6 bits + /// b00 = 5 bits. + pub word_length: WordLength, + /// FEN Enable FIFOs: + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers 1 = transmit and receive FIFO + /// buffers are enabled (FIFO mode). + pub fifos_enabled: Mode, + /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits + /// are transmitted at the end of the frame. The receive + /// logic does not check for two stop bits being received. + pub two_stops_bits: bool, + /// EPS Even parity select. Controls the type of parity the UART uses + /// during transmission and reception: + /// - 0 = odd parity. The UART generates or checks for an odd number of + /// 1s in the data and parity bits. + /// - 1 = even parity. The UART generates or checks for an even number + /// of 1s in the data and parity bits. + /// This bit has no effect when the `PEN` bit disables parity checking + /// and generation. See Table 3-11 on page 3-14 for the parity + /// truth table. + pub parity: Parity, + /// 1 PEN Parity enable: + /// + /// - 0 = parity is disabled and no parity bit added to the data frame + /// - 1 = parity checking and generation is enabled. + /// + /// See Table 3-11 on page 3-14 for the parity truth table. + pub parity_enabled: bool, + /// BRK Send break. + /// + /// If this bit is set to `1`, a low-level is continually output on the + /// `UARTTXD` output, after completing transmission of the + /// current character. For the proper execution of the break command, + /// the software must set this bit for at least two complete + /// frames. For normal use, this bit must be cleared to `0`. + pub send_break: bool, + } + + impl LineControl { + pub fn reset(&mut self) { + // All the bits are cleared to 0 when reset. + *self = 0.into(); + } + } + + impl Default for LineControl { + fn default() -> Self { + 0.into() + } + } + + #[bitsize(1)] + #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] + /// `EPS` "Even parity select", field of [Line Control + /// register](LineControl). + pub enum Parity { + /// - 0 = odd parity. The UART generates or checks for an odd number of + /// 1s in the data and parity bits. + Odd = 0, + /// - 1 = even parity. The UART generates or checks for an even number + /// of 1s in the data and parity bits. + Even = 1, + } + + #[bitsize(1)] + #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] + /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control + /// register](LineControl). + pub enum Mode { + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers + Character = 0, + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FIFO = 1, + } + + impl From for bool { + fn from(val: Mode) -> Self { + matches!(val, Mode::FIFO) + } + } + + #[bitsize(2)] + #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] + /// `WLEN` Word length, field of [Line Control register](LineControl). + /// + /// These bits indicate the number of data bits transmitted or received in a + /// frame as follows: + pub enum WordLength { + /// b11 = 8 bits + _8Bits = 0b11, + /// b10 = 7 bits + _7Bits = 0b10, + /// b01 = 6 bits + _6Bits = 0b01, + /// b00 = 5 bits. + _5Bits = 0b00, + } + + /// Control Register, `UARTCR` + /// + /// The `UARTCR` register is the control register. All the bits are cleared + /// to `0` on reset except for bits `9` and `8` that are set to `1`. + /// + /// # Source + /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 + #[bitsize(16)] + #[doc(alias = "UARTCR")] + #[derive(Clone, Copy, DebugBits, FromBits)] + pub struct Control { + /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled + /// in the middle of transmission or reception, it completes the current + /// character before stopping. 1 = the UART is enabled. Data + /// transmission and reception occurs for either UART signals or SIR + /// signals depending on the setting of the SIREN bit. + pub enable_uart: bool, + /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT` + /// remains LOW (no light pulse generated), and signal transitions on + /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is + /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, + /// in the marking state. Signal transitions on UARTRXD or modem status + /// inputs have no effect. This bit has no effect if the UARTEN bit + /// disables the UART. + pub enable_sir: bool, + /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding + /// mode. If this bit is cleared to 0, low-level bits are transmitted as + /// an active high pulse with a width of 3/ 16th of the bit period. If + /// this bit is set to 1, low-level bits are transmitted with a pulse + /// width that is 3 times the period of the IrLPBaud16 input signal, + /// regardless of the selected bit rate. Setting this bit uses less + /// power, but might reduce transmission distances. + pub sir_lowpower_irda_mode: u1, + /// Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u4, + /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is + /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR + /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed + /// through to the SIRIN path. The SIRTEST bit in the test register must + /// be set to 1 to override the normal half-duplex SIR operation. This + /// must be the requirement for accessing the test registers during + /// normal operation, and SIRTEST must be cleared to 0 when loopback + /// testing is finished. This feature reduces the amount of external + /// coupling required during system test. If this bit is set to 1, and + /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the + /// UARTRXD path. In either SIR mode or UART mode, when this bit is set, + /// the modem outputs are also fed through to the modem inputs. This bit + /// is cleared to 0 on reset, to disable loopback. + pub enable_loopback: bool, + /// `TXE` Transmit enable. If this bit is set to 1, the transmit section + /// of the UART is enabled. Data transmission occurs for either UART + /// signals, or SIR signals depending on the setting of the SIREN bit. + /// When the UART is disabled in the middle of transmission, it + /// completes the current character before stopping. + pub enable_transmit: bool, + /// `RXE` Receive enable. If this bit is set to 1, the receive section + /// of the UART is enabled. Data reception occurs for either UART + /// signals or SIR signals depending on the setting of the SIREN bit. + /// When the UART is disabled in the middle of reception, it completes + /// the current character before stopping. + pub enable_receive: bool, + /// `DTR` Data transmit ready. This bit is the complement of the UART + /// data transmit ready, `nUARTDTR`, modem status output. That is, when + /// the bit is programmed to a 1 then `nUARTDTR` is LOW. + pub data_transmit_ready: bool, + /// `RTS` Request to send. This bit is the complement of the UART + /// request to send, `nUARTRTS`, modem status output. That is, when the + /// bit is programmed to a 1 then `nUARTRTS` is LOW. + pub request_to_send: bool, + /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`) + /// modem status output. That is, when the bit is programmed to a 1 the + /// output is 0. For DTE this can be used as Data Carrier Detect (DCD). + pub out_1: bool, + /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`) + /// modem status output. That is, when the bit is programmed to a 1, the + /// output is 0. For DTE this can be used as Ring Indicator (RI). + pub out_2: bool, + /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1, + /// RTS hardware flow control is enabled. Data is only requested when + /// there is space in the receive FIFO for it to be received. + pub rts_hardware_flow_control_enable: bool, + /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1, + /// CTS hardware flow control is enabled. Data is only transmitted when + /// the `nUARTCTS` signal is asserted. + pub cts_hardware_flow_control_enable: bool, + } + + impl Control { + pub fn reset(&mut self) { + *self = 0.into(); + self.set_enable_receive(true); + self.set_enable_transmit(true); + } + } + + impl Default for Control { + fn default() -> Self { + let mut ret: Self = 0.into(); + ret.reset(); + ret + } + } + + /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC + pub const INT_OE: u32 = 1 << 10; + pub const INT_BE: u32 = 1 << 9; + pub const INT_PE: u32 = 1 << 8; + pub const INT_FE: u32 = 1 << 7; + pub const INT_RT: u32 = 1 << 6; + pub const INT_TX: u32 = 1 << 5; + pub const INT_RX: u32 = 1 << 4; + pub const INT_DSR: u32 = 1 << 3; + pub const INT_DCD: u32 = 1 << 2; + pub const INT_CTS: u32 = 1 << 1; + pub const INT_RI: u32 = 1 << 0; + pub const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; + pub const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + + #[repr(u32)] + pub enum Interrupt { + OE = 1 << 10, + BE = 1 << 9, + PE = 1 << 8, + FE = 1 << 7, + RT = 1 << 6, + TX = 1 << 5, + RX = 1 << 4, + DSR = 1 << 3, + DCD = 1 << 2, + CTS = 1 << 1, + RI = 1 << 0, + } + + impl Interrupt { + pub const E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; + pub const MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + } +} + +// TODO: You must disable the UART before any of the control registers are +// reprogrammed. When the UART is disabled in the middle of transmission or +// reception, it completes the current character before stopping diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs new file mode 100644 index 0000000000000000000000000000000000000000..8d066ebf6d016fa30db004933751a854d7e59117 --- /dev/null +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -0,0 +1,59 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::{mem::MaybeUninit, ptr::NonNull}; + +use qemu_api::bindings::*; + +use crate::device::PL011State; + +pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { + read: Some(pl011_read), + write: Some(pl011_write), + read_with_attrs: None, + write_with_attrs: None, + endianness: device_endian::DEVICE_NATIVE_ENDIAN, + valid: unsafe { MaybeUninit::::zeroed().assume_init() }, + impl_: MemoryRegionOps__bindgen_ty_2 { + min_access_size: 4, + max_access_size: 4, + ..unsafe { MaybeUninit::::zeroed().assume_init() } + }, +}; + +#[no_mangle] +unsafe extern "C" fn pl011_read( + opaque: *mut core::ffi::c_void, + addr: hwaddr, + size: core::ffi::c_uint, +) -> u64 { + assert!(!opaque.is_null()); + let mut state = unsafe { NonNull::new_unchecked(opaque.cast::()) }; + let val = unsafe { state.as_mut().read(addr, size) }; + match val { + std::ops::ControlFlow::Break(val) => val, + std::ops::ControlFlow::Continue(val) => { + // SAFETY: self.char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; + unsafe { qemu_chr_fe_accept_input(cb_ptr) }; + + val + } + } +} + +#[no_mangle] +unsafe extern "C" fn pl011_write( + opaque: *mut core::ffi::c_void, + addr: hwaddr, + data: u64, + _size: core::ffi::c_uint, +) { + unsafe { + assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + state.as_mut().write(addr, data) + } +} diff --git a/rust/hw/meson.build b/rust/hw/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..860196645e719624d2e2e6bc301b62b81ab2e19b --- /dev/null +++ b/rust/hw/meson.build @@ -0,0 +1 @@ +subdir('char') diff --git a/rust/meson.build b/rust/meson.build index 7a32b1b195083571931ad589965c10ddaf6383b1..def77389cddc52f5d4503840e9bdfb1207586fa2 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -1,2 +1,4 @@ subdir('qemu-api-macros') subdir('qemu-api') + +subdir('hw') diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 62a2cf45d28ed5565076443b9f931a647d395542..30677c3ec9032ea01090f74602d839d1c571d012 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -27,7 +27,9 @@ sub_file="${sub_tdir}/submodule.tar" # in their checkout, because the build environment is completely # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 - berkeley-testfloat-3 proc-macro2-1-rs quote-1-rs + berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" sub_deinit="" diff --git a/scripts/make-release b/scripts/make-release index cf7d694ef73ba1c6c5afad5d211a42f6a6fe1577..8dc939124c4fd4abf3509c3b64c0588bc8810962 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -18,7 +18,9 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 - berkeley-testfloat-3 proc-macro2-1-rs quote-1-rs + berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" src="$1" diff --git a/scripts/rust/rust_root_crate.sh b/scripts/rust/rust_root_crate.sh new file mode 100755 index 0000000000000000000000000000000000000000..975bddf7f1a4c6ca7770f800bdc894cdff1f3ab1 --- /dev/null +++ b/scripts/rust/rust_root_crate.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -eu + +cat < X-Patchwork-Id: 13849129 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 49DC0CE8E7B for ; Thu, 24 Oct 2024 14:04:18 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRW-0007Bd-P9; Thu, 24 Oct 2024 10:04:02 -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 1t3yR9-00078l-PV for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:50 -0400 Received: from mail-ej1-x62a.google.com ([2a00:1450:4864:20::62a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yR5-0003vD-78 for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:39 -0400 Received: by mail-ej1-x62a.google.com with SMTP id a640c23a62f3a-a99f1fd20c4so127480266b.0 for ; Thu, 24 Oct 2024 07:03:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778614; x=1730383414; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=swZs0PvDIygR17bMoZNG/KFXxGRO0tSgGnszjaO+Hkw=; b=wsE/65xdxQlLgyEig+PnPcXICtPxqB+Xg+WG3xJ4cJg/uAd7rteKTq8u5qz5/nWob6 bKT8ehX4kTV5UjeNUlO90Q8TNIS6u3cKpZyU6DvKpE8Ignzgya154zZmLNmtHVTDs5Tw eJnbHISO6nziIGnWVJjBX5tojxn6Yd54l9YR+neQxjWtx6Oi7w5M40xoeEJIuOAFcJjB g+hVYd57whWFirYJwMxwt6sCsj9P/SBmmVLW70Kb+3H6qmWw0e73ObYSs803JRJzlwsi 8CZ46qdDn80k5bd75gw0Vjd2xEY2fGBBOtjI2589FXZPP7RBYmWoC2o+4wYqYDU6+6+y SkAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778614; x=1730383414; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=swZs0PvDIygR17bMoZNG/KFXxGRO0tSgGnszjaO+Hkw=; b=XXgdI3KdB1QwQYBzK6Yo44BEqoQkBcv45kjZG6xmfvJmNdpOqxIe7blpbgSDjriAeq 5NVcEQTbKS+IYngd1y1IOMzps/BCLeoeos9LZBbExi36ysO6A8jvlhNnXqrnGaHqKr+p cJfo2cfIS/GSj5GARyfXrR/vfT7Zw3wzWNSYkWZXvKFgtuQkNdXaSVBfDudO/Eh7rnm+ JSOe5dedtMtlaZIlTCJoNFttmxASDda/VYjfmno1sweakMtbI2HWyA64+z0J4F0ZSynZ vLO4jHECHWQLecc9h3rm96+55/pDsr4aRFHTKNQ8EuHUWMHBnDmSBA7iMgnTKsnQilJz ViPQ== X-Gm-Message-State: AOJu0YzcNfT3kTAyZly6/N1a2VqpZT3t6HT0chwoYu8iC8UzIdUcK0Zt P3XUglXR4LvIdBRrunb7kv57IN5y4a0IK1LkaRs8uVktW/imXTPXLuMGybMxH50= X-Google-Smtp-Source: AGHT+IHOL8SJMdgpdOlMXhi240mYeHWvmTK+XMYm/R+T9TX6gQCO7ENeJrnX7hkWY7JmO3ETRIFWsg== X-Received: by 2002:a17:906:c115:b0:a9a:e6:a031 with SMTP id a640c23a62f3a-a9abf964e1amr597810866b.59.1729778610634; Thu, 24 Oct 2024 07:03:30 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:30 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:01 +0300 Subject: [PATCH 03/11] rust/qemu-api-macros: introduce Device proc macro MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-3-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=61915; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=I158Py0RnGmdnzcCp2wTlUb35MV778QZs4qZ5m1FnJk=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9pbXdQdlM1RS94RlM5b1Z4ZzVmTzVaN210CjF2VUxtYkFkQzBZT2dIUldx Uk9KQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG9nQUt DUkIzS2Nkd2YzNEowTGg0RC80bXN4SDdscm12dXV6Tk5BNGppc2NTcTM2NWhCdGU5djN6QWVJLw o3WFpYdGw4bm1nWGVKalcraW5wN1dxdUI5MHgwYnlJNEJ4dWk4WmdLL1hCSVFJMHFBMmsyMjJOW Fhwb3ArQnlwCitIVEdwRnVvU25wcEFzMUx1MW9WVkZZS1VSSlRuMVRqTHlHb1Jub0VTeE9kZklL ZDFaNEFMWmVERnJFdW9JajkKOEFlT0IwWUE5U1lNOXV6RGNjLys2aGRqU3hnZ0lGNU94YWxxcjk xdGhUM0lUdGFmeU83WjBESDkwZzd3RU9FegpkQ0s0NXZZRm9XVkhDNkNLYURIVFBQUWRPOU5vMU FHMWpHVjMxMi9ldEttNWxzbElzUzdoNFQ1NlZhZ003N25WCjY3Kzk5eWU5R01PdVphbDB3SWw2M y9FL3QyK3lOeDRPS1Vpa1VXYWl4dEl5ekhQTW1LVGNtTjZMYWJtaW1ETzkKNjgxd2FXRFRrcVc4 R21hbXBBU2NySWlRbTRVZmlWUkloVHdTTVJYN1dVY1RsYUkyYm1tRDZrL3NXNHYwNS9xVgpKRTB CaUwwSmw0OTFPUUZVQmVSRlBJNlVqRC9OT29GK1ppcEFCUGNZNEhWWUNoY2J3bHJMMkRVaUw4aD Mxbkt3CjhuV3dabDZhV2l2UTRndkU1amFSL0l1alBiTW1Ob1BLWWptUDdQMzhWSlYvNTdwNVVZT WZOeVczenR0RXMveVIKUGhQWTNkdGc0VzhDYWZMMVBONFcvTEU3ajhsdEp5VDdPZ3NmMEkwRU9M dGlVeVNGWTQ0WnNrd093ZFhsZ3kvZQpXRVVOcTMwMlVCaUNCWmZPa3JUOFl1azgyRnkrK2tLMUN wYXV6elBGMnVYSElPMms0VEdCajZmRjBNU1E2WGJzClQ4b1Rsdz09Cj1LejI0Ci0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::62a; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ej1-x62a.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Add a new derive procedural macro to declare device models. Add corresponding DeviceImpl trait after already existing ObjectImpl trait. At the same time, add instance_init, instance_post_init, instance_finalize methods to the ObjectImpl trait and call them from the ObjectImplUnsafe trait, which is generated by the procedural macro. This allows all the boilerplate device model registration to be handled by macros, and all pertinent details to be declared through proc macro attributes or trait associated constants and methods. The device class can now be generated automatically and the name can be optionally overridden: ------------------------ >8 ------------------------ #[repr(C)] #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::Device)] #[device(class_name_override = PL011Class)] /// PL011 Device Model in QEMU pub struct PL011State { ------------------------ >8 ------------------------ Properties are now marked as field attributes: ------------------------ >8 ------------------------ #[property(name = c"chardev", qdev_prop = qdev_prop_chr)] pub char_backend: CharBackend, ------------------------ >8 ------------------------ Object methods (instance_init, etc) methods are now trait methods: ------------------------ >8 ------------------------ /// Trait a type must implement to be registered with QEMU. pub trait ObjectImpl { type Class: ClassImpl; const TYPE_NAME: &'static CStr; const PARENT_TYPE_NAME: Option<&'static CStr>; const ABSTRACT: bool; unsafe fn instance_init(&mut self) {} fn instance_post_init(&mut self) {} fn instance_finalize(&mut self) {} } ------------------------ >8 ------------------------ Device methods (realize/reset etc) are now safe idiomatic trait methods: ------------------------ >8 ------------------------ /// Implementation methods for device types. pub trait DeviceImpl: ObjectImpl { fn realize(&mut self) {} fn reset(&mut self) {} } ------------------------ >8 ------------------------ The derive Device macro is responsible for creating all the extern "C" FFI functions that QEMU needs to call these methods. Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 124 +++----- rust/hw/char/pl011/src/device_class.rs | 70 ----- rust/hw/char/pl011/src/lib.rs | 1 - rust/qemu-api-macros/src/device.rs | 433 ++++++++++++++++++++++++++ rust/qemu-api-macros/src/lib.rs | 45 +-- rust/qemu-api-macros/src/object.rs | 107 +++++++ rust/qemu-api-macros/src/symbols.rs | 55 ++++ rust/qemu-api-macros/src/utilities.rs | 152 +++++++++ rust/qemu-api/meson.build | 3 +- rust/qemu-api/src/definitions.rs | 97 ------ rust/qemu-api/src/device_class.rs | 128 -------- rust/qemu-api/src/lib.rs | 6 +- rust/qemu-api/src/objects.rs | 90 ++++++ rust/qemu-api/src/tests.rs | 49 --- subprojects/packagefiles/syn-2-rs/meson.build | 1 + 15 files changed, 902 insertions(+), 459 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c7193b41beec0b177dbc75ac0e43fcfea4c82bfb..c469877b1ca70dd1a02e3a2449c65ad3e57c93ae 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,7 +9,7 @@ use qemu_api::{ bindings::{self, *}, - definitions::ObjectImpl, + objects::*, }; use crate::{ @@ -26,7 +26,8 @@ pub const PL011_FIFO_DEPTH: usize = 16_usize; #[repr(C)] -#[derive(Debug, qemu_api_macros::Object)] +#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::Device)] +#[device(class_name_override = PL011Class)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: SysBusDevice, @@ -51,6 +52,7 @@ pub struct PL011State { pub read_count: usize, pub read_trigger: usize, #[doc(alias = "chr")] + #[property(name = c"chardev", qdev_prop = qdev_prop_chr)] pub char_backend: CharBackend, /// QEMU interrupts /// @@ -68,38 +70,17 @@ pub struct PL011State { #[doc(alias = "clk")] pub clock: NonNull, #[doc(alias = "migrate_clk")] + #[property(name = c"migrate-clk", qdev_prop = qdev_prop_bool)] pub migrate_clock: bool, } impl ObjectImpl for PL011State { type Class = PL011Class; - const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; + const TYPE_NAME: &'static CStr = crate::TYPE_PL011; const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); const ABSTRACT: bool = false; - const INSTANCE_INIT: Option = Some(pl011_init); - const INSTANCE_POST_INIT: Option = None; - const INSTANCE_FINALIZE: Option = None; -} -#[repr(C)] -pub struct PL011Class { - _inner: [u8; 0], -} - -impl qemu_api::definitions::Class for PL011Class { - const CLASS_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), - > = Some(crate::device_class::pl011_class_init); - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), - > = None; -} - -#[used] -pub static CLK_NAME: &CStr = c"clk"; - -impl PL011State { /// Initializes a pre-allocated, unitialized instance of `PL011State`. /// /// # Safety @@ -108,7 +89,7 @@ impl PL011State { /// `PL011State` type. It must not be called more than once on the same /// location/instance. All its fields are expected to hold unitialized /// values with the sole exception of `parent_obj`. - pub unsafe fn init(&mut self) { + unsafe fn instance_init(&mut self) { let dev = addr_of_mut!(*self).cast::(); // SAFETY: // @@ -120,7 +101,7 @@ pub unsafe fn init(&mut self) { addr_of_mut!(*self).cast::(), &PL011_OPS, addr_of_mut!(*self).cast::(), - Self::TYPE_INFO.name, + Self::TYPE_NAME.as_ptr(), 0x1000, ); let sbd = addr_of_mut!(*self).cast::(); @@ -147,7 +128,49 @@ pub unsafe fn init(&mut self) { .unwrap(); } } +} +impl DeviceImpl for PL011State { + fn realize(&mut self) { + // SAFETY: self.char_backend has the correct size and alignment for a + // CharBackend object, and its callbacks are of the correct types. + unsafe { + qemu_chr_fe_set_handlers( + addr_of_mut!(self.char_backend), + Some(pl011_can_receive), + Some(pl011_receive), + Some(pl011_event), + None, + addr_of_mut!(*self).cast::(), + core::ptr::null_mut(), + true, + ); + } + } + + fn reset(&mut self) { + self.line_control.reset(); + self.receive_status_error_clear.reset(); + self.dmacr = 0; + self.int_enabled = 0; + self.int_level = 0; + self.ilpr = 0; + self.ibrd = 0; + self.fbrd = 0; + self.read_trigger = 1; + self.ifl = 0x12; + self.control.reset(); + self.flags = 0.into(); + self.reset_fifo(); + } +} + +impl qemu_api::objects::Migrateable for PL011State {} + +#[used] +pub static CLK_NAME: &CStr = c"clk"; + +impl PL011State { pub fn read( &mut self, offset: hwaddr, @@ -394,39 +417,6 @@ fn set_read_trigger(&mut self) { self.read_trigger = 1; } - pub fn realize(&mut self) { - // SAFETY: self.char_backend has the correct size and alignment for a - // CharBackend object, and its callbacks are of the correct types. - unsafe { - qemu_chr_fe_set_handlers( - addr_of_mut!(self.char_backend), - Some(pl011_can_receive), - Some(pl011_receive), - Some(pl011_event), - None, - addr_of_mut!(*self).cast::(), - core::ptr::null_mut(), - true, - ); - } - } - - pub fn reset(&mut self) { - self.line_control.reset(); - self.receive_status_error_clear.reset(); - self.dmacr = 0; - self.int_enabled = 0; - self.int_level = 0; - self.ilpr = 0; - self.ibrd = 0; - self.fbrd = 0; - self.read_trigger = 1; - self.ifl = 0x12; - self.control.reset(); - self.flags = 0.into(); - self.reset_fifo(); - } - pub fn reset_fifo(&mut self) { self.read_count = 0; self.read_pos = 0; @@ -583,17 +573,3 @@ pub fn update(&self) { dev } } - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_init(obj: *mut Object) { - unsafe { - debug_assert!(!obj.is_null()); - let mut state = NonNull::new_unchecked(obj.cast::()); - state.as_mut().init(); - } -} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs deleted file mode 100644 index b7ab31af02d7bb50ae94be0b153baafc7ccfa375..0000000000000000000000000000000000000000 --- a/rust/hw/char/pl011/src/device_class.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use core::ptr::NonNull; - -use qemu_api::{bindings::*, definitions::ObjectImpl}; - -use crate::device::PL011State; - -#[used] -pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { - name: PL011State::TYPE_INFO.name, - unmigratable: true, - ..unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() } -}; - -qemu_api::declare_properties! { - PL011_PROPERTIES, - qemu_api::define_property!( - c"chardev", - PL011State, - char_backend, - unsafe { &qdev_prop_chr }, - CharBackend - ), - qemu_api::define_property!( - c"migrate-clk", - PL011State, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool - ), -} - -qemu_api::device_class_init! { - pl011_class_init, - props => PL011_PROPERTIES, - realize_fn => Some(pl011_realize), - legacy_reset_fn => Some(pl011_reset), - vmsd => VMSTATE_PL011, -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) { - unsafe { - assert!(!dev.is_null()); - let mut state = NonNull::new_unchecked(dev.cast::()); - state.as_mut().realize(); - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) { - unsafe { - assert!(!dev.is_null()); - let mut state = NonNull::new_unchecked(dev.cast::()); - state.as_mut().reset(); - } -} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 2939ee50c99ceaacf6ec68127272d58814e33679..f4d9cce4b01f605cfcbec7ea87c8b2009d77ee52 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -42,7 +42,6 @@ extern crate qemu_api; pub mod device; -pub mod device_class; pub mod memory_ops; pub const TYPE_PL011: &::core::ffi::CStr = c"pl011"; diff --git a/rust/qemu-api-macros/src/device.rs b/rust/qemu-api-macros/src/device.rs new file mode 100644 index 0000000000000000000000000000000000000000..3b965576a065601cd5c97d5ab6a2501f96d16a61 --- /dev/null +++ b/rust/qemu-api-macros/src/device.rs @@ -0,0 +1,433 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{ + parse::{Parse, ParseStream}, + Result, +}; +use syn::{parse_macro_input, DeriveInput}; + +use crate::{symbols::*, utilities::*}; + +#[derive(Debug, Default)] +struct DeriveContainer { + category: Option, + class_name: Option, + class_name_override: Option, +} + +impl Parse for DeriveContainer { + fn parse(input: ParseStream) -> Result { + let _: syn::Token![#] = input.parse()?; + let bracketed; + _ = syn::bracketed!(bracketed in input); + assert_eq!(DEVICE, bracketed.parse::()?); + let mut retval = Self { + category: None, + class_name: None, + class_name_override: None, + }; + let content; + _ = syn::parenthesized!(content in bracketed); + while !content.is_empty() { + let value: syn::Ident = content.parse()?; + if value == CLASS_NAME { + let _: syn::Token![=] = content.parse()?; + if retval.class_name.is_some() { + panic!("{} can only be used at most once", CLASS_NAME); + } + retval.class_name = Some(content.parse()?); + } else if value == CLASS_NAME_OVERRIDE { + let _: syn::Token![=] = content.parse()?; + if retval.class_name_override.is_some() { + panic!("{} can only be used at most once", CLASS_NAME_OVERRIDE); + } + retval.class_name_override = Some(content.parse()?); + } else if value == CATEGORY { + let _: syn::Token![=] = content.parse()?; + if retval.category.is_some() { + panic!("{} can only be used at most once", CATEGORY); + } + let lit: syn::LitStr = content.parse()?; + let path: syn::Path = lit.parse()?; + retval.category = Some(path); + } else { + panic!("unrecognized token `{}`", value); + } + + if !content.is_empty() { + let _: syn::Token![,] = content.parse()?; + } + } + if retval.class_name.is_some() && retval.class_name_override.is_some() { + panic!( + "Cannot define `{}` and `{}` at the same time", + CLASS_NAME, CLASS_NAME_OVERRIDE + ); + } + Ok(retval) + } +} + +#[derive(Debug)] +struct QdevProperty { + name: Option, + qdev_prop: Option, +} + +impl Parse for QdevProperty { + fn parse(input: ParseStream) -> Result { + let _: syn::Token![#] = input.parse()?; + let bracketed; + _ = syn::bracketed!(bracketed in input); + assert_eq!(PROPERTY, bracketed.parse::()?); + let mut retval = Self { + name: None, + qdev_prop: None, + }; + let content; + _ = syn::parenthesized!(content in bracketed); + while !content.is_empty() { + let value: syn::Ident = content.parse()?; + if value == NAME { + let _: syn::Token![=] = content.parse()?; + if retval.name.is_some() { + panic!("{} can only be used at most once", NAME); + } + retval.name = Some(content.parse()?); + } else if value == QDEV_PROP { + let _: syn::Token![=] = content.parse()?; + if retval.qdev_prop.is_some() { + panic!("{} can only be used at most once", QDEV_PROP); + } + retval.qdev_prop = Some(content.parse()?); + } else { + panic!("unrecognized token `{}`", value); + } + + if !content.is_empty() { + let _: syn::Token![,] = content.parse()?; + } + } + Ok(retval) + } +} + +pub fn derive_device(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + assert_is_repr_c_struct(&input, "Device"); + + let derive_container: DeriveContainer = input + .attrs + .iter() + .find(|a| a.path() == DEVICE) + .map(|a| syn::parse(a.to_token_stream().into()).expect("could not parse device attr")) + .unwrap_or_default(); + let (qdev_properties_static, qdev_properties_expanded) = make_qdev_properties(&input); + let class_expanded = gen_device_class(derive_container, qdev_properties_static, &input.ident); + let name = input.ident; + + let realize_fn = format_ident!("__{}_realize_generated", name); + let reset_fn = format_ident!("__{}_reset_generated", name); + + let expanded = quote! { + unsafe impl ::qemu_api::objects::DeviceImplUnsafe for #name { + const REALIZE: ::core::option::Option< + unsafe extern "C" fn( + dev: *mut ::qemu_api::bindings::DeviceState, + errp: *mut *mut ::qemu_api::bindings::Error, + ), + > = Some(#realize_fn); + const RESET: ::core::option::Option< + unsafe extern "C" fn(dev: *mut ::qemu_api::bindings::DeviceState), + > = Some(#reset_fn); + } + + #[no_mangle] + pub unsafe extern "C" fn #realize_fn( + dev: *mut ::qemu_api::bindings::DeviceState, + errp: *mut *mut ::qemu_api::bindings::Error, + ) { + let mut instance = NonNull::new(dev.cast::<#name>()).expect(concat!("Expected dev to be a non-null pointer of type ", stringify!(#name))); + unsafe { + ::qemu_api::objects::DeviceImpl::realize(instance.as_mut()); + } + } + + #[no_mangle] + pub unsafe extern "C" fn #reset_fn( + dev: *mut ::qemu_api::bindings::DeviceState, + ) { + let mut instance = NonNull::new(dev.cast::<#name>()).expect(concat!("Expected dev to be a non-null pointer of type ", stringify!(#name))); + unsafe { + ::qemu_api::objects::DeviceImpl::reset(instance.as_mut()); + } + } + + #qdev_properties_expanded + #class_expanded + }; + + TokenStream::from(expanded) +} + +fn make_qdev_properties(input: &DeriveInput) -> (syn::Ident, proc_macro2::TokenStream) { + let name = &input.ident; + + let qdev_properties: Vec<(syn::Field, QdevProperty)> = match &input.data { + syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(fields), + .. + }) => fields + .named + .iter() + .map(|f| { + f.attrs + .iter() + .filter(|a| a.path() == PROPERTY) + .map(|a| (f.clone(), a.clone())) + }) + .flatten() + .map(|(f, a)| { + ( + f.clone(), + syn::parse(a.to_token_stream().into()).expect("could not parse property attr"), + ) + }) + .collect::>(), + _other => unreachable!(), + }; + + let mut properties_expanded = quote! { + unsafe { ::core::mem::MaybeUninit::<::qemu_api::bindings::Property>::zeroed().assume_init() } + }; + let prop_len = qdev_properties.len() + 1; + for (field, prop) in qdev_properties { + let prop_name = prop.name.as_ref().unwrap(); + let field_name = field.ident.as_ref().unwrap(); + let qdev_prop = prop.qdev_prop.as_ref().unwrap(); + let prop = quote! { + ::qemu_api::bindings::Property { + name: ::core::ffi::CStr::as_ptr(#prop_name), + info: unsafe { &#qdev_prop }, + offset: ::core::mem::offset_of!(#name, #field_name) as _, + bitnr: 0, + bitmask: 0, + set_default: false, + defval: ::qemu_api::bindings::Property__bindgen_ty_1 { i: 0 }, + arrayoffset: 0, + arrayinfo: ::core::ptr::null(), + arrayfieldsize: 0, + link_type: ::core::ptr::null(), + } + }; + properties_expanded = quote! { + #prop, + #properties_expanded + }; + } + let properties_ident = format_ident!("__{}_QDEV_PROPERTIES", name); + let expanded = quote! { + #[no_mangle] + pub static mut #properties_ident: [::qemu_api::bindings::Property; #prop_len] = [#properties_expanded]; + }; + (properties_ident, expanded) +} + +fn gen_device_class( + derive_container: DeriveContainer, + qdev_properties_static: syn::Ident, + name: &syn::Ident, +) -> proc_macro2::TokenStream { + let (class_name, class_def) = match ( + derive_container.class_name_override, + derive_container.class_name, + ) { + (Some(class_name), _) => { + let class_expanded = quote! { + #[repr(C)] + pub struct #class_name { + _inner: [u8; 0], + } + }; + (class_name, class_expanded) + } + (None, Some(class_name)) => (class_name, quote! {}), + (None, None) => { + let class_name = format_ident!("{}Class", name); + let class_expanded = quote! { + #[repr(C)] + pub struct #class_name { + _inner: [u8; 0], + } + }; + (class_name, class_expanded) + } + }; + let class_init_fn = format_ident!("__{}_class_init_generated", class_name); + let class_base_init_fn = format_ident!("__{}_class_base_init_generated", class_name); + + let (vmsd, vmsd_impl) = { + let (i, vmsd) = make_vmstate(name); + (quote! { &#i }, vmsd) + }; + let category = if let Some(category) = derive_container.category { + quote! { + const BITS_PER_LONG: u32 = ::core::ffi::c_ulong::BITS; + let _: ::qemu_api::bindings::DeviceCategory = #category; + let nr: ::core::ffi::c_ulong = #category as _; + let mask = 1 << (nr as u32 % BITS_PER_LONG); + let p = ::core::ptr::addr_of_mut!(dc.as_mut().categories).offset((nr as u32 / BITS_PER_LONG) as isize); + let p: *mut ::core::ffi::c_ulong = p.cast(); + let categories = p.read_unaligned(); + p.write_unaligned(categories | mask); + } + } else { + quote! {} + }; + let props = quote! { + ::qemu_api::bindings::device_class_set_props(dc.as_mut(), #qdev_properties_static.as_mut_ptr()); + }; + + quote! { + #class_def + + impl ::qemu_api::objects::ClassImpl for #class_name { + type Object = #name; + } + + unsafe impl ::qemu_api::objects::ClassImplUnsafe for #class_name { + const CLASS_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), + > = Some(#class_init_fn); + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void), + > = Some(#class_base_init_fn); + } + + #[no_mangle] + pub unsafe extern "C" fn #class_init_fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void) { + { + { + let mut dc = + ::core::ptr::NonNull::new(klass.cast::<::qemu_api::bindings::DeviceClass>()).unwrap(); + unsafe { + dc.as_mut().realize = + <#name as ::qemu_api::objects::DeviceImplUnsafe>::REALIZE; + ::qemu_api::bindings::device_class_set_legacy_reset( + dc.as_mut(), + <#name as ::qemu_api::objects::DeviceImplUnsafe>::RESET + ); + dc.as_mut().vmsd = #vmsd; + #props + #category + } + } + let mut klass = NonNull::new(klass.cast::<#class_name>()).expect(concat!("Expected klass to be a non-null pointer of type ", stringify!(#class_name))); + unsafe { + ::qemu_api::objects::ClassImpl::class_init(klass.as_mut(), data); + } + } + } + #[no_mangle] + pub unsafe extern "C" fn #class_base_init_fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void) { + { + let mut klass = NonNull::new(klass.cast::<#class_name>()).expect(concat!("Expected klass to be a non-null pointer of type ", stringify!(#class_name))); + unsafe { + ::qemu_api::objects::ClassImpl::class_base_init(klass.as_mut(), data); + } + } + } + + #vmsd_impl + } +} + +fn make_vmstate(name: &syn::Ident) -> (syn::Ident, proc_macro2::TokenStream) { + let vmstate_description_ident = format_ident!("__VMSTATE_{}", name); + + let pre_load = format_ident!("__{}_pre_load_generated", name); + let post_load = format_ident!("__{}_post_load_generated", name); + let pre_save = format_ident!("__{}_pre_save_generated", name); + let post_save = format_ident!("__{}_post_save_generated", name); + let needed = format_ident!("__{}_needed_generated", name); + let dev_unplug_pending = format_ident!("__{}_dev_unplug_pending_generated", name); + + let migrateable_fish = quote! {<#name as ::qemu_api::objects::Migrateable>}; + let vmstate_description = quote! { + #[used] + #[allow(non_upper_case_globals)] + pub static #vmstate_description_ident: ::qemu_api::bindings::VMStateDescription = ::qemu_api::bindings::VMStateDescription { + name: if let Some(name) = #migrateable_fish::NAME { + name.as_ptr() + } else { + <#name as ::qemu_api::objects::ObjectImplUnsafe>::TYPE_INFO.name + }, + unmigratable: #migrateable_fish::UNMIGRATABLE, + early_setup: #migrateable_fish::EARLY_SETUP, + version_id: #migrateable_fish::VERSION_ID, + minimum_version_id: #migrateable_fish::MINIMUM_VERSION_ID, + priority: #migrateable_fish::PRIORITY, + pre_load: Some(#pre_load), + post_load: Some(#post_load), + pre_save: Some(#pre_save), + post_save: Some(#post_save), + needed: Some(#needed), + dev_unplug_pending: Some(#dev_unplug_pending), + fields: ::core::ptr::null(), + subsections: ::core::ptr::null(), + }; + + #[no_mangle] + pub unsafe extern "C" fn #pre_load(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::pre_load(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #post_load(opaque: *mut ::core::ffi::c_void, version_id: core::ffi::c_int) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::post_load(instance.as_mut(), version_id) + } + } + #[no_mangle] + pub unsafe extern "C" fn #pre_save(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::pre_save(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #post_save(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::post_save(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #needed(opaque: *mut ::core::ffi::c_void) -> bool { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::needed(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #dev_unplug_pending(opaque: *mut ::core::ffi::c_void) -> bool { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::dev_unplug_pending(instance.as_mut()) + } + } + }; + + let expanded = quote! { + #vmstate_description + }; + (vmstate_description_ident, expanded) +} diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 59aba592d9ae4c5a4cdfdc6f9b9b08363b8a57e5..7753a853fae72fc87e6dc642cf076c6d0c736345 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -2,42 +2,21 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later +#![allow(dead_code)] + use proc_macro::TokenStream; -use quote::{format_ident, quote}; -use syn::{parse_macro_input, DeriveInput}; + +mod device; +mod object; +mod symbols; +mod utilities; #[proc_macro_derive(Object)] pub fn derive_object(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - let name = input.ident; - let module_static = format_ident!("__{}_LOAD_MODULE", name); - - let expanded = quote! { - #[allow(non_upper_case_globals)] - #[used] - #[cfg_attr(target_os = "linux", link_section = ".ctors")] - #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static #module_static: extern "C" fn() = { - extern "C" fn __register() { - unsafe { - ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO); - } - } - - extern "C" fn __load() { - unsafe { - ::qemu_api::bindings::register_module_init( - Some(__register), - ::qemu_api::bindings::module_init_type::MODULE_INIT_QOM - ); - } - } - - __load - }; - }; + object::derive_object(input) +} - TokenStream::from(expanded) +#[proc_macro_derive(Device, attributes(device, property))] +pub fn derive_device(input: TokenStream) -> TokenStream { + device::derive_device(input) } diff --git a/rust/qemu-api-macros/src/object.rs b/rust/qemu-api-macros/src/object.rs new file mode 100644 index 0000000000000000000000000000000000000000..f808069aea42de752dea7524fef64467427f105c --- /dev/null +++ b/rust/qemu-api-macros/src/object.rs @@ -0,0 +1,107 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, DeriveInput}; + +use crate::utilities::*; + +pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + assert_is_repr_c_struct(&input, "Object"); + + let name = input.ident; + let module_static = format_ident!("__{}_LOAD_MODULE", name); + + let ctors = quote! { + #[allow(non_upper_case_globals)] + #[used] + #[cfg_attr(target_os = "linux", link_section = ".ctors")] + #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] + #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] + pub static #module_static: extern "C" fn() = { + extern "C" fn __register() { + unsafe { + ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::objects::ObjectImplUnsafe>::TYPE_INFO); + } + } + + extern "C" fn __load() { + unsafe { + ::qemu_api::bindings::register_module_init( + Some(__register), + ::qemu_api::bindings::module_init_type::MODULE_INIT_QOM + ); + } + } + + __load + }; + }; + + let instance_init = format_ident!("__{}_instance_init_generated", name); + let instance_post_init = format_ident!("__{}_instance_post_init_generated", name); + let instance_finalize = format_ident!("__{}_instance_finalize_generated", name); + + let obj_impl_unsafe = quote! { + unsafe impl ::qemu_api::objects::ObjectImplUnsafe for #name { + const TYPE_INFO: ::qemu_api::bindings::TypeInfo = + ::qemu_api::bindings::TypeInfo { + name: ::TYPE_NAME.as_ptr(), + parent: if let Some(pname) = ::PARENT_TYPE_NAME { + pname.as_ptr() + } else { + ::core::ptr::null() + }, + instance_size: ::core::mem::size_of::() as ::qemu_api::bindings::size_t, + instance_align: ::core::mem::align_of::() as ::qemu_api::bindings::size_t, + instance_init: ::INSTANCE_INIT, + instance_post_init: ::INSTANCE_POST_INIT, + instance_finalize: ::INSTANCE_FINALIZE, + abstract_: ::ABSTRACT, + class_size: ::core::mem::size_of::<::Class>() as ::qemu_api::bindings::size_t, + class_init: <::Class as ::qemu_api::objects::ClassImplUnsafe>::CLASS_INIT, + class_base_init: <::Class as ::qemu_api::objects::ClassImplUnsafe>::CLASS_BASE_INIT, + class_data: ::core::ptr::null_mut(), + interfaces: ::core::ptr::null_mut(), + }; + const INSTANCE_INIT: Option = Some(#instance_init); + const INSTANCE_POST_INIT: Option = Some(#instance_post_init); + const INSTANCE_FINALIZE: Option = Some(#instance_finalize); + } + + #[no_mangle] + pub unsafe extern "C" fn #instance_init(obj: *mut ::qemu_api::bindings::Object) { + let mut instance = NonNull::new(obj.cast::<#name>()).expect(concat!("Expected obj to be a non-null pointer of type ", stringify!(#name))); + unsafe { + ::qemu_api::objects::ObjectImpl::instance_init(instance.as_mut()); + } + } + + #[no_mangle] + pub unsafe extern "C" fn #instance_post_init(obj: *mut ::qemu_api::bindings::Object) { + let mut instance = NonNull::new(obj.cast::<#name>()).expect(concat!("Expected obj to be a non-null pointer of type ", stringify!(#name))); + unsafe { + ::qemu_api::objects::ObjectImpl::instance_post_init(instance.as_mut()); + } + } + + #[no_mangle] + pub unsafe extern "C" fn #instance_finalize(obj: *mut ::qemu_api::bindings::Object) { + let mut instance = NonNull::new(obj.cast::<#name>()).expect(concat!("Expected obj to be a non-null pointer of type ", stringify!(#name))); + unsafe { + ::qemu_api::objects::ObjectImpl::instance_finalize(instance.as_mut()); + } + } + }; + + let expanded = quote! { + #obj_impl_unsafe + + #ctors + }; + TokenStream::from(expanded) +} diff --git a/rust/qemu-api-macros/src/symbols.rs b/rust/qemu-api-macros/src/symbols.rs new file mode 100644 index 0000000000000000000000000000000000000000..f73768d228ed2b4d478c18336db56cb11e70f012 --- /dev/null +++ b/rust/qemu-api-macros/src/symbols.rs @@ -0,0 +1,55 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::fmt; +use syn::{Ident, Path}; + +#[derive(Copy, Clone, Debug)] +pub struct Symbol(&'static str); + +pub const DEVICE: Symbol = Symbol("device"); +pub const NAME: Symbol = Symbol("name"); +pub const CATEGORY: Symbol = Symbol("category"); +pub const CLASS_NAME: Symbol = Symbol("class_name"); +pub const CLASS_NAME_OVERRIDE: Symbol = Symbol("class_name_override"); +pub const QDEV_PROP: Symbol = Symbol("qdev_prop"); +pub const MIGRATEABLE: Symbol = Symbol("migrateable"); +pub const PROPERTIES: Symbol = Symbol("properties"); +pub const PROPERTY: Symbol = Symbol("property"); + +impl PartialEq for Ident { + fn eq(&self, word: &Symbol) -> bool { + self == word.0 + } +} + +impl<'a> PartialEq for &'a Ident { + fn eq(&self, word: &Symbol) -> bool { + *self == word.0 + } +} + +impl PartialEq for Path { + fn eq(&self, word: &Symbol) -> bool { + self.is_ident(word.0) + } +} + +impl<'a> PartialEq for &'a Path { + fn eq(&self, word: &Symbol) -> bool { + self.is_ident(word.0) + } +} + +impl PartialEq for Symbol { + fn eq(&self, ident: &Ident) -> bool { + ident == self.0 + } +} + +impl fmt::Display for Symbol { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(fmt) + } +} diff --git a/rust/qemu-api-macros/src/utilities.rs b/rust/qemu-api-macros/src/utilities.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd8776539aa0bb3bcaa023bd88d962efe1431746 --- /dev/null +++ b/rust/qemu-api-macros/src/utilities.rs @@ -0,0 +1,152 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use syn::{parenthesized, token, Data, DeriveInput, LitInt}; + +#[derive(Default)] +pub enum Abi { + #[default] + Rust, + C, + Transparent, + Other(String), +} + +#[derive(Default)] +pub struct Repr { + pub abi: Abi, + /// whether the attribute was declared in the definition. + pub present: bool, + pub align: Option, + pub packed: Option, +} + +impl core::fmt::Display for Repr { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(fmt, "repr(")?; + match &self.abi { + Abi::C => write!(fmt, "C")?, + Abi::Rust => write!(fmt, "Rust")?, + Abi::Transparent => write!(fmt, "transparent")?, + Abi::Other(s) => write!(fmt, "{}", s)?, + } + if self.align.is_some() || self.packed.is_some() { + write!(fmt, ", ")?; + if let Some(v) = self.align { + write!(fmt, "align({})", v)?; + if self.packed.is_some() { + write!(fmt, ", ")?; + } + } + match self.packed { + Some(1) => write!(fmt, "packed")?, + Some(n) => write!(fmt, "packed({})", n)?, + None => {} + } + } + write!(fmt, ")") + } +} + +impl Repr { + pub fn detect_repr(attrs: &[syn::Attribute]) -> Self { + let mut repr = Self::default(); + + // We don't validate the repr attribute; if it's invalid rustc will complain + // anyway. + for attr in attrs { + if attr.path().is_ident("repr") { + repr.present = true; + if let Err(err) = attr.parse_nested_meta(|meta| { + // #[repr(C)] + if meta.path.is_ident("C") { + repr.abi = Abi::C; + return Ok(()); + } + + // #[repr(Rust)] + if meta.path.is_ident("Rust") { + repr.abi = Abi::Rust; + return Ok(()); + } + + // #[repr(transparent)] + if meta.path.is_ident("transparent") { + repr.abi = Abi::Transparent; + return Ok(()); + } + + // #[repr(align(N))] + if meta.path.is_ident("align") { + let content; + parenthesized!(content in meta.input); + let lit: LitInt = content.parse()?; + let n: usize = lit.base10_parse()?; + repr.align = Some(n); + return Ok(()); + } + + // #[repr(packed)] or #[repr(packed(N))], omitted N means 1 + if meta.path.is_ident("packed") { + repr.packed = if meta.input.peek(token::Paren) { + let content; + parenthesized!(content in meta.input); + let lit: LitInt = content.parse()?; + let n: usize = lit.base10_parse()?; + Some(n) + } else { + Some(1) + }; + return Ok(()); + } + + if let Some(i) = meta.path.get_ident() { + repr.abi = Abi::Other(i.to_string()); + } + + Err(meta.error("unrecognized repr")) + }) { + println!("Error while processing Object Derive macro: {}", err); + } + } + } + repr + } +} + +pub fn assert_is_repr_c_struct(input: &DeriveInput, derive_macro: &'static str) { + if !matches!(input.data, Data::Struct(_)) { + panic!( + "`{}` derive macro can only be used with structs, and `{}` is {}", + derive_macro, + input.ident, + match input.data { + Data::Struct(_) => unreachable!(), + Data::Enum(_) => "enum", + Data::Union(_) => "union", + } + ); + } + match Repr::detect_repr(&input.attrs) { + Repr { abi: Abi::C, .. } => { /* all good */ } + Repr { + abi: Abi::Transparent, + .. + } => { + // If the data layout is `transparent`, then its representation + // depends on the ABI of the wrapped type. We cannot + // detect it here. + } + other => { + panic!( + "`{}` derive macro can only be used with repr(C) structs, and `{}` {} \ + {}\nHint: Annotate the struct with `#[repr(C)]`.", + derive_macro, + input.ident, + if other.present { "is" } else { "defaults to" }, + other, + ); + } + } +} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index c72d34b607df1da90185046f4d9c26b3cb6c6523..0bd70b59afcc005251135802897954789b068e6e 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -3,8 +3,7 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', - 'src/definitions.rs', - 'src/device_class.rs', + 'src/objects.rs', ], {'.' : bindings_rs}, ), diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs deleted file mode 100644 index 60bd3f8aaa65ae131a9c4628a96ac52f590d7324..0000000000000000000000000000000000000000 --- a/rust/qemu-api/src/definitions.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Definitions required by QEMU when registering a device. - -use ::core::ffi::{c_void, CStr}; - -use crate::bindings::{Object, ObjectClass, TypeInfo}; - -/// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl { - type Class; - const TYPE_INFO: TypeInfo; - const TYPE_NAME: &'static CStr; - const PARENT_TYPE_NAME: Option<&'static CStr>; - const ABSTRACT: bool; - const INSTANCE_INIT: Option; - const INSTANCE_POST_INIT: Option; - const INSTANCE_FINALIZE: Option; -} - -pub trait Class { - const CLASS_INIT: Option; - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), - >; -} - -#[macro_export] -macro_rules! module_init { - ($func:expr, $type:expr) => { - #[used] - #[cfg_attr(target_os = "linux", link_section = ".ctors")] - #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static LOAD_MODULE: extern "C" fn() = { - extern "C" fn __load() { - unsafe { - $crate::bindings::register_module_init(Some($func), $type); - } - } - - __load - }; - }; - (qom: $func:ident => $body:block) => { - // NOTE: To have custom identifiers for the ctor func we need to either supply - // them directly as a macro argument or create them with a proc macro. - #[used] - #[cfg_attr(target_os = "linux", link_section = ".ctors")] - #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static LOAD_MODULE: extern "C" fn() = { - extern "C" fn __load() { - #[no_mangle] - unsafe extern "C" fn $func() { - $body - } - - unsafe { - $crate::bindings::register_module_init( - Some($func), - $crate::bindings::module_init_type::MODULE_INIT_QOM, - ); - } - } - - __load - }; - }; -} - -#[macro_export] -macro_rules! type_info { - ($t:ty) => { - $crate::bindings::TypeInfo { - name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(), - parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME { - pname.as_ptr() - } else { - ::core::ptr::null_mut() - }, - instance_size: ::core::mem::size_of::<$t>() as $crate::bindings::size_t, - instance_align: ::core::mem::align_of::<$t>() as $crate::bindings::size_t, - instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT, - instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT, - instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE, - abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT, - class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>() as $crate::bindings::size_t, - class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT, - class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT, - class_data: ::core::ptr::null_mut(), - interfaces: ::core::ptr::null_mut(), - }; - } -} diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs deleted file mode 100644 index 1ea95beb78dbf8637d9af1edb668d51411a9ac33..0000000000000000000000000000000000000000 --- a/rust/qemu-api/src/device_class.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::sync::OnceLock; - -use crate::bindings::Property; - -#[macro_export] -macro_rules! device_class_init { - ($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => { - #[no_mangle] - pub unsafe extern "C" fn $func( - klass: *mut $crate::bindings::ObjectClass, - _: *mut ::core::ffi::c_void, - ) { - let mut dc = - ::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap(); - dc.as_mut().realize = $realize_fn; - dc.as_mut().vmsd = &$vmsd; - $crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn); - $crate::bindings::device_class_set_props(dc.as_mut(), $props.as_mut_ptr()); - } - }; -} - -#[macro_export] -macro_rules! define_property { - ($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr, default = $defval:expr$(,)*) => { - $crate::bindings::Property { - name: { - #[used] - static _TEMP: &::core::ffi::CStr = $name; - _TEMP.as_ptr() - }, - info: $prop, - offset: ::core::mem::offset_of!($state, $field) - .try_into() - .expect("Could not fit offset value to type"), - bitnr: 0, - bitmask: 0, - set_default: true, - defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval.into() }, - arrayoffset: 0, - arrayinfo: ::core::ptr::null(), - arrayfieldsize: 0, - link_type: ::core::ptr::null(), - } - }; - ($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr$(,)*) => { - $crate::bindings::Property { - name: { - #[used] - static _TEMP: &::core::ffi::CStr = $name; - _TEMP.as_ptr() - }, - info: $prop, - offset: ::core::mem::offset_of!($state, $field) - .try_into() - .expect("Could not fit offset value to type"), - bitnr: 0, - bitmask: 0, - set_default: false, - defval: $crate::bindings::Property__bindgen_ty_1 { i: 0 }, - arrayoffset: 0, - arrayinfo: ::core::ptr::null(), - arrayfieldsize: 0, - link_type: ::core::ptr::null(), - } - }; -} - -#[repr(C)] -pub struct Properties(pub OnceLock<[Property; N]>, pub fn() -> [Property; N]); - -impl Properties { - pub fn as_mut_ptr(&mut self) -> *mut Property { - _ = self.0.get_or_init(self.1); - self.0.get_mut().unwrap().as_mut_ptr() - } -} - -#[macro_export] -macro_rules! declare_properties { - ($ident:ident, $($prop:expr),*$(,)*) => { - - const fn _calc_prop_len() -> usize { - let mut len = 1; - $({ - _ = stringify!($prop); - len += 1; - })* - len - } - const PROP_LEN: usize = _calc_prop_len(); - - fn _make_properties() -> [$crate::bindings::Property; PROP_LEN] { - [ - $($prop),*, - unsafe { ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init() }, - ] - } - - #[no_mangle] - pub static mut $ident: $crate::device_class::Properties = $crate::device_class::Properties(::std::sync::OnceLock::new(), _make_properties); - }; -} - -#[macro_export] -macro_rules! vm_state_description { - ($(#[$outer:meta])* - $name:ident, - $(name: $vname:expr,)* - $(unmigratable: $um_val:expr,)* - ) => { - #[used] - $(#[$outer])* - pub static $name: $crate::bindings::VMStateDescription = $crate::bindings::VMStateDescription { - $(name: { - #[used] - static VMSTATE_NAME: &::core::ffi::CStr = $vname; - $vname.as_ptr() - },)* - unmigratable: true, - ..unsafe { ::core::mem::MaybeUninit::<$crate::bindings::VMStateDescription>::zeroed().assume_init() } - }; - } -} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index e72fb4b4bb13b0982f828b6ec1cfe848c3e6bdf0..b94adc15288cdc62de7679988f549ebd80f895d7 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -27,11 +27,7 @@ unsafe impl Sync for bindings::Property {} unsafe impl Sync for bindings::TypeInfo {} unsafe impl Sync for bindings::VMStateDescription {} -pub mod definitions; -pub mod device_class; - -#[cfg(test)] -mod tests; +pub mod objects; use std::alloc::{GlobalAlloc, Layout}; diff --git a/rust/qemu-api/src/objects.rs b/rust/qemu-api/src/objects.rs new file mode 100644 index 0000000000000000000000000000000000000000..5c6762023ed6914f9c6b7dd16a5e07f778c2d4fa --- /dev/null +++ b/rust/qemu-api/src/objects.rs @@ -0,0 +1,90 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Implementation traits for QEMU objects, devices. + +use ::core::ffi::{c_int, c_void, CStr}; + +use crate::bindings::{DeviceState, Error, MigrationPriority, Object, ObjectClass, TypeInfo}; + +/// Trait a type must implement to be registered with QEMU. +pub trait ObjectImpl { + type Class: ClassImpl; + const TYPE_NAME: &'static CStr; + const PARENT_TYPE_NAME: Option<&'static CStr>; + const ABSTRACT: bool; + + unsafe fn instance_init(&mut self) {} + fn instance_post_init(&mut self) {} + fn instance_finalize(&mut self) {} +} + +/// The `extern`/`unsafe` analogue of [`ObjectImpl`]; it is used internally by `#[derive(Object)]` +/// and should not be implemented manually. +pub unsafe trait ObjectImplUnsafe { + const TYPE_INFO: TypeInfo; + + const INSTANCE_INIT: Option; + const INSTANCE_POST_INIT: Option; + const INSTANCE_FINALIZE: Option; +} + +/// Methods for QOM class types. +pub trait ClassImpl { + type Object: ObjectImpl; + + unsafe fn class_init(&mut self, _data: *mut core::ffi::c_void) {} + unsafe fn class_base_init(&mut self, _data: *mut core::ffi::c_void) {} +} + +/// The `extern`/`unsafe` analogue of [`ClassImpl`]; it is used internally by `#[derive(Object)]` +/// and should not be implemented manually. +pub unsafe trait ClassImplUnsafe { + const CLASS_INIT: Option; + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + >; +} + +/// Implementation methods for device types. +pub trait DeviceImpl: ObjectImpl { + fn realize(&mut self) {} + fn reset(&mut self) {} +} + +/// The `extern`/`unsafe` analogue of [`DeviceImpl`]; it is used internally by `#[derive(Device)]` +/// and should not be implemented manually. +pub unsafe trait DeviceImplUnsafe { + const REALIZE: Option; + const RESET: Option; +} + +/// Constant metadata and implementation methods for types with device migration state. +pub trait Migrateable: DeviceImplUnsafe { + const NAME: Option<&'static CStr> = None; + const UNMIGRATABLE: bool = true; + const EARLY_SETUP: bool = false; + const VERSION_ID: c_int = 1; + const MINIMUM_VERSION_ID: c_int = 1; + const PRIORITY: MigrationPriority = MigrationPriority::MIG_PRI_DEFAULT; + + unsafe fn pre_load(&mut self) -> c_int { + 0 + } + unsafe fn post_load(&mut self, _version_id: c_int) -> c_int { + 0 + } + unsafe fn pre_save(&mut self) -> c_int { + 0 + } + unsafe fn post_save(&mut self) -> c_int { + 0 + } + unsafe fn needed(&mut self) -> bool { + false + } + unsafe fn dev_unplug_pending(&mut self) -> bool { + false + } +} diff --git a/rust/qemu-api/src/tests.rs b/rust/qemu-api/src/tests.rs deleted file mode 100644 index df54edbd4e27e7d2aafc243355d1826d52497c21..0000000000000000000000000000000000000000 --- a/rust/qemu-api/src/tests.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use crate::{ - bindings::*, declare_properties, define_property, device_class_init, vm_state_description, -}; - -#[test] -fn test_device_decl_macros() { - // Test that macros can compile. - vm_state_description! { - VMSTATE, - name: c"name", - unmigratable: true, - } - - #[repr(C)] - pub struct DummyState { - pub char_backend: CharBackend, - pub migrate_clock: bool, - } - - declare_properties! { - DUMMY_PROPERTIES, - define_property!( - c"chardev", - DummyState, - char_backend, - unsafe { &qdev_prop_chr }, - CharBackend - ), - define_property!( - c"migrate-clk", - DummyState, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool - ), - } - - device_class_init! { - dummy_class_init, - props => DUMMY_PROPERTIES, - realize_fn => None, - reset_fn => None, - vmsd => VMSTATE, - } -} diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build index a53335f3092e06723039513a1bf5a0d35b4afcd7..9f56ce1c24d0ff86e9b0146b0f82c37ac868fab7 100644 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -24,6 +24,7 @@ _syn_rs = static_library( '--cfg', 'feature="printing"', '--cfg', 'feature="clone-impls"', '--cfg', 'feature="proc-macro"', + '--cfg', 'feature="extra-traits"', ], dependencies: [ quote_dep, From patchwork Thu Oct 24 14:03:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849132 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 F28FBCE8E78 for ; Thu, 24 Oct 2024 14:05:16 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRW-0007Bf-RB; Thu, 24 Oct 2024 10:04:02 -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 1t3yRC-00078q-EU for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:50 -0400 Received: from mail-lj1-x235.google.com ([2a00:1450:4864:20::235]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yR8-0003wK-OK for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:42 -0400 Received: by mail-lj1-x235.google.com with SMTP id 38308e7fff4ca-2fb51e00c05so14154581fa.0 for ; Thu, 24 Oct 2024 07:03:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778616; x=1730383416; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=2iGhVX3FthgCu9tCSB5ib+BmYJD5e1RgHUcKSHCgOOI=; b=rUGCQrLWPDkD2kiGqZSdoWxuu2ET2LjSjxyJPDSao/0rESoR17GvIP4KufdEdTeCx8 w3ZN3i+DgRIILLAHSBBI25LBRRRrQLWjfHfk3U7wRrggD15+xpyLqo6dfapcnR2F2wQJ X6iphCWjoTN7eNGQtQUgpoZgd7YdL7UFB+dKOMzjmNsT/wiWIJDtAsVmzJ+khkMm0Jzj ANxC3rQhVpK5owDxDyd5lpMCgFYxRwIMf5jOSvm6yCcUz3mrqtg9jqRhq2I0I+Z4mrNb FgSvxrpfBwTHSkV9warB9nyVBIF7q40o5RM7fhVwuTY/Xw7yDCHoo+/nu0nL7PqFuo4r DCAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778616; x=1730383416; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2iGhVX3FthgCu9tCSB5ib+BmYJD5e1RgHUcKSHCgOOI=; b=pmguOuAmTQedk+MOWQD3hoZnHpREKzG0Yo+VctXWWmhpArieOFvA5JBmTGdF9ZNsBA CtPL5ggadf39fv2JtKNgLwtX51QF5nL6JVFjWLCfiASpSA1SKHxmr7YjTEEKdAFIvqqN SvEL5wQi7Z0JBpln5iHLtV6XQKIlVcX6zll24eZhWzLbQ21JbtZmbg3az+P323iixAUQ DvhF1TrhpYWcHhE3/mDiLGgCxifPKnNEbYcgbTWA7OlnL6eYudjvSWLLTQsAnUP4VDOB CREEe6n7fPDNgPpdnxdnebA9hJMQY81xoFbthgOuOtzDmdMaNMhviPFYFc2Zuy1xMc2P C3aw== X-Gm-Message-State: AOJu0YyuJFm+muB3qd6jYt2Bx2g3oC1xogK6SDKZMBASPjrXC+DXzko6 lmUofYwTnb+40wbe4tW4tago8Ke5ZTJu+Zed9s5HKpF9ZR6ZiZV7VzzGIn5E/k8= X-Google-Smtp-Source: AGHT+IGhCJhXR6hGf4u3tLOUo/0A2sEJdUiGwOYPNA3umLPkGF/soXznrg3r49In9R5KsO2cYOw3oA== X-Received: by 2002:a05:6512:220d:b0:535:6baa:8c5d with SMTP id 2adb3069b0e04-53b1a33cc87mr5521258e87.20.1729778615109; Thu, 24 Oct 2024 07:03:35 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:34 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:02 +0300 Subject: [PATCH 04/11] rust: add support for migration in device models MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-4-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=37037; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=kB5qjmgamQwXPRu8ReZyXP7rlHpISzFf2JMFS5i2g8I=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9pWnc3Q3VJbkM0NUJpRGVnVXhqWTViMTRpCndBd1cxQU04RTNCTVU2cVMv SWlKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG9nQUt DUkIzS2Nkd2YzNEowQzJRRC85YVU0WGJnYWtIQVdMSnp2NGM0NHdEQW9mSnVzTWYreHY2RUxLZA p5N01aU2FTcm1yNk9uNi9VV216VUJEVlEwd2pLWFZrR3BWK1hJTml4UlhYY1o3Y0JOeTMzR1J1Y 3dZTkJjdGdWCkZkMEdWQVNIWkJadXlyU0FqL0RrbFdoVXhBSE5KOWVGa0RoSWl3WXhXc2dYMENr VmtuRDh2YTdRS054U0J0Z2kKdmpNOStjUU1qL28rNjJOWGlaWUN0WHYzbHluS0hEMDc1UXNLOGF LUkkySUJkcXVTc0NlQmpPQktKMHRQamtJUgptMFBTd3ZtN3pmVlBlY0llSm85bk5LdG9aUGY0dE l6WHN0TyszNnl6WjJkWlNBWEhQRHRQOUlvMENvazcxZkxsClhmWERlTWFGelJhRUxNREdUd2RtR EJjR0ZsWnA2MittbENqSVlzVU9ndjhCbGJ3ajdFUXd2ZmdjRE5sbm5OaTkKbE9HY3NkdWdmOFZI bHFDVy9Mc21KY2JOeHQ0VmFGcUNWcmVYaDJITkxHWG9XM0cxZGJUWXpNVjZQMjY3K2tpawoxR00 3R0hOdWtLcWp0VVl4ZjRJVlpTY1BWU3U4b2x1SWZKdTR3MDR5RmRhaFhXbjRvVUlad0EwVDdySV BENFdwCk44aVhpMkdtUXZMZjNRK0dnRnpRcXFmZHNIYnkrRFpWb1ZaRnVTYnAyNnRCZ0loQ3NmS EdVZTcwNVBYdXVsVjUKczdBYytUTkhjd0UwbFB6SVR2Yy92T1I3dXFBWTFFUjdWM3pjT0RFSmNy RUxEeFc5b29GODYvTUVYcFlXNlF5WApnSlB6SU1WSlhNWjVGWWJvbGNjOVF4OGcwdGFpOGlVbzh FM3dNZ25nR085ZU9ZZkp4T2ZUT01QdEJidnhBbFZXCkJKTHBGQT09Cj05SFZ0Ci0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::235; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-lj1-x235.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 commit adds support for declaring migration state to device models in Rust. This is done through different but related parts: - The Device derive macro gains new attributes `vmstate_fields` and `vmstate_subsections`. This allows the device declaration to include the vmstate fields directly at the struct definition. - a new qemu_api module, `vmstate` was added. There a bunch of Rust macros declared there that are equivalent in spirit to the C macros declared in include/migration/vmstate.h. For example the Rust of equivalent of the C macro: VMSTATE_UINT32(field_name, struct_name) is: vmstate_uint32!(field_name, StructName) This breathtaking development now allows us to not have to define VMStateDescription ourselves but split the notion of migration to two parts: - A Migrateable trait that allows a type to define version_ids, name, priority, override methods like pre_load, post_load, pre_save etc. - Define the actual vmstate fields and subsections through the Device derive macro right there with the struct definition: ------------------------ >8 ------------------------ #[repr(C)] #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::Device)] +#[device( + class_name_override = PL011Class, + vmstate_fields = vmstate_fields!{ + vmstate_unused!(u32::BITS as u64), + vmstate_uint32!(flags, PL011State), + vmstate_uint32!(line_control, PL011State), + vmstate_uint32!(receive_status_error_clear, PL011State), + vmstate_uint32!(control, PL011State), + vmstate_uint32!(dmacr, PL011State), + vmstate_uint32!(int_enabled, PL011State), + vmstate_uint32!(int_level, PL011State), + vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), + vmstate_uint32!(ilpr, PL011State), + vmstate_uint32!(ibrd, PL011State), + vmstate_uint32!(fbrd, PL011State), + vmstate_uint32!(ifl, PL011State), + vmstate_int32!(read_pos, PL011State), + vmstate_int32!(read_count, PL011State), + vmstate_int32!(read_trigger, PL011State), + }, + vmstate_subsections = vmstate_subsections!{ + VMSTATE_PL011_CLOCK + } +)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: SysBusDevice, ------------------------ >8 ------------------------ Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 92 +++++++- rust/qemu-api-macros/src/device.rs | 111 +++------- rust/qemu-api-macros/src/lib.rs | 1 + rust/qemu-api-macros/src/symbols.rs | 2 + rust/qemu-api-macros/src/vmstate.rs | 113 ++++++++++ rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 3 + rust/qemu-api/src/vmstate.rs | 403 ++++++++++++++++++++++++++++++++++++ 8 files changed, 637 insertions(+), 89 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c469877b1ca70dd1a02e3a2449c65ad3e57c93ae..57dc37dadef631fbccfa3049a3d8701b4e62b5b3 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -10,6 +10,8 @@ use qemu_api::{ bindings::{self, *}, objects::*, + vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, vmstate_uint32, + vmstate_uint32_array, vmstate_unused, }; use crate::{ @@ -20,14 +22,74 @@ static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; +/// Integer Baud Rate Divider, `UARTIBRD` +const IBRD_MASK: u32 = 0x3f; + +/// Fractional Baud Rate Divider, `UARTFBRD` +const FBRD_MASK: u32 = 0xffff; + const DATA_BREAK: u32 = 1 << 10; /// QEMU sourced constant. pub const PL011_FIFO_DEPTH: usize = 16_usize; +#[no_mangle] +extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { + unsafe { + debug_assert!(!opaque.is_null()); + let state = NonNull::new_unchecked(opaque.cast::()); + state.as_ref().migrate_clock + } +} + +qemu_api::vmstate_description! { + /// Migration subsection for [`PL011State`] clock. + pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { + name: c"pl011/clock", + unmigratable: false, + early_setup: false, + version_id: 1, + minimum_version_id: 1, + priority: MigrationPriority::MIG_PRI_DEFAULT, + pre_load: None, + post_load: None, + pre_save: None, + post_save: None, + needed: Some(pl011_clock_needed), + dev_unplug_pending: None, + fields: vmstate_fields!{ + vmstate_clock!(clock, PL011State), + }, + subsections: ::core::ptr::null(), + }; +} + #[repr(C)] #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::Device)] -#[device(class_name_override = PL011Class)] +#[device( + class_name_override = PL011Class, + vmstate_fields = vmstate_fields!{ + vmstate_unused!(u32::BITS as u64), + vmstate_uint32!(flags, PL011State), + vmstate_uint32!(line_control, PL011State), + vmstate_uint32!(receive_status_error_clear, PL011State), + vmstate_uint32!(control, PL011State), + vmstate_uint32!(dmacr, PL011State), + vmstate_uint32!(int_enabled, PL011State), + vmstate_uint32!(int_level, PL011State), + vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), + vmstate_uint32!(ilpr, PL011State), + vmstate_uint32!(ibrd, PL011State), + vmstate_uint32!(fbrd, PL011State), + vmstate_uint32!(ifl, PL011State), + vmstate_int32!(read_pos, PL011State), + vmstate_int32!(read_count, PL011State), + vmstate_int32!(read_trigger, PL011State), + }, + vmstate_subsections = vmstate_subsections!{ + VMSTATE_PL011_CLOCK + } +)] /// PL011 Device Model in QEMU pub struct PL011State { pub parent_obj: SysBusDevice, @@ -165,7 +227,33 @@ fn reset(&mut self) { } } -impl qemu_api::objects::Migrateable for PL011State {} +impl qemu_api::objects::Migrateable for PL011State { + const NAME: Option<&'static CStr> = Some(c"pl011"); + const UNMIGRATABLE: bool = false; + const VERSION_ID: c_int = 2; + const MINIMUM_VERSION_ID: c_int = 2; + + unsafe fn post_load(&mut self, _version_id: c_int) -> c_int { + /* Sanity-check input state */ + if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { + return -1; + } + + if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 { + // Older versions of PL011 didn't ensure that the single + // character in the FIFO in FIFO-disabled mode is in + // element 0 of the array; convert to follow the current + // code's assumptions. + self.read_fifo[0] = self.read_fifo[self.read_pos]; + self.read_pos = 0; + } + + self.ibrd &= IBRD_MASK; + self.fbrd &= FBRD_MASK; + + 0 + } +} #[used] pub static CLK_NAME: &CStr = c"clk"; diff --git a/rust/qemu-api-macros/src/device.rs b/rust/qemu-api-macros/src/device.rs index 3b965576a065601cd5c97d5ab6a2501f96d16a61..a666c64087715b9dc0d9ebe33f2b22d965381c64 100644 --- a/rust/qemu-api-macros/src/device.rs +++ b/rust/qemu-api-macros/src/device.rs @@ -10,11 +10,13 @@ }; use syn::{parse_macro_input, DeriveInput}; -use crate::{symbols::*, utilities::*}; +use crate::{symbols::*, utilities::*, vmstate}; #[derive(Debug, Default)] struct DeriveContainer { category: Option, + vmstate_fields: Option, + vmstate_subsections: Option, class_name: Option, class_name_override: Option, } @@ -27,6 +29,8 @@ fn parse(input: ParseStream) -> Result { assert_eq!(DEVICE, bracketed.parse::()?); let mut retval = Self { category: None, + vmstate_fields: None, + vmstate_subsections: None, class_name: None, class_name_override: None, }; @@ -54,6 +58,20 @@ fn parse(input: ParseStream) -> Result { let lit: syn::LitStr = content.parse()?; let path: syn::Path = lit.parse()?; retval.category = Some(path); + } else if value == VMSTATE_FIELDS { + let _: syn::Token![=] = content.parse()?; + if retval.vmstate_fields.is_some() { + panic!("{} can only be used at most once", VMSTATE_FIELDS); + } + let expr: syn::Expr = content.parse()?; + retval.vmstate_fields = Some(expr); + } else if value == VMSTATE_SUBSECTIONS { + let _: syn::Token![=] = content.parse()?; + if retval.vmstate_subsections.is_some() { + panic!("{} can only be used at most once", VMSTATE_SUBSECTIONS); + } + let expr: syn::Expr = content.parse()?; + retval.vmstate_subsections = Some(expr); } else { panic!("unrecognized token `{}`", value); } @@ -272,7 +290,11 @@ pub struct #class_name { let class_base_init_fn = format_ident!("__{}_class_base_init_generated", class_name); let (vmsd, vmsd_impl) = { - let (i, vmsd) = make_vmstate(name); + let (i, vmsd) = vmstate::make_vmstate( + name, + derive_container.vmstate_fields, + derive_container.vmstate_subsections, + ); (quote! { &#i }, vmsd) }; let category = if let Some(category) = derive_container.category { @@ -346,88 +368,3 @@ unsafe impl ::qemu_api::objects::ClassImplUnsafe for #class_name { #vmsd_impl } } - -fn make_vmstate(name: &syn::Ident) -> (syn::Ident, proc_macro2::TokenStream) { - let vmstate_description_ident = format_ident!("__VMSTATE_{}", name); - - let pre_load = format_ident!("__{}_pre_load_generated", name); - let post_load = format_ident!("__{}_post_load_generated", name); - let pre_save = format_ident!("__{}_pre_save_generated", name); - let post_save = format_ident!("__{}_post_save_generated", name); - let needed = format_ident!("__{}_needed_generated", name); - let dev_unplug_pending = format_ident!("__{}_dev_unplug_pending_generated", name); - - let migrateable_fish = quote! {<#name as ::qemu_api::objects::Migrateable>}; - let vmstate_description = quote! { - #[used] - #[allow(non_upper_case_globals)] - pub static #vmstate_description_ident: ::qemu_api::bindings::VMStateDescription = ::qemu_api::bindings::VMStateDescription { - name: if let Some(name) = #migrateable_fish::NAME { - name.as_ptr() - } else { - <#name as ::qemu_api::objects::ObjectImplUnsafe>::TYPE_INFO.name - }, - unmigratable: #migrateable_fish::UNMIGRATABLE, - early_setup: #migrateable_fish::EARLY_SETUP, - version_id: #migrateable_fish::VERSION_ID, - minimum_version_id: #migrateable_fish::MINIMUM_VERSION_ID, - priority: #migrateable_fish::PRIORITY, - pre_load: Some(#pre_load), - post_load: Some(#post_load), - pre_save: Some(#pre_save), - post_save: Some(#post_save), - needed: Some(#needed), - dev_unplug_pending: Some(#dev_unplug_pending), - fields: ::core::ptr::null(), - subsections: ::core::ptr::null(), - }; - - #[no_mangle] - pub unsafe extern "C" fn #pre_load(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { - let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); - unsafe { - ::qemu_api::objects::Migrateable::pre_load(instance.as_mut()) - } - } - #[no_mangle] - pub unsafe extern "C" fn #post_load(opaque: *mut ::core::ffi::c_void, version_id: core::ffi::c_int) -> ::core::ffi::c_int { - let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); - unsafe { - ::qemu_api::objects::Migrateable::post_load(instance.as_mut(), version_id) - } - } - #[no_mangle] - pub unsafe extern "C" fn #pre_save(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { - let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); - unsafe { - ::qemu_api::objects::Migrateable::pre_save(instance.as_mut()) - } - } - #[no_mangle] - pub unsafe extern "C" fn #post_save(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { - let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); - unsafe { - ::qemu_api::objects::Migrateable::post_save(instance.as_mut()) - } - } - #[no_mangle] - pub unsafe extern "C" fn #needed(opaque: *mut ::core::ffi::c_void) -> bool { - let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); - unsafe { - ::qemu_api::objects::Migrateable::needed(instance.as_mut()) - } - } - #[no_mangle] - pub unsafe extern "C" fn #dev_unplug_pending(opaque: *mut ::core::ffi::c_void) -> bool { - let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); - unsafe { - ::qemu_api::objects::Migrateable::dev_unplug_pending(instance.as_mut()) - } - } - }; - - let expanded = quote! { - #vmstate_description - }; - (vmstate_description_ident, expanded) -} diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 7753a853fae72fc87e6dc642cf076c6d0c736345..7b5c0c044da879241b05bba75edcb17b498e5d5a 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -10,6 +10,7 @@ mod object; mod symbols; mod utilities; +mod vmstate; #[proc_macro_derive(Object)] pub fn derive_object(input: TokenStream) -> TokenStream { diff --git a/rust/qemu-api-macros/src/symbols.rs b/rust/qemu-api-macros/src/symbols.rs index f73768d228ed2b4d478c18336db56cb11e70f012..79c242cf069d5de1dd0cd61b2a5c7814564af47e 100644 --- a/rust/qemu-api-macros/src/symbols.rs +++ b/rust/qemu-api-macros/src/symbols.rs @@ -15,6 +15,8 @@ pub const CLASS_NAME_OVERRIDE: Symbol = Symbol("class_name_override"); pub const QDEV_PROP: Symbol = Symbol("qdev_prop"); pub const MIGRATEABLE: Symbol = Symbol("migrateable"); +pub const VMSTATE_FIELDS: Symbol = Symbol("vmstate_fields"); +pub const VMSTATE_SUBSECTIONS: Symbol = Symbol("vmstate_subsections"); pub const PROPERTIES: Symbol = Symbol("properties"); pub const PROPERTY: Symbol = Symbol("property"); diff --git a/rust/qemu-api-macros/src/vmstate.rs b/rust/qemu-api-macros/src/vmstate.rs new file mode 100644 index 0000000000000000000000000000000000000000..2d72bf13b5acc861fac0814d749762ddb76824d5 --- /dev/null +++ b/rust/qemu-api-macros/src/vmstate.rs @@ -0,0 +1,113 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use quote::{format_ident, quote}; + +pub fn make_vmstate( + name: &syn::Ident, + vmstate_fields: Option, + vmstate_subsections: Option, +) -> (syn::Ident, proc_macro2::TokenStream) { + let vmstate_description_ident = format_ident!("__VMSTATE_{}", name); + + let pre_load = format_ident!("__{}_pre_load_generated", name); + let post_load = format_ident!("__{}_post_load_generated", name); + let pre_save = format_ident!("__{}_pre_save_generated", name); + let post_save = format_ident!("__{}_post_save_generated", name); + let needed = format_ident!("__{}_needed_generated", name); + let dev_unplug_pending = format_ident!("__{}_dev_unplug_pending_generated", name); + + let migrateable_fish = quote! {<#name as ::qemu_api::objects::Migrateable>}; + let vmstate_fields = if let Some(fields) = vmstate_fields { + quote! { + #fields + } + } else { + quote! { + ::core::ptr::null() + } + }; + let vmstate_subsections = if let Some(subsections) = vmstate_subsections { + quote! { + #subsections + } + } else { + quote! { + ::core::ptr::null() + } + }; + + let vmstate_description = quote! { + #[used] + #[allow(non_upper_case_globals)] + pub static #vmstate_description_ident: ::qemu_api::bindings::VMStateDescription = ::qemu_api::bindings::VMStateDescription { + name: if let Some(name) = #migrateable_fish::NAME { + name.as_ptr() + } else { + <#name as ::qemu_api::objects::ObjectImplUnsafe>::TYPE_INFO.name + }, + unmigratable: #migrateable_fish::UNMIGRATABLE, + early_setup: #migrateable_fish::EARLY_SETUP, + version_id: #migrateable_fish::VERSION_ID, + minimum_version_id: #migrateable_fish::MINIMUM_VERSION_ID, + priority: #migrateable_fish::PRIORITY, + pre_load: Some(#pre_load), + post_load: Some(#post_load), + pre_save: Some(#pre_save), + post_save: Some(#post_save), + needed: Some(#needed), + dev_unplug_pending: Some(#dev_unplug_pending), + fields: #vmstate_fields, + subsections: #vmstate_subsections, + }; + + #[no_mangle] + pub unsafe extern "C" fn #pre_load(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::pre_load(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #post_load(opaque: *mut ::core::ffi::c_void, version_id: core::ffi::c_int) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::post_load(instance.as_mut(), version_id) + } + } + #[no_mangle] + pub unsafe extern "C" fn #pre_save(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::pre_save(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #post_save(opaque: *mut ::core::ffi::c_void) -> ::core::ffi::c_int { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::post_save(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #needed(opaque: *mut ::core::ffi::c_void) -> bool { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::needed(instance.as_mut()) + } + } + #[no_mangle] + pub unsafe extern "C" fn #dev_unplug_pending(opaque: *mut ::core::ffi::c_void) -> bool { + let mut instance = NonNull::new(opaque.cast::<#name>()).expect(concat!("Expected opaque to be a non-null pointer of type ", stringify!(#name), "::Object")); + unsafe { + ::qemu_api::objects::Migrateable::dev_unplug_pending(instance.as_mut()) + } + } + }; + + let expanded = quote! { + #vmstate_description + }; + (vmstate_description_ident, expanded) +} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 0bd70b59afcc005251135802897954789b068e6e..11984abb878bef18be3c819f61da24ce1405ea59 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -4,6 +4,7 @@ _qemu_api_rs = static_library( [ 'src/lib.rs', 'src/objects.rs', + 'src/vmstate.rs', ], {'.' : bindings_rs}, ), diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index b94adc15288cdc62de7679988f549ebd80f895d7..d276adfb6622eee6e42494e089e1f20b0b5cdf08 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -26,8 +26,11 @@ unsafe impl Send for bindings::Property {} unsafe impl Sync for bindings::Property {} unsafe impl Sync for bindings::TypeInfo {} unsafe impl Sync for bindings::VMStateDescription {} +unsafe impl Sync for bindings::VMStateField {} +unsafe impl Sync for bindings::VMStateInfo {} pub mod objects; +pub mod vmstate; use std::alloc::{GlobalAlloc, Layout}; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs new file mode 100644 index 0000000000000000000000000000000000000000..4478febc9ac2768cca3e638ebae27b042edb1bf2 --- /dev/null +++ b/rust/qemu-api/src/vmstate.rs @@ -0,0 +1,403 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Helper macros to declare migration state for device models. +//! +//! Some macros are direct equivalents to the C macros declared in `include/migration/vmstate.h` +//! while [`vmstate_description`], [`vmstate_subsections`] and [`vmstate_fields`] are meant to be +//! used when declaring a device model state struct with the [`Device`](qemu_api_macros::Device) +//! `Derive` macro. + +#[doc(alias = "VMSTATE_UNUSED_BUFFER")] +#[macro_export] +macro_rules! vmstate_unused_buffer { + ($field_exists_fn:expr, $version_id:expr, $size:expr) => {{ + $crate::bindings::VMStateField { + name: c"unused".as_ptr(), + err_hint: ::core::ptr::null(), + offset: 0, + size: $size, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, + flags: VMStateFlags::VMS_BUFFER, + vmsd: ::core::ptr::null(), + version_id: $version_id, + struct_version_id: 0, + field_exists: $field_exists_fn, + } + }}; +} + +#[doc(alias = "VMSTATE_UNUSED_V")] +#[macro_export] +macro_rules! vmstate_unused_v { + ($version_id:expr, $size:expr) => {{ + $crate::vmstate_unused_buffer!(None, $version_id, $size) + }}; +} + +#[doc(alias = "VMSTATE_UNUSED")] +#[macro_export] +macro_rules! vmstate_unused { + ($size:expr) => {{ + $crate::vmstate_unused_v!(0, $size) + }}; +} + +#[doc(alias = "VMSTATE_SINGLE_TEST")] +#[macro_export] +macro_rules! vmstate_single_test { + ($field_name:ident, $struct_name:ty, $field_exists_fn:expr, $version_id:expr, $info:block, $size:expr) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::core::ffi::c_char, + err_hint: ::core::ptr::null(), + offset: ::core::mem::offset_of!($struct_name, $field_name) as _, + size: $size, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: $info, + flags: VMStateFlags::VMS_SINGLE, + vmsd: ::core::ptr::null(), + version_id: $version_id, + struct_version_id: 0, + field_exists: $field_exists_fn, + } + }}; +} + +#[doc(alias = "VMSTATE_SINGLE")] +#[macro_export] +macro_rules! vmstate_single { + ($field_name:ident, $struct_name:ty, $version_id:expr, $info:block, $size:expr) => {{ + $crate::vmstate_single_test!($field_name, $struct_name, None, $version_id, $info, $size) + }}; +} + +#[doc(alias = "VMSTATE_UINT32_V")] +#[macro_export] +macro_rules! vmstate_uint32_v { + ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ + $crate::vmstate_single!( + $field_name, + $struct_name, + $version_id, + { unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) } }, + u32::BITS as u64 + ) + }}; +} + +#[doc(alias = "VMSTATE_UINT32")] +#[macro_export] +macro_rules! vmstate_uint32 { + ($field_name:ident, $struct_name:ty) => {{ + $crate::vmstate_uint32_v!($field_name, $struct_name, 0) + }}; +} + +#[doc(alias = "VMSTATE_INT32_V")] +#[macro_export] +macro_rules! vmstate_int32_v { + ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ + $crate::vmstate_single!( + $field_name, + $struct_name, + $version_id, + { unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32) } }, + i32::BITS as u64 + ) + }}; +} + +#[doc(alias = "VMSTATE_INT32")] +#[macro_export] +macro_rules! vmstate_int32 { + ($field_name:ident, $struct_name:ty) => {{ + $crate::vmstate_int32_v!($field_name, $struct_name, 0) + }}; +} + +#[doc(alias = "VMSTATE_ARRAY")] +#[macro_export] +macro_rules! vmstate_array { + ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr, $info:block, $size:expr) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::core::ffi::c_char, + err_hint: ::core::ptr::null(), + offset: ::core::mem::offset_of!($struct_name, $field_name) as _, + size: $size, + start: 0, + num: $length as _, + num_offset: 0, + size_offset: 0, + info: $info, + flags: VMStateFlags::VMS_ARRAY, + vmsd: ::core::ptr::null(), + version_id: $version_id, + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_UINT32_ARRAY_V")] +#[macro_export] +macro_rules! vmstate_uint32_array_v { + ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr) => {{ + $crate::vmstate_array!( + $field_name, + $struct_name, + $length, + $version_id, + { unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) } }, + u32::BITS as u64 + ) + }}; +} + +#[doc(alias = "VMSTATE_UINT32_ARRAY")] +#[macro_export] +macro_rules! vmstate_uint32_array { + ($field_name:ident, $struct_name:ty, $length:expr) => {{ + $crate::vmstate_uint32_array_v!($field_name, $struct_name, $length, 0) + }}; +} + +#[doc(alias = "VMSTATE_STRUCT_POINTER_V")] +#[macro_export] +macro_rules! vmstate_struct_pointer_v { + ($field_name:ident, $struct_name:ty, $version_id:expr, $vmsd:expr, $type:ty) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::core::ffi::c_char, + err_hint: ::core::ptr::null(), + offset: ::core::mem::offset_of!($struct_name, $field_name) as _, + size: ::core::mem::size_of::<*const $type>() as _, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: ::core::ptr::null(), + flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), + vmsd: $vmsd, + version_id: $version_id, + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_OF_POINTER")] +#[macro_export] +macro_rules! vmstate_array_of_pointer { + ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $info:expr, $type:ty) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::core::ffi::c_char, + version_id: $version_id, + num: $num, + info: $info, + size: ::core::mem::size_of::<*const $type>() as _, + flags: VMStateFlags(VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0), + offset: ::core::mem::offset_of!($struct_name, $field_name) as _, + err_hint: ::core::ptr::null(), + start: 0, + num_offset: 0, + size_offset: 0, + vmsd: ::core::ptr::null(), + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_OF_POINTER_TO_STRUCT")] +#[macro_export] +macro_rules! vmstate_array_of_pointer_to_struct { + ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $vmsd:expr, $type:ty) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::core::ffi::c_char, + version_id: $version_id, + num: $num, + vmsd: $vmsd, + size: ::core::mem::size_of::<*const $type>() as _, + flags: VMStateFlags( + VMStateFlags::VMS_ARRAY.0 + | VMStateFlags::VMS_STRUCT.0 + | VMStateFlags::VMS_ARRAY_OF_POINTER.0, + ), + offset: ::core::mem::offset_of!($struct_name, $field_name) as _, + err_hint: ::core::ptr::null(), + start: 0, + num_offset: 0, + size_offset: 0, + vmsd: ::core::ptr::null(), + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_CLOCK_V")] +#[macro_export] +macro_rules! vmstate_clock_v { + ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ + $crate::vmstate_struct_pointer_v!( + $field_name, + $struct_name, + $version_id, + { unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) } }, + $crate::bindings::Clock + ) + }}; +} + +#[doc(alias = "VMSTATE_CLOCK")] +#[macro_export] +macro_rules! vmstate_clock { + ($field_name:ident, $struct_name:ty) => {{ + $crate::vmstate_clock_v!($field_name, $struct_name, 0) + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_CLOCK_V")] +#[macro_export] +macro_rules! vmstate_array_clock_v { + ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr) => {{ + $crate::vmstate_array_of_pointer_to_struct!( + $field_name, + $struct_name, + $num, + $version_id, + { unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) } }, + $crate::bindings::Clock + ) + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_CLOCK")] +#[macro_export] +macro_rules! vmstate_array_clock { + ($field_name:ident, $struct_name:ty, $num:expr) => {{ + $crate::vmstate_array_clock_v!($field_name, $struct_name, $name, 0) + }}; +} + +/// Helper macro to declare a list of +/// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return a +/// pointer to the array of values it created. +#[macro_export] +macro_rules! vmstate_fields { + ($($field:expr),*$(,)*) => {{ + #[used] + static _FIELDS: &[$crate::bindings::VMStateField] = &[ + $($field),*, + $crate::bindings::VMStateField { + name: ::core::ptr::null(), + err_hint: ::core::ptr::null(), + offset: 0, + size: 0, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: ::core::ptr::null(), + flags: VMStateFlags::VMS_END, + vmsd: ::core::ptr::null(), + version_id: 0, + struct_version_id: 0, + field_exists: None, + } + ]; + _FIELDS.as_ptr() + }} +} + +/// A transparent wrapper type for the `subsections` field of +/// [`VMStateDescription`](crate::bindings::VMStateDescription). +/// +/// This is necessary to be able to declare subsection descriptions as statics, because the only +/// way to implement `Sync` for a foreign type (and `*const` pointers are foreign types in Rust) is +/// to create a wrapper struct and `unsafe impl Sync` for it. +/// +/// This struct is used in the [`vmstate_subsections`] macro implementation. +#[repr(transparent)] +pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); + +unsafe impl Sync for VMStateSubsectionsWrapper {} + +/// Helper macro to declare a list of subsections +/// ([`VMStateDescription`](`crate::bindings::VMStateDescription`)) into a static and return a +/// pointer to the array of pointers it created. +#[macro_export] +macro_rules! vmstate_subsections { + ($($subsection:expr),*$(,)*) => {{ + #[used] + static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ + $({ + #[used] + static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection; + ::core::ptr::addr_of!(_SUBSECTION) + }),*, + ::core::ptr::null() + ]); + _SUBSECTIONS.0.as_ptr() + }} +} + +/// Thin macro to declare a valid [`VMStateDescription`](`crate::bindings::VMStateDescription`) +/// static. +#[macro_export] +macro_rules! vmstate_description { + ($(#[$outer:meta])* + pub static $name:ident: VMStateDescription = VMStateDescription { + name: $vname:expr, + unmigratable: $um_val:expr, + early_setup: $early_setup:expr, + version_id: $version_id:expr, + minimum_version_id: $minimum_version_id:expr, + priority: $priority:expr, + pre_load: $pre_load_fn:expr, + post_load: $post_load_fn:expr, + pre_save: $pre_save_fn:expr, + post_save: $post_save_fn:expr, + needed: $needed_fn:expr, + dev_unplug_pending: $dev_unplug_pending_fn:expr, + fields: $fields:expr, + subsections: $subsections:expr$(,)* + }; + ) => { + #[used] + $(#[$outer])* + pub static $name: $crate::bindings::VMStateDescription = $crate::bindings::VMStateDescription { + name: ::core::ffi::CStr::as_ptr($vname), + unmigratable: $um_val, + early_setup: $early_setup, + version_id: $version_id, + minimum_version_id: $minimum_version_id, + priority: $priority, + pre_load: None, + post_load: None, + pre_save: None, + post_save: None, + needed: None, + dev_unplug_pending: None, + fields: $fields, + subsections: $subsections, + }; + } +} From patchwork Thu Oct 24 14:03:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849133 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 967ACCE8E7A for ; Thu, 24 Oct 2024 14:05:34 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRY-0007CZ-56; Thu, 24 Oct 2024 10:04:04 -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 1t3yRD-00078r-JN for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:53 -0400 Received: from mail-ej1-x630.google.com ([2a00:1450:4864:20::630]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRC-0003ww-55 for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:43 -0400 Received: by mail-ej1-x630.google.com with SMTP id a640c23a62f3a-a9a0ef5179dso120046166b.1 for ; Thu, 24 Oct 2024 07:03:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778620; x=1730383420; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=hbU95iGb43ykjR6uVl1fMgZcXOfT8jfUmlrwgLmbMOQ=; b=X+iPHOIFgB8rwSu4oe5M/bIl1rtDoPymBneOA6eRH/SGvGyvOYlGn9Zer/6QEhO/bc Lmtk6ULwbBCOhwo6mOfWopwayFJ6Kwe0qj53zwrahnS40IqbJRCKxcVdiHM4UWQunIXK Zdq4W1oxNxhjlN7eySIzgy/F4b0/m5JhIJsXkd/AsTwd7xRgxrW8LHcXRMT7CxbkeWAY TG0eCyibaeuzGRsgCwZolIAns9DGJXnpST87XfVfPIo3lnnYgKTu1ifquT/GTW9J3sB3 V1UDliBMO4v1P2L1nKKPw79LrRfUN5G9T71UqZG/oYDJrvBm7p8WxcSIjTbn8QnLbBKH u8ww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778620; x=1730383420; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hbU95iGb43ykjR6uVl1fMgZcXOfT8jfUmlrwgLmbMOQ=; b=BT3yvV9/gGSyGR5apfSY0MHLZFlPA7rKagLdmkTpeaw80EuHrJOOC5xvBl8ibMFIWf zK3QOCSRbmdUs6Wm4GfJhVEU5TFLB+Nf72hYkbsrsX/ILztzf4tz3pHOaM70HBNsyoU6 BLXWAuApnZaMXe55NFbZOjFDFxqXjmT0vU6sTtEM/d4fNT8rGpTQV8R1kH5J+S4YSiOl Urm7SfB1FlliHBIEHFZ1k5R1fU74T7PC3bR4eHs8bW9EKens8GWf4MRkOLCGgiGS5/O7 48KD71MG+ewlmlMkh9MTFGzMimY+YTBc1QGIRimipWkHHmjq+R3s8V0e7E4IJVCTJO3L c45Q== X-Gm-Message-State: AOJu0YwNxGiwuAViVhmLkuTZg5HHCaCc7BT4P8otjfz2AXeV4IXtiEAe BuOl+83/A/9arsOwkqERL8UhdgculCwwyH4u4a0eUS5FK1K0nht/IKP8YIhPRCo= X-Google-Smtp-Source: AGHT+IGhCxLJqvJjntDHNM49xQFQgHimhRC45q6AuV8nHKxtr49b04s9hnT4dgW9VYj1o7UoyDb3PA== X-Received: by 2002:a17:907:7e8d:b0:a99:fb10:128e with SMTP id a640c23a62f3a-a9ad275718amr213407166b.36.1729778618544; Thu, 24 Oct 2024 07:03:38 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:38 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:03 +0300 Subject: [PATCH 05/11] rust/pl011: move CLK_NAME static to function scope MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-5-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=1107; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=aBBqr/qTxXGjUVQdy1uoZ/LYcMTQXrtSqv1QzWzz+ig=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9qZXdsVi9FOC9ONWVmUXJkQkJyenZIOEhaCm11d2xNL1k4blRaQXZiQVEw U1dKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG93QUt DUkIzS2Nkd2YzNEowSG1KRC80bHpOTFZDL3hMQTRyOE8yMWs5OVM1S3pWZWNXZUViOWJDbFAwMg p1ejd5V1IvMGxyTERCakdKbUJGLzFTQ3BQV2RBMWJVV1FybytwQVlsNE0rbzcxRUpuaVgyQ1EzQ 085R1NHK015ClZBVWVoZERZVnF3TnNBYkgrN2NsemtxeTNwNlZBU3pRcStLUi9oOFlWYVNDTG5M cHlsd0R2L21abldQUHk3Q0oKNVh6UkRybnhHNDBnSkJmdHZTckhNY25HQm9jaVg0RDU3SjFlRDh TSFRsYU9HYjJTUGxaSHozQmdMdzkweEVxLwpaUFFHbUlldlZaWjl3MExKRGtES2ZlakZRc1FzZ1 Z1eDVnUUFxSUxIaThuV1FCYjhxMkRyeGI4RDFqSFlVbFJRCi9Md2NQVmRaM05sZ2pFcDVWNEQ5N lNsbXZYV0xYQmpHWStadW12UDR6SnVVVmJQYWZCZU54c0h1US9MRmxtQUMKeWdNakNOaUtBQ0Nn OERwblNvamRUemF4VGxlanpGdmJJUGVnUVROWGhwRmhYOC9KeklQdm5UVnBkVHltcSs5RgpPUTB td21xeHcrcE42NlRMYzBBbW83ZFdCWE1CVEx6L1pQYmh2U1o1azN5S21xN01OeDFrOWM4WkdVMW IvTVoxCnNaYU41eFkyeFo5ektxUXNGZDVzODB4YkpvdEE0Lzc5L29RV3RHVW9nM1lobVlqUmFJS VFDQ1dWTDBKSjRlN1MKL0lBRGMyc2Y5WGxjajk0d0NnRGQvMUVzMjBWZ2hnVkxUczByUENzWndT RTNvYUFpMWpBUXQweDZhaUdndGNRMwp6azFtbjJPY3NobXNVbkdGRWVWU2tXMkJ6R3pQdU13Q1V oSjNpblpYK3BoZzRteStxYkVjb1FEeGNoR2gyNFJYCkF5QndzQT09Cj1iTEExCi0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::630; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ej1-x630.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 We do not need to have CLK_NAME public nor a static. No functional change. Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 57dc37dadef631fbccfa3049a3d8701b4e62b5b3..115786f9fa7f03c16cd44462cb7df5623ba3a6d7 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -152,6 +152,8 @@ impl ObjectImpl for PL011State { /// location/instance. All its fields are expected to hold unitialized /// values with the sole exception of `parent_obj`. unsafe fn instance_init(&mut self) { + const CLK_NAME: &CStr = c"clk"; + let dev = addr_of_mut!(*self).cast::(); // SAFETY: // @@ -255,9 +257,6 @@ unsafe fn post_load(&mut self, _version_id: c_int) -> c_int { } } -#[used] -pub static CLK_NAME: &CStr = c"clk"; - impl PL011State { pub fn read( &mut self, From patchwork Thu Oct 24 14:03:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849139 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 A2F8FCE8E78 for ; Thu, 24 Oct 2024 14:06:08 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRX-0007CO-E7; Thu, 24 Oct 2024 10:04:03 -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 1t3yRH-000792-6V for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:53 -0400 Received: from mail-ej1-x631.google.com ([2a00:1450:4864:20::631]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRF-0003xB-Hs for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:46 -0400 Received: by mail-ej1-x631.google.com with SMTP id a640c23a62f3a-a99ebb390a5so383654866b.1 for ; Thu, 24 Oct 2024 07:03:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778624; x=1730383424; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=WITWBs3eKYPfFgEanUhD+BoP6AjEmCFyXtxJHZOASQo=; b=eQDGlCVyR+czNw0e67pIM8U3C414XJb/npqTkn/7jCLbgu9FEfeAD37DSDKFvYGSeI n+x0N4qC4f7iKhrteoVy+0Sha8u/1mHgrPLRVzb1AY5+FT1DTgEjjm9paS5TM7Y2PjP+ wlmU6CZlAHBA1aZEkbY3aKuGdolOBOzyh0Cy9rKSdCCh/lkmuhhA8CgFUVuNDQ3bjwLD aBn2NKPdOtD4kt5m/sv2JaMS/QDdkh+R4i/OT7khSYfiR75hQTB7wVuZeF6icOdcwspb T2Hbv/6zYpHomx/ua4ckJDtIPAxR9PA+PJhdGIDnBIx8NYK1zHr3+wmrEKWH+SQHxFW4 rXNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778624; x=1730383424; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WITWBs3eKYPfFgEanUhD+BoP6AjEmCFyXtxJHZOASQo=; b=eTOdGpuraLvzcgXeEd/a6G7ccCmR6nDXoOvc7OaELvznl4LLJ4QBYQ89u5fwcBGerQ latioMl2dbQmM0TVtjY/eWT+eivxfjJw9Oju85LQ5NIKMcbFUz2+JtlqNkLDRSUgr+dh arSGGCPrLCMz5/5hUxhIOuA/eBmCroqmIgJWX++G2egY84Evl2XCe9wm83G20Z3Pot6G KpkR20Ch4F24162JIBaycADPSdy2NWjnIN+YrHyNrFolxE+/aPRpWxMTccT8NzO4SYJU u7nJsEEiKkGWg/EqqRnbH3nvacDZroskVasRDZhzZ/LM9+aKi4KtjeVWjIkw4MKGkh/e YwHQ== X-Gm-Message-State: AOJu0YxtUfQ/L7ZGa/OL0uiER6VMg4B1xaMn75dcUDb5BYWRWl/IZT5p 3a2lCLprUQmQrz2S5lOJJ8NyXxvD+6Yokz8+p2HxsovdCDv9dn9xtwU/rt4ZhXY= X-Google-Smtp-Source: AGHT+IEMYBiQMjr2BL3EoE9SxB2HcVAYOfP34g8IZx7/rITMhbDhJHUlXGemCYov7ZZm+VDQG/Bl5w== X-Received: by 2002:a17:907:94cd:b0:a99:5587:2a1f with SMTP id a640c23a62f3a-a9ad19c145emr259934166b.15.1729778621456; Thu, 24 Oct 2024 07:03:41 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:40 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:04 +0300 Subject: [PATCH 06/11] rust/pl011: add TYPE_PL011_LUMINARY device MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-6-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=4216; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=hUIDgKKomo5D5BKIdaGye033FBHKY8t65zO/UI15g8k=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9qQ2lVMlh0SVNieTVIa2pFNGwwTjVIMDFBCkptQkZrTGU4U01GakV5aGN5 d2FKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG93QUt DUkIzS2Nkd2YzNEowQTAzRC85a3NsRFlPelBPbll2RFdleFFkRnJTTXYwelllNytWMWFZOHh2Rw oyeUdNZDl5b2E3SGV1VTVTWjdqbTN0SDM4TUtrSE1qZVExWFFmVWYrTHVraEZNYjNBUWQ5TkNnM jJTNDJ1anBQCmt3bldxWldnY2YzcnUzMTlaRDIzMGFaUVB0RDVqNlY1MkpCbHdFR0o1SFhMWEEz eWQ4eDVjb3V5UzZJQXFLeTEKOFA2b2ZieFM0SnQ3WEhXMm9mL0p4VnVNS2wveWgzNkFRUzRVdEt FT2daSU1IQklaZzI0eTVWT01SeENPdW5zbQphZHlCMHg4clBlUUdIZmhRUkFxOG1GNGhHWlVpaH R1QlJPYjVXUi8zRnlCQmRsZGtmUk4wTGtROE9CK0xCczJTCjQ0ZmhDN3MxN251d2UwNlN4UVdxZ HNRdU96aUE0VXgyWCs5YnFqQ3dJY2JlYmo4S2dMWjNJK2U4UVdCWUdLcloKeElWd2FFRnkxNFRL L1FwejloNDE2Zk5mcHMybkMxYkV1Ky9PZm9TOXlIbC9SVHlWUitNTVAzckVwTUIxNUQ1NQpRWXh ReEphSm42Z2l5Y0cxSU9ZN3ZUblhVQnlmUWtYWFdZMEdVUzR6UTlIK3NtdHJlKzdjYm0vNm5oQm NsSm80ClBmNmd4cHJoNStKdXdPTUg4V3NwQzBsR0xOdk1oc1hHV0k1eUlCaTNtUW9XZTZ0dytCR 2pqYmU1aytNV2V0TmYKaFNGMm9ZUDlvRzJ4by95NG5UNVF3bitINUZYaHVVUnZ4M2lZWWR6M1BL MVFyWkdINGtPRGpwMFpkM1RiWWI3aApmYVoxOU43dHVkdUh3NXFNUllnZG1hRFQ2SFZsdHNRRnh GbmJhQXpVbkZ4cWQ1UndvZWZ5TUhiWTEzRmZXbE5DCmhHMi9TUT09Cj1iT3FTCi0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::631; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ej1-x631.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Add a device specialization for the Luminary UART device. This commit adds a DeviceId enum that utilizes the Index trait to return different bytes depending on what device id the UART has (Arm -default- or Luminary) Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 59 ++++++++++++++++++++++++++++++++++++++-- rust/hw/char/pl011/src/lib.rs | 1 + 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 115786f9fa7f03c16cd44462cb7df5623ba3a6d7..3aa055dee4b10866a624505a9d05ef1ab8182dce 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -20,8 +20,6 @@ RegisterOffset, }; -static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; - /// Integer Baud Rate Divider, `UARTIBRD` const IBRD_MASK: u32 = 0x3f; @@ -64,6 +62,29 @@ extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { }; } +#[derive(Clone, Copy, Debug)] +enum DeviceId { + #[allow(dead_code)] + Arm = 0, + Luminary, +} + +impl std::ops::Index for DeviceId { + type Output = c_uchar; + + fn index(&self, idx: hwaddr) -> &Self::Output { + match self { + Self::Arm => &Self::PL011_ID_ARM[idx as usize], + Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize], + } + } +} + +impl DeviceId { + const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; + const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]; +} + #[repr(C)] #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::Device)] #[device( @@ -134,6 +155,8 @@ pub struct PL011State { #[doc(alias = "migrate_clk")] #[property(name = c"migrate-clk", qdev_prop = qdev_prop_bool)] pub migrate_clock: bool, + /// The byte string that identifies the device. + device_id: DeviceId, } impl ObjectImpl for PL011State { @@ -267,7 +290,7 @@ pub fn read( std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&v) => { - u64::from(PL011_ID_ARM[((offset - 0xfe0) >> 2) as usize]) + u64::from(self.device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); @@ -660,3 +683,33 @@ pub fn update(&self) { dev } } + +#[repr(C)] +#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::Device)] +/// PL011 Luminary device model. +pub struct PL011Luminary { + parent_obj: PL011State, +} + +impl ObjectImpl for PL011Luminary { + type Class = PL011LuminaryClass; + + const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); + const ABSTRACT: bool = false; + + /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. + /// + /// # Safety + /// + /// `self` must point to a correctly sized and aligned location for the + /// `PL011Luminary` type. It must not be called more than once on the same + /// location/instance. All its fields are expected to hold unitialized + /// values with the sole exception of `parent_obj`. + unsafe fn instance_init(&mut self) { + self.parent_obj.device_id = DeviceId::Luminary; + } +} + +impl DeviceImpl for PL011Luminary {} +impl qemu_api::objects::Migrateable for PL011Luminary {} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index f4d9cce4b01f605cfcbec7ea87c8b2009d77ee52..5516e018b4bfebe5175c515e5aa4598f54b39dfc 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -45,6 +45,7 @@ pub mod memory_ops; pub const TYPE_PL011: &::core::ffi::CStr = c"pl011"; +pub const TYPE_PL011_LUMINARY: &::core::ffi::CStr = c"pl011_luminary"; /// Offset of each register from the base memory address of the device. /// From patchwork Thu Oct 24 14:03:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849138 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 3CBE5CE8E7C for ; Thu, 24 Oct 2024 14:06:06 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRW-0007Bb-Au; Thu, 24 Oct 2024 10:04:02 -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 1t3yRJ-00079A-G9 for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:53 -0400 Received: from mail-ej1-x62e.google.com ([2a00:1450:4864:20::62e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRH-0003xI-PG for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:49 -0400 Received: by mail-ej1-x62e.google.com with SMTP id a640c23a62f3a-a9a0ec0a94fso122898066b.1 for ; Thu, 24 Oct 2024 07:03:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778626; x=1730383426; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=KCn73+YkWwVjQeZCOwMpqD8LHrnSQOeaidvNNKE/Tbg=; b=baid7ihQiUxezkPyj1QXwB7iZV9kJjX8e7B1tbK7Ir3ldidu/1+VEWdZHc8WHo2FRp lOeMwa8UxA/odZ+EXak0i+MzG3AYq2A4LnIxPNUYscf/h9E0uuEdkWkL8I0do3ffnkkL 6c0syXUeAVrZVvA1G1Qh3KtHWPd3EQn4/5IhJcqAaSJ+wx5f431gtR23/7bBBDQQyrff P/Y2jNK26K3MV0dcL/0bOx8oL7uAWjQUBOhvQy8KRxzB3TWId73cMH6uX+b+61EJlakk cgn5dyrB3iWa0pwKsMdYbuzFDkqXGdnll0nBF61FvR3RdPFXB8nZqCvjuWc5oI9Z4wqS Wyvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778626; x=1730383426; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=KCn73+YkWwVjQeZCOwMpqD8LHrnSQOeaidvNNKE/Tbg=; b=DtxC0al+DU9POHfJjIQMJHaCxP15tjkE5b1HkWuU1VE/J6QyeUVYnlgvo83RVZi3zM 8jdgaJsMkvpIHq20LdPUwyv8uCoESPo13SLmgTEmKpv32G/TTg/odEIVE29eMIoVOEME c4munONUoj6NGV2713lBgTjRR2ZkVg/7oLHyfStfi46YAnOZ71WMVjNi6p0CqEDIneXu Y0TwBcPBj4vKGW58tyYsEdmTdno5hvgjigMR7uIqU6uupGht6x2tsBOJdRr8JV8ThFvV JvEAcTh0tfm1qMJ1ORfqpTHoKHCt2DIX3tuGuqd424HCExnFQZFwOxgpTh7LlPeQsqiW +p4Q== X-Gm-Message-State: AOJu0YxoaTMn5BwEgVbda93XYdNUQe6aKYH9uejMMQ+T3QpqGFEe5wFa 8Xah0Dq0d8MHqC/f2aUfd83yxFZZbPc/z/Zw2it1R4OF8PGnO1iphpiwxxJ28Ng= X-Google-Smtp-Source: AGHT+IEwX1dxqhQrGkfgK6YAEvKzZoJXmTBgXHkYG5P2OIcSO99hXifidTHt70f2mij4Idcd1CeErg== X-Received: by 2002:a17:906:794b:b0:a99:8178:f7ed with SMTP id a640c23a62f3a-a9ad2711849mr199678366b.4.1729778623981; Thu, 24 Oct 2024 07:03:43 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:43 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:05 +0300 Subject: [PATCH 07/11] rust/pl011: move pub callback decl to local scope MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-7-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=5176; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=t89qGxVY7Bbmwy9c+qHVuDpEPvucQqPydBhWY3bNDGg=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9qY1RxSXBLa0N5NGh3RFc5NEJjeUliaXFwCmdwc3V0ZUZNbFNxaDdGVExO N1dKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG93QUt DUkIzS2Nkd2YzNEowTEgzRC80NVhmUFFWSnFrMHB1Zmd4ZUluaUxUK0RrTjhINis3ekNHajhSMQ o2QWNxc3VxZ2hQaEhkUWJ4RVhSZUxVT3VqNXQ1WUVFbUlpZjlCRWJSbGludnpxZVJsdHRyN2huQ m5hUmM0SStXClh5ejI1d0dMSWlzWXdvdy9WcDAzejVJK1ZlSmNEdG9NTkxQc0xtZTRCTTN5Rkov VHo1UmdITGVTMWlPcmxRWmcKWFpZSmFKSHZWM2prbkcvOWhRb2MyY0UwcmVhYnI2aDcwWGJTQ2R 1M0psMThxK2psenlwcU5RaUVlWDJibUlzaApoYTNUa0dsb1Voa2poOE5ZckVRUEtZTzZQR2svSW Z3cVFDdmh2WmtrMkkwSjcxcUY4MVpyOG5mUzVhOGV5Nkx6CnRvNE8yVUdHVzJJL2tqNmxSM2Fmb kpVcyt2VWtQWGtlTjlxN21wQTI0MFc3NzhqYlJJSjJscGsxeThwb3o0eGcKbjE0bnlrM3pKUnlq bmV1bUhxVEhVTHZYZk5qUDM4U3pxcmpYTS94dXovRTVoaWlIYkk3WmlxYkpUL0F1RndabwptaDY 3S3Y2elJNaU9uS1ZkTVRhMWhDTnlBRkZyOHNaQW14RWJ6Ynlob1hoSVVWaXBQMXlVK1lNN1FZQW YxS2dtCjVuLzc4TDgxanAvYVppcEFqc1JCOWs3YjlvVTQ4U3B6WXBOR2U0TjZxTnQ2U09VQjU1M nFkN3JlNUQ1bFRzSmwKQTVhWGRIakdBa1U0ZGFQTytqMU0vbW44Q1U2V3ZvQUN5aG5oUjM2Y2NN T05zMWdVNTRIenpyRllwaklGeXgxdQpCQ0RUQ1lIYmo5R1o1cklENUJjL0FIYU5rNlh6aUcvN0w rQ3FESkUrdmNKNG5VMzk5bXA5ZGMwclhHZlhkUU9BCmpCLytFUT09Cj1ybTNjCi0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::62e; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ej1-x62e.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 extern "C" callbacks in instance_init() do not need to be public. Move them to local function scope instead. No functional change. Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 104 +++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 3aa055dee4b10866a624505a9d05ef1ab8182dce..75399fa6352916fa9cc24164af0ea2e20fe29399 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -219,6 +219,56 @@ unsafe fn instance_init(&mut self) { impl DeviceImpl for PL011State { fn realize(&mut self) { + /// # Safety + /// + /// We expect the FFI user of this function to pass a valid pointer, that has + /// the same size as [`PL011State`]. We also expect the device is + /// readable/writeable from one thread at any time. + unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { + unsafe { + debug_assert!(!opaque.is_null()); + let state = NonNull::new_unchecked(opaque.cast::()); + state.as_ref().can_receive().into() + } + } + /// # Safety + /// + /// We expect the FFI user of this function to pass a valid pointer, that has + /// the same size as [`PL011State`]. We also expect the device is + /// readable/writeable from one thread at any time. + /// + /// The buffer and size arguments must also be valid. + unsafe extern "C" fn pl011_receive( + opaque: *mut core::ffi::c_void, + buf: *const u8, + size: core::ffi::c_int, + ) { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + if state.as_ref().loopback_enabled() { + return; + } + if size > 0 { + debug_assert!(!buf.is_null()); + state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) + } + } + } + + /// # Safety + /// + /// We expect the FFI user of this function to pass a valid pointer, that has + /// the same size as [`PL011State`]. We also expect the device is + /// readable/writeable from one thread at any time. + unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + state.as_mut().event(event) + } + } + // SAFETY: self.char_backend has the correct size and alignment for a // CharBackend object, and its callbacks are of the correct types. unsafe { @@ -611,60 +661,6 @@ pub fn update(&self) { /// # Safety /// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::()); - state.as_ref().can_receive().into() - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -/// -/// The buffer and size arguments must also be valid. -#[no_mangle] -pub unsafe extern "C" fn pl011_receive( - opaque: *mut core::ffi::c_void, - buf: *const u8, - size: core::ffi::c_int, -) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - if state.as_ref().loopback_enabled() { - return; - } - if size > 0 { - debug_assert!(!buf.is_null()); - state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) - } - } -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer, that has -/// the same size as [`PL011State`]. We also expect the device is -/// readable/writeable from one thread at any time. -#[no_mangle] -pub unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::()); - state.as_mut().event(event) - } -} - -/// # Safety -/// /// We expect the FFI user of this function to pass a valid pointer for `chr`. #[no_mangle] pub unsafe extern "C" fn pl011_create( From patchwork Thu Oct 24 14:03:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849135 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 39C62CE8E7A for ; Thu, 24 Oct 2024 14:05:42 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRY-0007CS-2H; Thu, 24 Oct 2024 10:04:04 -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 1t3yRK-00079O-4O for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:53 -0400 Received: from mail-lf1-x12e.google.com ([2a00:1450:4864:20::12e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRI-0003xP-JE for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:49 -0400 Received: by mail-lf1-x12e.google.com with SMTP id 2adb3069b0e04-539f58c68c5so1719229e87.3 for ; Thu, 24 Oct 2024 07:03:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778627; x=1730383427; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=+f2xVZQLPWqFbFB+BWu+Obk2BX5gjbKzI8BtdihEOhw=; b=x757YAgWhbgySev7v88ehyBUJcBK9gMUzbg7l129YYQaw8E51akmR11EYuu1pPtLhL G8ABzozRA3WPB3hFEgL0QNtTF+FdhWYh0aZsqJzhWMfyZD6HO1YfyBviCmwaSVsTDFjB 9w1rZe3qOmVuV1D7rrB2O9+uIjlRae/KkPeOZ3x7P/rx+z9uAXeoRjFAw/vLzW6UxCW+ nMlicj3kY92c5oFKlcWUUOr616inv1XKu1cUZyUjvh0CwbBqQpxfRJYm2BuOYPjpYGCS U9tybzEXCbltR+a10/6e4CFDBgYF9xDNnCbfqRLQjCa++0VsGXboynsFZOqJyRyT21Kq KQHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778627; x=1730383427; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+f2xVZQLPWqFbFB+BWu+Obk2BX5gjbKzI8BtdihEOhw=; b=BS96wOYMrwXntGNHMrq2EeioxCeUWTtNGR86CBIqcLTT9chTaE5+mxDEERx8413Jkh RCecjqHQPFIPwsgv1+6a5inpTNYImjb4jEkp6Vo4mf5CzNGzFMz0a7uk5dTWTQrIX7wn uRYmiBFzsZnRcHIOy4oagLZRFTFWmXF45ELm+ujiB/bI4uwl5G+QHXD87Pj+904aPQ81 gys1yiV0tQi0B4UIZiZxslsXDQFKFdLfQ1TlzlHVMB9yGWbHehoCeoCljUrKEdGhp7PX xZZ3TopjoyfSLoIe5p7MQifWtyoPw/doz9LdvKBMGIsGm0/jzRFsGn5gilF59fhE5Q+I IS4w== X-Gm-Message-State: AOJu0YxahGxMAPHLSaoS3otJnB3b5lOUQD23UzV4ughcuZxU6+FTxWBu keLuyWy7XTjx0Mz627c/i1tj46/eSaM4KzS0FhKC+INKVreBPdrkj5qsJZgG3H8= X-Google-Smtp-Source: AGHT+IFNTpaW98rOnxMm3Y8ZLBNHbKqK4YnC2gjjC0lC3jKFXMUfWGFAHXaNRPhkcGtxIe0uKymlZA== X-Received: by 2002:ac2:4f06:0:b0:53b:2105:18bb with SMTP id 2adb3069b0e04-53b21051ad7mr3631272e87.51.1729778626320; Thu, 24 Oct 2024 07:03:46 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:46 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:06 +0300 Subject: [PATCH 08/11] rust/pl011: remove commented out C code MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-8-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=3106; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=BgYU2Od7c4AJykhXahujBUfmPiu+WkyIxURQdVBKM9o=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9qRHdJNXlTUzlzK2RlZmF3MktMbzRJOG9uCkZvVExPM3FtMGcwUnVWZStL RHlKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG93QUt DUkIzS2Nkd2YzNEowSUMvRC8wVXdGNFFPZEZPZzBLY0tkeTFmU083TGZFYXBqR2RXZ1Bsa0VEcw pBZ2FvdFBVZGVaTkpHMk15eDJ4dndHbDZoeWlzaGlYeHltRy9LZGo3dmtRNC81MkRCYlhUUjhWb TdlYkc0UE4xCnZyNExuRlhyL1lqSncvcDdoM0tLWjhUN1BGT3M3NzU1QnpjcDVGNitzWk1HdlZQ ZjN0QlpDbGFqR043cUhFdjkKK3pmN2ttSnZ2ckduZ2hvZlFRYWhBRjN5dHh1TGZ3bG1xWGlZUGl 6c3ZIOU1MMmF6Sy9nUGg5bkxCeUtrQlNUNwpMK2NTVTZlUVA3K1RZUEJFVXdvb2dqT0JsSnE0RX dnYmt1aE1iaFkzYXpyQU9TeVB3Z1FDT05YMVdJYTE0Ny9XClo1Y1ByaldidDYzVkUxdEw3RXZkU 2JLYWpKS2xrUnRzaTBhSlFpeDg4UzZDYjJIR3JFZ2ZwQThKWmZiWWI1MXQKU3dMaFRQVGVpbjVS bXl2c2xLU3RjMWhyMTVpZHFQTTIzVXVqNno4K3RlenNuVzlMZEkrbHg1VVpFQmtobUlGZwp0WXN 2R3VhNnpzWUVKRHFKYmtLUkNNQ3N6N0M3UE9VSkdQRWFNcG9GckVrc0RrdTljQ2F2SHcxQld4Un ZFVG1NCjhPcnVlWndWd0VNdjBlOWZYUlA3TzFJc2R5QmJsZ3F5VnhqM0dvQ3hyVEJ3ME1CYnlZU nVlZmMwSHc0QzROMHEKbTYxaitNeEJaVndEanNhblA4M1F3WlUzZG5wZFVqWUM3MjFBeXYrcitT N28xL2NnYlVUd3p3a0tPbDFPQzdTZApwd1VwcEtPWldaMnRTU0pXbGd2ZzJRbmRkSm9icHJjNjI 5M3NOQUtKZ3hOamd6aWJVTnZuTmZTNnEwSHlpSWt1CjRWbnl1UT09Cj0rVS9jCi0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::12e; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-lf1-x12e.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 code juxtaposed what should be happening according to the C device model but is not needed now that this has been reviewed (I hope) and its validity checked against what the C device does (I hope, again). No functional change. Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 75399fa6352916fa9cc24164af0ea2e20fe29399..cf1964fecdfd6d9dae3378890aa8b515a1ddc036 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -347,7 +347,6 @@ pub fn read( 0 } Ok(DR) => { - // s->flags &= ~PL011_FLAG_RXFF; self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { @@ -355,11 +354,9 @@ pub fn read( self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); } if self.read_count == 0 { - // self.flags |= PL011_FLAG_RXFE; self.flags.set_receive_fifo_empty(true); } if self.read_count + 1 == self.read_trigger { - //self.int_level &= ~ INT_RX; self.int_level &= !registers::INT_RX; } // Update error bits. @@ -529,13 +526,6 @@ fn loopback_mdmctrl(&mut self) { * dealt with here. */ - //fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD | - // PL011_FLAG_DSR | PL011_FLAG_CTS); - //fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0; - //fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0; - //fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0; - //fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0; - // self.flags.set_ring_indicator(self.control.out_2()); self.flags.set_data_carrier_detect(self.control.out_1()); self.flags.set_clear_to_send(self.control.request_to_send()); @@ -546,10 +536,6 @@ fn loopback_mdmctrl(&mut self) { let mut il = self.int_level; il &= !Interrupt::MS; - //il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0; - //il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0; - //il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0; - //il |= (fr & PL011_FLAG_RI) ? INT_RI : 0; if self.flags.data_set_ready() { il |= Interrupt::DSR as u32; @@ -622,10 +608,8 @@ pub fn put_fifo(&mut self, value: c_uint) { let slot = (self.read_pos + self.read_count) & (depth - 1); self.read_fifo[slot] = value; self.read_count += 1; - // s->flags &= ~PL011_FLAG_RXFE; self.flags.set_receive_fifo_empty(false); if self.read_count == depth { - //s->flags |= PL011_FLAG_RXFF; self.flags.set_receive_fifo_full(true); } From patchwork Thu Oct 24 14:03:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849134 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 63FCACE8E7A for ; Thu, 24 Oct 2024 14:05:39 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRZ-0007Cu-Ep; Thu, 24 Oct 2024 10:04:05 -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 1t3yRO-00079u-Ca for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:04:00 -0400 Received: from mail-ej1-x635.google.com ([2a00:1450:4864:20::635]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRL-0003xf-TO for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:53 -0400 Received: by mail-ej1-x635.google.com with SMTP id a640c23a62f3a-a9acafdb745so196016766b.0 for ; Thu, 24 Oct 2024 07:03:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778630; x=1730383430; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=8Qgg42N8SuToqImklsCIv0xNcAbCllJIjfd+s7ENKDE=; b=YvwCchidzJFAYBLkeCggH3glPoFBSqVKI6wNXScIUyeEPtfBJofvenTSA9YFJd/zC3 M4iev+BUW95yK35WAQY7omYfYcfp7DfsisNi5hbIaTV4lLcBqsfoALfu8hwOdtXkld87 yIYz7Qqlvf6dB03QULBMjVhKo9/jGP9yoCSvanvT1TDlXURjHoO2Gb8Liu3ADTG28d4o 3tmvOlipagZEEFEOxCoR47+w9TEkTAhR2gsiJWX6u8H1z9Mn2YXAx9G8B4NOqWDPGlAi 1LiZgeUNKWc7h7Um/KsDm+5xPle6FiExulsXA8YM3VykoNdvulySDfYH7roZn7GHrGb9 J7Ow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778630; x=1730383430; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8Qgg42N8SuToqImklsCIv0xNcAbCllJIjfd+s7ENKDE=; b=RxW3hg7ECeQTh1hqZkW+Odz+HpXh4T2SJOJucDmEUqgyOkON68FCx7tbNmNGNz4IZD IKZPVvT1QA81sEU9eNSaPK0k5Pvs/C2QLGs/t/+UX7NbmvMRHca1p5CUYHljuam/QX7t taQmNOBusK35Faq9ZTjZ/M0xZR511pJjVghgwtXA2Yel1v7wwNN+Tf4FIGzmT78pgUYu de9eV8dBix0pfL2kTyrMkGHdxV6HXMwG/fZecX35Zy1/gus5n869Y1er8RCNGYFfQesI noerPOCWsh/3w9qt/V/Nnnyszs0z/hX3xDqkTGLZBPSu/9Lr6uGy9X0L5Mfs48i5EuqT L+FA== X-Gm-Message-State: AOJu0Yyw414YxajHwX7No7Z9gzPUxxCUS7VAM6AMSw6IdlwCxJaR1UxU L4jrBhkCi/GAi9evGwTD0usQN3ZG0oxNjBVss0olWOFg4lNgCRHEzUptU38UjwE= X-Google-Smtp-Source: AGHT+IFXmSbnHJhHsJg856w4Wo5guxUQvrw+8QKSwCcJ6BivTfnSyFN3SBBQpSQeunB3GnJnVqSuLg== X-Received: by 2002:a17:906:db0d:b0:a99:fcbe:c96b with SMTP id a640c23a62f3a-a9ad1a5eee0mr225800166b.25.1729778629072; Thu, 24 Oct 2024 07:03:49 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:48 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:07 +0300 Subject: [PATCH 09/11] rust/pl011: Use correct masks for IBRD and FBRD MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-9-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=926; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=NKJsSY6EnujOR9+vYFNGnvQMyXTGi/+JMzK/b442IcI=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9qdjQrZ3JWeElma0w3ODB4SzlUSWpFd0s4CllWcFh1UzllLzNLenpETysw K1dKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG93QUt DUkIzS2Nkd2YzNEowRWdGRC85TUZuN0svSnNwMEVVUUxZTml2QTZKVVJabnNqbWI5ZSt2dE82VA pOU3plNUNISmY3SklzamM4d0ZaK1VqcUpVWEZSd3k3bkZCYUdvYVQ2MzI1Y3gyUUhuMHh0VUl0a m5LeHNKUDBLCkdoeWpPT0Q5bnRoTUVHRGE1dFN6UFgwbXNEbCtBN0JOSGxCcThSRXVNWGhlMDJt c0lKYU5DZFBSWTJJZzV0cDUKUnZvcUdqSVFEKzdxdFJSVVJGK1lrb2YvVHpoVUdJYzJFWFQ3QVp PbnJwNzZ0TjQvWlAzTDM3dy9nMjdrbjNUQQpOVzlndHA1NXR0eDMrN3NENHp6SnJLTVF4MWdmMX FlOWZmcEhoZkxQYU1qWVp6SE1iM2FnZTUvQ0pBMnhiZFFVCjVtOXd2cE5DT3hMQVZhekZHYkx5b nVFNkZiMWtiZWNTMWMrRjdQYTRFR2cyN1paMTNodFNyazNJTXB6QkdId3cKeW5WRzNrTFpRNVdQ RGtvVjlTbFdOY0lKQTJVY1BhTXJESlRic05NV25YVGlQQWtEYnMyR0lXUnhCSm84QTR6NQpVUVF CQzRieHAxNXRDOWkreHc4T2VEWkRDR0h0VzkwL0E1TWNYTlNuTXRWRkgyaStLVGN4dktvQUZrSn FiMGU1Cmh4aGN5OGwyMStYcTZwcWlZbU5kMzA5eE9RcFlKWk1DMzM4MzV4K1QySnpVelZpQ1M3b lVFNEFoY3N5alRhYUwKQi8veTR5eC9tK0pmS0hzYTAyb0Q5SzBENjd2a3VpYVdjR2g4N24zbnZO aUsrQWhORUdPdjg2M29NV0VpeUMweApnYjdGYmxVMjV1djdyWGJuSW95c0RLM0k2NUdiTWhwNUh yWGp4TGNOOXFlbURZWjZOQlg0M1IxMm9wbGZ6NmwxCjI5eUxSUT09Cj0wVHd2Ci0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::635; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ej1-x635.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 Port fix from commit cd247eae16ab1b9ce97fd34c000c1b883feeda45 "hw/char/pl011: Use correct masks for IBRD and FBRD" Related issue: Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index cf1964fecdfd6d9dae3378890aa8b515a1ddc036..6d1353dafc14bfe73703b5cff7e1ff7659de220e 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -21,10 +21,10 @@ }; /// Integer Baud Rate Divider, `UARTIBRD` -const IBRD_MASK: u32 = 0x3f; +const IBRD_MASK: u32 = 0xffff; /// Fractional Baud Rate Divider, `UARTFBRD` -const FBRD_MASK: u32 = 0xffff; +const FBRD_MASK: u32 = 0x3f; const DATA_BREAK: u32 = 1 << 10; From patchwork Thu Oct 24 14:03:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849136 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 1CA40CE8E7A for ; Thu, 24 Oct 2024 14:05:50 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRY-0007CT-2e; Thu, 24 Oct 2024 10:04:04 -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 1t3yRR-0007A2-3v for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:04:01 -0400 Received: from mail-ed1-x52e.google.com ([2a00:1450:4864:20::52e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRP-0003xn-4I for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:03:56 -0400 Received: by mail-ed1-x52e.google.com with SMTP id 4fb4d7f45d1cf-5c9454f3bfaso1106930a12.2 for ; Thu, 24 Oct 2024 07:03:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778634; x=1730383434; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=P6v2Co+JCkBUSGoUJI3FUDr2BSiZdpBcLeYpZLITdCA=; b=QU7pUCNu5hr7fssiGl3qik1gmMhMfEQb39Vq7TK9G+cUq2o3gktZ3JK3AB6xDiBeQk 7zKAoJ0+mbVkkQiZHDHPNi0jzJtlZcofXr/QR/aZFTSUzWxWgL8YTIEOtKN5/VnJpa6y EaD8V9OOe5dEsLjo5id/xugCjwVgXSkcNjWHclYNzWGkLi5+swLeBAzSPkz2tH9YWdhQ go/n2pjZOWYUoXmqynEz+EXv428VWdew1+QLKgFS+xYuL1J66ntxkW/4kiwjS1FXUHD1 rsGtepe2ZP5afyLL1CMpGj1pDg73MSKZuAVvBY2T4REDTGFcdDRB4j4G7ynFz45tlfGz lbsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778634; x=1730383434; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=P6v2Co+JCkBUSGoUJI3FUDr2BSiZdpBcLeYpZLITdCA=; b=nYqbevQV4OCR1FjTwkMXeww/F8WaPl9+g7aIrEI+6TQooDp+hbcBQMlL4IADf2nKuo YXJZj3klDIruapv+m1IztprGhvOi5ovVdsaes7PwxmWherRVI2d6578oYPquQGcd/PVH YVbViryWBRuRNzgNpNaM8FVasM1EecSRdqK1VKh8WuqxbsarSVu8TsDOSoVfuZlGhohV 8Iw/nTZqs61fMnILu/nbzC6KQYZbavBi6sKpO7h5FWZ4Oji73JIEWtXMo48fzpVn8SwJ 1mr0+NDhlFr6//makfXtvdT9rWYOmxrPyeiwQ33zE7IeoA/Bta5SS0DNw5Da7zoVbnIL Cv+Q== X-Gm-Message-State: AOJu0YyTLXtRR9vNG9sDgChwRN0FyzrbZJFWp2w6//f+r6sjRxdjcf2q /2G7YDBwoVuFJWM5Fi8Ui3ErLnJiSXkEz9lEgpc2gTfjUY8Hmdwq35+1znGye7w= X-Google-Smtp-Source: AGHT+IFRnERUtukvp4G+Z711fIrDuHFwABPP3sJHPHNJiv+AZCggeRVFxL2S2nuDQr/qf9n6x43Xcg== X-Received: by 2002:a17:906:f590:b0:a99:f750:bf79 with SMTP id a640c23a62f3a-a9abf8aefa7mr629785366b.34.1729778631898; Thu, 24 Oct 2024 07:03:51 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:51 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:08 +0300 Subject: [PATCH 10/11] rust/qemu-api: add log module MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-10-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=7789; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=PnKwn5RfvPLyaFLPXzkFaOcLVrtk0owxj+F4Pzht9MM=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9qTndYdUxYWVhLSk5CcllMalZEVFkxTXY2Ck5SU2pIOGhkRzdFMktXZTQ4 bG1KQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVG93QUt DUkIzS2Nkd2YzNEowQnJaRUFDUHdvSVAwMnNqc3RtdXQ4NjJCVFdLM21rYXNPdlVyM0tRaWpJWg pmWkRCOWh5bFBUa1lGTVpFdGFPaU11K1I2VlhEMnU3b05wV1NQWVpKWmJUUzFqQmRDVk9mYmhvb 2xha3kyTExKClI2ZHNtbWJ4Rm8rZlkydjMyamNCSkQzd1pvREIzemMvWGRMY0V5ZmNEUGZTaGZU ckg2cFlYUHNJZURnOGZJS0oKUEw0ZzNIdlBCKzlkT2JtdUhsTUlmU2tWZ3VGQ3RORXRXYU52UVA wbTNpdmdYR2E0OXAybHJRaTB1WkRGK01iMApDQzBhSUtXbU5QMEptbENwNlFVMXp1bGdBdDFFYj N5dE1id0RFcUxWbmpicml0QVkvVkl1eTFQSHJreGQxRkFVCmNpOFlmVC9qY2ZWQXpBNFRoYkZMT k91b3pydUg0a1RtcDZ0dFYwSVY0ZEZhYXBLZmFiQTY3VTVCWU9Tdmd4aFgKUkpYS3hVQ3QvdDJo S2ovTkFFeXloZzlESDVDSENjRGJKRlNCQldyblZoUVZaY2pqcjZURVdKeVpNM2ZDUmlHMwpKbnZ HampjUGEwbklFVUNDMk5KWGVjVFVMVlNUamdBYU9WZFpRblJtNjhRM3B6bHp0SHl1UXkyTnh1eD J1SFpmCjhNTGFqV2xTcUVFUG9XKzFtUEtSWVg0RmtocTJXR1h5UTVUMlVvRkVHYVorb1FPYmpMa Ud4L3owVVQvZC9MeEcKV1crUmN1NmtQYlhsaDA2d3dDVFJLcGpyUjErRFU2WldFZ1ZWNUhUbThk RTRkRVVQVmRHVWthU2JKemJ4QTFFegpKTDNvTlR3ckhCMURLTFVMcTE2NXlzVE1talI2NG5HOFh HbE5GaUMwRmdWVk5JQStvS25QUGpMZFlNeEpTMmpqCkZKV25NUT09Cj1ab2VQCi0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::52e; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-ed1-x52e.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 Logging in QEMU is done by function-like C macros, which we cannot use directly from Rust with bindgen. This commit adds a new qemu_api module, `log`, that provides the following interfaces: - a `LogMask` enum type that uses the mask values from the generated bindings, and makes sure the rust enum variant names and values will match. - `LogMask` aliases `LogMask::GUEST_ERROR` and `LogMask::UNIMPLEMENTED` for convenience. - a private qemu_loglevel_mask() function, counterpart of `qemu_loglevel_mask` in `include/qemu/log-for-trace.h`, which we cannot use from bindgen since it's a static inline item. - public qemu_log(), qemu_log_mask() and qemu_log_mask_and_addr() functions that should act like the C equivalent: pub fn qemu_log_mask_and_addr(log_mask: LogMask, address: u64, str: &str); pub fn qemu_log_mask(log_mask: LogMask, str: &str); pub fn qemu_log(str: &str); It takes a 'static or allocated string slice as argument, but in the feature we will introduce better log macros in Rust that make use of Rust's format arguments. This is not really a bad compromise since generating a log item is not a hot path so allocating here is fine. Example usage will be: ```rust qemu_log_mask(LogMask::GUEST_ERROR, "device XYZ failed spectacularly"); qemu_log_mask( LogMask::UNIMPLEMENTED, &format!( "We haven't implemented this feature in {file}:{line} out of pure laziness.", file = file!(), line = line!() ) ); ``` Signed-off-by: Manos Pitsidianakis --- rust/wrapper.h | 1 + rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/log.rs | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+) diff --git a/rust/wrapper.h b/rust/wrapper.h index 77e40213efb686d23f6b768b78602e4337623280..8f76ef26f111d5e1f308268f445696acc7ddbfef 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "qemu/log.h" #include "qemu-io.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 11984abb878bef18be3c819f61da24ce1405ea59..a82ff0d39a2263d15bda312aa0a2d46c77b2501f 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -3,6 +3,7 @@ _qemu_api_rs = static_library( structured_sources( [ 'src/lib.rs', + 'src/log.rs', 'src/objects.rs', 'src/vmstate.rs', ], diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index d276adfb6622eee6e42494e089e1f20b0b5cdf08..c3eb464f66361ee2349e636c49e38d3a6b57ad97 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -29,6 +29,7 @@ unsafe impl Sync for bindings::VMStateDescription {} unsafe impl Sync for bindings::VMStateField {} unsafe impl Sync for bindings::VMStateInfo {} +pub mod log; pub mod objects; pub mod vmstate; diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs new file mode 100644 index 0000000000000000000000000000000000000000..50525ac6b7f49786c2975843b7dc70b91c18d5a0 --- /dev/null +++ b/rust/qemu-api/src/log.rs @@ -0,0 +1,140 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Logging functionality. +//! +//! This module provides: +//! +//! - a [`LogMask`] enum type that uses the mask values from the generated +//! bindings, and makes sures the rust enum variant names and values will +//! match. +//! - [`LogMask`] aliases [`LogMask::GUEST_ERROR`] and [`LogMask::UNIMPLEMENTED`] +//! for convenience. +//! - a private `qemu_loglevel_mask()` function, counterpart of +//! `qemu_loglevel_mask` in `include/qemu/log-for-trace.h`, which we +//! cannot use from bindgen since it's a `static inline` item. +//! - public [`qemu_log`], [`qemu_log_mask`] and [`qemu_log_mask_and_addr`] functions that act like +//! the C equivalents. +//! +//! # Examples +//! +//! ```rust +//! # use qemu_api::log::*; +//! # fn main() { +//! qemu_log_mask(LogMask::GUEST_ERROR, "device XYZ failed spectacularly"); +//! +//! qemu_log_mask( +//! LogMask::UNIMPLEMENTED, +//! &format!( +//! "We haven't implemented this feature in {file}:{line} out of pure laziness.", +//! file = file!(), +//! line = line!() +//! ) +//! ); +//! # } +//! ``` + +use crate::bindings; + +macro_rules! mask_variants { + ($(#[$outer:meta])* + pub enum $name:ident { + $( + $(#[$attrs:meta])* + $symbol:ident + ),*$(,)* + }) => { + $(#[$outer])* + pub enum $name { + $( + $(#[$attrs])* + $symbol = bindings::$symbol + ),* + } + }; +} + +mask_variants! { + /// A wrapper type for the various log mask `#defines` in the C code base. + #[allow(non_camel_case_types)] + #[derive(Copy, Clone, Eq, PartialEq, Debug)] + #[repr(u32)] + pub enum LogMask { + CPU_LOG_TB_OUT_ASM, + CPU_LOG_TB_IN_ASM, + CPU_LOG_TB_OP, + CPU_LOG_TB_OP_OPT, + CPU_LOG_INT, + CPU_LOG_EXEC, + CPU_LOG_PCALL, + CPU_LOG_TB_CPU, + CPU_LOG_RESET, + LOG_UNIMP, + LOG_GUEST_ERROR, + CPU_LOG_MMU, + CPU_LOG_TB_NOCHAIN, + CPU_LOG_PAGE, + LOG_TRACE, + CPU_LOG_TB_OP_IND, + CPU_LOG_TB_FPU, + CPU_LOG_PLUGIN, + /// For user-mode strace logging. + LOG_STRACE, + LOG_PER_THREAD, + CPU_LOG_TB_VPU, + LOG_TB_OP_PLUGIN, + } +} + +impl LogMask { + /// Alias. + pub const GUEST_ERROR: Self = LogMask::LOG_GUEST_ERROR; + /// Alias. + pub const UNIMPLEMENTED: Self = LogMask::LOG_UNIMP; +} + +/// Returns `true` if a bit is set in the current loglevel mask. +/// +/// Counterpart of `qemu_loglevel_mask` in `include/qemu/log-for-trace.h`. +fn qemu_loglevel_mask(mask: LogMask) -> bool { + // SAFETY: This is an internal global variable. We only read from it and reading invalid values + // is not a concern here. + let current_level = unsafe { bindings::qemu_loglevel }; + let mask = mask as ::core::ffi::c_int; + + (current_level & mask) != 0 +} + +/// Log a message in QEMU's log, given a specific log mask. +pub fn qemu_log_mask(log_mask: LogMask, str: &str) { + if qemu_loglevel_mask(log_mask) { + qemu_log(str); + } +} + +/// Log a message in QEMU's log only if a bit is set on the current loglevel mask and we are in the +/// address range we care about. +pub fn qemu_log_mask_and_addr(log_mask: LogMask, address: u64, str: &str) { + if qemu_loglevel_mask(log_mask) && { + // SAFETY: This function reads global variables/system state but an error here is not a + // concern. + unsafe { bindings::qemu_log_in_addr_range(address) } + } { + qemu_log(str); + } +} + +/// Log a message in QEMU's log, without a log mask. +pub fn qemu_log(str: &str) { + let Ok(cstr) = ::std::ffi::CString::new(str) else { + panic!( + "qemu_log_mask: Converting passed string {:?} to CString failed.", + str + ); + }; + // SAFETY: We're passing two valid CStr pointers. The second argument for the variadic + // `qemu_log` function must be a `*const c_char` since the format specifier is `%s`. + // Therefore this is a safe call. + unsafe { bindings::qemu_log(c"%s\n".as_ptr(), cstr.as_ptr()) }; +} From patchwork Thu Oct 24 14:03:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manos Pitsidianakis X-Patchwork-Id: 13849137 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 500CCCE8E7B for ; Thu, 24 Oct 2024 14:06:05 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t3yRX-0007CN-DO; Thu, 24 Oct 2024 10:04:03 -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 1t3yRU-0007AI-EJ for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:04:01 -0400 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t3yRS-0003y1-Q1 for qemu-devel@nongnu.org; Thu, 24 Oct 2024 10:04:00 -0400 Received: by mail-lf1-x12d.google.com with SMTP id 2adb3069b0e04-539f58c68c5so1719687e87.3 for ; Thu, 24 Oct 2024 07:03:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1729778637; x=1730383437; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=10FS6ba90SAfmfgqA/GoqytJLsUTvwlTO03Bcs2OdSo=; b=t7E4R9dyiCoCeNrMyEfiuapGVXLjaVV3VMQVhXIJ1FVZLliNLUiAxWIGYduFtBS3M7 enzpo9ndmAKvti2vr1MW1olYoJ06ArQw3yAx8kd/6//LQI9fLz+rpRnHBiWzbYagqomH BUTwyNHiBP5xmfM4fDkeEpVWHGCYl9AnDHkCFP7ltxlFFQdao+KJb9dQYQI9kyFuehz7 hit1SLU7P+pNjbjydJJLZUftf9Xc4V/nUU/8ylm/QrKMLSz5Li8ghHv1bIxiClBpQUmd eDyFJewGX1axLd3WjPhPPa2ye4Yi6LbH76JHtn3X0os4qIneAc/vDT0ijT/5xs6P/eGY okVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729778637; x=1730383437; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=10FS6ba90SAfmfgqA/GoqytJLsUTvwlTO03Bcs2OdSo=; b=JiHXfpSj0P5GWmVW9eIaBsSVIvyUK7Hm5hRPWFNs3xwb6yTg+t9dtHoS5xAuc5AUqC bakr9T76yxU5Eig1ATARBNyCVjdHn/zlZysLfM/woeA4hzYduD0Aju/6icULRDlflrhK L2BVIhZeP2sTe2b/Vm1dtaXp6ORll/b9Bc5erZepEN2pyqDTXOcG1tQ8TsNroAVz7Gvi wuwugKBN5d8qa/4tdphwToh+V6v1AlrTafDsYC/RJ8uMYNcAjRoV+95UnULjGeoSq17i xYjrRuDAaetsl8xtfWrIsZREMoGf7/leS/p2zif9mOH0Mn3+heNOLJwgjx9kJSMLs9G+ LATQ== X-Gm-Message-State: AOJu0YxUgD6D77bW6h1cYY8n56GM/wFLXrH7JgXuJNTJNgAVnepVYKDs 15GeJboLG+vSe4U+R8ZA5nF5U6vgEH6mqk53CI2eCV/vHtFG98EdzIi9iY8KTxU= X-Google-Smtp-Source: AGHT+IF/X6Ld09RtROSNXWtaw4b8x85ob/Ww0GPDmQEOLwkcFq3XSO+EWSUbg/IhA25BoWQkhro2Wg== X-Received: by 2002:a05:6512:104c:b0:536:53e3:feae with SMTP id 2adb3069b0e04-53b1a315e5bmr5989441e87.11.1729778634976; Thu, 24 Oct 2024 07:03:54 -0700 (PDT) Received: from [127.0.1.1] (adsl-113.37.6.2.tellas.gr. [37.6.2.113]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9a912f6878sm621407566b.86.2024.10.24.07.03.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Oct 2024 07:03:54 -0700 (PDT) From: Manos Pitsidianakis Date: Thu, 24 Oct 2024 17:03:09 +0300 Subject: [PATCH 11/11] rust/pl011: log guest/unimp errors MIME-Version: 1.0 Message-Id: <20241024-rust-round-2-v1-11-051e7a25b978@linaro.org> References: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> In-Reply-To: <20241024-rust-round-2-v1-0-051e7a25b978@linaro.org> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Peter Maydell , =?utf-8?q?Marc-Andr=C3=A9_Lureau?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , =?utf-8?q?Phil?= =?utf-8?q?ippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Alex_Ben?= =?utf-8?q?n=C3=A9e?= , Thomas Huth , Junjie Mao , Junjie Mao , Zhao Liu , Kevin Wolf , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Richard Henderson , Gustavo Romero , Pierrick Bouvier X-Mailer: b4 0.15-dev-12fc5 X-Developer-Signature: v=1; a=openpgp-sha256; l=3497; i=manos.pitsidianakis@linaro.org; h=from:subject:message-id; bh=7/quJHuk1AVNPeKw9d+kvD8KhgiKTwdd6ez7kP/JrJw=; b=LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0VCYlFLUy9aQU5Bd0FLQVhjcHgzQi9mZ 25RQWNzbVlnQm5HbE9rMGpsSGgzQ1FTLzI0UEliREVZTTB0WTlFCjNsaXBqempXNjhrcFN3N25G M0dKQWpNRUFBRUtBQjBXSVFUTVhCdE9SS0JXODRkd0hSQjNLY2R3ZjM0SjBBVUMKWnhwVHBBQUt DUkIzS2Nkd2YzNEowQVRYRC80eGduSlhmcEkvWTJrL0lEWitIVUZSRlFyWHRQNExWWWxkUWxyQw pnSXZjclVEelErdmlabkdTQkVMcGE5c1dYajFIUk9lSGVoRFZGMCsyZ0o3NlFhSkFjL05UZ3Fvc GQrNTVweDZKClBDbDgrWHdJU2FBZjFJWkJqZlA0anRobzhvZzZCbWJTcTNOVlB3WmkvZ3A2M1BE YWc2Q2U3SGwzbkxOa3EvVCsKcGNJOWNobU5YS1djUThvd0ZCS0VIMkt4ZGFvYlpZeHB0dy8zUjQ 0VTZ4SGM3eXJnNW9jREp4dkpaRGwxM3BsegpndFhHOEpaUUNCWmZ1K3dOUjFWQWlMcmZhbG9UbV JzRWZncGNrK2ZHZ0FkRzR0Uzk0bWpWVGltd2RoS2owbTBCClB2QVNlY3ZpNWh2N0ZOdGVZVWhOd 2Z3OERCeExDcEg3WXA1dENwZEdvZWtJYW81UHhTMUZlTXQydDNJYys0djUKM2RSVE1uUXY2UXBE WlRvWGxpa1E2dWVJWHBPMENPTTJrcDNpVjRVd3kxRDdmVThrVURCYWZVWU5MTjVMRlh4aApGaWE wV3Fsc1V5OWYrQjkrRVdHQ0xZV3ViQXZ1YytWTmRMUW84dXpvRktkaW1GNCtBZ1p5SEVHaVRNS0 1yR1VwCmFqRGpZT3Zia3BkTXg5eFEySiswSzJTdXk1SElaV0MvTmkyMDFSRkVPemxsKzB4bEh6a ExjMVByK082UWovYngKYmptQUtGZ2FVKys0QW93VFM3YzJuRkszend6T2ZtWTlhMXhTcVNFa3Vz UlpPUnl5aGNHSGg5L1d0M0xOYkwzQwpMdzhKL2Y0dDBKOTcxMVdaYTdsNS9wbElQcFQvczREeW4 vbTdtZFdDcFcrTHRQWCtUdU9jWkdvb2xwZFJKcndqCmRyVTMvdz09Cj1xanY5Ci0tLS0tRU5EIF BHUCBNRVNTQUdFLS0tLS0K X-Developer-Key: i=manos.pitsidianakis@linaro.org; a=openpgp; fpr=7C721DF9DB3CC7182311C0BF68BC211D47B421E1 Received-SPF: pass client-ip=2a00:1450:4864:20::12d; envelope-from=manos.pitsidianakis@linaro.org; helo=mail-lf1-x12d.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, 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 Use the qemu_log_mask() functions introduced in previous commit to log errors like the C pl011 device does. Signed-off-by: Manos Pitsidianakis --- rust/hw/char/pl011/src/device.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 6d1353dafc14bfe73703b5cff7e1ff7659de220e..57b38f44f90d74d0c1a94bb9144eff08db94fadf 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -9,6 +9,7 @@ use qemu_api::{ bindings::{self, *}, + log::*, objects::*, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, vmstate_uint32, vmstate_uint32_array, vmstate_unused, @@ -343,7 +344,14 @@ pub fn read( u64::from(self.device_id[(offset - 0xfe0) >> 2]) } Err(_) => { - // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask( + LogMask::GUEST_ERROR, + &format!( + "pl011:{file}:{line}: Bad offset 0x{offset:x}", + file = file!(), + line = line!(), + ), + ); 0 } Ok(DR) => { @@ -389,15 +397,30 @@ pub fn read( } pub fn write(&mut self, offset: hwaddr, value: u64) { - // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; let value: u32 = value as u32; match RegisterOffset::try_from(offset) { Err(_bad_offset) => { - eprintln!("write bad offset {offset} value {value}"); + qemu_log_mask( + LogMask::GUEST_ERROR, + &format!( + "pl011:{file}:{line}: Bad write offset 0x{offset:x} of value 0x:{value:x}", + file = file!(), + line = line!(), + ), + ); } Ok(DR) => { - // ??? Check if transmitter is enabled. + // Check if transmitter is enabled. + if !self.control.enable_uart() { + qemu_log_mask(LogMask::GUEST_ERROR, "PL011 data written to disabled UART"); + } + if !self.control.enable_transmit() { + qemu_log_mask( + LogMask::GUEST_ERROR, + "PL011 data written to disabled TX UART", + ); + } let ch: u8 = value as u8; // XXX this blocks entire thread. Rewrite to use // qemu_chr_fe_write and background I/O callbacks @@ -474,8 +497,10 @@ pub fn write(&mut self, offset: hwaddr, value: u64) { Ok(DMACR) => { self.dmacr = value; if value & 3 > 0 { - // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); - eprintln!("pl011: DMA not implemented"); + qemu_log_mask( + LogMask::UNIMPLEMENTED, + "pl011: DMA functionality is not implemented", + ); } } }