this is a first take at the poke_memory primitive, which is necessary
for GDB to patch in debug breakpoints.
I think the memory patching is *working*, but...stuff like single
stepping is still not working. This might be just me using GDB
wrong, tho. Need someone who actually uses GDB/is more familiar
with it to poke around a bit more.
this is gated behind the feature "debug-proc" because this could
leak attacker-useful information, and also it adds a primitive that
causes the kernel to write the debug information into any page
that the userspace already has access to. There are some checks
to ensure that the page is actually in RAM (so you can't use this
to trigger weird peripheral states, etc.) so hypothetically this
should just cause corruption in the userspace process if it is
abused, but still - more attack surface, more problems.
however, the feature is useful enough and the risk is small enough
that I think I'm going to leave it enabled for the foreseeable future.
It's definitely helpful to be able to see how much free memory
you have, and other resource allocation trackers like connection
limits and interrupt handler allocations.
debug shell is actually not reachable in bao1x targets because
the DUART is Tx-only. thus this code is just dead weight in the
kernel for bao1x platforms.
A particular edge case was finally reproduced & fixed. The edge
case occurs when a swap is triggered while handling an interrupt,
and an OOM happens during the context switch to/from the interrupt.
The background on this is that the hardware drivers for the swapper,
as well as much of the logical heavy lifting on e.g. what pages to
swap out, live in a userspace process known as xous-swapper.
The userspace process has to run free of interrupts - when you're
OOM'd or trying to swap, interrupts should not happen. The way to
do this in Xous is to run that code inside an interurpt context,
as interrupts are not interruptable.
Thus, the xous swapper userspace consists of two halves: a regular
userspace stub that provides some amount of control/debug over
the swapper, and a handler that gets run in a "pseudo-interrupt"
context.
Like all other interrupts, the handler "borrows" the stack of the currently
running thread - interrupt contexts don't have an explicit stack, they
just grow on top of the current stack of the current thread in the
current process at the time of the interrupt.
This is broken because when you have a swap event inside an interrupt
context, there are conditions where you might not have a stack. What
was in fact happening is that the stack pointer was being orphaned
as being the "exception stack" - which is a temporary location,
only accessible by the kernel, used for storing all the registers
going into and out of exceptions. Since no stack pointer is set up,
the moment the userspace starts to run and spill its registers to
stack, the system trips a "nested swap" event, which leads to a panic
because this is not allowed.
The fix to this is to devote two hard-wired pages, mapped in the
loader, to be the swapper's private stack area. Instead of behaving
like an interrupt and borrowing the caller's stack, the swapper instead
goes back to its hard-wired stack to do all the processing.
This should also improve the performance of swap handling in some
cases because we can now guarantee the stack is always in-memory
and doesn't need to be swapped in during swap processing.
One complication is that the "reserved memory" that the swapper
puts aside in case it *does* need to allocate extra pages during
handling can't be freed at the top of the OOM routine. Thus if
the swapper ends up using new pages, we should expect yet another
crash.
However, in practice, this doesn't seem to happen, because the
cases in which we hit needing a new page are typically related
to cases early on in boot where superpages/new page table entries
are being allocated, or if we're growing the heap that passes
data back and forth between swap and userspace. Because this call
triggers in such a specific condition, I hypothesize that actually
in practice it should generally be able to run without allocations.
One case where maybe it could hit an allocation is if the swapper
hit, for the first time, in an interrupt handler that was swapped
out, and never hit before, and the interrupt handler needed a
superpage to be allocated, thus triggering an allocation request
for a new PTE or two. I think this is probably pretty unlikely, but
if we do hit it, the fix would be to, for the purposes of targets
with "swap" enabled, to mark unmap_memory and map_memory to be
legal syscalls in interrupt contexts. These are normally illegal
because interrupts should always return - and these calls could
potentially fail or block. However, I think in this case it's
a trade-off between "we should be failing if we run out of memory"
and that failure being a potentially incorrect behavior that is guarded
against by a runtime check to discourage such calls.
have to absorb a version change due to xous-semver change
might as well also update the utra/svd generator dependencies
since that's been held off for a long time now.
The code base had evolved through several code names, interim
names, company names and chip names that alternatively did or did
not exist.
Baochip is the definitive name of the company, and the Baochip1x
(full part number BAO1X2S4F-WA) is the definitivey part number of
the SoC. For convenience, the part number is shortened to 'bao1x'
in most feature flags and variable references.
This commit fixes all of the odd names and removes some unmaintained
early targets.
This relies on a trick I was not aware of previously, which is
that transitive dependencies absorb the additive set of features
enabled within a crate.
see
https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
for the full description. but basically by referring to the feature
required in "aes" despite not being used in the crate but pulled
in by aes-gcm-siv, i can ensure that the transitive dependency
is in fact used.
I think this was always there but previously I just didn't understand
it correctly or didn't use it right when I tried to deploy it, but
this time I think I got it right.
Based on this benchmark of elapsed_ms():
```rust
pub fn elapsed_ms(&self) -> u64 {
let response = send_message(
self.conn,
xous::Message::new_blocking_scalar(api::Opcode::ElapsedMs.to_usize().unwrap(), 0, 0, 0, 0),
)
.expect("Ticktimer: failure to send message to Ticktimer");
if let xous::Result::Scalar2(upper, lower) = response {
upper as u64 | ((lower as u64) << 32)
} else {
panic!("Ticktimer elapsed_ms(): unexpected return value.");
}
}
```
We find the following context switches:
1. send_message -> kernel/src/syscall.rs @ 166 - ss.remember_server_message()
- *context switch round trip* to enqueue the message into the target thread's space, then switch back.
- takes 546ns just in the target context, out of 4430ns round-trip
(measured as leave userspace of caller to enter userspace of callee) (12% of total time)
- emplaces the message in the target queue
- generates "sender_idx" which is the index in the message queue to find the message, which is put into Envelope
2. send_message -> kernel/src/syscall.rs @ 222 - ss.activate_process_thread()
- activates the thread to run
- Total penalty is closer to 1us - 22% of total time - once pre/amble/post-amble overhead considered
This set of patches removes the context-switch round trip to emplace messages in the target's thread
space, as well as to set thread results, by passing them as arguments to `activate_process_thread`.
What then happens is that these items are emplaced after the activation privot to the target
process space occurs, saving a set of TLB-flushes and page table walks.
More testing is required, but at least on a reasonable set of cases the system seems to be stable
with these patcehs, and we get a 24% speedup on blocking scalar messages and 8% speedup on
mutable lends in the case that the target is waiting to receive messages.
The double-flush in activate() has a big impact on performance,
and I'm pretty sure it only needs to happen after the SATP update
not before, at least architecturally. Eliminating the extra
flush improves the fast message passing benchmark time by 9%.
The "main" product line is affected here in that the UART
init is hosted above the trng init. This should have no impact
on security or boot time, but allows us to more easily debug
TRNG init issues. This is necessary because in the vexii test
version the XADC interface is broken by the slower clocking
and I'm not taking the time to trace down that issue as it
does nothing to further the goal of testing the vex-ii core
in a non-production config.
legacy_int handler allows vex-ii to emulate/behave like the vex-i
in terms of interrupt handling.
eventually this will be replaced with/augmeted by a PLIC.
1. Instead of panicing when a page is re-requested, return an
error message to the caller. This allows us to have the UART
pre-allocated by the loader and the kernel to request it prophylactically
2. add A/D flags for R/W pages in vexii-testing. This is a work-around
until we have proper A/D tracking in the page lifecycle mechanism.
cramium-soc i think should be deprecated everywhere because
it is redundant with the board targets: anywhere you see a board
target that includes the crossbar soc it should also transitively
pull in the cramium-soc dependency equivalents.
we'll chase this out more later, but for now we're just trying
to get the PDDB to compile
this is a much simpler, race condition-free version of flash
writing which has the penalty of requiring a copy of the page
to be made by the caller before the write. However the
copy is mostly already paid for because on the initial access
to the page, we had to copy the flash into RAM.