#FrostyOS
1 messages ยท Page 4 of 1
Yes
ah I was thinking of the global bit
What makes this more annoying is that the custom page table hasn't even been loaded yet
i can confirm the issue is unrelated to the PAT
this scrolling is so slow
i'm tempted to hack in some double buffering to speed it up
i decided to do it
due to lack of a vmm, I'm just putting a 16MiB raw buffer inside the kernel image
this is just temporary so I can debug easier on real hardware
first attempt resulted in an empty screen
second (latest) one resulted in a triple fault
and its working
it was such a stupid mistake
scrolling is so fast on real hardware now
like at least 100x QEMU
but, I can confirm my 1GiB page mapping function is not the issue
there is something else someone hiding from me
somewhere not someone
it's me! Froste!
This was checked by redirecting that function to just calling the 2MiB page variant
it seems to work no issues if I don't check if 1GiB pages are supported
it is only when I do check that things go badly
yep, I can confirm that the page tables are perfectly valid for the 6 or 7 1GiB pages
so more asm function for checking has some issues some how
hopefully its something stupid like forgetting to pop something or forgetting to save a register
well I found something wrong
but it is for the case where 1GiB pages aren't supported
when I know they are
now it works???
I'm going to boot to arch on it and just see what it says about 1GiB pages
linux claims that 5 1GiB pages are being direct mapped
maybe I'm doing the wrong cpuid
I found the mistake
so stupid
I was anding the correct bit, but testing the wrong register
and it all works
finally
I can't believe it took me that long to find the problem
I'll do some clean-up, commit this and merge to master
then it'll be IRQ time
I'm going to keep the double buffering support, because I'm sure it will be useful later
but I will get rid of the cursed 16MiB raw byte buffer in the kernel
I'm going to stick with the do while based assert as it just seems better
and also works for both C and C++
and it is merged
I'm starting to wonder whether or not I should do the IRQ system now
I want to have full APIC support, but that requires the ACPI tables
and if I want to use uACPI for that and not just some temporary crappy tables, I need a VMM and heap
so maybe I should just start the VMM
the whole reason I wanted to make the IRQ system and scheduler first was so the scheduler doesn't use the heap or VMM
another bonus of doing the scheduler first is that I'm forced to make the VMM thread safe
I'm going to think about all this for a bit
damn nice work, congrats on the fix.
you dont need uacpi for parsing the tables, especially for apic stuff. You can deal with them in-place.
also you dont need to include 40kloc for parsing some simple structs that way too 
yeah
since I'm lazy, I'm just asserting that the XSDT exists
and not bothering with RSDT
its only a handful of extra lines to support the rsdt, well worth it imo
I can't be bothered to. This code won't last long anyway
so far things are going well:
I'm going to make some changes due to ACPI only guaranteeing 4 byte pointer alignment
that took way longer than it should have, but I think it works
and UBSan doesn't complain
unlike my previous kernel
had to have a short break, but now I have MADT parsing working:
that last entry looks a bit odd, so I'll look into that
that's better:
now I think I'll quickly get the legacy i8259 PICs handled
doesn't look like much, but has taken a decent amount of effort to get to this point:
I'm going to attempt to do what the SDM says and map the LAPIC registers as UC
it's going to be annoying though due to the HHDM
so if the MTRRs are set properly, I don't need to bother?
I forgot about this:
as long as the MTRR is set as UC, then regardless of what the PAT says, it should be UC
other than WC, which I only map the framebuffer as
so what I was about to do was going to be a waste of time
Yup
Which they are
They should really copy the BSP settings
ok
Or you will have very interesting corruptions
so I shouldn't realistically need to touch the MTRRs at all?
Yes
ok
this also works on real hardware
I'm not sure whether or not I want to do SMP start-up now, or wait until I have a VMM
If I do it without a VMM, I'm not going to be able to get a proper stack on the APs
so it might have to wait
I don't think I can do the IRQ system properly without a heap
it will just be a mess
so I don't think it is worth it
I think I'm just going to clean-up the current LAPIC, basic ACPI tables, and 8259 PIC code and commit it.
then tomorrow I'll start on the VMM
I forgot about the scheduler
I could attempt it
but I won't be able to make it preemptive without a timer
Even if I use the LAPIC timer, I need something to calibrate it with, which means an IRQ system
so once again, I think it is a better choice to just move on to the VMM
Ill add heapless uacpi table support soon and maybe even an option like UACPI_TABLES_ONLY
So that even most basic kernels can start with uacpi before they have anything available
And then enable proper uacpi later on
that would be quite useful
Instead of refactoring table find code later to use uacpi which is annoying
Ill definitely use it for my kernel I think
for some reason my spinlock.h was missing a copyright header
I wonder if that is a remnant from the old kernel
I'll read the working set paper a bit more, so I understand it enough to make an implementation
In a computer operating system that uses paging for virtual memory management, page replacement algorithms decide which memory pages to page out, sometimes called swap out, or write to disk, when a page of memory needs to be allocated. Page replacement happens when a requested page is not in memory (page fault) and a free page cannot be used to ...
read this too
thanks, I'll read that as well
I think I might need to make a virtual region allocator first, but I'm not sure
No I don't
But I do need a VMM heap
So I think I will just do what I did with my last kernel and have a separate VMM heap which uses PMM pages
I think I want to make it a slab allocator, but I'm not sure how many sizes I'll need
Maybe I'll start with 16, 32, 64, and 128
time to stop procrastinating and just get this done
Heap allocation should be O(1), but unsized free is looking like it could be O(n^2)
sized free is O(n)
Actually, I have a stupid but functional idea for making any free O(n)
I couldn't work on this at all yesterday because I decided to go outside for the first time in a while. I have a lot of time today though
outside? what's that?
hopefully I have finished implementing the vmm heap
time for testing
not a good start:
I'm very tempted to use the heap from my old kernel
because it worked
what I have just tried to make was a disaster
I won't use the heap from my old kernel, but I am going to attempt to make one that follows the same concept
just hopefully faster
because I'm going to try make it in a way where the whole heap doesn't need to be locked on every operation on it
I have successfully implemented allocate of a hopefully better heap
it can get to 57/100000 iterations of my test before it freezes
so stupid
I fixed a bug, now it gets to 11 before it page faults
which is worse in some ways
another bug fixed and it is back to 57
I found the bug
100 thousand iterations of the test were successful
just need to add merging of free blocks
and removal of empty chunks
wait
I just realised my free isn't doing anything
making free actually work, including merging of free blocks results in it freezing at iteration 123
I just realised why though
I'm dealing with the typical remove 1 bug, and 3 appear in its place
finally it works
after so many bugs
I forgot something, and now it is back to failing
this time something is nullptr when it shouldn't be on iteration 51
1 bug fixed, another appears in its place
I decided to add a Verify() function like in my old kernel
and it gets to iteration 368 before it fails
after improving Verify() even more, it only gets to iteration 7
which is not good
What iteration
of the test I run
Oh
Yeah oberrow needed this test
it's just a single threaded test, but later I'll make a multi-threaded one once I have SMP and a scheduler
I had one almost exactly like this
allocate is fixed finally
free is being a pain
finally it works
time for auto shrinking
which I hope works
this is definitely wrong:
i see the problem
so stupid
that is better:
after realising a had forgotten a simple continue;, it now works fully
just need to add proper reporting of the current heap memory usage areas
that will be free mem, metadata mem, used mem, and total mem
then after that, VMM heap is done for now
I'll have to do further testing later once I have SMP
and that is done
for realloc, I'm just going to take the lazy option and instead of checking if the block can expand, I'll just allocate a new one
1 million iterations of the test are successful, so I think I can say that the VMM heap is done
I guess now I have to start making a working set implementation
The problem is, I have no idea where to start
I'm starting to wonder if I need a scheduler first
ok, I'm starting to understand more
I think I need to have the concept of processes, but not necessarily a scheduler
I understand that the working set is essentially just a list of pages being used by a process
I assume a linked list will do for that
I don't understand the size/capacity stuff
I think I understand now
I know I need to set a minimum and maximum size
but how to calculate that, I don't know
I understand what they are, but not how to calculate them
Nope
A list of pages in a process that need to be swapped in for the process to run as efficiently as possible
My size field is simply the size of all pages in there
The capacity is the maximum size of the working set
Size must be <= capacity
The capacity should be constant
I did say essentially
No a list of pages used by the process is different
You would use one of the page replacement algorithms I sent to determine what pages go in the working-set
Ok
If I try look at it now, I might read it, but won't absorb any information from it
*constant for each new vmm context
I understand that
Just clearing any possible confusion
alright, time to look at page replacement algorithms again
so far, Not recently used (NRU) is looking good, but Least recently used (LRU) might be better
actually LRU is much better
an interesting method is:
Another method that requires hardware support is as follows: suppose the hardware has a 64-bit counter that is incremented at every instruction. Whenever a page is accessed, it acquires the value equal to the counter at the time of page access. Whenever a page needs to be replaced, the operating system selects the page with the lowest counter and swaps it out.
i don't think that is possible on x86_64 though
there might be other archs that can do it, but I have no clue
you probably don't need a counter that's per every insn?
i'd imagine any monotonic counter works, so on x86_64 the tsc
yeah, I guess every timer tick I can use the tsc value as the timestamp
after thinking a bit, I'm going to change the order I'm going to do things in
I'll put aside the working-set, page cache, and page replacement stuff for now
Then I'll follow this order:
- Virtual Memory Allocation
- kernel heap (same as VMM heap except will use pages from the VMA)
- IRQ system + SMP startup
- preemptive scheduler
- working-set, page cache, and page replacement stuff
the reasoning behind this is that a need a IRQ system to properly make a page cache
so I thought while I was at it, I might as well do SMP startup and start on the scheduler
as for the reasoning being the VMA and kernel heap, I want to use the heap to allocate objects for the IRQ system and thread/process states
now I need to think about how to approach virtual memory allocation
My old kernel did some weirdness with AVL trees and linked lists, but I don't know how well that actually performed
free list search was O(nlog2(n))
reserved/used list search was O(log2(n))
so it was all in logarithmic time
wait no
searching for a node in the tree was O(log2(n), and then searching for a node in the list within the tree node was O(n)
I'm not sure if I want to try do something similar in this kernel
now I think even more about the old kernel, the finding of a node of an appropriate size in the free pages tree, was horrible
first it would search the entire tree for a node of matching size, then it would like for one that is bigger
I could just use a linked list
but with how frequently virtual memory allocation occurs, that might be too slow
AVL trees have more memory overhead per node, but are so much faster
I think I'm going to try do something with AVL trees again
I could try use a RB tree, but I already have an AVL tree implementation
I might actually use the same data structures as my old kernel
because sorting free pages by size is more optimal as allocation is normally requesting pages based on size
it is rare for address-based allocation requests to occur
turns out this was my fault for not thinking about it at the time
I problem with this though is that the tree will get very fragmented
and to defragment efficiently, the tree needs to be temporarily reformed into a tree sorted by address
so maybe I need to think of other options
I can think of a way to deal with this, but it will have double the memory overhead
actually, it's not quite double
since I'm bored, I'll describe my idea
I could potentially have 2 trees, one sorted by size, other by address
the one by address doesn't need the linked list at each node
so it has an overhead of 40 bytes per region
the one by size has an overhead of 40 bytes by size, then 24 bytes for each address within those sizes
but, I just thought that I could merge the user/reserved tree with the free tree sorted by address
so no extra overhead
from the original idea
maybe that might work
there still is the problem of the free pages tree needing to be rebuilt frequently
there definitely must be a better solution
just take free bsd's tree.h which has an rb-tree
look at aging
I did briefly look at it, but not enough to understand it
What advantages does an rb-tree provide over an AVL tree?
the freebsd one is a wAVL tree
well this is my explanation of how I understood it in my code
if it helps
there isn't a great deal of difference in terms of performance characteristics
I'll look into it more when I have time. Tomorrow, I probably won't have much time
I got some time now
AllocatePages is done
and FreePages is done with no merging of free regions
I just want to test the basics first
before I attempt the mess it will be
also need to do reserve/unreserve later
had to disappear last night before I could run the test, so I'll do it now
I'm using my kernel heap test, just slightly modified
and it fails during iteration 3
with a page fault
I'm just going to get GDB ready
because I know I'll need it while debugging this at some point soon
after a couple of very stupid bugs, it gets to iteration 2468
it fails in the heap
oh no
this can't be good
I'm currently avoiding this project as I don't feel like dealing with the heap
When I feel like dealing with it, I think I'll modify my heap test a bit and rerun it
just fix your heap instead of fixing the test lmao
does the test even work when you run it under linux (with glibc)?
haven't tried
I need to improve the test so I can track down the problem
I forgot to actual check the heap object could be read to / written from freely after being allocated
you probably should
time to stop procrastinating and get back to this
looks like my suspicions about the VMM heap are true
it is dodgy
I deliberately added a line in my VMM heap test to fill each allocated buffer with 0x41
and I get a GPF
RemoveEmptyChunk chunks is responsible
wow I'm really struggling to type properly at the moment
why is there a separate function in the objdump called Heap::RemoveEmptyChunks.cold?
which a bunch of ud2s
and mov rax, QWORD PTR ds:0x0
weird
and it makes a magic call to something r12 points to?
not related to the problem though
something weird is definitely going on:
those sizes are very high
I'm going to test this in a userspace program
getting a userspace program was just annoying
not hard at all
just dumping lots of stuff into one source file
Possibly UB?
that code appears to be unreachable, but still weird that it isn't a bunch of nops
i once again spent another whole day procrastinating to avoid dealing with this
turns out the wrong sizes bug was due to me memseting the buffer at the wrong time
I've been trying to debug something for the last couple of minutes that is entirely my fault
as in, bad porting to a userspace program
I was using malloc/free when I should be using mmap/munmap to have similar conditions to kernel space
metadata seems to be getting overridden under certain conditions:
damn sounds like a heap (ha) of memory management issues ๐ฆ
are you doing a userspace port?
No
Not at this stage
Just dumped the vmm heap code plus the bitmap code into a file along with a main function to run the test
Along with some headers that are exclusive to my kernel
Maybe I'm experiencing the oberrow curse because he sent links to some resources for my VMM
lol
is this metadata stored in the heap?
it is the metadata for the heap allocated block
could it be an overrun/underrun somewhere?
I don't think so:
I can confirm that the current issue is unrelated to removal of empty chunks
have you tried logging everything at every step in alloc/free and seeing if all the details match?
maybe some bad pointer maths or alignment happening somewhere
it must be happening under a very specific condition because the test gets to 2207 iterations
I'll try adding even more logging
so my Verify() is reporting an invalid size at entry to Heap::alocate on iteration 2208
if I read that block as a void*[2], I get 16 bytes of 0x41
which tells me that the metadata for that block is being overridden somehow
what I've done in the past is put some checksum in each block of metadata and very it with each alloc/free call
maybe thats useful to you
I found out what is happening
oh?
the previous allocation is at 0x...3dc0
of 576 bytes
which means it ends at 0x...4000
but the block's header with the overridden metadata starts at 0x...3ff0
so I'm suspicious that the size of a block was wrong
hmm progress
I think I might have tracked down a condition for this to occur
still need to investigate further
its very similar to what I saw before, except no metadata corruption yet
I was correct
it works
time to re-enable removal of empty blocks
still works
alright, time to get this back into the kernel
nice
The issue was quite simple once I realised what was happening
I was forgetting to subtract the size of the metadata under 1 specific condition
haha ah its always something simple
also works in the kernel
finally I can get back to my virtual memory allocator testing
my vma test passes
how
resolving this is next
No I think it might be yours now
I hope not
you have been trying to fix heap bugs for the past 3 weeks maybe
procrastination
not as bad as my 4 or 5 months at the end of 2022, beginning of 2023
its been 11 days not 3 weeks
first started on the VMM heap on the 17th
its now the 28th
after procrastination and distractions, free region merging should be done
the code is a bit weird to restrict the amount of locking
which I just realised whilst sending this is a waste of time
sort of anyway
it needs to be locked the whole time
wait no
I'm overthinking it
no now I am
ahhhh
This looks very much initialised to me:
anyway
I get a page fault
due to a nullptr dereference
my new code is stuffing up the free tree
I found a bug
slight improvement
now I get an assert to deal with
looks like there is mismatched data between the 2 trees
I'm going to deal with this tomorrow
this is confusing me
I have tried formatting it in multiple ways, but it still says it is uninialised, when it isn't
can you show the full code?
because the insert function takes a uint64_t
right, i think this is ub though
aliasing and all that fancy stuff
idk if that stuff matters right now tho
if I just do *(uint64_t*)&data, I get a strict aliasing warning
yeah...
so I was trying to avoid that
you shouldn't do that
you are still doing that though lmao
use a union instead
or maybe you can do some std::bit_cast stuff
or std::launder, idk how all that modern c++ stuff works
but i know you can do some aliasing with that somehow
how?
union {
PageRegionData data;
uint64_t uint64_value;
} u = {.data = {...}};
map.insert(..., u.uint64_value);```
or something like that
or std::bit_cast
assuming sizeof(PageRegionData)==sizeof(uint64_t)
it does
i think you might be able to use std::bit_cast then
is that freestanding?
it's defined in <bit>
should be..?
available since c++20
in anyway you should use https://github.com/osdev0/freestnd-cxx-hdrs
unless your license prohibits you from doing that
you can also get inspired: https://github.com/osdev0/freestnd-cxx-hdrs/blob/trunk/x86_64/include/bit
it really just boils down to a compiler builtin
github is having a moment:
lmfao what the heck
I use GPLv3, so I assume it should be fine
that repo has GPLv2 and GPLv3
- The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
that would imply that you can use this code with any project that uses gpl >=2
ok
I decided just to use a union for now, but will probably use those headers at some point in the future
back to this
I have now fixed the mismatched size problem, now I am dealing with something that isn't free being in the free tree
to debug this easier, I'm trying to get lambda captures to work
I want to try do it without std::function, but have no clue how
I gave up and just decided to use a void* for extra data
after investigating further, I have determined that the initial issue I was seeing is due to part of a free region still being in the all address tree, so when I attempt an insert when the region is split, it can't happen
that region shouldn't even be in the free tree
by randomly commenting out stuff until it works, I have determined that this code is responsible:
might not be directly
could be indirectly
it might just allow it to be detected
my suspicion that there might be duplicate addresses in the tree was wrong
next I will check for overlapping entries
and I was correct
there is an overlap
all this verify stuff is just to test the allocator
once it works, I will remove the Verify()s
because they are very slow and heavy on the VMM heap
the VMM heap heavy stuff is to speed it up a little
ah type punning is UB in c++ iirc
you're only supposed to access the last member assigned to (because of lifetime stuff) so in that case that would only be allowed if you wrote to uint64_value and not data anyway.
it's UB in C too, isn't it?
so what?
I thought C allowed it?
?
so what? so dont do it lol
or do, and deal with the behaviour you get
iretq suggesting ub 
huh
union { float a; int b; } c; c.a = 1.0f;
is c.b supposed to always be 0x3f800000?
whatever the representation of 1.0f
yes
thats one of the reasons why c++ sucks for osdev
that's kinda stupid
its really stupid
unions in c++ really got left behind
there's all kinds of edge cases with them
Yeah
C++ logic: type punning via unions is UB but bit_cast isn't.
makes total sense !
__builtin_bit_cast my beloved
why would bit cast be ub
do u know how it works?
T val;
memcpy(&val, src, sizeof(val));
return val;
the question you should be asking is: why would type punning be UB
because it makes the modifications via the type punned value escape the compilers ability to recognize modifications
and prevents a lot of optimizations
and its an uncommon thing so its good to optimize it
*except that it's constexpr
you sure?
sounds like a violation of strict aliasing to me
Yes
Not in c
honestly this is rather cursed
it means that #define foo(x) (*&(x)) is not equivalent to #define foo(x) (x)
i think?
Probably because no constructors or object lifetimes as strict as c++
Why?
oh wait maybe it is
what the fuck is this
This explains it more I think
So whats the point of unions if their main purpose is U.B.
sharing storage
e.g. tagged unions
so how do you do type punning in C++?
reinterpret cast?
std::bit_cast
or some sort of reinterpret_cast, but idk
bit_cast works well for simple types like float/int/double
but i don't think you'd want to do that on a big ass struct for example
i'm not a C++ person tbh so i don't have the answers
theres a new type of helper in c++23 i think
Memcpy if you want to follow their crazy rules
Now I need to find out why
I'm back at school now, so I don't have as much time
after looking into this overlap further, I have discovered that the smaller region is the end of larger one
both of which seem to have a valid reason to exist, but not quite sure
I'm going to check for overlaps in the all address tree as well
to attempt to find the source of the issue
I caught something a lot earlier in the all address tree
this math doesn't add up
supposedly the bad region has a size of 570 pages when it should be 480
which would mean no overlap
wait
there is a use after free here
there we go
seems to be running well now
taking ages because of the checks
running at ~100 iterations per second
its down to 20/s now
too slow
I'll disable the overlap checking
and it works
with kvm back on, all 100k iterations, no overlap checking takes less than a second
1M takes a few seconds
10M took about 45-60 seconds
with no verify, it only takes 10-15 seconds
not a good way of benchmarking, but can be used a guide that it is fairly fast, but does slow down when it has a lot more regions to deal with
not directly related, but I just built my kernel with -O3 instead of -O2 for the first time and it works perfectly fine
not a noticible performance impact
I leave it at -O2 to make debugging a little easier
not really important, but all this new stuff does work on real hardware
with and without ubsan
Need to improve the allocator a bit, such as address-based allocation
which will hopefully include the ability to reserve regions
then I should be able to move on to some kind of PageManager or PageAllocator
need to think about the naming a bit
the virtual memory region allocator is currently called the VirtualMemoryAllocator
allocating at a specific address is now supported
need to do some testing, but hopefully it works
and it doesn't...
overlap on the first allocation
stupid mistake
I was subtracting and address from another forgetting that I need to divide by PAGE_SIZE after
gets to iteration 271 now
I forgot to mention that it is pretty much the same test as before, except some slight modifications so that a random address which isn't already in use is selected
after some minor bugs, it seems to work
I've got the test running 100k iterations, full verify with overlap checking
so it might take a while
oh and I just realised ubsan is on
its been running 5 or so minutes and is at 56k, so I'll leave it to finish
even though it is only single threaded, my CPU is slowly warming up my room
2 minutes later, and it is at ~58k
so it slows down with the more regions to work with
its almost done
99k
only ~1k left
it finished
I forgot the finish message goes to the screen not the debug console, so I didn't realise until I heard the CPU fan suddenly go a lot quieter
the height of the freeSizeTree is 9
and 10 for the all address tree
I just quickly ran it again with no verify, and freed all the allocated blocks at the end, and the height of both trees is 1
I starting to wonder if frees were dodgy after seeing thoses sizes, but this test confirms merges are working perfectly well
due to this test taking ages, I'm out of time today
tomorrow I'll do reserving, and proper tracking of free/used/reserved page counts
then the allocator should be done
cool, thats some serious testing
yeah, I don't want to have the same issues as my last kernel where many things were going wrong at once, and it was a massive pain to debug
and this time I'm trying to make everything with multithreading support in mind
yeah I respect it, might be the most thorough testing in this server besides uacpi
I'm interested to see how uacpi will go running in this kernel once I'm ready to add support for it
If I really wanted to, I could implement it after the kernel heap, and replace my temporary ACPI table parsing
but there isn't much point
fair enough
uStressTest
Uacpi has heapless table parsing support now fyi
That isn't my problem with doing it earlier, but good to know
reserve/unreserve should be done
reserve is very similar to allocating at a specific address and unreserve is essentially free
I'm going to run a similar test as before. I'll take out the overlap detection because I really don't feel like waiting another 35 minutes for a simple test
and it works
nice
time for proper tracking of reserved/free/used page counts
wait, that was already happening
it isn't happening properly
The code it there, but at the end when I check everything, the counts are wrong
found the bug
the pageCount argument gets updated in certain situations, but it was still being used at the end to update the counts
so instead, I have an initialPageCount, that is set at entry to the functions, which is const so it can't be modified
and the VirtualMemoryAllocator is done
I'll do some clean-up, get this properly setup, and commit it
clean-up is done, there is some documentation on how it operates for allocation and freeing, but not how reserve/unreserve works
Now I need to set it up properly
I think I'm going to have a global instance, which covers all of address space
and then a kernel allocator for the last 2GiB of address space for now
later, each userspace process will have its own instance ranging from the beginning of the 2nd page (0x1000) to the beginning of non-canoncial address space (1<<48 for 4 level paging)
then the HHDM starts at 0xFFFF800000000000 (also the end of non-canonical address space for 4-level paging)
not sure how long that should go for
I guess to 0xFFFF900000000000 should be plenty
that's 1<<44 of space
reuse the linux memory map
I don't know what it is
never looked it up
thanks
ffff888000000000 | -119.5 TB | ffffc87fffffffff | 64 TB | direct mapping of all physical memory (page_offset_base)
ffffc90000000000 | -55 TB | ffffe8ffffffffff | 32 TB | vmalloc/ioremap space (vmalloc_base)
nevermind
just saw that
I won't copy this exactly, but I'll use it as a rough guide
I don't understand the point of all the holes, especially the 8TB one at the beginning of the second part of canonical address space
who knows
I think there is a mistake in the non-canonical space for 5 level tables
and there is more space allocated for physical memory than what is supported
the maximum theoretical physical memory size is 4PiB (1<<52), but the linux map allocates 32PiB (1<<55)
this mistake doesn't exist in the raw file
After some thought, I'm going to extend this to 1<<46
which means HHDM goes to 0xFFFFC00000000000
which is 64TiB of maximum theoretical physical mapping space
with current computers, that is way more than what is needed
for the majority of computers anyway
then I think until 0xFFFFF00000000000 should be for virtual memory allocation
then from there until 0xFFFFFFFF80000000 is unused
then from there until the end of address space is for the kernel's ELF segments and any modules in the future
this code segment should hopefully show what I mean by all this:
its a little messy due to those functions taking a start address and page count
I'll write proper docs for this somewhere
couldn't work on this at all yesterday
Just wrote these docs in the arch specific paging init header file:
wait thats not very consistent
'arch specific', are you planning to go multi arch?
thats better:
eventually yes
not exactly sure, but the only non-x86 hardware I have is aarch64 and arm-v7
I have 1 raspberry pi 3B+ 2GB, 2 raspberry pi 4B 4GBs, an aarch64 phone (will not be used for any form of testing though), and an old arm-v7 tablet
testing on your phone would be bold lol
yeah, won't be testing on my phone, just listing devices
I just realised that there is actually only 256TiB of usable virtual memory with 4 level paging
I don't know why that came as a surprise
everything is cleaned up and ready to be committed
alright, time to move on to the PageManager
I've implemented some PageTable abstraction in a way that doesn't use any ifdefs
I liked what I did in my old kernel page manager where I had an AVL tree of regions, sorted by address
So I'm going to do that again
I've just implemented a temporary OOM handler which for now just calls PANIC
having a proper function early is better than later removing random PANICs in the PMM
I now have a basic PageManager implementation
I will need to expand on it a bit to support lazy physical allocation and eventually shared mapping
I'm scared to test the current code
because every time I test something, I get so many bugs
stupid nvidia driver keeps making vscode freeze
not a good start to testing:
why is something calling kmalloc
ohhh
so stupid
forgot to set the AVLTree into VMM mode
now I get a GPF
forgot to mention that it is pretty much the same test I've been using for everything else
it's in the PMM
specifically FreePage
looks like a dodgy address is being parsed to that function
it is called by x86_64_UnmapPage
oh I see the issue
forgot to convert a HHDM address back to a physical address
after only 18k iterations I get an OOM
that shouldn't be happening
maybe stuff isn't being freed properly
this time it was 30k
free is definitely working
maybe there just isn't enough physical memory for this test
I'll have to modify the test a bit to store the size of each allocation
yeah, I can confirm that I need to scale down the maximum allocation size or increase the RAM size
I think I'll go with just halving the maximum allocation size
that still wasn't enough to make it through the whole test
I'll also up the RAM size I guess
and it was successful
it does seem to slow down a bit when there is a lot of allocations, but realistically, I doubt that 1 instance will constantly have 150-200+ allocations at once
it is most likely the page mapping slowing things down
but once again, realistically, there isn't going to be constant 200-500 page allocations
I will eventually add larger page support to the PMM and VMM
which will help with that possibility
I have it implemented in the PageTable, but not the PMM, VirtualMemoryAllocator or PageManager
I won't be doing lazy physical allocation until my kernel has the concept of processes
just so I can do it properly
instead of hacking together something messy now
I'm going to clean this up, get it committed then I guess I'll do the kernel heap?
which will be another instance of the HeapAllocator, so no actual testing is needed
alright, kernel heap time
the kernel heap is fast
with KVM on, a looked away for a couple of seconds waiting for OVMF to load, and it had done 100k iterations
no verify, but I have already thoroughly tested the HeapAllocator, so there isn't much point
I'll quickly do an eternal heap, then this will be done
and it is done
I went with a size of 2MiB or 512 pages
which is way more than what will probably ever be needed, but just to be safe
IRQ system was next on my list
but I might do kernel symbol parsing first
idk
I'll think about it
I just realised something
In my old kernel, it took me 7 months to get close to this point. In my current kernel, its taken ~6 weeks
I started the rewrite on the 17th of august
Its been 50 days
Or ~7 weeks
you can also return NULL/nullptr and make it the callers problem.
That's what I was doing
ah, not a fan of that idea?
No
Makes more sense to me to handle it the same in all situations, instead of leaving the caller responsible
I disagree, but I can see the logic there
also cool to see more testing of your kernel
I was busy during the week || definitely not just watching stuff on disney+ ||
I was going to work on this today, but I managed to brick my windows install whilst trying to move it to another drive, so I'm clean installing.
Luckily arch was unaffected
alright, time to return
I want to make more debugging information available to the user during a kernel panic
the first step is symbol parsing
in my old kernel I tried to simplify the symbol table before runtime to make it easier on the kernel
the simplified format was just this:
- raw address
- 0x00
- name
- 0x00
which worked alright, but ubsan didn't like it
due to the unaligned 8-byte reads
have you thought about parsing the elf symbol table?
the only downside is you'll get mangled names
i only want demangled names, so I don't think I like that option
fair
I have a freestanding version of libiberty's demangle code, but its like 4K loc
reminds me I need to finish cleaning that up
so what are you thinking, store the symbol info at the end of the kernel?
no, a limine kernel module
I just made a simple 73 loc C++ (could be C) file which just removes the extra space and character between the address and symbol name in the nm output
I might also add duplicate symbol removal
I'll make it so it uses the last symbol at an address
actually nevermind
too much effort
I probably could make it a lot smaller by using more builtin C functions, but doesn't really matter
I want to try make a hash map implementation for this, but I don't know how they are supposed to work
I know that I could create a super simple one which uses a linked list, but I feel like that isn't the proper way
ordered hash map that is
It appears that the libstdc++ in GCC uses a rb tree
I'll just use an AVL tree since I already have an implementation
Actually, I wont bother with a hash map. I'll just use an AVL tree directly
this doesn't look right:
all of the symbols seem to be added to the table correctly
oh wait
stupid mistake
I really should add some attribute to my printf so GCC warns me about bad args
there we go:
even better:
nice
first part is going to be SMP
then proper IPIs
then I/O APIC stuff
I might experiment with going from real mode straight to long mode
It seems like the PML4 has to be under 4GiB so I can actually load cr3
I think I might have a working AP trampoline
it triple faults
not a good start
I added -no-reboot and -no-shutdown, but it still just reboots
that's weird
it dies on the INIT
doesn't even do an SIPI
it seems to be trying to do it on the BSP
I don't know why though
I see the problem
for some stupid reason, the initial LAPIC code was assuming the BSPs LAPIC id was 0
so when I changed the default to be 0xFF instead of 0, it broke everything
so I should be getting the BSPs ID from CPUID I believe
it gets further now
the BSP somehow tries to execute 0xfec0000000000c01
with no stack trace
not sure how that happened
oh I see
now it appears like an INIT and SIPI are sent, but nothing happens
and there is a bogus MADT entry
I forgot to add a second CPU
it still doesn't work
I remember something about there needing to be a delay between the INIT and SIPI
but I don't have a timer
I tried a 100 million iteration for loop in between the INIT level desassert and SIPI as a replacement just to test
and nothing changes
even a 1 billion iteration for loop doesn't do anything
For timers, I guess I'll do PIT and HPET
Since QEMU doesn't put a PIT device in the ACPI namespace, even though it exists, I could probably just assume its existence
I remember someone saying that linux does that
fixed this problem
For some reason I was assuming that the length of a MADT entry was fixed
isn't there some flag in the FADT
that defines that
Idk
fadt.iapc_boot_arch
bit something
ACPI_IA_PC_LEGACY_DEVS
and you can further detect the ps/2 controller by checking the ACPI_IA_PC_8042 flag
#define ACPI_IA_PC_LEGACY_DEVS (1 << 0)
#define ACPI_IA_PC_8042 (1 << 1)
#define ACPI_IA_PC_NO_VGA (1 << 2)
#define ACPI_IA_PC_NO_MSI (1 << 3)
#define ACPI_IA_PC_NO_PCIE_ASPM (1 << 4)
#define ACPI_IA_PC_NO_CMOS_RTC (1 << 5)```
these are all the flags
and the flags in the acpi spec
Ok
you're using uacpi right? it has those flags defined in acpi.h
linux uses PIT unconditionally, it doesnt check those tho
not yet, but I think I might start using it before I do the timer interface
so, I'll do lapic improvements, and I/O APIC, commit it
Then I'll do a uacpi port
then timer interface
just discovered a bug where the BSP's LAPIC isn't actually being enabled
I might actually leave the I/O APIC until after the uacpi port
I won't commit these LAPIC improvements on there own
I'll just start on using uacpi again
and all the functions that I can implement are implemented
just need to migrate the existing ACPI code to use uACPI instead
uacpi init has changed a bit since I last used it
I don't know how to set the log level anymore
I think I need to call uacpi_context_set_log_level before uacpi_initialize?
I don't have mutexs yet, so I had to do this to get uacpi to actually initialise:
this is done now
this isn't good:
maybe I should have done some more testing on the kernel heap
I tried running the heap test on its own, and I get an OOM
I think something is off here...
there shouldn't be an OOM:
there is plenty of free memory
something is very wrong:
I'll start with the heap issue and then go from there
because it could be causing this
I just checked the VMM heap, and it is unaffected, which means that the page manager could be responsible
yep, it appears to be
I'll need to test a bit more to confirm this though
ohhh
I forgot that the test needs more than the normal 256MiB of RAM
the page manager is fine
I couldn't work on this at all yesterday due to being stuck at school for 14 hours....
After I checked that the page manager was fine, I went back to testing the kernel heap
It seems to cause an OOM very fast
So I'm suspicious that something is not freeing properly
Damn school days have lengthened since I attended
Wait wtf
I had to stay after school to do stuff for a music concert
time to sort out this OOM
my first thought was that bad regions were being given to PageManager::FreePages, but they aren't
this isn't directly related, but for some stupid reason, the kernel .text section is being mapped as Read/Write/Execute
only 194 virtual pages are allocated in kernel address space
so where are all the physical pages disappearing to
I wonder how big the VMM heap is
only 36864 bytes
the used memory variable in both the VMM and kernel heaps is very wrong
the free and metadata variables in both heaps are also wrong
there must be some case(s) where I forgot to add/subtract from them
I have found some incorrect locking, but no bad addition/subtraction
well I found it
numbers are still wrong, but not as bad
the used mem for the VMM heap is -258192
Time to continue debugging this
I found a tiny hidden bug
it occurs only a certain edge case where a chunk has a size of precisely size + sizeof(BlockHeader)
chunk with one block that is
I meant that the only block in a chunk has precisely that size
for some reason the next chunk isn't being set
it was detected by the total number of free memory in the list not matching the variable
I just found another mismatch
this time in free
this one is where the recently freed block has become the last block in a chunk
but is not exactly after the old last free block in the chunk
this basically confirms that something is disappearing from the list:
at entry 4032 bytes are free
at exit, 4928 bytes are reported and 3888 are actually
it is a free, so that number shouldn't decrease
the reported appears to be correct
but the actual is wrong
so something is being taken out for case 2.2
this is the problematic code:
wait
I see the problem
current must be less than header
so prev isn't relevant
sort of anyway
wow the simple fix I did made the test get a lot further
there is still a bug somewhere though
looks like it is in allocate this time
the issue appears to be a block's size exceeding the size of its chunk
oh no
that block shouldn't even be in that chunk
looks like the isLastBlock member variable isn't being set properly
it was set correctly, just weirdness was going on
which is now fixed
but I'm back to getting an OOM
I don't understand how or why it OOMs
the kernel heap is 192512 bytes, and the VMM heap is 12288 bytes
the kernel heap has 139296 bytes of used memory
the test is using 25040 of that
if that is correct, kernel symbols are using 114256 bytes
which might be true
there are ~1400 symbols
so that is definitely possible
but why is it running out of physical memory
it should have 1GiB
there is 0x2f pages mapped in allocatable kernel address space
that's only 188KiB
so where is all the physical memory disappearing to
I think there might be a bug in the PMM
those numbers are wrong
I think
the free mem one anyway
looks like I might be testing the PMM again
I think that 100 million test iterations might be too many...
oh well
its done ~60 million, so I might stop it there
it was only single page allocation/free
I'll try doing multi-page
I'll do a maximum of 256 pages
a maximum of 1024 regions can be allocated at once
giving a maximum theoretical usage of exactly 1GiB
but, it shouldn't get too close to that
I'm going to disable OOM detection for this and just return nullptr
and it just works
I've tried overwriting the first part of the page region so that any metadata is unusable
and it still works
I can't reproduce the issue
the test finally finished
so I have no idea what the problem is
all these stupid memory management bugs are really annoying me
my motivation for this is really low at the moment
I might work on some other projects for a bit until I feel motivated again
fair enough, no fun in burning out - anything in particular you want to work on?
I'm going back to working on my custom cpu architecture
I think it might be time I return
I need to work out why the PMM only stuffs up when running the kernel heap test, and not when running it's test
I've changed the PMM test to only allocate 1 page at a time, but with a much higher maximum number of allocations
i modified it even further so that frees only happen 25% of the time
might need to modify that a bit though
I changed it to 40%, and its slightly better
with 50% of each allocate and free, not that many pages would be mapped at once, so it isn't similar to the heap test
40% being free still isn't that good either
45% is better
I can't go for 50% though
i'm just going to run the heap test again
what
the test passes?
how
this is really strange
kvm was on