#OBOS (not vibecoded)
1 messages ยท Page 11 of 1
But it's fine if something that owns a spin lock is interrupted by something at higher IRQL
Well yeah but you should only lock the same lock from within the same irql right
Correct
Although there's an exception which is that it's acceptable to acquire spinlocks from <DISPATCH_LEVEL because the spinlock will raise/lower the IRQL to DISPATCH_LEVEL if needed
send code ill figure it out
it's all over the place
one boot it faults there because something returned to some random kernel address
another, it's at some random address in the kernel heap
I currently suspect that the problem is somehow in the thread context switch code
maybe the rsp was set incorrectly somehow
lea rax, [rsp+0x10] ; skip the return address
mov [rdi+thread_ctx.frame+0xC8], rax ; rsp```
The ret address + some value were pushed at the beginning of the function
which means rsp before the call would be the current rsp+0x10
right
for a second I thought I'd found the bug
but I was wrong
time to look at the switch to thread context function
that looks clear
found a subtle bug in the timer dispatcher
two variables were named oldIrql
amazing
it still crashes
I think the bug has to be with my yielding function
the only difference between when these tests run and when they don't is that there are multiple threads yielding often
ok now wtf is going on
Kernel Panic on CPU 2 in thread 6, owned by process -1. Reason: OBOS_PANIC_EXCEPTION. Information on the crash is below:
Page fault at 0x0000000000000000 in user-mode while to read page at 0x0000000000000000, which is unpresent. Error code: 0
Register dump:
RDI: 0x0000000000000000, RSI: 0x0000000000000000, RBP: 0x0000000000000000
RSP: 0x0000000000000000, RBX: 0x0000000000000000, RDX: 0x0000000000000000
RCX: 0x0000000000000000, RAX: 0x0000000000000000, RIP: 0x0000000000000000
R8: 0x0000000000000000, R9: 0x0000000000000000, R10: 0x0000000000000000
R11: 0x0000000000000000, R12: 0x0000000000000000, R13: 0x0000000000000000
R14: 0x0000000000000000, R15: 0x0000000000000000, RFL: 0x0000000000000000
SS: 0x0000000000000000, DS: 0x0000000000000000, CS: 0x0000000000000000
CR0: 0x0000000080010011, CR2: 0x0000000000000000, CR3: 0xffffff000008ce88
CR4: 0xffffffff800b92ea, CR8: 0x0000000000000001, EFER: 0xffffffff8009fea7```
see anything wrong with this crash log
nice EFER
ikr
cr3 is beautiful as well
well that crash was a result of a thread's context not being initialized
was easy to fix
needed to change a jz to a jnz
I shall continue debugging tomorrow
studies have shown that I don't enjoy debugging this shit
What bug are you having now
@flint idol I notice something
what this for
oh nvm
how dis work
https://github.com/OBOS-dev/obos/blob/2bf2f7eb12a8ecaeb28fdafc22cc982484bd9423/src/oboskrnl/arch/x86_64/thread_ctx.asm#L217
~~here you can just do this cant you
xor rdi, rdi
call Core_Schedule
```~~ nvm
~~ that digs into the stack~~ nvm
kinda weird that youre hardcoding the stack size in asm
also @flint idol do you ever call this recursively/reentrantly
like does a function called by CoreS_CallFunctionOnStack call that function CoreS_CallFunctionOnStack
anyways the scheduler code is kind of a mess
feel free to take a look at Boron's scheduler for an idea on how to do it better https://github.com/iProgramMC/Boron/blob/master/boron/source/arch/amd64/misc.asm https://github.com/iProgramMC/Boron/blob/master/boron/source/ke/sched.c
and then look at mine if you want to know how to do it better than that
It's not much better given that it's awfully similar to yours
Especially now that I don't use self IPIs anymore
Ok maybe the next thread picker and stuff
But I was talking about the switching logic itself
fine grained locking!
oh yeah but again, I was talking about the switching logic
in that regard its not much better as it's basically the same
boron does lack fine grained locking but I'm totally fine with that
https://github.com/iProgramMC/NanoShellOS/blob/master/src/task.c#L1001 better than whatever the fuck this is
Probably shouldn't ever happen
It definitely shouldn't!
Now that I'm at my computer I'll search for it
Nope, it's impossible for it to get called recursively
As the only other reference of it is under the exit current thread function
But it masks all IRQs before calling that function
This seems to be linked to SMP
As without SMP nothing crashes
something very weird has happened
basically has something to do with 0x8000 bytes of stack being used in the idle thread
my brain's starting to hurt
what would happen if I theoretically rewrote ||the scheduler||
I was right
After commenting out the TLB shootdown code
nothing crashes
now time to figure out why
I'll first check if it's a bug with the isr return code somehow
If it is, then it should always crash on the first IPI shootdown
nope
that's not the case
well don't ask why, but the IRQ for TLB shootdowns is an NMI
idk if that'll be a problem
In case anyone was wondering how I found that, it was because I was just sitting thinking about the problem, then that thought came outta nowhere
I have found the problem
I think
The NMI handler lives off the cpu temp stack
therefore on return there's a chance that it goes in the scheduler
which uses the cpu temp stack
Solution: Throw the NMI handler on another stack
OH AND OF COURSE WHEN I TURN ON THE TIMER INTERFACE IT BREAKS AGAIN
AHHHHHHHHHFDVHFXDSVGHXDRFHVGXDRFSGHVS'
Kernel Panic on CPU 3 in thread 6, owned by process 1. Reason: OBOS_PANIC_FATAL_ERROR. Information on the crash is below:
Core_LowerIrqlNoThread: IRQL 2 is greater than the current IRQL.```
I think I found the bug
yup seems like it
I'd forgotten to lower the irql at a return statement
This also kinda just popped into my head
and was somehow right
ok so what was I doing
ah yes page fault handler + page replacement algo
Just pushed the fixes
I just pushed some PF handling code
what do you guys think of my explanation of the page replacement algorithm I'm going to implement
// Basically how this works is:
// The page replacement algorithm goes through each page in the working set and the referenced list.
// For each page in there, it runs the "aging," PR algorithm.
// This algorithm basically states that:
// 'u' is a string of bits, in our case a string of 8 bits.
// Each time a page reference occurs 1 -> u0.
// At the end of each sampling interval or, the bit pattern contained in u0,u1, ... ,
// uk is shifted one position, a 0 enters u0, and uk is discarded
// The sampling interval in our case, is the time between two calls of this function.
// Any page in the working-set with an "age" of 0 is removed, and is replaced with one in the referenced list.
// The working set shall never exceed it's set size in the context structure.```
should be pretty good
if anyone spots any mistakes in my understanding/wording, please let me know
anyway, I now go to sleep
some parts I added in myself
as in some implementation details that might not exist in other kernels
idk though
tbh I never looked at other kernels
I mean I think that algorithm is good
but I should go to sleep and not implement it rn
I am now awake
and shall implement this*
*I started half an hour ago in my sleep
Just finished the implementation
Time to test it
but it can't be the swap
why\
tf
is kernel code getting corrupted
because like it's the code in __asan_load4_noabort that's corrupted
but that's marked to not be swapped out
and if it's never swapped out it's never swapped in either

