mbox series

[RFC,v2,0/5] Implement ARM PL011 in Rust

Message ID rust-pl011-rfc-v2.git.manos.pitsidianakis@linaro.org (mailing list archive)
Headers show
Series Implement ARM PL011 in Rust | expand

Message

Manos Pitsidianakis June 11, 2024, 10:33 a.m. UTC
Changes from v1->v2:
- Create bindgen target first, then add commit for device (thanks 
  Pierrick)
- Create a special named generated.rs for each target as compilation 
  would fail if more than one targets were defined. The generated.rs 
  target names would clash.
- Add more descriptive commit messages
- Update MAINTAINERS
- Cleanup patch order for better review, hopefully

Previous series was 
<cover.rust-pl011-rfc-v1.git.manos.pitsidianakis@linaro.org>

--

Hello everyone,

This is an early draft of my work on implementing a very simple device, 
in this case the ARM PL011 (which in C code resides in hw/char/pl011.c 
and is used in hw/arm/virt.c).

The device is functional, with copied logic from the C code but with 
effort not to make a direct C to Rust translation. In other words, do 
not write Rust as a C developer would.

That goal is not complete but a best-effort case. To give a specific 
example, register values are typed but interrupt bit flags are not (but 
could be). I will leave such minutiae for later iterations.

By the way, the wiki page for Rust was revived to keep track of all 
current series on the mailing list https://wiki.qemu.org/RustInQemu

a #qemu-rust IRC channel was also created for rust-specific discussion 
that might flood #qemu

------------------------------------------------------------------------
A request: keep comments to Rust in relation to the QEMU project and no 
debates on the merits of the language itself. These are valid concerns, 
but it'd be better if they were on separate mailing list threads.
------------------------------------------------------------------------

Table of contents: [TOC]

- How can I try it? [howcanItryit]
- What are the most important points to focus on, at this point? 
  [whatarethemostimportant]
  - What are the issues with not using the compiler, rustc, directly? 
    [whataretheissueswith]
    1. Tooling
    2. Rust dependencies

  - Should QEMU use third-party dependencies? [shouldqemuusethirdparty]
  - Should QEMU provide wrapping Rust APIs over QEMU internals? 
    [qemuprovidewrappingrustapis]
  - Will QEMU now depend on Rust and thus not build on my XYZ platform? 
    [qemudependonrustnotbuildonxyz]
- How is the compilation structured? [howisthecompilationstructured]
- The generated.rs rust file includes a bunch of junk definitions? 
  [generatedrsincludesjunk]
- The staticlib artifact contains a bunch of mangled .o objects? 
  [staticlibmangledobjects]

How can I try it?
=================
[howcanItryit] Back to [TOC]

