#pmOS - microkernel OS for RISC-V, x86, and LoongArch
1 messages · Page 6 of 1
fair point
(because of bad design)
The same way that my pthread_mutex_unlock can also ENOMEM
Yes
ahh
Though idk I don't even have signals fully implemented
signals are :(
I only have threads in kernel
And for signals I have userspace thread per process
But idk
I'll see
"rustc has very good error messages"
nvm, I'm dumb
i have a microkernel question
how does one start a posix server without the posix system
like
wouldn't you need a lot of ipc calls to itself?
because i noticed you're not compiling with -ffreestanding
all my ipc is async
and the library thread is what makes the calls
lib/libc/src/generic/worker_thread/worker_main.c
Idk if it's a good design...
i mean, how are you able to use a hosted environment for e.g. bootstrapd
It doesn't use POSIX API
And everything is done lazilly
I mean it does
Nvm
It calls malloc and friends
same thing with managarm
It doesn't use the things provided by posix server
i don't see how i'm supposed to bootstrap e.g. the posix server without a vfs
if that needs an interpreter
lol
Come on, why you do that
?
It just loads stuff directly without the posix server?
what a joke. i mean Lol, yes it is
Also, I don't have exec* functions yet
Because I have no filesystems
(For which I'm porting Rust)
by not writing a microkernel 
I would just not use posix functions in the posix server
maybe im misunderstanding things
because e.g. posix-subsystem on managarm has a PT_INTERP entry
That's boring
I just dont know what I would typing
Just link it statically?
in mammogram the kernel handles the small set of posix requests needed to dynamically link and run posix-subsystem and all the drivers etc
for the vfs it serves stuff from the initrd and whatever you upload at runtime
fake microkernel
it's called being pragmatic
i kinda want to keep the posix server self contained
I am offering $12
I also have a very small ELF loader in kernel to load bootstrapd
same
You can also just use a flat binary
you should do scheduling in userspace or yotrure not a real microkernel
I'm planning to do that 
Idr why
But I've been thinking about it a lot and it made sense
I think Tanenbaum put the line at the VFS
performance is cringe anyways
My kernel has no knowledge of vfs
As in the kernel maintains the priority queues, and some userspace server decides on priorities, migrates the tasks and stuff like that
I remembered why
Linux added userspace schedulers in 6.12
It made a lot of sense in my mind
Where is everyone,,, now is morning
in managarm all the posix glue for servers is contained in one file so the rest of the kernel doesn't know about it
technically we could get rid of it
by having a second set of mlibc sysdeps or not using mlibc at all
@agile patrol can you stop posting random things to derail the discussion
but that'd be a major downgrade since we make heavy use of the standard c++ library in drivers and posix-subsystem
I also use C++
Sorry
Wow interisting person
yeah but i mean stuff like std::map and std::shared_ptr etc
not just freestanding stuff
banger
the only freestanding program is kernel, everything else is fully hosted
yeah and for that you need at least a skeleton of a libc
What are you mean?
and for managarm instead of having a separate libc or whatever we just have the minimum needed to run mlibc programs in the kernel instead

although it's still all done via ipc, the programs just talk over ipc to the kernel
how dumb would it be to just have the posix system be a freestanding binary 
where do you use that?
I just wrote my own freestanding containers and stuff for the kernel
this is the way
the only real syscall imple,mented should be write()
nono the kernel doesn't use mlibc
ioctl*
i mean, posix-subsystem and drivers use regular managarm mlibc, and the kernel handles more or less the minimum ipc requests to facilitate that
more or less you get stdin/stdout/stderr tied to the kernel log, the initrd in place of the vfs (read only too iirc), and mmap
My stdin/stdout/stderr just waits for the log server to appear (the first time they are used), and libc also only talks to vfs when you call open() or whatnot
It's done without vfs
well we need the vfs since posix-subsystem et al are dynamically linked
and we use dynamic libraries for more than just libc
doing that in init server seems reasonable
i mean we could, but mlibc static linking support came after all this was in place
I don't have dynamic linking yet
is the initrd mapped in the pid 1 address space?
or is it ipc
no, you do the same requests as you'd do to posix-subsystem to access files off of it
just to an ipc stream where the other lane is listened to by the kernel instead of posix-subsystem
my bootstrap server can (could in the past, it's probably broken now) also act as a filesystem
oh damn
so tmpfs driver could just open() the module passed by the bootloader
it is a small impurity in the microkernelness but it's self-contained in a 700 line file (+ associated header file)
the other code just does runService(name, controlHandle, myNewThread); and that spins up the coroutine that does the request handling
looking at the file, we handle: sigaction (noop since no signals), openat, isatty, close, mmap, read, write (only on stdin/out/err), seek, readdir
free
please just wire
It is NVidia hardware... Difficult to understanding. Move to private chat if you want money
.!naenae @agile patrol no soliciting in DMs!
also on the note of static linking, im not a fan of it, esp. in this case since i'd have to relink everything after fixing mlibc bugs or whatever, and the dependency on libc is not tracked by meson (since it's brought in by the compiler itself), so that'd be a pain
.!naenae @Dd-Aing no soliciting in other project's threads
SyntaxError
Expected type: user for argument: target but got: "@Dd-Aing no soliciting in other project's threads" (string) instead
NOTE: Attempted implicit cast from: string ->user failed (not found)
.!naenae 450856877472088066 no soliciting in other project's threads
@agile patrol is already banned. Do you want to update the ban reason/author?
ethan kernel has it
bruh wsl crashed
lol wtf did u do
maybe it got OOM'd
my laptop has 32gb of ram
Anyway, the only things missing are this rust dumbness (or more likely my skill issue), ioctl and a couple of broken things
I think this is the only thing that I need to add to my libc/Rust?
if you would use mlibc you could just autogenerate the bindings for the libc crate 
we have a script that uses libclang to parse mlibc headers and output rust libc bindings that you just copy+paste into the crate
and then you need a 2-line patch to the crade to pub mod managarm; or equivalent
does managarm have a patchset for rust on linux-mlibc?
if no i think that should be a thing
too late
I did it manually
(though I might switch to mlibc later once I stabilize my API)
if yes I should use it in nixpkgs
I don't think the question makes sense
like
linux-mlibc has no relation to managarm
yeah still what does that refer to
e.g. for gcc there's a fork i can use
but if there's no such thing for rust I'll have to do it myself (either using the rust binding generator in nixpkgs or uploading the fork somewhere
so for providing the (patched) libc crate, we pull the libc crate source, copy/paste our autogenerated bindings, and apply a small patch for it to include our bindings
ah so there's no downstream fork for it or anything and is just patched by the build system?
I'm not sure how relevant that would be on linux with mlibc tho
Someone can also just upstream mlibc to rust 
I would assume it needs something to build against mlibc at least
yeah, we just have a small patch file we apply on top of the crate source
generally we do not maintain forked repos of anything except gcc and binutils
we always just ship patch files that we apply on top of the source
wait do i understand it correctly, if mlibc was upstream, would that enable it for all projects using it?
I have Rust??
I think it just makes it easier to port
You still have to add OS-specific stuff?
isn't that then handled by libc?
is rust std entirely implemented in terms of libc calls?
would be omega based
duality of man
nah i fully stand behind implementing std in terms of syscalls
linking against libc in non-C code is cringe
after all it's the C library...
not the Rust library
for a big os, maybe
but its tedious if you have to do it on your own
libc my beloved
it's the library to the OS
OSes and languages always went together historically
no
it needs to be per-os
obv as the ABI and options selection changes
what I will attempt to do is to upstream the (auto-generated) managarm bindings
depends on the target
it needs to be per-os yes but quite a lot can be libc calls i believe
does it hello the world
Idk I had lectures
I'll test now
It probably won't link
How do I give cargo sysroot?
Do I just use normal lld?
are you rewriting pmos in rust?
ah
Like I normally link C/C++ with clang --target=x86_64-pmos --sysroot=/home/misha/pmos/i8042/../sysroot ./ports.o ./main.o ./timers.o -o i8042.elf or whatever
Is there no way to see why it's failing?
I'm too dumb for this...
no such file or directory: crt0.o?
bruh
I didn't see that
I'm blind
Do I even need crt0? 
If I'm linking with libc anyway
probably?
-nodefaultlibs so you need it yes
also test it with rust-lld too when you have crt0
Though I should have it
Does it not search in usr/lib?
(time to recompile the compiler)
(I wonder how long my laptop will survive compiling rustc on battery)
Ok, I suppose I only need compiler_rt?
Bruh it also wants this for some reason
Which I don't have
And compiler rt?
bruh my libc was compiled for i686 for some reason??
Wtf
(like this stuff builds fine on i686 and riscv)
Idk how, but I compiled it
nevermind
it just casually failed

Bruh I have no idea
(and how it just magically works on i686)
I think copying random things from chatgpt solved it...

(totally perfectly working build system)
wtf wdym

The functions are for Real Time Solaris (Real Time means guaranteed response in a time boundary), which has since been integrated into mainline Solaris and is part of libc. Librt is present only for historical, backward compatibility reasons and you do not need to reference it.
what's interesting is i've literally never had my wsl crash, but it crashed today out of nowhere
had my first time in like 5 years today
What if I just link it with clang++?
This is like an extreme jank
(it didn't work)
Missing functions to hello world
(I guess I can just stub them)
(except writev)
(it should be easy)
(famous last words)
export RUSTFLAGS="-C link-arg=--sysroot=$HOME/pmos/sysroot/ -C linker=clang++ -C link-arg=--target=x86_64-unknown-pmos -C link-arg=-lunwind -C link-arg=-static"
you will get absolutely shafted by libc ngl
?
Ok, I have no idea how to implement poll
Since everything is IPC...
ok, I think I have an idea
(though my libc is kinda casually becoming a huge mess)
Time to test
Ok, Rust really wants poll
(But it is working I'd imagine)
(enough for libc to printf the error)
!!!
Idk, now
(I need to unstub poll though)
(and a couple of other places)
(but it works for println)
I wonder if panic works
imagine if it fails to fail
does unwind work??
ah it probably does
Where does it get variables?
It's broken
It's probably trying to open /proc/self or whatever, which I don't have
but still
I didn't
surprising
isn't that linux specific though?
idk
it it is that should be guarded behind a OS == Linux check
I don't really care I guess
Go needed to open that to print backtraces
bruh I'm having a skill issue with git
gc shouldn't need it...
i guess the shitty go compiler might though
although
its not a thing on macos, or the BSDs
so
no
So I'm just gonna use it
go green threading asm is like
not hard
lol
also if you are cool with a mildly scuffed version of go there's also tinygo
rust doesn't have a thread runtime
in std
which has LLVM do the magic for you
there's stuff like tokio instead
yeah rust also takes the "let's have a stupid% performance degradation in pathological cases" approach
instead of "let's realloc() the thread stack when it gets too full"
idk
I just need to figure out how to implement my kernel's interface in Rust now...
Ok, I managed to push it to gitlab
I guess I'll just delete the local repo and clone it again 
I think I need to finally add handles to IPC
The interface gets clunkier every time...
(I'm currently writing mbus-inspired mechanism for device discovery)
cool!
Like they could both be useful to distinguish various async IPC message replies, create permission memes, and also have some system to track the servers programs dying and not replying to IPC
The only issue is that I need to have some mechanism for handles to be shared between several threads
(for which I could use task groups which I already have, now that I think of it)
The only thing I'm worried about is performance, since it would be allocating more stuff for every IPC message...
Also, I want to try and write that server in Rust
do you have an async runtime or something like that?
I think I can also tie it to the init server (which I don't have at the moment), so that it can directly start necessary servers for published devices, without doing what I do now, which is running drivers, and then having them fork
No, but that can be synchronous
My C++ stuff is async
at least for parity
I need to investigate how it's done
In Rust
basically: like C++, but dumber
there is a "Future" trait
which has one function named poll
which takes a context and returns a future status
the context contains a waker
when you want to do io, you return a future which, when polled, tries to read/write/whatever
and if it blocks, it arranges for the waker to be woken up when it is ready
so yeah like c++ but dumber
its not that dumb tbh
yes it is tbh
the C++ approach produces faster code
because their wakeup logic wakes up a coroutine instead of queuing a coroutine to be scheduled
in rust, you need a scheduler
yeah
like the original top-level waker is provided by the scheduler

yes.
"the" scheduler = the userspace future scheduler that is
yeah C++ coros are a lot easier to work with
the main disadvantage is that they allocate implicitly
Like I have an event loops, which block on reading from IPC queues
and aiui they allocate one allocation more
And then when a message arrives, I just run the blocked coroutine which that message was for
Or create new coroutines
(idk what's the term for awaking)
yeah and in rust you ALSO need a scheduling queue to go along with it
so a message arriving would unblock a future
and then you run the unblocked futures until all of them are blocked
(yes this is stupid)
So the future would have just 1 coroutine in its queue?
Ok I will not use coroutines for now then
(Also, I need to implement handles first anyway)
a rust "future" is like a c++ "coroutine"
this sounds weird
except its implemented differently
since in rust futures only run when the future scheduler decides they should
and not immediatly when work completes
it's hard to implement but it's basically what you need to do if you want to not provide a thread runtime in the std lib
no its not lol
C++ doesnt need it
libstdcxx provides a thread runtime tho right?
no
libstd provides the same amount of thread stuff as libc++ and libstdc++
tbh I'm not well versed into this so i can't really say anything
but my experience with rust async (including implementing a simple thread runtime) was nice
the biggest issue is that the ecosystem is split between async and sync
sans-io ftw
C++ allows you to do wtf you want with coroutines
the main difference is that C++ coroutine wakeup isnt handled by the scheduler, but by the individual coroutine being woken up
which makes impl significantly easier
its also faster in certain semi-pathological cases
that's only a minor inconvenience
for rust?
it makes code quite a bit uglier
for c++
it's negligible imo
probably
the important factors are that rust wakes up the root coroutine
and not the leaf coroutine
which is not negligible
Bruh my Rust abstractions to kernel API just work..?
(I think I'll write more tomorrow, if I have time)
based
Now I just need to implement the parsing of the stuff that I send over IPC...
(It will be C, C++ and Rust programs talking together)
(also I maybe really need to look at using xbstrap or something, since Makefiles are really rough for building 2 compilers and 12 executables...)
now implement pop_front_async
also damn you have std, very nice
I need to implement handles for IPC first
handles for ipc?
Like for IPC ports/chanels/whatever
Yes
ah
Like I send a message and give provide a handle to reply
(that's what I want to do)
ahhh
And then I can distinguish where messages come from
And it would also be a great permission system
which allows the sender to, well, send exactly one message in return
But I want to have one port, and give out different "handles" to it
Idk
I don't have a mechanism to block waiting for more than 1 port
And I don't think I need to
yeah thats the point
What I do at the moment is I usually include "user_arg" field inside the messages, which can be used to know which message was replied to
when you do an ipc call, you send (call arguments, send once port right)
But it's obnoxious and not universal
and then when they return, they send a message to that send once port right
I just send
(at the moment)
But I want it to send a handle, along with message
Which would resolve to a port
But the sender would know the id of the handle which was assigned when a message was sent
So even though the messages would all still come back to the same 1 port
(which I can read from in an event loop)
I can universally have sync IPC-lookalike behaviour
To know which coroutine to unblock/schedule/resume/whatever by the handle
Idk
(I think I'm repeating myself)
Like I've already added a mechanism to send memory objects
Which in reality are handles for memory objects
It shouldn't be too difficult
And since my ports are bound to threads, a lot of the stuff could be CPU-local
And I have RCU in kernel...
So like I'm hoping the performance shouldn't be bad
For zero-copy IO
you could make it possible for the sender to attach a pointer-sized field to the send-once port, that is then included in the response.
The thing is, how do you handle the responses?
Like take a disk driver, it pops a bunch of requests from the message queue, sends commands to disk, blocks for however long the operation takes, and replies
There needs to be some mechanism to explicitly reply to a message
yup
Another thing I've though about it holding onto messages
it responds by writing to the send-once port included with the message it is replying to
But then this is problematic
"everything is SCM_RIGHTS" kernel 
not really. a possible solution: the send-once handle is a 'thin wrapper' around a 'real' port handle, that simply sends a message to the real port along with the auxiliary dara attached to the send-once port
And that's what I'm calling a "handle"
and multiple send-once handles can point to the same port
literally this
(maybe the name is bad)
But it's not a bad API
But I really need to think about it
Since I want to have a mechanism to send multiple handles at a time
Using my libc 
send-once port is bad too. because they might not actually be send-once, e.g. if you decide to expand the functionality to be able to reply more than once. it might be worth designing it such that a process can instantiate a special kind of handle to a port that associates auxiliary data with all messages sent to it, and then making the 'send-once' part a usage detail
Yeah, so I'm thinking about reply once and reply many, with optional transferable rights
And also ports requiring handles by default, but some ports could be public
like a general facility for creating these, and it can specify a usage limit
Although don't even know if that's necessary
I can move my ports discovery mechanism to userspace, and just have that server hand out handles
as in open_port("/path/to/port")
you can make ports implicitly private by not giving them a name
mach has a special "send once" port right which explicitly requires one message
And anyone can send to any port
and a "send many" port right
idk. I'm not a microkernel expert lol
And you can 1. guess them, 2. send to them once you know them
So like there's no protection
yeah, that feels like a fundamental flaw to me
And then I have a global namespace kind of thing, which allows to get port id by its name, if the port owner decides to publish it
That's how I discover things atm
It's simple and apparently somewhat effective, the only problem is that there is no protections whatsoever
But this will solve everything at once
Another problem is that the kernel doesn't know about processes
So the issue is in making a handle available to different threads which form part of the same process
'handles' are not part of this global port namespace, i.e. a malicious or faulty process cannot guess and write to a 'handle'?
I don't have them at the moment
I'm thinking of making them global
Although potentially not
you can have a refcounted fd table (or, well, fd table equivalent), and tasks can choose to unshare them at fork
The issue is not with fork
The only thing that's common in a process inside the kernel is a page table/address space
So there is nothing to latch to
Idk how to explain it
Like a filesystem would give a handle for a file
But then, how do I share that with threads inside a process
to me, this feels like the 'obvious' solution. keep a pointer to the fdtable in struct task or whatever.
My fdtable is in libc in userspace
a task doesn't have a private handle namespace. rather, it is shared among tasks.
yeah, I should've called it something else (like handle namespace)
task groups (I'm bad at naming things) 
But there can be many
So I will have to pass task_group + handle to ipc_send
Which will be 2 uint64_t's
Which don't fit into i686 registers for syscalls
why? the kernel should know the current task anyways, right?
Yes, but any given task can be part of as many task groups as it likes
the pointer to the handle table is not kept in a 'task group', rather, it is refcounted and pointed to by each individual task that makes up the task group
I could make handles global, and check that the task is part of task group that the handle is attached to
ok this, this is an interesting design decision.
what is the usecase for this?
It is done like that so that task groups can be used to check the object lifetimes (?)
Like a filesystem consumer opens a file, and gives a task group. That task (thread) group can represent a single thread, a process, a set of tasks (threads), maybe two processes
(task is thread)
(I need to work on naming)
This also applies to disks, devices, and so on
what is the purpose of doing this? like are there any end users that actually do this?
No
well then
I just create a task group for the process and use that everywhere
But it seems like a nice abstraction
Instead of doing that for the whole process, it could be done for select threads
does the kernel know about task groups?
It's provided by kernel
and it has a mechanism to notify userspace when there are no threads left in a group
So if the process dies, the files are closed in servers, and so on
(and it's also used for permission checking)
you could also, like, not use 64-bit integers for both of these.
like what are the odds that someone will exhaust 32 bits of task group id space?
idk
I could just do this
yup, but then you lose out on some of the benefits of a 'local' handle table
what it does is lock the refcnt to 1
you could also notify when an end of a "socket" closes. (where a "socket" represents a sender/receiver handle pair, and if the sender dies, yell at the receiver)
do I need it?
for signals
I meant do this instead of notifying when a process dies
have an handle namespace per-process
I don't have processes
no
@carmine nacelle it feels like you are overcomplicating object management by, like, a lot.
but its refcount can be nonzero
(e.g. you can have a different send-once right to the same port)
"Send-once rights always have a user reference count of one. Tasks may hold multiple user references for dead names. Each send-once right generated guarantees the receipt of a single message, either a message sent to that send-once right or, if the send-once right is in any way destroyed, a send-once notification."
If you haven't noticed, everything (not just object management) is more complicated than it should be
yes
I am talking about the right
not the port
That's what I want to do
yup. and iiuc, the (or rather, a) problem is how do we make sure that the sender has the right to respond to the message? and a proposed answer is local handles
pmOS is about 60k LoC (excluding external libraries), and I don't have a shell, or a filesystem driver
What's the problem with global though
what can it do tho
idk
sender?
no lol. I meant receiver
u just pass an handle along with your message
Like I ported rust std, so I could write ext4 driver with it
last time I checked, Davix was 30k LoC, and approx. 20k of those was from a generated test case for a kernel datastructure 
but like
my kernel doesn't do much either (hardly anything)
and LoC is a bad measurement unit anyways
im currently microkerneling so the kernel isnt doing that much either
I should finish writing my in-kernel debugging tools (unwind and symbol lookup), and then implement the page cache so my files can actually have contents lol
I had libunwind and libcxxrt with C++ exceptions in kernel, but I've removed that
Now I just kinda keep my fingers crossed that the kernel doesn't crash since it only writes to COM0 on panic
When running it on laptops and whatnot
real

By default, HashMap uses a hashing algorithm selected to provide resistance against HashDoS attacks. The algorithm is randomly seeded, and a reasonable best-effort is made to generate this seed from a high quality, secure source of randomness provided by the host without blocking the program. Because of this, the randomness of the seed depends on the output quality of the system’s random number coroutine when the seed is created. In particular, seeds generated when the system’s entropy pool is abnormally low such as during system boot may be of a lower quality.
Good and bad news 
🪨
Also second panic while unwinding was in progress
I know it's because of syscall that's not implemented by kernel
Imagine syscalls
Which function does write call?
(ok, I think I'll just not print long strings then and pretend it's working
)
This is unfun
you said you'd implement it
I'm just gonna use BTreeMap for the time being 
There are too many things that need to be implemented
Box<str> pains me for some reason
What am I supposed to use instead ?
i mean, i guess it ends up being functionally equivalent because of Deref<Target=str> (or smth, idk rust lol) but String is a nicer, owned string type
but as i said, Box<str> works as well
I've read that it stores 2 usizes instead of 3
Although does Rust do SSO?
SSO has been intentionally left out, because it adds trade-offs (like extra checks on each data access) and you can use a library to implement your own flavor of SSO (there are many crates for this with various levels of compactness and unsafety).

this hurts to read for some reason
cause serialization and deserialization hurt to do in higher level languages
speaking from experience
it's much much easier to do it in C
I've done it in JS with a kind of messaging protocol between JS and C (some IoT things) and man it was awful to write that in JS
port tokio and make the api nonblocking
i wonder how hard it is to port tokio
considering it basically reimplements std
something simpler might be prefferred
pain
did u do it?
no but i looked at tokio internals
i wonder if async-std is simpler
or one of the lower level stuff (that don't reimplement std)
pmos all servers in rust when
my opinion is that userspace drivers would be fun in rust
but I won't do them for stuff that needs to be optimised for binary size
sure
e.g. i don't need huge rust binaries for a uart driver
ns16550 and friends would be so simple to implement, doing them in C or C++ wouldn't make you lose anything really
I'm not rewriting everything in Rust
(yet)
Also I need to implement a bunch of stuff
Namely finish pmbus, fix AHCI, write ext4 driver, implement IPC handled/whatever
At which point I might actually finally implement a slab allocator in kernel
name it premarketOS
in rust, x86 means i*86
and with i*86 the only way to do anything on more than 4 bytes is cmpxchg8b
seems reasonable
so i386 as well?
i386 and i486 don't have cmpxchg8b and thus it is impossible to do atomic ops on 64-bit values
well you can use libatomic but that's implemented with spinlocks
ok, that's reasonable then
also note that that's only saying that atomic loads on 8 bytes are only guaranteed to work on rw memory, not that it's not supported
i suspect an atomic load of 8 bytes gets lowered into cmpxchg8b(ptr, x, x)
xmm isn't guaranteed to be available on 32 bit
but if it is yeah that's an option
i686 is a subset of 32 bit x86 that is guaranteed to support sse (like x86_64)
specifically i686 = everything the pentium pro (P6 microarch) supports
i586 def doesn't support xmm, it doesn't even guarantee mmx
but yeah so on i386 and i486 it'd get lowered into libatomic, i586 to cmpxchg8b, i686 to sse (probably)
Also maybe floating point instructions?
how does x87 help with atomics
is it not atomic as well?
ah you mean like loading something as a double and copying to a cpu reg
idk what the atomicity guarantees were like on pre-i586
on i586+ it'd work probably but then you have cmpxchg8b so why would you
isn't cmpxchg8b slow?
like if you just want to read something with relaxed ordering
depends on implementation and whether it's successful
relaxed ordering isn't a thing on x86, everything is acq rel
or well there's some newer simd instructions that might have relaxed ordering idk but in general everything's acq rel
but I mean if you just want to read a value from memory, not exchange it
lock cmpxchg8b is def much more expensive than a normal mov yeah
but idk how it compares to fp load + copy to int reg
clang on i586 uses fp ops so i guess that's more performant than cmpxchg8b
Bruh, recieving objects with Rust is easy, now the question comes, how do I serialize them...
Since with C/C++ I just sent structs or vectors directly, but it doesn't seem to be easy with Rust idk
I've talked to ChatGPT and it just said to use std::borrow::Cow
My data is represented as C structs
im afraid that asking chatgpt might not be the best thing you could have done (lol) but oh well, let's see what you come up with
maybe i misunderstood what you're trying to do :^)
bytemuck Pod?
okay, sure
if you just want to bytemush the whole thing over ipc then that would work i guess
strings aren't repr(C) though lol
I want to bytemuck static stuff, and serialize into Vec or Box the dynamic stuff
Hence Cow
Which is a universal representation of both without writing my own enum for either &[u8] or vec
yeah i understand, might work
native rust bindings > generated rust bindings
though if you want you can write rust wrappers around generated bindings
I'll just write that stuff myself
Is there a way to implement some sort of universal thing on everything that's bytemuck Pod?
So it translates it to Cow

There's no function overloading in Rust??
Ok, I'm gonna try something...
Is it a bad idea to use #![feature(default_field_values)]?
What the fuck
error[E0726]: implicit elided lifetime not allowed here
--> /home/misha/pmos/rust/pmos/src/ipc_msgs.rs:29:18
|
29 | impl Default for IPCBusPublishObject {
| ^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
|
help: indicate the anonymous lifetime
|
29 | impl Default for IPCBusPublishObject<'_> {
| ++++
I'm extremely dumb
How am I supposed to do this?
pub const IPC_BUS_PUBLISH_OBJECT_REPLY_NUM: u32 = 0x1b0;
#[repr(C)]
#[derive(Debug, Copy, Clone, Zeroable, Pod)]
pub struct IPCBusPublishObjectReply {
msg_type: u32,
pub flags: u32,
pub result: i32,
reserved: u32,
pub sequence_number: u64,
}
impl IPCBusPublishObjectReply {
pub fn new() -> IPCBusPublishObjectReply {
IPCBusPublishObjectReply {
msg_type: IPC_BUS_PUBLISH_OBJECT_REPLY_NUM,
flags: 0,
result: 0,
reserved: 0,
sequence_number: 0,
}
}
}
Like so that msg_type and reserved are private, but always set
Without writing new for everything
- mut memes
this is what I would have done
but your approach should also work
efficiently and generically
I don't understand how anything could be more efficient than just casting structs to pointers
make msg_type an enum pls :3
And I'm not using json or other stuff for IPC
Like this is simple, effective, fast and easy to use
And works across 3 languages
I don't understand what you're looking for
you can implement Default
if that's what you want
or you can pass arguments to new
It complained that default fields are private
Like idk so that someone can do ```rust
let msg = IPCBusPublishObjectReply {
flags: 0,
result: whatever,
sequence_number: bruh,
// The rest is default
}
..Default::default()
there's a spread operator
and I would just pass arguments to new
Doing ```rust
let msg = IPCBusPublishObjectReply {
flags: 0,
result: whatever,
sequence_number: bruh,
..Default::default()
}
(I've tried it and rustc refused to compile it)
yea the issue isn't default
the issue is that you can't construct a struct with private fields this way
also don't put a semicolon there, put a comma
I want msg_type to be constant, but it needs to be there
IPCBusPublicObjectsReply::new(IPCBusPublicObjectsReplyFlags::WHATEVER, whatever, bruh)
And I don't want to have to copy the struct every time I send a message
why would it copy this way and not copy your way?
And this initializes the private variables?
if you do so in new() yes
new isn't special
it's just an associated function (aka static method)
it can take arguments
No, I mean I wanted to initially omit the type field and just add it when serializing
But I don't like it
yea then keep it in the struct
So the struct is just normal rust struct, and the data sent between stuff is different
impl IPCBusPublishObjectReply {
pub fn new(flags: u32, result: u32, sequence_number: u64) -> IPCBusPublishObjectReply {
IPCBusPublishObjectReply {
msg_type: IPC_BUS_PUBLISH_OBJECT_REPLY_NUM,
flags,
result,
reserved: 0,
sequence_number,
}
}
}
also please make FLAGS an enum unless it really needs to be bitflags
and if it really needs to be bitflags, use bitflags or whatever the crate was named
only make it an integer if you don't want to depend on that crate
rn you could be passed an invalid flags and would have to check it manually
if you had a IPCBusPublishObjectReplyFlags type where only the valid flags can be constructed, you don't have to check that
Flags is basically a reserved field atm
(it doesn't matter for now)
I want to see this working, I will add this stuff later
(later as in before I push the commit)
How do I pass int into bitflags! memes?
(bruh, vscode is as lost with Rust as it is with C++)
(idk which is worse)
Creative variable naming (result.result)
What the fuck
Why does it suddenly want linux?
it's being used by your main.rs directly
possibly something lsp did based on your host system?
yeah it autoimported based on your host system
Fun, I think it's working
(better screenshot)
The top 4 lines are printed by Rust, the bottom one is printed by AHCId/C++
(Now there is too much stuff to do)
That is (not including all my previous plans):
- Implement requests for stuff from pmbus
- Publish ACPI and PCI devices in pmbus (which needs to be done in C
) - Write an init server, which matches devices in pmbus and starts servers this way as they appear
- Implement handles to radically decrease the obnoxiousness of IPC API (among other things) and provide a good interface for coroutines
- Change blockd to discover disks with pmbus, discover filesystems using it (talking to init server, see step 3), publish partitions onto it and do disk mounting (etc.)
- Finish (though it's almost not even started) the ext4 driver
(this implies improving VFS, which I wrote when I was experimenting with ChatGPT in 2023, so the code is very bad) - Do what @ infy wanted, that is discover PS/2 controllers with ACPI (now if I do 2, everything can be discovered like that...)
- ... improve the build system (it's so bad I'm having to call cargo build and then make (several times) and then qemu manually to run pmOS)
I'm going to sleep, this is too much
(though I guess I can just call cargo with make
)
when will pmOS get suspend support????

lmao
that warning message is useless
they could've just said "you probably meant to do *obj"

(I think it's stack overflow?
)
How does this even GPF?
Wtf, how does this get to shit in %rcx?
This was a fat UB
Ok, I don't know what to do
So I guess I'll finally look at xbstrap
Or something
(improve my build system that is)
I don't understand anything
real
xbstrap my beloved
nixstrap my beloved 😍
Idk, I need something which can build (in this order) llvm, my C libraries with CMake, rustc, libc++ and company, kernel with CMake and a bunch of userspace (about 10 servers, with more planned
) with Make and Cargo (basically C, C++ and Rust), and create the root image
Like maybe I'm dumb, but I haven't figured out how to make cargo produce output binary in the same location
That's not in target/{arch}/bin/whatever (idr the exact path)
Since I'm targeting 3 architectures, with more to come
I think you can make it "install" to some destination dir
but nix will do it :3 (nixstrap shilling
look at how gentoo does it
I doubt the gentoo cargo eclass or whatever is complicated
well it also vendors in crates so it likely is but you don't need to do that
but for maximum reproducibility you want to read the cargo.lock file and vendor in the needed crates
then pass --frozen or whatever to cargo so it doesn't fetch from the internet
I don't really care about reproducibility, but the way I'm passing files/programs to be loaded is as modules in Limine/Hyper
Like I don't have a proper filesystem
but if you don't care about reproducibility, wrapping cargo is fine
So my makefiles are just kinda hardcoded to assume files in their locations
And Rust was producing that
Idk my build system is a mess
Maybe I should finally have an init ramdisk or something like that
So I can just dump a bunch of files into a directory and it be dynamic
Without having to change the bootloader configs
yea I don't have that problem because I do like '${pkgs.whatever}/bin/whatever'
and the nix recipe will install to the store so where cargo puts it doesn't matter
But then I don't want everything in ram (eventually)
Idk I need to think
Maybe I'll consider it
you know you could do like string substitution to get the path to the binary in target/
then copy it to your ESP or wherever
for now
the exact path is target/release/{arch}/whatever
Ok I've just managed to do what I want with makefiles
The build system migration effort is on halt until I get S3 sleep working 
obos but microkernel
I need to implement some cool feature for obos
to get it known for something other than suspend
and crashing all the time
bruh I'm having a skill issue with a relative far jump
no
make it networking™️ cuz you're already working on that
roleplay making an OS in the 90s
oboslop
and be like ¡¡ THIS IS THE INTERNET IT IS THE FUTURE !!
make it not crash or build an era-appropriate network for it
also 5 minutes is gokd
good
I could never let my stuff run for 5 minutes, not because it wouldn't work but because I couldn't wait that long
llvm optimizes my memcpy memes to nopl (%rax)
Could it be llvm miscompilation? 
I know what's happening...
Fun, my pmm decided that SMP/ACPI bootup vector will be at 0x9E000
(also, maybe hyper port will get smp now that I'm implementing it anyway...)
was it?
im gonna guess no
I've been copying char * to void **, which llvm was assuming to be aligned to 8
Which was emitting broken assembly
yeah thats ub
hmm actually
is it
(Which was the end of a section in assembly)
i dont think memcpy of anything to anything is ub
I declared the variable as char and it worked
Tbh I'm memcpying code to some random place in memory
actually no it can be ub if the two pointers overlap
well ok
or if there is a data race
no its UB i think
the parameters are restrict, passing overlapping pointers is UB
no its clear ub
they don't
yeah ^^
It was an issue with alignment
memcpy doesnt really care i think?
If copying takes place between objects that overlap, the behavior is undefined
N3219 (c23) is pretty explicit about it being UB i think
with restrict it makes sense
It's C++23 
the alignment UB here isn't in the memcpy, that's just where it manifests itself
it's UB to have a misaligned pointer
yeah id say so
actually no
yeah clang's intrinsic rightly assumes that your pointer to a void* is aligned to a void*
A pointer to an object type may be converted to a pointer to a different object type. If the resulting
pointer is not correctly aligned59) for the referenced type, the behavior is undefined
n3220 6.3.2.4
ah okay
it's UB to produce a pointer that is unaligned therefore it is UB to have one
6.3.2.4 doesnt seem to say that
6.3.2.3 sorry
well first of all i don't think thats necessarily true
was looking at the bottom of the page
ah okay
llvm notices that the pointer you give to memcpy came from a type that has to be aligned, and optimizes memcpy assuming that the pointer is aligned
I had a misaligned global variable
(which wasn't a variable)
that is hard UB though anyway
how'd you manage that
because llvm can see the alignment
oh wait declaring it in asm?
Yes
.equ CODE_SEGMENT, 0x08
.equ DATA_SEGMENT, 0x10
.data
init_vec_stuff:
.code16
.globl acpi_trampoline_begin
acpi_trampoline_begin:
.globl acpi_cpu_startup
acpi_cpu_startup:
cli
movw %cs, %bx
xorl %ecx, %ecx
movw %bx, %cx
addl $(acpi_trampoline_gdt - acpi_cpu_startup), %ecx
movl %ecx, %cs:(acpi_tampoline_gdtr_addr - acpi_cpu_startup)
lgdt %cs:(acpi_trampoline_gdtr - acpi_cpu_startup)
movl %cs:(acpi_trampoline_startup_cr3 - acpi_cpu_startup), %eax
movl $0x01, %eax # Protected mode
movl %eax, %cr0
jmp 1f
1:
.code32
movl %eax, %cr3 # cr3 (Page table)
movl %cr4, %eax # PAE
orl $(1 << 5), %eax
movl %eax, %cr4
movl $(1 << 10), %eax # LME BIT
xorl %edx, %edx
movl $0xC0000080, %ecx # IA32_EFER
wrmsr
# Enable paging
movl $(1 << 0) | (1 << 31), %eax
movl %eax, %cr0
movw $DATA_SEGMENT, %ax
movw %ax, %ds
movw %ax, %ss
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
shr $16, %ebx
leal (acpi_trampoline_kernel_entry - acpi_cpu_startup)(, %ebx, 1), %esp
leal (_64bitst - acpi_cpu_startup)(, %ebx, 1), %eax
pushl %eax
lretl $CODE_SEGMENT
_64bitst:
.code64
jmp *acpi_trampoline_kernel_entry(%rip)
.p2align 4, 0xCC
acpi_trampoline_gdtr:
.space 2 # alignment
acpi_trampoline_gdtr_limit:
.word 23 # 3 entries - 1
acpi_tampoline_gdtr_addr:
.long 0
acpi_trampoline_gdt:
.quad 0
.quad 0x00af9b000000ffff
.quad 0x00af93000000ffff
.globl acpi_trampoline_startup_cr3
acpi_trampoline_startup_cr3:
.space 4
.globl acpi_trampoline_kernel_entry
acpi_trampoline_kernel_entry:
.space 8
scratch_stack:
.space 8
.globl acpi_trampoline_startup_end
acpi_trampoline_startup_end:
does it not include C's language about how converting a pointer requires said pointer to be correctly aligned for the destination type?
I memcpy from init_vec_stuff to acpi_trampoline_startup_end
ah yeah that'd require them to be declared as char
And the acpi_trampoline_startup_end was not aligned to 8 even though I declared it as extern void * in C++
first of all, just because it is C ub to convert it, doesn't mean it is C ub to have a misaligned pointer
Since clang didn't let me declare it as void
Memecpy compiled to ```x86asm
ffffffff80013c4d: 31 d2 xorl %edx, %edx
ffffffff80013c4f: 90 nop
; *((char *)dest++) = *((char *)src++);
ffffffff80013c50: 0f b6 34 02 movzbl (%rdx,%rax), %esi
ffffffff80013c54: 41 88 34 16 movb %sil, (%r14,%rdx)
ffffffff80013c58: 0f b6 74 02 01 movzbl 0x1(%rdx,%rax), %esi
ffffffff80013c5d: 41 88 74 16 01 movb %sil, 0x1(%r14,%rdx)
ffffffff80013c62: 0f b6 74 02 02 movzbl 0x2(%rdx,%rax), %esi
ffffffff80013c67: 41 88 74 16 02 movb %sil, 0x2(%r14,%rdx)
ffffffff80013c6c: 0f b6 74 02 03 movzbl 0x3(%rdx,%rax), %esi
ffffffff80013c71: 41 88 74 16 03 movb %sil, 0x3(%r14,%rdx)
ffffffff80013c76: 0f b6 74 02 04 movzbl 0x4(%rdx,%rax), %esi
ffffffff80013c7b: 41 88 74 16 04 movb %sil, 0x4(%r14,%rdx)
ffffffff80013c80: 0f b6 74 02 05 movzbl 0x5(%rdx,%rax), %esi
ffffffff80013c85: 41 88 74 16 05 movb %sil, 0x5(%r14,%rdx)
ffffffff80013c8a: 0f b6 74 02 06 movzbl 0x6(%rdx,%rax), %esi
ffffffff80013c8f: 41 88 74 16 06 movb %sil, 0x6(%r14,%rdx)
ffffffff80013c94: 0f b6 74 02 07 movzbl 0x7(%rdx,%rax), %esi
ffffffff80013c99: 41 88 74 16 07 movb %sil, 0x7(%r14,%rdx)
; while (n--) {
ffffffff80013c9e: 48 83 c2 08 addq $0x8, %rdx
ffffffff80013ca2: 48 39 d1 cmpq %rdx, %rcx
ffffffff80013ca5: 75 a9 jne 0xffffffff80013c50 <limine_main+0x29e0>
Even though the size was 0xb4
what is mozbl wtf
in C the only way to get a misaligned pointer is by converting a pointer to a type that has stricter alignment requirements though
ah load
is this supposed to crash or something?
idk clang memes (I need a better memcpy)
actually no
notice how it prints None even though the value being printed is Some(...)
ah fair enough
oh lol wtf
i did not notice that
for some reaosn
btw why does clang unroll memcpy to copies of byte width?
i have no idea
it shoulkdn't
unaligned memory?
Like without mgeneral-regs-only it unrolls to 4 xmm loads then 4 stores
the wording in the C++ standard is:
Attempting to create an object ([intro.object]) in storage that does not meet the alignment requirements of the object's type is undefined behavior.
isel does not have alignment constraints
fair enough
or avx memes if I enable that 
