Skip to content

Blog

Implementing FRED

A while back on January 30th of this year a few people in my online communities were discussing who would be the first hobby operating system to end up implementing FRED which is a new mechanism for handling interrupts and syscalls separate from the legacy IDT.

I ended up having a once over at the specification for it and decided it wouldn't be too bad to implement into my kernel especially since the existing linux patches are relatively simple. Although I decided to not look too closely at the linux kernel due to it being more complicated for my desires and having the skill issue of being unable to read linux kernel code at times.

The IDT

Before we get into the details of FRED I would like to talk about what was used before. The Interrupt Descriptor Table. On x86-64 which is the architecture I am most familiar with it takes the form of an array of 256 structs with a strangeish format due to it originating from the 8086 as the IVT and progressing with the x86 architecture along with the GDT added with the 286 and the 16 bit protected modes causing the structure to look something like this when represented in C

struct [[gnu::packed]] IDTEntry {
    uint16_t offset_low;
    uint16_t selector;
    uint8_t  ist;
    uint8_t  type_attr;
    uint16_t offset_mid;
    uint32_t offset_high;
    uint32_t zero;
};

In this structure the offset/address of the has ended up split across so when filling it in you have to do some shifts like this to properly get it filled in

void set_idt_entry(struct IDTEntry idt[], int index, int ist, int attr, void (*handler)()) {
    uint64_t addr = (uint64_t)handler;
    idt[index] = (struct IDTEntry){
        .offset_low = addr & 0xFFFF,
        .selector = 0x08,
        .ist = ist,
        .type_attr = attr,
        .offset_mid = (addr >> 16) & 0xFFFF,
        .offset_high = (addr >> 32),
        .zero = 0
    };
}

Then once you use the lidt instruction with an IDTR to set the address and the size of the structure the IDT is loaded and you can start getting interrupts to the functions you set. But you shouldn't although you can do this in C or any high level language for that matter due to how you need to handle an interrupt made partly worse by how the CPU give you information.

When you get an interrupt the CPU pushes the following things onto the stack (which may be changed based on an IST or the RSP0 in the TSS)

  • Error Code (!!! May not even get pushed based on the vector !!!)
  • The Instruction Pointer
  • The Code Segment
  • The Flags Register
  • The Stack Pointer
  • The Stack Segment

You may have noticed two things. The CPU does not push the vector so the code does not know what vector it is assigned to. The CPU also may or may not push some things so you will need special handling for each case.

So you will end up using assembly macros to generate 256 stubs which for many exceptions need a slightly separate macro so you can have a unified frame in C and so you can know what vector was called to have a common entry point. They would end up looking like this using NASM

%macro ISR 1
global isr%1
isr%1:
    push 0
    push %1
    jmp dispatch_interupt_asm
%endmacro

%macro ISR_ERR 1
global isr%1
isr%1:
    push %1
    jmp dispatch_interupt_asm
%endmacro

THEN finally after all of this you can have a common handler in assembly to push the GPRs you need to save according to your ABI to then call some C code and give it a pointer to the stack with all the data in needs and then have an exit prologue after you call C to restore the things and run iretq.

SYSCALL/SYSRET

I am not covering these again, go read my fedi post on it. It also happens to mention FRED albeit in less detail.

Flexible Return and Event Delivery (FRED)

Now onto FRED. Since the IDT and syscall/sysret are well not exactly good Intel & AMD have worked on this to solve the problem and make a much better system to work with.

To get interupts you now only use two entry points. One for when it happened in CPL0 and another for CPL3. You simply need the entry point for ring3 to be aligned to a 4kb page and load the value into the IA32_FRED_CONFIG MSR added with FRED and the CPL0 entry follows 256 bytes after. So with NASM you would use some code like this to align it right

section .text
align 4096

fred_ring3_entry_asm_stub:
    jmp fred_ring3_entry_asm

times 256 - ($ - fred_ring3_entry_asm_stub) db 0

fred_ring0_entry_asm_stub:
    jmp fred_ring0_entry_asm

Then whenever an interrupt happens it calls those stubs. They jump to their handler and pass the arguments to run C code. You can see the full code for this on my old repo for my kernel before the rewrite (which will get FRED port soon ofc) on this commit do note that a few of the parts in the old kernel are bad due to me not wanting to change much of the IRQ system so i converted structures though a lot of this can be avoided which I will do in the rewrite properly.

