this is a fairly major re-org of where key constants go that
define platform features. things that were in xous-rs that were
platform specific (namely precursor) were moved into a boards file,
and things that are generic to the system (like PAGE_SIZE) are moved
to a public location in xous-rs::arch.
So in Rust 1.83, we now get a strongly worded warning telling
us to really, really don't use static muts. They aren't wrong,
except that in some places like the kernel we have few options:
we are conjuring pointers out of thin air, creating memory mappings,
and then doing things with them that are hard for the compiler
to reason about.
I think the newly minted warning was made because the intention
is that we could use the new fancy `const` semantics to take
the place of static mut usage by wrapping things in UnsafeCell,
and I think maaaaybe in some
cases this could apply here, but as a simple example a dive
into the kernel debug UART indicates that to initialize the
shared variable we have to allocate pages in the page table,
which involves calling the memory manager and doing some
decidedly non-const type of things. So one option is we could
just push the debug UART initialization into the loader and
paper over the whole thing by pretending it's static.
Instead of that, we adopt a pattern of casting all our static
muts to a raw pointer, and then back into what we want. The
cast to raw pointer is like an "uber unsafe" marker that says
"no we really mean that this pointer is where it is and we
conjured it out of thin air and we promise that the behavior
is defined and values are representable, etc.".
In general we're fairly judicious about the use of static
mut in the kernel, and when in the kernel there's no thread
pre-emption (although we could take an interrupt or page
fault) so generally we are single-threaded. The trickiest
spots are when we have to take a page fault while handling
a page fault (for example, allocating a new L1 page table
entry would require something like this) but this code path
is fairly carefully vetted.
So instead of just doing a global lint silence, we use an
`&raw` idiom to call out the fact that we're doing something
naughty wherever we do it, which makes the compiler "happy"
(for now) while still making the code ugly enough that it
looks sufficiently scary and you have to apply some
cognitive burden to even reason about what's going on,
which I *think* is the intention of Rust in this case.
Basically you get a get out of jail free card but you can't
simply wear it on a lanyard and pass through the gate -- every
time we need to use it, we have to do a little dance and
wash ourselves in the waters of unsafety and recite the
mantras about defined behavior, representable numbers, thread
safety, etc.
not doing a reformat of the entire project to match
nightly formatting changes, but capturing the published
crates so we can maybe save a version bump later on to get
the project clean.
previously we could re-enter into a process through a divergent
call into the swapper. Now, we only ever re-enter as a syscall
through the syscall interface.
in order to make this happen we had to store all the thread and
stack state for the handler so we can do a re-entrant syscall, but
I think this is worth it. makes the code so much better.
was accidentally passing the whole address before, which was
causing the LSB's that are assumed to be empty to muck up
some page table computations.
Also make sure old PTE entries are properly masked when recycling
flags.
- no swapping of kernel pages
- mark L1 PT's properly in the memory allocation table
(was accidentally marking them with the VA of the first address
allocated)
instead of sending scalar messages on every page alloc/free, which
is a lot of traffic, introduce an API where the swapper can request
a snapshot of the alloc status by mapping the kernel's alloc
table as a read-only structure into the swapper's memory space,
and then invoking the swapper in its interrupt context to read that out.
The structure is unmapped on return from the context.
additionally, a running tally of free space is now maintained
and this can be queried with a fast syscall.
Thus, the normal workflow is the swapper would have a timer that
periodically queries the kernel's free space level, and when it
falls below a critical amount, it will invoke the expensive snapshot.
this can also be extended so that the kernel can call into
the swapper's interrupt context with a "help, OOM imminent"
message down the road.