#OBOS (not vibecoded)

1 messages ยท Page 15 of 1

lean glen
#

would be simpler I think

flint idol
#

the refcnt for the vnode includes dirents

#

so the page cache wouldn't be freed even if there aren't opened files

#

I'm going to add a mutex to the page cache entry

#

for locking IO on it

#

actually

#

I'll have two mutexes, one for modifying the structure (e.g., expanding the page cache size)

#

and another for the dirty region list

#

which I'll actually make an rb tree

#

maybe not

#

this is all just me thinking

#

not final

#

maybe not an rb tree

#

but regardless of the type of the structure

#

on read, the dirty list will be queried to see if there is a dirty entry

#

if so, it's locked

#

same thing on write

#

this way, reads on non-dirty regions can be done without any locking

#

and writes will ensure synchronization

#

(I finally spelled 'synchronization' right first try wow)

#

I'm also thinking that async io will be done using an event lock (not really a lock)

#

since it seems pretty good for the job

#

when the kernel is done the IO, it can set the event

#

causing any thread waiting on it to wake

#

and it can also be queried

#

so a thread can peridoically check if it's done

#

the work itself will probably be done on a worker thread

#

but the driver interface doesn't currently support async io

#

I'll do async io after fixing the page cache

flint idol
#

if so, it's expanded if needed

#

otherwise a new one is made

flint idol
#
typedef LIST_HEAD(dirty_pc_list, struct pagecache_dirty_region) dirty_pc_list;
LIST_PROTOTYPE(dirty_pc_list, struct pagecache_dirty_region, node);
typedef struct pagecache_dirty_region
{
    // take this lock when any reads or writes are done on the region this represents.
    // also take it if expanding 'sz'
    mutex lock;
    size_t fileoff;
    size_t sz;
    LIST_NODE(dirty_pc_list, struct pagecache_dirty_region) node;
} pagecache_dirty_region;
typedef struct pagecache
{
    // Take this lock when expanding the page cache.
    mutex lock;
    char* data;
    size_t sz;
    // Take this lock when appending a node to the dirty region list.
    mutex dirty_list_lock;
    dirty_pc_list dirty_regions;
    atomic_size_t refcnt;
} pagecache;```
#

Those are my finalized page cache structs

flint idol
#

now cached reads are as simple as:

pagecache_dirty_region* dirty = VfsH_PCDirtyRegionLookup(&desc->vn->pagecache, desc->offset);
if (dirty)
    Core_MutexAcquire(&dirty->lock);
if (desc->vn->pagecache.sz <= desc->offset)
    VfsH_PageCacheResize(&desc->vn->pagecache, desc->vn, desc->offset + nBytes);
memcpy(buf, desc->vn->pagecache.data + desc->offset, nBytes);
if (dirty)
    Core_MutexRelease(&dirty->lock);```
#

cached writes are essentially the same

#

except the memcpy goes the other way

#

page cache flushing is pretty easy:

driver_id* driver = vn->mount_point->fs_driver->driver;
for (pagecache_dirty_region* curr = LIST_GET_HEAD(dirty_pc_list, &pc->dirty_regions); curr; )
{   
    pagecache_dirty_region* next = LIST_GET_NEXT(dirty_pc_list, &pc->dirty_regions, curr);
    driver->header.ftable.write_sync(vn->desc, pc->data + curr->fileoff, curr->sz, curr->fileoff, nullptr);
    curr = next;
}```
#

I have no way to test writing atm, as I have no RW fs drivers

#

but I did implement it at least

#

however, it is practically a memcpy

#

for cached writes

#

damn

#

I have ||2244|| additions in two days

#

and ||98|| deletions

#

maybe I should start going outside

#

now time for async io

#

kernel is sitting at 26K loc

#

which is 3k more than my third kernel

#

this rewrite is starting to reach the state of the third kernel

#

all that's different is the fact that it had userspace code that ran in ring 3

#

I think async io will use a combination of event (signallable objects) and worker thread(s)

#

when an async read or write is made, the kernel starts a (kernel-mode?) thread

#

which does the IO, rather through the filesystem driver, or through the page cache, depending on the fd's flags

#

when the request is completed, the kernel will set the event

#

causing any thread waiting on it to wake

#

but if the data is already present in the page cache, no worker thread is made, and the operation completes immediately

#

(taking slight inspiration from NT)

flint idol
#

if the filesystem is not backed by a disk

#

then this case simply does not exist

flint idol
#

actually no

#

the data is copied over

#

but a worker thread is made to fill in the missing parts in the user buffer

#

and not the page cache

flint idol
#

I might rewrite the initialization code soon

#

to not be so fucking cluttered

#

arch/x86_64/entry.c is 1100 lines

flint idol
#

I am currently looking for a bug with the vma and huge pages

#

some funky shi be happenin'

#

currentNodeAddr < lastAddress

#
uintptr_t currentNodeAddr = currentNode->addr - (currentNode->addr % pgSize);
#

idk why I round it down

#

now it allocated the virtual memory

flint idol
#

ok async reads seem to work properly...

#

I think I might use async reads while loading additional drivers from the cmdline

#

why? because async is cool

#

the vfs is probably nearly done

#

I just need to fix bugs

#

and also add some way to list directories

#

and open directories

#

but that won't be too much effort

#

and also unmount

#

that might take a bit

#

now I could do that tomorrow

#

or I could touch grass

inland radish
#

tbh

#

consider trying this

MMPFN DirectPages[12], *IndirectPtr, **DoublyIndirectPtr, ***TriplyIndirectPtr /*etc*/;
main girder
#

hey thats my idea.

inland radish
#

it's also your idea

#

but it's also something ext2 does

main girder
#

not just ext2

#

the very first unix filesystem did it

#

probably got the idea from multics

inland radish
#

idk thats where I remember the idea from

flint idol
inland radish
flint idol
#

That was an old pagecache entry

flint idol
flint idol
#

I'm going to implement file mapping in a bit

#

shouldn't be too hard

#

just map some regions with the page cache

#

the parts that don't exist in the page cache

#

will be unmapped

#

so on page fault, that part of the page cache can be filled

#

and the pages can be mapped

lean glen
#

Yeah then do CoW on those pages

flint idol
#

for private maps, then yeah I'll do ๐Ÿ„

vale nymph
#

moo

inland radish
#

I would use a tree keyed by initial address in your case

flint idol
#

the normal page struct which is an rb-tree

inland radish
#

and then use something like RB_NFIND except the condition is opposite to find the relevant mapping

flint idol
#

so RB_FIND

inland radish
#

RB_FIND finds an exact matching element

#

im talking about something that looks for the greatest element less than or equal to the key

#

RB_NFIND finds the least element greater than or equal to the key

flint idol
#

so like:

page what = { ... };
page* found = RB_NFIND(page_tree, &context->pages, &what);
if (found->addr == what.addr) // the address is the key
  return found;
return RB_PREV(found);```
#

idk why I would want to do this instead of just finding the exact element

inland radish
inland radish
flint idol
#
uintptr_t virt = getCR2();
virt &= ~0xfff;
if (Arch_GetPML2Entry(getCR3(), virt) & (1<<7))
    virt &= ~0x1fffff;```
#

maybe that'll be a problem for getting the offset

inland radish
#

certainly not within a range that spans several pages

#

eh you might be able to use the PTE to find the address of the VAD list entry

flint idol
#

I am currently working on CoW

#

before implementing file mapping

#

it should work when the page is shared across two contexts

#

more than two contexts, likely not

#

actually maybe

#

I am also making OOM within page faults be handled better

#

if a page is being swapped in, but there is not enough physical memory, it will try to swap out enough pages to get enough

#

if it cannot

#

then it will block until there is enough memory

flint idol
#

along with a linked list of threads, along with how much memory they are requesting

#

then whenever a page is swapped out, the VMM will check wake as many threads as it can

#

actually, I could just use a bare struct waitable_header to implement this

#

then keep how much memory the thread needs in struct thread

flint idol
#

so it'll only try to swap out one page

#

otherwise it will block until enough memory is freed

flint idol
#

it only took me 4 months to realize that I should've done that

flint idol
#

the thingy broke

#

it's incorrectly calculating the physical memory boundaries somehow

#

this is all it is:

if ((phys + nPages * OBOS_PAGE_SIZE) > Mm_PhysicalMemoryBoundaries)
    Mm_PhysicalMemoryBoundaries = (phys + nPages * OBOS_PAGE_SIZE);```
#

at some point phys became garbage it seems

#

ok I fixed it

flint idol
#

wait...

#

the pmm has been lock free

#

the entire duration of the kernel

#

and I have had no problems

chilly osprey
#

clearly a sign that it can remain lockfree

flint idol
#

I'll just keep it lock free until I have problems meme

elder hornet
#

I have no idea what this means but it seems like nice progress :D

flint idol
#

I think it is

#

I'll be honest idk what's happening here either

#

When I wrote this code, God and I knew what it did.

#

Now, only God knows.

lean glen
#

I'd lock at least the page queues

flint idol
#

discord is being funny

chilly osprey
#

i dont think that was a discord thing

flint idol
#

why would he send the same message twice

lean glen
#

I'm in a low cell coverage zone atm

flint idol
#

but with a word swapped

lean glen
#

I sent the first message then it disappeared somehow so I sent another one saying the same thing

flint idol
#

ah ok

chilly osprey
#

i could see it happening if they were identical

#

but swapping words seems unlikely

flint idol
#

I thought he might've edited one of the messages

chilly osprey
#

ah thats true yeah

flint idol
#

but discord somehow sent it as a separate message

flint idol
#

anyway, now that I have CoW implemented

#

I can implement file mapping probably

lean glen
#

What method do you use for CoW?

#

Refcount then check on fault?

flint idol
#

map pages as RO

#

then on fault, allocate a separate physical page

#

copy the contents there and remap one of the pages to use that phys. address instead

#

exit

#

this only works for two pages though

lean glen
#

Ok but what if it's a page that wasn't forked

#

how do you know whether a page has been forked

flint idol
#

basically if that's nullptr, the pf handler knows that the page faulted for some other reason

#

and not because of CoW

chilly osprey
#

i was thinking you just

#

remap to a new page if it faults

#

and memcpy from the original

#

and set the write bit obviously

flint idol
#

shit I almost forgot to memcpy

chilly osprey
#

good thing i expressed my confusion

chilly osprey
#

i was thinking of a different situation which wouldnt really apply

flint idol
#

I think I should maybe defer file mapping-related page faults

#

outside of the pf handler

#

and block until it's done

#

as I don't think reading from files and allocating memory for the page cache is a good idea while the pf handler is running

chilly osprey
#

worker thread is always the solution

flint idol
#

or I could implement APCs

#

then start a kernel APC on the current thread

#

that does the pf handler's work

#

or I could add:
TODO: Use kernel APCs instead
then use a DPC to do the work, then signal an event when it's done

#

which causes the thread to wake up

#

actually a kernel apc might not work

lean glen
flint idol
#

I know

lean glen
flint idol
lean glen
#

I'm unsure where refcounting comes from, I took it from uvm but it may have been done in solaris

flint idol
flint idol
chilly osprey
lean glen
#

Yeah it's simpler and more efficient to do refcounting

chilly osprey
#

refcounting is what i thought was the way to do it

#

yeah

lean glen
#

Shadow objects implements copy on write. When a VM object is duplicated (e.g., at fork) a shadow object is created. A shadow object is initially empty, and points to underlying object. When the contents of a page of shadow object is modified, the page is copied and insert in the list of pages for the shadow object. A series of shadow objects pointing to shadow objects or original objects is a shadow chain.

#

idk if this is a course, I know pdos is the MIT OS team tho

#

Looks cool anyway

flint idol
#

and I'm also going to need to start using mutexes more often

#

and converting some places to use mutexes instead of spinlocks

flint idol
#

as I've thought about it, and unless someone does something schizophrenic, it'll be fine

chilly osprey
flint idol
#

so that it doesn't break with more than two pages doing CoW

#

I think this code is right if I want to insert a page inside a linked list:

if (pc_page->next_copied_page)
    pc_page->next_copied_page->prev_copied_page = page;
page->next_copied_page = pc_page->next_copied_page;
page->prev_copied_page = pc_page;
pc_page->next_copied_page = page;```
#

specifically to insert page in front of pc_page

lean glen
chilly osprey
#

i already intended on doing the refcounting

lean glen
#

Yeah fair

chilly osprey
#

it seems simple enough but ill read the paper anyway

lean glen
#

You don't strictly need it but the concept of amaps is pretty simple and makes it easy to add other fancy features too

chilly osprey
#
on failed write(page):
    page.refcount++;
    if page.refcount = 1:
        set write bit in page
    else:
        allocate new phys page and map it to virtaddr
        copy to the new page``` this is my current understanding basically
chilly osprey
lean glen
#

and check perms too

chilly osprey
#

i just incremented it above

lean glen
#

yes

chilly osprey
#

how?

lean glen
#

no

#

nvm

#

Not in your case

chilly osprey
#

ah

lean glen
#

But it can and will drop to 0

#

then you can release it

chilly osprey
#

but surely then it would be incremented again to 1 if another page writes to it

#

yeah that too

lean glen
chilly osprey
#

this is only on failed writes though which is the only time we would need to copy

#

or am i misunderstanding

lean glen
#

refcount is used to keep track of how many times the page was copied

#

Well

#

Forked

chilly osprey
#

yeah

#

ill just read the paper

#

it will save us both all of this back and forth

lean glen
#

so you need to copy the page when it was written to and was forked (refcnt>1)

lean glen
ornate ginkgo
flint idol
#

something weird happened

#

added:

ctx->workingSet.size -= (page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE);
#

when a page is removed from the working set

#

to be greeted with:

ASAN Violation at ffffffff80053ea9 while trying to write 8 bytes from 0xffff80000257f0b8 (Hint: Use of memory block after free).```
#

uStressTester strikes once again nooo

#

(that's what I'm calling uACPI from now on)

main girder
#

have you not stress tested your heap

flint idol
#

I have

#

although this seems to be a bug in my vmm

#

that wasn't caught

#

because of another bug in my vmm

main girder
#

describe it

flint idol
#

The vmm swaps out pages (specifically those in the working-set that are getting evicted) in the page replacement algo code. I seem to have forgotten to subtract that page from the size of the working-set pages (context->workingSet.size)

#

causing the working-set to keep the same pages in it practically forever

#

the fix to that bug uncovered other bugs in the vmm

main girder
#

like ?

flint idol
#

use after frees because nodes aren't being removed from structures

main girder
#

ur swapping is bad btw

flint idol
#

how come?

main girder
#

you do the same naive thing i did at first where you write out 1 (one) page and then immediately free it

#

probably on demand in response to a memory allocation that would otherwise fail

#

MmH_FindAvaliableAddress

#

typo

#

avaliable

flint idol
#

what the H

#

?

#

oh

real pecan
#

ava liable

main girder
#

what thehell is this doing...

flint idol
#

supposed to initialize a spinload

#

*spinlock

#

shit

#

spinlocks aren't just atomic flags anymore

main girder
#

when you trim a page from a working set (or its refcount otherwise hits 0 but thats the most common reason)

#

and it is marked dirty

#

i.e., it has either never been written to the pagefile, or, it has been written to since the last time it was written to the pagefile

#

you place it on the 'modified page list'

#

when the number of pages on that list exceeds a threshold OR memory gets low, a thread called the modified page writer is awoken to 'clean' those pages by allocating contiguous clusters in the pagefile and writing them out in as big of clustered IOs as you can

#

this does not reclaim those pages immediately

#

they are merely marked clean and placed on the 'standby list'

#

which is where clean (non-dirty) pages go when their refcount hits 0

#

page frames on the standby list are considered free memory

#

and when the free list runs out, you yoink a standby page

#

for instance most of the page cache at any given time will (probably) be on the standby list

#

when both lists run out, by that point the modified page writer has been signaled to start writing pages for one reason or another and some will be available on the standby list at some point

#

so you block until they are

#

if you were holding any locks that need to be grabbed in order to free memory (such as your working set lock) then you drop them before blocking for free pages otherwise youll cause a deadlock

#

which may require you to back out of whatever half-finished thing you were doing and then retry it after youre unblocked and can reacquire the lock

main girder
#

everything i just said applies to anonymous (pagefile-backed) pages but analogous things occur with file pages as well

main girder
#

which means you have more time, if you happen to need it again, to fault it back into your working set before it gets reclaimed

#

and then if it gets trimmed again and you only read from it while it was mapped, itll go back on the standby list immediately when trimmed cuz it already has valid backing in the pagefile from last time it was trimmed and went on the modified list

#

but if you wrote to it then youve dirtied it which invalidates its backing in the pagefile

#

so then itd go back on the modified page list

flint idol
#

I would check if age == 1

#

but once the sampling interval ends, age is shifted left by one

#

but sometimes, I guess the page isn't removed from the referenced list at the end of the sampling interval

#

now a garbage physical address is being passed somewhere

#

:/

#

I added an assertion to allocate physical pages:

Assertion failed in function Mm_AllocatePhysicalPages. File: /home/oberrow/Code/obos/src/oboskrnl/mm/pmm.c, line 142. phys < Mm_PhysicalMemoryBoundaries```
#

maybe I should add a lock to the pmm

#

that didn't fix the issue (at hand)

flint idol
#

I was passing a byte count instead of a page count to Mm_FreePhysicalPages in one specific case

#

but otherwise, I get

Stack corruption detected at IP=0xffffffff80005788 (overwrite of stack canary).```
#

"fixed" that

#

because now I get

Page fault at 0xffffffff8000516a in kernel-mode while to write page at 0x0000000000000000, which is unpresent. Error code: 2```
#
page* node = Mm_Allocator->ZeroAllocate(Mm_Allocator, 1, sizeof(page), &status);
#

that's where it faulted

#

bug doesn't happen on m68k, x86_64 support is dropped

#

the m68k port has so much less bullshit going in it

#
[ DEBUG ] Arch_KernelEntry: Initializing allocator.
[ DEBUG ] Arch_KernelEntry: Initialize kernel process.
[ DEBUG ] Arch_KernelEntry: Initializing IRQ interface.
[ DEBUG ] Arch_KernelEntry: Initializing VMM.
[ DEBUG ] Arch_KernelEntry: Initializing timer interface.
[ DEBUG ] Arch_KernelEntry: Initializing scheduler timer.
[ DEBUG ] Arch_KernelEntry: Loading kernel symbol table.
[ DEBUG ] Arch_KernelEntry: Loading test driver.
[ DEBUG ] Drv_LoadDriver: Loaded driver at 0xc04a3378.```
this is the log on the m68k
#

the one for x86_64 doesn't fit in a message

#

all for the exact same functionality (except for PnP)

flint idol
#

don't we just love it when an allocation of 13 bytes fails

#

it was because huge pages aren't supported on the m68k (at least I don't know of such thing for the m68040's mmu)

#

causing the map function to return unimplemented

#

after fixing that I can do all x86_64 can

#

but with about 30 less files

#

and it doesn't crash either

#

no way I actually got the x86-64 port to OOM

ashen goblet
#

has the obos-curse returned?

flint idol
#

only for x86-64

ashen goblet
#

maybe it's just hidden on m68k but still there

flint idol
#

actually this isn't OOMing, I'm simply running out of swap space

#

one would think 64 mib would be enough

lean glen
#

now compress it

flint idol
#

nvm, the in-ram swap is getting deep fried

#

fixed that

#

but the swap still claims to have no space

#

(there's only ~15000 nodes with the exact amount of space needed)

#

I fixed that

#

to get a ubsan violation in swap_free

flint idol
#

my in-ram temporary swap seems to be very broken

#

it's causing everything from ubsan violations to page faults to KASAN violations

#

bro wtf

#

when I copy+paste the same thing, but on the m68k port

#

it's completely fine

#

like the initial swap thing was copy+pasted from x86_64's implementation

#

all that's different are the IRQLs being raised

flint idol
#

why is this shit like this

#

the m68k driver loader has decided to break

flint idol
#

it saw

#

*was

#

the ld script is broken

inland radish
#

like .text.init, .text.page

flint idol
#

won't work, I have to be able to separate them through PHDRs

#

it's not quite allowed to use sections within an elf (program) loader

inland radish
#

you dont need to do that

#

you simply do like

#

1s

#
    /* Define the regular text section. */
    .text : {
        *(.text)
    } :text
    . = ALIGN(CONSTANT(MAXPAGESIZE));
    
    /* Define the initialization text section, which will be permanently reclaimed after system initialization. */
    KiTextInitStart = .;
    .text.init : {
        *(.text.init)
    } :text
    . = ALIGN(CONSTANT(MAXPAGESIZE));
    KiTextInitEnd   = .;
    
    /* Define the paged text section, which may not be resident at all times, pages may be swapped out and
       back in at the memory manager's discretion.  */
    KiTextPageStart = .;
    .text.page : {
        *(.text.page)
    } :text
    . = ALIGN(CONSTANT(MAXPAGESIZE));
    KiTextPageEnd   = .;
    
    /* Glob all of the other text sections into .text. */
    .text : {
        *(.text.*)
    } : text
#

this is what I do in boron

#

they all belong in the text phdr

#

the sections themselves are aligned so that they dont straddle page boundaries which would make pageout/reclamation impossible because part of untouched .text is in the way

flint idol
#

time to lock in and get file mapping done

#

I've done like everything for file mapping- except for file mapping itself (i.e., in the virtual memory allocate function)

flint idol
#
obos_status status = Vfs_FdOpen(&file, pathspec, FD_OFLAGS_READ_ONLY);
if (obos_is_error(status))
{
    OBOS_Debug("Could not open %s. Status: %d\n", pathspec, status);
    goto end;
}
Vfs_FdSeek(&file, 0, SEEK_END);
size_t filesize = Vfs_FdTellOff(&file);
Vfs_FdSeek(&file, 0, SEEK_SET);
char *buf = Mm_VirtualMemoryAlloc(&Mm_KernelContext, NULL, filesize, 0, VMA_FLAGS_HUGE_PAGE, &file, nullptr);
OBOS_Debug("Mapped %s. Contents:\n%*s\n", pathspec, filesize, buf);```
#

file mapping works

#

at least shared mapping does...

#

if the region is already in the page cache, it crashes

#

oops

#

now the region can already be in the page cache

#

as I expected, private mapping doesn't work

#

I think that's fixed

#

except I don't do CoW

#

for private mappings

#

I'll do that "quickly"

#

CoW seems to work

#

maybe that's mainly because there are no writes...

#

CoW works!

#

although I think there could be a bug where a page originally marked as RO can become RW if it's marked as CoW

#

before I commit this, I just need to make one last change with shared mappings

#

so that they are marked as dirty in the page cache if they are modified

flint idol
flint idol
#

I think shared and private file maps work

#

just pushed the code

vale nymph
#

syscalls/mlibc now?

flint idol
#

TODOs for tomorrow:

  • Directory handles
  • Making sure devices work when mapped on a vnode
  • Maybe unmounting
flint idol
#

I want to refactor the x86-64 code, as it is quite a mess

flint idol
#

mapping the uart driver's file works flawlessly on the first try

#

idk about freeing though

#

that might be a problem

#

Oh my god I might be dumb

#

the virtual memory free never releases any physical pages

ornate ginkgo
#

lol

vale nymph
#

who needs to release memory anyways computers have millions of pages

ornate ginkgo
#

just put everything important in the swap space, reset the machine, memory freed!

flint idol
#

so if the file isn't a mapped file or it's a shared mapping, then I think it's safe to free the physical page

#

but now something decides to hang

#

or maybe the PMM is just really, really slow

#

nvm something's hanging

#

I hate tlb shootdowns

#

this is the millionth time something's hung because one cpu decided it was special and didn't want to handle a tlb shootdown

#

idk how safe it is to wait until after the context lock is released to unmap pages

#

might cause a race condition in which one thread tries to map the reclaimed pages while they're being freed

main girder
#

context lock?

flint idol
#

a lock for a vmm context

flint idol
#

I can write to a serial port using the VFS!

#

I now need to fix a bug with the UART driver though

#

basically, a synchronous read is asynchronous

#
pathspec = "/dev/COM1";
Vfs_FdOpen(&file, pathspec, FD_OFLAGS_UNCACHED);
Vfs_FdIoctl(&file, 6, /* IOCTL_OPEN_SERIAL_CONNECTION */0, 
    1,
    115200,
    /* EIGHT_DATABITS */ 3,
    /* ONE_STOPBIT */ 0,
    /* PARITYBIT_NONE */ 0,
    &file.vn->desc);
file.vn->un.device->desc = file.vn->desc;
char message[15] = {};
Vfs_FdRead(&file, message, 14, nullptr);
Vfs_FdWrite(&file, message, 14, nullptr);```
#

If you write 14 bytes onto the serial port, it will echo them back to you

#

(most exciting 14 bytes I've had in a while)

#

tomorrow, I will work on async IO from devices

#

as it needs to be handled slightly differently to async IO on files

#

actually maybe not

flint idol
#

I will also add an API to the driver interface which adds a special file to /dev

flint idol
flint idol
#

I've added some APIs to allocate vnodes and dirents for drivers

#

I am now working on block devices to make sure they work properly

flint idol
#

in fact maybe even forever

#

time to implement unmounting nooo

#

in theory, this will be simple

#

for each dirent, deref the vnode (decrement its refcount, and free it once the ref count reaches zero)

#

then free the current dirent

#

then I will also need to close any open files on the mount point, and flush each vnode's page cache

flint idol
#

and wait for any pending async IO operations to finish

#

Now in order, the unmount function needs to:

  • Lock the mount point
  • Close all opened files.
  • Wait for pending async IO to finish
  • Free the name cache.
  • Flush the page cache
  • Dereference each vnode within the mount point
  • Free each dirent
  • Unlock the mount point, and return
flint idol
#

theoretically, I should be able to unmount now...

flint idol
#

I can't because my foreach_dirent doesn't work

#

mainly because skill issue

#

(idk how to recursively iterate over the tree without using recursion)

inland radish
#

simply fetch the "successor" node each time

#

assuming a binary tree

flint idol
#

it's not a binary tree

inland radish
#

which kind of tree is it

flint idol
#

the kind where a node can have any amount of children

inland radish
#

so why are you storing dirents

#

is it some kind of cache?

flint idol
#

no it's for directory/file lookup

inland radish
#

oh path lookup

flint idol
#

yeah

inland radish
#

so whats path lookup have to do with unmounting

flint idol
#

and I gotta free it on unmount

inland radish
#

you can store all the dirent nodes on that mountpoint in a list as well as the path tree

flint idol
#

that's what I'm thinking to do instead

inland radish
#

you can do both

#

have a path tree to assist lookup, AND store them in a list to assist their deallocation on unmount

flint idol
#

ye

flint idol
#

I have finished implementing unmounting

#

now, all I have to do is make all this thread-safe

white mulch
# flint idol the kind where a node can have any amount of children
vector<Node*> stack {root};
while (!stack.is_empty()) {
    Node* node = stack.pop();

    // do whatever with node

    for (Node* child : node->children) {
        stack.push(child);
    }
}
``` assuming you are fine with top down depth first (as in if you eg. have a subtree with children then the childs are visited before the subtree's siblings
flint idol
#

accidentally got off track and yapped to someone about multithreading (oops)

#

and synchronization things

#

but now I'll make this thread-safe

flint idol
#

VFS unmounting is done

#

this should all be kind of thread safe

#

almostโ„ข time for userspace

flint idol
#

for future reference:
if you want to iterate over a directory, you call VfsH_DirentLookup to get the directory's dirent, then you set your curr node to the first child of it (dir->d_children.head), then you iterate over that like it were it a linked list (curr = curr->next)

#

that's just so I can look back though if I forget how

flint idol
#

so I think I can merge the VFS

#

now

flint idol
#

Finally, no merge conflicts

#

oh god

#

in 4 days too

#

I think I just saw some green stuff on the floor outside- what is that?

#

along with 3225 LoC added total

flint idol
#

I can almost start porting stuff

#

I just need to implement a couple things

#

Anyone knows what this is

#

?

elder hornet
#

whats that

#

must be a glitchy framebuffer test

flint idol
#

I went outside of the door of my house

#

And found it

#

I think I might be lagging

#

@real pecan

#

Do you know what that is

real pecan
#

id need a spec to tell u

elder hornet
flint idol
#

In fact the air out here smells different

#

Idk whether I like it or not

elder hornet
#

must be poisonous. Outside is dangerous

flint idol
#

There's even some great big light

#

That's hurting my eyes

elder hornet
#

Yeah just sounds like a glitchy framebuffer to me

#

Do you know who manufactured it? Maybe you can get a refund

flint idol
#

ok I'm back in

flint idol
#

then I just implement a bunch of syscalls

#

then I port mlibc

#

then I suffer

flint idol
#

instead of the initrd

#

and then eventually I'll write documentation

#

eventually

eventually

eventually

eventually
-# eventually

flint idol
#

I can now throw drivers into the InitRD and have them detected and laoded

#

*loaded

vernal chasm
#

O_O

#

Idk what I'm responding to, but I felt obligated to respond with that kekw

flint idol
#

basically the kernel can detect devices

vernal chasm
flint idol
#

then find the right driver for it

#

if one exists

vernal chasm
#

So you added that back in now

flint idol
#

Yes

vernal chasm
#

Now make it possible to get validated drivers from the internet

flint idol
#

I also need to load drivers as passed on the kernel cmd line

#

--load-modules=driver,driver,...

#

this should even work on real hw

#

Forgot uacpi broke stiff

#

*stuff

#

But I'll need to fix that for pnp to work

#

Just noticed there is a security vulnerability in OBOS

#

Basically if you free a vnode which includes the page cache

#

You get a use after free in any virtual memory region that's allocated it

#

But specifically in the physical pages backing the pages, which is undetectable by the kernel

#

Which means someone (no one) could exploit this to get access to arbitrary data

#

Wouldn't say the severity is too high though, since the data is undefined

#

unless...

#

it uses VfsH_PageCacheResize with the size parameter set to zero to free the page cache

#

and this function does seem to remap the mapped regions

flint idol
#

although there is still a use-after-free

#

but it's not related to the mapped regions themselves

#

instead it's related to the struct pages

flint idol
#

@real pecan I think your thing might actually be broken this time. I decided to run the uACPI test runner on my computers DSDT and SSDTs to see if uACPI was failing, and to my surprise, something did seem to be broken

#

all goes well up to the point where my kernel crashes

#

uACPI's test runner's last message is:

[uACPI][ERROR] failed to disable fixed event 0```
#

before exiting

#

I'll set uACPI to trace

#

and see what happens

#

I can also try to run OBOS on other hardware

#

to see if it still crashes there

#

I can also hopefully run managarm to see if it crashes aswell

#

currently trying to build managarm

flint idol
#
xbstrap: Action regenerate of source mlibc failed```
#

the managarm isn't managarming

#

I think I got that working

#

this is so slow

#

dam

#

there was a nightly build this entire time

flint idol
#

Can't be bothered with that any more

#

Only thing I can think of tbh is a vmm bug assuming it's my problem

#

I'll try on another piece of hw

flint idol
#

Then crashes while crashing

#

Then just goes on recursively

flint idol
#

Forgot that hyper had broken on the other test subject

flint idol
#

On the other test subject it fails

#

Likely do to make kernel api

#

As it said invalid srgument

#

*argument

real pecan
#

It cant perform io properly in userspace, so whatever it wrote it didn't read back

#

Thats a to-do for later

#

But thats not a bug nor a problem

#

Just extra log spam

#

Btw the runner is compiled with asan and ubsan, and also aborts on leaks, so if it did do something wrong you would see it

flint idol
#

Ah ok

#

Also when hyper old uefi bug fix

real pecan
#

Soon โ„ข๏ธ

elder hornet
#

Idk why but I find your VFS monologues very interesting

flint idol
#

Good to know

flint idol
#

I was able to get uacpi to initialize on real hw when I disabled the gsi

real pecan
#

Which gsi

flint idol
#

#9 for GPEs

real pecan
#

Skill issue

flint idol
#

I am aware

real pecan
#

Lol

flint idol
#

why it causes the kernel to crash, I don't know

#

and why it causes the kernel to be in an infinite panic loop, I don't know

flint idol
#

There is 7676 kib of pageable pages on this hw

#

And 9328 kib paged out

#

Time to start an AHCI driver

lean glen
#

if u wanna port stuff right now you could too

flint idol
#

I know

lean glen
#

use a tmpfs

#

it'd be easier to test on real hardware too

flint idol
#

I want to write this first

#

Before ports

#

I also need a syscall interface before ports

flint idol
#

finally found a use for semaphores in the kernel

#

I can use them in the AHCI driver for ports

#

since you have a max of 32 command slots

vale nymph
#

Thats one of the places where I use them in astral too, but for nvme queues and stuff

flint idol
#

the text renderer thing has decided to break on me

#

in flush_buffers

flint idol
#

bro wtf

#

when I comment out everything in get_bar_size in the pci file

#

all goes back to normal

flint idol
#

I got very little code in the AHCI driver, but it's a start

#

just detects the AHCI controller after being loaded by pnp

#

then maps the HBA

#

I'll do more in a bit

flint idol
#

Anyway, I need to initialize the hba

#

Then detect and initialize the ports

#

Then send identify ata to them

#

Then I need to implement reading and writing from the ports

flint idol
#

Then that's probably all I'm going to do for the ahci driver for a long time

#

As there isn't much I want to do with it

flint idol
#

For said port

vernal chasm
#

When do we get obos graphics?

flint idol
#

Eventually

flint idol
#

some funny stuff is happening

#

basically something is remapping already-mapped pages

#

while it doesn't seem to be a problem directly

#

it's causing the kernel to hang waiting for a tlb shootdown to finish

#

one cpu has decided it's special

#

and doesn't want to handle the tlb shootdown irq

#

because eflags.if=false

#

because it's in a pf handler

#

which is a bug

#

which is easily solved by sti

vernal chasm
#

Good job

flint idol
#

something could've just been examining the state wrong

vernal chasm
flint idol
#

-# since it doesn't crash, it's fine

flint idol
#

I've had:

 223 files changed, 25330 insertions(+), 866 deletions(-)```
this summer
#

along with 20k LoC added

#

along with 6 PRs merged

flint idol
#

What is so special about slot 31, function 2

#

On bus zero

ornate ginkgo
#

so you get lpc, ahci, smbus and some other peripherals there

ornate ginkgo
thick jolt
#

ping?

flint idol
#

Pong

flint idol
flint idol
#

I have just added a way to allocate physical memory but using strictly 32-bit addresses

#

will be useful for supporting old ahci controllers that don't have 32-bit addressing

#

and other devices

inland radish
flint idol
#

yeah that's what I meant, mb

#

I can initialize the HBA, as well as the ports

#

I just need to send identify ata to get drive info

flint idol
#

the coolest thing about this rewrite is that it will turn on the drive led

#

in the ahci driver

flint idol
#

ikr

flint idol
inland radish
flint idol
#

I could've sworn it was...

#

I was looking into the ahci spec to see what I needed to do

#

but found nothing

inland radish
#

That's so...

#

...stupid!

flint idol
#

I'm testing to see if commands are sent

#

... and Core_SemaphoreAcquire is a hidden symbol

#
Kernel Panic on CPU 2 in thread 4, owned by process 1. Reason: OBOS_PANIC_FATAL_ERROR. Information on the crash is below:
Unhandled NMI!```
#

while sending an IPI

haughty abyss
#

exciting

flint idol
#

the IPI was sent in the function that's supposed to halt all cpus

#

which is called on panic

#

oh wait nvm

#

it was while the idle task was idling on cpu 3

#

I'm sure it's fine...

#

and I got another one...

#

@real pecan Am I getting the GSI for a PCI pin from uACPI properly here:

uacpi_namespace_node* pciBus = nullptr;
    uacpi_find_devices("PNP0A03", pci_bus_match, &pciBus);
    uacpi_pci_routing_table *pci_routing_table = nullptr;
    uacpi_get_pci_routing_table(pciBus, &pci_routing_table);
    for (size_t i = 0; i < pci_routing_table->num_entries; i++)
    {
        if (pci_routing_table->entries[i].pin != HbaIrqNumber)
            continue;
        if (pci_routing_table->entries[i].source == 0)
            HbaIrqNumber = pci_routing_table->entries[i].index;
        else
        {
            uacpi_resources* resources = nullptr;
            uacpi_get_current_resources(pci_routing_table->entries[i].source, &resources);
            HbaIrqNumber = 
                (resources->entries[pci_routing_table->entries[i].index].type == UACPI_RESOURCE_TYPE_IRQ) ?
                resources->entries[pci_routing_table->entries[i].index].irq.irqs[0] :
                resources->entries[pci_routing_table->entries[i].index].extended_irq.irqs[0];
            uacpi_free_resources(resources);
        }
        break;
    }
    uacpi_free_pci_routing_table(pci_routing_table);```
#

HbaIrqNumber is a pin number based from zero

#

0=A
1=B
2=C
3=D

#

to the managarm source I go nooo

real pecan
#

Yeah check managarm or ask @short mortar im on my phone rn

flint idol
#

ok

short mortar
#

I'm also on phone lol

flint idol
#

from that I get GSI 20

#

but the only IRQs on the IOAPIC I see are 10 and 16

#

managarm does the same thing

#

except it indexes zero in the routing table

#

*resource table

#

to the ACPI spec I go

#

which I assume is the index field of the pci route table

short mortar
#

There is a good freebsd page that explains it

#

Idk if you might have seen it already

flint idol
#

I'll search for it

#

?

short mortar
#

Yes (I think)

vernal chasm
short mortar
flint idol
#

I know

#

when I fetch it, I subtract one to make it the same as what ACPI expects

#

I fixed it

#

I was supposed to use the irq_line field as well as the pin

#

to get the IRQ

#

YEAH

#

I can identify sector count

#

and sector size

#

and get IRQs

#

from the AHCI controller

white mulch
flint idol
#

idk

#

hold up...

#

I resolved the wrong IRQ

real pecan
#

It depends on how the table is specified in hardware

#

Its 50/50 from what I've seen

#

Some hw links to a link device, some provides a gsi directly

flint idol
#
uacpi_namespace_node* pciBus = nullptr;
    uacpi_find_devices("PNP0A03", pci_bus_match, &pciBus);
    uacpi_pci_routing_table *pci_routing_table = nullptr;
    uacpi_get_pci_routing_table(pciBus, &pci_routing_table);
    for (size_t i = 0, pi = 0; i < pci_routing_table->num_entries; i++, pi++)
    {
        if (pci_routing_table->entries[i].pin != HbaIrqNumber)
            continue;
        if (pi != PCINode.irq.int_line)
            continue;
        if (pci_routing_table->entries[i].source == 0)
            HbaIrqNumber = pci_routing_table->entries[i].index;
        else
        {
            uacpi_resources* resources = nullptr;
            uacpi_get_current_resources(pci_routing_table->entries[i].source, &resources);
            HbaIrqNumber = 
                (resources->entries[0].type == UACPI_RESOURCE_TYPE_IRQ) ?
                resources->entries[0].irq.irqs[0] :
                resources->entries[0].extended_irq.irqs[0];
            uacpi_free_resources(resources);
        }
        break;
    }
    uacpi_free_pci_routing_table(pci_routing_table);```
#

I need to look at this code

#

until I see what's causing me to get the wrong GSI

#

I know the GSI should be 16

#

but instead it's 20

#

and the time it "worked" it was zero

#

which was the hpet IRQ

#

I think I know

#

why are there so many entries

#

and how do I know which to choose

#

Each object contains four members that describe the routing for a single PCI interrupt. The first member is an ACPI PCI address using the same format as _ADR. Thus, the upper four bytes contain the slot, and the lower four bytes contain the function. Since the PCI function is not part of a PCI interrupt's address, it is always specified as a wildcard value of 0xFFFF and should be ignored by the operating system. The second member is a single byte indicating the intpin. A value of 0 specifies INTA#, 1 specifies INTB#, etc.

#

from some free bsd thing

white mulch
#

you should remove all the ifs (except the last one that checks for the source + the else for that) and add an if to check the routing's pci device + routing's irq pin to make sure that they match what you are trying to find and if they match then continue with the other ifs (and if there is a source then you want to actually loop through the resources instead of blindly assuming that the first one is even an irq)

flint idol
#

I'm pretty sure I don't loop, I just use the index in _PRT

#

for the last part

flint idol
white mulch
#

oh yeah, ig I have been doing it wrong KEKW

flint idol
#

my irq interface is selling

#

(translation: my irq interface broke)

#

it's causing double + triple faults

#

stack overflow it seems

#

SP=0000:fffffeffffffffd8

flint idol
#

fixed that

flint idol
#

AHCI is terrible

#

*The AHCI driver

#

it occasionally hangs

#

during init

#

how fun right

flint idol
#

and as soon as I turn on ahci tracing it goes away

#

ok there it is

flint idol
#

I occasionally get a (false positive?) KASAN violation

#

while accessing a command header

haughty abyss
#

false positive KASAN violation

#

seems legit man

flint idol
#

it's my implementation of kasan

#

so it sucks

#

it was probably non-zeroed memory from the allocator

#

*physical memory

#

that was reallocated after it was freed

flint idol
#

it's because sometimes an IRQ gets sent, but port->ci is still set

#

so something in the init waiting on a command to finish never gets signalled

#

because there aren't any IRQs after

#

I think the bug was because I never initialized the event

#

for the command

#

that's set when the command is done

chilly osprey
#

.!t managarm

umbral iceBOT
#

It's Managarm, not Managram

flint idol
#

it works

#

I need to implement reading and writing

#

and that's really all I have to implement for the AHCI driver (in the sense of what the kernel needs)

#

The kernel has decided it wants to triple fault during smp init on real hw

vernal chasm
flint idol
#

It seems to be because of the new pmm changes

#

confirmed after setting qemu's memory to 4G + 1 mib

#

but it only ever triple faults in smp init...

#

could be because cr3 is allocated in a 64-bit memory region

#

changing it to use the 32-bit version of the pmm allocate function causes it to not triple fault

flint idol
#

There is a bug with the ahci driver on real hw

#

I don't get an irq

#

It's probably more like I get an irq but resolved the wrong gsi

#

lemme see what lspci throws at me

#

it says IRQ 29

#

but idk if that's a gsi or just some random number chosen by linux

#

I will now become an ASL interpreter

#

to maybe find where I messed up

#

into the dsdt I go!

#

I think I got 0x11

#
Package (0x04)
{
    0x0001FFFF, 
    One, 
    Zero, 
    0x11
}, ```
#

address.slot=0x1f
address.function=ANY
pin=PINB#
source=0
index=0x11 (GSI to connect the thingy to)

#

I match this:

Package (0x04)
{
    0x001FFFFF, 
    Zero, 
    LNKF, 
    Zero
}, ```
#

LNKF is confusing

short mortar
flint idol
#

you see I would, except every cpu is on the idle task

#

so I can't

#

without some funny buisness

short mortar
#

Can you not busy wait?

flint idol
#

I can

#

but that's boring

flint idol
#

I currently use WaitOnObject to wait on an event that's set on command completion

#

which blocks the current thread

#

I'll add logs to the ahci irq handler

flint idol
short mortar
short mortar
flint idol
#

hmmm

#

I got 19 on my intel computer

#

probably something to do with the chipset

#

let me guess, the ahci controller was on slot 31, function two on the intel machine

short mortar
#

Idk

#

I can check...

flint idol
#

nah it doesn't matter

short mortar
#

Do you have other IOAPIC interrupts?

#

Working

flint idol
#

indeed

real pecan
flint idol
#

I did

real pecan
#

What do they say

flint idol
#

Let me check

#

On my phone rn

#
Method (_CRS, 0, Serialized)  // _CRS: Current Resource Settings
{
    Name (RTLF, ResourceTemplate ()
    {
        IRQ (Level, ActiveLow, Shared, )
            {}
    })
    CreateWordField (RTLF, One, IRQ0)
    IRQ0 = Zero
    IRQ0 = (One << (PFRC & 0x0F))
    Return (RTLF) /* \_SB_.LNKF._CRS.RTLF */
}
real pecan
#

Ah ok

flint idol
#

the numbers, what do they mean

real pecan
#

I guess it just reads the gsi from some hw reg

flint idol
#

SRS seems to write to said register

real pecan
#

Makes sense

flint idol
#
Method (_SRS, 1, Serialized)  // _SRS: Set Resource Settings
{
    CreateWordField (Arg0, One, IRQ0)
    FindSetRightBit (IRQ0, Local0)
    Local0--
    PFRC = Local0
}```
#

_STA reads from it to get the link status

real pecan
#

I see

flint idol
#

after temporarily switching to a busy loop

#

I've heard vbox's ahci emulation is better than qemu's

#

and behold; it hangs

#

last vbox log before it hangs is:

AHCI#0: Reset the HBA```
flint idol
#

from what I've seen so far

#

I seem to have resolved the wrong IRQ number / have a bug with the IOAPIC implementation

#

I get HPET irqs

#

so it's not the IOAPIC

flint idol
#

after some more debugging

#

it's a problem with my hba init

inland radish
#

@flint idol does the AHCI controller not support MSI / MSI-X

short mortar
inland radish
#

So then he should consider using that

short mortar
#

But where's fun in that

flint idol
#

exactly

#

(I don't want to implement MSI(-X))

short mortar
#

Also MSI doesn't work everywhere

flint idol
#

uhhh yeah that too

inland radish
flint idol
#

Fine. I'll take a look after I fix this bug with AHCI

inland radish
#

msi is basically

flint idol
#
Kernel Panic on CPU 1 in thread 6, owned by process 1. Reason: OBOS_PANIC_DRIVER_FAILURE. Information on the crash is below:
Port physical layer not online after reset.
inland radish
#

device writes 32-bit payload to address

vale nymph
#

I only support msi(x), I very much prefer that since it doesnt depend on having acpi working

#

The device doesnt support it? Your computer is bad, sorry

inland radish
#

i also prefer it because it allows a very nice integration with interrupt layering

inland radish
flint idol
#

oh well that's nice I guess

inland radish
#

that address is usually an address in LAPIC MMIO

flint idol
#

is it x86 specific though

vale nymph
#

The data and address are arch specific

#

The concept isnt iirc

inland radish
#

i think its PCI specific

short mortar
inland radish
#

and not necessarily specific to the arch

flint idol
#

ok

#

I know how I'm going to abstract interrupts to drivers further. I would've thought an irq interface which dispatches your IRQs would be good enough, guess not.

inland radish
#

I'm pretty sure it's good enough

flint idol
#

basically I should prefer MSI over the IOAPIC, and MSI-X over MSI

#

and if none of those exist, yell at the driver

#

(return OBOS_STATUS_INVALID_OPERATION)

inland radish
#

i think ahci controllers that operate only via legacy IRQs are golden eggs

flint idol
#

damn this is actually quite simple

#

set message address to LAPIC

vale nymph
flint idol
#

although idk what these are supposed to be

#

nvm

#

I think I set mask to one to mask the IRQ

#

and pending is just to say whether there is an irq pending or not

#

If capable, you can mask individual messages by setting the corresponding bit (1 << n), in the mask register.

If a message is pending, then the corresponding bit in the pending register is set.

#

From the osdev wiki

flint idol
#

I think I got MSI implemented

#

idk if it works yet

#

time to go back to debugging AHCI nooo

#

and this is why you don't write AHCI code at 12 in the morning...

#

so MSI works...

#

... except I'm checking if there was an IRQ wrong

#

I'm checking if bit 3 of the PCI status register is set

#

but it isn't for some reason

#

after the IRQ

#

Interrupt Status - Represents the state of the device's INTx# signal. If set to 1 and bit 10 of the Command register (Interrupt Disable bit) is set to 0 the signal will be asserted; otherwise, the signal will be ignored.

#

which means I need another way

#

I can check the pending bit for MSI(-X)

#

but as for the IOAPIC fallback

#

I'll have a weak function that the arch defines if it can check the IRQ status

#

otherwise it's up to the driver to check the IRQ status

#

if the IRQ checker callback is nullptr

#

guess I have to always use the devices IRQ checker

#

so I can receive MSI IRQs

#

I have no luck receiving an IRQ on vbox

#

maybe if I check what @ infy's OS does

#

because apparently that code works

#

PxIE does permit all irqs

#

same thing with GhcIe

#

maybe the PCI command register's IRQ disable bit is set

#

doesn't seem like it

#

it isn't set

#

at all

#

or maybe I don't know how to read

#

I was accessing command at status' offset

#

and vice versa

#

in the AHCI driver

flint idol
#

I have confirmed it's not my IRQ interface

#

it is my AHCI driver

short mortar
flint idol
#

idk

#

but I now check it using the HBA register

short mortar
#

So like I think you should be checking IS register?

flint idol
#

I do

short mortar
#

(I haven't tried vbox, but my code seems to be working on QEMU and 3 PCs)

flint idol
#

what about IRQs

#

I've heard that vbox has better IRQ emulation than qemu

short mortar
#

With IRQ routing

flint idol
#

so I'm using it instead of testing on real hw each time

short mortar
flint idol
#

I meant

#

AHCI emulation

flint idol
short mortar
#

Have you tried VMware?

flint idol
#

No

flint idol
#

OBOS on real hw hangs during HBA init

#

specifically during port init

#

I think I might git revert my HBA init

#

because I rewrote it at midnight when I was very tired

#

guess I'm rewriting HBA initialization code now

#

Enable interrupts, DMA, and memory space access in the PCI command register
Memory map BAR 5 register as uncacheable.
Perform BIOS/OS handoff (if the bit in the extended capabilities is set)
Reset controller
Register IRQ handler, using interrupt line given in the PCI register. This interrupt line may be shared with other devices, so the usual implications of this apply.
Enable AHCI mode and interrupts in global host control register.
Read capabilities registers. Check 64-bit DMA is supported if you need it.
For all the implemented ports:
Allocate physical memory for its command list, the received FIS, and its command tables. Make sure the command tables are 128 byte aligned.
Memory map these as uncacheable.
Set command list and received FIS address registers (and upper registers, if supported).
Setup command list entries to point to the corresponding command table.
Reset the port.
Start command list processing with the port's command register.
Enable interrupts for the port. The D2H bit will signal completed commands.
Read signature/status of the port to see if it connected to a drive.
Send IDENTIFY ATA command to connected drives. Get their sector size and count.

#

seems simple enough

#

I think I'll follow what the spec says instead...

short mortar
short mortar
flint idol
#

probably is, which is why I'm reading the spec instead

flint idol
#

@infy has saved me twice, once with uACPI, and now twice with the AHCI init

#

when uAHCI

vale nymph
#

uKernel

real pecan
#

Lmao

#

Glad to be of service

short mortar
#

I think there needs to be some sort of agreement for universal driver format for hobby OSes

flint idol
#

mine

#

why?