#shkwve: an incomplete kernel/maybe bootloader/UEFI thing/maybe something else if i get bored again
1 messages ยท Page 4 of 1
i dont even know anymore
im gonna blame qemu pl011 emulation
this doesnt break on 8250 uart
okay
it hard reset really quickly
fuck
no logs
oh wait
im not running it on hv
damn!
ok good that i have a usb reset
in hv i get this error
cool
i guess
damn
ah
caches!
i need to turn those on
yeah its an atomic to log::STATE
IMPLEMENTATION DEFINED fault (Unsupported Exclusive or Atomic access).
wtf
why?
because my code sucks ass lol
make a uFDT
lol
in this case, the issue is rust ownership
the dtb is global
actually that doesnt matter i think
does it
lmao
'static
yeah thats what i do right now
but theres a lot of big ugly there
okay time to replace every single place that does any DMA with a proper dma buffer allocation
and proper cache flushes
i should add backtracing from exceptions on arm64
okay first crash
fdt parsing is fucked
there's a nice library by r4
why write?
i need to edit the FDT to adjust the /chosen node
plus, limine requires me to remove memory nodes
okay, now pci
and now im getting a weird L1 translation fault
wtf
ah its oob
oookay so on qemu the pci window is outside of the low 128 gigs
fucking hell
okay
new plan
i'll construct a page table with a single PTE
which maps just the loader
then i parse the FDT
i'll assume it lives within a single 1 gig contingous area
so it can just get its own PTE
actually im overcomplicating it, i'll just parse the FDT in a prekernel, clean up the memory map, then map my loader and be done with it
and then build the prekernel with -mstrict-align
i'll call it "cmodule" instead of "prekernel" though
and reuse it for a bunch of other stuff
e.g. SMP spinning
hmm or maybe not
I sometimes look in this thread and wonder what the hell is happening
lmao
me too, me too
lmao
fwiw it does do write, its opt-in behind a define and can operate with a static buffer if you're using it pre-allocator (its a little gross but it works).
afaik the only thing its missing is being able to set the reserved memory entries, which I guess you probably do care about
ah and good docs
but yeah, cant help you with the 'being written in c' part ๐
oh cool
i might use it then idk
since using C FFI is a famous strategy of escaping rust UB
have rust UB? just put the UB in C code and its not UB anymore
have rust? just put the rust code in C and it's not rust anymore
lol
actually, can it turn the changed DTB back into bytes?
It can make a new fdt if that's what you mean
Mm I think that requires an allocator though, for the buffer to write into
Fair
unless i just binpatch your FDT to add a rsvmem region
wait do you not expose rsvmem at all???
Lol or that
I haven't needed to personally, so I haven't bothered
ah you just use it with limine?
Its a long story, in the cpp version I did, but since making smoldtb I'm yet to need it
fair enough
Pretty much
The CPP version was before limine had rv support
So it had those features
ahhh
Honestly, if you're willing to give me a weekend I'll add them
apart from the fact that it requires allocations, it looks quite nice
also why uintmax_t
i'd prefer to be given raw bytes
there's like a bunch of places where i need the raw cells too
its just what I wanted at the time. I was using size_t's for everything but got bitten by that somewhere (I forget where now). uintmax_t is hilariously overkill I know but solved my issue.
yeah fair, could be easily added. You could get that via actually read_string(0), which willl return a char* to the beginning of the property data.dtb_stat_prop() returns the base + length of the cells.
I should implement be an api for that though, maybe also returning the number of cells. thanks.
i'd want to get the length
aah stat_prop works thanks
I will eventually finish the docs, sorry
no worries
okay the new new plan is to bring back a preloader
i'll map it with two 2mb pages, then give it 2 more 2mb pages for mapping the FDT and a 4k page for mapping whatever physical memory
running off of the stack that i'll later reuse as the normal shkwve stack
and the preloader will map shkwve into virtual memory
and also map all of physical RAM
and then device memory (e.g. PCI BARs) need to be mapped later by device drivers, on demand
About that...
/home/blanham/projects/rooster/build/_deps/smoldtb-src/smoldtb.c:350:34: error: passing argument 3 of 'dtb_read_prop_1' from incompatible pointer type [-Wincompatible-pointer-types]
350 | dtb_read_prop_1(prop, 1, &handle);
| ^~~~~~~
| |
| size_t * {aka unsigned int *}
In file included from /home/blanham/projects/rooster/build/_deps/smoldtb-src/smoldtb.c:1:
/home/blanham/projects/rooster/build/_deps/smoldtb-src/smoldtb.h:90:70: note: expected 'uintmax_t *' {aka 'long long unsigned int *'} but argument is of type 'size_t *' {aka 'unsigned int *'}
90 | size_t dtb_read_prop_1(dtb_prop* prop, size_t cell_count, uintmax_t* vals);
| ~~~~~~~~~~~^~~~
/home/blanham/projects/rooster/build/_deps/smoldtb-src/smoldtb.c:360:34: error: passing argument 3 of 'dtb_read_prop_1' from incompatible pointer type [-Wincompatible-pointer-types]
360 | dtb_read_prop_1(prop, 1, &handle);
| ^~~~~~~
| |
| size_t * {aka unsigned int *}
/home/blanham/projects/rooster/build/_deps/smoldtb-src/smoldtb.h:90:70: note: expected 'uintmax_t *' {aka 'long long unsigned int *'} but argument is of type 'size_t *' {aka 'unsigned int *'}
90 | size_t dtb_read_prop_1(dtb_prop* prop, size_t cell_count, uintmax_t* vals);
| ~~~~~~~~~~~^~~~```
lmfao

lol
rip
Btw this was fixed last night (I also exposed the reserved memory stuff). What target was that for?
I got the same error on eight different 32-bit targets 
That's a bit embaressing lol
tbh, some of those don't even use DTB, but I haven't done any fine level conditional dependencies in my build system yet so it got pulled into all of them 
wait a second...
I mean it's nice that it was tested anyway
There's been a handful of updates since you integrated it into your kernel
yeah i need to update it
okay now i'm working on the shkwve first stage loader(tm)
so far i have an FDT parser
and a naming scheme that is truly horrifying
oh and a print function straight out of c++
i've heard that C doesn't have function overloading
but clearly anyone who says that has never used clang(::overloadable)
why yes i would like to have C++ name mangling in my C program
it kinda does
if you have a very broad definition of overloading
_Generic() 
yeah but that sucks
you can't even define an overload for void* and have it catch every kind of pointer
you can afaik?
@untold basin https://godbolt.org/z/8YnY81cfW
yeah but that's a stupid catch all
which is boring
overloadable is way better
overloads are boring
its also decentralized so including more headers can add more things
so for example including my endianness support header makes my print function know how to print le32 and be32
(my endian support header is so hilariously overengineered too lol https://paste.sr.ht/~pitust/2f27640c30bae08e335ca24cd5c0ad93c78e5201)
hmm i wonder
should i respect /chosen/stdout-path
or should any use every serial i find as the stdout
probably the former tbh
this is terrifying
terrifyingly good you mean?
terrifyingly neutral
it is actually quite nice
if you dont want all of C++ but you want just one thing or whatever
clang is not all C
idk if you want C++ features why not just use C++
that would require me to write clang++ in my compiler invocation
and exposes me to C++ naysayers
also it would probably make me do a lot of c++ crap i dont want to have
icc
idk
thats just clang
gcc is not clang
ok clang is almost all C
ok fine
but only having 1 compiler is boring
true
memory map cleanup!
and now i have 4k alignment
tbh in rust the binary for boot0 would be like 50x bigger
and not 44k
which is mostly bss
you're rewriting in c?
no
i'm adding a C first-stage loader
because i need it for bootstrap
hmm maybe i should rewrite in C
ill think about it later
ah arm true
yeah arm
for now
ill be booting with it on riscv too
actually now that you said it
ill defo use it in M mode if im gonna be supporting split S/M
and if im gonna be supporting returning to EL2 etc etc
because otherwise you're gonna lose SBI :3
so it will be hard tied to the rest of the logic
well no i can implement my own SBI stuff
would be a lot of effort but its totally doable
there's a rust sbi implementation i think it's by xvanc?
yeah but why
:^)
also now that you said it maybe i should rewrite in C
idk
ill think about it
rust is fine
this loader might be constrained by size tho
so C for this and rust for the rest makes perfect sense
its more that i dont want to add too much to code size
the entire thing is gonna be one big binary
rust for the rest:^) rhymes
baked together by some build type process
oh(
lol i dont want to write two disk drivers
i thought this was gonna be an SPL essentially so stored on rom
ah
because i dont want to compile everything without aligned access support
and also rust without paging on arm64 isnt really feasible
ah ok i understand
because you need paging to do atomics
btw i really hate the fact that u-boot is basically the only SPL option in risc-v
i see but also im not gonna write stupid amounts of disk driver code
it would easily double the code size and require me to write lots of careful handover code
one day I'll work on some hardware and I'll write an in-house firmware for it because i have an issue or two with everything out there (uboot, edk2, whatever)
maybe not coreboot, coreboot's cool
okay the new version of the shkwve boot0 can now set up paging too
now i'll do handover ig
hmm now im wondering if i should do like
rust for drivers and C for everything else or something idk
why is that
nah
rust for everything
rust binaries are so fucking big though

lmfao
they also consume terabytes of stack
and their ub rules are insane

only 32k its fineeeeee (lol)
im seriously considering using more C
especially because C codegen is a lot better lul
eh, with lto + strip its not that bad
eeeh
i can lto and strip C code too lol
because i dont want to have too big of a binary
then doing it in rust was a mistake to begin with 
fair lmao
why do u have multiple stages now?
i'll be packing them into one binary
i need to have some paging setup
and the paging setup is nontrivial because it needs to allocate and parse FDT and shit
and i need it to not be rust
and i need it to be a separate TU
also, im fine with having one copy of the rust stdlib
two copies are a bit excessive
want to fix it? implement new-in-place in rustc :^3
e.g. Box::new() and stuff that doesn't first allocate on stack
it's been an open issue for decades
nah id rather use a language like zig or c
:(
i would be so happy to have new-in-place
and a syntax for new-in-place too (like C++'s new-in-place
be the change u want to see 
sure but there's lots of bikeshedding
i wish rust wasn't as big as it is and we could do changes faster
at some point I'll return to langdev
I would really enjoy a language that only I can use
that would allow me to not care about other people's opinions
lzcunt software license
Definitions
Licensor means lzcunt and lzcunt only, if you're not her you're not licensed to use this software
I am of this opinion that rust is a reasonable languagr to write drivers in for a system written in another language
you dont need a license for that?
Because a good driver API is an abstract one. So the drivers don't have to deal so much in the problems rust can't answer
it was supposed to be funny
that's why it's a license
anyway
maybe
maybe i'll do C++
Why not Go
no.
okay so how should i implement my new shitty driver model
hmm maybe i should use message based async
what if i did C++
and used C++ coroutines
because C++ has implicitly shit perf instead of explicitly shit perf
and i have some C code for boot0 already
holy shit who came up with this crap
Who needs oom handling amirit
it's a bootloader
OOM handling isn't a thing there anyway
C++ coroutines are a headache compared to rust async
nah
well
maybe
but consider this
its not rust
true
c++ coroutines are fun (to debug)
lol
What are you doing to make your Rust binaries so large? I see this brought up a lot, but never experienced it myself, so I am wondering whats causing it?
I am pretty sure this has been fixed: https://github.com/rust-lang/rust/pull/82806
Also really cool project btw, I like the way your register dump looks lol
I don't think this is what I mean
what I mean is like guaranteed new in place
e.g. in debug mode
i don't really know, tbh
yep I asked in the rust server
this is just enabling an LLVM optimization for this
there's no rust guarantee that the stack allocation will be removed
so your max allocation is still limited by your stack size kinda
you can overflow the stack when creating huge strings or similar
How the fuck does this happen
Does it just not put string contents on the heap?
it does but heap allocations are first on the stack
e.g. take Box as an example
Box::new(123) will stack allocate 123 then memcpy it to the heap
LLVM can and will optimise that out but in debug builds and stuff it might not
Borrow checker meme?
so if you have a huge boxed array for example
nah
new() is a function so it's arguments are created as they normally would
so 123 is created as it normally would be
then passed
tho 123 will likely be assigned a register so imagine something more complicated
Oh and new doesn't take references?
nope it takes ownership of the value it can't take references
the solution to this problem is to guarantee that temporaries passed in this way are initialised in place
so guaranteeing the optimisation
and sometimes you want to do it manually as well which is new-in-place in C++
new (ptr) Foo();
rust doesn't have an equivalent
it used to have box expressions as an equivalent but it was removed
the rust "equivalent" is *rawptr = whatever;
i remember there is but i couldn't find it
and there's lots of bikeshedding
ofc there is
this is awesome because it requires no extra syntax
it's basically copy elision but you're eliding a stack allocation
getting rid of the temporary
bc the temporary can sometimes not fit in the stack
e.g. it's hard to create huge boxed arrays
not impossible mind you
box expressions are the same as Box::new
Box::new is a language item
and the impl is literally fn new(x: T) -> Box<T> { Box::new(x) }
I thought the impl was // compiler implemented or something and it was #[rustc_box]ed
Ahh I see. I guess you always have the fallback option of allocating a zeroed box and then repeatedly moving small amounts of data into it after it's already constructed. I don't remember ever encouraging a stack issue however, I do agree that it should just behave like that by default.
The thing in my eyes is that, yes -- Rust has a lot of growing pains (especially around osdev) and is kinda difficult to use when navigating around them. However, when it works it works really well, and for most things (that I care about at least) are actively getting worked on. In the grand scheme of things, Rust is still a baby language. I mean yeah obviously you have Zig and others that might not have some of the same annoyances, but I find that Rust's rules are much more complex than Zig. I cannot go back to not having proc_macros and not having fmt etc... lol
Something you could look into is using the defmt crate. Its a much lighter version of fmt which is quite large for low level stuff. I've seen fmt consume ~30k of executable size alone, and without it you should notice much smaller executables. I think its like this because dyn has quite a bit of executable complexity, and the default formatter is optimized for zero branching. Oh, and you should disable stack unwinding. You can still get stack traces, just that Rust cannot recover from a panic and unwind its stack like C++'s exception can. Its not impossible to get executable sizes really tiny, just a bit of work.
i obviously have unwind off
i dont have panic-unwind anyway
Ahh okay good
i have my own unwinding logic though, for stacktraces
and i build with frame pointers on
yeah thats fine, shouldn't contribute much to large executables
the tables are actually pretty big tbf
but its not THAT much
i might look at defmt yeah
like are you including your debug symbols in your executable?
no
i am taking dwarf tables
doing custom magic
then including the results of custom magic in the final executable
the tables are a lot smaller than dwarf
still quite big but not ruinously big
isn't defmt serialization-only?
which kinda sucks
ahh I was about to say that Rust's symbols are really large lol, I have seen ELFs >2Mib for a <100Kib final executable
Anyway, hopefully you end up figuring out something that works. Good luck on the project!
Oh and one last thing, if you haven't already, you can make custom compile profiles in your Cargo.toml file to enable some optimizations even in debug mode. Because in debug mode Rust will stub out every BinOp to a function to ensure it doesn't overflow, this gets optimized out in Release mode. You can disable this check without enabling other optimizations, so it's still easier to debug
But do you have overflow-checks=false
Yeah unfortunately they are pretty big, and I also leave them on were I can... Idk I wouldn't stress about debug's executable size too much unless it's causing tons of issues.
idk if you already do all these
i dont have location details off
Release mode with opt-level="z" 
@vapid echo please tell me more
idk if you remember the library with rust-like functions
sounds interesting
the one with the hamt and stuff
Rich freestanding C++ library. Contribute to nyx-os/atlas development by creating an account on GitHub.
that

oh also you need the std header crap
yeah
i dont have it
so add it 
freestanding headers good
including these thingies
what does it really give me tho
they are trivial to write
yes
whatever idrc I just thought it might be cool to use
auto v = arr.iter().collect<Vec<int>>();
literally rust

yeah true
that looks cool
ill think about it
no
i was not
your thing definitely looks better than frigg
idk about the names but like
the rest is fine
also i dont like std
wdym
like why have StringView
etc
tbh personally i prefer just using Span<const u8> for strings if possible
but yeah whatever it doesnt matter
u can just use StringView::as_slice()
true
idk
ill think about itโข๏ธ
no bounds check cringe https://github.com/nyx-os/atlas/blob/master/include/atlas/string_view.hpp#L21
ah I may have forgotten
I have it in slices though
but yea whether you end up using it or not, I'll probably look into contributing to your thing cuz it would be cool as fuck to boot my OS using a custom firmware
trueee
okay i think im gonna have to do bootloader stuff for shkwve now
good stuff
based
make it rust FFI-able
it would probably need a fork of the rust stdlib tho
because rust does not have a standard struct layout in memory
you'd need to make rust stuff #[repr(C)]
but it would be cool to be able to pass any rust stdlib type (that your thing also defines in C++) over an ffi boundary
but why
im doing it all in C++ now
fuck rust :^)
fun!
looks like im targetting c++2c then
ugh "const unsigned char" is not """similar""" to "const char"
i have -funsigned-char too lol
so its like. literally the same type
okay so lets get shkwve++ booting
except it isn't because lol
it has the same size, alignment, signdness, layout
std::is_same<unsigned char, char> || std::is_same<signed char, char> is always false
yeah i know i know
or in custom c++ goop: is::Same<unsigned char, char> is false :^)
okay now i have a custom "span" helper type in my C++ template goop
fun!
okay wtf
how do i make my initializer_list not weird
does it only have begin/end ptrs or alternatively begin ptr + size?
those are the only variants of it that are supported
doesn't integrate with my print() function macro
what I did for my fmt thing is I have a Formatter<T> class that types can extend
so you can do like
struct Formatter<Vec<T>> {}
then do a display function
cringe
because that integrates well with my evil wonderful print macro
ah yeah that makes sense
tbh
passing around a sink doesnt produce good codegen tho
I checked
and it wasnt that bad
a hello world was roughly the same size as frigg (or smaller? I forgor)
ok makes sense
okay i have SmallVec
okay now im gonna add a refcounted pointer i think
okay now i have Box and Ref
now i will create a String type
a totally unique idea i did not steal from anyone else!
I called my Ref<> Rc<> 
okay im gonna use a Formatter type
its definitely a better approach
tho it has some disadvantages, especially around overload resolution
but its probably a better approach
literal plagiarism
noooo
im not writing to a format buffer
also my impl puts __thunk on every function which is why its totally unique!
#define __thunk [[gnu::always_inline]] [[gnu::artificial]] inline
yeah probably a good idea
i should stop putting it on like everything lol
i now have this distinction too
which prints as expected
oh and other things like this
implementing random C++ data structure crap for my new rewrite
what rewrite
yeah im rewriting shkwve!
bro
in c++
why?
oh yeah good point
ill still keep the go stuff
ill just make it generate ninja for depfile handling
because having symbolized stacktraces is like. a cool thing
mhm
I think my way is cleaner
its very strange tho
my way
oh you mean for collect?
or like in general
tbh i like the iterator vs cursor distinction
but yeah anyway maybe i should make it run on bare metal
since unless i want to go wild with implementing literally every DS possible
im kinda out of things to implement
THIS IS JUST RUST
does your formatter support {:x} and stuff
i did that for mine in C++
it was tough
no
also i produce better codegen than rust does
because i was using very recent C++ features I was just learning
in what way?
example?
for the formatter?
yeah
i dont do loop stuff
or like slice stuff
uh
rust produces really bad codegen for println! lol
it was โพ๏ธ before
the Formatter<Span<int32_t>>::debug stuff has a bunch of logic etc
(putchar is temporary its gonna turn into real debug io stuff later)
but its all fully monomorphised
and i parse format strings at compile time
@vapid echo lmao atlas doesnt even pass its own test suite
oh and now i managed to induce a crash
TEST_CASE("collide") {
struct FakeHash {
uint64_t operator()(uint64_t a, size_t gen = 0) const {
(void)a;
return gen;
}
};
Hamt<uint64_t, uint64_t, TracingAllocator, FakeHash> h;
h.insert(1, 1);
h.insert(2, 2);
h.insert(3, 3);
h.insert(4, 4);
h.remove(1);
h.dump();
if (Option<uint64_t> v = h.get(4)) {
CHECK(v.unwrap() == 4);
}
}
you should prooobably handle this kind of endless hash collision
struct FakeHash {
uint64_t operator()(uint64_t a, size_t gen = 0) const {
return gen ? a : 0;
}
};
Hamt<uint64_t, uint64_t, TracingAllocator, FakeHash> h;
h.insert(1, 1);
h.insert(2, 2);
h.insert(3, 3);
h.insert(4, 4);
h.remove(1);
if (Option<uint64_t> v = h.get(4)) {
CHECK(v.unwrap() == 4);
} else {
CHECK(false);
}
and this fails in the CHECK(false)
i wanted to see how you dealt with these kinds of collisions and the answer is that you do not 
tbh i might externally allocate entries
its less efficient
but its easier too so
actually fuck it i dont need a hashmap for now
or any map
i might port frigg's rbtree
or implement an AA tree or something
idk
okay it is time to make it do os stuff
Huh?
All passes for me
You probably need to run it in the proper directory
I read an elf file
sounds like a broken test suite
but anyway i foud two different breakages with your HAMT too so
lol
This one?
yes
its a valid hash
this is the case if you get a first-level hash collision
which can happen
I did extensively test collisions
its called testing using brain instead of using brute force 
also i almost got my shit build!
:^)
(almost)
progress
it links
I distinctively remember searching for collisions for testing them
idk sounds like you just missed an edge case
yea so to run tests you need to call ./build/tests directly
ninja -C build test wont work
i did ./tests
because of the elf thing I told you
lol
okay yeah whatever
same thing
i commented out that test and it worked
anyway time to fix arm bullshit
hmm weird it should work fine
wtf
ok i fixed it
this seemed to be the problem
just removing that condition fixes it
esr suggests L3 translation fault
the ptes have bits 0x403 except for the last level which is 0x401
yeah that was definitely a wrong codepath
403 is ACCESS|VALID|TABLE
I tested with the huge word dictionary
401 is ACCESS|VALID
and it doesnt get hit once
does my test pass now?
yeah
cool
i think the page also needs bit 0 set?
isnt that the bit 0?
VALID is 0
or bit 1
inline constexpr uint64_t kPageValid = 1;
inline constexpr uint64_t kPageTable = (1 << 1);
inline constexpr uint64_t kPageL3Page = (1 << 1);
from mammogram
huhhh
i think the only case bit 1 is unset in a valid entry is 2M/1G/512G blocks
I should probably make the elf test suite better
so it doesnt not work when calling ninja -C build test
also page tables don't need ACCESS set
iirc those bits are just ignored for non-leaf entries
i see
still nope tho
okay yeah now its crashing with FAR=0x18
and an L1 translation abort
thanks for the bug report btw i wouldnt have caught it
okay next problem
i need to map peripherals
okay i have boot0 working (tho with a massive hack)
okay now the hack is gone too
boot1!
the bad news is that weird things are happening
like it shouldnt reach MISC_abort_slow
ever
yeah it works at -O0
and i get a garbage elf at -O1
wtf
oh
oh god
i hate LLVM's "lets just optimize out random """empty""" loops" pass
it optimized out my panic code
oh!
its a clang bug
fixed in 19
fedora only packages 18
grumble
and then i get to an allocator failure which is expected
cool
that one time when the UB is actually a compiler bug lol
i dont actually know if it is tbh
the standard is stupid here
newer clang fixes it
C mode fixes it too
afaik for(;;) { /* some code with no 'break' */ } cannot be removed
fear the Big Bad Optimizing Compiler
by a standard compiler, no
by clang++ versions 18 and older, yes
also thats not true
for (;;) {} cannot be removed
for (;;) { /* any statement */ } can
๐
was that a DR to a previous standard or changed in a new standard
not that i expect the compiler to actually change optimizations based on -std=c++XY but i'm curious
pretty sure it was a dr
and clang was basically the only compiler that optimized it so aggressively
lmao
im on -std=gnu++2c too so
lol
hmm there is no way to fit more than 4 standards into a decade right?
hmm no ... 20 23 26 29 32 35 38 41 44 47 50 ...
10/3 lol
shhhh
the max you can do is obviously 00, 03, 06, 09
although
09 12 15 18 21 would work if they were really slow for 09 and really fast for 21
i was more interested in calendar decades, so that you get c++X{a,b,c,d,e} or whatever
but that obviously can't happen
ahh ahh
handoff
memory map from C++ (boot1)
and from C (boot0)
the 0x980 LoaderImage is zeros for padding
okay now it even runs with TCR_EL1.EPD0=1
it crashes on no malloc atm though
and its ugly as hell
okay i think its time to write a shitty malloc
and by "write" i mean "steal"
and ill probably have to write a new one because im stupid
but its fine
okay so now that i have a stolen completely novel memory allocator, what's next
@agile nexus i hit that incompatible uintmax_t type thing too btw
also. you should stop vomiting global state into the public symbol namespace with the state global
Bloody hell, thanks for letting me know. Just checking you're on the latest commit? Should be 72d3f85.
Ahah yeah that's fair about the global, I'll change that.
you pass a uintmax_t* to a size_t*
tbh i forgot where because i just typedefed them together lol
also the global is the only way of getting the fdt root
other than path stuff ofc
lol, I'll have a look at this soon hopefully
okay heres my new device subsystem tree thingy
does this have any use besides being nice to look at? and what is nextLetter?
its a link
its test stuff
yes it will be used for things
when i do more impl shit
laterrrr
but like... what is the point of nextLetter? if this is part of the filesystem hierarchy (like devfs or sysfs), code can just iterate over readdir
its a test entry
lol
like its a symlink type of thing
yeah
but what does it permit software to do that it cannot without it?
also, assuming a usual VFS, is it not better to have the symlink contents be ../b instead of /b? the latter would lookup b in the filesystem root
or maybe it is implemented as a 'magic symlink' and it does not matter, idk.
thats not how its implemented no
its for introspection stuff
like for modelling relations between devices
or whatever
but is there any meaningful relationship between two "adjacent" devices on a bus, other than belonging to the same bus?
no
the "nextLetter" entry is just a test thingy
to make sure that this mechanism works correctly
okay im back from implementing a shitty raytracer
and i decided to redesign the device system yet again
// idl, defining a kind of device
class Foo
poke(u8 thingy) -> void
// a thing that is this kind of device
struct AFoo : Device(Foo) {
void poke(u8 thingy) { print("poked: {}", thingy); }
};
// user API
Dev<AFoo> root = Dev<AFoo>::make(new AFoo{});
root->poke(0x41_u8);
Dev<Foo> lower = root.downcast<Foo>();
lower->poke(0x42_u8);
basically its rust traits except in C++
don't forget to insert a STREAMS implementation as well
that sounds like an interesting, although not necessarily good, idea
i'm definitely planning to add a network stack
now i have an event thingy as well: ```c++
// subscribtions
static void someHandler(evt::FooRelatedEvent* relev) {
print("handle a foo-related event");
}
static void someHandler2(evt::FooLocation* loc) {
print("handle a foo-located event");
}
EventHandler(someHandler);
EventHandler(someHandler2);
// creation
evt::FooLocation::dispatch(true, false);
evt::FooRelatedEvent::dispatch(true);
// idl
event FooRelatedEvent
bool isRealEvent
event FooLocation : FooRelatedEvent
bool requestCreation
with some cheap type casting logic which handles the simple single-inheritance case
(i form a tree of the events, then do a dfs over all of the events and assign IDs as i go, so typecheck is just min <= id && id < max)
my plan is to allow for events to be "claimed" by device drivers as my mechanism for creating new devices
no not concepts
concepts are in no way equivalent to rust traits
They are kinda similar
Traits are better tho
okay i think i'll implement STREAMS now
maybe
for uart
less priorities, because i do not need them
ig
and also other things probably, because looking things up is hard too
okay i have probing going again
still need to do clk and pll torture
and implement a gic + pl011 driver
What language is this
first two C++, last is custom idl thing
#include <fdt.hh>
#include <irq.hh>
#include <option.hh>
// generic protocols
class InterruptController
resolveFDTInterrupt(fdt::Cells cells) -> Option<u32>
bindIRQ(u32 id, InterruptSlot* slot) -> void
// arm64-specific devices
#if arm64
class ARM64PlatformIC
handleIRQ()
handleFIQ()
#endif
// devices
event GICv2Found
fdt::Node fdtNode
event FDTNode
fdt::Node fdtNode
// TODO: also add vmspace when i add that as an abstraction
``` it has a bunch of new maximally cursed features now
like #if and #include support
which get lowered to C++ as needed
is that implemented with the c preprocessor?
ofc not lol
well it could be
if you make your idl lowered to headers you could include idl and headers
uh, the headers define types
those definitions arent compatible with my idl parser
ah ok
okay i implemented a very shitty pl011 "driver"
okay i have my own totally unique memcpy! https://paste.sr.ht/~pitust/b9ac15faff533b44ae7d7073ebce626250aad48f
totally different from mangarm's
[[gnu::artificial]] [[gnu::always_inline]] static inline void memstore(u8*& dst, word value) {
__builtin_memcpy_inline(dst, &value, wordSize);
dst += wordSize;
}
[[gnu::artificial]] [[gnu::always_inline]] static inline word memload(const u8*& src) {
word dst;
__builtin_memcpy_inline(&dst, src, wordSize);
src += wordSize;
return dst;
}
but hey i have this impl which is a lot nicer than the weird enum tricks managarm uses
also i mess around with the impl to induce ldp/stp on arm
80 columns
aka stupid line
yes its super distracting
cant you turn it off?
you can using stylus :^)
its the same approach as managarm, except that i induce stp/ldp on arm + i use __builtin_memcpy_inline
lol
okay i think its time to acutally write a reasonable memory allocator
okay i have my shitty malloc now
and i have page table logic working i think
kinda
still need to improve it a bit
okay i have mapping pages down i think
i think i have a bit too many debug assertions
also wtf is clang's optimizer doing
ahh yes, now that is true clang
if (x) { if (x) { do stuff(); call a noreturn function() } if (x) { call a noreturn function() } call a noreturn function() }
does clang just not do noreturn analysis at -O1?
and immediatly after that: if (x) { call a noreturn function() }
tbh it would be cool if c++ had something that worked like #[track_caller]
and on one hand i could write a clang plugin to add like, [[track_caller]] or w/e
but on the other hand
that sounds really fucking hard
so i wont
clangir would make this way easier tho
or maybe not idk
actually it probably would yeah
unfortunately its also really fucking broken
okay now i init my pl011+gic driver more better
and map pages
next step
interrupts
ugh
in newer qemu i get alignment faults
in the pre-vm logic
basically assertboot 
okay i routed an irq to the pl011 driver!
okay now im handling the dpc
okay now i have serial
with interrupts!
time for shitty STREAMS
or maybe i wont
hm
okay lets start by entering the hell that is C++20 coroutines
one thing i have to give to rust's async system props for
it is way easier to understand
hmm i think i have a race condition somewhere
and now i'm reading from a different coroutine via an MPMC
my new set of async primitives is basically just spamming out semaphores tbh
i should switch to a proper round robin style scheduler
Another successful case of IDT init... 
actually its VBAR_EL1 init

and also theres a bunch of other things that go into my interrupt handling because (a) arm moment and (b) pitust shitcode
also also i initialize the idt as the second instruction in the proper loader ("boot1")
boring, they should've called it IDT_EL1
ok sorry 5th
the first thing is setting up the stack
and handoff info
the third thing is vbar_el1
so things i have to do:
- a real round robin scheduler
- actual drivers
- idk all the other thinsg
okay i think ill do more drivers
hmm. maybe i can do clk and pll torture
or device selection or something idk
okay lets actually do something useful
like an fwcfg driver
I like the fact it's written in Go that's pretty cool!
its not written in go
lol
the go is only the build system
the build entrypoint is basically ```sh
#!/bin/sh
set -e
mkdir -p obj
fence_rev=r22
if ! [ -e obj/x ] || ! [ -e obj/fence-buildsrc-$fence_rev ]; then
rm -rf obj
mkdir -p obj
go get ./build-src
go build -o obj/x ./build-src
touch obj/fence-buildsrc-$fence_rev
fi
obj/x "$@"```
obj/x is just the main builder program
There is no such thing as having too many assertions 
actually thats not true
"too many" just means that i cant show off my cool status code system
:^)

