tomorrow i think i will re-factor how arguments are passed to a program (they'll be separated by null instead of spaces), add a blob (crt0.o) linked into every executable that parses all the arguments and passes them off to main() (right now everything starts from, well, _start), and write a small program that outputs something to the terminal, and then try and run it from FullScreenTerminal (i might also rename FST)
#Boron (not vibecoded)
1 messages · Page 4 of 1
A Windows wrapper for this would be pretty cool, although I'm not sure if it'd make development faster
Nah
The build and run cycle is already pretty fast
I figured
yooooo 🤩
Well, the nextstep resemblance was intentional
What font is that btw
the sunOS font
I see
I think it's called Gallant
I'm thinking of using a different font and that one looks really good
It'd look great imo
fine w me
I can send it to you, just a sec
or maybe I can find a smaller similar font
you can send it here
also what's "the NYX Operating System"?
and is the similarity to NixOS intended
what happened to it
i work on another project now
i see
nyx is a wordplay on the greek goddess and unix
and i had a bunch of greek names so like the support library is atlas
i was thinking of a name for the operating system distribution because boron is kinda bad for an OS name
just realized the TL in atlas could mean template library 
maybe find things made in boron or something?
why the name boron actually?
it's described in the readme, but basically in 2023 i needed a new name for the kernel and i wanted it to be some chemical element. so after some deliberation I landed on boron, and went with it because:
- it's my 5th OS project (nanoshell I - private, nanoshell II - private, nanoshell III - the released one, nanoshell64 - the abandoned one, and finally boron). and boron has the atomic number of 5 (H, He, Li, Be, B, C, N, O, F, etc)
- boron could also stand for "born outofthe remnants of nanoshell64"
as a fun fact i've been partaking in osdev in some capacity since 2019!
kinda sounds like moron tho 
i've had this pointed out to me
even back in 2023
i've had a gnawing thought in my mind about borrowing a code name from iOS (namely, iPhone OS 1.1 which is codenamed Snowbird)
or iPhone OS 1.0 which is codenamed Alpine
AlpineOS sounds cool but I fear it's taken by Alpine Linux
CarpathianOS is kinda a weird name for an OS
I try to follow a theme kinda, like atm my project with ethan was originally named dufay (after the composer), but I thought of a funny name instead, 'bach' (sounds like mach), so we pivoted to that and so the OS is named fuga and the kernel bach
I also noticed that the kernel for nyx OS was called "gaia"
which is another greek mythology thing
sure
gaia is the earth, nyx is the moon, atlas is the one who is supporting the skies
kernel, OS, support library

dont have to suffix it with OS tho
it doesnt look that good imo
only works for macOS
indeed i could not use the OS suffix
i'm still thinking
if a name comes up i will announce it and rename the thread
idk what kinda name it could be
are there mountains made of boron?
(what even is boron?)
boron is a chemical element
specifically a metal
name it borax
boron is one of the chemical elements used for doping semiconductor
there's borate mining in the mojave desert, name it mojave 
so your CPU, GPU, RAM etc might have boron in them!
(if it's old enough)
nevermind looks like it was phased out in the 70s
you could also name the OS boron and find something else for the kernel?
i think the kernel could still be named boron
chatgpt
boreal doesnt sound bad
(remove the OS)
everest is pretty cool as well
yeah it's hard to find a name you like
I'm terrible with names which is why all my projects' names I am not proud of
"one of the hardest problems in programming is naming things" and they're damn right
I'm sure that's not the original intent of the phrase but in this case it sure is true
oh i completely forgot to send the font @glass heart
here it is
I also quite like the https://github.com/fcambus/spleen font but idk if that'll fit with your aesthetic
uhhh
why did this shit crash
stack overflow?? but why?
cool!
so hold on
WHY does it go into the other thing instead of the fucking pipe write function
I CREATED A PIPE OBJECT
why are these different, wtf
well i fixed it
now the userspace terminal works
(this is still not an external process but thats fine, i'll do it later.)
This turns out to have been a use after free
banger commits today... and now i need to go to class
i finished my homework like 2 hrs ago and now i shall continue for a bit
its 10:40pm rn so i won't be able to do much, but whatever
now with an external process!
but letting it run amok (removing sleeps) causes it to break. oops.
in other news nanoshell (my previous OS) just reached 900 commits because i forgot to push a UTF-8 parsing fix
Cool progress
tysm! btw i heard of a debate you had with will about putting the full screen terminal in userspace
and he didnt want to do it for memory saving reasons
do you remember?
if i disable this assertion, it keeps going, but it randomly drops some data
and crashes again with a different assertion
so the error happens on both sides
Hmm not sure I remember anything like that
looks like it wasnt that dramatic, i think i fixed it
https://github.com/iProgramMC/Boron/compare/a89336475f7d...0ba1ade221e0 i think its stable now
crazy idea
enabling FPU/SSE/SSE2 extensions on demand per user process
or even per thread
Not useful, all 64 bit apps use it unconditionally, even for simple stuff like zeroing large structs
Also if they detect them as disabled they won't use them so u slow everything down
FPU and SSE are expected of an x86_64 system
so if you enable them also enable AVX or whatever else
also isnt the increased cost of context switching for all programs worse than allowing some programs not to have it
Also how do they even detect them
Try the instruction out and then see if you get a signal?
Because if so the problem gets remedied on the first invalid opcode and then that same opcode magically works and it's happy
It's like it was never disabled
cpuid is available in ring 3
Even better
Because you can just query cpuid
Instead of "detecting" that the instructions are disabled
and no, I don't believe in the lie that the CPUID flags pertaining to ISA extensions are controlled by the CR bits that enable said ISA extnesions
because CPUID is constant in that regard
to a certain point, but since sse is used by basically everything you should at least be saving and restoring those when changing threads that use them (user threads if you dont use sse in the kernel), because the #NM exception can be quite costly compared to doing it up front, and its basically guaranteed to happen.
I dont have any proper numbers, before you ask
what's #NM
i thought you got #UD for disabled ISA extensions
#NM is intended for lazy fpu/vector save/restore
also the idea is that that one exception is enough to enable SSE for the entire duration of the program
including all new threads
so the cost is minimal and will only be paid once per process
based on whether CR0.TS is set/clear fpu and vector ops will either have their normal behaviour or fire an #NM exception.
even better because I don't have to try and decode the opcode on a #UD
just wait for a #NM
anyway I guess this is more useful on 32-bit when it's not guaranteed that a program will use MMX/SSE instructions
yeah for 32 bit it makes sense
for 64 bit just do it up front, since SSE is assumed to exist. Maybe there's some benefit to doing it with ymm/zmm registers if you're really concerned with memory usage.
I'm more concerned with context switching time than anything
if you dont do it when context switching, you might incur an interrupt and then still have to do it.
this is literally documented in the sdm
the bits used to detect whether it's usable in userspace are a bitwise and of what's supported and what's enabled
what
https://youtu.be/CTUMNtKQLl8?t=43m42s but in this video matt describes that the CPU tells .net runtime that sse2 exists but windows 95 doesn't support it
â–¶VOTE for my NEXT PROJECT: https://www.patreon.com/posts/march-2024-poll-101363953
â–¶dotnet9x on GitHub: https://github.com/itsmattkc/dotnet9x
â–¶FOLLOW on Twitter: https://twitter.com/itsmattkc
â–¶FOLLOW on Twitch: https://twitch.tv/mattkclive
â–¶FOLLOW on Instagram: https://instagram.com/itsmattkc
â–¶SUPPORT on Patreon: https://patreon.com...
userspace is capable of checking the bits intended for kernel use - that's probably what happened there - but they're not supposed to when determining what instructions are usable
So it's not possible to enable these features on the fly upon the first instruction that uses them, without userspace programs potentially noticing they're disabled on init and never using them?
well
beginning of the other end of the terminal process
i still need to add line discipline
then i should probably make a setup so that the fullscreen terminal and a future serial terminal can share most of the code (because they should be able to)
also the latency is so much higher on SMP and I don't know why
I'll want to figure that out at some point
the highest latency I've seen was about 7.5M ticks and it hangs around 500-600K ticks on 1 core
anyway im making real progress and soon i think i will start working on the mlibc port again
short summary of the recent changes
- FullScreenTerminal renamed to VideoTerminal but actually I'm considering another rename to simply ScreenTerminal
- init.exe now reads a configuration file and assembles some environment variables
- initial terminal and interactive shell are configurable from said configuration file
- VideoTerminal takes commandline parameters for specifying keyboard and frame buffer device names
- commandline parameters used to be just one string but became a null-character-separated list
and more
oh and a kind of unusual thing, I pass arguments into the entry point of an ELF from libboron.so. this will be unexpected by applications linked against mlibc, so only libboron-interpreted programs will be subjected to such problems
Would be nice if you report the bug on their github
@noble sphinxso the problem is I don't know how to make up a minimal repro
it seems to work here
It reminds me of the NeXtStep console when you boot it up
well yeah
Oh lmao i didn't see this
i should work on it again but i got kinda bored
Fair enough
having to implement terminal line discipline in the kernel to allow for posix compatibility seems like a pain
because iirc posix mandates that write()s to a cooked mode terminal go through line discipline
and things like xterm are literally simulating actual dumb terminals and shit like that
and dumb terminals can only show character and send character from keyboard
and sometimes format (vt100)
im thinking of removing the FCB rwlock and letting filesystems handle their own locking in a possibly more versatile way
well thats one edge case i didn't think of
so either I move file offset handling back in the kernel (and add a flag to ignore the file offset... and dont create separate file objects on handle inheritance) or somehow communicate file offsets between processes
although the problem was already solved when appending
also i figured out how i could implement arbitrary mountpoints
the way i could do it is that each FCB has a boolean variable called "IsMountPoint", and this FCB will probably be pinned (so it cannot be freed) (but i'd need to figure out when i should assign it to true in the first place)
if IsMountPoint is true, refer to the linked FCB which is the root of another filesystem
btw you handle appending by passing a flag to read/write, right? if so, how do you plan on handling fcntl(F_SETFL), which can change O_APPEND and is supposed to do so for the file description (i.e. cross-process)
yes, by changing whether or not I pass the append flag.
when they turn off the flag, set the current file offset to the size of the file
but cross process?
yeah, no. making the file object in the kernel contain the file offset would make that work
so the upcoming changes would be:
- do not duplicate file offsets when their handles are inherited (only on dup())
- move file offset handling back into the kernel, allow specification of the file offset only if a flag is (or is not) specified
- handle append mode in the kernel too?
specifically in posix these fd flags can be changed cross process
btw dup() duplicates the file description right?
so they'll have two separate file offsets
oh wtf
so they won't have two separate file offsets
the only ways to get a new file description in posix are open(), pipe(), socket(), etc
correct
i see
so i guess if i want posix compatibility i'll have to compromise in some ways in the kernel
which i don't like, but i'd rather do that than write complex IPC based machinery which could fail for no reason
yeah it's not ideal
managarm does that because of their whole microkernel thing
but i wont
this wont stop me from achieving my other goals with this OS though, so i think it's okay
what about adding a deep/shallow flag to your duplicate handle thingy
deep = you copy the whole structure and shallow = you get a reference to it
well the duplication function would normally duplicate the handle and not the object
but file objects were an exception for some reason
ahh
in the case of posix compatibility though this isn't going to be done
the file object will be able to have multiple references
so basically file objects in boron (and NT and mintia and stuff) are equivalent to file descriptions on linux/etc
and FCBs are equivalent to vnodes
and handles are somewhat equivalent to file descriptors. but in my design at least they don't have all the properties of file descriptors
for example standard I/O handles are not handles 0, 1, and 2
they might be handles 4, 8, and 12, or they might not
but the posix layer inside mlibc will handle that discrepancy because the values of the standard I/O handles are stored in the PEB (process environment block)
i think what I'll do is have a separate posix fd to handle table
that's what i was gonna do as well.
so my OS won't expose a POSIX API right off of the bat, but it'll have all the components to emulate POSIX compatibility, when I finish that part
i think i will use the file object's offset IF a flag is specified. because the behavior in that case is to acquire a mutex guarding the offset lock on the file object
however this isn't necessary if the offset is pre-specified
Another proposed redesign would be using sections for absolutely everything, anonymous memory or file backed memory
This would imitate NT a bit closer but I think it would simplify the code a lot
It'd allow CoW'd file pages to be paged out through the same codepath as anonymous pages
The system call interface would still support mapping file objects directly, and anonymous memory directly. No changes will happen there
But new APIs will be added to allocate named sections/section objects, which will be useful in the implementation of shmget()/shmat()/shmdt() APIs in boron
wtf is this codegen ðŸ˜
(GCC this time)
????
but it generates properly in compiler explorer ðŸ˜
what's the actual compiler output (pre-assembler)
aka the output with -S
i'll check
and heck
the .o file itself has invalid asm too
right well the assembly itself looks fine
what's the output of objdump -dr <.o file>
shared above but fine
ida formats it weirdly
objdump does a much better job for this kind of stuff
the assembler is just using a completely incorrect relocation type
it's using GOTOFF64 (64-bit offset between GOT start and GOT slot) instead of GOTPCRELX
so _GLOBAL_OFFSET_TABLE_@GOTPCREL(%rip) is broken with the gnu assembler, great!
sounds like you need to mark the GOT as hidden in C so that the compiler generates something like lea _GLOBAL_OFFSET_TABLE_(%rip), %... instead
as a fun fact I DID have it as hidden and it still doesnt work
although here it does seem to be using GOTPCREL
for that one you seem to have marked _DYNAMIC as hidden but not _GLOBAL_OFFSET_TABLE_
here i see GOTPCRELX
you're right my bad
so ts one looks fine
let me see the whole binary
yeah looks like it works
then i'll also see if it works on 32-bit because i had issues in this very place on 32-bit too
the not marking as global thing is was the workaround for the clang 32 bit issue right?
and the error has returned
yeah not marking the got as hidden
this is the reason why i just avoid referencing _GLOBAL_OFFSET_TABLE_ and _DYNAMIC from C at all
https://github.com/managarm/mlibc/blob/05bac8bca31f9fd40d7a6c10907632f179feca3c/options/rtld/generic/main.cpp#L32 mlibc seems to work fine though ðŸ˜
but it's C++
so i guess it somehow makes a difference?
i highly doubt that makes a difference considering the error is in llvm's integrated assembler which is below even llvm ir at which point input language is barely known
maybe nobody has tried compiling mlibc i686 with clang before?
btw as for what the bad instructions with the gnu assembler are, the erroneous relocation it's using is a 64 bit one, and it's a 32 bit field, so the upper 32 bits spill over and become the next instructions
ðŸ˜
so this has been implemented
the shared file offset as well as not duplicating file objects when their handle is duplicated/inherited
handling append mode wouldn't be hard either
(actually I kinda already do that :P)
OKAY
im getting line discipline working
no echo though
??
whys it going down
im pressing the left arrow
oh right
wtf happened here
aliens took control of your OS
Looks more like vikings did.
this line doesnt exist anymore btw
because apparently cooked mode doesnt support in-line editing
@glass heart https://github.com/iProgramMC/Boron/blob/master/drivers/test/source/fworktst.c#L200 just wanted to point out this may result in UB (negative shift)
i dont see any shift
INT_TO_FP is a shift
it's a positive shift left
SinTable[angle] may have a negative value
and?
ubsan complains 
well either you add a no_sanitize attribute or you find a way around using negative numbers, I'm probably gonna do the former 
i don't use ubsan so this isn't a problem for me
bruh
#define INT_TO_FP(Int) ((FixedPoint)((uint64_t)(Int) << FIXED_POINT))
looks like this is a fix
i wouldnt ignore UB just because you dont use ubsan
i guess my initrd is too big?
(its like 20mb now)
i guess i don't support symlinks
also it crashes in the slab allocator
thats not good news 💔
ok, fk it, i'll delay ncurses+bash for later
okay
time to run a test executable with mlibc
hmm so it fails to load test
why
thats one of them yeah
damn
so it expects this
but wtf why is it a page fault
so why the fuck didnt it get mapped
it said initialized data
DbgPrint("Checking Interpreter at address: %p\n", Interpreter);
if (strcmp(Interpreter, "libboron.so") == 0) ...
what does this print
😂
i'm in the address space of the SPAWNING process
not the SPAWNED process
duh
thats why it didn't work... and that's why it "worked" with normal applications
broke 900 kernel builds!
and this is the first time it tries to load ld.so!!
oh god
i gotta implement SSE/FPU support
do i need to save FPU state in addition to SSE state or can I just save SSE state
you have to use xsave and xload
save the context of the old thread before switching
and load the new one
so I don't need to save FPU state
just SSE state
ye
do I need to check for xsave and use that, or can I just use fxsave and fxrstor
you have to check like i showd in the screen
xsave will save both fpu and sse
you have to enable it like this thi
No need to check for xsave support, mlibc doesn't require AVX
You're probably not gonna want AVX for a long time
they showed that their initrd needed it XD
here
He probably compiled stuff with AVX by accident since his cpu probably has it
still good to have it tbh
makes stuff faster
still needs xsave
lol
i'll add support for it for now
the code i showed you is all you need btw
Lol
gn
turns out that i don't need xsave and that is indeed SSE
i just enabled fxsave and SSE and it works
now I get this
and here it is
well
why
why not just use it anyway tho
it's just an if
i cbf to do it
because it's not entirely clear how much memory it takes up
(not specified like hey, this instruction saves X bytes)
(fxsave is a clear 512 bytes)
and i need to write some insns for xcr0
and im lazy
wdym
the size depends on the cpu
and you can get it
exactly
why isnt it just one size
even if its like 2kb
i could tell
libboron doesnt have it
looks like i had 4 symbols that had weak binding
or whatever
making them hidden fixed it
wow
what the fuck
why is this fucking parameter not 0
looks like i cant use init array either
maybe memsafecopy is doing stuff to it?
idk what i even did but it was fixed
somehow
maybe i forgot to ninja install??
idk
i think memcpy is copying the pointer to BaseAddress to BaseAddressInOut
so making BaseAddressInOut the value of the pointer of BaseAddress
i think that's what''s happening
we're really chipping at this
yep
apparently i will need to implement overlapping mmaps
which sucks but okay
i'll try to do it
later
Yeah, mlibc first mmaps memory to allocate the space for the dso and then maps/reads it
it mmaps with PROT_NONE, doesn't it?
I think so
so i implemented a function called MmOverrideAddressRange, which will be used to support overlapping fixed mmaps, and it turns out i wrote it "perfectly" (i'll figure out later if there are any bugs, right now i just tested one specific case)
the first two bugs i found are unrelated to this function entirely
the first bug: not incrementing the exclusive owner lock count on recursive lock
(mintia1, whose implementation i "stole" with permission, does this)
and the second: i forgot to put an "else"
the first test, which looks like this, works
okay, i discovered a hang
new test. hanging here
OSAllocateVirtualMemory returned base address 0000000000021000.
MmOverrideAddressRange LOCKING SPACE EXCLUSIVE.
MmLockSpaceExclusive(0000000000000000)
MmOverrideAddressRange LOCKING SPACE EXCLUSIVE DONE.
MmLockSpaceExclusive(0000000000029000)
MmUnlockSpace(0000000000029000)
MmUnlockSpace(0000000000000000)
Split succeeded. BaseAddress2: 0000000000029000
MmOverrideAddressRange LOCKING SPACE EXCLUSIVE.
MmLockSpaceExclusive(0000000000000000)
?????
whys it failing the second time
okay
that was another rwlock bug, actually the same one, i just fixed it wrong
FINALLY we found a mm bug
uhhhh
so the resulting PTE at that address is 8
MM_DPTE_COMMITTED is 1 << 8
going to sleep, will figure ts out tomorrow
but yeah. idk why that PTE value shows up
it DOESNT show up when i allocate that region and free it
only when i split and then deallocate
wait!
ahh its MM_DPTE_DECOMMITTED
well I'll fix it tomorrow
after this fix i think it'll likely just work
damn, a function that found like 2 or 3 bugs in other locations
that's one mighty function
okay i found another bug
this time it's actually in that function, and it happens when trying to shrink heap (read: free list) ranges
i fixed another bug that was actually tangentially related to said function which is that i was accidentally setting the decommitted PTE when i actually meant to set 0
Support for overlapping mmaps has officially been added and I believe it is stable now
Next I will add support for partial overlapping unmapping
Partial unmapping support has been added now, leveraging the same machinery as overlapping mappings
now im struggling to figure out why libc isnt being mapped
what the fuck is a 0x8 error code
null deref maybe?
the order is FaultPC, FaultAddress, FaultMode
thats not an address
uhhhhhhhhhh
what fucking kind of PTE is this
where is it from?
if only I knew
is it not your OS?
what are you asking
okay, so context
i am trying to port mlibc's rtld to boron
i have just finished implementing overlapping mmap and partial munmap
and i discovered a freeze
and i discovered that that freeze is located in OSReadFile
and i discovered that for some weird reason it keeps page faulting over and over on a specific address
AND THEN I FOUND
THAT THERE IS AN INVALID PTE
WHY
(i dont know)
lol
oh but i notice that there are TWO page faults
the first is the normal one
so likely that's at fault
WAIT
I THINK I FIGURED IT OUT
see this
thats an int
a SIGNED int
then i use it here
and GUESS WHAT FKING HAPPENS
XDDD
IT FUCKING SIGN EXTENDS
THANK GOD IT WASNT A MEMORY CORRUPTION!
OKAY
were making great progress
well
it printed now!
however I call DbgPrint and dont see anything... what gives?!
maybe i didnt install it properly
yeah no thats weird. it only calls puts() and not DbgPrint
oh its because DbgPrint isnt defined since this isnt debug mode.
duh
success though
Features that will need to be implemented going forward:
- Time/RTC related system calls
- Anonymous sections, and shift all anonymous memory reservations to said sections
- I don't know if I should use a sparsely allocated data structure or not. Will have to figure it out.
- Technically this isn't required for forking support, but it will be when I add support for page out, so best to add it now
- Possibly even move away from the separate reserve and commit model I borrowed from Win32/NT, which will allow me to start freeing paged out page tables without worrying about losing information
- Maybe there is a way to do it by putting the decommitted/committed information inside the proto-PTEs inside the section object
- Well that'd require duplicating the section objects...
- Still need to hash out the design
- Signals
- Duplicating address spaces (and then the whole process)
- Job Control
shared memory can be implemented using sections as they are
private memory will have to have sections created at the time of the fork
containing the forked state of the mapping
also CoW'd pages are gonna have to be paged out separately
i'm struggling to come up with something
I should port rtld to my os
do it
its not even that much harder than static binaries
all you need to do is load a second elf file called ld.so (you'll find its path with the PT_INTERP entry) with any base address (preferably in the high addresses though)
and jump to its entrypoint instead of the program's
(and of course have POSIX compliant mmap support)
tomorrow i'm going to mock up a scheme to implement forkable address spaces. since I plan on adding support for pageout I'm going to need to think this through a little bit more carefully.
i'm excited to see it
what can be done for fork support: you can introduce a kind of software pte which you can call a fork pte
when a forked page is evicted from the working set you create such a pte instead of an ordinary transitional pte
it has a pointer to a structure consisting of the actual pte + a refcount
so i was thinking that all the memory ranges would get a section which is mapped CoW into both processes' memory
that structure, you can see as being like a tiny vm object all its own
or actually that might not work
hmm
by section object i mean a container for all the pages
this WOULD work for shared memory
but maybe not for privately allocated memory
the big problem is that there may be privately mapped copied-on-write pages within the mapped region
and this applies to both private anon mappings and private file backed mappings
if I didn't have to worry about pageout I'd literally just make the PTEs say that the page is CoW-able
so above i wrote my main worry
which is copied-on-write pages within existing mappings
hmm. so I could implement an object called "CoW overlay" or something, which points to a mappable object (either a file or in-memory section). each entry inside this object would have a bit set:
- if the bit is 0 (or actually it might just be if the whole entry is zero), then redirect to the backing object
- if the bit is 1 (or actually it might just be if the whole entry is non-zero), then this is a CoW page and map and evict that one
if this were C++ I'd just make each part of a common interface
in C I could still do it using a vtable or manual if/switch based checks
so I'm thinking it could be something like this
(don't ask why it's vertical when i could've made it horizontal, thinking of it vertically made me think of it a little better)
and actually this CoW overlay object could redirect into another CoW overlay object, technically.
however I'd rather avoid that if possible?
but I don't know how, without actually duplicating those pages
In other news, the function I wrote to support overlapping mmap and partial munmap looks like this and is incredibly gnarly https://github.com/iProgramMC/Boron/blob/master/boron/source/mm/vad.c#L115
Uses the property of recursiveness of BOTH mutexes AND rwlocks!
This sure does look like an afterthought
This can maybe be avoided for anon memory, because you're most likely always going to want symmetric CoW. If you are creating a CoW overlay of another CoW overlay, you can instead make it of the backing of that other CoW overlay (essentially duplicating it).
Actually no, this is likely slightly trickier than that (at least the duplication part is a bit tricky)
Yeah, so creating a CoW overlay on top of the existing CoW overlay
That's exactly what I said
Isn't that what you mean?
no. if you see that you are doing this, instead create it on top of the backing directly.
except that of course doesn't work just as simple as that, because the CoW overlay that you are duplicating can itself have dirty pages.
But then they're going to see the data on top of the original data, and not the data as it was at the time of the fork
exactly
Let's say that I map a file whose first byte is "1"
As private CoW
Then the program increments the first byte by 1, resulting in "2"
Then, it forks
The child process increments the data and puts "3"
The parent also increments the data and also puts "3" (because it saw "2" in that memory region)
Neither of them see the true backing memory of the file which still has "1" mapped to it
The main thing I'm worried about is, what if the parent forks a lot of times? For each fork there would be an additional layer of CoW overlay
You can evaluate the CoW overlay layerings in a non-recursive way, that's not a problem, but then you still run into the risk of running OOM (if the process forks too much, even if the processes all exit), and slowing down lookup for the particular VA a lot
Something like bash can fork thousands of times in just a few hours of regular use, and that's without shell scripts and the like
And I'm also not sure how you can "merge" two CoW overlays. I saw an edge case but I can't remember it
I am also thinking of giving these CoW overlays a silly name like "Memory Cel"
the "Cel" here from "animation cel"
anyway, the dumb design i was thinking of before would lead to something like this
if we're smart about it we might be able to merge these
but we'd kinda have to walk the tree of CoW overlays and it's a mess and surely there must be a simpler way
the CoW overlay concept seems simple, but if you aren't careful it can lead to weirdness
an alternative is to wait until a child process exits, then start merging
you've reinvented from first principles the concept of 'object chaining' in mach vm, and also started developing the techniques to collapse/garbage collect object chains
oh so this concept is really used in a production kernel
that's really neat
that must mean i'm on the right track
btw what do you think of this "monstrosity" I wrote yesterday @uncut lance
this is not the best illustration but you can see it at work in the top example ("BSD VM")
i'm sorta proud of this because from the first commit i got most of it right (i just got the tree walk wrong)
so they're called "shadows" in the BSD VM?
interesting
it is conceptually nice and it's used in most of the mach vm descendents today (freebsd, dragonfly bsd, xnu) but was ditched by chuck cranor in favour of solution from sunos called amaps and anons, which is based on reference counting pages instead
the idea is that i want to be able to page these out relatively easily
idk how that's done with amaps and anons
the classic mmap style api implementation, you can tell when the mmap api was invented they must have had in mind a simplistic vmm that probably wouldn't have had any structure other than individual pages, since implementing it with anything more sophisticated requires dealing with all these possible cases
it looks like it should work at least, definitely one of those apis where having a test suite is helpful to be certain about it
basically it is a second level of indirection
i understand that
nanoshell would have had a considerably simpler implementation probably
an amap is like a vm object/section, except instead of directly containing pointers to pages (or swap), it contains pointers to anons; and anons are reference-counted
oh I've been testing this alright
to fork you clone the whole amap 1 and end up with amap 2, where amap 1 and amap 2 at this point are identical, pointing to the same anons; each anon's refcount was incremented in the process
and an anon is like a single page? or a range of pages?
and you've made the actual process mappings all read only. then whenever you write-fault on a page within an amap, if the anon's refcount is > 1, you clone the anon and its backing page
an anon has a reference to a single page, so it might be a page which is in memory, or a page which has been swapped out
that seems kind of hard to scale because an anon is a slab/pool/whatever allocation
and for a lot of pages parallel memory usage would grow considerably
i think i will go with this object chaining mechanism. personally it looks simple to implement and i think i've figured out how to collect garbage too
the anon incurs a cost of no less than 16 bytes for each cloned page (unless you settle for a smaller refcnt and can tolerate, say, a 10 or 12-byte anon which, if you pack, you end up with your 8-byte page/swap pointer being misaligned) but in principle you only need the anon to exist for as long as there are >1 mappings of that anon
i see
that sounds moderately better
i'd then ask "couldn't you just use the page frame's refcount instead?"
but maybe the anon's ref count is for a different purpose
it's so that swapout can work
the anon becomes like a sort of single-page anonymous vm object/section, in that it can point to either swap or to a physical page
i see i see
btw what's been going on with your OS?
didnt see too many updates
i finally picked up work on it again a few days ago
i had forgotten i had a rewrite branch where i had started prototyping some new ideas (one or two of them made it to master) and rediscovered it
i've been working on that
i hadn't touched it in almost a year, 2 or 3 days ago i completed enough of the page fault on mapped object codepath to do this, which you will have to trust me is interesting because it's demonstrating pagefault clustering/readahead of a mapped file on an ext2 volume:
Did read in object page (520; tot. 8)
We read: < [what] understandest thou, which [is] not in us?
Job 15:10 With us [are] both the grayheaded and very aged men, much elder than thy father.
Job 15:11 [Are] the consolations of God small with thee? is there any secret thing with thee?
Job 15:12 Why doth thine heart carry thee away? and what do thy eyes wink at,
Job 15:13 That thou turnest thy spirit against God, and lettest [such] words go out of thy mouth?
Job 15:14 What [is] man, that he should be clean? and [he which is] born of a woman, that he should be righteous?
Job 15:15 Behold, he putteth no trust in his saints; yea, the heavens are not clean in his sight.
Job 15:16 How much more abominable and filthy [is] man, which drinketh iniquity like water?
Job 15:17 I will shew thee, hear me; and that [which] I have seen I will declare;
Job 15:18 Which wise men have told from their fathers, and have not hid [it]:
Job 15:19 Unto whom alone the earth was given, and no stranger passed among them.
Job 15:20 The wicked man travaileth with pain all [his] day>
(i always use the bible as my test file when working on filesystem/vm/where the two meet)
what is new in that rewrite branch: mostly rewritten dispatcher (hopefully with timers that won't have weird problems on aarch64!!) with some topology awareness; a nice mechanism for calling things on other CPUs; hopefully more hotplug-ready io system; new ext2 driver; proper riscv port; the start of fdt support; proper kernel modules support; and the latest exciting VM changes
interesting
as an update i finally built mlibc
i got a basic "hello world" application working
see here
i am also planning to port my OS to different platforms in the future
i have some real hardware platforms which i can test on:
- the iPhone 3G (probably booted with OpeniBoot)
- the Nintendo Wii
a hobby os for an iphone 3g is the most insane thing i have ever heard
its probably gonna be real at some point â„¢
they ported android to it so why not
maybe
taking a small break. i have homework to catch up to
@glass heart I have question, do you use serial or framebuffer to log? Both now and when you've just started. Thanks in advance, please answer TvT
i use serial
FB is too slow currently
also, why did you send the same msg on all threads?
Port 0xE9 (or serial if necessary through a define)
Qemu debug con
On real hardware it tends to do nothing
No need for init and no need to wait until buffers are clear
you should keep the serial output for non-qemu/bochs machines
what is the qemu flag to get it out to stdio or a file?
i'll use serial for info logs
and spam debug on e9
-debugcon
k
dont do this
itll significantly slow down the machine
even if the messages are the same amount as serial?
i mean dont spam everything you can possibly think of to e9
yeah
keep the log amount the same
atp i think i can start using flanterm again to print just info logs
it was wayy too slow when i sent both debug and info log there
no kidding
i have several debug switches so if i dont need debug output from something i can disable it
like PMMDEBUG and PAGE_FAULT_DEBUG
i might do that
for my usecase those options significantly slowdown because an e9 write is a call to the windows conhost and thats slow as fuck
windows conhost?
and i can't use qemu with accel on windows
yes
i have had only bad experiences with it
lol
why not dual boot then
and hyper-v sucks ass for legacy OSes in general
either i'm lazy or just dont have time to install linux
vmware
prior to april this year it also was that i didnt have enough storage either
just a 512gb ssd
now i have a 2tb ssd
i didnt even partition it fully because i knew i would dualboot
but i just never did it
4 yrs ago today i began the nanoshell project (which is the precursor to this one)
i was learning how paging worked and i felt that a fresh start was needed
Wow only 4 years? I thought it was older than that.
I have technically been doing it for 6, but for the first 2 years or so it was basically just a glorified bootable program
Actually that reminds me
In October 2021 I implemented multitasking for the first time, but when I found out what I have to do, I decided that in December I'd start the project that would become nanoshell
Well, it's actually pretty fast to boot into Linux so...
I have only 256GB SSD
Must be a pain
not really, I don't do much anyway
i was considering moving the "sparse linear array" structure from MM (where the page cache is its only use) to Ex
so that I can use it in multiple places
(well also in MM but ykwim)
My only have 3GB of memory and 32GB of internal storage, now..this is pain
lol
HOW many stars?
also is it nextstep or windows nt i cant even figure out anymore
unix 
inspired by a particular 1990s workstation OS
i have once again rewritten my page cache
this time the data structured is a bit more generic
still a progressively more indirect data structure just like the previous 2 versions
i don't know if there are any bugs because it mostly just worked first try
well i need to check 32-bit too
looks like i had a typo where the system call number for OSSetEvent was mistakenly set to OSResetEvent
and the 32-bit build is fixed!
other than inodes, mappable objects are another place where i miss OOP languages like C++/Java. right now I could be specifying a MappableObject interface and having files/sections/cow-overlays implement said interface. instead I'm going to have to do it manually since, y'know, C
isn't boron written in c++?
no
in fact i have only written one os in c++ and its dead
that being nanoshell64
imagine if i had fixed that bug that caused me to drop nanoshell64 lmfao
32-bit?
the x86_64 version boots with limine, the x86 version boots with multiboot1
it wasnt that bad
Damn
what WAS bad was the transition from 64 to 32-bit
that one was hard
because i did rely on the HHDM in many places and had to compromise
My bad, that was what I'm talking about
i also omitted certain features like SMP support from the 32-bit build
the reason is simple
And that is?
because if you have an SMP system you're likely already on a 64-bit platform, so just use 64-bit
however this assumption wont hold up for comparatively very long
because there are 32-bit SMP platforms that are not x86
fortunately I don't intend to port to any of those for now
I heard that on x86, people usually did Higher-Half Mapping by placing the kernel at PDPT[3] which is the top 1GB of the VM
Heh
i do have a 256 MB higher half
for the other 4gb-256mb i use a switchable 64KB mapping (or whatever)
(my initial design had 4mb but i realized that was slow as fuck)
Idk what that is .w.
guarded by a "spinlock"
How is that slow?
Isn't that less query for the CPU
??
because you have to change 4 MB's worth of mappings and that's 1024 PTEs
AND invlpg them all
also on non SMP spinlocks should just be equivalent to disabling interrupts
but in my case they're a BIT more involved
they still dont wait
just crash if the spinlock is already locked
the x86 port is targeted towards legacy hardware
you'll notice that NO modern features are being used by the 32-bit port
that's why
AHh
i suppose youre also not considering uefi in the x86 port?
since mb1 with uefi is broken (no way to find acpi)
mb2 doesnt have this problem so its a simple enough fix 
but i guess mb1 lets you use grub legacy etc
you can just pretend it says Copyright (C) 1997 iProgramInCpp instead of 2025
(ignore that i was born 8 years later)
LMAO >_<
That's funny (ik my humor is broken, kinda)
the 1% of core duo users
first test with overlay objects instead of the old bad CoW mechanism
fail
although that might be API misuse (not setting PAGE_WRITE) instead of anything else
oh yay
deadlock averted
thank you....
but maybe there is actually a deadlock because disabling the crash leads to one
well
shit doesnt seem to be working anymore
i broke it 🥺
oh this one's failing at execution
literally the first instruction
it couldnt read
you didn't use PSE?
whys it trying to use NULL ðŸ˜
the x86 port is targeted towards legacy hardware
and by legacy i mean really legacy
I mean yeah but you still target i686 right
386/486/pentium type shit
ah ok
no
that makes more sense then
in fact i dont support any modern x86 feature such as the APIC or ACPI on the x86 version
there is going to be an unsupported gap but I don't care about that one that much
where you have 32-bit PCs that have modern features unused by the 32-bit kernel
that could be used by the 64-bit kernel if the CPU was 64-bit
no really
why
cr2 really is 0
i bet it didnt even load the content of the file
can't see how endbr64 would even cause a page fault so yeah the code is probably zeroed
oh yep
💀
thats 0 alright
but that makes me think
how did mapping even succeed if it cant read files
wait i know why
its because i apply the section offset twice
i dont need to specify it when creating the CoW overlay
(it creates a sparse linear array so it wont use more memory than it has to)
okay, got a bit further by making sure OSGetMappedFileHandle actually returns the file handle and NOT the CoW overlay on top of it
oh no
its failing in the heap
that was due to not handling CoW properly, apparently
now that I AM though
i got an issue regarding IopPrepareWriteFile
after fixing that
i get a DEADLOCK!
WHAT!
bruh
YESS
YESSSSS
FINALLY
@uncut lance i have implemented CoW overlays and anonymous sections
and also opted files in to the same system
now the page fault routine is considerably simpler because it doesn't have to treat two separate cases
i will remove the separate commit/reserve feature i borrowed from windows because i don't use it
and then i'll go on to implement fork
I already had it, but I made it better and more pagefile-ready
Yes
Loads executable and shared objects
i have started work on the clone address space part of the fork process
the part where i inherit handles is already basically done however for fork i will have to do it a bit more aggressively as some things can opt in to a mechanism where child processes created through OSCreateProcess wont see some handles
I have already written most of it however I will need to write code that calls it and code to test it
eventually it will be implemented
i do still remember like 3 1/2 years ago where i said "fuck fork"
well while i do still think it's not a good API, i have decided that i should implement it
#progress-report message (from archived channels)
#progress-report message (also archive)
@uncut lance you say that your implementation of fork requires extra allocations to make anonymous private memory work with symmetric CoW. well guess what, mine will too. i haven't yet decided to make OSAllocateVirtualMemory calls allocate anon sections instead of just creating non-object VADs where the information is stored inside the page tables. so for fork I'm introducing a mechanism where these non-object VADs are automatically upgraded to section based VADs during fork, before creating the cow overlays on top of private mappings
due to laziness reasons I am not going to roll back the section VAD upgrade upon failure of the subsequent code path, since that'd involve tracking every modified VAD in a list and I don't feel like doing that, and for just a couple extra kilobytes per process on average?
i'll explain more tomorrow
i have a plan
after my vacation starts im going to lock the fuck in
and port boron to powerpc
and then run it on the wii
so i can get ppc fireworks by new year's
i think i could do it without making lots of extra allocations at time of fork, but the cost would be allocating what i'd need for a fork in advance
or in other words everything would need the anon indirection
Yeah, exactly what I was thinking
Based 
Let me know if you want some patches to help it work better in Dolphin (it's super inaccurate for low level stuff), or if you need testing on real hardware with a USB Gecko serial console, would be happy to provide either
I also have some original minimal bootstrap (enough to get everything mapped and a sane enough env to run C code) if you need some of that to get it off the ground; currently licensed under GPLv2 but since it's my own I'd be happy to relicense it under whatever if you want it
Yes please! I did hear of issues like that
I'm going to commit a slight breach of boron's "only implement what you need in the kernel" principle: I'll be putting most of the fork implementation in the kernel
I will remain unbudged for execve type functions though. Those will still be implemented in mlibc or libboron.
In other news I have thought of a new codename for this OS: Flutter (or Flutter OS). But I'm not 100% sure about it
afaict, there is no OS named flutter on the internet currently. I originally considered butterfly but there is already a Butterfly OS.
there IS however a piece of software called flutter
and flutter OS sounds like a shitty website os
like those fake ones that just run in the browser pretending to be one
yeah maybe
anyway yeah im still looking for potential names
a rename is most likely going to happen sometime in 2026, because i don't think boron is that good of a name for a complete desktop OS distribution
take shrooms and go out in the wilderness to find a name
call it zoron 
Zoron OS 

okay
first fork test
guaranteed to fail, but let's see ...
well
what did i say
fix one bug and like 2 more appear
well
it thinks its the parent process twice?!
okay
we're making progress
i screwed up by returning a status from OSDLLForkEntry instead of just returning the status value from the OSForkProcess system call
32-bit: stack overflow?!
in the parent, when entering OSForkProcessInternal
child process
of course it wouldnt fucking work the return SP is messed up
wtf now its the same
i'm pretty sure you can use print/x instead of typing it into the calculator to see the hex representation
or p/x for short
okay NOW it entered the child process
different comand
thats like
not the same esp
so of course its messing up
WTF am I doing??!
writing shit to userspace memory
previously you were doing print/x *(uint32_t *)$esp?
this is extremely brittle
as in examine/x $esp?
i was doing like x $esp or whatever
doesnt matter
what matters is what i was smoking when i wrote this function
because i want it again
(jk. i don't smoke)
technically there is no need to worry since most of the time this deals with entirely new threads where we do indeed want to pass a parameter as context
okay i removed that modifying the stack part out of the assembly and put it in an MmSafeCopy when entering the thread.
ayy i fixed it
okay, there's one more thing i need to do before i can merge fork support into master
which is to make an entirely new event object for critical sections
okay i have added support for OB_FLAG_DUPLICATE_ON_FORK
the object is duplicated if it can be duplicated, upon any fork operation
if you try to create or open an object with this flag and the duplicate method isnt implemented then opening will fail
based, forking will open up a lot
i still need to implement execve
then i think ill be set for bash
actually i think i might need signal support as well
Bash doesn't require signals
yay
and bash also doesnt require job control
signals and job control are 2026 goals i guess
2026 is going to be a good year for boron. that's when most of the ports to other platforms will happen
2023-2025 were foundation setting
ayy nice 🔥
You'll be right I'd think
Bash is not too demanding
for the past 2 days or so ive been working on an armv6 port
now i got a basic higherhalf kernel separate from the boron source tree
and also got boron to compile (but not link because of missing dependencies)
isn't armv6 ancient?
armv6 showing some signs of life
i have my doubts im gonna be able to achieve a real hardware port in 1 day until new year's but at least it's running
idk why its accessing that address though
todo 😔
to be fair I don't know how to calculate a stack trace on ARM
fair enough
ooh exciting
Happy new year!
I have decided to take up implementing execve