#BadgerOS
1 messages · Page 2 of 1
anyway this is in no way an actual immediate problem
just something to keep in mind
so no need to worry about it too much
So now I mustm't;dshall figure this out maybe eventually idk
yes it is an interesting problem
for now you should consider not making your ramfs use massive contiguous buffers since that probably will make the allocator unhappy
I don't see a way to do this without a stack?
you "simulate" a stack
just like how you simulate one for other defunctionalization problems to turn a recursive problem into an iterative one
Yeah but a softstack is still a stack
ig it's better than using 64 bytes or some shit per layer on the actual kernel stack
Actually it's probably more
rust stack usage gameplay
I have 8K stacc right now.
At some point I will impl stacc guards so I can detect overflow
Anytime it does I make it larger and try again 
I guess it's the same thing as using the softstack in the walk function
but in the I/O functor insteads.
too small unused ram is wasted ram, make your entire stack swappable to disk and use all the unused address space as stack 
For real though why the fuck does anyone bother trying to swap literally any part of their kernels?
some people use systems with a solid 2 Megan Bytes of RAM so they don't have the luxury of keeping everything mapped all the time
well I'm dysphoria programming too hard to bother with that bullshit
one cool thing you can trivially do is put init functions in a custom linker section and after all of them run, you unmap them and mark them as free in the physical memory allocator
lots of real kernels do this like linux with __init
it not very hard to do either
what if you want to boot your system on a dec alpha

very realistic concern
realim
actually those had up to 8 Giggler Bytes of RAM nvm I forgor
I don't know if there's any good reason to page kernel code anymore
But kernel heap there def is
Because there's hundreds of megabytes of that
That mean I would need to tag krnel heap with swappable or not
imma go ahead and don't
More for me
you can have my heap i wasnt gonna eat it
the javascript developers will be very unhappy that the kernel is using 12 whole precious kilobytes of RAM that they can't use to keep their node modules cached in ram
PAGE_FAULT_IN_NONPAGED_AREA 😔 ✊
Why are you talking like that
Now working on realpath and mount
Those work, now make_file, link, unlink and rename...
With that part of VFS mostly done, I'm going to add the character and TTY device classes now.
/dev/zero and /dev/null will become character devices
My VFS now has the infrastructure needed for something like mount /dev/sdb /mnt to work 
I will now attempt to make the switch over to the new VFS for the whole kernel
This may take a while
I'm in a slightly akward spot where the C side is now going to be lobbing around fat pointers (read: a struct with two pointers in it). And my ELF loader (which is already a bad fit because it's designed to linked dynamic files into the current executable, not be a kernel static ELF loader) doesn't accept anything but normal pointers. Kinda tempted to rewrite that too but for now I'll just box it.
good luck!
Is it like a typical struct Path { DEntry *dentry; Mount *mount; }; kind of thing that you're moving everything over to?
Many things that only deal with individual files and never care how they are connected to the rest of the filesystem hierarchy are probably fine with dealing with DEntries directly.
No the problem is that my file handles are Arc<dyn File> (a trait object) so they're fat pointers.
Ah, ok.
So it'd be a struct {void *data; void *metadata;} situation
ah yeah so this is a C <-> Rust interop thing?
Yeah
Existing C code will need to interact with the VFS. This mainly is the program loader.
The VFS works so far.
Init crashes because it's a test for pipe and I haven't reimplemented that yet.
I also need to reconnect the devtmpfs to the code that puts stuff in it.
Currently working on reconnecting the syscalls to the VFS. With a bit of luck, the VFS rewrite will be finished today, after which I'm going to make the logic for mounting the root filesystem.
EVERYTHING the old one did is IN and WORKS 
Even the pipe test!
Well except for the FAT fs driver. I still need to do that one.
I guess I'm going to make that now
fucking balling
Did some good progress on FAT the last two days, I'd say I'm 70%? To reading a known-good FAT filesystem.
epicc
which fat
32/16/12?
or exfat + vfat extensions and whatnot
lol if this is a unix system you'll probably have to fake inode numbers for FAT
🤪
pretty cool though
check iunique from linux to see how they do it 👍
VFAT32/16/12
indeed
awesome sauce
Reading dirents works now... Unfortunately, reading the FAT doesn't :P
OK so I'm tired of VFS for the moment and since I also need to re-work my PMM (and a little bit the VMM too), I'm going to do that now.
Long live git branches.
yooo at least dirents work !
understandable
I worked some on the PMM today, main goal is to make things like demand paging and CoW mappings possible. I'm making space for mmap'ing files and swapping userspace out, but that's more VMM than it is PMM.
To achieve this, I'm making a new page allocator (buddy alloc again) that uses a refcount instead of just an allocated/free state.
Fortunately most of that is in already

Also my mom says hi 
hi mom
Making new MMU abstractions in Rust right now, as the code that will be dealing with vmm is, of course, getting written in Rust now.
makes sense
I'll be doing just replacement of page table manipulation first, which I'll test with the existing C memprotect infra.
After, then knowing the page table code works, I will then replace the remaining memprotect with the new Rust VMM.
All this will be based on the old page allocator because it works fine and I don't need to replace it yet.
Bored of Lily-CC again, now we ball wiff more VFS code.

