I get the error “out-of-bounds pointer arithmetic: expected a pointer to 240 bytes of memory, but got 0xffffffa000fee00000[noalloc] which is a dangling pointer (it has no provenance)”, but the address is completely valid and not out of 64-bit space.
The short answer is that Rust is moving away from providing ways to access MMIO outside of its abstract machine.
One workaround is doing integer arithmetic instead of pointer arithmetic. That is the same as doing the arithmetic in another language through FFI, for all Rust is concerned. Once you have created a pointer, it will begin tracking its provenance.
Another consideration is to first create the pointer to a slice or struct and do pointer arithmetic so you are always pointing at in-bounds valid allocated objects. (Take this with a grain of salt, the compiler may end up being very unhappy with you creating pointers to objects out of thin air. It ought not be a problem as long as MMIO does not overlap with any address space that Rust considers part on its abstract machine. But I’m not writing the rules, only interpreting them.)
edit: In my opinion, it is better to only use raw pointers to primitive types when doing MMIO access. Because they are all Copy types that cannot violate ownership rules by writing through the pointers! Using higher level types gets ugly very quickly, and that's what usually ends up ruffling feathers when it comes to int-to-ptr and provenance discussions (from my perspective, anyway).
Thanks for answer.
I partly understand that, but it seems odd anyway, especially since I'm using unsafe and taking responsibility.
The fact that instead of using byte_add() I have to use numeric arithmetic looks like nothing but a fight with the compiler, because the result is the same, I just add bytes to an address that is just a number, the memory location number, everywhere.
It actually is not the same due to subtle reasons. The pointer you created has a spacial region of validity. Advancing the pointer beyond its valid region is something the compiler is always on the lookout for, among many other common mistakes that people have been consistently making for 60 years.
In general, as far as I understand, there is always some “allocated object” behind the pointer and if I understand correctly, the default address arithmetic is wrong from the language point of view, since we get away from this “allocated object”, right?
(edit: This is still using the bad "offset pointer beyond u32" code. It should be rejected with the same error.)
It generates unoptimal code (useless copying between GPRs) because the assembly is opaque to Rust. But that's also the reason it's accepted by the compiler. It's basically a poor opaque transmute.
Hmmm, in fact, the original code is accepted as long as it isn't run in const context. I don't know the reason for that. Maybe the provenance checks use const assertions?
Ok, I dug around in the rustc and core code a bit, and the reason it fails to error when outside of const context is because there is no guarantee that let bindings are const evaluated. Even in cases where it can trivially be done (all function calls are const and all arguments are constants).
That satisfies my curiosity. I think this limitation with let may be lifted in the future.