The stuff pushed to the stack is similar to with the IDT but it is fully uniform and always 64 bytes and encodes much more data in it. You get stuff like the vector to properly handle things. The way this is encoded also allows for more than 256 interrupts for one CPU core. You can also check what type it is so if it's a hardware or a software or even a syscall event. There is NMI source reporting so you can get that information easily too.

I am not going to verbatim spit off the entire spec here but you can look at the code from the commit as well of course the spec itself. FRED is an excellent addition to the x86 architecture and implementing it was easy. Well except finding an emulator for it.

Finding an emulator

Now since this is such a new feature when I was writing the code for this i of course needed something to test the code on. Normally I would use QEMU since it is a very good emulator. Sadly this was not an option. Bochs also "supports" FRED now but when making this it did not at the time as far as I am aware and later when i tried to try bochs with it after compiling from source with FRED enabled. It

  • Could not properly boot my kernel with FRED
  • Could not properly boot my kernel without FRED
  • Could not properly boot linux (64bit)
  • Could not properly boot the astral kernel
  • Could boot 32bit linux atleast

So the only option happend to be Intels own emulator SIMICS. Which does support FRED! But given that this is corpo software it was VERY fucking annoying to figure out. Thankfully they do provide a linux native version which works. But when you want to get into an VM and run an ISO you have to use the GUI to make a project folder and install a bunch of the packages to it AND THEN goto your terminal and run the script and run this series of commands to make it boot properly and this is not very well documented and it took longer to get this to work than it did to even implement FRED.

$cpu_comp_class = "x86-experimental-fred"
run-script ./targets/qsp-x86/qsp-dvd-boot.simics iso_image="/home/evalyn/Documents/Programming/evalynOS/evalynOS/evalynOS.iso"
run

This assuming the ISO file is at the path you set should start up a VM. But of course because this is intels peice o' crap and i am on AMD you get no acceleration and it is much slower than QEMU TCG (possibly because it is more accurate despite its PS/2 emulation being strange when playing doom)

Despite all of that it wasn't too bad to get working and testing and FRED is very nice to have and support in my kernel projects. And as far as i am aware besides Linux and possibly some BSD's along with windows my kernel is one of the first to get support for this and the first out of my hobby operating system groups that do this for fun.

The flashy photos and videos for people who just want to see it go brrrr

Happy late New Years!

Welcome to 2026 everyone. I hope everyone had a good christmas and new years. I am a little late in making this post but who cares.

I hope 2026 goes better than glances at the previous few years... Yeah all we can do is hope.

For me though 2025 went well in terms of making progress on my projects!

EvalynOS is now able to properly run doom (1993) and has mlibc ported over to it and as of today sleep() is now working so your CPU can breathe a bit better lmao.

I have plans to get system() working to get a basic shell in userspace and then getting fork() with proper CoW to get bash or some other proper POSIX shell working.

I also have a buddy working on sata and ext2 (maybe ext4?) drivers so there can be persistance for things properly so the kernel and OS should be getting plenty of improvments and hopefully becoming usable as a TUI OS.

But by the end of this year I want get x11 ported so GUI apps can run properly and then contunue working on bigger and bigger things.

I hope everyone here has a great 2026 and many more years after this.

Files, Git, and ZFS

ZFS

Yesterday I found two old 512GB laptop hard drives and I put them together into my modded minecraft server because some friends told me i should use ZFS and well I had two okayish drives so I put them in a mirror for redundancy.

I also got some scripts put together to get alerted for my ZFS pool's health and the S.M.A.R.T status of my drives juuusssssttt incase because they are old, and I want to replace them soon with some proper 4TB NAS drives

The scripts can be found here for the ZFS one and over here for the S.M.A.R.T one.

output of the monitors

The scripts have comments for the proper way to use them but using --all will make it always send to the webhook and without that flag it will only send on errors.

Git Server

Since i now have a decent reliable storage situation (aisde from fires or other major issues) and finding Forgejo which is a fork of Gitea which I did not want to set up due to usage of AI.

It is hosted over at git.evalyngoemer.com

If you or a friend needs an account Contact Me and if your projects are good enough and suitable to be on my instance you may have an account.

