Hi, first of all I want to clarify the raison d'etre for this posting, which I have also explained to Manos. Nothing you see here is code that will be certainly included in QEMU; it's (mostly) throwaway by design. I don't have much attachment to any of the code except perhaps the casting and reference counting stuff (which is in, like, the third rewrite... I got nerd-sniped there :)). But at the same time I think it's plausibly not too different (in look and complexity) from what the actual code will look like.
It's also not an attempt to bypass/leapfrog other people in the design; it's more a "let's be ready for this to happen" than a design document. The first step and the more immediate focus remains the build system integration. But you have good questions and observations so I'll put something below about the design as well. On Fri, Jul 5, 2024 at 8:52 PM Pierrick Bouvier <pierrick.bouv...@linaro.org> wrote: > To give an example of questions we could have: > > Why should we have distinct structs for a device, the object > represented, and its state? If you refer to the conf and state, a bit because of the different traits required (ConstDefault is needed for properties but not the rest) and especially because we can enforce immutability of configuration vs. interior mutability of state. In particular, from Manos's prototype I noticed that you need to access the Chardev while the state is *not* borrowed; methods on the Chardev call back into the device and the callback needs mutable access to the state. So some kind of separation seems to be necessary, for better or worse, unless interior mutability is achieved with a dozen Cells (not great). > Having to implement ObjectImpl and DeviceImpl looks like an > implementation detail, that could be derived automatically from a > device. What's wrong with calling a realize that does nothing in the end? The problem is not calling a realize that does nothing, it's that you are forced to override the superclass implementation (which might be in C) if you don't go through Option<>. class_init methods update a few superclass methods but not all of them. The vtable in ObjectImpl/DeviceImpl could be generated via macros; for now I did it by hand to avoid getting carried away too much with syntactic sugar. > Could device state be serialized with something like serde? Probably not, there's extra complications for versioning. A long time ago there was an attempt to have some kind of IDL embedded in C code (not unlike Rust attributes) to automatically generate properties and vmstate, but it was too ambitious. (https://www.linux-kvm.org/images/b/b5/2012-forum-qidl-talk.pdf, slides 1-9). Generating property and vmstate declarations could be done with "normal" macros just like in C, or with attribute macros (needs a lot more code and experience, wouldn't do it right away). However, serde for QAPI and/or visitors may be a possibility, after all JSON is serde's bread and butter. > This is the kind of things that could be discussed, on a reduced > example, without specially looking at how to implement that concretely, > in a first time. There are some things that Rust really hates that you do, and they aren't always obvious. Therefore in this exercise I tried to let intuition guide me, and see how much the type system fought that intuition (not much actually!). I started from the more technical and less artistic part to see if I was able to get somewhere. But yes, it's a great exercise to do this experiment from the opposite end. Then the actual glue code will "meet in the middle", applying lessons learnt from both experiments, with individual pieces of the real interface implemented and applied to the PL011 sample at the same time. The main missing thing in PL011 is DMA, otherwise it's a nice playground. > usage of magic macros as syntactic sugar should indeed be thought twice. > Return a collection of QDevProperty could be better than having a magic > @properties syntactic sugar. No hard opinions there, sure. I went for the magic syntax because the properties cannot be a const and I wanted to hide the ugly "static mut", but certainly there could be better ways to do it. > Another thing that could be discussed is: do we want to have the whole > inheritance mechanism for Rust devices? Is it really needed? Eh, this time unfortunately I think it is. Stuff like children and properties, which are methods of Object, need to be available to subclasses in instance_init and/or realize. Reset is in Device and we have the whole Device/SysBusDevice/PCIDevice set of classes, so you need to access superclass methods from subclasses. It's not a lot of code though. > Between having a clean and simple Device definition, with a bit more of > magic glue (hidden internally), and requires devices to do some magic > initialization to satisfy existing architecture, what would be the best? > Even though it takes 1000 more lines, I would be in favor to have > Devices that are as clean and simple as possible. Because this is the > kind of code what will be the most added/modified, compared to the glue > part. That's an opinion I share but I wasn't sure it's universal, which was another reason to prototype and post some "scary but perhaps necessary" code. > Or have multiple traits matching every possible operation, and allow a > device to implement it or not, like Read/Write traits. And the glue code > could call qemu_chr_fe_set_handlers. Possibly, yes. https://lwn.net/Articles/863459/ you see many such techniques: traits (gpio::Chip etc.), composition (device::Data), lambdas (only for initialization). The advantage of the lambda approach is that it scales to multiple backends or multiple memory regions. We'll see. > I hope my answer helped to understand more my point that discussing the > interface is more important than discussing the glue needed. Sure, and I don't think we are very much in disagreement, if at all. Paolo