I'm going to go play mc
I'll be back in
time
Smh shouldve carried a shield
I had a nasty bug today
basically the wait function in my kernel went (no longer goes) like this:
- setup the wait
- unlock dispatcher and lower IPL
- yield
during the lowered IPL phase DPCs might come in which might actually wake up the current thread
since its "waiting" its placed "back" on the run queue, and since it just got a priority boost the current thread will yield, leading to a curious situation where the current thread will switch to itself
due to the nature of how I wrote the thread switch code, this causes a crash
the solution was to unlock the dispatcher lock but not lower IPL, and in fact never lower IPL until after the thread was completely switched from
So I was dealing with kernel code corruption
The only thing I can think is happening rn is a bug in the pmm
where it returns two identical physical addresses
doesn't look like it
modules/something else you are loading into memory or your actual kernel binary loaded by hyper
Hmm good luck figuring that out
actually, it seems to only be some functions
this reminds me of the reason I decided to play mc instead
the page looks like it was used as a stack
I see addresses from the kernel
the page is mapped as Read, Execute
I think I'll check if that phys address is used anywhere else
weirdly there are pages that are mapped to phys address 0 while being RO
(possible TLB shootdown bug?)
it's used nowhere else
wth
the stack pointer is set to the page of the function
that's corrupted
this happens in the kernel main thread
It's a stack overflow
which happens in the pf handler
because one specific function wasn't marked as non-pageable
(starts to regret making functions opt-out of being able to get paged out instead of opt-in to getting paged out)
idk why the processor was allowed to write the page that was marked as RO
I'll check cr0
the supervisor protection bit thing
well would ya look at that
it's off
chances are it's off on each cpu
I just pushed the page replacement algorithm and this fix
All that should be left are allocation functions for the vmm
It should not have taken two months for the VMM
considering how simple it is
but you know the bugs and stuff take a lot of time
and there was a rewrite
of the vmm
so whatever
this math isn't mathing
0x1000-0xffffff0000083000 is apparently 0xfffff7e000
and also apparently 0x1000-0xffffff00001000 is zero
I don't think my RB Tree is RB treeing
If I call RB_MIN on an rb tree (freebsd's implementation), it should return the node with the smallest address
well it doesn't
inline static int pg_cmp_pages(const page* left, const page* right)
{
if (left->addr == right->addr)
return 0;
return (left->addr < right->addr) ? -1 : 1;
// return (intptr_t)left->addr - (intptr_t)right->addr;
}```
nvm a rebuild fixed it
probably some weird dependency thingy
All that's left for me rn is to add the vma's free function
then I can merge my two month old branch for the vmm
after this I'm making the driver interface (for some reason I like doing this)
I find it fun making driver interfaces, idk why
I get to port uACPI for the driver interface
and for the vmm tests
I get to practically rewrite relocation code
for loadable kernel modules
my previous kernels that had it were all in C++ and used c++ things
and I don't wanna port that to C
for Boron the entire kernel is available and can be linked against (except for static functions)
I don't know if I can, should, and want to change that
technically that's what obos rewrite 3 did
except if you wanted most symbols you needed to do some linker tricks*
*it was literally just marking the symbols as weak so that the dynamic linker had to resolve that symbol
You could, however, probably restrict what modules can use
If you have aslr
Else you could do some paging memes probs
by default a bunch of symbols were marked as weak (only for drivers though) for this reason
the downside is that you can call internal functions such as KiSetEvent etc which aren't defined as static
in this version of the kernel, it's going to use the visibility attributes
instead of marking the symbol as weak for drivers
bruh today I told myself I'd sleep early
*earilier than the last week or two
that went wrong
osdev > sleep /j
maybe this mental health break from osdev made me better
note: my mental health wasn't really affected by osdev
but the break was nice
and ig without it I would still have lost my hard drive
I basically had a two week mc grind in the time I was gone
like I've pretty much maxed out my stuff
my world was on a server on which I played on with my friends
we had duos
and I ensued chaos after deciding I wanted to piss off a certain duo
I blew them up
didn't even deny it
then continued to plan with one of them to trap and kill the other
we ended up making the other guy rage quit
now it's just us two on the server
server had a max of 5 people on it
we had beaten the ender dragon by the 2nd (irl) day
I got an elytra before doing that
we had some mod that let you teleport randomly
and to places
I got fireworks and used my elytra to destroy the end crystals
but anyway that's enough off-topic stuff
when I wake up I'll implement the vma's free function
and tests
make the allocator free properly once again
then finally merge the branch
anyway gn all
Imo it's a bad way, what about protecting functions you don't want drivers to call?
I like the model of "nothing is unavailable outside of the kernel unless it's explicitly marked as such"
It's easy to do too
With elf you can use the symbol visibility, and you have control over the linker so you can error out if a module tries to link against something it's not supposed to.
It's bad if you don't have kaslr
Or during
ok I am now back after a couple detours
The free function needs to:
- Unmap the pages
- Remove the pages from any VMM data structures (working set, page tree, referenced list)
- Exit
and most importantly, not have some dumb off-by-one bug that causes a week or twos worth of debugging
it also needs to account for any guard pages
added during allocation
well the question is how
I could just add a flag to the page struct
I just finished implementing the function
Time to test it
So it works
which is too good to be true
as the only fix I needed was changing RB_RIGHT to RB_NEXT
I'll test it to see if it can detect guard pages
then use my allocator test that runs the allocator multithreaded
while it seems the guard page was found
it doesn't seem to have the flag set
that's because there's a bug in the virtual allocate function
I forgot to make it offset the base when there's a guard page added.
wait why didn't it page fault then 
waiiiiiit
why are these pages already mapped
whoops
why aren't these pages in the rb tree
that's because they were never meant to be
I accidentally rounded up somewhere
where it was supposed to be rounded down
which caused the pages to appear
which isn't the case
yes
Nice
now I need to figure out who's dreaming up this new page, and isn't registering it
Gl
found it
it was in the initialization of the timer interface
it used the basicmm instead of the vma
to allocate a stack
but that memory was never recognized
the solution is to rather use the vma or initialize the timer interface earlier
I think it's time to add a back trace to the panic
too much work
OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "%s: Timer interface cannot be initialized before the VMM. Status: %d.\n", __func__, OBOS_STATUS_INVALID_INIT_PHASE);

I wonder why I put this
what could possibly go wrong
nothing blows up so I'm good
and it doesn't give me an already mapped page
still some bugs with guard page allocation/freeing
which I think I fixed
they're fixed
the vma free function is finished
now time for tests
time to search the git history for my multithreaded allocator tests
oh nvm it found a way to crash
I probably accidentally removed a line in preparation to commit
I forgot to check if the allocated pages had a guard page
before offsetting a page to the returned base
adding the check fixed that
so I'll now commit this code
that code is committed and pushed
lmao adding the test causes an immediate crash
the threads don't even get the chance to start
after rebooting, it double faults
damn it I got to stop making functions in the allocator pageable
I don't think the vma is ready for those bigger test yet, so I'll test having two allocated blocks from the vma at once
a smaller test
gives a KASAN violation
wait I forgot to remove pages from the vmm referenced list and working set on free
which is why it did a use after free
after fixing that, there is no use after free
bruh how is there stack corruption in a thread
before it even gets a chance to start
like it's right as it starts, before it even gets the chance to say it started
it's in KASAN
in __asan_storeN_noabort
but... how?
it doesn't make any writes to the stack but the stack canary (at least that's what it seems like)
probably some other thread sharing the same thread
which probably explains why sometimes it just double faults
gdb be acting kinda suspicious rn
I do print stack and it says unavaliable
or maybe I shouldn't be allocating the thread stacks pageable
bruh I added the flag VMA_FLAGS_KERNEL_STACK just for it to continue to do nothing
after fixing that, it page faults
but the thread at least gets the chance to say it started
oh no the crashes are random

The VMA returns a base address that isn't mapped
which causes a crash
now three things could be happening:
- The pf handler is having a smooth brain moment
- It just plain doesn't do anything and returns some random base
- It doesn't register some page node, causing the pf handler to not recognize the page
well it isn't a pf handler smooth brain moment
at least that's not what it seems like
I don't lock the free virtual memory function
I forgot to
so I'll do that and see if that fixes anything
my friend sent me this
OBOS is now pronounced "OBOE+SSS"
yeaaaa
it doesn't fix anything directly
That's how I've been pronouncing it in my head this whole time xD
good
I don't get why the allocator would just not allocate
if only I had a way to reverse process execution
the page faults, no matter the faulting address, always happen at the first reference to blk in the allocator
(note: blk is allocated by the vma)
which allocator are you using?
mine
what no
sorry now I'm confused
like malloc as in allocator?
my brain is a bit smooth xD
yeah that's what I meant by userspace allocator, bad wording sorry
Update: This seems to be a problem with multi-threading
Using 16 threads causes the kernel to crash eventually
however, using only 4 threads causes the tests to pass
actually nvm, it just crashed
it only seems to crash in two cases:
- The thread count > cpu count (scheduler bug?)
- on any reboots (unzeroed memory?)
actually no, I think my kernel just hates me
so it decides to crash occasionally
With 8 threads, the kernel sometimes crashes with an allocator failure
(well the test threads do)
OBOS_STATUS_IN_USE
which can only be coming from the VMA
if (exists)
{
if (flags & VMA_FLAGS_HINT)
{
base = 0;
goto top;
}
else
{
set_statusp(ustatus, OBOS_STATUS_IN_USE);
return nullptr;
}
}```
when the pages requested are already in memory, it returns this error
except the base is (indirectly) found using MmH_FindAvaliableAddress
which can only mean that it returns an address already in-use
OBOS_ASSERT(!(oldIrql & ~0xf));
has failed
I think I fixed it
TIL the allocator is slow(er) multithreaded
if I were to make it lockless then it could be faster
but I can't
(mainly because skill issue)
4/8 threads have exited successfully
so far
maybe this would be faster if I don't just lock unconditionally at the beginning of Allocate()
I'm just going to dump where the allocator needs to have the lock held in here
- When allocating a new region
- While appending a node
- While searching for a free node in a region
- Possibly while looking for a region to use
maybe I could optimize the allocator by using an rb tree somehow for the regions
or some sort of tree
instead of a linked list
as the time to search a region for the allocator is O(n) worst case
and if that ends up failing because there is no region big enough
it just ends up allocating a new one
and this is a lot of time wasted
I can finally merge the vmm
I've resolved the conflicts (it was only in the readme)
bruh now I have to write non-trivial code
time to design a driver interfac
I'll just dump everything here
each driver has it's own header
this header identifies the driver
as in how it should be detected in PnP
what it is
possibly the address of any callbacks like read,write,ioctl
drivers are going to be position independant executables
drivers will be able to access any kernel function, unless the kernel explicitly blocks it
yeah that seems good
after all, what could a driver interface possibly ever need other than that
also I'll need to implement DPCs of some form
since this all pretty non-trivial and boring
I'll port uACPI (@real pecan)
Noce
Drivers also want to access other drivers interfaces
oh yeah that as well
this time I won't rewrite in the middle of writing the driver interface
(no promises)
Lol
I wonder how much kernel api I can copy+paste without major changes
only one way to find out
I wouldn't be surprised if some headers still exist in this rewrite
well it uses lambdas because the previous rewrite was in C++
bruh
I have to deal with the IRQ code and deferred execution code

I like how most of the old kernel's code for the uacpi kernel api has mostly stayed untouched
deferred execution code ported
last time I checked uACPI wasn't this annoying to port
ok I got it to compile
Page fault at 0xffffffff80001cc5 in kernel-mode while to read page at 0x0000000000000000, which is unpresent. Error code: 0```
๐
bro
all that testing
just for it to hang in the idle task
on all CPUs
oh wait qemu was just being slow
That's why you use OOP ๐
I was doing something in the wrong order in the kernel api
uACPI, ERROR: unable to create FADT GPE block 0: invalid argument```

it was because my kernel api did something stupid
@real pecan
It works
idk why I decided to ping infy there
but it works
shutting down works
so does rebooting
so I'll just assume everything else works
unless uACPI is broken
I think I'll test OBOS on real hw
Lol literally nothing changed in kernel api
It was because of the new override macros causing compilation errors because I didn't have them
And speaking of nothing changing, the kernel ported my old kernel's impl of uACPI
Barely anything changed
Anyway I'll be back in a couple hours
Nice
I think I've figured out how driver dependencies will work. At driver load, the kernel will keep a list of each driver that the driver imports symbols from, and save that somewhere in memory. This way, I won't have to worry about drivers providing an invalid list of dependencies (and I don't have to worry about how the list would be stored).
If a driver is requested to be unloaded, but other drivers depend on it, the unload will fail with OBOS_STATUS_IN_USE
I'll just give each driver a ref count
idk whether to count the kernel as a module
for dependencies
that wouldn't make make sense
but the kernel can have dependencies on driver symbols. Meaning it can be counted in a driver's ref count.
except, it is not neccessary for the kernel to do this for symbols like the driver's read/write/ioctl functions
TODO: Figure out what the "required" symbols actually will be
just a screenshot of it not crashing
what should I call the directory that contains the code for the driver interface
'driver_interface' is a bit long
'di' makes no sense
should I do this like how I did in my previous kernel, or how I did it in my third kernel
my previous kernel had a special type of driver that was basically just for pnp
that would be used to detect pci(e) devices, as an example
there would be a seperate driver for each bus supported
and a driver would simply specify which driver it wanted to detect it
then the kernel would do the work
however acpi devices were the responsibility of the kernel
my third kernel was the same, but simplified
the driver would specify what bus it was on (ACPI and PCI were the only choices at the time)
and it would specify some stuff about the device it was representing
for PCI that would be the class(es), subclass(es), and progIf(s)
(I just realized it should've also optionally had a vendor id)
ACPI would just have the HID and CIDs
I don't want to load drivers for the sole purpose of detecting whether said drivers should be loaded or not
The driver should be loaded and its probe function called only after the device it controls is detected
U can keep the supported device list in a separate section or something if u want
But usually dynamic drivers are loaded by userspace
in this case it's for pnp
Statically compiled ones always register themselves at init and provide the supported device list
(not just ACPI PnP, but also loading drivers for PCI)
shit
I just realized I'm going to have to implement fork eventually
for user space
that sounds like a problem for future me
maybe I should make it be the other way around
I got the ACPI powerdown button to work
it hangs on real hw with that code, but I'm not too worried about that, as I have no EC driver
Nice
It shouldn't hang tho, it should exit with error if an ec access is attempted
You can also disable osi above windows xp and it wont touch ec
Lol
Yes
ive just started planning mine a few minutes ago also lol
how does the header look?
enum { OBOS_DRIVER_MAGIC = 0x00116d868ac84e59 };
typedef enum driver_header_flags
{
/// <summary>
/// Should the driver be detected through ACPI?
/// Cannot be specified if DETECT_VIA_PCI is specified.
/// </summary>
DRIVER_HEADER_FLAGS_DETECT_VIA_ACPI = 0x1,
/// <summary>
/// Should the driver be detected through PCI?
/// Cannot be specified if DETECT_VIA_ACPI is specified.
/// </summary>
DRIVER_HEADER_FLAGS_DETECT_VIA_PCI = 0x2,
/// <summary>
/// If the driver does not have an entry point, specify this flag.
/// </summary>
DRIVER_HEADER_FLAGS_NO_ENTRY = 0x4,
/// <summary>
/// If set, the driver chooses its entry point's stack size.
/// Ignored if DRIVER_HEADER_FLAGS_NO_ENTRY is set.
/// </summary>
DRIVER_HEADER_FLAGS_REQUEST_STACK_SIZE = 0x8,
} driver_header_flags;
typedef struct driver_header
{
uint64_t magic;
uint32_t flags;
struct
{
uint32_t classCode;
/// <summary>
/// If a bit is set, the bit number will be the value.
/// <para></para>
/// This bitfield can have more than bit set (for multiple values).
/// </summary>
__uint128_t subclass;
/// <summary>
/// If a bit is set, the bit number will be the value.
/// <para></para>
/// This bitfield can have more than bit set (for multiple values).
/// <para></para>
/// If no bit is set any prog if is assumed.
/// </summary>
__uint128_t progIf;
} pciId;
struct
{
// There must be an HID, but CIDs are not required.
// These strings are not null-terminated.
char hid[8];
char cids[16][8];
} acpiId;
size_t stackSize; // If DRIVER_HEADER_FLAGS_REQUEST_STACK_SIZE is set.
} driver_header;```
some lines have been copy+pasted (tm) from the previous kernels
it's more like metadata for the driver
yeah i have something similar
it can be anywhere in the file
maybe I'll make a section where it checks first
mine is a lot more basic than this currently
trust me most of that is just docs
just a function pointer to the get_kernel_func and stuff like ID and stack size right now
enum { OBOS_DRIVER_MAGIC = 0x00116d868ac84e59 };
typedef enum driver_header_flags
{
DRIVER_HEADER_FLAGS_DETECT_VIA_ACPI = 0x1,
DRIVER_HEADER_FLAGS_DETECT_VIA_PCI = 0x2,
DRIVER_HEADER_FLAGS_NO_ENTRY = 0x4,
DRIVER_HEADER_FLAGS_REQUEST_STACK_SIZE = 0x8,
} driver_header_flags;
typedef struct driver_header
{
uint64_t magic;
uint32_t flags;
struct
{
uint32_t classCode;
__uint128_t subclass;
__uint128_t progIf;
} pciId;
struct
{
char hid[8];
char cids[16][8];
} acpiId;
size_t stackSize; // If DRIVER_HEADER_FLAGS_REQUEST_STACK_SIZE is set.
} driver_header;```
that's without any documentation
yeah, still a lot more than mine
will your drivers be PIEs?
i plan on that yes
I need to rewrite relocation code (again)
otherwise it will be quite annoying to load them at the correct places
i dont have any kind of elf loading right now, still very early on in dev
started like 3 days ago
Drivers will have their own main thread that they can do anything they want with
at one point they were processes
but that design was cursed
so just like a "driver process" that has threads for each one?
in some of my previous designs, yes
my previous kernels' drivers mainly use their main thread for initialization of the device/fs/whatever it controls
then just exits
why not make it just block until it gets a request to do something?
that way you dont need to create extra threads later on
requests from the kernel were handled via a function table
for example, if the kernel needed to read from an AHCI disk, it would call the read function in the driver's function table
yes
so just sync IO then?
ye
i guess thats fine yeah
Yeah I found sending an init event to the driver was a lot less cumbersome than giving them their own thread.
although then tbh i dont know if you even need a thread for that
since its just one init func
Why separate his and cid fields
You can always create one later if you need it
you won't believe why:
||my previous kernels did so, so I assumed what they did was right||
Also reminder to both of you to not directly use elf sections.
why is that?
then it goes on to search for the header magic
because there is no guarantee to a symbol table
They are not required to be present at all, they're for the linker.
ah
i did not know that
perhaps i can just throw the header at the start of the file after linking it
How am I meant to do dynamic linking then
then when the kernel parses the elf, it just skips past the header
Yeah I didn't know for a while either. I recommend a custom phdr or just a big magic number
dynamic phdr
PT_DYNAMIC has everything you need
but what if there happens to be the exact instructions that the magic number represents somewhere

Use a bigger magic number

does this have its own flaws?
i guess it makes readelf slightly more annoying
looking at my elf loader code
it seems i had to specifically hunt out the GOT section to add the load base to everything inside that section
there was some thing in there
I forgot what
wait a sec
DT_PLTGOT
my previous kernels' dynamic loader just assumed it was a GOT section
and said:
// TODO: Find out whether this is the PLT or GOT (if possible).```
basically
ah i see
i am doing dynamic linking
not sure if I can call it proper
i planned on just providing a get_kernel_func that you call to get a function from its name
I had support for almost every elf relocation in x86_64
but I've only had one issue which was unimportant to me so I didn't address it -- that references to variables inside the kernel arent resolved properly
so like size_t MmTotalAvailablePages; isnt accessible by drivers
exactly what I'm doing
why would that happen?
thats why i called it unimportant
because of a loader skill issue i think
where is the loader
in the kernel
?
nope
I appreciate the comment ๐
its actually just the load base of the test.sys driver
and I appreciate you basically letting me adapt your work into mine
thanks for that
did r4 write your relocation code by chance
he didn't write any code for boron, I just adapted the Northport version
anyway MmTotalFreePages is defined in the GOT
IDA correctly identifies it as an external reference somehow
You shouldn't need to worry about the got directly, unless you're doing lazy load shenanigans
if it weren't for this, I would be cooked in terms of dynamic linking
This book describes the operations of the Solaris Operating System (Solaris OS) link-editor and runtime linker, and the objects on which these link-editors operate. The book covers the Link-Editor: ld(1), the Runtime Linker: ld.so.1(1), Shared Objects (sometimes referred to as Shared Libraries), and the ELF object file format.
wow this would have been useful
but I don't know of a better way
i wrote a userspace dynamic linker purely by examining elf files a while ago

readelf and objdump were my best friends at that time
but well I've been able to get by without such external references so it's probably ok
Do you process the dt_jmprel entries?
Yeah you do
oh yeah and does anyone know how to stop clangd from telling me this:
Unsupported option '-fsanitize=kernel-address' for target 'x86_64-unknown-unknown-elf'clang(drv_unsupported_opt_for_target)```
clangd 18.1.8
I think I process all the entries northport did when I was looking at it
I have all the elf structs I need for relocation code
now I'll just write the base of the loader
but before that I need to write some code to find each kernel symbol and stick it in a symbol table
because I'm lazy, the symbol table will be an rb tree
why are you in here and not in polls
Idk
Yeah, not the place for this?
coming back to this, if there is a global variable in that section is it required to be present?
or would it just be the var that is required
just the var
hm thats annoying
I've written the driver verification code
this too
I've made a resolve symbol helper function
now time to get to the loading part
basically it first searches the kernel for the symbol
if that fails, it searches the loaded drivers
if that fails, it returns nullptr
The VMA needs a memprotect function
or something similar
for the driver loader
I don't want to work around not having one
which I don't wanna explain how, but it's not very good
anyway I'll do that in about an hour, I gotta do something now
I'm back
I now make thing
the memprotect function will just take in a base and size
and new prot flags
sounds good enough
ok I'm done
I'll leave it completely untested
it's basically just a bunch of code stolen from the free function (verifying the pages' existence + looping over them)
and some code stolen from the allocate function (protection flags to the page's protection bitfield)
struct relocation
{
uint32_t symbolTableOffset;
uintptr_t virtualAddress;
uint16_t relocationType;
int64_t addend;
};
struct relocation_array
{
struct relocation* buf;
size_t nRelocations;
};
void append_relocation(struct relocation_array* arr, struct relocation* what)
{
arr->buf = OBOS_KernelAllocator->Reallocate(OBOS_KernelAllocator, arr->buf, ++arr->nRelocations, nullptr);
OBOS_ASSERT(arr->buf); // oopsies
arr->buf[arr->nRelocations - 1] = *what;
}```
This looks pretty good so far
(none of this is relocation code yet)
what file format are you using for your drivers?
ELF
then why not just have a pointer to Elf64_Rela from the file instead of copying that info into an array?
that is a good question
which I will have an answer for in a sec
iirc it was because there can be multiple Elf64_Rela things
something like that
yeah that is why
also I wanted a unified way to represent the relocations so I didn't have to copy a bunch of code
I just discovered a bug dating all the way back to the 3rd kernel
it would treat DT_REL dynamic headers as DT_RELA
true there's both DT_RELA and DT_JMPREL
idk how I hadn't found that eariler
also DT_REL you're unlikely to find anywhere these days
which I assume why that bug hadn't showed up
maybe it's more likely on 32-bit architectures
until I saw it while copy+pasting the code from my third kernel
maybe it'd be more memory efficient to store pointers to the Rela tables
in the array
instead of copying the entire thing
well not maybe, it would
i mean there's only 2 arrays that you only need to look at once
it's not like you're gonna keep referring to them all throughout the runtime of th driver
yeah true
I'll just have the array store pointers to the tables
struct relocation_array
{
Elf64_Dyn** buf;
size_t nRelocations;
};
static void append_relocation_table(struct relocation_array* arr, Elf64_Dyn* what)
{
arr->buf = OBOS_KernelAllocator->Reallocate(OBOS_KernelAllocator, arr->buf, ++arr->nRelocations, nullptr);
OBOS_ASSERT(arr->buf); // oopsies
arr->buf[arr->nRelocations - 1] = what;
}```
with the help of copy+paste from previous kernel where I did all the work, I think I'll be able to get this done in <20 minutes
first thing I'm going to do as soon as I have a stable driver interface is make an AHCI driver
then make swap be on disk
I think I'm done the driver loader
I'll test it
inb4 everything just breaks
like straight up doesn't work
because I'm too lazy to make a proper driver to test with
I'm going to make some makeshift driver with some dumb header
basically only bare minimum to get it loaded
done
I added in some dumb header'
after some bug fixes
it finds the driver header
but it never loads the elf object
||because I forgot to add in the call to the function that does so||
it almost works
I just need to solve some paging shenanigans
done
it was something to do with the tlb
it now has yet another cryptic log message
now if anyone was wondering, the test driver doesn't actually exist on the git repo
it's a < 100 line file that just has some dumb driver header and imports OBOS_Log
that's compiled into a PIE
that's turned into a c header file
which is loaded by the kernel
I'll test further tomorrow
and make this an actually proper driver
but until then
so yeah tomorrow I'll test dependency counting
which is actually not finished
_ _
load it as a module with your bootloader man
also limine can load multiple modules
I know, this is purely temporary
๐
The test driver isn't even built alongside the kernel
This "driver" was purely there to test relocation
so the thing broke
I think I told the linker to do something stupid
but I can't quite figure out what
one of the relocation tables is just zeroes
unless I'm parsing this wrong
readelf seems to understand it
I don't get it
how does 0x1178 become 0x2178
for readelf
well it's using the section which has that offset
I think I wrote my linker script wrong then
Can anyone make sure I didn't write anything that would mess up the dynamic header
i'd ask mint
I link using:
-nostdlib
-Wl,--export-dynamic
-Wl,-shared
-Wl,-pic
-T driver_link.ld```
@sharp pike ?
is there anything that would mess up the dynamic header though
what is the loader?
OBOS
no, as the dynamic header seems to be messed up
when it finds the PT_JMPREL header
it gets the offset 0x1178
which is the same as reported by readelf
except, there's only zeroes at that offset
Dynamic section at offset 0x4000 contains 15 entries:
Tag Type Name/Value
0x0000000000000004 (HASH) 0x1148
0x0000000000000005 (STRTAB) 0x10f8
0x0000000000000006 (SYMTAB) 0x1050
0x000000000000000a (STRSZ) 79 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x2158
0x0000000000000002 (PLTRELSZ) 72 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x1178
0x0000000000000007 (RELA) 0x1178
0x0000000000000008 (RELASZ) 24 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffb (FLAGS_1) Flags: PIE
0x0000000000000000 (NULL) 0x0
Relocation section '.rela.dyn' at offset 0x2178 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000002150 000100000006 R_X86_64_GLOB_DAT 0000000000000000 __stack_chk_guard + 0
000000002170 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __stack_chk_fail + 0
000000002178 000400000007 R_X86_64_JUMP_SLO 0000000000000000 OBOS_Log + 0```
in the memory image?
as in the base you load the driver at + dt_jmprel_offset
wait that's what I was supposed to do
I thought it was an offset into the program binary
How are all these bugs in my code (since my third kernel!) only showing up now
lol
what would I want then?
well idk that's up to you, i am saying that because you're passing -shared -pic to the linker
that's usually done for shared objects, not PIEs
the only reason I passed -shared was because I didn't really know how to get the linker to understand that the symbols that were unresolved were from the kernel
for PIE you link with -pie, and as a matter of fact i am surprised the final object is marked as PIE, unless the C compiler driver is passing -pie on its own
PIEs can still link to outside symbols
how do I get the linker to assume that any unresolved symbols are somewhere else
like undefined reference to `OBOS_Log'
one option would be to make a shared library that contains stubs for all the exported kernel symbols and link the exe against that
I use -shared in boron
that's what I was just advised not to do
or just make the driver be a shared library and pass --allow-shlib-undefined to the linker
here are my flags
override CFLAGS += -fno-omit-frame-pointer -std=c11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIC -m64 -march=x86-64 -mabi=sysv -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone -MMD -MP -I.
override CXXFLAGS += -fno-omit-frame-pointer -std=c++17 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIC -m64 -march=x86-64 -mabi=sysv -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone -MMD -MP -fno-exceptions -fno-rtti -I.
override LDFLAGSBASE += -nostdlib -shared -e $(DRIVER_ENTRY) -m elf_x86_64 -z max-page-size=0x1000
it's fine to make a PIE for anything that does not need to be linked against
if drivers are meant to use symbols from each other, then shared objects is the way to go instead
ok
so yeah you can pass --unresolved-symbols=ignore-all i guess, even when making a PIE
additionally, you should probably add the headers to your PHDRs
which headers?
like something like PHDRS { headers PT_PHDR PHDRS; text PT_LOAD FILEHDR PHDRS;
not enough room for program headers, try linking with -N```

wait
ok I fixed it
now time to continue with the driver loader
all that's left for me to do is the dependency thing
which will be pretty simple
and adding the driver's symbol to some list
also simple
btw scratch this
ld doesn't seem happy unless you make a shared object instead of a PIE
with unresolved symbols
so it's either shared object or what qwinci said
afaik
well shared object is a lot more convinient
so I'll go with that
btw, how do I set the visibility of a symbol defined in a linker script
i'm not sure you can within the linker script, but i'd check the documentation
well some stack overflow answer says you cannot
[ DEBUG ] Loading test driver.
[ LOG ] OBOS_DriverEntry: Hello from a test driver. Driver base: ffffff00004a4000.
[ LOG ] Test!```
it works again
I needed to implement some hashtable code
for accessing symbols internal to the shared object
and by implement, I mean copy+paste from my previous kernel
all that's left is this
my thought is to just iterate over symbols in the dynamic symbol table, and all symbols that aren't marked as undefined will be added to the list of driver symbols
then there's one more thing I'd need to do, and that's make all phdrs without the flag PF_OBOS_PAGEABLE be non-pageable
the spaghetti code that is the driver loader is completed
HIDDEN(whatever_sym = .)? idk if that's what you meant tho
or is it
I'm getting a bogus address in the GOT
uACPI init takes way to long in tcg+debugger attached
my vmm is probably the bottleneck
or maybe dozens of breakpoints within critical kernel api wasn't a good idea
oh wait
the bug with the loader
wasn't a bug with the loader
it was because the imported symbol was hidden
upon making the api for finding symbols better it errors out on load
instead of crashing during driver execution
now that I've exported that symbol\
the driver loads successfully
that's like, the majority of the driver interface done
the other part is pnp
and determining what functions a driver might have in some function table
then I'm making an AHCI driver
probably
then I'm making swap run off of the AHCI driver
now I'm going to test whether drivers can import functions from others
for that, I'll need yet another test driver
the code is pushed
the symbol resolver does not work properly
but I will debug that later
I fixed it
I forgot to insert the symbol into the symbol table lol
just pushed the bug fix
I think drivers will be split into devices that can be read/written, and ones that do other stuff
an example of the former is an AHCI driver
an example of the latter is a gpu driver
however, each driver will have an ioctl callback
no matter the type
for example, maybe the AHCI driver would have an ioctl for power management of the drives
since most devices aren't just read and written from
and have some additional configuration you might need to do
or some other stuff you might want to do for a performance boost
in theory, pnp will be pretty simple
the kernel compiles a driver list
then the pnp thing will seperate those drivers into ACPI drivers and PCI drivers (a driver can be in both of these lists)
then, the kernel will iterate over the ACPI devices using uACPI, and for each one, it will search the ACPI list for a driver that has said device's HID
or maybe I'll have a map of HIDs->Driver(s)
that'd be faster
as for PCI, the kernel will simply enumerate the bus and for each device, the kernel will search for a driver supporting the class code, subclass, prog if, and vendor id
perhaps also with a map
and maybe I'll throw PnP into a kernel module
in case someone wants to just have a config file of drivers to load at init
what happens if there is a device which is both in PCI and ACPI?
both cases are run
oh wait I thought you meant driver
I'll probably just make the driver header have a member which specifies its preferred interface
to be detected
holy
the driver interface so far has added 2000 loc
to the kernel
I'm starting work on this
but before that
I'm going to compile the new qemu version
(debian is stuck on 7.2.11)
this is taking a lot longer than I thought
6000 more files left
bruh
it was compiling all targets
I think I'll just do x86_64-softmmu for now
then when I port obos I'll compile more targets
I'll just leave it to build overnight
it only took me ~5 minutes for the compilation
but I was having problems with configure
QEMU 9.0.50 monitor - type 'help' for more information```
unfortunately, tcg isn't much faster
@real pecan Your uACPI broke
on real hw
hold on let me turn on trace logging
oooo UACPI_LOG_DEBUG
nvm my tty is too slow
maybe the scheduler did something dumb
it did
what it did?
I have no idea
debugging a scheduler bug present only on real hardware without a debugger
yeah no
why in the world would it just hang
without any trace
randomizing qemu's memory doesn't reveal any problems
since I don't care enough about that at the moment
I tried compiling as release
for it to pf in find avaliable address
lastNode is nullptr
idk why
if ((currentNodeAddr - lastAddress) >= (size + pgSize))
isn't supposed to be true at the first iteration of the loop
now why is the kernel just not adding pages to the rb tree
you know what fuck this
I'm too tired for this shit
X to doubt
Yeah it was the scheduler
Rename uACPI to uStressTester
Lol
it wasn't uACPI that was causing problems with the scheduler
when I masked out the scheduler during uACPI init, it went through init just fine
but as soon as the scheduler was unmasked everything hung
I was able to reproduce this bug in debug mode