#Maestro - Linux-compatible-ish OS in Rust
1 messages ยท Page 6 of 1
I just wasn't expecting to have to use so many registers
- update Rust
- kernel modules don't build anymore
๐ค๐ค๐ค๐ค๐ค๐ค๐ค๐ค๐ค๐ค
adding a bunch of internal documentation: https://github.com/maestro-os/maestro/pull/67
many such cases
when i do that i need to nuke my build dir
that fixes it
it's broken in the CI too so I guess this won't solve it
$ ../build
Compiling ps2 v0.1.0 (/home/luc/Desktop/dev/maestro/mod/ps2)
error: linking with `x86_64-elf-ld` failed: exit status: 1
|
= note: "x86_64-elf-ld" "--version-script=/home/luc/Desktop/dev/maestro/mod/ps2/target/x86_64/debug/deps/rustcnzzO0n/list" "--no-undefined-version" "/home/luc/Desktop/dev/maestro/mod/ps2/target/x86_64/debug/deps/rustcnzzO0n/symbols.o" "<31 object files omitted>" "/home/luc/Desktop/dev/maestro/mod/ps2/target/x86_64/debug/deps/rustcnzzO0n/rmeta.o" "--as-needed" "-Bstatic" "/home/luc/Desktop/dev/maestro/kernel/target/x86_64/debug/libkernel.rlib" "/home/luc/Desktop/dev/maestro/kernel/target/x86_64/debug/deps/libcore-0ea7efbfa1b40ef8.rlib" "-L" "/home/luc/Desktop/dev/maestro/mod/ps2/target/x86_64/debug/deps/rustcnzzO0n/raw-dylibs" "-Bdynamic" "--eh-frame-hdr" "-z" "noexecstack" "-L" "/home/luc/Desktop/dev/maestro/kernel/target/x86_64/debug/deps" "-L" "/home/luc/Desktop/dev/maestro/kernel/target/debug/deps" "-o" "/home/luc/Desktop/dev/maestro/mod/ps2/target/x86_64/debug/deps/libps2.so" "-shared" "-soname=libps2.so"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: x86_64-elf-ld: /home/luc/Desktop/dev/maestro/kernel/target/x86_64/debug/libkernel.rlib(kernel-1a6ff93b310e3a7e.9ihszl50ykblvwpc6ktwsaep5.0am2d36.rcgu.o): relocation R_X86_64_32 against symbol `multiboot_entry' can not be used when making a shared object; recompile with -fPIC
x86_64-elf-ld: failed to set dynamic section sizes: bad value
error: could not compile `ps2` (lib) due to 1 previous error
You're mixing PIC and non-PIC code
That's what causes this
mfw assembly
currently writing that blog article
can't put the current context to sleep in the NVMe driver when reading the disk before the PID 1 process been created because putting the context to sleep requires to be in a process
๐ค๐ค๐ค
I need to put it to sleep to wait for an interrupt after adding something to the submission queue
or should I create the PID 1 process before any disk access?
that means I cannot immediately open stdin/stdout/stderr, but I can add them later I guess?
i would do this but with a hlt as if i understand correctly you want to have disk access before starting the scheduler?
why?
yeah
when creating the PID 1 process I do:
let tty_path = PathBuf::try_from(TTY_DEVICE_PATH.as_bytes())?;
let tty_ent = vfs::get_file_from_path(&tty_path, true)?;
let tty_file = File::open(tty_ent, O_RDWR)?;
let (stdin_fd_id, _) = fd_table.create_fd(0, tty_file)?;
do u not support kernel threads?
when the kernel starts it's PID 0 so that's the idle thread (maybe I it shouldn't be)
putting the idle thread to sleep is a bad idea
should I have a kernel thread while booting?
imo that's unnecessary, i would just have it check if the current process pointer is valid and if it is not then resort to another method of sleeping
nah that's fine; my kernel has the cleaner process as pid 0 but that's because i create it before the idle process (which is created in arch-specific code which now that i think of it is stupid)
the thing is, my current process ptr cannot be NULL. If there's nothing running then it's a pointer to the idle thread
how do you handle that? @shell tusk do you have a kernel thread from the very beginning?
ah. then maybe you could have a flag
I don't feel like having a special case for that is a good idea. There probably will be other places in other drivers where I will have to do that too
hmm
yeah from the moment the scheduler is up it runs in a kernel thread environment
before that it doesnt sleep at all (controlled by init dependencies and shit) and after that it can sleep
do you start the scheduler before doing anything with drivers/disks?
yeah
my 2 cents are that initializing the scheduler, in particular the idle threads (one for each core), then switching into an initialization thread (which can safely block, because it is not an idle thread), is literally the very first thing done by the kernel proper in NT
(aside from basic architectural initialization)
this is done long before any drivers are up
or other subsystems
OH MY GOD ALL OF YOUR FINGERS ARE BENT THE WRONG WAY WHAT THE HELL HAPPENED
DO YOU NEED AN AMBULANCE
what I do is I initialize my equivalent of Ex in a thread and that's done after SMP bringup and kernel initialization, but smp bringup requires memory allocation so a portion of the vmm is also initialized there so it's probably not as clear of a distinction as NT
lmao
why does it need memory allocation
to allocate per-cpu data
chads do this in the bootloader/prekernel
I dont have that
so theres no real other way of doing it
i have an early vmm init
then I do full vmm init in ex_init
(which includes the per-cpu caching portion of the slab allocator)
I have kernel threads. As in. You can just create new threads that run in kernel mode whenever. So in that model you could just spawn threads all day before you spawn init because they don't use up process IDs
As to this, I just bring VMM up all the way before I start the scheduler followed by the other CPUs.
Like rustc, my CPU-local data is just heap allocated
I mean i do the early vmm init before the scheduler
Does your complete VMM init depend on your scheduler, then?
my percpu data is only partially allocated at runtime
the bsp always has its percpu data as part of the kernel binary, and is only extended by modules
well yeah the bsp is statically allocated
maybe I should make log messages prettier by adding colors, a status (info, warning, error, etc...) and the uptime before them

@inner pilot you can't get the uptime before initializing clocks, right? do you just display messages without uptime before you init those?
with github making you pay for self-hosted runners I am now considering using something else than github actions for this project
this has the same energy as plex making you pay to have hardware acceleration on your own server
in any case, github actions is crap, let's be honest
it works
I have written a 2025 retrospective blogpost for Maestro, now I just need to make a banner for it and I'm ready to post it. tomorrow I guess unless I find something wrong in it
quick banner I made for the blog article (not sure for the font)
will reuse it for the next years
yesterday I used AI to vibe code a tool that converts OKLCH into hexadecimal in SVG (because OKLCH is very recent and many tools don't understand it). Dirty code but it works
https://github.com/maestro-os/maestro-lnf/blob/master/tools/src/main.rs
Maestro operating system's Look And Feel. Contribute to maestro-os/maestro-lnf development by creating an account on GitHub.
the 30 line vibe coded program ๐

You laugh but for programming this kind of "quick'n'dirty" thing is exactly what LLMs are good at.
i am not pretending like i wouldn't do that
i am not pretending either, because i wouldn't
especially when there's literally a crate for it

how is it called?
you used it
or rather, the AI used it

converting between color spaces is the (arguably) hard part
i thought you were saying "there's a crate specifically to turn oklch to hex in svg files"
tons of things have been done but badly
that's the beauty of it
you google waht you are looking for
and find a shitty crate
that depends on nothing but still pulls in std and for some reason it hasnt been updated for 10 years
I never trust crates that don't have many downloads or haven't been updated for more than a few months
mfw "there's a crate for everything" but it turns out there are no crates for nothing so u gotta write everything urself
Next years goals:
Running X (the windowing server, not the neo-nazi social network) or Wayland.

Drew DeVault emailed me to congratulate me for my last blog article. This is the second time he emails me so I guess he's following my blog
so, I need to figure out how interrupts and PCI work together
for my NVMe driver
so that I can get an interrupt when something completes
wild
you don't need that
yeah but I don't want to do polling
like, I don't even need a NVMe driver, I could just use IDE 
MSI-X, MSI, or PCI IRQ pins via APIC through ACPI or the PIC
Iirc the nvme spec requires MSIs to be supported, so no need for the legacy interrupt goop
and the pcie spec requires msi-x to be supported
right?
I can't remember off the top of my head
It says something about the msi capabilities no longer extensions, but individual capabilities are still opt-in?
PCI Express permits devices to use these legacy interrupt messages, retaining software compatibility with PCI drivers, but they are required to also support MSI or MSI-X in the PCI layer.
Oh nice
I mean still permits legacy behaviour
So kind of useless but nice that they encourage it
yeah but msis are mandatory which is what you want
I support only PCI and not PCIe. Is PCI still worth it or should I switch to PCIe only?
Also, do I need PCIe for NVMe with interrupts or is PCI fine?
pcie is backwards compatible with pci
unless you wanted to say ECAM
in which case you only need it for more advanced features and it also allows you to avoid locking around pci accesses
pcie includes pci
nice
I do be implementing MSI and MSI-X right now
been looking at PCIe and it looks easy to implement once you have PCI
my NVMe on QEMU doesn't seem to have the capability for MSI? Is there an option to add on QEMU or is it an issue on my side? I am re-checking my capabilities enumeration to make sure it is correct
did you only check for msi
or also msi-x
first i check for msi-x and then I fallback onto msi
alright guys I'm stoopid
the capability for MSI-X is 0x11, not 11
on the NVMe driver I have the fatal status flag set after init (but before identify), I am searching why
if any of you guys have an idea ๐๐
https://github.com/maestro-os/maestro/blob/b2259b4c1ae4e53f0e2b31c68163ab4091d8fb6a/kernel/src/device/storage/nvme.rs#L430
can't work, busy playing clair obscur
๐ญ ofc you are playing that

Best game I've played in a while btw, I highly recommend it
Yes I finished it recently
Amazing game
Lune pronounced loonay tho ๐ญ
alright fixed it
https://github.com/maestro-os/maestro/pull/65/commits/96b8198fab4fb1c3beef4c2b6fc46b8d70e9b9c8
I am stoopid again
bruh
now I am trying to send a command and I get no interrupt for completion, searching why
I didn't even try polling though, maybe I should do that first to help me figure out where it comes from
did you enable interrupts on the nvme controller 
huuuuuh
you have to tell the nvme controller what interrupt it should fire
is that MSI or something else?
do you use these registers
no
you should 
bro is copying astral code smh
@shell tusk how
Pragmatic microkernel-based OS with fully asynchronous I/O - managarm/managarm
you apparently need it for MSI
I feel like this is not doing much
because I dont support msi for nvme
only msix
and thus:
right
well I am using MSI-X right now so I don't need those
probably means my MSI-X stuff is broken I guess
qemu nvme tracing to the rescue
that exists?
yes
--trace nvme* or w/e
it prints a bunch of info about what nvme is doing
including if it is raising an irq etc iirc
no fucking way
thanks!
that would probably have saved me entire days if I knew about this feature before 
alright I've got pci_nvme_irq_msix raising MSI-X IRQ vector 0
no interrupt received for now though
your msix code is borked then
yes I was masking interrupts in the message control because of a wrong bitmask
just noticed I have 0o111 instead of 0b111 (in rust, 0o is the prefix for a value in octal)
cring
no u
no u
Interrupt handlers are now FnMut instead of fn. Meaning I can now use closures that capture stuff
I can now identify the controller and list namespaces on it
Now, trying to identify individual namespaces. I am looking in the spec for the structure it returns
alright, now let's create the I/O submission/completion queues
you get a fosdem pass
i do be willing to have a desktop env
once I'm done with nvme, I'm starting the port of xorg
should I assume (at least for now), that filesystem blocks size = page size (4096), and if this is not the case, mouting the filesystem fails?
also, should filesystem data be memory-mapped in kernel space? because I don't have a mechanism for memory-mapping a block device in kernel space. that works only for userspace
to overuse a cliche that sounds like xy problem
the first thing
whats making you afraid of filesystem block size being less than the page size
no the opposite. having blocks larger than a page size
did you mean <= rather than =
and yes that assumption is currently made in all modern vmms that im aware of
wait wait wait
SECTOR SIZE <= page size is commonly assumed
as in the minimum addressible block on disk
filesystem block size <= page size is not
and is regularly violated by things like fat32 which can have clusters (blocks) up to 64kb or something
in that case you just read partial fs blocks into page cache pages
should the page cache store only chunks the size of a page? because my struggle is about having different sizes in there
let's say the sector size is 4096, and the filesystem block size is 8192.
Someone reads from the block device directly -> that inserts a 4096 bytes entry in the page cache
now the filesystem attempts to read the same location from the page cache. The entry is 4096 bytes large, when it requires 8192
I am probably over-thinking something. I think the solution to this problem is probably a lot simpler than I imagine
that sounds complicated
its very very simple
the fractional fs blocks you read will be composed of an integral number of disk sectors so
theres no complication
so the page cache stores disk sectors, right?
but then when you access a fs block, they are not necessarily contiguous in memory
i have implemented this so im not talking out of my ass
well, the page cache stores virtual file stream data
if thats an on-disk file then ideally you can DMA directly into the cache pages
a page cache page contains file data for a consecutive run of bytes within the file
so the underlying sectors could be at random locations on the disk
I am talking about continuity in memory, not on disk
i dont see what the problem is supposed to be
I'll try to asking questions on a different angle
If you read partial fs blocks, doesn't it make the filesystem implementation more complicated?
see my response in #filesystems
Ntfs can do up to 2mb I think
I'm working with a friend to put github actions to trash and potentially use drone ci instead
Also I am struggling to refactor my page cache
If I setup a second interrupt with msi-x, the previous one isn't working anymore, so I think I fucked something up
I am realizing my kernel really doesn't have enough logs
when I try to read a page at LBA 0 or 8 it's alright, but when I attempt LBA 536 I don't get any completion interrupt (even though --trace on QEMU tells me an interrupt has been sent)
weird
maybe you failed to ACK the irq in the ctlr?
like perhaps you forgot to clear status bits or something
causing it to not send any further IRQs
unless i misunderstood the situation
I am updating the doorbell reg. The first two reads (at 0 and 8) work successfully, but the third at 536 does not
alright I think it doesn't actually have a relationship with the LBA. The issue arises at the 3rd read regardless of the LBA
alright Im stoopid
I am removing the nvme controller's interrupt handler once it is initialized because I am dropping the controller's structure, because I didn't keep it anywhere once initialization is done
so that's why I don't get interrupts anymore
I am close to having the nvme work.
The last issue I am having is the following:
When I exec the ELF for the PID 1 process, I create a new virtual memory context onto which I build the programme image from the ELF.
To build this image, I temporarily switch to this memory context. This temporary switch enters a critical section, which is exited when getting back to the original context.
In the new memory context, I map the ELF at the required locations, and I fill the requires parts with zeros.
Filling with zeros triggers a page fault, and the handler reads the content of the ELF to put it into memory.
Reading from the file uses the NVMe driver, which puts the process to sleep until the operation is completed.
The operation completes, but the process cannot resume since we are still in a critical section -> the kernel is stuck
maybe I should have a debug_assert in the reschedule function to check we are not in a critical section?
a section where preemption is disabled
yeah generally speaking you cannot take a page fault while preemption is disabled
a page fault may inherently lead to blocking which defeats the purpose
so youll need to find some other way to do that
alright
that wasn't an issue before because I only had PATA with polling 
also I should probably write something to allocate interrupt IDs, because right now I am using hardcoded ones
when framebuffer
once the NVMe is done
I am removing the thing to temporarily switch to another memory context, but that means I have to rewrite the function to read from another process's memory (required for /proc/<pid>/mem and stuff)
that's annoying
why remove that entirely
Doing it without a critical section fucks up context switching because when restoring the context, it would be using the process's virtual memory instead of the one we temporarily switched to
The solution to this is to change the process reference inside the thread which is used by the scheduler to determine what page tables to switch to
Sounds doable, yeah
I think I've managed to run the init program, but it's extremely slow. Way more than when using PATA
Alright, time for that moment in my life where I start to think I am a total failure
So, the tracing tells me one of the issue (maybe the whole issue?) is the following:
When waiting for the nvme interrupt to come, we put the process to sleep and switch to the idle task (so far so good), but when the interrupt comes, we handle it and wake up the corresponding process, and then instead of switching back to the process directly, the kernel keeps looping on the idle task until the next tick indicating we must switch context
I have that function (preempt_check_resched, same name under Linux) I call at the end of every interrupt handler, where I check if a tick was received, and if we are not in a critical section (disabled preemption), in which case I reschedule. Should I modify this function to reschedule if we are running the idle task and another process is waiting for CPU time, even if the tick hasn't been received yet?
now that I think about it, this may also be the reason why pipes are so slow
fixed, see #schedulers message
The next issue is that interrupts handler are behind a spinlock because they are not per-core yet. That produces a deadlock when there is a nested interrupt.
As a solution, I will make them per-core
do you support interrupt priorities?
if not, there should be no nested interrupts
and if yes, there should still be no nested interrupts for the same irq
No. How does that work?
by assigning a priority to each interrupt and blocking all interrupts below the current priority
for example by using cr8 on x86
but if you don't have it, you also shouldn't have nested interrupts
since nested interrupts without any limit will potentially stack overflow
What is supposed to prevent nested interrupts?
the fact that I do the End Of Interrupt before unlocking the spinlock probably is an issue
by just not enabling interrupts in your interrupt handler
right
should I use only trap gates then?
okay I think this is not actually a nested interrupt, but rather:
- Process 1 page faults, locking the IRQ handler's spinlock. We use the NVMe controller to read the page from disk, we put it to sleep until we get the completion
- Process 2 resumes
- Completion for process 1 arrives, we enqueue it to run at the next scheduler tick
- Process 2 page faults, attempts to lock the IRQ handler's spinlock -> deadlock
you should not sleep with spinlocks taken
Why is there a spinlock for the concept of page faulting on a process anyway? Seems to me that you'd rather have some locks in the memory map?
this is a lock for registering an interrupt handler. To make sure there isn't a CPU core registering one while it could be used by another core. This is a legacy stuff that has to go away. Interrupt handlers should be per-core, not global
That and I don't think the page fault handler is normally something you install. You already know what vector it is after all.
yeah idk I didn't feel like hardcoding that in the interrupt handling code at the time
IDK it's how I and probably lots of others do it
this is unrelated to that problem, but the secret of responsiveness is that in a case like this you want process 1 to resume sooner than the next scheduler tick
Currently it should be the case only if it has a higher priority than process 2
So I wanted to have interrupts per-core. So that when I register an interrupt handler, I can just disable interrupts during the insertion so that I don't need a spinlock. That works, but only allows to register/unregister interrupts on the current core.
However I just realized this might be a problem when registering an interrupt in a kernel module.
When I do finit_module, the init function of the module is called on the running process's core (same for delete_module). In this init function I sometimes register interrupt handlers.
The issue is: if I unregister that interrupt handler when calling delete_module, it might be a different core than the one where the interrupt handler was registered.
How do I handle that?
i have a generic cross-call mechanism for calling functions on other cores by means of IPI
also helpful as on some architectures there is one IPI vector only
I have something to defer a function call to another core using IPIs, that I could use to register the handler on another core, but that seemed a bit overkill
need to find motivation to do stuff
dont we all
yup
it comes in bursts of several months for me
then it fades and something else cycles in
Same
NVMe working ๐
I'm making sure the CI passes. I'll probably do a few modifications (like change the interrupt ID being used, by allocating some dynamically) and then I'll merge that into master I guess
@teal dock u alive???
also I'll write a blog article about NVMe
yes, I have been resurrected
๐
after NVMe, the next step is graphicsโผ๏ธ
@inner pilot how do I display stuff on screen?
no like, the part in between the kernel and hardware. not the part in between the userspace and kernel
no need to do VGA I guess?
EFI Grand Old Party
btw I have a friend switching my CI towards Jenkins
fuck github actions
limine when
@late oar you should just bite the bullet
or write a uefi loader yourself
but fuck mb2
it's utter trash
mb2 
megabad2
bite lol
idk if I don't support mb that means I can't boot from grub, right?
fuck grub
grub suck
yeah but that's what people use
for linux lol
fuck discord but that's where people are
ok so? this is your OS, not theirs
bro is making linux 2
just support linux protocol
like bruh
multiboot is genuinely dog shit
if you want to boot with grub you can do efi chainloading
Even for Linux, Limine is just a better bootloader
i use limine on all my pcs
for a multiboot2 framebuffer, add a tag like this:
dd MB2_MAGIC
dd MB2_ARCH
dd MB2_LENGTH
dd MB2_CHECKSUM
align 8
dd MB2_TAG_TYPE_FRAMEBUFFER ; 5
dd MB2_TAG_FRAMEBUFFER_SIZE ; 20
dd 0 ; width (0 means default)
dd 0 ; height
dd 32 ; bpp
align 8
dd MB2_TAG_TYPE_END
dd MB2_TAG_SIZE_END
and then to get the address, search for tag type 8 and parse it
thx
btw I haven't started the framebuffer yet. The CI doesn't pass for some reason. I broke SMP, and there's another issue with NVMe that I haven't figured out yet, that happens only on the CI and not on my computer
โซ 99 little bugs in the code โซ
โซ 99 little bugs โซ
โซ take one down, patch it around โซ
โซ 137 little bugs in the code โซ
lmao
repost
repost
^
repost
had this issue too:
#osdev-misc-0 message
fixed it by passing hardcoded values first to make QEMU happy
I still have the SMP issue. Trying to figure this out
I didn't work much on this during the week. I am in Paris hanging out with friends
To remove that ugly implementation of /dev/zero that I had where I copied bytes from an array of zeros
Can anyone help me figure out what's wrong with my NVMe driver pwease? UwU ๐๐
When I have more than one CPU core on the system, I have a concurrency issue that makes the kernel panic. I suspect this may be the NVMe controller writing somewhere it should not
This issue does not happen when I use IDE instead of NVMe so I am fairly certain it comes from the NVMe driver
After looking closely with gdb, I suspect the issue happens systematically after the first process forks, when the parent resumes, if it has been migrated to another CPU core, on the second read operation it attempts after resuming (this line might be difficult to understand and maybe not very useful, sorry)
This is the function to enqueue a command: https://github.com/maestro-os/maestro/blob/e58a7eb6e8501b49129b24e180200cbbdf8acb0c/kernel/src/device/storage/nvme.rs#L796
This is my submission entry to read a page (writing is just below): https://github.com/maestro-os/maestro/blob/e58a7eb6e8501b49129b24e180200cbbdf8acb0c/kernel/src/device/storage/nvme.rs#L535
This is the location where I handle NVMe interrupts: https://github.com/maestro-os/maestro/blob/e58a7eb6e8501b49129b24e180200cbbdf8acb0c/kernel/src/device/storage/nvme.rs#L838
Could you guys tell me if you see anything suspicious? I am clueless
are you writing to userspace pages directly? if yes, my guess would be memory locking / copy on write bug
yes I write to userspace pages directly. If by memory locking you mean mlock, I didn't implement it. Although I have copy on write
you are missing barriers
The driver allocates the physical page it writes to, and then it inserts it in the page cache. This page is then mapped to userspace
before and after the command write_volatile you need an empty asm block to make sure the commands are all written out by the compiler
cow + direct writes to user w/o mlock-equivalent (not really exact mlock semantics, but locking the physical addresses during the read()) sounds like a recipe to get corruption
thats probably not the issue tho
but idk
actually it could be something like that, with a tricky sync issue
dark magic
what happens if you take a lock around the entire nvme op function
so that only one read can proceed at a time
because volatile ops are not allowed to cause memory reads or writes because wow i love llvm
can't you use sync::atomic::compiler_fence() or is not that available in no_std?
What the fuck?
uh, yes, it says so in the rust manual and the langref and like its also what the c standard says
does not change much
volatile cant be used to sync against other threads
you could but it doesnt have the required effect
adding the asm before and after does not change much either
figured
Ah I thought you mean like it could still optimize away a read/write
well no the write will be there
wait, what is the required effect?
but surrounding writes could be removed
it is available in no_std
make sure that all preceding writes have actually been written into memory
and can be read by other threads
why not?
why is compiler_fence not enough?
you cant use fences of any kind to make volatile ops atomic and hw is modeled as a different thread
the rust compiler has to assume that a signal can occur immediately and read the value written by the volatile store, followed by reading the non-volatile data
so it has to order the writes before the fence
no, that is not true
volatile ops are not enough to sync against signals
yes but you dont want to use an atomic because then that could be merged with other ops and isnt guaranteed to be one instruction
or an instruction suitable for mmio
does rust even guarantee that volatile is suitable for mmio?
what kind of panic is it even
often a page fault, sometimes a global protection fault
yep 100%
sometimes a gpf happenning while printing the panic message for the page fault 
global 
*general
what kind of stacktraces do you get?
also, what happens if you just add like a 10ms sleep in various places
this is something that needs fixing anyway ^ btw
surely you dont make pages accessible to userspace before a read completes though?
like the stacktrace at the moment the fault happens? I have great difficulty getting it. I think it's probably all corrupted
nah
i mean just x/512gx $rsp
and then write a python script which determines if something has the kernel code pointer vibe
and pass all such pointers to addr2line
its like the easy way to do very fault tolerant stack traces
so what do you do on a read() to user stack, for example?
I think that would read the content of the disk to a page in the page cache, and then there's a copy from that page to the user page
writing directly to pages mapped to userspace happens only when the file is mmap-ed
okay that sounds unproblematic w.r.t. fork() etc
it does not make much sense
here are the logs of the kernel running, interlaced with the logs given by QEMU's --trace pci_nvme_*
(the kernel panic does not show much info because it deadlocks. that's an issue I have to fix)
alright I think I'll stop for today. I've spent the whole day on this and I'm tired
??? isn't that the purpose of it?
no, the purpose of volatile is so that you can claim conformance
so I will have to figure that x2apic thing
and that nvme bug too
and implement pcie (currently have only pci)
and get MINECRAFT to run
minecraft minecraft or minecraft minecraft?
We skipping a few steps here maybe set a closer goal like xorg firdt

Tomorrow firefox running on Maestro fr
Step 1: networking
Step 2: xorg
Step 3: on Linux run DISPLAY=maestro_ip:0 firefox
Step 4: wait an eternity
Step 5: profit
(Firefox does literally hundreds of unnecessary redraws, killing any hopes of fast remote X without at least probably X compression and definitely some config tweaking on Firefox's end)
so, back to trying to figure out why this shitty nvme driver doesn't work
just don't write bugs

note: I am now on a faster computer and it seems to fail a lot more often
adding compiler_fence in some places seem to help (decreases the likelihood of the issue happenning), but do not eliminate it
what do you need compiler fences for?
I don't even know what I am doing anymore
bruh
ok so
i didn't read the backlog
what's the problem in the first place?
are you manually writing to mmio addresses?
I am using write_volatile for that
I have a concurrency issue in my nvme driver that happens only when I have more than 1 CPU core
and no idea where it comes from
starting to think maybe I should propose giving money to someone so they figure the issue for me 
@late oar if you want me to test it i can
to see compliance with the x2APIC enabled by default
I haven't even look at how x2APIC works yet so I guess it does not work?
when you have support lol
it's not hard
really
it's technically simpler than xAPIC unless you care about sending interrupts to CPUs with lapic IDs >=256
u have x2 only hw?
no but qemu
i have a patched qemu (see announcement) but i also have an Arrow Lake system that has an x2APIC that can be firmware locked
Ah
even for cpus > 256 it's quite simple
just don't use the weird flat clustering mode (actually, that doesn't exist for x2apic anyway)
logical addressing without optimizing for multiple destinations is fine (there's still only one address for each CPU)
i've implemented this for the virtual machine monitor in our commercial product and it was not very hard
the way logical addressing works with the x2apic is that you have a cluster ID (= the upper 28 bits of the logical apic id) and then a 16-bit bitmask that selects targets within the cluster
so basically to send to logical APIC id x, so need to program the logical destination register to ((x >> 4) << 16) | (1 << (x & 15)
and if you really want to, you can send to multiple apics at the same time by setting more than one of the lower 16 bits
I am starting to think maybe this is fucked up:
https://github.com/maestro-os/maestro/blob/e58a7eb6e8501b49129b24e180200cbbdf8acb0c/kernel/src/process/mem_space/mod.rs#L597
or the functions it uses
/// `f` is executed in a critical section to prevent the temporary memory space from being
/// replaced by the scheduler.
this isn't even true anymore. gotta remove this
I modified this function for the nvme driver to remove the critical section, so that I could make the process sleep while handling a page fault, because the page fault might need to read the page from disk
But maybe I fucked up the refactor
Didn't you already have SATA working? I heard NVMe was supposed to be easier
I have PATA, not SATA
The thing is, my pata driver does polling, while the nvme one uses interrupts. That's probably why pata works and the other doesn't
rip interrupts
it is easier
I had my TLB shootdown interrupt tied to the same vector as the one I use for preemption. I untied those but it does not fix the issue
however, making the tlb shootdown to all cores and making it flush the whole virtual memory seems to change a lot of things. Now it does not crash at the same place, and the new crash seems to be a double free
If your shootdown code is correct, it shouldn't matter which interrupt vector you use for it
yeah but I called it in the reschedule code, so if we are in a critical section, the shootdown does not happen until we leave the critical section
I'm gonna attempt using this: https://www.qemu.org/docs/master/system/replay.html
qemu-system-x86_64: Record/replay: Record/replay is not supported with multiple CPUs
:(
The record replay system is buggy af as is lol
alright, new idea: Let's try to use https://www.qemu.org/docs/master/about/emulation.html#uftrace to visualize what's happening and try to get a clue of what's going on
idk if it supports more than one core though
it does
nice
I already managed to do it once, with only one core though
I had to add swap memory to my system because otherwise the browser was OOMing 
there's also eclipse trace compass that I think supports uftrace
it's a java program
you can also run perfetto cli and connect it to the web ui
I think I might have found where the issue is
When I submit a command to NVMe, I put the process to sleep until woken up by doing:
process::set_state(State::Sleeping);
schedule();
set_state removes the process from the current core's scheduler's run queue
Once this is done, I call schedule to switch context to another process (this function returns when the process resumes)
However, I think the other CPU core is resuming the process before schedule has finished. So it resumes even though it is still running on the first core, and with invalid register state (since schedule saves registers, but we haven't reached this yet)
Now I have to figure how to fix this issue
you have one run queue for all cores?
no there's one per core
the process is inserted in the run queue of the other core at the moment it is woken up (when completion is received from the NVMe)
I think the issue is that this happens in between process::set_state(State::Sleeping); and schedule();
@shell tusk is this the reason why you have a sleeplock in your process structure?
Ultimately you could probably get away with a cas loop
cas loop?
__atomic_compare_exchange
(not related to your issue but for accessing the state or w/e)
Your issue in specific I kind of solved by switching to a sort of intermediary context inbetween switches
Where I add to queue
"whatever"
okay I might have fixed that panic but my solution seems to deadlock. Investigating
I have two contexts acquiring two locks in the opposite order
turns out uftrace is very very very efficient at helping me figure out concurrency issues
but i thought rust was concurrency safe
I have to remind myself to use it directly each time I have a concurrency issue
(is my ragebait working?)
how do you use it
the data
I put it in https://ui.perfetto.dev/
then I look at what's happening precisely. I put markers to help me
currently working on making a website for Maestro, with help from @fringe needle
claude managed to figure out a bug again (I think, I'll be sure when I have a fix)
see: #osdev-misc-1 message
In order to fix this issue, I am implementing mprotect
ci passed for the first time in 3 months 
I think NVMe is finally done. I'll review everything and merge it
That also means, I can now start working on graphics and porting xorgโผ๏ธ
how do I display shit on the screen? VESA?
imma use that
you can ask for one
32 bit only right?
Multiboot2 Specification version 2.0
uh idk? will switching to long mode break it?
GOP isn't mentionned in the multiboot2 doc. But VBE is
mmmmmh
Id you tryhard enough you can probably display a very shitty x.org screen using text mode 
lmao, 80x25 pixels resolution
@inner pilot can you have vsync with gop?
no
there's no syncing, it's just a dumb linear buffer
if you want e.g. 60hz you'll have to time page flips yourself
that means using something else than gop?
no
the gop draws at some unspecified refresh rate
because nothing is accelerated,the rendering is done on the cpu
the gpu only displays it on the screen
now I am wondering if I should have the console in userspace or kernelspace
like, I don't exactly feel like having font rendering in kernelspace
what have you been using then?
vga text mode
kernel is fine
there's a possibility I attempt to make my own fonts for my OS
nah it gives you a framebuffer anyway if you're on uefi in my experience
although on my laptop it will default to 1024x768, so i have to go to the grub console and force it to be 1920x1080
unlike limine where it does the correct resolution by default
qemu's window changed shape, so I guess I've got a framebufferโผ๏ธ
exciting time to be alive
I have socketpair iirc
probably not. tomorrow I'm going to buy groceries, I'll be cooking during the weekend, and there are elections in France on Sunday
โผ๏ธ
poke @fringe needle
yellow
now do blue
I am currently writing the code to have a /dev/fb0, and I'll reuse it for the init console I think
Eiffel 65 - Blue (Da Ba Dee) Official Music Video Remastered @Videos80s
1998
#80smusic #remastered #popmusic @Videos80s
Nice!
maybe there are missing bits lmao
https://github.com/maestro-os/maestro/blob/ad3b1e16ca964627439f78230ebe3d9df0131d4b/kernel/src/file/socket.rs#L186
__ensure(!"Not implemented!");
I am currently not working on video stuff. Instead I am reworking the tools to build a bootable image so that it's easier to make demos (currently I have to rebuild everything by hand)
I am going to Paris with my laptop for the week end. I didn't commit my video code and it stayed on my home computer, so no video this week end I guess
I'll continue working on the bootable image instead
I am trying to build a script to turn a ttf into a binary file that has each char rasterized into it, to see how it renders
looks kinda shit ngl
I need to remap the framebuffer with write-through, right? That means I need to have all the memory management stuff initialized before I can display shit on the screen
so if there's an error while initializing memory, I can't display it
I just realized I also need to do this remap when using text mode and I don't do it at the beginning
write combining iirc
write-combining
so that writes are only released to mmio when the cache line fills up
so u write 64 byte bursts instead of 4
this speeds up your drawing 16 times
Ah I didn't know the difference
write back -> written data is not flushed to ram at all unless the cache fills up etc (this is the default for normal memory)
write through -> data is written to both cache and ram at the same time (can be used for prefetchable PCI aka where reads have no side effects)
uncached -> cache is not used at all
write combining -> explained above
so I should map prefetchable bars as wt?
probably yes
it's the next best thing for gpu memory bars after WC
or rather, for aperture-like things
you can only do that if the BARs are only manipulated by the CPU though
so doing it in general is probably not a good idea
WT is only useful in very limited scenarios
you usually want either WB, UC or WC
I have it as UC now
setting prefetchable BARs to WC is safe in general but setting them to WT is not
I guess Im just gonna use unifont
decent choice
indeed, I use it as well
currently implementing the thing to read from the unifont .hex file, turn it into a format that consumes less memory, and include it in the kernel
what's a unifont
it's like the default font for tty on every distro I've used
huh
vt
to draw text
I've always downloaded it from https://ftp.gnu.org/gnu/unifont/
huh, same thing I guess?
but why
wdym why
a bitmap is enough no?
do you need chinese or what
yeah and this font provides a bitmap format that is convenient to use
one does not need to include all characters in the font
ah
getting close
โผ๏ธ
(and then I think I am getting a kernel panic in the font display function or something)
alright, it's working correctly but it's so fucking slow, and the screen clear is very slow and visible too (waiting for it to finish printing stuff before making a screenshot)
I have to avoid clearing and redrawing the whole screen each time I write something
why not use flanterm
writing a terminal is an entire project by itself
because I don't feel like it :3
Not really
You could say writing a memory manager is an entire project by itself
if you want to just write characters sure 
terminal in the kernel isn't really necessary for a proper os, you can have it in userspace for example
Yes, so why bother having a good one
have a shitty homemade one in the kernel and good user one
fair enough but maestro is linux compatible
I have finally made it! I am now running KDE Plasma, Firefox, Discord and RustRover on Maestro! It is now production-ready.
I am also proud to announce the project has been acquired by Microsoft. Sources won't be publicly available anymore, and a license will cost only 299.99โฌ per month.
We will also shorty integrate AIโข in every feature of the OS, and Maestro will be renamed to Microsoft Maestro Office 365 Copilot.
Thank you for your support!
Microsoft Maestro Office 365 Copilot - Linux-compatible-ish OS in Rust, powered by AI
windows abi compat
There will be an AIโข live-translating Windowsโข system calls to the Linux ABI ๐ฅ๐ฅ๐ฅ
Didn't know Microsoft has enough consumer market control to charge IBM prices :^)
WAIne ๐ท โจ
E = mcยฒ+AI
A.I. stands for Albert Instein.
That's how it's so smart...
can I sponsor this
You need to ask Microsoft support:
https://support.microsoft.com/en-GB/contactus
Contact Microsoft Support. Find solutions to common problems, or get help from a support agent.
have you tried sfc /scannow ?
idk why it doesn't work :(
Password:
/home/xrc2 # sfc /scannow
bash: sfc: command not found
/home/xrc2 #
thank you!
Maestro - Linux-compatible-ish OS in Rust

bro really wanted to have maestro owned by microslop
How do I implement /dev/dmesg?
I need to save logs somewhere in memory but I don't exactly feel like mallocing for each new log message
usually you do this by making a ringbuffer
Log ring
what's /dev/dmesg
do you mean kmsg?
Also it's called /dev/kmsg
yes
also I resent this message in #osdev-misc-1 so the convo is there
now using full screen size
Imagine using a language which mangles
smh
how does rust do that
do you just compile in a demangler?
doesn't that need memory allocation?
no
interesting
implementing a tty is fun
eh it's alright, i can read it
looks like abseil has a no allocation demangler as well. the catch is that it doesn't do function parameter types and template arguments
crates.io serves as a central registry for sharing crates, which are packages or libraries written in Rust that you can use to enhance your projects
i'm using this
it's the official one
and only needs allocation when you want to store it in a string
gay
โผ๏ธ
(don't pay attention to the filenames, it's a trashy image I just use for testing)
I know some of those should not be there
neofetch in the root dir 
yeah, testing
(it doesn't even work anymore)
I guess the next step is to add /dev/fb0, finish the implementation of unix sockets, and then I'll attempt porting xorg
Thanks!
idk from the top of my head
str::from_utf8 returns a &str. If it was allocating, when would this memory be freed?
nevermind
the diff looks weird on mobile
i thought you were calling that on the result of rustc demangle
on the other hand, String::from_utf8 is allocating memory
the inline diff view kinda sucks
I just did that too :P
๐
/dev/fb0 popped up. Not working yet, figuring out why
also why the fuck do I have both sda and nvme0n1 present when I only plugged a NVMe in???
โผ๏ธ
(idk why the whole screen hasn't filled)
okay that's a /dev/random issue. When using /dev/urandom it works fine (although looking at this, my urandom doesn't seem to be very random
)
alright, now let's check how to port xorg
pretty consistent urandom
why was /dev/random created in 2023
my /dev isn't a tmpfs because... trust 
I am reusing the same disk image since a long time
oh jeez lol
maestro is 56 years old
@shell tusk can I cross-compile xorg or do I need to have gcc, autoconf and all that stuff to build it on my os directly?
You can cross compile it obv
The super annoying package to cross compile is perl but you can get a 3rd party configure script to do it for you
And I dont think xorg needs perl at all so
(if you went the compiling it yourself in maestro route you would need it tho cuz autotools are written in perl)
alright, now I have to figure out how to cross compile Xorg
Do astral binary compat 
nah
I wanna do it because I want to see if I can make one that does not require python installed by default (aside from building packages. Like you may need meson for building some packages, but maybe you won't need python at runtime)
also if I could remove perl too it would be nice
found docs for building xorg and cross compiling it:
time to add packages in https://github.com/maestro-os/blimp-packages i guess
Contribute to maestro-os/blimp-packages development by creating an account on GitHub.
I packaged expat, libpng, openssl and zlib (apparently required for cross compiling xorg)
https://github.com/maestro-os/blimp-packages/commit/77cf6c846a1efbc8097dc8f3ab8c88ce63de39ab
writing all the necessary xorg recipes was the worst fucking experience of my life
there's so many of them lol, even if you already have all the deps and just need to install the actual xorg software
it was 90% fighting with autotools 5% copy pasting recipes and 3% changing package names and repo links and 2% patching
like pretty much every libx* recipe is stemmed from like one library
and getting the right dependencies in each recipe was also painful
i had to search through astral's recipes, even went in the arch linux repos to figure out dependency lists of packages
alright, I'm trying to do that right now and I feel this is gonna be annoying
it's easy with jinx 
cp -r gloire/recipes myos/recipes
s/gloire/myos
rn I am wondering if my package manager should have something to install build dependencies in a directory and chroot into it in order to build packages
@shell tusk what are you using to build your packages and does it chroot?
Jinx, it uses a container so yes
thx
after way too long, I started writing a blog article about nvme
just get ai to do it lol
its 10 times easier
and yea writing recepie is so fucking annoying considering theres like 30 dependencies
btw if its linux binary compatible why dont you steal packages from like alpine
thats what cavos and naos did
dead project
๐
oh wow it's been a long time since I have worked on this
@fringe needle has been implementing support for a chroot environment in blimp so that packages can be built in isolation
https://github.com/maestro-os/blimp/pull/11
I have to test it, but for this I need to rebuild my bash package which has apparently been built against glibc instead of musl
New blog article about NVMe:
https://blog.lenot.re/a/nvme
I wouldn't map the bar as wc
write through instead? why?
Writethrough definitely wouldn't work lol
It only works for bars where u are the only one modifying the data
For this one only uncached and nothing else
WC is not only wrong but UB I think
Since it doesn't expect to see 64 byte writes
what kind of device would you use wt for?
Depends, any prefetchable bar works, a common case is gpu bars for framebuffers ofc, since reading from it is slow af and with writethrough it gets faster since you cache the data locally, but for that purpose u can just do double buffering for example
Also obviously it doesn't work if you use gpu helpers for modifying it since those bypass the cpu
But WC here wins in general since reads you can optimize in software, and writes u want to be as large as possible
WC on non prefetchable bars just simply won't work
I guess it works on qemu
it does indeed. imma fix that
neither WT nor WC will work (reliably) on non prefetchable bars for the reason that infy mentioned. Devices will simply not be prepared to handle 64 byte block writes to their registers
They may ignore the writes / only execute a subset of them / raise SErr instead of behaving correctly
did u test this driver on real hw btw
damn
the only computers i have with nvme in them aren't really computers I feel installing maestro on for now 
cant u just boot it off of a usb drive?
I could, but what if my thing is actually buggy and it corrupts the system I have on my nvme?
i guess its possible in theory, but u could just not do any writes
alright guys I'm back trying to cross compile the X server
while doing so I realized my cross compilation toolchain was broken from the beginning
merged
turns out packaging stuff is a lot funnier once you don't have to bother about cross compiling gcc anymore 
currently attempting to package python because xorg requires meson to be built
just use the host meson
wait what is he not using host meson?
can I cross compile xorg without chrooting?
just give it a cross file with your sysroot
cross file?
have you used meson before?
yes
all packages astral can run do not need chrooting
closest one would be perl but theres a third party script that comes in clutch for that
no
a cross file determines your sysroot, cross compiler, etc
I asked Claude Code to audit my kernel's code, well... 
forgot to ask for memory leaks too
jeez it didn't need to make everything bold lol
side quest: I have built Vim for Maestro and now I have to add support for file locks (which means I have to understand what they do first)
alright, easy
I tried telling claude code to implement flock and he's stoopid. imma do it myself
odds are a decent couple are it hallucinating issues that don't exist
Interesting, what prompt did you use for this?
wait do you have your own Arc
did you not use the arc from alloc
Yes
Something something "audit the whole codebase for undefined behaviours, stability issues, security issues, bugs and obvious performance issues"
No that's because I need allocations to return a Result in case the allocation fails
flock implemented. Now Vim wants membarrier
vim???
vim!!!
that sounds wrong
first because membarrier is very new
second because it doesnt appear in the code lmao
third because it doesnt show up on strace -ff
flock also doesnt
mmmmh
I build vim with musl and musl seems to call membarrier in dynlink.c and pthread_create.c
i don't feel like implementing membarrier so I guess I'll try to return ENOSYS and see what happens
the simplest form of membarrier is just
for(int i = 0; i < numCpus(); ++i)
onCpu(i, [] { atomic_thread_fence(); })
right, sounds easy
imma implement it then
atomic_thread_fence is just a mfence instruction?
you dont need a fence
usually its lock or [rsp - 64], 0
why this in particular?
because x86 moment pretty much
why -64?
less likely to be contended
would using this be alright?
https://doc.rust-lang.org/std/sync/atomic/fn.fence.html
An atomic fence.
yes
I have to admit I am not very good at using atomic ordering stuff
fence(seqcst)
thx
Depends on how strong your onCpu() equivalent synchronizes
it has to be at least acqrel right?
Anything else would probably be silly, yes
But i think membarrier() is a seqcst fence? I might be wrong though
idk if lkmm has a distinction between seqcst and acqrel tbh
anyway on x86 and arm, i think acqrel==seqcst
does your syscall layer not do that by default?
yeah but it also sends a SIGSYS with it. I was wondering what would happen if I returned ENOSYS without sending SIGSYS
huh idk?
I implement membarrier and fixed a bug in mprotect. Now it seems Vim wants futex
at least it runs
A syscall is missing to save the file, so no 
lol
a bit better, but still missing stuff
Emacs when 
idk
fixed a bug that made the kernel crash when booting in release mode (stack overflow)
I am using a larger kernel stack in debug mode because Rust

also running the kernel under kvm seems to work now (it didn't before)
and gcc stopped working
I really need more integration tests (soon)
I have no tests
why
because having tests require setting up tests
yes
setting this up takes time but it will save you a lot more time later
investment
I am tired of having deadlocks because my print stuff isn't reentrant so I have started implementing a lockless ring buffer for it. That will make debugging easier
do you use rust's #[test]?
#[test_case], with #![reexport_test_harness_main = "..."]
that allows to specify a function that launches tests
useful for running tests in kernelspace
for userspace tests (in a separate library), I do use #[test]
This post explores unit and integration testing in no_std executables. We will use Rustโs support for custom test frameworks to execute test functionsโฆ
for integration tests I have a script that builds an image with a program in it, trying to create, edit, remove files and do other stuff to do basic tests on system calls
and I run all of that in the CI
the missing syscall was ftruncate. Now I can save files and I think I've got vim fully working
(actually I'll do it later, that's complicated to implement
)
I got "unable to start udev" or something similar 
haven't tried since
obviously I need to implement netlink first 
it seems to want epoll_create1. Does it make sense or did I do something wrong?
it does make sense
nice
you can compile it without epoll afaik
No
if you expose sys/epoll.h it will use it
if you don't it won't
I'll need epoll sooner or later anyways, right?
you can patch its meson.build to disable epoll even if it detects it
yes
systemd needs it 
and you can build it with musl
also, why not just use glibc?
Eh idk I just started using musl and I'm committing to it
https://doc.maestro-os.org/book (automatically deployed by the CI/CD thanks to @fringe needle )
alright, how about I start implementing the epoll interface?
hey lukgpt implement epoll make no mistakes
implement eventfd first
it's way simpler and it'll give you an idea on how the interface should work
eventfd is independent from epoll though