Now I shall implement the long filename extension for reading.
Yes I know I probably have ADHD lol
You'll never guess what happened when I tested long file names 
Ok I fixed it, reading LFN no longer causes an I/O error 
Tested multi-cluster file reads, those work too now, very good
Writes to existing regular files work
We doing more FAT today
I've mostly finished unlink/rmdir and am now working on mknod
That always happens to me when implementing filesystems
I don’t even know how to prevent that
WOW I DID NOT REALIZE THAT MESSAGE WAS OVER A MINTH OLD
While I was at it, I added support for the extension that lets you have all-lowercase filenames without LFN entries
Almost done with the logic that writes new dirents
Removing regular files works without leaking nor corrupting the filesystem!
Same for directories
Have untested stat now, but first time for eep
stat works, mknod time
Ok so now I have working mknod
Only rename is left before FAT is completely finished 🎉
Nice
After some discussion #filesystems message, I've re-worked my dirent caches a little bit and made my VFS no longer implicitly rely on inode numbers. Instead, for filesystems without real inode numbers, they are faked, and the dirent cache is used to prevent duplicate file opens.
Damn, I really thought I'd be done by today.
But at least I'll end up with a proper proper VFS and FAT impl.
Ok so mknod for the directories had a bug but that's fixed now.
And: I've got rename in too!
I've got FULL support for vfat! 
No, I don't abuse my poor git repo, what are you talking about?
Currently squashing FS and VFS bugs, and working on finishing umount.
If all goes well, I'll have FS automount done by this evening.
I am beyond stunned that reclaiming memory directly from the limine_memmap response without caching it (thus overwriting the response as it's being read) hasn't caused a crash before.
93 files changed, 8620 insertions(+), 6172 deletions(-)
VFS rewrite is finally merged! And automounting root works!
epics
I'm actually kinda proud of what I did here for the Ext2 on-disk structure definitions
@solid bronze Do you approve of my fancy Rust macros here? :D
cursed
lol
Almost ready to do a read test already!
Simple mk2efs in build script, let's see how hard BadgerOS explodes when automounting this lmao
Calculated addresses of shit wrong so now the block group descriptor table reads 0... classic.
It's progress
@fading elm if you want to install your OS from a USB stick, you'll need to be able to write the fs, right?
Yeah, I'm actively working on Ext2 write right now.
I'm going to use existing userspace tools for partitioning and filesystem formatting, then mount the filesystems and copy it all over.
Since I boot with Limine, a bonus would be porting the Limine installer binary so it can boot on BIOS (if somehow a 64-bit system has BIOS but not UEFI)
that's what I did too
on Maestro I use GRUB. I have grub-install working so I am able to install it from a USB stick too
AFAIK that's how most installers work
But I will need some stuff like USB drivers before any of that will happen
Modesetting would also be cool at some point, no idea how much effort that'd be
Oh and don't forget VMM refractor, fork+exec, shebang, TTY, actual shutdown and reboot, etc
I don't have a USB driver. I just have GRUB loading the whole fs (with packages to install) in an initramfs
(that's bad, don't do that)
I implemented it just for this purpose 
that's less efforts than USB
why lol
Because I didn't write anything that unpacks one

Also just remembered I'm gonna need to make a new ELF loader at some point because KBELF, though made by me, is not made for kernels.
(It's designed to load stuff into the running program)
((And even then only sort of))
whats kbelf
My ELF loader library
Not intended to be a kernel ELF loader, though.
https://github.com/robotman2412/KiloElfLoader Not the cleanest codebase ever either lmao
I think the reason I'm getting Ext2 FS corruption is because my pagecache is broken 
First Ext2 write without corrupting it!
Free block count is still wrong, but it no longer makes the FS unreadable
And now those counts are also correct 🎉
struggling with the same thing 😭
Working on link and unlink now
unlink without corrupting the FS 👌
link too, let's test making dirs and symlinks
Yeah mkdir still corrupts a bunch of crap lol
Now there's just rename and some (other) codepaths for inode cleanup left
MM rewrite time
New page table creation and walking code is already there, now it's time to build VMM on top of that.
I feel like after this, a process subsystem re-write is in order.
Ok the new page table building code works now
Well then
Bad news: crippling dysphoria
Good news: I finished the last two little shits about Ext2 at 4AM
Got the kernel to boot under new VMM, but the init process explodes and generally takes really long to make the page table mappings
Damn, command lines people invent to avoid —vcs=git
wait what?
XD
This is so much easier lmfao
Don't know where it's getting fortran from lol
Thank you for bringing this to my attention XD
Np lol
Incorrect page table mappings 
How am I supposed to debug my page table when this is what info mem prints???
There shouldn't even be anything mapped with accessed and dirty bits cleared 
Now I'm at the part of the rewrite where I need to actually change my PMM.
VMM is up to where it was, but now I want to implement CoW mappings and memory accounting, so I need to update my PMM to make that possible.
After some discussing with ilobilo, korona and marvin, my buddy alloc is almost finished; I estimate by this weekend I'll have the new PMM done.
However, I don't know exactly how I want to go about mmap'ing files from the VFS perspective just yet.
@flint shell My buddy alloc is almost done but I'm concerned that trying to allocate blocks from multiple threads concurrently may cause the operation to spuriously fail, even if there's enough available pages of that order after e.g. the other thread splits a larger block. Do you have any suggestions as to how I should go about this?
I can only really give suggestions if you explain your proposed design
Current design uses these:
/// Physical memory page metadata.
#[repr(C)]
pub struct Page {
/// Page refcount, typically 1 for kernel pages.
pub refcount: AtomicU32,
/// Page flags and usage kind.
pub flags: AtomicU32,
// TODO: Pointer to structure that exposes where it's mapped in user virtual memory.
// Kernel virtual mappings need not be tracked because they are not swappable.
}
/// Physical memory freelist link.
#[derive(Clone, Copy)]
struct FreeListLink {
prev: PPN,
next: PPN,
}
/// Physical memory freelist head.
struct FreeListHead {
first: Spinlock<PPN>,
amount: AtomicPPN,
}
/// Free lists per buddy order.
static FREE_LIST: [FreeListHead; MAX_ORDER as usize] = unsafe { core::mem::zeroed() };
Spinlock guards each order in the free lists, and the amount is used to atomically reserve one without needing to lock immediately
But I'm considering just using a global spinlock as the current design arguably still doesn't really let multiple threads truly concurrently allocate pages.
I could always help the concurrency bit by making a sort of wrapper around this that reserves a pool of pages per CPU
you could use sharding for the free lists
= not have a global one but have around #cpus ones
Okay
but that ofc adds complexity because you need to look up the right freelist for the page that you're freeing etc
Yeah
And if you're very memory-contented, you will need to "steal" from other CPUs' sections
I think I will keep it simple for now and just use the one spinlock for the freelist. If I need better perf in the future, then I can improve it.
It manages to do VMM init now before crashing just after.
Can confirm that my buddy's freelist is horrendously broken.
But at least I know in which way now, so I can fix it.
Turns out I was accounting the pages incorrectly when they got split, causing the blocks that were created to think they were an order bigger
Now there's a nullptr crash in DTB, which is a bit interesting
I suspect that some part of the MM refactor caused this
My DTB parsing code appers written in an incredibly bugprone and fundamentally broken way; I'm going to replace it and see if the issue persists.
DTB parsing should be pretty straightforward
Yeah, well, I wrote it on crack it appears
It's one of the oldest pieces of C in my kernel atp because most everything else has been updated since then or even rewritten in Rust.
seems like we do need a uDTB afterall 
I mean
I could make the DTB rewrite into a more platform-independent way
But I won't
how can you make dtb parser platform dependable 😭
It's called my stupid ass thought it was a good idea to make my own version of string.h
But I'm not going to fix that right now because eventually C is going out of my kernel anyway.
libfdt is basically uDTB 
true
Fortunately DTB is a simple enough datastructure, even if you want to have a map from phandles to nodes.
yeah its hard to come up with a simpler structure
it's almost a bit too simple
you basically need to build an auxiliary data structure to work with it efficiently
since you can't just skip over a node without reading it entirely (and all its children etc)
i do realize that this is by design and that it allows firmware to modify it in a straightforward way by just memcpying data around (w/o changing any pointers or offsets etc)
but it's sometimes a bit annoying
Yeah, it does result in unpacking it in its entirety.
I do still kinda hate how DTB is big-endian for some stupid reason
we already have that
i think @scarlet bison made one?
nvm
it was @mental pagoda
Tiny and portable device tree parser, written in C. - DeanoBurrito/smoldtb
honestly
I'm just gonna bind that for Rust and call it a day
Parsing DTB isn't high on my "want to do" list
Just goes to show how much more you can do with the help of others I suppose
Isn't it easier to write DTB parsing code than it is to write rust bindings for that?
Just make a trait Visitor with two functions on_node and on_property, walk through the DT once and call these functions?
I've just finished my new DTB parser. I will create tests for it later, then see if this fixes the problem.
If it doesn't I may consider binding smoldtb instead.
No, writing bindings is still easier, but I was already most of the way though my own DTB parser so I just kinda finished it.
Out of curiosity, which part are you having issues with?
Like I said, the old one was just made badly
Don't know why
But it probably has to do with that it was written fairly early on
I've become a much better programmer over the course of this
I might have to take another break from BadgerOS
I just want to get cool stuff running but all that's happening is fixing tech debt
thats fair take your time
I just realized I'd been arbitrarily slowing down BadgerOS' logging 
I have no idea why the delay is in here 
I have nothing more to add
Oh lol
Well I do. for one, no SUID binaries at all.
That means getting root permissions is talking to init
That’s not stopping you from leaning on work already done, also systemd run0 comes to mind
Hell Managarm didn’t support SUID for a while (we forgor whoops)
So that should work with some patching
Another one is I'm adding a custom API for both input and window management because I have some funny ideas about making the same binary run on a microcontroller and on this cursed mess
(Yes, I still haven't given up on BadgerOS microcontroller edition)
Valid, doesn’t concern libc at all, requires the obvious patching to relevant stuff getting your backend in
This bit right here is also why I was considering newlib, because the environment that said microcontroller runs has newlib
Don't worry, I'm not going to make this entire userspace run on a microcontroller
It's more like running what is on the microcontroller on the bigger computers too
NS16550A time because I don't want to work on PMM right now but I do want to work on BadgerOS.
That break didn't take very long, did it? 
I'm going to focus on some more fun stuff before I return to the chores like PMM and such
One hour of badgers, mushrooms and snakes.
MrWeebl is a channel of fun and silliness, with songs and cartoons and even cartoon songs. Wow. Some call it random. It's not random. It's absurd look at life because day-to-day life is ridiculous.
...
haha yep
I'm continuing work again on the device subsystem re-write
This time with much less unsafe shenanigans to cause shitfuckery
The new Rusty DTB parser that isn't made out of bugprone-ness is finished
Ok so that's actually going to be a ton of effort I don't want to do now. I will still rewrite it all to Rust eventually, but I'm going to try to debug the existing thing first.
I actually
at this when I saw it
Because this means my serial driver, interrupt subsystem and device subsystem all work properly!
Ok so it turns out that had some PLIC bugs causing interrupts to only half work, but now that's fixed too.
The new page table code I wrote for the MM refactor a while back is panicking due to an apparent corrupt page table.
I've also fixed some other bugs that were blocking adoption of this refactor, though there's probably loads more. Also, my way of finding an empty range of virtual memory is highly inefficient.
Yooo the new page table code actually works!
Time to add the new features (CoW pages and mmaps)
I'm changing how this is structured slightly, making a struct Memmap for all user memory maps (and perhaps the kernel too?); this will allow me to build an API where everything within is purely based on the intent of the mapping, rather than the old system of directly manipulating page tables.
Turns out my new PMM is both horrifically slow and broken for unknown reasons 
Oh wait the slowness is just a debug assert that walks the entire freelist.
Still broken though.
Turns out I was marking part of the page structs as usable memory 
Ok now realloc is broken
I wasn't updating the block size correctly when allocating physical memory
I am 1. somehow running out of virtual address space (should be impossible with only 1G RAM) and 2. still wondering why the PMM is slow
Yeah so QEMU TCG (because RISC-V on an x86 host) + Rust being -O0 by default really does a number on performance
you can override optimization level for the debug and release profiles in Cargo.toml
i think you can do toml [profile.dev] opt-level = 2or something like that
i do opt-level 1 by default
O0 is unusable
yep
Look like I'm getting an interrupt storm now
Actually it's a deadlock?
Long live heisenbug land 
Well it's not a heisenbug. It's just. Something that will be hard to track down.
I also fixed implicit hugepages so setting up VMM isn't super slow anymore.
In theory the new MM is good to use, but I don't want to merge before I've excluded this from being an MM bug.
Turns out the new page table code has some more mutexes and stuff in it, which is fine until I realized that the thread reaping unmaps the stack. Trying to acquire the threads lock again because of the waitlist_notify when you release a mutex.
I'm probably going to have to rewrite 2.0 the scheduler at some point looking back at all this deranged garbage. But it works just barely fine enough.
Actually that was only 1/2 of the problem
The interrupt storm is still there
I could see it breaking in this way if perhaps the I/O pages for the PLIC aren't mapped properly, causing interrupt sources to be misconfigured?
Ah. The interrupts are being enabled before the driver for the CPU-local interrupt controller is installed. Then a normal preemption timer interrupt fires.
Now why exactly this timer interrupt took nearly 30ms to deliver I do not know.
Ok, all the new MM code works! Now, it's time to make mapping pages fast again.
Tangent to make x64 run again with new MM
I hope to be able to run the QEMU profiler on that so I can optimize the MM a little bit.
New MM merged, time for some drivers
Ok so I know y'all voted ACPI and I did work on it but I put that on a branch for the time being. Now begins the process subsystem re-write because I wanted to.
Funny how easy ELF loading is if you don't for some reason try to make your kernel do dynamic ELF loader duty.
trolled
Yeah I clearly had no idea how to ELF load first time I made any of this crap
better than loading an elf using its sections
Even though I didn't call it an actual rewrite, in effect I am rewriting most of BadgerOS since I decided to add Rust.
Funny how much faster it goes once you know what you're doing.
did someone actually? lmao


I'm approaching a point where I need to go out of my way to rewrite things in Rust because the C code just gets in the way otherwise
Over 50% of my kernel is Rust now
I think in particular the scheduler is one of these things
load_executable works, now it's time to reintegrate syscalls
(and also decide whether I want to keep doing it the same way)
I've decided I will keep syscall working as they do now and put signals on hold. I will refactor how context switching happens after most of the new process subsystem is in, after which I can reimplement signals and possibly do some more polishing on the scheduler.
After some discussion with @solid bronze, I've implemented safe user-copy abstractions and with that started reimplementing all the syscalls
currently impl'ing signals
Synchronous signals work™; I'm not dealing with the async ones until after the scheduler rewrite so now it's time for fork and exec!
Actually, I need to do some more VMM stuff before I can support that because fork() makes CoW mappings but the VMM doesn't actually implement those yet.
CoW is in but untested
how bad is it
2 years ago I wrote sched but didn't separate it from context switching at all
Makes it unreadable and inconsistent af
It's probably the same kinda effort it was for me to e.g. rewrite VFS
Unfortunately I think fork is dereferencing a stale pointer and/or causing memory corruption
Rust didn't prevent this because there's a lot of switching between C and Rust layers here, making preservation of safety harder.
Now we have a nice clean fork() except for a dirty scheduler side because I'm rewriting sched soon anyway. Next up will be exec() and then porting some fun stuff like busybox perhaps.
Cool, exec() works too now
Wow, almost 2 years done now! Congrats.
Thanks!
And after those nearly two years, it is now finally time for me to build a distro. First step: Port mlibc and make a cross compiler for BadgerOS userspace :)
looking forwards to it.
my toolchain is just f'ed.
looking forwards to taking inspiration from your code :D
I'm probably going to look into using jinx
I try to keep it sane and readable... Sometimes I succeed 
for me the problem is my patches
and my build system which wont reqonise the target auroraos when building libgcc
Yeah that's why jinx uses a Debian container
Well, it aint the container thats the problem. I straight up stole the patches from gloire.
And now its something that its missing.
It is imperative that jinx be run under Linux, as the container environment relies on non-POSIX Linux features. You will run into issues on other systems.
Oh. I really want to be able to bootstrap at some point (compile BadgerOS from source on BadgerOS).
@sullen cipher Is there a chance I'll be able to implement the Linux-specific features jinx relies upon?
Eh I'll figure that out when I get there. Let's get jinx set up for now.
jinx wants user namespaces
ah interesting
That does sound like a feature I'd want to implement some form of myself at some point too
But not exactly soon :P
I cannot use Jinx; it is practically undocumented and I am not willing to read its code to find out how to use it.

xbstrap
Yeah but that's outdated by seemingly quite a bit
And also doesn't explain e.g. why this:
name=binutils
revision=1
imagedeps=build-essential
tarball_url=https://sourceware.org/pub/binutils/releases/binutils-2.45.tar.gz
tarball_sha256=5c71ec80884c0b4c0f7a4f8600946f1f6feebe584261b63185b0942cba4062d5b6cf2337539132d0ca03505c0a74f3c2760bc62ed08843c3b7db03df7f5798ad
configure() {
${source_dir}/configure \
--prefix=${prefix} \
--target=${TARGET_TRIPLET} \
--with-pic
}
build() {
make -j${parallelism}
}
package() {
DESTDIR=${dest_dir} make install
}
Gets me the error "could not find source recipe 'binutils'"
I am clearly not using a source recipe here
host recipes can't have inline source recipes like that
you either need a recipe/binutils or source-recipe/binutils
it is kinda roughly mentioned in the .md file if you read hard enough lol
to be fair xbstrap is also not documented :^)
me when schema.json is the documentation
Actually tempted to go get some bullshit like how arch's packaging system works
Because it'll be documented
they are not very user friendly
they are made specifically for their distros
jinx and xbstrap are probably the most "portable" solutions out there
oh great
I'll take SOME over NONE then
Let's get binutils going
Ok... I need to patch binutils configure it appears because it ofc hasn't seen the combination of BadgerOS and mlibc before
Fingers crossed I hit configure over the head with my baseball bat correctly
I'm actually at a point now where I can be making my mlibc sysdeps 
ooo sounds good, you got both gcc and binutils done?
Well GCC only to the point it complains about no headers since I hadn't ported mlibc yet
Nice!
Currently writing and/or defining the mlibc ABI headers
I copied astral lol
and he copied linux
basically everyone copies linux's-
I don't
Making my own types for this
Once I have them all defined I'm copying them into the kernel repo
Making your own ABI is quite involved and also error prone
For example, all of my ID types (PID, UID, GID, inode, etc.) are 64-bit. This is not true of Linux.
and provides almost no benefit over an existing one
So then what? Use Linux' but replace just stuff like the ID types myself?
yeah you could do that
Ok so I mostly took Linux but have my own fdset (because my limit is only 256) and integer types.
I also included my syscall headers in it, which are of course different than those of Linux.
I've successfully build binutils, gcc, mlibc and libgcc. Initial bindings for BadgerOS are in mlibc. Now it's time for me to collect the ABI bits from mlibc into a neat little module for BadgerOS.
Wow Congrats!
Do you have a gh or codeberg repo?
lmk when you pushed it, I would love to see you're patches cuz mine are broken asf.
lmao you don't want to copy those from me
no but i want to check ur logic to build it cause mine is hella scuffed
Yo WTF is wrong with the Linux ABI definitions
hm?
struct stat has different definitions per CPU architecture!
How is that better than making one struct stat definition for myself instead?
well, that's indeed braindead
Adding stat.h to my overrides
yeah it's a bit of a nightmare
linux initially was forked for every new architecture added
@fading elm why don't you join the managarm server?
makes it easier to communicate
My WIP mlibc port and distro are up now:
https://github.com/badgeteam/BadgerOS
https://github.com/badgeteam/badgeros-mlibc
Kernel repo was renamed:
https://github.com/badgeteam/BadgerOS-Kernel
managram 
no bsd/glibc extensions?
i just defined the kernel ABI, and delegated everything to the kernel
that way i don't need to make adjustments in mlibc every time i implement something
Well the kernel ABI isn't done yet
LARG?
very big
TL;DR: The scheduler is so bad it's better for me to rewrite it than fix the current one.
Has piss poor separations of concerns too.
But this is a pretty damn big re-write because it needs to interact with so many other systems.
you think thats bad. they had to rewrite the windows scheduler recently
oh no
What’s that down to, then?
Funny thing is, Microsoft were saying only recently that they'd be sprucing up the scheduler's performance for gaming - https://www.techpowerup.com/343895/microsoft-promises-more-performant-windows-11-optimized-for-gaming - most likely off the back of a fair few grumbles cropping up on Windows 11.
i was pretty sure it was because of big.LITTLE
If this has truly forced them to rewrite the scheduler (dispatcher?), then it's rather exciting, in a way.
Damnit I just realized this scheduler rewrite is likely to also be an SMP rewrite
Yeah this one is gonna be a PITA
how
is your stuff THAT tangled?
By being the stubborn kinda idiot that wants to do everything herself
I didn't read any literature about any of this, just some occasional wikis
That's the easy part finished.
Also the SMP thing isn't because it meddles with the scheduler but more like it's not feasible to make proper interop between a C SMP layer and a Rust scheduler because most of the Rust types there can't be represented in C.
Just wrote the new assembly file for the exception and system call trampoline for RISC-V 64-bits.
I will make some sort of more formal CPU spinup thing tomorrow and install it from there.
This 200-line clean assembly file (prolly woulda been shorter without the extensive comments) replaces about 500 lines of spaghetti asm
lol, that was me a few years ago with this stuff. You live and learn.
I think it's also just that, with the scheduler becoming Rust, I've reached the threshold where I need to rewrite C into Rust for the codebase to remain clean. I estimate that by the end of this rewrite, Rust will probably make up over 70% of the codebase.
what's wrong with dropping C altogether
Too much to rewrite at once
I don't feel like having a kernel that doesn't even compile for multiple months because I decided to try rewriting everything at once.
no like, gradually
That's basically what I've been doing the past few months
By the end of this scheduler 3.0 rewrite, some of the SMP stuff might be left as well as the device subsystem and misc utilities and libraries that C depends on (e.g. collections).
you really should take a look at the code bro
?
If you look at my code, the last couple months have set a trend of replacing C code with improved versions written in Rust
i got that
i don't think anyone said dropping C is bad
in fact, the entire point of rewriting small part of the kernel in rust IS to drop C eventually
(i think, i'm not robot lol)
it just seems that writing a new kernel in rust is counterproductive
when you have all of that C code that works but can be improved upon
Well atp most of the code is Rust
i wonder if it might make sense to write a microkernel in C and the drivers in Rust
(not that I'm planning to do that)
just hypothetically
sure
i was gonna do rust drivers for managarm lol
though the kernel is C++, but it's close enough
make a proper tty stack in rust for Managarm :^)
RUSTREAMS
i don't even know how to make a proper one in C
Integrating Rust with C++ properly isn't exactly trivial
it's as trivial as integrating rust with C
Is it? I assumed it wouldn't since C++ has a bunch of shenanigans with classes.
(Which, of course, don't fit in Rust's type system)
"classes" are just data, but nobody forces you to use them
just like you don't use rust data types in your C code
i'm assuming you have some structs which are repr(C) that you share with the C code
or some which are bindgen'd
you can do the same with rust and C++, you can have POD structs that you use at language boundaries
that's inefficient i guess, there's also the cxx crate which is like bindgen for C++
I pushed the scheduler 3.0 stuff to a branch just now since its proof of concept works.
Also fun fact: If you were to remove the sched3_test from main it'd function as normal with the old sched because the new one doesn't collide with it, what being written in Rust and all :P
This actually happened back when I did the VFS rewrite too
well
there's an entire abstraction layer between the kernel
bragi (similar to protobuf) can output code for all languages in theory
I only need to implement RCU again and then I should be ready to switch over to the new scheduler.
I'm going to do a plain translation of the C RCU into Rust and then it's time for the part that's a lot of work (switching everything outside to use new sched)
Back at this again, currently doing actually switching over to sched 3.0. There's a bunch of stuff that couldn't really be prepared but just have to be done all at once.
Right now that means exception handling and the process of bringing up APs.
Time for fallible instructions 2: electric boogaloo.
(My usercopy works by detecting page faults)
yeah.. this is the sane way.
but what does usercopy have to do with the scheduler?
Old usercopy depended on old interrupts and old interrupt depended on old scheduler for ugly reasons you can see above.
BTW @solid bronze I've reached a "milestone": my main.c needs to be nearly completely rewritten and most of the things it calls into are Rust anyway, so I'm going to be making that a main.rs 
(main.c was some misc init order stuff, not very long)
Also why the mallard duck lol
lol

Yep yep I'm actually at the point where it's no longer possible to have C interop in certain places. After this re-write, there'll be a new "kernel" subfolder that contains the scheduler, SMP, synchronization primitives and CPU-local data (all written in Rust ofc).
Re-writing my SMP almost verbatim into Rust because it needs to read and write the CPU-local data structs but those can't be represented in C.
For some reason this isn't working...
I checked, and it does appear between the request delimiters
So I'm not sure why exactly Limine doesn't see these.
which limine are you using
or rather, which base revision
3
Setting it to revision 3 in there fixes it.
Which makes me wonder why my old C code for this had rev 2 but didn't explode
LLVM opts?
It was revision number
why even use the request delimiters?
You either have to use those, or put all requests in a specifically named section.
I chose the latter.
you have to do neither
Pretty sure you do in the latest revision
nope
limine scans the entire binary for requests, the delimiters are used as an optimization
Well then that's just nice, isn't it?
it will only consider requests between the delimiters, and stop scanning upon reaching the end delimiter
i mean, maybe for some people but i simply find them unnecessary :p
it still has to go through the entire binary to find the start delimiter, so the performance benefit isn't that big to me
and actually, the requests section support was deprecated and removed in base revision 1
but i was just curious why people do it, i guess because it's how limine templates are written
if you put the section at the start of the binary then it doesn't have to scan the entire binary tho
and the deprecated part is the usage of a specifically named section, the request delimiters feature isn't deprecated
yeah i said the requests section support was deprecated
and that's a good point about putting the requests section at the start of the binary, but personally i still don't care about the little bit of performance you can gain from doing that
DPC time
So turns out it was not DPC time because those aren't actually required to do preemption.
I will support DPCs in the future probably but I just don't need them immediately.
I've re-written the RISC-V timekeeping code into Rust and soon I'll have preemption up again.
At that point all that's left is load balancing, which I will probably do with work stealing once again.
(I.e. if a core goes idle then it'll try to steal a runnable thread from whichever CPU is under the heaviest load)
Preemption works sometimes and other times it'll just blow whatever thread was running to smithereens.
It looks like the register file is corrupting.
The last thing that happens is the user process calling syscall_proc_fork, then a preemption happens somewhere within the fork implementation, even though it switches right back to the same thread, and the result is corrupt register.
The stack is intact (ish?) because I can still backtrace and the backtrace makes sense.
That said the backtrace does break at some point so maybe some interrupt accidentally reuses the top of the stack there instead of continuing at the bottom?
Yeah that was it, forgot to add this to the exception handler because of course preemption happens from interrupts so we should probably tell it to use the correct stack for interrupts.
That leaves only AP bringup before I can finally merge scheduler 3.0 and get back to building userspace!
Shit
Bringing up APs has brought to my attention that my current RCU is broken
As in the CPU that's starting up won't properly sync with the already running ones, locking it up, and thereby the boot process as the BSP waits for it to come online.
Current activity: BadgerOS with this playlist in the background: https://www.youtube.com/playlist?list=PLrjpwekK1wIAhdAOTmRyAryvpm45MutRu
RCU is fixed but now syscall_proc_exec sometimes hangs
Due to some kind of problem in copy_to_user in create_entry_stack
Yeah I wasn't updating the PC properly so it would just retry the fallible_store_u8 over and over
That was another small bug: Was decrementing the SP after writing to it instead of before in create_entry_stack
So it would just fail depending on stack alignment
Now it's time for 1. reaping dead threads and 2. work stealing as a simple form of load balancing.
BTW: Thanks to @flint shell for helping me fix RCU, they made the suggestion that ended up fixing my problem
Almost have work stealing finished but now it randomly jumps to PC=0 
I'm guessing because some of the thread state didn't migrate CPUs properly? idk
I currently have statically compiled modules but I will do dynamic ones in the future.
nice
eh what gives I'll stick to rust
i do have a C project tho
libudev-portable
Not 100% sure on how I'll do dynamic modules but right now I'm thinking of making them depend on my kernel, both as dynamic objects, and the kernel will dynamically link them in at runtime.
that's what i'm doing
This would allow at least dynamic modules to be built. As to changing the crate type to static and linking it into the kernel statically, I would need an external build system.
Around cargo
comes with the neat benefit of getting free dep tracking
I don't think there's a way I can with the exact same sources make it a compile time option to choose between module (dynamic) and module (static)
Can a Rust staticlib depend on a dylib?
If it can then I think I know how I'll link them to the kernel?
The kernel needs to be a dylib for the purpose of dynamic modules, period.
with cargo there is no way to do that, yes
But if a staticlib can depend on a dylib, then if you choose static for some module, it will build as a staticlib and be added to the linked command line.
You would, of course, need more than just cargo to achieve this.
i've been exactly where you are rn
because you only need to link it, the kernel doesn't actually depend on anything besides the "module" info
But I was going to do that because I'm not satisfied with Rust's #[cfg(feature = "...")] for configuring the kernel anyway.
At which point the only remaining question is which wrapper would give me the nicest config system
CMake has one in theory but it's very bad
if only cargo supported -rdynamic
Linux apparently passes std'd lib.rs directly to rustc
No, official rust std
no because I heard this from someone else who didn't send a link
mfw
Excuse me Rust, I need this please
This is the annoyance
/// Wrapper for the `cpuid` instruction.
#[inline]
pub fn cpuid(leaf: u32, sub_leaf: u32) -> CpuIdResult {
let eax;
let ebx;
let ecx;
let edx;
// LLVM sometimes reserves `ebx` for its internal use, we so we need to use
// a scratch register for it instead.
unsafe {
asm!(
"mov {0:r}, rbx",
"cpuid",
"xchg {0:r}, rbx",
out(reg) ebx,
inout("eax") leaf => eax,
inout("ecx") sub_leaf => ecx,
out("edx") edx,
options(nostack, preserves_flags),
);
}
CpuIdResult { eax, ebx, ecx, edx }
}
This is a burh moment
huh, why does it reserve rbx?
Ask LLVM
i think the diagnostic tells you
Ah shit
My PIT-based TSC calibration code is fucked
I set QEMU to make it 1.0GHz but it thinks it's about 789'946'458'571'974 Hz (790THz)
unsafe {
ioport::outb(0x43, 0x32);
ioport::outb(0x40, 0xff);
ioport::outb(0x40, 0xff);
}
let before_tick = time_ticks();
let mut after_tick = before_tick;
let mut pit_tick = 0u64;
while pit_tick < 100000 {
after_tick = time_ticks();
let mut tmp: u16;
unsafe {
ioport::outb(0x43, 0);
tmp = ioport::inb(0x40) as u16;
tmp |= (ioport::inb(0x40) as u16) << 8;
tmp = !tmp;
}
if tmp < pit_tick as u16 {
pit_tick += 0x10000;
}
pit_tick = (pit_tick & !0xffff) | tmp as u64;
}
let elapsed_nanos = pit_tick * 838095344 / 2000000000;
let elapsed_ticks = after_tick - before_tick;
logkf!(LogLevel::Debug, "PIT ticks elapsed: {}", pit_tick);
logkf!(LogLevel::Debug, "Nanoseconds elapsed: {}", elapsed_nanos);
logkf!(LogLevel::Debug, "TSC ticks elapsed: {}", elapsed_ticks);
let ticks_per_sec = (elapsed_ticks as u128 * 1_0000_0000_000 / elapsed_nanos as u128) as u64;
logkf!(LogLevel::Debug, "TSC frequency: {} Hz", ticks_per_sec)
Here's what I do right now ^
[00000.000] DEBUG PIT ticks elapsed: 100000
[00000.000] DEBUG Nanoseconds elapsed: 41904
[00000.000] DEBUG TSC ticks elapsed: 331019164
[00000.000] DEBUG TSC frequency: 789946458571974 Hz
And the result ^
@solid bronze How do you establish the frequency of the TSC?
Because that's all I'm really interested in
depends on the cpu
I should add that 32-bit CPUs and CPUs old enough not to have invariant TSC are unsupported on BadgerOS
Yeah and it turns out the CPUID info depends on the core clock frequency, which I have yet to successfully find the source of
Kinda wondering why my previous impl of this "worked" as well as it did...
hmm
I will look into the rest of this tomorrow
Because having a timer is kinda useful 
<- Getting x86_64 to run after sched3 re-write
I also tested on HW recently and neither RISC-V nor x86_64 booted so... Gotta fix that too.
It still thinks my 1GHz TSC is 400GHz...
Calibration is certainly closer but not as it should be yet.
I timed an exaggerated calibration of 10 million PIT ticks using a stopwatch and the difference between the elapsed nanos according to PIT and my stopwatch was well within the margin of error.
In other words: the PIT isn't the problem.
My host CPU is 3.949GHz and the result from calibration appears to be 100 times that for some reason
So apparently QEMU did not get the TSC down to 1.0GHz for one
let ticks_per_sec = (elapsed_ticks as u128 * 1_0000_0000_000 / elapsed_nanos as u128) as u64;
What's wrong with this line, I wonder 
@solid bronze look at what I was debugging all day ^
some extra zeroes by accident 
shitshitshitshitshit
I don't think the random page fault was because of work stealing because now it's happening even without work stealing
Aaaaand threads are going missing again
FFS
I'm not good at schedulers holy shit man
Meanwhile, test on real HW:
LinuxBot badgeros-kernel$ picocom -b 115200 /dev/ttyUSB1
picocom v3.1
port is : /dev/ttyUSB1
flowcontrol : none
baudrate is : 115200
parity is : none
databits are : 8
stopbits are : 1
escape is : C-a
local echo is : no
WARNING: Could not meaningfully match the boot device handle with a volume.
Using the first volume containing a Limine configuration!
PANIC: No volume contained a Limine configuration file
Stacktrace:
[0xfe693daa] <panic+0x98>
[0xfe68cf98] <uefi_entry+0x298>
End of trace. System halted.
Terminating...
Thanks for using picocom
LinuxBot badgeros-kernel$

Something's broken with the backtracing still but at least updating Limine has resolved this particular issue.
Let's fix the backtrace so debugging on HW is slightly easier
Ok cool, so it "boots" on hardware again (but I don't have drivers for SD/MMC so it can't mount the filesystem)
I think I'm gonna merge the uapi branch. Or try to, at least.
Ah shit I have an old branch where I was working on the ACPI stuff to get x86 to actually boot
It's probably gonna end up easier to cherry-pick diffs to apply from scratch than try to merge it.
Merging uapi was surprisingly simple. Now, let's make an image that contains the bare mimumum to boot.
Not me needing to recompile GCC again 
it takes 5 minutes it's not that bad
It gets boring pretty quickly if you have to recompile often
Thanks to some help from @echo valley, I've got a little distro built. However, the kernel still crashes, so there is some work to do.
This is almost certainly not the distro's fault, though.
It was a stack overflow. I'm going to need to check the recursion up there.
bruh
I'm reading an invalid PTE to copy into the user process
cringe
How fucked up this page table is, is quite surprizing
This must be some kind free happening too early and something else allocating the same block as the page table here for something else?
I can check, actually, I'll look in the PFBDN using the debugger what the PageUsage is
The memory is marked as an in-use page-table.
So something else is corrupting it.
since the entire thing is affected, I'll try setting a hardware watchpoint on the first address within
LMAO my elf loader breaks after mapping a grand total of two pages
After that every page repeats the second ever mapped
This is Ext2's fault
Or rather my driver for it
Looks like the indirect blocks are not working as they should
well
This is why you don't run your OS on something with a drive that has important data
Imagine having an FS bug like this one but you're writing instead of just reading
Instant data loss
I'm just reimplementing my E2VNode::get_block_unlocked (the function which finds and/or allocates an inode's data block on disk)
Much closer but init still page faults
I think I have fixed program loading but on the other hand it page faults still
Looks like an mlibc variable that says whether TLS is available early is set to true while it should have been statically initialized to false
Time for another try at the program loading
This time I have completely rewritten the function that loads a specific program header
If that works, I'll have mlibc running on BadgerOS.
Ok cool, let me quickly create device nodes (e.g. /dev/null, /dev/tty0, /dev/sata0)
That's done! Very cool. Now, let me make TTY devices a real thing and make the kernel switch over to those for logging after early init. The init process will be started with a handle to the first found TTY device for FDs 0, 1 and 2.
For some reason when the kernel tries to open /dev/tty0 to give it to init, the file I get is actually /dev/null?
Damnit, I didn't want to be dealing with complicated FS bugs right now...
I am going to have an anneurism
Ok so
turns out that
I did not recompile init
And the old version explicitly opened /dev/null
Yeah so turns out I forgot to actually put argv and envp on the stack but reported an argc of 1 
I'm gonna need dynamic linking ASAP to get the image size under control
Shouldn't be too bad for the kernel
all you have to do is load an interpreter if one is requested
and load its base onto the stack
i know
Loading /usr/bin/echo fails on reading in the first program header
:(
It had to do with my pages tabling again
I changed how I override the page table temporarily during ELF loading, lettuce try anew.
Let's go
This is /usr/bin/echo
I should also test running for an extended period of time after a fork happened.
Yeah okay. I am going to be porting the PMM, VMM and heap I'm making in Positron to BadgerOS eventually. I really like them.
They share a lot of primitives, most of the things that need adjusting are just where stuff is imported from.
Do I write a new scheduler again in Positron and port it to BadgerOS later, or do I fix BadgerOS' scheduler when multiple cores are up and port it to Positron... 🤔
I think I'll try fixing BadgerOS sched first. Maybe I can get work stealing in there too.
??? BadgerOS' scheduler doesn't crash with SMP now?
I'll try adding back work stealing too then.
Seems like just the work stealing is borked.
Ah shit
It seems as though the stack is being written to whilst the context switch is in process.
Which is a catastrophic bug either in the spinlock impl or in the thread queue impl.
Namely, it is overwritten from a perfectly logical return address to 0.
In the middle of the context switching into it.
Perhaps an interrupt is using that stack when it should really have been using a different one?
I guess I'll just see what happens with preemption disabled then.
It happens with preemption disabled too...
I need to rule out a thread being in the queue for multiple CPUs at once.
OH FUCK
If a thread yields or gets preempted, there is a window where it is in the queue but the scheduler is still using its stack!
So it's not actually the sched it came from corrupting the one it went to, but the one it went to corrupting the one it came from!
My queue is currently a linked list and "choosing" a thread just pops the first one off the list, I'm not sure how to fix this...
I guess I'll just keep the lock for the queue during the entire Scheduler::sched_yield function
That answers the Positron sched question too: I will polish up that of BadgerOS and port it to Positron.
Actually no, I can't do that either
Because now the owner of the queue spinlock can spontaneously migrate to a CPU it's not for
OK that's fixed. Now I'm gonna have to fix whatever it is that causes dynamically linked stuff to just segfault
UGH
Program loader must be borked for interpreted ELFs
Not a single syscall before segfault
Userspace wants to access address -2 for some reason
ffs
Looks like I lot a lot of changes I made in the kernel before I was working on fixing the scheduler
This will almost certainly affect the program loading
And I do not want to deal with this BS right now so I'm just gonna not
Yeah so since Zinnia as a microkernel is dead I'm just going to continue fixing BadgerOS now.
Just fixed a UaF (using a Limine response data after having reclaimed bootloader memory)
Process 1 is segfaulting, which is almost certainly the kernel's fault.
I'm going to see if I can band-aid fix it but either way I'm going to be converting into UVM-style virtual memory very soon.
👍
Bug seems NOT to be my VMM but the entry stack layout instead... argc == 1 but argv[0] == NULL
argv[0] should be pointing to a string /sbin/init
I just nuked create_entry_stack and made a new one. That part works now.
/usr/bin/echo appears to start, but it doesn't make any FS syscalls, so something is going wrong there.
LMAO didn't install coreutils so this is the fork succeeding but the exec failing
Anyway let's fix up dynamic ELFs
oof yeah the code for dynamic ELF interpreters got lost too
Lets find out what the auxvs I need are again...
Ok now the dependency is on sys_vm_protect
I will do a dirty and map everything RWX and stub protect, because I will be rewriting VMM later
Now I'm working on integrating the kernel into the build system of the distro
I've already removed all the old garbage from when I thought it was a good idea to have the kernel and distro be in the same repo
Time to first rebuild coreutils and then impl more random syscalls
which coreutils are you using
GNU
fair
shit
I built userland as RV64GC
And now I'm being held to that promise (float op was executed by printf)
So uh time to do that I guess
Bit of a tangent
problem?
I'm just on a tangent implementing float support in the kernel
In fact I should now be able to test said float support.
FUCK my pid_t has to be int. Now I can't just rely on it never overflowing.
why cant you do long??
Because of pgkill
The definition of it uses int, forcing pid_t = int
That's a POSIX function
oh
Yeah
Shit's fucked yo
this is outright evil
what is that
i don't see the function
do you mean killpg?
because then this is false
Oh yeah it's killpg
where does it say int?
int killpg(int pgrp, int sig); <- definition in both mlibc and glibc
yeah
I'm going to impl more shit bash wants from me now
musl declares killpg like it's supposed to be
lol mlibc uses it correctly
but has incorrect arguments in the header
lololol
mlibc#1707
@fading elm merged
that was fast
I'm debating whether I should use the Linux ucontext_t structs or make up my own for signals
I'll make my own because I don't feel like bothering to learn the Linux one's semantics
NVM I'm using the Linux structs
Lol
No need to reinvent this wheel
Yeah fair
I'm going to make some excuse for a "shell" that needs minimal features and go around checking coreutils using it
Then try bash again
Its just funny to me the pattern on this server of saying "I won't do x, I'll do my own thing" and then we get "oh no, I'll just do x".
What trouble did you have with bash?
Jumps to a nullptr and generally needs a lot more sysdeps working than I have RN
e.g. concept of working dir is missing
signals are my current WIP
All IDs are a stub
Oh ok, yeah those sound useful for bash
Bash 100% working is the next big milestone I'm slowly working towards
I also need to implement signals (really the whole process management subsystem 😬)
I'm also going to rip out my VMM entirely for a third time and replace it with UVM-style
Only the third time? 😅
lolololololol
That sounds good though, uvm is really nice.
I never bothered researching VMM for some reason
Then I look at the UVM paper once and I'm like "this"
Its nice, I got interested in it after seeing fadanoid monologuing about it a while ago. There's some patterns that I think I'll find other uses for.
Also
I'm going to have to port to x86_64 because I can no longer put up with how slow TCG makes shit
Currently implementing waitpid
Also doing SIGSTOP and SIGCONT while I'm at it
Async signals and waitpid work now
Oh BTW bash no longer segfaults
five hundred segfaults
what was the cause for it
best i can think of is you not mapping enough ram
Probably bash assuming some random lib call would never fail except secretly it actually did fail
howd you fix it
Implementing random sysdeps
[00000.000] INFO ==============================
[00000.000] INFO BadgerOS 0.1.0
[00000.000] INFO bad3f7c-dirty 2026-03-18 04:00:52 CET
[00000.000] INFO ==============================
New feature
Also added uname
Bash prompt achieved!
Really wishing my mutexes were recursive right now...
I've added some more sysdeps that fcntl will use behind the scenes but it looks like I have more syscall debugging to do
Because bash will not show the prompt for some reason if F_GETFD is implemetned
It still runs just no prompt showing
I'm looking for some input on how to do my device abstractions because I'm not satisfied with the current situation
Basically in the current situation my kernel represents devices in a tree analogous to the DTB (and indeed DTB information is copied into it on RISC-V). The problem comes when I try adding drivers to said devices. The devices are a union of all possible "device classes" recognised by the kernel. When a driver is added to a device, its class gets set accordingly, and the device is then usable through an interface specific to that class.
- If a device has its driver removed, then another one added, users may not notice if they weren't using it in the mean time (it will retain its current class and handle)
- I can't fit the current structure into Rust's memory model nor type system cleanly
I would like to see if it's possible to do use dynamic dispatch, e.g. Arc<dyn BlockDevice>. (A C++ analogy would be std::shared_ptr<block_device> where block_device is an interface)
I thought what I'd do is have the device tree contain only the abstract information about devices, and which drivers they currently have, then when you go to search for devices you get given a handle to its driver (which in turn refers to the info in the tree internally)
However, in this proposed model I would be unable to put a higher level interface above raw access to the driver (e.g. line discipline around a TTY, page caches above a block device)
I've decided to work on UVM before the new device rewrite so that it doesn't need to be updated after the fact and can immediately use the new features I want to give it.
First off, I have implemented a radix tree that will be used to build the page caches on memory objects.
I can now continue writing out the UVM structs.
Looks like I unfortunately can't use the class-like structure from UVM's memory objects directly in Rust
I think I will instead make the interface of one a trait, and create one or more helper structs for implementers to use.
After some compromises on not being able to do inheritance with pure virtual function in Rust properly, the page cache is coming along nicely.
how did you end up designing it
Passing the pager ops as a parameter to every PageCache function
@fickle sail I post about my own OS here from time to time
I've come to the conclusion that to check whether we had already mapped a certain page, we just ask the pmap.
From that information the page fault handler determines whether to get pages from the memory object, CoW them into an anon, or simply flush the local TLB.
Having a "is page present" function sounds like a bad idea
That easily runs into TOCTOU
It should be safe because the page tables are managed exclusively through the UVM abstractions, and only pages that virtually lie within an entry (of uvm_map, which is guarded by spinlock) may be modified by that entry.
Basically I don't want to run into having to keep an entire second radix tree just to keep a copy of what is already in the page cache for most memory objects anyway.
Unless you can propose some other solution that can avoid unnecessary overhead like this while still allowing the memory object to manage its own page cache.
You could theoretically run into my method being a problem if you had e.g. PPC paging where you'd need to evict some pmap entries to map others, but RISC-V, x86 and ARM don't do this so I don't really care.
I'm almost done with the PoC version, all I need to do now is make the actual implementations of map, unmap, protect and fault in VmSpace
you can design it in such a way that the PTE is transient
and can be thrown away / regenerated from the memory object
And then what? Just add a another datastructure to the whole ordeal solely to keep track of the physical addresses of pages borrowed from the memory objects?
It's a bit hard to tell w/o knowing how your VMM works but most of the time you need a separate data structure anyway
to support shared memory and page caches for files
Well I have structs Anon, AnonMap and MapEntry for a reason
They correspond to the UVM structs anon, amap and whatever the entry for map was named
The only thing "missing" here is the information of whether a certain page at an offset in some memory object is or is not mapped at the moment
It's also on GH here so you can see what I mean: https://github.com/badgeteam/BadgerOS-Kernel/blob/vmm/src/mem/vmm
A MIT-licensed hobby operating system. Contribute to badgeteam/BadgerOS-Kernel development by creating an account on GitHub.
Particularly map.rs there is relevant to this discussion, it is meant to be the abstract representation of mappings that are then translated into actual page tables (which are stored in PhysMap from pmap.rs)
Okay but then you have all the data structures already, right? On read-only PF you get the anonymous page if it exists. On write PF you get or allocate (and copy) the anonymous page.
yeah but
the MemObject will still need to have its own internal refcounts
And the map should correctly tell it when it uses and frees the pages from it
There isn't something right now that stores this information, and I was planning to imply it from the page tables
Obviously you need to increment refcount numbers on map and decrement on unmap, yeah
but this should probably be handled by your pmap code
For all the pages in the mapping?
No, for each individual page
Not th physical pages I mean, but the pages within the MemObject
Basically a page dealt out by a MemObject needs to remain in its page cache at least for as long as it is physically mapped
Ideally the pmap doesn't deal with ownership semantics at all and could be treated as a cache written by software and read from by hardware
Well, that's not possible, at least not combined with "I want no auxiliary data structure to remember the pages that are currently present"
since as you said, you somehow need to remember which pages have their refcounts bumped and which haven't
Yeah
So the compromise was going to be checking the pmap; if it's in the pmap but not in a matching amap then it belongs to the memory object
But if I understand you, this is a bad idea?
Why isn't the refcount for page cache pages attached to the physical page?
If it was, the pmap could just decrement it when unmapping
I thought those were meant to track when a page was to be freed in terms of PMM
I could change these semantics to make the refcount used the way you suggest
I suppose this way of using that refcount makes more sense
I think of it like this: while the page is owned by the memory object, the memory object decides for what purpose the refcount in the page struct is used
And then the memory object checks that refcount to determine whether it must stay cached
okay
yeah
the refcount is for arbitrary use by the owner, and for memory objects that refcount means how many times it is mapped
This does mean some refactoring in my PMM now. I must change how it sees a page as being free or not.
Accidentally left my VMM tree at home so I'm currently working on my new syscall marshalling system. I'll have a little python script generate me everything needed, from C wrappers and header files to Rust marshalling code that calls a safe implementation.
There's only two syscalls that'll need exceptions on the Rust side: syscall::proc::fork and syscall::proc::sigret
Ok back to my PC, let's do the actually productive VMM work now
First, I'll try to adapt the entire kernel to the new (slightly different but not much) VMM API, then I'll implement the last couple functions needed for an initial test.
Nice, gl with the initial test.
Discovered a couple things for which I still need to think up the new API

monkuous to the rescue again