My projects are also moved off GitHub to Codeberg which has the same repos as my instance for my projects so people can still be able to contribute until forgejo gets federation done.

File Server

I also set up a Copyparty server on that server, and it has a public read only share for me to share a few files but there isnt much to see there if you weren’t sent a link but its worth mentioning.

Uptime Monitoring Part 2: Episode 2: DIY Monitoring Tool

Well well well. Here we are again. Today i woke up to ~130 notifications from my uptume kuma instance.

EVERY SINGLE GOD DAMN ONE WAS A FALSE POSITIVE

WHAT. THE. FUCK.

So. I've had enough with this BS and made my own uptime monitor tool basicUptime in golang

I added are bare minimum features i need and a basic UI you can see here. Made nginx cache the backend to make it preform decently, and it works a bit better than Uptime Kuma at least and I may add support for instances asking others about status to see if it should send a ping.

For now though It's perfectly fine and il demo it without pings just in case it's gonna false positive too and hopefully it works a lot better

I will add support for Minecraft Java server monitoring though at some point.

Making HRMR

When making my Discord Bot I was thinking of having a SQLite based database backend but I also still wanted to support JSON based databases. I did not want to make a mess of code with either inline if (config.databasetype == "json") etc or making an abstraction layer in the codebase to make things harder to deal with.

So I made a universal abstraction layer in C++ that is fully modular and can have modules hot reloaded at runtime and called it HRMR because it's a hot reloadable module router (yes very good name. im very good at names)

It runs as a TCP server and if the first bit of the TCP request matches a function in a module the module function gets called with the full request passed into it and then it returns a string that gets stent back over TCP.

Its flexible enough to implement an HTTP/1.1 server which is included as an example module in the repo.

I am going to be making a module for HRMR to act as a broker between either a JSON or a SQLite database for my discord bot so it has the same interface for talking to both and it would have no idea. I also can use this same database interface if I want many instances to share one database or if I want anything else to have access to it at the same time.

HRMR also has a thread pool for executing module functions if they do I/O and you do not want to block the main thread. All modules are loaded from .so files and act as native code if they were compiled in which means they have very little overhead besides the indirection system that comes with modules and the need to dispatch things to a thread pool sometimes and calling malloc every time.

But they can also crash the server if not made properly, and they are programmed in C++ so be careful.

This is a pretty nice piece of software I made for myself, and I’m sharing for everyone under the LGPLv3 license and I hope me and many others can use this software as part of thier system. It is fairly well bug tested besides some that I know of and won't fix due to how are they are and I don’t expect to need to change much of the core until a v2 with ABI breakage of modules and some better data structuring to speed things up.

Typescripted & More

So a bit ago i decided to do a complete rework of my Discord Bot to convert it from JavaScript to TypeScript to reduce the amount of issues with it as well as implement proper slash commands and a better module system to support slash commands.

Modules can still be made in JS with a more crude module format that won't support as many features. I have plans to redo the module system a bit more where I only have one listener for each type of events and it gets dispatched by my code but that’s for later because I still want to add a few more features and base included modules.

Aswell maybe extracting and cleaning up the link cleanup function into a separate NPM library people could use and configure?

Overall converting to TypeScript was well worth the effort (along with implementing slash commands). TypeScript is not too hard to get used to, and it makes things way easier to code with having IDE hints in KATE at least

Slash commands are well slash commands, and they do their job and are nicer to work with for argument validation but fall flat for some commands, so I still have some normal text commands for !repost as the slash command version cannot properly handle images and text with new lines and no \n does not work here by default and would be too obtuse for most users because the text box is so small

I am planning on implementing the following features soon™

  • Logging of deleted & edited messages (Will hopefully be setup to ignore messages deleted and reposed by bots even with minor changes like with the link cleanup module)
  • Giving roles on certain level milestones
  • Configuration of XP gain multiplier (0.1x to 10x perhaps. Default: 1x)
  • Configuration of XP decay (if you say you are inactive for a week or more you could lose some XP? Default: Off)

Uptime Monitoring Part 3: Tokyo Drift (Finale)

Okay. So. I'm gonna level with you. The previous "fix" helped but there were false positives sadly.

So I caved and a few days ago i got some stock notifications for servers and I got a new server in the family located in Bern, Switzerland with 1GB ram 1/4th of an 7950x3d (burstable) 25mbps networking (burstable to 10gbps)

