this is probably the most significant source of consternation but
also this code path has been fairly well-trodden.
That being said it might be worth considering if there is any
way to use some of the new Rust const based reasoning logic
to see if we can't tighten this up someday, but today is not
that day.
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.
we don't have pio block on nto; let's avoid maintaining two
versions of the code base for a non-essential feature (the
downside of not having this is that the system could end
up locking up if no processes call yield_slice(), but in
practice this happens often enough that we can get away with
it -- at least at this stage).
- updates enables or disables updates in the loader
- verilator-only will turn off routines that access hardware not
present in verilator (e.g. leads to a bus lockup)
- simulation-only turns of features that take a long amount of time
and aren't necessary in simulation, such as bulk clearing of
all RAM - in simulation, we can configure the simulator to do this
for us prior to boot; but in HW, of course, RAM is just random,
or worse, has the previous session's secrets in it.
cramium-soc is moving closer to the platonic SoC specifier,
and the board-* features are for the specific boards that use
cramium-soc
This division of feature flags feels a lot nicer and cleaner -
if it holds up to further development, I think this style might
become the new standard for Xous (as opposed to a single level
of feature flag where both SoC and board target are conflated in
terms of meaning - which kind of made sense when the CPU was
an FPGA and could actually have different features for different
boards, but for ASICs this wouldn't happen).
verilator doesn't implement the TRNG, so the TRNG is bodged.
A warning is left in to help remind us/alert us if the bodge
is accidentally put into production.
the 'dabao' board type is introduced, which is the breakout board
codename.
camera and display are feature-gated behind their respective board
definitions.
the `cramium-soc` target is now the simulation target (just
the bare bones chip in a simulation harness, using the `dabao`
board configuration which is basically just a breakout board).
baosec continues to be the prototype for the first USB dongle
coming out.
I felt uncomfortable leaving the oscillator setting entirely
outside the PLL routine, so as a compromise I defer the oscillator
switch-over until after the initial PLL settings are loaded.
This makes both the simulation model and the actual hardware
happy. I think probably what the simulation model *should* have
done is consider the "powerdown" pin when checking the validity
of the signals going into the PLL model - but this is impossible
to modify/check because the PLL is provided as an encrypted blob :(
this transition was in the reference C code, but in verilog simulation
it turns out it activates the PLL in an invalid mode which can lead
to glitching. Removing this makes both the simulation happy and
presumably the PLL happier as well.
this doesn't protect against malicious tampering (because
the public key is the developer key) but it will at least
catch correctness errors in USB upload and filesystem
behavior.