While learning to develop a RISC-V OS in rust (target: riscv64gc-unknown-none-elf ), I'm encountering inconsistent memory behavior when using core::ptr::write or direct pointer assignment (*ptr = ). The TrapContext structure appears valid in GDB's variable inspection but shows unexpected zero values in raw memory dumps.
Here is my code:
I do not know if this is the cause of your problems but I think your code has UB because you modify a pointer derived from the shared reference &self . To my knowledge using a usize round-trip does not change that.
Currently, the os works on single thread, and sync components haven't been implemented. Since the KernelStack is a static variable, I cannot directly use &mut self as the signature of function push_context. I'm not sure what the difference between &mut self and &self is here, aren't they converted to raw pointers with the same value? Could you expand on that?
Thatās what youāre doing here, by taking a &KernelStack and returning a &mut TrapContext to the same memory. You must explicitly mark your data as interior mutable by using UnsafeCell:
use core::cell::UnsafeCell;
struct KernelStack {
data: UnsafeCell<[u8; KERNEL_STACK_SIZE]>,
}
Then use UnsafeCell::get() to get a pointer to the data. This is the only way to allow mutation. All interior mutability in Rust is built on either UnsafeCell, or using an existing raw pointer to other memory.
Promise of &mut self: I'm the sole owner of that data, I can change it in a any way I want to, no one else may obsetver it.
Promise of &self: the data behind that pointer is āfrozenā and couldn't be changed by anything.
There are no way to āopt outā of these promises ālocallyā. They are global properties that program as whole have to support.
But there are āglobalā way to āout-outā from #2 promise: UnsafeCell. It gives you the ability to violate that promise wrt the data structure that embeds UnsafeCell into it.
But abuse of UnsafeCell can also be used to violate rules #1 and #2 for other types⦠and that's what you have to, now, guarantee manually (since compiler could no longer do that for you).
Thanks for all of your adviceļ¼
Finally I've found that I was miscalculating the spec offset within the TrapContext structure in my assembly code. Additionally, I spotted a typo in the command ā I'd accidentally used x/34xw instead of the correct x/34xg.
By the way, the aliasing and immutability properties of references (& and &mut) apply the same in single-threaded environments, e.g. holding a &mut _ active across a function call that locally creates an aliasing & or &mut (e.g. from a static mut) is UB even on a single thread.
That is to say, single-threaded is usually a red herring. You still have to follow the rules.