Hopefully applying this patches (or checking out `master` branch from 
https://gitlab.com/epilys/rust-for-qemu/ )

Tag for this RFC is rust-pl011-rfc-v2

Rustdoc documentation is hosted on

https://rust-for-qemu-epilys-aebb06ca9f9adfe6584811c14ae44156501d935ba4.gitlab.io/pl011/index.html

If `cargo` and `bindgen` is installed in your system, you should be able 
to build qemu-system-aarch64 with configure flag --enable-rust and 
launch an arm virt VM. One of the patches hardcodes the default UART of 
the machine to the Rust one, so if something goes wrong you will see it 
upon launching qemu-system-aarch64.

To confirm it is there for sure, run e.g. info qom-tree on the monitor 
and look for x-pl011-rust.


What are the most important points to focus on, at this point?
==============================================================
[whatarethemostimportant] Back to [TOC]

In my opinion, integration of the go-to Rust build system (Cargo and 
crates.io) with the build system we use in QEMU. This is "easily" done 
in some definition of the word with a python wrapper script.

What are the issues with not using the compiler, rustc, directly?
-----------------------------------------------------------------
[whataretheissueswith] Back to [TOC]

1. Tooling
   Mostly writing up the build-sys tooling to do so. Ideally we'd 
   compile everything without cargo but rustc directly.

   If we decide we need Rust's `std` library support, we could 
   investigate whether building it from scratch is a good solution. This 
   will only build the bits we need in our devices.

2. Rust dependencies
   We could go without them completely. I chose deliberately to include 
   one dependency in my UART implementation, `bilge`[0], because it has 
   an elegant way of representing typed bitfields for the UART's 
   registers.

[0]: Article: https://hecatia-elegua.github.io/blog/no-more-bit-fiddling/
     Crates.io page: https://crates.io/crates/bilge
     Repository: https://github.com/hecatia-elegua/bilge

Should QEMU use third-party dependencies?
-----------------------------------------
[shouldqemuusethirdparty] Back to [TOC]

In my personal opinion, if we need a dependency we need a strong 
argument for it. A dependency needs a trusted upstream source, a QEMU 
maintainer to make sure it us up-to-date in QEMU etc.

We already fetch some projects with meson subprojects, so this is not a 
new reality. Cargo allows you to define "locked" dependencies which is 
the same as only fetching specific commits by SHA. No suspicious 
tarballs, and no disappearing dependencies a la left-pad in npm.

However, I believe it's worth considering vendoring every dependency by 
default, if they prove to be few, for the sake of having a local QEMU 
git clone buildable without network access.

Should QEMU provide wrapping Rust APIs over QEMU internals?
-----------------------------------------------------------
[qemuprovidewrappingrustapis] Back to [TOC]

My personal opinion is no, with the reasoning being that QEMU internals 
are not documented or stable. However I do not see why creating stable 
opt-in interfaces is bad. It just needs someone to volunteer to maintain 
it and ensure there are no breakages through versions.

Will QEMU now depend on Rust and thus not build on my XYZ platform?
-------------------------------------------------------------------
[qemudependonrustnotbuildonxyz] Back to [TOC]

No, worry about this in some years if this experiment takes off. Rust 
has broad platform support and is present in most distro package 
managers. In the future we might have gcc support for it as well.

For now, Rust will have an experimental status, and will be aimed to 
those who wish to try it. I leave it to the project leaders to make 
proper decisions and statements on this if necessary.


How is the compilation structured?
==================================
[howisthecompilationstructured] Back to [TOC]

First, a meson target that runs `bindgen` on a bunch of header files 
(defined in `rust/wrapper.h`) is created as a target and as a dependency 
for any rust hardware device that needs it. You can see the generated 
bindings by running

  ninja aarch64-softmmu-generated.rs

inside your build directory.

The devices are defined as dictionaries in rust/meson.build because they 
depend on the bindgen dependency, which is available much later in the 
meson process (when the static qemu lib and target emulator executables 
are defined).

A cargo wrapper python script under scripts/ exists to build the crate 
library, by providing the path to the generated.rs bindings via the 
environment. Then, the qemu-system-aarch64 binary links against the 
staticlib archive (i.e. libpl011.a)

The generated.rs rust file includes a bunch of junk definitions?
================================================================
[generatedrsincludesjunk] Back to [TOC]

Yes, bindgen allows you to block certain types and identifiers from 
being generated but they are simply too many. I have trimmed some of the 
fat but vast improvements can be made.

The staticlib artifact contains a bunch of mangled .o objects?
==============================================================
[staticlibmangledobjects] Back to [TOC]

Yes, until we compile without the `std` module library or we compile it 
manually instead of linking it, we will have some junk in it.

--


Manos Pitsidianakis (5):
  build-sys: Add rust feature option
  rust: add bindgen step as a meson dependency
  rust: add PL011 device model
  DO NOT MERGE: add rustdoc build for gitlab pages
  DO NOT MERGE: replace TYPE_PL011 with x-pl011-rust in arm virt machine

 .gitignore                     |   2 +
 .gitlab-ci.d/buildtest.yml     |  64 ++--
 MAINTAINERS                    |  13 +
 configure                      |  12 +
 hw/arm/virt.c                  |   4 +
 meson.build                    | 102 ++++++
 meson_options.txt              |   4 +
 rust/meson.build               |  93 ++++++
 rust/pl011/.cargo/config.toml  |   2 +
 rust/pl011/.gitignore          |   2 +
 rust/pl011/Cargo.lock          | 120 +++++++
 rust/pl011/Cargo.toml          |  66 ++++
 rust/pl011/README.md           |  42 +++
 rust/pl011/build.rs            |  44 +++
 rust/pl011/deny.toml           |  57 ++++
 rust/pl011/meson.build         |   7 +
 rust/pl011/rustfmt.toml        |   1 +
 rust/pl011/src/definitions.rs  |  95 ++++++
 rust/pl011/src/device.rs       | 531 ++++++++++++++++++++++++++++++
 rust/pl011/src/device_class.rs |  95 ++++++
 rust/pl011/src/generated.rs    |   5 +
 rust/pl011/src/lib.rs          | 581 +++++++++++++++++++++++++++++++++
 rust/pl011/src/memory_ops.rs   |  38 +++
 rust/rustfmt.toml              |   7 +
 rust/wrapper.h                 |  39 +++
 scripts/cargo_wrapper.py       | 221 +++++++++++++
 scripts/meson-buildoptions.sh  |   6 +
 27 files changed, 2234 insertions(+), 19 deletions(-)
 create mode 100644 rust/meson.build
 create mode 100644 rust/pl011/.cargo/config.toml
 create mode 100644 rust/pl011/.gitignore
 create mode 100644 rust/pl011/Cargo.lock
 create mode 100644 rust/pl011/Cargo.toml
 create mode 100644 rust/pl011/README.md
 create mode 100644 rust/pl011/build.rs
 create mode 100644 rust/pl011/deny.toml
 create mode 100644 rust/pl011/meson.build
 create mode 120000 rust/pl011/rustfmt.toml
 create mode 100644 rust/pl011/src/definitions.rs
 create mode 100644 rust/pl011/src/device.rs
 create mode 100644 rust/pl011/src/device_class.rs
 create mode 100644 rust/pl011/src/generated.rs
 create mode 100644 rust/pl011/src/lib.rs
 create mode 100644 rust/pl011/src/memory_ops.rs
 create mode 100644 rust/rustfmt.toml
 create mode 100644 rust/wrapper.h
 create mode 100644 scripts/cargo_wrapper.py


base-commit: 01782d6b294f95bcde334386f0aaac593cd28c0d

Comments

Daniel P. Berrangé June 12, 2024, 8:37 a.m. UTC | #1
On Tue, Jun 11, 2024 at 01:33:29PM +0300, Manos Pitsidianakis wrote:

> 
>  .gitignore                     |   2 +
>  .gitlab-ci.d/buildtest.yml     |  64 ++--
>  MAINTAINERS                    |  13 +
>  configure                      |  12 +
>  hw/arm/virt.c                  |   4 +
>  meson.build                    | 102 ++++++
>  meson_options.txt              |   4 +
>  rust/meson.build               |  93 ++++++
>  rust/pl011/.cargo/config.toml  |   2 +
>  rust/pl011/.gitignore          |   2 +
>  rust/pl011/Cargo.lock          | 120 +++++++
>  rust/pl011/Cargo.toml          |  66 ++++
>  rust/pl011/README.md           |  42 +++
>  rust/pl011/build.rs            |  44 +++
>  rust/pl011/deny.toml           |  57 ++++
>  rust/pl011/meson.build         |   7 +
>  rust/pl011/rustfmt.toml        |   1 +
>  rust/pl011/src/definitions.rs  |  95 ++++++
>  rust/pl011/src/device.rs       | 531 ++++++++++++++++++++++++++++++
>  rust/pl011/src/device_class.rs |  95 ++++++
>  rust/pl011/src/generated.rs    |   5 +
>  rust/pl011/src/lib.rs          | 581 +++++++++++++++++++++++++++++++++
>  rust/pl011/src/memory_ops.rs   |  38 +++
>  rust/rustfmt.toml              |   7 +
>  rust/wrapper.h                 |  39 +++
>  scripts/cargo_wrapper.py       | 221 +++++++++++++
>  scripts/meson-buildoptions.sh  |   6 +

Given the priority of getting the build system correct, what's missing
here is updates/integration into our standard GitLab CI pipeline. If
that can be shown to be working, that'll give alot more confidence in
the overall solution.

Ideally this should not require anything more than updating the docker
container definitions to add in the rust toolchain, plus the appropriate
std library build for the given target - we cross compiler for every
arch we officially care about.

Most of our dockerfiles these days are managed by lcitool, and it has
nearly sufficient support for cross compiling with the rust std library.
So to start with, this series should modify tests/lcitool/projects/qemu.yml
to add

  - rust
  - rust-std

to the package list, and run 'make lcitool-refresh' to re-create the
dockerfiles - see the docs/devel/testing.rst for more info about
lcitool if needed.

Assuming these 2 rust packages are in the container, I would then
expect QEMU to just "do the right thing" when building this rust
code. If it does not, then that's a sign of gaps that need closing.

Getting rid of the need to use --rust-target-triple will be the
immediate gap that needs fixing, as CI just passes --cross-prefix
for cross-builds and expects everything to be set from that.

The main gap we have is that for Windows I need to update lcitool
to pull in the mingw std lib target for rust, which I something I
missed when adding rust cross compiler support.


With regards,
Daniel
Manos Pitsidianakis June 13, 2024, 5:13 a.m. UTC | #2
Good morning Daniel,

On Wed, 12 Jun 2024 11:37, "Daniel P. Berrangé" <berrange@redhat.com> wrote:
>On Tue, Jun 11, 2024 at 01:33:29PM +0300, Manos Pitsidianakis wrote:
>
>> 
>>  .gitignore                     |   2 +
>>  .gitlab-ci.d/buildtest.yml     |  64 ++--
>>  MAINTAINERS                    |  13 +
>>  configure                      |  12 +
>>  hw/arm/virt.c                  |   4 +
>>  meson.build                    | 102 ++++++
>>  meson_options.txt              |   4 +
>>  rust/meson.build               |  93 ++++++
>>  rust/pl011/.cargo/config.toml  |   2 +
>>  rust/pl011/.gitignore          |   2 +
>>  rust/pl011/Cargo.lock          | 120 +++++++
>>  rust/pl011/Cargo.toml          |  66 ++++
>>  rust/pl011/README.md           |  42 +++
>>  rust/pl011/build.rs            |  44 +++
>>  rust/pl011/deny.toml           |  57 ++++
>>  rust/pl011/meson.build         |   7 +
>>  rust/pl011/rustfmt.toml        |   1 +
>>  rust/pl011/src/definitions.rs  |  95 ++++++
>>  rust/pl011/src/device.rs       | 531 ++++++++++++++++++++++++++++++
>>  rust/pl011/src/device_class.rs |  95 ++++++
>>  rust/pl011/src/generated.rs    |   5 +
>>  rust/pl011/src/lib.rs          | 581 +++++++++++++++++++++++++++++++++
>>  rust/pl011/src/memory_ops.rs   |  38 +++
>>  rust/rustfmt.toml              |   7 +
>>  rust/wrapper.h                 |  39 +++
>>  scripts/cargo_wrapper.py       | 221 +++++++++++++
>>  scripts/meson-buildoptions.sh  |   6 +
>
>Given the priority of getting the build system correct, what's missing
>here is updates/integration into our standard GitLab CI pipeline. If
>that can be shown to be working, that'll give alot more confidence in
>the overall solution.
>
>Ideally this should not require anything more than updating the docker
>container definitions to add in the rust toolchain, plus the appropriate
>std library build for the given target - we cross compiler for every
>arch we officially care about.
>
>Most of our dockerfiles these days are managed by lcitool, and it has
>nearly sufficient support for cross compiling with the rust std library.
>So to start with, this series should modify tests/lcitool/projects/qemu.yml
>to add
>
>  - rust
>  - rust-std
>
>to the package list, and run 'make lcitool-refresh' to re-create the
>dockerfiles - see the docs/devel/testing.rst for more info about
>lcitool if needed.
>
>Assuming these 2 rust packages are in the container, I would then
>expect QEMU to just "do the right thing" when building this rust
>code. If it does not, then that's a sign of gaps that need closing.
>
>Getting rid of the need to use --rust-target-triple will be the
>immediate gap that needs fixing, as CI just passes --cross-prefix
>for cross-builds and expects everything to be set from that.
>
>The main gap we have is that for Windows I need to update lcitool
>to pull in the mingw std lib target for rust, which I something I
>missed when adding rust cross compiler support.
>


Thanks very much for the pointers! I will start dealing with this in the 
next RFC version.

Re: the target triple, I agree 100%. In fact it wasn't my addition, I 
kept it from the previous rust RFC patchset that was posted on the list 
some years ago. It should be possible to construct the triplets 
ourselves and let the user override if they want to as mentioned in 
another email.

The rust project has official Docker images, do you think it's something 
we could use or is it unnecessary? 

https://github.com/rust-lang/docker-rust

Thanks,
Manos
Daniel P. Berrangé June 13, 2024, 7:56 a.m. UTC | #3
On Thu, Jun 13, 2024 at 08:13:01AM +0300, Manos Pitsidianakis wrote:
> Good morning Daniel,
> 
> On Wed, 12 Jun 2024 11:37, "Daniel P. Berrangé" <berrange@redhat.com> wrote:
> > On Tue, Jun 11, 2024 at 01:33:29PM +0300, Manos Pitsidianakis wrote:
> > 
> > > 
> > >  .gitignore                     |   2 +
> > >  .gitlab-ci.d/buildtest.yml     |  64 ++--
> > >  MAINTAINERS                    |  13 +
> > >  configure                      |  12 +
> > >  hw/arm/virt.c                  |   4 +
> > >  meson.build                    | 102 ++++++
> > >  meson_options.txt              |   4 +
> > >  rust/meson.build               |  93 ++++++
> > >  rust/pl011/.cargo/config.toml  |   2 +
> > >  rust/pl011/.gitignore          |   2 +
> > >  rust/pl011/Cargo.lock          | 120 +++++++
> > >  rust/pl011/Cargo.toml          |  66 ++++
> > >  rust/pl011/README.md           |  42 +++
> > >  rust/pl011/build.rs            |  44 +++
> > >  rust/pl011/deny.toml           |  57 ++++
> > >  rust/pl011/meson.build         |   7 +
> > >  rust/pl011/rustfmt.toml        |   1 +
> > >  rust/pl011/src/definitions.rs  |  95 ++++++
> > >  rust/pl011/src/device.rs       | 531 ++++++++++++++++++++++++++++++
> > >  rust/pl011/src/device_class.rs |  95 ++++++
> > >  rust/pl011/src/generated.rs    |   5 +
> > >  rust/pl011/src/lib.rs          | 581 +++++++++++++++++++++++++++++++++
> > >  rust/pl011/src/memory_ops.rs   |  38 +++
> > >  rust/rustfmt.toml              |   7 +
> > >  rust/wrapper.h                 |  39 +++
> > >  scripts/cargo_wrapper.py       | 221 +++++++++++++
> > >  scripts/meson-buildoptions.sh  |   6 +
> > 
> > Given the priority of getting the build system correct, what's missing
> > here is updates/integration into our standard GitLab CI pipeline. If
> > that can be shown to be working, that'll give alot more confidence in
> > the overall solution.
> > 
> > Ideally this should not require anything more than updating the docker
> > container definitions to add in the rust toolchain, plus the appropriate
> > std library build for the given target - we cross compiler for every
> > arch we officially care about.
> > 
> > Most of our dockerfiles these days are managed by lcitool, and it has
> > nearly sufficient support for cross compiling with the rust std library.
> > So to start with, this series should modify tests/lcitool/projects/qemu.yml
> > to add
> > 
> >  - rust
> >  - rust-std
> > 
> > to the package list, and run 'make lcitool-refresh' to re-create the
> > dockerfiles - see the docs/devel/testing.rst for more info about
> > lcitool if needed.
> > 
> > Assuming these 2 rust packages are in the container, I would then
> > expect QEMU to just "do the right thing" when building this rust
> > code. If it does not, then that's a sign of gaps that need closing.
> > 
> > Getting rid of the need to use --rust-target-triple will be the
> > immediate gap that needs fixing, as CI just passes --cross-prefix
> > for cross-builds and expects everything to be set from that.
> > 
> > The main gap we have is that for Windows I need to update lcitool
> > to pull in the mingw std lib target for rust, which I something I
> > missed when adding rust cross compiler support.

I've updated lcitool for this gap:

  https://gitlab.com/libvirt/libvirt-ci/-/merge_requests/492

> The rust project has official Docker images, do you think it's something we
> could use or is it unnecessary?
> 
> https://github.com/rust-lang/docker-rust

They're irrelevant - we need the containers with the full set of QEMU build
dependencies present, and it is the Rust versions that are in the distros
that matter to us.

With regards,
Daniel
Richard Henderson June 19, 2024, 3:31 a.m. UTC | #4
On 6/11/24 03:33, Manos Pitsidianakis wrote:
> If `cargo` and `bindgen` is installed in your system, you should be able
> to build qemu-system-aarch64 with configure flag --enable-rust and
> launch an arm virt VM. One of the patches hardcodes the default UART of
> the machine to the Rust one, so if something goes wrong you will see it
> upon launching qemu-system-aarch64.

What version is required?

On my stock ubuntu 22.04 system, I get

/usr/bin/bindgen aarch64-softmmu_wrapper.h ...
error: Found argument '--formatter' which wasn't expected, or isn't valid in this context

USAGE:
     bindgen [FLAGS] [OPTIONS] <header> -- <clang-args>...

$ bindgen --version
bindgen 0.59.1


r~
Manos Pitsidianakis June 19, 2024, 5:36 p.m. UTC | #5
On Wed, 19 Jun 2024 06:31, Richard Henderson <richard.henderson@linaro.org> wrote:
>On 6/11/24 03:33, Manos Pitsidianakis wrote:
>> If `cargo` and `bindgen` is installed in your system, you should be able
>> to build qemu-system-aarch64 with configure flag --enable-rust and
>> launch an arm virt VM. One of the patches hardcodes the default UART of
>> the machine to the Rust one, so if something goes wrong you will see it
>> upon launching qemu-system-aarch64.
>
>What version is required?
>
>On my stock ubuntu 22.04 system, I get
>
>/usr/bin/bindgen aarch64-softmmu_wrapper.h ...
>error: Found argument '--formatter' which wasn't expected, or isn't valid in this context
>
>USAGE:
>     bindgen [FLAGS] [OPTIONS] <header> -- <clang-args>...
>
>$ bindgen --version
>bindgen 0.59.1

I added version checks in the (yet unpublished) next version:

(Which we will also need to match with distro ones whenever possible)

+# FIXME: Latest stable versions, refine to actual minimum ones.
+msrv = {
+  'rustc': '1.79.0',
+  'cargo': '1.79.0',
+  'bindgen': '0.69.4',
+}
+
+# rustup = find_program('rustup', required: false)
+foreach bin_dep: msrv.keys()
+  bin = find_program(bin_dep, required: true)
+  if bin.version() < msrv[bin_dep]
+    # TODO verify behavior
+    if bin == 'bindgen' and get_option('wrap_mode') != 'nodownload'
+      run_command(cargo, 'install', 'bindgen', capture: true, check: true)
+      bin = find_program(bin_dep, required: true)
+      if bin.version() >= msrv[bin_dep]
+        continue
+      endif
+    endif
+    message()
+    error(bin_dep + ' version ' + bin.version() + ' is unsupported: Please upgrade to at least ' + msrv[bin_dep])
+  endif
+endforeach