Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's not really about infrastructure but yes kernels and firmwares have to do a lot of stuff the compiler can't verify as safe, eg writing to a magic memory address you obtained from the datasheet that enables some feature of the chip. And that will need to happen in unsafe code blocks. I wouldn't call that a problem but it is a reality.




Are you one of the authors? Concerning the "infrastructure": Rust assumes a runtime, the standard library assumes a stack exists, a heap exists, and that main() is called by an OS; in a kernel, none of this is true. And the borrow checker cannot reason about things like e.g. DMA controllers mutating memory the CPU believes it owns, Memory-mapped I/O where a "read" has side effects (violating functional purity), context switches that require saving register state to arbitrary memory locations, or interrupt handlers that violate the call stack model. That's what I mean by "infrastructure". It's essentially the same issue with every programming language to some degree, but for Rust it is relevant to understand that the "safety guarantees" don't apply to all parts of an operating system, even if written in Rust.

I am a maintainer. I think what you're referring to is the problem where `std` is actually a veneer on C - so for example, when Rust allocates memory on an x86-class desktop, it actually invokes a C library (jemalloc, or whatever the OS is using) and that networking is all built on top of libc. Thus a bunch of nice things like threads, time, filesystem, allocators are all actually the same old C libraries that everything else uses underneath a patina of Rust.

In Xous, considerable effort went in to build the entire `std` in Rust as well, so no C compilers are required to build the OS, including `std`. You can see some of the bindings here in the Rust fork that we maintain: https://github.com/betrusted-io/rust/tree/1.92.0-xous/librar...

Thus to boot the OS, a few lines of assembly are required to set up the stack pointer and some default exception handler state, and from there we jump into Rust and stay in Rust. Even the bootloaders are written in Rust using the small assembly shim then jump to Rust trick.

Xous is Tier-3 Rust OS, so we are listed as a stable Rust target. We build and host the binaries for our `std` library, which native rustc knows how to link against.


Thanks, interesting. My concern was less about which language implements std, but rather about the semantic mismatch between Rust's ownership model and hardware behavior (e.g. DMA aliasing, MMIO side effects). So I was curious what work-around you found; do you e.g. use wrapper types with VolatileCell, or just raw pointers?

Hmm, I think I see what you're asking. I'm not sure if this exactly answers your question, but at least for aliasing, because we have virtual memory all pages have to be white-listed to be valid.

Thus "natural" aliases (say due to a decoder that doesn't decode all the address bits) can't be mapped because the OS would not accept the aliases as valid pages. This is handled through a mechanism that goes through the SVD description of the SoC (SVD is basically an XML file that lists every register and memory region) and derives the list of valid pages. The OS loader then marks those as the set of mappable pages; any attempt to map a page outside that list will lead to a page fault. One nice thing about the SoC RTL being open source is that this entire process of extracting these pages is scripted and extracted from the SoC's source code, so while there can be code bugs, at least human error is eliminated from that process.

DMA devices on their own right can have "god mode" access to memory, because they operate on physical memory addresses and lack page translation tables. To that end the preferred DMA mechanism in hardware has an "allow list" of windows that can be enabled as DMA targets; on reset the list is nil and nothing can be DMA'd, the OS has to correctly configure that. So this is not a Rust-level thing, this is just a driver-level hack. Not all the DMA-capable peripherals have this safety mechanism though, some of the IP blocks are just a big gun with no safety and you're free to point it at your toes.

However, if you set up a DMA and then you read from it later on - you're in unsafe territory. Rust can't reason about that. So for structures that are intended as DMA targets, they are coded in a peculiar way such that they are marked as unsafe and you're using the read_volatile() method on the raw pointer type to force the compiler to not try to optimize out the read for any reason. Furthermore, fence instructions are put around these reads, and a cache flush is required to ensure the correct data is pulled in.

This complexity is baked into a wrapper struct we create specifically to handle dangerous interactions like this.


Thanks, that's exactly what I was asking about. So if I understand correctly: for the hardware interface layer (DMA, MMIO), you're essentially writing disciplined C-style code in unsafe blocks with volatile reads and manual memory barriers, then wrapping it to contain the unsafety. That's pragmatic.

That's correct.

I was looking for information about Xous's raw IPC performance to get an impression of how it performs compared to e.g. the L4 family, especially L4re and sel4. Also a comparison to QNX would be very interesting. Are there any "cycles-per-IPC" benchmarks for Xous available somewhere? What are your plans/goals in this regards?

I have no affiliation, I'm just a commenter.

The standard library requires a heap and such, but you can enable the no_std attribute to work in environments where they don't exist. https://docs.rust-embedded.org/book/intro/no-std.html

Rust's safety model only applies to code you write in your program, and there's a lot that's unsafe (cannot be verified by the compiler) about writing a kernel or a firmware, agreed. You could have similar problems when doing FFI as well.


standard library assumes a stack exists, a heap exists, and that main() is called

A small assembly stub can set up the stack and heap and call main(); from then on you can run Rust code. The other topics you mention are definitely legitimate concerns that require discipline from the programmer because Rust won't automatically handle them but the result will still be safer than C.


  Rust assumes a runtime, the standard library assumes a stack exists, a heap
  exists, and that main() is called by an OS;
Wrong.

Source: I'm writing Rust without a runtime without a heap and without a main function. You can too.


The Rust runtime will, at a minimum, set up the stack pointer, zero out the .bss, and fill in the .data section. You're right in that a heap is optional, but Rust will get very cranky if you don't set up the .data or .bss sections.

As will C.

No idea what either of you are talking about. It's the operating system that sets those things up not the language runtime.

Not for kernels.



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: