#uACPI - a portable and easy-to-integrate ACPI implementation
1 messages · Page 6 of 1
this means i can now up the coverage for real hardware blobs by a huge amount
before this i would get a whole bunch of errors while loading the DSDT
now the only error is
it hangs trying to poll for SMM response
Method (SMI, 5, Serialized)
{
Acquire (MSMI, 0xFFFF)
CMD = Arg0
ERR = One
PAR0 = Arg1
PAR1 = Arg2
PAR2 = Arg3
PAR3 = Arg4
APMC = 0xF5
While ((ERR == One))
{
Sleep (One)
APMC = 0xF5
}
Local0 = PAR0 /* \PAR0 */
Release (MSMI)
Return (Local0)
}
like bro
who tf designed this
The only solution is to obviously port modern chipset to qemu and run the real bios in qemu
Obviously 
Don't take the coward's way out 
it's no surprise that tables from physical machines don't work in qemu
yeah except this is userland
or userland, yeah
it'd say that's not even cursed, it's what ACPI is supposed to do, right?
abstract from the chipset
yeah
except some tables don't have any time outs or anything like that and just wait forever
not all of them but a lot
so it makes it hard to test properly
and obviously there's no hardware to reply to their mmio write 
you might be able to actually map some ranges using sysfs
so that you could perform mmio
(linux-only ig tho)
maybe QEMU needs a ACPI Hardware Emulator
Indeed
Let's see what the corporation ©️ has to say
i dont think anyone would like the uacpi test runner to poke around random mmio because of random aml blobs lmaoo
Yeah that's not something for random blobs, more like for debugging your own shit
i can assure you linux would not be happy with userspace doing its own acpi
Minor details
just enable acpi mode again, whats the worst that could happen :^)
Looks like a proper AML test suite might be happening: https://github.com/acpica/acpica/discussions/956
or at least the ACPICA maintainer from intel is interested
@fiery turtle Looking to add uACPI to Boron in the future. I noticed this API: https://github.com/UltraOS/uACPI/blob/master/include/uacpi/kernel_api.h#L85 void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len); and void uacpi_kernel_unmap(void *addr, uacpi_size len);
Boron's memory manager is a bit different in terms of these. Of course, it allows someone to reserve a range of memory in the kernel heap (called pool), however, the APIs for them look like this:
MIPOOL_SPACE_HANDLE MiReservePoolSpaceTagged(size_t SizeInPages, void** OutputAddress, int Tag, uintptr_t UserData);
void MiFreePoolSpace(MIPOOL_SPACE_HANDLE Handle);
I accidentally sent it too early, sorry
It maps a physical address into memory
On AMD64 I could just add the HHDM offset to the physical address and be done but I may have ambitions of porting it to 32-bit x86 later
the kernel pool can have anything mapped into it, and the kernel heap is just a participant
you can map physical memory into a range you allocate with MiReservePoolSpaceTagged
anyway, the only difference is your unmap function doesnt take in a size?
no, the difference is that i have a special cookie identifying the range instead of the address being the cookie
it's done because tacking on a 4KB header to the memory range would be incredibly wasteful
so I slab allocate the headers separately
nvm these are the private APIs
the public APIs are:
BIG_MEMORY_HANDLE MmAllocatePoolBig(int PoolFlags, size_t PageCount, void** OutputAddress, int Tag);
void MmFreePoolBig(BIG_MEMORY_HANDLE Handle);
void* MmGetAddressFromBigHandle(BIG_MEMORY_HANDLE Handle);
size_t MmGetSizeFromBigHandle(BIG_MEMORY_HANDLE Handle);
oh ok, well linux keeps a hash table of ACPICA mappings
you can do the same for uacpi
virt addr -> handle
I was thinking of using an AVL tree for that but I don't like the prospect of having a log n access time
hash table sounds good but I don't have a facility for it in Rtl
so I'll want to implement it too
yeah an avl tree might be overkill here
these map/unmap calls don't happen very often, so log n is definitely fine
mapping mostly happens on first opregion access lazily
if it goes out of scope then its unmapped
which usually doesn't happen since they're global "variables"
I was thinking that maybe you'd want to add a cookie value returned by uacpi_kernel_map and passed into uacpi_kernel_unmap, but it's understandable if you don't want to
I could then pass my BIG_MEMORY_HANDLEs into that
but maybe I should just use a hashtable or whatever
that's something to think about I guess
if more use cases for that come up I might do it like i did for sized frees for example
but its very unusual to have a handle in this case
why do you do it and whats the advantage?
I do it because I want a way to track the memory regions dished out by the kernel pool manager and I don't want to tack on a 4KB header to each range nor do I want to give up the page aligned-ness of the ranges dished out
and I don't know of a better way to manage them without creating a circular reference, I guess
why not have a separate data structure tracking these?
thats the thing: the handles are actually just pointers to MIPOOL_ENTRY
which tracks that stuff
they're in a list tracked by the kernel
and im guessing you dont have anything like struct page in your kernel?
that would save you the hassle
I do, it's called the PFN database
but it manages physical memory
actually no struct page does that too
ignore my redundant comment
it could, but I only track usable memory with it, not MMIO
hmm
Doesnt a pfn db track what vas are mapped to a pas?
and you give the kernel the virtual memory pointer that was returned in uacpi_kernel_map
No
it really doesn't
that'd be horribly inefficient
yes, my pfn database is the equivalent of the struct page database
I see, sorry for interrupting: )
how does windows solve this
I don't know
||read the source code
||
anyway yeah probably just going to use a hash table
id love to know how windows tackles this
actually that goes in Ex I think since it'd use memory APIs, Rtl is strictly forbidden from doing so
I think it just does things sanely
but a hash table seems optimal as a solution for now
like being able to use a pointer to a vm range to free it up
instead of requiring a handle to said range
yeah but it still stores the entry somewhere
like theres still some metadata somewhere
probably
ive explained this to u im 99% sure
this is about memory range management
yes
I don't remember
there is a range of kernel space mapped by "system PTEs" which are allocated by a free list threaded thru the PTEs themselves
thats what they allocate virtual space out of
they free via the virtual address
how is it threaded through the PTEs themselves
as in the PTE for 1 page below the first address is completely invalid and only stores the link to the next range or what?
the same way youd do it with a free list heap
but the way I'd do it with a free list heap is to shove in a small block above it to store details about the memory such as range and the entry within the list of entries, or whatever
so that's not going to work
how does that not work
PTEs are just memory dude
they dont have to have meaningful values just make sure bit 0 is off
.
ok so I was partly right here
by completely invalid I meant, its present bit is zero
not actually completely invalid sorry
yknow this way also has the benefit of introducing a "guard page" type thing
the PTE whose present bit is zero and it has the pointer to the mipool_entry won't be treated as valid by the MMU so itll basically be like that
how didn't I think of this...
thats actually pretty smart
So right now I have this log message
Im thinking of maybe adding something like (N ops in N ms)
overkill or not?
e.g. successfully loaded & executed 25 AML blobs (129312 ops in 3ms)
or maybe even (1233 ops/s)
I guess those could be fun statistics to know
I can't wait to port uACPI to OBOS rewrite 5
and watch as all goes to shit it successfully initializes
lol
right now I have to deal with the VMM 
good luck
lmao same
ty
gl
Last time I got stuck on ACPI before even trying VMM, thinking I needed to do it first :P
Since then I've learned to reinvent the wheel less, so I'm going to use uACPI
But that's gonna take a bit
gl
Also since ACPICA fixed their broken ToHexString I had to come up with a different NT incompatibility example in the readme 
also have you added or changed any of the tests recently? just curious because then I need to rerun them on qacpi lol
What's so different about it? Is this another case of Windows messing up and then "It's not a bug, it's a feature!" happening?
well i added a few tests for table overrides and stuff
but nothing huge
just cuz its windows doesnt mean its wrong
its a case of ACPI being underspecced and everyone doing their own thing
and since windows dominates the market it gets to decide whats right
Ah yes
Never forget, firmware devs are dumbdumbs
Just like the wacky protocol android phones use for USB filesystem access
like i'd say 50% of AML behavior is not covered by the specification at all
some simple things like ToHexString let alone complex stuff like reference semantics, or object lifetimes
ah, then I already know that's one more failing test 
lol
to be fair it was mostly for testing uacpi API for table overrides lol
this one probably won't work for you as well https://github.com/UltraOS/uACPI/commit/de1b72716cc1787e62b63ebed646a1145f28e69e#diff-15da19193cd501a2c4c5fa5d307af16eb52b414c00e4b4089290ce8309da3327
Signed-off-by: Daniil Tatianin [email protected]
yeah I don't have all of those reported (and the test runner part would fail too)
funny enough acpica reports "AnotherTestString" as supported
because acpiexec enables it to test the api
thankfully its easy to add new ones tho so I should do that, its just one aml if and return (though one caveat about doing it this way is that you can't dynamically change them)
btw qwinci if you have any input here you're welcome to post it: #1217009725711847465 message
perhaps you would be interested as well
yeah tahts the problem
i have made a proper subsystem for that now
because i looked at how other systems use it
and its often enabled and disabled at runtime
e.g. from the kernel command line
or procfs
its relatively small and should cover all use cases i think
nice, yeah a shared test suite would be nice
yeah like js has a giant test-262 suite that covers literally everything
aml should have one as well
something like that would be nice yeah, I just didn't want to introduce the concept of native methods at that time so I took the easy route of assembling asl and then embedding the code for the osi method in the interpreter (and adding it as an aml method with that code)
you can technically dynamically create new aml code as well
like IfOp EqualOp Arg0op StringOp
just "recompile" the method whenever a new interface is enabled or disabled
ig that would work and its only a few ops so it wouldn't be that hard
yeah u can even hardcode them as a byte sequence
well not hardcode because if has a package which has a length which depends on StringOp length
but idk if this is easier than just making a native method lol
yeah for a native method it would basically just be a function pointer inside the method object and nothing more
- a check for that function inside the place where a method call is dispatched
anyway this isn't really related to uacpi :p
my native methods are kind of bad
like i didnt bother implementing it to an extent where it can be exposed to user
and i dont think its a feature that anyone needs anyway
my native methods just get passed the entire execution context and the return value object
I guess you could do some fun stuff like load an almost empty dsdt with whatever scopes you need and then add methods to the scopes implemented as c functions
yeah
Isn't that the way interpreted languages do native methods?
but yeah I don't really see a practical use for that other than doing it for the fun of it
yeah but the execution context is a private struct local to interpreter.c 
lol
so its an opaque handle in this case
i need to deal with interpreter.c in the future anyway since its a giant 5.5k loc file
yeah its kind of annoying to navigate as well
I can imagine
pretty much why i havent bothered
When files hit 1000 lines you have to consider whether it should be multiple
probably
(Not that I perfectly adhere to this rule...)
I also need to find the motivation to break my huge 111 case switch to free functions or methods to fix the stack usage on debug builds. I didn't really realise how bad switches were in terms of unoptimized stack usage when I started writing this
yeah i didnt know this could be a problem as well
luckily i dont have a huge switch but rather a triply indirect array of function pointers instead 
I didn't know that
What causes it?
well it basically just doesn't merge the space that the variables you have inside the cases take, eg. c switch (whatever) { case 0: { Some4KbStruct a {}; break; } case 1: { Some4KbStruct b {}; break; } } this would take in total 8kb from the stack on debug builds because it reserves space for all the variables inside all cases not considering the fact that not all of the cases are ever entered at once (so it could use the same stack storage for a and b)
Oh it's that
really strange considering there's a break
I avoid declaring any variables in switch cases
like its impossible for both to be accessible
have you tried wrapping the case like case 0: { }
oh nvm u literally do
even more strange
This is probably due in part to the fact that switch cases are labels in the compilers' eyes.
So it's like jumping to some label in arbitrary code, which kind of implies all of the stack is there.
And this would be an optimizer didn't see it type situation.
actually it looks like gcc does merge them even without opts but clang doesn't
u could probably even say its a bug
because its clearly unreachable from other branches
yeah might be but considering its kind of an optimization to use the same storage for them I am not sure. I might ask in the llvm discord
wouldnt u expect that storage to be allocated upon entry in the said branch tho
especially in debug
you have to
somehow only appleclang complains when i forget to wrap my switches
I don't wrap my switches (yes, boo.) but I also never declare any variables within the confines of a switch case.
i meant if there are variable declarations inside
if not, doesnt matter
yeah thats what i meant as well
oh
im pretty sure the standard says they have to have their own scope to declare variables but maybe im wrong
that may be what im thinking of actually yes
sometimes i forget C has different rules for this stuff
yeah c doesnt have "constructed" object state i think so
Yeah technically all a C variable is is a location, into which you can place a value. If you don't place a value, it's undefined (and compilers may yell at you).
@mortal yoke speaking of tests, I also recently added infinite recursion and infinite while loop tests, Im guessing you probably dont handle that as well?
Now that the test runner can load any extra number of SSDT I made it so the "large" tests that run 500 hw blobs against the runner also dump SSDTs and feed them along with DSDTs
That should really bump the coverage by a lot
because now all the undefined references should be gone
And apparently that already found a new leak
no other bugs unfortunately but still nice
this is how tests are generated
and each directory contains the tables
Method (_INI, 0, Serialized) // _INI: Initialize
{
ADBG ("Hello World!")
If ((GGGG () == One))
{
GGGC = (GGGC | 0x00040000)
}
GGGC = (GGGC | 0x10)
GGGC = (GGGC | 0x20)
GGGC = (GGGC | 0x40)
PFM0 ()
GGGC = (GGGC | 0x80)
GGGC = (GGGC | 0x00010000)
PDS2 ()
GGGC = (GGGC | 0x0800)
EVT0 ()
GGGC = (GGGC | 0x2000)
EZV5 ()
DIM0 ()
GGGC = (GGGC | 0x00080000)
PTC0 ()
}
what the fuck
me when i ship debug "hello world" logs in release build
also something is telling me the way i generate checksum is wrong lol
oh nice i can reproduce it from just feeding ssdt8 alone
what the fuck
Name (MARK lmao
aight found the memory leak, pretty nice
anyway that aml was so brain damanged i think i lost a few braincells
this was the code causing the memory leak
should I email this mark guy and tell him that this is insane
imagine creating operation region and fields in local scope
yay
noice
ngl this shit is really annoying
To: [email protected]
Cc: [email protected]
Subject: What are you doing in my firmware?
Lol
Body: your code sucks man
perhaps we should do another round of fuzzing
Lol
Now that it can load ssdts from the command line perhaps the coverage could go up
that's nice
if you have a problem you know which hw engineer you need to complain to
Its funny that he made it an actual global object that has to consume memory at runtime
oh wow yeah another 8 bytes
so bad
how will we recover from this crazy loss of less than one page table
Yeah on uACPI it would take up about 40 ish bytes
He could make it a method and store the string there tbh, then it wouldn't even consume anything
Like storing pointers in a smaller integer?
so lol
uh yeah?
you store uint32(pointer - offset)
v8 does this btw
Offset to what tho
the place where all the pointers go
Like a separate table?
I feel like to implement this properly you would need the client to coordinate with you
what v8 does is they just allocate only within a 4 gigabyte window
By itself almost nothing, it all depends on the namespace being loaded
well yeah ofc lol
i mean
with the average aml
you have lots of aml, just calculate heap usage per byte of aml
Average aml is probably a thousand objects, maybe 64-128 kib
thats quite a bit, huh
Yeah it's pretty huge
implement pointer compression then :^)
Dtbs are also probably in the neighborhood tho are they not?
Yeah a lot of that data is pointers
in memory they are expanded, i guess
yeah that's why im suggesting you implement it
But for pointer compression I would need the host kernel to promise me some invariant about the heap location
true
I could make it optional ofc
or play weird games around allocations
like, allocate memory in chunks of X
and then have your own malloc over those chunks
Ye probably
All of aml memory is pageable tbh so
Ye
but this invariant isn't very hard to do
You can probably page most of it and it will never be touched again
for most things
Like that Marc object
Yeah
Like the kernel knows the heap limits
Especially if its a per object heap
tbh not necessarily
my object heap is bounded to [base of direct map + phys base of first usable entry, base of direct map + phys end of last usable entry)
(and it's per-object slabs already)
Thats quite large right
yeah quite possibly
and its not compile time known either
i guess it depends on how you architect your kernel heap though
Yeah
if you have a paged kernel heap then you can probably guarantee that much more easily
or possibly just give uacpi a private heap
Yeah thats what I would do
Just allocate a paged heap
There are some objects you need more often, like stuff used for polling fan/power usage/battery
But other stuff is just dead forever
implement an AML JIT to compile only the parts of AML that you want to optimized code :^)
i mean technically they could use dynamic aml loading or whatever but like, what if no
Lol
i mean, is it that bad?
Probably doable
honestly that might be not even too crazy
like if the aml isn't recursive, then you can probably just inline everything aggressively
and do a linear scan regalloc or something over that
Problem is aml byte code is super high level
You would have to compile it into a lower lever bytecode
i mean you always do lowering when doing jit
Then yeah
bcm2711-rpi-4-b.dtb is 50.3K :^)
at least this 2 year old copy i have is
yeah no I don't
Can I access the acpi tables without heap allocations ?
yes
Perfect
And uacpi doesn't need the heap to make them accessible via its table api?
not directly, no
but they're installed as part of uacpi_initialize, which absolutely does use heap a lot
primarily to allocate mutexes etc
Would be cool if at least the Numa tables are accessible via a basic no heap api
Like acpica does it with a table init (which sadly needs at least some heap and can't use the tables directly from memory)
For example the srat table should be read before you start to init the pmm to prevent any pmm internal allocator from crossing a Numa domain, and for that it's sufficient if i could iterate over the srat or index it like a array to get the needed ranges one by one without any heap allocation
Sure I could do it myself for my kernel but it would be thematically better located in uacpi if possible
I agree yeah
Ill add this to the to-do issue
https://github.com/UltraOS/uACPI/issues/75 added here
I might even prioritize this tbh
its ridiculous to not have table api available before pmm init

Assembling a mid 90s Windows NT workstation computer! Specifically NT 4.0 with two Intel Pentium Pro 200MHz processors, maxed-out RAM, and a Matrox MGA Millennium video card. All in an awesome metal case on wheels. Testing, dusting, building, troubleshooting, games, CAD rendering and more! Most of which I've never done so let's dive in and make ...
this 1995 motherboard says ACPI BIOS in the settings
would be so cool to get a dump from that
Shoot your shot and email the guy
🙏🙏🙏
Alternatively you could always just build a time machine and just go back to 1996 :>
Should I do this
Yes
sound advice
Alright ship me the parts ill build one
my delorean is on the way
that BIOS screen is from 2002 tho
ah wait lol
heh, the release notes are included inside the BIOS flash disk image
It's just slightly too old. It uses the PIIX3 chipset, and ACPI support was added with the PIIX4, which corresponds with a 430TX or 440LX northbridge, Then "PIIX4E updated the ACPI support", which would be 440BX, 440GX, and also 440EX, 440ZX, and 450NX (fucking Intel with shitty naming schemes amiright?)
Hmm, 86box lists a 440LX machine...
no dice
what a bios though lmao
Okay, some of the 440*X machines in 86box do have ACPI support
anti-virus
icon that would scare most users
why would the bios setup screen have an anti virus option lol
honestly this looks more usable than most modern firmware uis
it probably has the ability to write a fresh DOS boot sector to the disk or something
boot sector viruses were a big thing then
thats cool i guess, sort of makes sense
or blocks writes to the MBR
Is that like windows 1.0 or something O_O
a BIOS that knocks it off
well, more like 2/3 but still lol
motherboard manufacturer: how large of a bios rom do you need?
American Megatrends: yes
Yes but at the time that would be a lot...
For a bios rom it would've been xD
no
Iirc my motherboard only has a 256 MB one 
i don't think it needs that much rom?
that looks like text mode with a custom char set to me
Yeah probably
modern motherboards have like 16MB ones?
that sounds much more plausible
Same as how much storage I'm putting on led matrix panels O_O
because who knows how much storage is needed during development
i wouldn't be surprised if dev versions of motherboards had larger flash chips
Time to buy a 4 Gbit rom and swap out the one on my motherboard 
so I started working on this
I see that ACPICA has an ability to disable early table verification, which allows not to map the entire table but only the header initially
so all early mappings are 4k only
What im not sure about is whether this is needed or useful
it breaks the invariant that i currently have which is all tables in the array are mapped and valid
if a kernel can map 4k it can alos map a multiple of 4k i assume??
okay I see linux needs it because it has a limited number of early map slots
jesus ACPICA has so many workarounds for linux lol
init functions that check random things that are client dependant and generate errors based on that
If the headers are always the same size static internal buffers should be sufficient, and mapping the tables into virtual memory might be annoying if fresh pages are needed for the Paging tables but every bootloader that premaps acpi tables (like limine) would negate that annoyance
yeah i get that but in order to get the headers u need to first map the table
and ACPICA optionally always maps only the header which is fixed size
(during early init)
which is a workaround for when u dont have enough slots to map things during early init
which makes sense but idk if this complexity is needed
e.g. for stuff like limine it would just do hhdm + phys addr
Imho I would take the simple route but add the possibility (like table_init(tables_not_mapped: bool) that later allows to support the complex path in a sane way (like using internal buffers for all of the static stuff and reading any acpi tables with a x pages big moveable virt to phys window )
Or add it as a compiler flag
The question is what percentage of currently used bootloader for 64bit systems don't provide any kind of identity map.
In the long run uacpi should support both paths but for now implement the simple route and think about sane ways for the complex path
limine doesn't do identity mapping
as of protocol base revision 2 that is
or 1, i dont remember
It does with a Offset -> Hhdm
Maybe give it the Offset directly
Or let it make the usual map requests and you can simply return the virt address
It boils down to two paths:
Acpi tables premapped by the bootloader
Acpi tables not mapped
Only the second path needs discussions
As a side note making uacpi identity-offset-mapped + detail level (only tables, tables + acpi NV memory, ....) aware could be a nice optional feature
Yes but without any memory allocation, for a hhdm it's a simple addition
Thoughts on:
- Pass RSDP pointer to uACPI directly
- Have uACPI request the RSDP pointer via some API
The latter is what ACPICA does
the idea is that each arch has its own way to store it so you would have to make a per-arch wrapper to get RSDP
but if you're using stuff like limine your RSDP is always in the same place so its not needed
Any ideas whats better
id say the former
thats what i think as well
i dont get the idea of making some mandatory api that will be called exactly once
I was thinking like what do I do if i'm unable to allocate some ciritical data structure during init, like a mutex, or a spinlock, or a predefined namespace object, etc etc
so I just added this
I wanted to prevent being able to spam uacpi_initialize multiple times and cause it to enter an invalid state
now this init level locked_up should prevent that
yeah I agree, it feels wasteful.
can you make init atomic?
i.e. first allocate the entire required reservation
then do any init required
well i can't know beforehand how much memory I'm gonna need, e.g. the number of GPE blocks is only known after the FADT is parsed
so some inititialization must be done before another can proceed
which requires allocations to begin
can you not parse the FADT without allocating memory?
well technically everything is possible
basically
GPE block creation happens after namespace initialization
because u need to match AML handlers
which only exist after u parse the namespace
so its very very late init
its not even FADT tbh
yeah, after uacpi_namespace_load and before uacpi_namespace_initialize u can execute anything
gpe creation happens in namespace_initialize
ah i see
like its probably even dangerous to operate the computer if u enter acpi mode and then event initialization fails
because if firmware wants to run aml to do something relating to power or fans and u dont have SCI set up u wont run it
worst case it will auto shutdown
ye
honestly, how much aml is written to assume oom won't happen
all of it lmao
then don't handle oom
at all
force the os to sleep
or alternatively write your interpreter with coroutines
aml doesnt even have a notion of oom or any failure whatsoever
so you can suspend execution
NT probably puts everything aml related in paged pool
yeah
for that reason
i mean im just guessing
good way to avoid oom since you can allocate like 200gb paged pool
like we're talking non existant cases here to be clear
imo: don't handle oom
allocations happen very early when u dont even have userspace brought up
so u have all the memory in the world
there's literally nothing you can do about it
yeah like
oom during initialization is usually a failure case anyway
i mean
thats when you want to move all the oom cases ideally
is to initialization
of whatever thing
oom at runtime is the scary thing
my policy has been no panic handlers or fatal failures from uacpi, so i just return the failure to the caller from the init function and make sure uacpi state is locked up, and all further calls to any api will return UACPI_STATUS_INIT_LEVEL_MISMATCH
thats why every hobby kernel whose scheduler uses the heap to allocate linked list item structs is scary
like ooms when u run normal aml are fully handled in uacpi, they dont lead to state corruption or anything
its the early init that allocates critical stuff that matters
unless they fuck up aml state ofc
well they abort the call
is aml written to handle such aborts?
nope
(no)
u just stop executing it and say sorry no memory
yeah it would be unpredictable
the "global variable" state
those are the two options
i think this is bad
its probably fine if all AML were written to do all allocations at initialization time
but i doubt thats the case
well that's up to the kernel in uacpi_kernel_alloc, i would just use the flag that makes the allocator spin forever
thats really not a place u want to fail an allocation
yes, its really really bad
imagine using a ridiculously high level language for firmware runtime
you dont really have to write oom-safe code
because you can just wait for the oom to not be oom anymore
and then proceed on allocating
yeah
it's oom safe in the sense that it won't segfault or leak any more memory on oom
i think its a good goal to have for a generic library
worst case it melts :^)
okay uh...
I'm scrapping this
any fatal uACPI initialization error will just cause uACPI to automatically call uacpi_reset_state
which will just free everything and zero global state
so u can just spam uacpi_intialize to your hearts content
the problem is i must first implement uacpi_reset_state...
this helper will also be useful for will who wanted an ability for uACPI to free anything heap allocated prior to shutdown
in order to detect leaks easily
i'll add something like a uacpi_cleanup_state_at_shutdown(true)
(at some point TM)
also just merged@jaunty fox
whats the current problem with pre heap table init if any?
it's not implemented 
thats the problem atm
it's blocked on another issue, which is blocked on another issue
some internal refactoring before i get there
thats not a problem
can i help you with that?
uhh well not sure, maybe once i figure out the exact steps
is this something you're blocked on?
nope just bored
i see
it would be a problem if i were that far with my code that i need numa from the start 
lol
atm the problem is in order to implement early table init I need (or want):
- design API to either enable or disable early full table mapping, which requires:
- "table array only contains verified and usable tables" invariant breakage
- introduction of flags for current table state
- distinguish between "early" mappings and "late" mappings, because this might need different in-kernel mechanisms to map, which requires:
- mapping ref-count
- "table always mapped" invariant breakage
- Additional code for accepting fixed early storage for the table array
basically lots of annoying things that together need a lot of mental gymnastics
thats just off the top of my head
basically everything i need to do in uACPI is composed of these annoying recursive problems
but i will have to do all of it to get to 1.0
initially i got rid of table mappings ref-count because i thought well tables are so small that it doesnt matter i can just keep them mapped at all times
and the ref count system was making everything harder than it needed to be
but now i have to bring it back
as well as any api surrounding it
like uacpi_table_put/unref
like its possible to hack together a shitty early table init system very quickly
but it would be shitty and non extensible
i kinda try to do things the right way with uacpi
Thats why its easier to procrastinate 
i feel the same
best way to procrastinate is by playing mc
I delayed fixing a bug by about 2 weeks by doing that
Thats what I do
how about splitting the table init into a heapless and a heap version which are independent from each other
the number of tables that could be relevant at pre heap is small and that api could work with in place accesses and iterators
I want to steal the ACPICA approach where its optionally supplied with a fixed length buffer at init, then replaced with a heap array when subsystem_initialize is called
"but why"
And it also accepts extra flags like whether its allowed to resize the array etc
Well why have a split approach if u can do it seamlessly as opt in for people who want it
And the pre heap buffer can live in an init section that is freed by the kernel after init
Like __init in Linux
but then you have the problem that you cant know the required size for the buffer
I mean, uacpi doesn't know either right
It depends on the system
Just allocate a reasonable large one
U mean like dont store the metadata anywhere at all?
Just walk the xsdt every time?
either that or store fixed size addresses and tables and fall back to iteration for dynamically sized tables
Can u elaborate, im not entirely sure what u mean
the pre heap system would work with a known table list so each table that has a fixed size by the spec can be read into compile time allocated storage and for all tables that have a unknown size a iterator would be used where the table would be accessed like a array
if you need allocations do it in a way that is known at compile time
Oh I see
I already do this sort of for FADT
Problem is NUMA tables are variable width 
The ACPICA approach is metadata goes into a kernel provided storage buffer, and the tables are mapped on demand
The tables themselves are not copied anywhere
Metadata is just refcount, flags, address, signature , length
the metadata is also known at compile time, number of possible entry types and their layout
Not this sort of metadata
Like internal bookkeeping
Every table listed in xsdt gets an entry in this array
So u can look them up quickly
And then map when requested by the early code
And obviously with limine and stuff this mapping is also almost a no op
So it solves the issue I think
" address, signature , length" those are compile time known sizes if the amount of accessible tables is known at compile time, which it is for the pre heap api
U mean like the struct is known size?
the ability to make uacpi aware of identity offset mapped acpi tables/nvs could be usefull
Well yes
yes
The only reason why the kernel reserves this static buffer and not the library is to wrap it in a special section optionally or not provide it at all
Which frankly all current users of uacpi will end up doing
But I could wrap it in a macro ofc
maybe a compile time flag to disable the preheap api to reduce filesize if thats a concern?
or what exactly is your concern?
Then the api would need to walk the table tree each time or move the metadata on the stack
noo, just let the kernel provide the static buffer for the metadata
which uacpi will be using prior to heap availability
my point is since this is a feature for advanced kernels making it more opt-in is better imo
I will write down a example tomorrow
Just don't use the pre heap api
And write down it's ub to use it after heap table load
i think im also not communicating super clearly, ill give u an example of how this will work later
or not will, rather the idea that i had at least
Anything will be helpfull
which one is it
what is it
*factorio
can agree, procrastinating by playing factorio rn
Greg tech new horizons
forge mod I assume?
The LOD mod?
cheers
will be very useful
i want to implement NUMA-partitioned allocator for keyronex in the future and i'll need to look at acpi's slit and srat as part of allocator init
yeah thats the usecase
how do they know it's beatable?
I mean it has a finite quest book and a surprising amount of people have beaten it
Even multiple times
Yeah
or you mean 400 not 4000
No, 4000
Its a famously hardcore pack
Its a lot of chemistry and automation and stuff
U need millions of resources for end game machines
jesus christ
that sounds not fun
i'll stick to factorio where "long" is defined as something closer to 400 not 4000 hours
Since the progression is so slow every minor milestone feels rewarding af
so basically osdev in mc
if I were to install it, would my computer be killed near-instantly
actually let me reword that
is it resource-intensive
is it graphics intensive
its the normal 1.7.10 minecraft
then I guess I'll install it (later)
the quest book is super good as well
it tells u what to do and gives advice and stuff
u unlock more stuff as u progress
branches
u can automate renewable energy, or use oil, or use steam etc
wait people still play minecraft 1.7?
of course
its basically a different game tbh, since they redid everything
all recipies, mobs, ore spawning, biomes
still
idk much about older mc, but wasn't ore spawning and most biomes mainly the same until 1.18
very polished and coherent basically
in this pack all ores spawn in huge veins
if u found iron, u found thousands of it
etc
oh I thought you meant 1.7.10 was that different from newer mc
nah
anyway I have a stupid bug to find
gl
a page that is pageable, and paged out, is reported as non-pageable and paged-in
in its page node
damn
the protection field says the page is present (it's not)
lol
it also happens on a different address each time it crashes like this
god bless grugtech nh
so I can't capture how the node looks like right after creation
anyway that's enough littering your thread
turned out I should've locked the vmm context the entire duration of the VMA's allocate function
not just the mapping part
silly me
does it work now
I think
nice
time to make the PR to merge the VMM with master
Well…Pyanodon’s…
Ever heard of Space Exploration?
@fiery turtle here is my example, currently only extended for the srat
all required metadata is stored in the table structs on the kernel stack
struct SRAT {
address: u64,
version: u64,
valid: bool,
entry_type_length: [u64; 0],
entry_type_index: [u64; 0]
} //System Resource Affinity Table
struct SLIT {} //System Locality Information Table
struct PPTT {} //Processor Properties Topology Table
struct PMTT {} //Platform Memory Topology Table
fn RSDP_get_SRAT() -> Option<SRAT> {}
fn RSDP_get_SLIT() -> Option<SLIT> {}
fn RSDP_get_PPTT() -> Option<PPTT> {}
fn RSDP_get_PMTT() -> Option<PMTT> {}
//SRAT
fn SRAT_isvalid() -> bool {}
fn SRAT_version() -> u64 {}
fn SRAT_get_number_entries_S_APIC(&SRAT) -> u64 {}
fn SRAT_getEntry_S_APIC(&SRAT, index:u64) -> Option<APICEntry> {}
fn SRAT_getEntry_S_APIC_Iterator(&SRAT) -> Option<APICEntry> {}
fn SRAT_get_number_entries_Memory(&SRAT) -> u64 {}
fn SRAT_getEntry_Memory(&SRAT, index:u64) -> Option<MemoryEntry> {}
fn SRAT_getEntry_Memory_Iterator(&SRAT) -> Option<MemoryEntry> {}
fn SRAT_get_number_entries_x2APIC(&SRAT) -> u64 {}
fn SRAT_getEntry_x2APIC(&SRAT, index:u64) -> Option<x2APICEntry> {}
fn SRAT_getEntry_S_APIC_Iterator(&SRAT) -> Option<x2APICEntry> {}
fn SRAT_get_number_entries_GICC(&SRAT) -> u64 {}
fn SRAT_getEntry_GICC(&SRAT, index:u64) -> Option<GICCEntry> {}
fn SRAT_getEntry_GICC_Iterator(&SRAT) -> Option<GICCEntry> {}
fn SRAT_get_number_entries_GIC_ITS(&SRAT) -> u64 {}
fn SRAT_getEntry_GIC_ITS(&SRAT, index:u64) -> Option<GIC_ITS> {}
fn SRAT_getEntry_GIC_ITS_Iterator(&SRAT) -> Option<GIC_ITS> {}
fn SRAT_get_number_entries_Generic_Initiator(&SRAT) -> u64 {}
fn SRAT_getEntry_Generic_Initiator(&SRAT,index:u64) -> Option<Generic_Initiator> {}
fn SRAT_getEntry_Generic_Initiator_Iterator(&SRAT) -> Option<Generic_Initiator> {}
why is this better than just calling uacpi_kernel_map(SRAT)?
like why do we want to store it somewhere
its already in reclaimable memory
The return value only contains additional Metadata the tables stay in reclaimable memory
You can access the api without a heap and only need the stack and the required memory is known at compile time
but why is this api needed?
The returned struct could also contain static parts of the table if you want to optimize it
why cant u just use the normal uacpi_table_find api
then cast the pointer to the SRAT table
Because it needs heap?
why?
if you mean as it currently stands, then yeah
but it doesnt inherently need heap
Well it needs managing chunks of memory doesn't it?
managing how? it just maps the address pointed to by the XSDP
currently there's an array of table descriptors that uses heap
but a -- its tiny, and b -- i can request the kernel to provide a placeholder buffer before the heap is available
struct uacpi_installed_table {
uacpi_object_name signature;
uacpi_phys_addr phys_addr;
union {
void *ptr;
struct acpi_sdt_hdr *hdr;
};
uacpi_u32 length;
#define UACPI_TABLE_LOADED (1 << 0)
uacpi_u8 flags;
uacpi_u8 origin;
};
this internal struct is used for each table, these structs live in an array
the array currently has a fixed static length of 16, after which it grows into heap
i want to redesign it so that its always heap unless user explicitly provides a static buffer
#ifndef UACPI_STATIC_TABLE_ARRAY_LEN
#define UACPI_STATIC_TABLE_ARRAY_LEN 16
#endif
DYNAMIC_ARRAY_WITH_INLINE_STORAGE(
table_array, struct uacpi_installed_table, UACPI_STATIC_TABLE_ARRAY_LEN
)
this is the current impl
example from linux: https://elixir.bootlin.com/linux/v6.10.2/source/drivers/acpi/tables.c#L36
Elixir Cross Referencer - Explore source code in your browser - Particularly useful for the Linux kernel and other low-level projects in C/C++ (bootloaders, C libraries...)
Elixir Cross Referencer - Explore source code in your browser - Particularly useful for the Linux kernel and other low-level projects in C/C++ (bootloaders, C libraries...)
what i dont like about this is they leak the internal table descriptor into a public header to allow proper storage sizing
but its not necessary, u can just take an annoymous byte buffer
I mean, you really don't need acpi as early as before you initialize a simple heap
So requiring a heap is really not that out of the question
u kinda do if u do NUMA 
because heap init really depends on numa
For a simple early alloc it really doesn't matter
probably, but linux decided it does
If it really wants to access srat that early just parse it yourself
It's not like parsing acpi tables is a complex task
tbh
Like have an early get table that just parses in place
Like, you need to be able to map memory anyways, which requires at the very least a page allocator
Idk how many allocations the table init does but if it fragments up to a single page size it really doesn't matter
And that small array wouldn't go over a page size anyways
linux has an early slot mapping system
but u must unmap everything before it proceeds to later init
For I in memory map
If bigger than needed for alloc
Length -= size
Return ptr
a l l o c a t o r
But tbh if all you need is like srat for early init, have a function to parse the rsdp in place every time and use that
but like
u just imported that giant acpi library
so would be nice if it provided thta functionality
especially since u dont really need the heap for table iteration
You can expose your table iteration as a function that takes a callback
So that it also does map&unmap as required
You could expose that then and have that be the API
yeah
Have it map and unmap internally so nothing would leak and then the early slots would be happy
And if someone wants to save pointers into that data it's their job to figure how to allocate it
acpica just says "u figure it out lol"
if u dont unmap then it logs a warning during late acpica init
if u try to unmap it later the kernel will crash
because it will use late unmap for early mappings
it just has a ref-count per table of how many times it was requested
I always prefer to have the allocator be the one that frees unless it's absolutely required someone else will do it
yeah
Linux has some very annoying functions where on some failure paths you need to free and on other failures or success paths you don't need to free
And it always causes strange leaks
true
I have found multiple net drivers where in a failure path from the hardware they would accidently leak the skb it's just annoying
giant net drivers written by 2 outsourced people are a constant source of rces lol
they have like 20k loc .c files
that no one ever reviewed
So when the temp buffer in uacpi is implement I will just give it one page and call it a day
Basically
I haven't looked at the table api yet, so does it only allocate tables on demand or all found tables as soon as possible?
the entire array is populated at init time
but it doesnt allocate tables, only these structs i sent earlier
One thing that can make the heapless phase easier would be giving a additional parameter to the page mapping function that indicates if it's a table map/nvs map/aml map... so that for example on limine table and nvs would be nop without having to check the memory map
💀
what's the skb?
hmm maybe
tbh this info can be assumed implicitly
tables are only mapped during uacpi_initialize
the rest of the mappings are for AML
It would be nice if you could explicitly state that in the docs so that we don't have to rely on undocumented behavior
Its about to change sooo
I might look into your idea
I need it for userspace emulator
Socket buffer, it's how Linux manages it's packets in kernel (owns the buffer and contains pointers to different layers and such)
it can also be a linked list of packet parts
B-b-b-based
Userspace emulators are very nice but absolutely under-utilized
I mean yeah
It already works pretty much flawlessly
And runs hundreds of blobs for ci
Its just that if I change this table subsystem it will have to be changed
They aren't needed to emulate, for mmio I just return all zeroes, for IO I return all 0xFF
Sometimes it hangs some blobs that spin until some value changes, but uacpi has while loop timeouts
So its not a problem nor an error
ah
@fiery turtle there's a slight mistake in the uacpi wiki page.
uacpi_status ret = uacpi_get_current_resources(node, &kb_res);
if (uacpi_unlikely_error(ret)) {
log_error("unable to retrieve PS2K resources: %s", uacpi_status_to_string(ret));
return UACPI_NS_ITERATION_DECISION_NEXT_PEER;
}```
you used uacpi_unlikely_error, when you probably intended uacpi_likely_error
well wouldn't that mean it's probably not an error
or am I just understanding this all wrong
Indeed, that's the idea
Why do you expect an error here
because it logs an error
ok what does uacpi_unlikely_error do
It checks ret ! = 0 wrapped in builtin expect
then what's uacpi_likely_error
It tells the compiler the error is unlikely
ohhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
The opposite
that confused me
Lol
did you know someone called Mintia is inventing a technology to use uacpi from rust?
Yeah iirc we did talk about it here a little bit but I think its abandoned now
doesnt uacpi use cmake?
it does and rust has a cmake-rs crate that would have made the buildscript considerably smaller and easier to maintain on uacpi version bumps
It doesn't use anything per se, cmake is one of the options, the one present simply accumulates all sources into a variable
There's also a meson file
true
if uacpi-rs is really abandoned i will probably ask mintia11 to take over once i need uacpi, as it looks like a solid foundation to work on
also how does the link to uacpi's github work there?
That would be awesome
Good question
but first i need to overcome procrastination and implement the pmm, vmm and heap from my notes that i made on vaccation
I know that feeling
i prefere cmake over cc and meson bc it abstracts most internal code awareness away and is more popular than meson (downloads: 42M vs 6k)
Same
working on implementing this now
hmm freeing the entire namespace is kind of not so trivial
i wonder if its possible without a node queue buffer
why?
in qacpi I just kept a linked list of all nodes and then I can just go through it to free everything (and also the potentially attached objects that the nodes might have)
because it has peer and child links
but i think i can reuse the same algorithm i used for depth-first walk
to do it non recursively
ah you want to do it without an extra link?
well you don't really need to find children while freeing and I have separate parent ptr + child array
unless you are talking about like freeing only one acpi scope and not everything
so u have a separate list of every node and then each node also has the links to parent and children?
yeah though the children are actually stored in an array of pointers
it was specifically for freeing them in case eg. some aml fails and it has created some temporary scope, though yeah ig it could be done using only the parent/child pointers
ah
i have this algo
void uacpi_namespace_for_each_node_depth_first(
uacpi_namespace_node *node,
uacpi_iteration_callback callback,
void *user
)
{
uacpi_bool walking_up = UACPI_FALSE;
uacpi_u32 depth = 1;
if (node == UACPI_NULL)
return;
while (depth) {
if (walking_up) {
if (node->next) {
node = node->next;
walking_up = UACPI_FALSE;
continue;
}
depth--;
node = node->parent;
continue;
}
switch (callback(user, node)) {
case UACPI_NS_ITERATION_DECISION_CONTINUE:
if (node->child) {
node = node->child;
depth++;
continue;
}
UACPI_FALLTHROUGH;
case UACPI_NS_ITERATION_DECISION_NEXT_PEER:
walking_up = UACPI_TRUE;
continue;
case UACPI_NS_ITERATION_DECISION_BREAK:
default:
return;
}
}
}
for non recursive iteration
i can reuse it for non recursive deletion as well i think
yeah probably
that's one of the nice things you can do with linked list of children, I don't think I could do that with the way I did it with arrays
yeah
well ofc you can do it using a stack but it uses extra mem
nah every node has
how do u find peers?
every node has NamespaceNode** children and NamespaceNode* parent
but what about peer
like
parent -> child1 -> child2
|
peer1
can child1 find peer1?
oh they would have to scan the parent's childern array for themselves?
and then look for the next node
yeah you'd have to do it like that
i see
in fact I kinda do it like that in the code that looks for nodes by name, if the node is not found in the current node's children it continues the search on the parent going through its array of children which includes the first searched node too (but it doesn't go down to any of the child nodes because that's not how the scoping works)
yeah
new addition to the insane real hardware AML collection
two identical tables with a slightly different name
bleeding edge hw too
uACPI worked fine btw 
25 aml blobs on that laptop
#1236769772805554206 message
It also worked first try on aarch64 and riscv, huge credit to qookie for the countless rounds of fuzzing with which we were able to find and eliminate a ton of corner cases and bugs
And Fadanoid for the first kernel advanced enough to support all 3 platforms with acpi
soon I will test it on IA64 (I am waiting on a serial cable)
partially, it's untested as of now
I'm not sure if it ever got to a functional enough state to test with
Ah
@sterile egret has a fork, but idk how far he got it
Is the source available anywhere?
The former
my kernel is not public as of now, no
it's on a local git server
oki :3
gonna see if I can test with it, actually
then later I can test on actual hardware