Obligatory fastfetch of the VM
fastfetch program output on the VM in switzerland

I got Uptime Kuma setup on there with PM2 & nginx and got it all loaded to monitor my servers.

And you would think this would work. aaaaaandd drum roll please. Did it work? NOPE.

As it turns out optime monitoring is a pain in the ass like anything on computers and is hard to make right.

Uptime Kuma keeps dropping requests from my discord bot that has a perfectly valid user agent of EvalynBot/UptimeMonitor/1.1 and nothing should be triggering it to drop. I had even implemented retries and yet it still fails

John ULTRAKILL

FAILURE AFTER FAILURE AFTER FAILURE
AFTER FAILURE AFTER FAILURE AFTER
FAILURE AFTER FAILURE AFTER FAILURE
AFTER FAILURE AFTER FAILURE

THE RESULTS REFUSE TO ALTER

AGAIN AND AGAIN AND AGAIN AND AGAIN AND
AGAIN AND AGAIN AND AGAIN AND AGAIN AND
AGAIN AND AGAIN AND AGAIN AND AGAIN AND
AGAIN AND AGAIN AND AGAIN AND AGAIN

MY FAITH BEGINS TO FALTER

So I give up I’ll just give it a bunch of retries and widen windows of things so it won't bother me or make the website say its down when it shouldn’t.

If it actually is down it's going to alert me anyway so it should be fine for a smaller scale personal deployment, and maybe it's just a bug with the software.

Maybe I should write my own? Maybe.


TLDR:
Uptime monitoring sucks and I got a proper dedicated VPS for it now.
Learn from my lessons and try to keep sane when doing this crap.

Uptime Monitoring Part 2: Electric Boogaloo (False Positives)

So when setting up my uptime monitor I was noticing there were false positives happening about every 8 to 12 hours. I am on shared hosting so that may have been why, but it was strange.

I ended up asking some peeps on the discord and made a ticket, and eventually I got the owner saying that it was possibly due to an nproc limit being hit. I couldn’t see it in the monitor but there were several threads called libuv-worker and all together there were about 50 threads. The limit is 50, but this was really strange for having a basic uptime monitor running.

I look into this, and it turns out this is a thing node uses for async work but by default there is meant to be 4. I even look into the code and nothing should be changing it. But these servers are running on CPUs with 96c/192t cpus so something may have been funky.

So I looked more into and I can set UV_THREADPOOL_SIZE to a value and force it. I set it to 2 to be reasonable and... It worked!

But it seems after a bit it decided to spawn two extra threads and become 4 but it is still way more in line at ~20 threads and using less memory.

There should not be any more false positive on the status page hopefully. It's been about a day or two without any so and I have disabled retries so we should be all good!

Uptime Monitoring

I have been meaning to set up some kind of monitoring for my services but my VPS provider (i would like this monitor to be actually up 24/7 XD) is always out of stock so I never got a server for this. But I had forgotten I have a shared hosting plan with them that can run NodeJS and Uptime Kuma is light enough so i was able to put it up on there!

It should be fineee as its only uses a small bit of ram and CPU pinging my servers and worst case il take it down for a bit and get a VPS. But it seems I have a limit of 2G of ram on the panel and 1GB is stated on the plan and I’m only using ~450mb so it should be all good and I get a neat uptime monitor that should have high availability.

Check here or the sidebar to see the status monitor
And check here for the path.net status monitor because they keep goin down XD

(note these sites will not auto redirect to https for whatever reason. Please switch to https if possible)
This has now been fixed

Fuck CSS

Okay, I was trying to fix up my old website that was trying to be in the style of a mother fucking website because less HTML & CSS and no JS means it will load faster.

But holy fucking hell I am not a front end dev at all and I come from a lower level language background with C/C++ and CSS was causing me so much pain that I just redid my entire website to use mkdocs with the material theme because I have been using it for making documentation for other things, and it is so much fucking better than the hell of front end dev.

And as a bonus it looks way better, and it works properly on mobile devices and I have this blog and I can glob all the documentation for my projects.

It is at least still static so for the most part my goal of a simple fast website is still being done. Just with a bit more on top so I can use a sane markup language for making websites called markdown.