#uACPI - a portable and easy-to-integrate ACPI implementation
1 messages Β· Page 12 of 1
almost all allocations are under 2048 bytes (the number at which i don't use the predefined slabs and instead escalate to the kernel wired memory allocator.) these should do well usually
oh hold on, i see one ever-escalating allocation that turns up repeatedly, ramping up to well over 2048 bytes
i get very inconsistent results generally between runs
this revision adds a 4kib slab cache
and doesn't include some sanity checks designed to check for double-free
and this one doesn't free anything at all, which makes a useful comparison
i find a modest improvement with the 4k slab cache added and little difference with no freeing
wait, these are built with -Og
Uacpi speedrunning has been born
i have found a major limiting factor
namely the IPL management
@hollow elm this one should perform significantly faster
the build is not generally usable (it uses the lazy IPL mechanism, a mechanism i started work on which allows the issuing of writes to %cr8 to be deferred until such a time as they are actually necessary, i.e. until such a time as an interrupt exceeding the current soft IPL arrives - then it will write to %cr8 and set a flag indicating that %cr8 was raised and must fall later. this is helpful because the %cr8 write can induce a vmexit in some cases. it's broken however for edge-triggered interrupts)
interesting
how does this one perform on your machine
and the other ones?
my final submission, this one includes a faster memcpy and memset too
this one is consistently around 1.4m/s
before any changes, let me see what i was getting
before changes, [uacpi]: successfully loaded 1 AML blob, 1726 ops in 4ms (avg 370306/s)
so the improvement is very large indeed
unfortunately a huge chunk of the improvement is the lazy IPL
3.5 times
which is sadly broken and i fear would require significant work to fix
and to become nontrivial in implementation
Is it difficult to implement properly?
the problem is edge triggered interrupts
the basic idea is this: instead of strictly maintaining %cr8 (alias of the upper 4 bits of the lapic's TPR), a value in memory is maintained instead, and if an interrupt arrives which based on the value in memory should have been masked, then %cr8 is written to with the value in memory
and a bool is set to indicate that this happened
and splx() (which lowers IPL and is paired with spl raises) will test that bool, and if it's true, it will write the new lower value to %cr8
and then your level-triggered interrupts which were previously masked, they'll come through now
but the edge triggered ones won't
[uacpi]: successfully loaded 1 AML blob, 1726 ops in 1ms (avg 1542020/s)
fantastic, this is what i hoped for, cheers
out of curiosity: ```
[uacpi]: successfully loaded 1 AML blob, 1726 ops in 2ms (avg 735246/s)
it really is down in large part to having a better memcpy and memset than the trivial implementation, then
and writing to %cr8 really is slow under VMs, slow enough that i should try to get away from it
i guess it's always a vmexit
i saw reference to intel APICv which should help, but i don't know how new it is
presumably not old/widespread enough to get the benefit
does qemu use apic virtualization?
and it looks like you need to opt in to TPR shadow
i would think so, it looks to be an old feature, i'll have a look
for the most common case (IPL manipulations between 0 and dispatcher level) i can avoid touching %cr8 at all
now i can consistently reach around 1.4m/s which is fine
interesting
big!
Well I'll be damned
lmao
@fiery turtle you should probably make use of a microsecond-precise timer for measuring uacpi perf
to reduce variance
hm?
i dont use any specific timers, whatever the kernel returns
under the hood it could be using us converted to ns for the purposes of that api
the main use has been the Timer opcode in AML which expects 100ns ticks
oh did u think it was using ms internally? its nanoseconds converted to ms
(for printf specifically)
yeah i just didnt want the log to be way too long
fair enough
heyy
it's been some time since i checked this thread out
what major things have happened in the last 2 months?
there was some work on reactos i remember?
thread safety, lots of new api and helpers, opaque objects
is 1.0 here
havent heard from that person yet
yes
i hate the fact that i need to study this hard to get into a university to slack off for 4 years and then struggle to find a job anyway
they had an almost ready pr but then something happened and they disappeared idk
we have central university entrance exam
which means
many people who otherwise aren't interested in CS just pick CS because they can and "they don't want to waste their ranking"
medicine and CS are overly inflated
to study in a university that is not any random "apartment university" and actually has recognition abroad
i need to be like
top 1000
out of 3 million people entering the exam
universities that have recognition abroad: METU, BOUN, maybe ITU, maybe YTU and a couple private universities like Bilkent, SabancΔ±, KoΓ§
number of universities in turkey: 209
if you heard anything it was probably METU (middle east technical university)
oh wait, uacpi perf is largely allocator based? im using zig's std GeneralPurposeAllocator backed by a free-list based page allocator
or at least at this scale of benchmarking that is
the interpreter is very allocator bound yeah
my page allocator is a free list slab thing that idk if it has its own name but i think of it as the florence pmm because i took the concept and basic impl from florence https://github.com/FlorenceOS/Florence/blob/aaa5a9e568197ad24780ec9adb421217530d4466/subprojects/flork/src/memory/pmm.zig
afaik its basically half of a buddy allocator but im not 100% sure what the terminology would be
whats your current ops/s?
240107 in ReleaseSafe which is where im usually operating because safety checks are nice for debugging and catching issues
maybe you should optimize that
you could do escape analysis 
lol
what actually allocates in uacpi?
like theres a bunch of stuff that kinda has to
but most things shouldn't?
pooling might not be a bad thing to have in uacpi tbh since a lot of stuff is objects that presumably mostly have a known size
yeah but thats a general allocator thing
what do the profiles look like i wonder
tbh the bytecode construction doesn't feel particularly fast in general
if i was going for maximum performance, i'd definitely try doing a compile step so that you don't jit parse aml
i mean rn we're comparing the initial load where the aml gets parsed, or at least thats what people are reporting throughput for
okay fair
hm
Call frames, locals, intermediate expression evaluation, temporary objects
Some of those could be elided probably
Yeah it's designed to suck
But its still very tiny amount of time compared to the rest of kernel init
ah so basically everything got it
what a funny synchronicity
i was just reading an article by a guy at bilkent
http://istvanaranyosi.net/resources/Should we fear qt final.pdf this one specifically
What am I supposed to use instead of uacpi_namespace_node_get_object?
purpose?
I'm trying to update uACPI
And I have this function
uacpi_iteration_decision find_pci_device(void *param, uacpi_namespace_node *node, uint32_t depth)
{
struct DeviceSearchCtx *ctx = param;
uint64_t addr = 0;
(void)depth;
uacpi_object *obj = uacpi_namespace_node_get_object(node);
if (obj == NULL || uacpi_object_get_type(obj) != UACPI_OBJECT_DEVICE)
return UACPI_ITERATION_DECISION_CONTINUE;
uacpi_status ret = uacpi_eval_integer(node, "_ADR", UACPI_NULL, &addr);
if (ret != UACPI_STATUS_OK && ret != UACPI_STATUS_NOT_FOUND)
return UACPI_ITERATION_DECISION_CONTINUE;
if (addr == ctx->addr) {
ctx->out_node = node;
return UACPI_ITERATION_DECISION_BREAK;
}
return UACPI_ITERATION_DECISION_CONTINUE;
}
for_each_device can now filter by type
or rather
for_each_child
with UACPI_OBJECT_DEVICE_BIT
will automatically do what u want
uacpi_for_each_device_resource?
uacpi_namespace_node_for_each_child
Are there examples?
I wrote this code half a year ago and don't remember what it does π
looks like it finds the address of a pci device
theres even a dedicated function for adr iirc too
plus a function to get the type of a node
for_each_child can filter by node type already but yeah
if (class == 0x06 && subclass == 0x04) { // Host bridge
struct DeviceSearchCtx ctx = {
.addr = d->device << 16 | d->function,
.out_node = NULL,
};
uacpi_namespace_for_each_child_simple(node, find_pci_device, &ctx);
uint8_t secondary_bus = pci_secondary_bus(c);
printf("PCI secondary bus %x\n", secondary_bus);
uacpi_pci_routing_table *pci_routes;
uacpi_status ret = uacpi_get_pci_routing_table(ctx.out_node, &pci_routes);
if (ret != UACPI_STATUS_OK) {
fprintf(stderr, "Warning: Could not get PCI routing for root bridge bus %0x: %i\n", bus, ret);
} else {
parse_interrupt_table(g, secondary_bus, pci_routes);
uacpi_free_pci_routing_table(pci_routes);
}
check_bus(g, secondary_bus, d, ctx.out_node);
}
though you probably want to just use the filter
Wiki has this
uacpi_namespace_for_each_child(
uacpi_namespace_root(), acpi_init_one_device, UACPI_NULL,
UACPI_OBJECT_DEVICE_BIT, UACPI_MAX_DEPTH_ANY, UACPI_NULL
);
thats it

Oh and I remove this ```c
uacpi_object *obj = uacpi_namespace_node_get_object(node);
if (obj == NULL || uacpi_object_get_type(obj) != UACPI_OBJECT_DEVICE)
return UACPI_ITERATION_DECISION_CONTINUE;
yup
lets see your ops/s
hm?
I wrote it myself
why not reuse existing libc
anyway then just keep this logger
the fact that uacpi can do its own printf format is so nice for my case since zig's format doesnt use the same format string format as c does and this way i dont need to implement it in a shim somewhere
yeah language interop is a big reason why it was added
Do I need UACPI_KERNEL_INITIALIZATION?
Atm I just call ```c
uacpi_initialize(&init_params);
uacpi_namespace_load();
uacpi_set_interrupt_model(UACPI_INTERRUPT_MODEL_IOAPIC);
// init pci
uacpi_namespace_initialize();
uacpi_finalize_gpe_initialization();
And uacpi_initialize isn't working...
uacpi_initialize takes a flags bitmask now
if youre passing rsdp theres a kernel_api function you implement for that
pretty sure this one is for getting callbacks to the kernel but im not 100% sure
Oh uacpi_kernel_get_rsdp
thats a question to you
What do I need to give it in uacpi_kernel_get_thread_id()?
pthread_self?

thats the only path that may fail there
fix your nanoseconds_since_boot btw
What is the best way to implement it btw
kvm clock -> invariant tsc -> hpet -> ACPI PM timer -> manual tick counting
time to write another server
wtf
I had HPET in userspace
bruh imagine the latency of that
Maybe I'll add invariant tsc
Since hpet requires ioapic
Which I also manage from userspace...
reading hpet counter requires quite literally nothing
But it needs interrupts.....................
No I've been using it before for event timer
yeah
Like for sleep()
And then I've switched/unified everything to use lapic after porting to risc-v which only had local timers
you're mixing up rescheduling timers vs current time readback
those are unrelated concepts
I know
I'm just updating a global counter at the moment
From lapic interrupts
On bsp core
Which are 10-100* ms
(*which could be anything since the kernel is nonpreemptive)
(and it's running in single shot mode)
anyways
I think it initialized
But it doesn't shut down π¦
did u fix nanoseconds_since_boot btw?
not bad
Since I have homework due today
as accurate as your timer readback 
Which I hadn't started
Who needs a precise time anyway
+- 10 seconds accuracy is fine for most use cases
indeed
How can I debug this?
It was working with the old version
(and 64 bit userspace and before I made a bunch of changes to everything)
But it did get interrupt from the power button
So that should be working
add printfs
or at least addr2line where its stuck
i remember someone having a similar bug
i dont recall how they fixed it tho
bruh
Commenting sleep(3) out makes it shut down 
maybe its trying to sleep for a month 
Ok I think I'll finish the uni stuff and try to fix timers (in about 1 hour)
Or I could keep procrastinating 
I think it was indeed sleeping for a month

I have been passing size_t to a function which takes uint64_t
And it probably got garbage in the upper 32 bits
unrelated but why is your AHCI segfaulting lmao
My fork is broken
It corrupts heap
(I think)
I haven't investigates
And AHCI driver forks for every controller it finds
is fork done via userspace?
No/yes
My fork clones 2 threads
Because each process has a library threads
So that library thread saves the state of the forking thread, clones itself, and recreates the thread that forked
And that saves all registers to .data
Which are defined as 16 longs
bruh why is this so complicated
Which are 32 bit on i686
But I haven't added a special case for that in kernel
(I think this is what's happening)
It usually works fine
Since it's 64 bit
Because I didn't think of a good way to make kernel interrupt userspace on signals
So I came up with this
(though I didn't implement signals yet)
Lol even more things are failing now...
But it doesn't wait for a month anymore 
Ok, time to fix nanoseconds_since_boot
Are there 32 bit hpet systems in existence?
yes
yeah, my dev system (b550) has a 32 bit hpet
I think it was 42 seconds?
idk I don't use hpet for anything other than tsc calibration
actually no I think that was a different system, this one was 5 minutes
as long as the maximum time between reads is less than the rollover time you're fine
wiki must've been written in 2001
there's a cpuid bit that tells you whether the tsc is usable for general timekeeping and it's set even on the oldest systems I own
invariant tsc bit
tsc used to be a simple cycle counter which could change freq which is why it was unreliable, but for decades now it's been a fixed frequency
for vms u want kvm clock
it was but nowadays you could probably get away with denying systems that don't have an invariant, constant, nonstop tsc
yeah just rdtsc, also has the advantage that you don't need actual syscalls for time stuff. to get the frequency just read both it and a known-frequency clock like hpet before and after sleeping a few milliseconds
on newer Intel systems and in hypervisors cpuid can tell you the freq but that's not ubiquitous yet
(which needs to be changed to HPET/time base CPUID stuff but it doesn't matter)
Is it synchronized between cores?
Wiki says it isn't
And I haven't reached to the explanation in SDM...
Oh no
per-CPU differences in TSC values in a NUMA system
important distinction there: "in a NUMA system"
So multi CPU
I'm not sure if it's explicitly guaranteed anywhere but I've never seen a system where the CPUs in a single package have different TSC values or frequencies
NUMA != multi CPU
I have Ryzen 9, it has 2 CCDs
Still not NUMA
But I think it's not NUMA
As two sockets
oh yeah multi socket systems are more likely to be numa
But I don't have those
but then again you'll most likely never encounter those; the approach I take is to simply not use any CPUs that aren't in the same package as the boot CPU
I don't think my OS is useful enough for that to happen anyways
So I just rdtsc on boot cpu and use it as the reference?
that'll work on basically any consumer system yeah
The synchronization of TSC adjustment can be simplified by using the 64-bit IA32_TSC_ADJUST MSR
Ok that doesn't apply to me I think
Also I can add a mechanism to syncronize the timers
that just helps with increasing/decreasing the tsc value by the same decrement on every cpu
for stuff like NTP I think?
or just adjusting system time in general
Hmm I think I can just replace LAPIC with TSC deadline...
I experimented a bit with tsc synchronization but from what I can tell the hardware does it for you and it's incredibly difficult to do it more accurately than the hardware does
Although it doesn't matter
if it's available yeah but there's plenty of systems where it isn't
for example, my Ryzen 5xxx doesn't support it
So the counters are just syncrhonized and I don't have to do anything?
yep
That's what I have lol
And I've recently bought b550 mobo
Ok
It makes it easy (?)
(I need to stop saying that and keep reeding SDM)
technically I don't think there is any guarantee in the spec about tsc being synchronized between cores
Do I need to enable it or something?
Practically because it's a single timer for all cores in a single socket it is synchronized
Invariant/constant tsc just exists, it just means that's how the timer behaves
The invariant TSC is based on the invariant timekeeping hardware (called Always Running Timer or ART), that runs
at the core crystal clock frequency. The ratio defined by CPUID leaf 15H expresses the frequency relationship
between the ART hardware and TSC.
On some newer cpus you can use the cpuid to calculate it directly, on others you can't
yeah I haven't seen a synchronization guarantee in the sdm either but every system I've tested on is synchronized
that's only supported on new-ish Intel CPUs, AMD doesn't have it
Tbh I would dig in the lkml instead, alot of guarantees only appear there and no where else
And if Linux does synchronize it at boot then there is most likely a reason (which might just be numa, but that's why you wanna check why they added it)
wait does amd seriously not have invariant tsc? wow
it does, just not the cpuid leaf that tells you the freq
the invariant TSC itself is still supported
you just need to calibrate it to figure out the freq
it does
Then why didn't they enable it lol
they do
I mean the cpuid leaf
I wish there was a way to detect constant tsc without requiring the power p/c state stuff for invtsv tbh. unless there is and I don't know it anyway
they set the bit that indicates it's supported, they just don't have the frequency leaf
Ah
I think early ryzens also didn't have invariant tsc
And I thought you were talking about that
the lack of frequency leaf i knew because its making things more annoying to me because I only have amd cpus lol
I doubt that considering the supported bit is in the AMD CPUID block and my pre-Ryzen Intel system supports it (Haswell)
I am also certain it doesnt have it, but I don't have a Ryzen to test rn
I think since Prescott the tsc is constant
But for a while it wasn't invariant and would change across C states
Or maybe it was during sleep idr
are you sure? intel introduced it in like 2008 or so
Can I assume rdtsc always exists?
er, no, that's constant tsc
Pretty sure, I can check when I get to my pc
iirc constant vs invariant is across deep sleep states
Constant TSC is between C states n stuff right
Could be that
so acpi sleep etc
yep
Ok so it seems nehalem and up should have invariant tsc across p/c states
But not across s states
invariant is across s states and constant is p/c states afaik
the problem is iirc theres no way to check for constant unless you also have invariant
solution: ```
if(invariant)
return;
if(!constant)
panic();
turn_off_acpi_sleep();
id assert
before nehalem it seems that the tsc was constant across everything but c states excluding hlt state
Idk if there were other sleep states tbh
Well if Linux can figure it out on my cpu without invtsc, then surely there is some way
My Intel i5 4200M has the bit set
But I am almost sure that before Prescott it wasn't constant at all
Maybe I just misremembered
Although I doubt it
from Intel sdm:
The invariant TSC will run at a constant rate in all ACPI P-, C-. and T-states
Then constant TSC is what
I think it's the other way around, constant TSC is consistent as long as you don't change C-states and invariant TSC is always consistent
The hell is T state
And it seems that anything P5+ on the Intel side will basically be invariant as long as you don't enter any sleep state other than hlt
yeah that's constant I think
and invariant is the same but you are allowed to enter sleep states
Technically hlt enters a c state, it's just that in the cpu impl case it also increments the tsc normally
Invariant also means it's synchronized with all other CPUs
What was the trick for this, but without overflowing?
return rdtsc() - boot_tsc * 1000000 / tsc_ticks_per_1_ms;
Where you saw that?
(except missing parenthesis)
I was asking
i think so on all hardware and I assume so in my kernel but I don't think it's explicitly guaranteed in the sdm
if it is I can't find it
I think the spec says nothing
If Linux and windows assume it then surely you can too
But practically every single socket cpu has a single tsc
But idk if they assume it
I am not sure if they do
Linux almost certainly does not because of numa, and they might as well always sync it just in case
you change the multiplier in such a way that the divisor becomes a power of two, then use 128-bit multiplication and shift right instead of division
I forgot the formulas for how to do that but they're in this server somewhere
Imagine using anything but the acpi timer smh
But will I have 128 bit multiplication in i686? 
I have a specific hardware which I want to target
not in a single instruction but I believe it won't need to use libgcc for it
It's the other way around
Might as well use i286
i think on 32-bit it'll be 4 muls and some additions for a 64x64=128 bit multiplication
I don't remember
I don't understand any of it
actually iirc GCC doesn't support 128 bit arithmetic at all on <64 bit arches
as in the int128 type doesn't exist
then idk
agree but I would like my kernel to at least be compilable with it
GCC is based since it has the new struct changes in c23 already
But I would need to rebase my patches on the latest version
<source>:4:22: error: __int128 is not supported on this target
4 | return (unsigned __int128)a * (unsigned __int128)b;
| ^
``` clang also doesn't support it on 32-bit
the only times I've messed around with 32 bit is while writing for the absolute lowest common denominator (Compaq Deskpro 386) so I didn't have to deal with tsc
Otherwise atm I have gcc 12
The last time I messed around with not 64bit is either using armv7-m or an 8bit cpu
which
It doesn't matter, I just use uint64_t's for timers in my kernel
Where multiple definitions of the same struct are allowed (as long as they have the exact same fields)
why is that based
It allows to have generic macros without also needing a define if the type
oh yeah same even in that one I used 64 bit but for timers as fast as the tsc you really want to be able to have 128 bit intermediate values
So things like a result type are easy to impmement
hm?
Like a imagine a macro defining ```c
struct result_int { err_t err; int value; }
Where the int is pasted from a macro parameter
So RESULT(int)
You can not use that macro in both the function signature and when returning
And it would be the exact same type
how would that work
with 32x32=64 multiplication, treating 64-bit numbers as two digit numbers in base 2**32:
AB * CD
= uint128_t(B * D)
+ uint128_t(D * A) << 32
+ uint128_t(C * B) << 32
+ uint128_t(C * A) << 64
amazing, thanks discord for the code block
RESULT(int) my_func() {
return OK(int, 123);
}
And OK can be (RESULT(int)){.value = 124};
Like it's almost entirely for macros defining structs to have a easier time
every time I hear something about c23 I didn't know before the urge to target it in my next project gets more intense
I mean ignoring the cool libc additions, I would take it just for the #embed lol
the only thing stopping me right now is that afaik the implementations aren't fully complete yet
So many projects where I need to setup an objcopy just for thatv
Yeah both clang and GCC are missing stuff
Also I am not sure if anything fully added the libc additions
eh it'd be nice but I just run the files in question through xxd manually
Ouch
Or well
I have at works projects where we need to include mbs of files in the binary
works fine enough for small files and embedding large files would give me the ick anyway
Which can take literal minutes to compile sometimes
oh yeah fair enough, the largest files I've ever needed to embed are like 4k-16k
In one project we have an entire rootfs embedded in the binary (we really like standalone static binaries)
The bit precise integers are also cool
Especially cause no integer promotion
Oh and yeah, typed enum
Wait that's the uacpi channel wtf
@fiery turtle
What does that even mean?
Am I the 1st worst?
Feels bad to be nyaux though
I thought mine was bad
Which it is
But nyaux' was terrible
nah, nyaux is the worst at 150/s
It's mostly an allocator and mutex benchmark right?
ah
I'm using dlmalloc
Is it running on a big endian subleq cpu by any chance
lmao
I think uacpi's memset and memcpy are a big culprit for me
My normal ones are accelerated
But idk if uacpi uses those ones
I kinda wanna try integrating it into my kernel just for the benchmark but so far I've managed to get away with no real kernel vmm (all kernel allocations are <=PAGE_SIZE and in hhdm) and I'd like to keep it that way
that looks about the same as what i get when building in releasesafe (no idea what the equiv is)
not if u made it use those
Yeah ik
look at the libc header
you can override them if it isnt, thats what im doing (though just forwarding to zig's builtins for me)
I override livc
Libc
I think
I indeed do
void *uacpi_memcpy(void *dest, const void* src, size_t sz)
{
return memcpy(dest,src,sz);
}
void *uacpi_memset(void *dest, int src, size_t cnt)
{
return memset(dest, src, cnt);
}```
zig's builtins might be accelerated but idk, i also have all simd/vector things turned off in my kernel anyway so idk how much it can accel apart from rep instruction stuff
that's probably not the culprit
well in that case it is using yours
I use rep movsb and friends
I know
but memcmp
int uacpi_memcmp(const void *src1, const void *src2, size_t cnt)
{
const uint8_t* b1 = (const uint8_t*)src1;
const uint8_t* b2 = (const uint8_t*)src2;
for (size_t i = 0; i < cnt; i++)
if (b1[i] < b2[i])
return -1;
else if (b1[i] > b2[i])
return 1;
else
continue;
return 0;
}```
does this
which isn't very efficient
id assume zig uses rep movsb but im legit not sure
void *uacpi_memmove(void *dest, const void* src, size_t len)
{
if (src == dest)
return dest;
// Refactored from https://stackoverflow.com/a/65822606
uint8_t *dp = (uint8_t *)dest;
const uint8_t *sp = (uint8_t *)src;
if(sp < dp && sp + len > dp)
{
sp += len;
dp += len;
while(len-- > 0)
*--dp = *--sp;
}
else
while(len-- > 0)
*dp++ = *sp++;
return dest;
}```
memmove can probably be improved
yeah thats not uh
how any of this works
what
oh right its #defines i forgot about that
oh yeah I know
libc.h
#define uacpi_memcpy uacpi_memcpy
#define uacpi_memset uacpi_memset
#define uacpi_memmove uacpi_memmove
#define uacpi_memcmp uacpi_memcmp
#define uacpi_strcmp uacpi_strcmp
#define uacpi_strncmp uacpi_strncmp
#define uacpi_strnlen uacpi_strnlen
#define uacpi_strlen uacpi_strlen
#define uacpi_snprintf uacpi_snprintf```
I do fnuy macro stuffs
This is in userspace btw
yeah only 3 orders of magnitude behind managarm 
well my strcmp and memcmp functions are non conformant
I am missing memmove
and strnlen
π
What do they do
they return a bool instead of int
memcpy and memset should still be builtin
Stack allocator?
just a normal frigg allocator
if memcmp is nonconformant you're violating one of gcc's assumptions
managarm and keyronex are both at 1.5M, I think for keyronex reducing vmexits for cr8 writes and improving memcpy was the fix
initially keyronex was at 250K i think
oh and a 4k slab
I forgor how much mine was
astral is at 250k also
my cpu does indeed have invtsc
+invtsc
(and it didn't use tsc)
in qemu
but idk why linux doesn't report it
QEMU explicitly clears it because it messes up VM migration
in /proc/cpuinfo
this is why with qemu+kvm u use the kvm clock
it just gives u the values to convert tsc to elapsed ns
Add support for kvmclock or use -M migratable=off (both only have an effect if kvm is used)
I wasn't using KVM
wait why
I haven't found a way to get QEMU to report invariant TSC while using TCG
for no kvm 20k is a decent result
oberrow@AcerAIO:~$ cpuid -l0x80000007
CPU 0:
RAS Capability (0x80000007/ebx):
MCA overflow recovery support = false
SUCCOR support = false
HWA: hardware assert support = false
scalable MCA support = false
Advanced Power Management Features (0x80000007/ecx):
CmpUnitPwrSampleTimeRatio = 0x0 (0)
Advanced Power Management Features (0x80000007/edx):
TS: temperature sensing diode = false
FID: frequency ID control = false
VID: voltage ID control = false
TTP: thermal trip = false
TM: thermal monitor = false
STC: software thermal control = false
100 MHz multiplier control = false
hardware P-State control = false
TscInvariant = true
CPB: core performance boost = false
read-only effective frequency interface = false
processor feedback interface = false
APM power reporting = false
connected standby = false
RAPL: running average power limit = false
fast CPPC = false```
also fun fact, tcg doesn't explicitly emulate invariant tsc, but the tsc is just the host's tsc value on x86_64 hosts so if the host has an invariant tsc the tcg guest does too :^)
yeah I just decided to assume the TSC was invariant if kvmclock isn't supported, no matter what CPUID says
u support kvmclock also?
yeah
if it's available I use it, if not TSC regardless of what CPUID says
nope, it's a private repo and will likely stay as such for a very long time
why is that
worst case tcg's tsc is just the host's CLOCK_MONOTONIC time in nanoseconds
just personal preference, I like to keep things private until they're usable
syscall per rdtsc π
a little thing called vdso
tru
like my kernel can do quite a lot but it's not at the point where I'd like to make it public
works until the clock source in linux is assumed unstable
at which point it switches to hpet
and vsdo no workie
It didn't work π¦
it?
Am I checking it wrong?
what did u change
u64 c = cpuid(0x80000007);
if (c & (1 << 8)) {
global_logger.printf("[Kernel] Info: TSC Invariant bit is set\n");
your qemu command line..
Qemu doesn't pass invariant tsc by default
sudo qemu-system-x86_64 -device pcie-root-port,id=root_port1,port=0x10,chassis=1,multifunction=on,slot=1,bus=pcie.0,addr=0x10 -device x3130-upstream,id=switch_upstream,bus=root_port1,addr=0x0 -device xio3130-downstream,id=switch_downstream1,bus=switch_upstream,chassis=3,addr=0x1 -device xio3130-downstream,id=switch_downstream2,bus=switch_upstream,chassis=4,addr=0x2 -device pcie-pci-bridge,id=pci_bridge1,bus=switch_downstream1,addr=0x0 -device pcie-pci-bridge,id=pci_bridge2,bus=switch_downstream2,addr=0x0 -device e1000e,id=nic1,bus=pci_bridge2,addr=0x0 -drive file=limine/pmOS.iso,if=none,id=hdd0 -device ide-hd,drive=hdd0 -M q35 -smbios type=0,uefi=on -bios ovmf-x86/OVMF.fd -m 2G -smp 4 -serial stdio -cpu host,+invtsc -enable-kvm
-M migration=off
I keep forgetting whether it's migration or migratable
iirc migratable?
could be the latter
qemu-system-x86_64: Property 'pc-q35-6.2-machine.migration' not found
migratable
also isn't it a property of cpu and not machine?
machine i think
bruh qemu-system-x86_64: Property 'pc-q35-6.2-machine.migratable' not found
hm
-cpu migratable=off
u have an old ass qemu btw
is it migrateable?
if that doesn't work either it's probably just because your QEMU is too old
from managarm's qemu script cpu_model = "host,migratable=no"
btw why do you have such a complex pcie topology
I've been implementing PCI interrupts routing
@vast kestrel btw what happened to AML OS?
Is this correct?
ah
Never had the chance to continue sadly
F
Like I had a super busy time at work
happens
And the last week I am on vacation but too burned out to really work
my vacation starts this saturday so im happy
Noice
Btw @winter orbit any updates regarding ReactOS? I think you mentioned it was almost ready or w/e but then something happened?
Iβm basically rewriting the entirety of hal ACPI and pci at the same time
Lmao
damn
How fun, it (uACPI) died
make sure to fetch the latest uacpi, a lot has changed since then!
It workwd though for what I needed ROS to do

And it didn't detect TSC either
didnt it use to work lol
Bare metal
Hereβs some screen shots using uACPI though
daaamn
One on the left finally detects the touch screen
80 fps is quite decent
One on the right is using the gpu properly
what had to be changed?
uACPI bug until proven otherwise 
Should really be saying that to you lol uACPI works great
Hey that's my line
Iβm almost done porting it to windows 10
Face reveal
almost impossible to segfault uacpi, 99.9% confidence its your skill issue 
Minecraft is impressive
thank you! happy to hear that
yes!
StarCraft 2
yeah
only gamers get that joke π
its a reference to one of their vids actually running crisis on reactos
It crashed in uacpi_execute_table
and i am referencing Jensen Huang being cringe
How do I compile with dwarf?
if u want me to prove that it's your bug give me the table dump and ill run those in userspace with asan and ubsan 
This isnβt uACPI though
Everything Iβve sent today otherwise is :)
still crazy
yeah turns out GPUs were getting broken on pci, Hal, ACPI etc
So Iβm rewriting stufff to be more like windows 10
Thatβs why itβs taking so long, itβs kinda a pain
and will probably still take many months
I've sent it to you before
Iβll make a thread next interesting thing happens and start posting on it
oh cool
My bad I get tunnel vision
pls do
was it here or
I don't remember
yeah thats quite understandable
i wonder what sort of ops/s is reactos getting
I think it crashed in prepare_table_load
Ok but seriously how do I make objdump show code with clang
cant u just addr2line the rip?
??:?
do u compile with -g?
Yes
try -ggdb3
Still nothing
is it even a valid rip
Ok, llvm-objdump worked for some reason
Yes, I looked at assembly
It crashed in ```x86asm
; return cause != UACPI_TABLE_LOAD_CAUSE_INIT;
224683: 41 83 fe 02 cmpl $0x2, %r14d
224687: 41 0f 94 c6 sete %r14b
; in_method->code = dsdt->definition_block;
22468b: 48 8d 43 24 leaq 0x24(%rbx), %rax
22468f: 48 89 45 c8 movq %rax, -0x38(%rbp)
; in_method->size = dsdt->hdr.length - sizeof(dsdt->hdr);
224693: 8b 43 04 movl 0x4(%rbx), %eax
224696: 83 c0 dc addl $-0x24, %eax
224699: 89 45 d8 movl %eax, -0x28(%rbp)
; in_method->named_objects_persist = UACPI_TRUE;
uACPI/source/interpreter.c:1260
whats the cr2 btw
4
so the dsdt pointer was null?
Like this is what it printed
can u recheck your mapping callback?
What do I need to check?
this looks like all of the tables to me
whats missing
The last one looks cut off
facs doesnt have those fields
so they arent printed
its special cased in the dump_table_header fn
so it dies when trying to load the DSDT, when preparing it for load
u can add a printf to uacpi_execute_table to see the value of tbl
btw can u not add a trailing newline to uacpi logs because it looks ugly
aa000?
well
looks like a stack corruption or something similar, because mapping is checked for nulls
i can see it propagating the error
tbl->ptr = uacpi_kernel_map(tbl->phys_addr, tbl->hdr.length);
if (uacpi_unlikely(tbl->ptr == UACPI_NULL))
return UACPI_STATUS_MAPPING_FAILED;
maybe log all the mappings happening?
Does it call unmap right after mapping?
Lol how do I dump the tables
From Windows
after loading, yeah
huh, mapping is not even called?
that might be a heap corruption that happens to overwrite the table's reference count value
try increasing UACPI_STATIC_TABLE_ARRAY_LEN
to like 128 or something
[uACPI][INFO] RSDP 0x00007FFE47B42BE0 00000024 v02 (uOEMID)
[uACPI][INFO] XSDT 0x0000611000000CC0 000000DC v02 (LENOVO uTESTTBL)
[uACPI][INFO] DSDT 0x0000630000000400 0000E586 v02 (LENOVO AMD_EDK2)
[uACPI][INFO] FACP 0x00006120000004C0 00000114 v06 (uOEMID uTESTTBL)
[uACPI][INFO] SSDT 0x000061E000000080 000009AE v01 (LENOVO UsbCTabl)
[uACPI][INFO] SSDT 0x000062D000000400 00008416 v02 (LENOVO AmdTable)
[uACPI][INFO] SSDT 0x000062A000000200 0000547E v02 (LENOVO AmdTable)
[uACPI][INFO] SSDT 0x0000619000000A80 000003D5 v02 (LENOVO Tpm2Tabl)
[uACPI][INFO] SSDT 0x000061B000000080 00000655 v02 (LENOVO OEMPMF )
[uACPI][INFO] SSDT 0x000061F000000080 00000BEB v02 (LENOVO CPMDFIG4)
[uACPI][INFO] SSDT 0x0000626000000100 00002C36 v02 (LENOVO CDFAAIG2)
[uACPI][INFO] SSDT 0x000062E000000400 00009F19 v02 (LENOVO CPMCMN )
[uACPI][INFO] SSDT 0x000061F000000E80 00000CC7 v02 (LENOVO NVME )
[uACPI][INFO] SSDT 0x000061E000000C80 0000098D v02 (LENOVO GpMsSsdt)
[uACPI][INFO] SSDT 0x0000608000000220 00000058 v02 (LENOVO GPP1 )
[uACPI][INFO] SSDT 0x00006080000002A0 00000058 v02 (LENOVO GPP0 )
[uACPI][INFO] SSDT 0x0000622000000100 00001678 v02 (LENOVO UPEP )
[uACPI][INFO] SSDT 0x000061C000000080 0000074E v02 (LENOVO THERMAL0)
[uACPI][INFO] SSDT 0x0000621000000100 0000108F v02 (LENOVO GPP_PME_)
[uACPI][INFO] SSDT 0x000062D00000A400 00009B61 v02 (LENOVO INTGPP03)
[uACPI][INFO] SSDT 0x000062A000006200 000052B4 v02 (LENOVO INTGPP01)
[uACPI][INFO] SSDT 0x000061E000001880 00000B69 v02 (LENOVO CPMGPIO0)
[uACPI][INFO] SSDT 0x0000611000000E00 000000E7 v02 (LENOVO MHSP )
[uACPI][INFO] SSDT 0x000061D000000080 0000097B v02 (LENOVO CPMMSOSC)
[uACPI][INFO] SSDT 0x000060D0000002B0 00000090 v02 (LENOVO CPMMSLPI)
[uACPI][INFO] SSDT 0x0000621000001500 00000FDF v02 (LENOVO CPMACPV5)
[uACPI][INFO] FACS 0x0000606000000320 00000040
[uACPI][INFO] successfully loaded 23 AML blobs, 10241 ops in 259ms (avg 39409/s)
[uACPI][INFO] namespace initialization done in 22ms: 106 devices, 1 thermal zones
@calm latch
lets try more stuff tomorrow yeah
this is what i recommend
if behavior changes its a heap corruption
What could cause it though
who knows, could be a bug in your kernel_api impl, could be a bug in the kernel's vmm handing out the same phys page for allocator storage
anything
u use an existing malloc impl right
dlmalloc
Ye
I've recently changed how tlb shootdowns are handled, and that might have changed something in my vmm and I think it's been a bit fishy
The only path through which this doesnt call map is when a table already has alive references
Which is impossible in this case
Can I print references?
But tomorrow
U can add a printf
That sounds quite plausible
Yeye
I can try 32 bit userspace 
Try it
The strange thing is that it's consistent
Or maybe not strange
No, it is strange
Printfs should have changed the order of stuff
Well I mean its not multithreaded so why would it change
Which printfs do you mean
Like if the pages get stolen and double mapped or something
Yeah it's 2 am where I live
Gn
This is somewhat unrelated, but I can't get this to work
u32 ticks = apic_read_reg(APIC_REG_TMRCURRCNT);
u64 divisor = 1'000'000;
apic_freq = computeFreqFraction(ticks, divisor)
auto l = apic_freq*1'000'000'000;
printf("%li %li\n", l, ticks*1000);
prints 4294339902999 3667574296
(and 3667574296 should be correct I think)
the apic timer counts down so you have to subtract the read value from your starting value
I reset It in the beginning
yes but it still counts down
when you read it, you get the ticks remaining
that calculation needs the ticks elapsed so you need to subtract from the starting value
So -1
Ok I'm dumb that has worked
Thx
I think I have TSC
ΒΏ
Can it take 0ms?
@fiery turtle
what do you write to ICR when you start the timer? the value you want is icr - ccr
I write -1, start the timer, and read its value
ok yeah then it's UINT32_MAX - value_read_from_timer
So - what I've read should be number of ticks
Yes
Maybe I'm missing +1
Or -1
Anyway it's 662379999 662380000
And I'm using PIT for calibration...
So it probably doesn't matter
Time to test this...
Does this mean I have SEVERE heap corruption?
(and power button didn't work
)
Could ACPI stuff be in bootloader reclaimable memory?
(LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE)
maybe
probably not tho
not with limine anyway
yeah
thats unrelated
you're corrupting internal uacpi structures
that are on the heap
But that's weird
probably needs an EC driver, just like all modern laptops
managarm has an example
bruh
Some quality code
I need to try static analyzers
Or dynamic ones
Although the only thing that should be allocating in between uACPI initialization is my PCI routing stuff...
Lol it still does it
I don't know how that came to be
I need to stop using copilot
indeed
btw that AndOp error is a bug in your AML blob
they didnt account for osses without an EC driver
so if EC is not available it uses uninitialized variables
not critical but yeah
firmware dev brain rot
If (ECAV)
{
If ((Acquire (LFCM, 0xA000) == 0x00))
{
Local0 = BA1P /* \_SB_.PCI0.LPC0.EC0_.BA1P */
Release (LFCM)
}
}
If ((Local0 & 0x01))
{
Return (0x1F)
}
It initializes Local0 only if ECAV, which is EC availablility
it mallocs them
And clears them?
lets see
waiiiiiiiiiiiiiiiiit
waiiiiiiiit
might be a uacpi skill issue
can u revert table array len
and test one thing
ye
I've switched to git submodule btw
diff --git a/source/tables.c b/source/tables.c
index 7e8de51..a30b070 100644
--- a/source/tables.c
+++ b/source/tables.c
@@ -563,6 +563,7 @@ static uacpi_status verify_and_install_table(
dump_table_header(phys_addr, hdr);
uacpi_memcpy(&table->hdr, hdr, sizeof(*hdr));
+ table->reference_count = 0;
table->phys_addr = phys_addr;
table->ptr = virt_addr;
table->flags = flags;
@@ -571,7 +572,7 @@ static uacpi_status verify_and_install_table(
if (out_table == UACPI_NULL)
return UACPI_STATUS_OK;
- table->reference_count = 1;
+ table->reference_count++;
idk why i even bother running ubsan and asan
they dont catch shit
One sec I need to google how to apply patch
lol
just do it by hand
its 2 lines of code
green is new stuff red is removed stuff
Ok
maybe also set MALLOC_PERTURB_=170 or something?
er wanted to respond to this
is that like a thing you have to export at runtime?
yeah
it's glibc-specific but it causes malloc to fill all fresh bytes with the given value
in this case 0xaa
hmm are u sure thats the name?
Btw is this plausible?
0ms is possible yes, but you're getting 5 million a second
thats highly questionable
M_PERTURB (since glibc 2.4)
If this parameter is set to a nonzero value, then bytes of allocated memory (other than allocations via calloc(3)) are initialized to the complement of the value in the least significant byte of value, and when allocated
memory is released using free(3), the freed bytes are set to the least significant byte of value. This can be useful for detecting errors where programs incorrectly rely on allocated memory being initialized to zero, or
reuse values in memory that has already been freed.
Environment variables
...
MALLOC_PERTURB_
Controls the same parameter as mallopt() M_PERTURB.
I have SSE in userspace π π
man 3 mallopt
i did get the values wrong
it's ~0xaa when mallocing, then 0xaa after freeing
Ok I'm trying it
β― MALLOC_PERTURB_=170 /mnt/d/uacpi/tests/runner/build-linux-64bits/test-runner dsdt.dat -x ssdt1.dat ssdt2.dat ssdt3.dat ssdt4.dat ssdt5.dat ssdt6.dat ssdt7.dat ssdt8.dat ssdt9.dat ssdt10.dat ssdt11.dat ssdt12.dat ssdt13.dat ssdt14.dat ssdt15.dat ssdt16.dat ssdt17.dat ssdt18.dat ssdt19.dat ssdt20.dat ssdt21.dat ssdt22.dat ssdt23.dat ssdt24.dat
(I have to restart laptop (which is obnoxious with windows 11 for some reason), disable secure boot, reboot it again so it sees my usb drive, and then do everything in reverse)
well
sure but u get like a 3.5x speedup vs fastest kernel here
so idk
Though the new time source is like 60% probability broken
Why does it always boot windows
these types of bugs are why most linux distros ship with unconditional zeroing of absolutely all allocated memory
(in the kernel i mean)
Yeah kernel devs just forget
In our embedded code we just define malloc to zero to not have a chance to forget to zero
best bet for production
Speed
π
that avg is insane if true, but i doubt it tbh
I'm gonna check get_nanoseconds_since_boot() against sleep
Which is definitely working and uses lapic
oh then maybe not
--- a/tests/runner/interface_impl.cpp
+++ b/tests/runner/interface_impl.cpp
@@ -228,6 +228,8 @@ void* uacpi_kernel_alloc(uacpi_size size)
return ret;
allocations[ret] = size;
+ memset(ret, 0xFF, size);
+
ret
i did this
but still not repo
repro
idk
it should work with any tables as long as there's more than static array len
Could it be a particular case when the pointer is null but the rest isn't?
Ok it booted back...
Where does out table come from?
Ok I'm gonna remove the prints just to be extra sure
That I didn't boot an old iso or something
And also check nanoseconds stuff
ohhhh
its because i was using early_table_access
so my structures are copied from there on init
static uint8_t early_table_buf[4096];
auto st = uacpi_setup_early_table_access(
early_table_buf, sizeof(early_table_buf)
);
ensure_ok_status(st);
this stuff hid the bug
/mnt/d/uACPI/source/interpreter.c:1259:23: runtime error: member access within null pointer of type 'struct acpi_dsdt'
/mnt/d/uACPI/source/interpreter.c:1260:32: runtime error: member access within null pointer of type 'struct acpi_dsdt'
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1189==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000004 (pc 0x55bd8f039a97 bp 0x7ffc4f2720f0 sp 0x7ffc4f2720a0 T0)
==1189==The signal is caused by a READ memory access.
==1189==Hint: address points to the zero page.
#0 0x55bd8f039a97 in prepare_table_load /mnt/d/uACPI/source/interpreter.c:1260
#1 0x55bd8f039d04 in do_load_table /mnt/d/uACPI/source/interpreter.c:1272
#2 0x55bd8f03cb19 in uacpi_execute_table /mnt/d/uACPI/source/interpreter.c:1539
#3 0x55bd8f00c2a1 in uacpi_table_load_with_cause /mnt/d/uACPI/source/tables.c:1088
#4 0x55bd8f01b4e5 in uacpi_namespace_load /mnt/d/uACPI/source/uacpi.c:395
#5 0x55bd8ef6577a in run_test /mnt/d/uACPI/tests/runner/test_runner.cpp:486
#6 0x55bd8ef67b81 in main /mnt/d/uACPI/tests/runner/test_runner.cpp:622
#7 0x7f7517df3d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#8 0x7f7517df3e3f in __libc_start_main_impl ../csu/libc-start.c:392
#9 0x55bd8ef5f4e4 in _start (/mnt/d/uacpi/tests/runner/build-linux-64bits/test-runner+0x19d4e4)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /mnt/d/uACPI/source/interpreter.c:1260 in prepare_table_load
==1189==ABORTING
there u go
this is the crash u were seeing
yup verified that fix works
will push shortly
The timer is broken


