Commit graph

789 commits

Author SHA1 Message Date
bunnie
e27b30d818 fix edge case in swapper
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.
2026-01-08 02:08:03 +08:00
bunnie
f879068bbb cleanup retired apis and improve swap debug output 2026-01-08 02:05:01 +08:00
bunnie
a3a566b240 add API calls for debugging kernel under baochip targets
also updated some really bit-rotted docs

we will deprecate the docs/ directory soon, they are wrong
and misleading at this point.
2025-12-09 16:19:04 +08:00
bunnie
4f8169c3d9 fixup rand for simulation target
TRNG doesn't exist in simulation - substitute it for a
deterministic PRNG when the simulation target is specified.
2025-09-20 22:13:16 +08:00
bunnie
1255bf12d9 FLAG DAY: fix all names to harmonize around 'bao1x'
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.
2025-09-18 21:25:51 +08:00
bunnie
365657984c add debug info on irq failure 2025-09-09 17:50:03 +08:00
bunnie
ecbac27e76 fix rustc-1.89 warnings 2025-08-19 19:45:29 +08:00
bunnie
bd48886cc1 fix small syntax errors from refactor 2025-07-29 17:27:26 +08:00
bunnie
4ba2b476d1 Merge branch 'more-opt' of github.com:betrusted-io/xous-core into more-opt 2025-07-29 17:24:26 +08:00
bunnie
c151688822 convert enums into named struct per @xobs suggestion 2025-07-29 17:23:08 +08:00
bunnie
2119413b7d
Update kernel/src/syscall.rs
Co-authored-by: Sean Cross <sean@xobs.io>
2025-07-29 17:20:50 +08:00
bunnie
d58a0c763f
Update kernel/src/services.rs
Co-authored-by: Sean Cross <sean@xobs.io>
2025-07-29 17:20:35 +08:00
bunnie
a35f20b1c8
Update kernel/src/services.rs
Co-authored-by: Sean Cross <sean@xobs.io>
2025-07-29 17:19:26 +08:00
bunnie
f53502d9a2
Update kernel/src/services.rs
Co-authored-by: Sean Cross <sean@xobs.io>
2025-07-29 17:19:18 +08:00
bunnie
126896bf54 optimize kernel message passing times
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.
2025-07-29 01:24:38 +08:00
bunnie
25f7bd6f52 remove some redundant mmu flush commands
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%.
2025-07-26 00:46:15 +08:00
bunnie
9c7ed885f9 add vexii-test entries to the cramium target 2025-07-24 00:29:21 +08:00
bunnie
89d80f6288 make the kernel irq extension generic to verilator target 2025-07-24 00:28:10 +08:00
bunnie
1effeab858 add legacy_int handler to the kernel, handle vexii init case
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.
2025-07-21 17:11:23 +08:00
bunnie
52abe83b74 handle vex-ii a/d flags and error gracefully on page re-allocates
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.
2025-07-21 17:08:56 +08:00
bunnie
de8ddbf9c2 handle case where uart is re-allocated by the kernel
this case happens when the loader is instructed to pre-allocate
the kernel UART page based on the early-printk feature.
2025-07-21 17:07:28 +08:00
bunnie
4fb3a3da43 add vexii-test shims for irq subsystem in kernel 2025-07-20 19:03:29 +08:00
bunnie
04c7933c87 handle case of write pages being swapped out upon write release 2025-05-21 18:02:21 +08:00
bunnie
19455d1bc0 add block erase routine 2025-05-21 10:20:04 +08:00
bunnie
23c2b77ace fix hosted build 2025-05-18 09:24:22 +08:00
bunnie
b1a8b6fdae cleanup some debug 2025-05-18 04:33:49 +08:00
bunnie
289028fa48 deprecate sync/dirty and emplace the writepage syscall 2025-05-18 04:25:18 +08:00
bunnie
dfd82c94b8 refactor write page syscall for explicitly managed writing
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.
2025-05-18 04:23:53 +08:00
bunnie
788efb3056 cleanup dirty-bit attempt to track pages 2025-05-18 04:23:20 +08:00
bunnie
1a5a0a1388 attempt to fix race conditions is dirty/flush loop in swapper
this ultimately failed, we're going to try a different approach
2025-05-17 16:27:17 +08:00
bunnie
979ce7a589 add missing args to flush syscall 2025-05-17 16:26:51 +08:00
bunnie
ca61545ad4 implement syscall wrappers for marking dirty and sync of flash 2025-05-16 19:13:20 +08:00
bunnie
b86612f614 implement WriteToFlash on the kernel side 2025-05-16 19:11:05 +08:00
bunnie
75073aab99 add functions for marking pages as dirty in the RPT
this is what ultimately allows us to decide whether or not a
page can be flushed from memory safely
2025-05-16 19:09:53 +08:00
bunnie
b8c30c32ac fix bug where pages were not marked as belonging to USER
it's assumed the kernel would never want to access the flash
directly, so no mode is implemented to support this possibility.
2025-05-15 11:52:41 +08:00
bunnie
3f0b833b90 add swapped flag to flag translation 2025-05-15 11:51:53 +08:00
bunnie
7af009be78 migrations for moving constants out of xous-rs/kernel and into board/platform
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.
2025-05-11 12:50:02 +08:00
bunnie
63e4319314 add partial support for memory mapped flash (alloc virt without phys backing) 2025-05-11 12:49:23 +08:00
bunnie
f5315dc014 incorporate partial support for virtual-only alloc swap into kernel
also has migrations for constant locations
2025-05-11 12:46:56 +08:00
bunnie
4f14a67504 formatting changes due to changes in nightly rust
needed to get a clean check on gh
2025-02-01 22:26:40 +08:00
bunnie
c8cb5cda47 ignore dead code in shell.rs
when the debug stuff is commented out, a bunch of unused code
warnings happen in the shell interface
2025-01-01 22:48:52 +08:00
bunnie
03d67de1f6 kernel in -fpga variant uses duart 2025-01-01 22:48:32 +08:00
bunnie
4f21bf3c3e raw pointer dance for memory allocations and extra allocations
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.
2024-12-02 19:10:39 +08:00
bunnie
b59177176b raw pointer dance for the SWITCHTO_CALLER 2024-12-02 19:06:25 +08:00
bunnie
8876f6e7f5 raw pointer dance for IRQ handlers 2024-12-02 19:05:56 +08:00
bunnie
9662f13d31 raw pointer dance for kernel TRNG 2024-12-02 19:05:24 +08:00
bunnie
64612352d3 raw pointer dance for debug UART 2024-12-02 19:04:37 +08:00
bunnie
95611d9598 This begins a series of commits to cleanup a static_mut warning
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.
2024-12-02 18:55:18 +08:00
bunnie
6961bb3e54 fix a bug in debug-print when swap is not specified
a field doesn't exist that is accesed by a debug-print resource
in the case of swap being inactivated
2024-11-30 02:21:20 +08:00
bunnie
820acfa2e6 make sure that swapper pages are marked as wired once allocated
also improve debug output, it was a little nonsensical previously
(some bit rot on meaning of fields)
2024-11-07 16:10:27 +08:00