Redox Development Did you get your iron today?

Correctness, Security, and Documentation

Redox has a lot of functionality now. Audio, Graphics, Networking, Input all work in some form or fashion. User programs can be created to use these functions, in multiple languages. There are, however, some immediate problems to solve.

Redox is not correct, secure, or documented. It does a lot of things in a cavalier, leave it up to the application, manner. Applications can malloc and forget to free, Redox will not clean this up. Applications can access missing memory or kernel memory, Redox does not care. All applications run in Ring 0, and have all permissions. Some syscalls use vtables that rely on exactly the same struct defintions in userspace as kernel space. Syscalls cannot return values, except by writing them into a passed pointer. Memory allocation is done in the application, and uses a global memory allocation table that applications directly modify. None of its functionality is documented, outside of example source files.

I do, however, have a plan. I will make all applications run in Ring 3. I will do malloc and free through syscalls. I will move significant pieces of functionality into userspace libraries (a new libredox). I will use file numbers to identify file descriptors, and create read/write/seek/close syscalls. I will document the syscalls interface, provide more example programs, including programs that test the correctness of the implementation.

This is starting to look a lot more like Linux...

Justified Garbage Collection

I believe very strongly that garbage collection is unnecessary with RAII, and Rust has aligned with this belief strongly. In Rust, the lifetime of structs is known strongly, and is used to drop the struct when the scope is exited. There is, perhaps, a single area of code where this guarantee cannot be met, and that is when a thread is exited.

Here is the old code, which caused a 1 MB leak on every exit:

pub unsafe extern "cdecl" fn context_exit() {
let reenable = start_no_ints();

let contexts = &mut *(*contexts_ptr);

if contexts.len() > 1 && context_i > 1 {
let current_option = contexts.remove(context_i);

if context_i >= contexts.len() {
context_i -= contexts.len();
}
match current_option {
Option::Some(mut current) => match contexts.get(context_i) {
Option::Some(next) => {
current.remap(next);
current.switch(next);
},
Option::None => ()
},
Option::None => ()
}
}

end_no_ints(reenable);
}

One important thing to realize is that current_option is dropped when the if contexts.len() > 1 && context_i > 1 block exits. When it drops, the stack is freed.

Another thing is that current.switch(next) will replace the current context with the next context, causing the CPU to run code at a different address. In essence, RAII is broken here, the stack is not freed, garbage must be collected.

In order to fix this problem, the current context has an "exited" flag set on exit. On a future context switch, exited contexts are removed when encountered.

An OS Devers Dream

Over many years of developing software, I have run across all kinds of systems. I've had desktop machines packed with 12-core processors, dual GPUs. I've had Lenovos with extended batteries, gaming laptops, Macs. I've had an eeebook 900 that I even managed to turn into a Hackintosh. But the single, most valuable computer I have had, from the point of view of an OS dever, is the Panasonic Toughbook CF-18.

Sure, it is a bit old, it was released sometime around 2004 or 2005, I don't know the exact date, and it came with Windows XP. Cheap, too, you can pick one up on ebay for about $125. But it is very true to its name, it is a rugged system with a lot of unique features.

For an OS dever, the best thing to get your sweaty, dorito covered paws on after you feel you have graduated from the kiddie pool, is a legacy system with open spec devices. Quickly, you will realize that real machines are very different from what you saw in the sheltered, padded walls of virtualization. To make any progress, you need something that has the same hardware as the virtualization software implements, so that you can switch back and forth to test changes.

This old toughbook was lying in a drawer for several years, long neglected. When I found it, and looked at Device Manager in Windows, I knew what it was going to become. This is why I never throw a computer away.

This Toughbook has:

  1. PS/2 Touchpad and Keyboard
  2. USB Touchscreen
  3. Intel Graphics
  4. AC97 Sound Card
  5. RTL8139 Network Card
  6. Intel Wireless Card
  7. IDE Hard Drive
  8. USB Boot Support
  9. Serial Port
  10. ACPI and Battery

Why are these things important, to the point of making me drool dorito colored spit?

1 and 2: The Inputs

Every virtualization program supports a seamless input mode, based on a USB touchscreen, on UHCI. USB, however, is a very hard spec to implement, so having the old way (PS/2) of doing things left over is incredibly important. Convenient, then, that the toughbook has both!

3: The Graphics

Intel Graphics are the only major graphics cards with completely open specifications. This means that, when the time comes, I will be able to get into 3d acceleration without reading all of the nouveau project's source code. It also has full support for VBE 2.0, which I use to set up a linear frame buffer. Again, every virtualization program implements VBE in the same manner.

4: The Audio

No audio codec in the world has been implemented as often as AC97. Most High Definition Audio motherboard controllers support an AC97 legacy mode, meaning that HDA devices still implement AC97. Every virtualization program supports an AC97 controller, and it is very easy to program.

5 and 6: The Network

Similarly, RTL8139 is extremely easy to program, every virtualization program supports it. When the time comes, because it has an Intel wireless card with an open specification, I will be able to get wireless going easily.

7 and 8: The Storage

Without IDE, I would be lost for storage. This computer has a 40GB disk on the primary ATA channel, which every virtualization program has, as well. It also has the ability to boot from USB over EHCI, which lets me deploy a new version of the operating system in seconds. It also lets me test my USB drivers.

9: The Serial Port

Sometimes, things just don't work. The display is black, the mouse doesn't move, the keys don't work. You need something that you can fallback to when everything else goes wonky. That, my friends, is the serial port. The toughbook has a serial port on what is called COM1 by Windows, which is exactly what I use for debugging in every virtualization program.

10: The Battery

Another important feature of this device is that it can be unplugged. This means it will help me develop a power management system using ACPI.

Conclusion

The Panasonic Toughbook CF-18 is, in essence, the real hardware that every virtualization program attempts to be. It is the canonical PC. It lets me jump directly from virtualization to real hardware, with the same drivers. It has let me discover countless bugs in a much faster manner than I would accomplish if any of its features were different. It is, therefore, An OS Devers Dream.

A Bland Day At Intel

Let's talk about a man at Intel, his name is Bob. Bob has worked at Intel for 30 years, and is, perhaps, their most important employee. Bob presses CTRL Minus once a year to make the chips smaller, and CTRL Plus once a year, six months later, to make them faster. Bob spends his days in meetings, discussing how soon he could press CTRL Minus or CTRL Plus, and how much money such a keystroke could yield.

Sometimes, however, Bob gets bored and lonely. Bob likes to sit in on other meetings, to keep himself engaged. He likes to discuss Intel's current designs, and possible improvements. Bob sure does have a passion for new and innovative thinking.

There was another man in this story, whose name is Bill. Bill works for Microsoft, and is, perhaps, their most important employee. Bill presses the DEL button once a year to delete features from Windows, and F5 once a year, to see if they came back. Bill also tends to venture out of his domain, into meetings concerning current hardware support in Windows, and possible changes. Bill sure does have a passion for new and innovative thinking.

Once in a while, Bill and Bob will come to the same meeting, and share their passions concerning current designs.

AC'97 Meeting: Discussing the market share of AC'97 compatible sound cards:

Bob: Well, AC is nice, but what will a user do when they want to use more than 200 speakers? How will they cope with only one sound card per sound card? What if they want to rewire the outputs so that they are inputs, or an input to an input that is an output, or three outputs to an LED on a different codec?

Jane: I exactly don't understand. By having several PCI functions or PCI slots, one could easily support 200 speakers... The second thing... Finally, software could connect the inputs and outputs in any fashion, there is no need for specific hardware...

Bob: Well, what is the sampling rate of AC'97?

Jane: 20 bits at 48 KHz, which could be improved with some minor changes.

Bob: Aha! I could do so much better! 192Hz at 32 bits for everyone!

Bill: Bob, I think you have come up with a very general solution to obvious problems with AC'97. I wonder: would it be incompatible with AC'97 and would Microsoft have to write the driver for Windows?

Bob: Thank you. Incompatibility with AC'97 is a must! There is no conceivable way it could be retrofitted to support these features! And, as you know, hardware vendors, with help from Intel, would almost certainly write a functional driver for Windows, as soon as is possible.

Bill: Well, Bob, that sounds like a great idea. Intel would have Microsoft's full support in developing this, um, Intel, um...

Bob: Intel, um, High Definition Audio!

And so it began, a story like so many stories, one of triumph and despair. Here, the mighty Windows gained a new bus. Meanwhile, the lowly open source developers had to figure things out on their own.

ISA -> PCI -> PCI Express

IDE -> AHCI

APM -> ACPI

UHCI -> OHCI -> EHCI -> XHCI

BIOS -> UEFI

PIT -> HPET

PIC -> APIC, IOAPIC

The list goes on and on...

PS

I am not, however, saying that the newest versions are worse, or unnecessary. Except for UEFI, ACPI, and APIC, they are the Axis of Evil

Video Games

The most difficult applications to provide support for in an operating system are video games. Realtime video and audio, accurate time keeping, low overhead network programming, reliable memory allocation and task switching, and a filesystem that can handle thousands of reads and writes in one session are all requirements for the OS. It also needs support for many different formats of media, compression, and encryption. A good operating system must support gaming, in some form or fasion.

To provide some guidance for Redox development, I am going to see how much I can do in a simple application, in the filesystem/game folder, which will create a window, render graphics to it, accept user input, and play audio. This is the first test of a full featured use case of Redox. I will have to code an audio driver for this, and will stick to .wav files for now.

And So It Begins...

Redox development has been a long and arduous process, spanning several months of breaking things until they are fixed.

On came the compiler errors, which meant the kernel had to be linked as an ELF file, because the compiler had moved other functions above the entry function, and SSE had to be disabled to prevent aligned memory operations from being done on the stack pointer, because the compiler had decided to do that too. Some bugs went away after completely unrelated lines of code were changed, which led the compiler to make different optimization decisions. Most compiler optimizations had to be disabled, and a custom link script used to avoid compiler errors.

There was the transition to real hardware, and with it the realization that very few virtualization platforms implement the hardware faithfully, and very few manufacturer specifications are complete and correct. Sometimes the only reference for the 'correct' way of doing things is deep in the Linux source code. Often, device drivers would work on Qemu, but not VirtualBox, or on VirtualBox, but not real hardware.

Also, there was a discovery of bugs in critical areas, the IDE driver, the realloc function, the URL object, the list goes on. Discovery of these requires a complete understanding of the Redox source, and an investigation of the disassembly of the entire kernel, instruction by instruction. Some bugs disappeared when debugging output was added, the side effects of debugging only being different timing of instructions.

This blog is to chart the journey of discovery and rediscovery on my path of developing Redox, a Rust operating system. Most posts will be about new progress, some will be old anecdotes. All will be focused on demonstrating the plight of the not-so-humble operating system developer, who is often too far buried in principle or ego to discover their mistakes, or to replace them with sometimes compromising solutions.

In the end, I hope to develop something that proves that Rust can be a full-stack solution, from operating system to device drivers to user applications, or fail spectacularly at doing so. What I have made already supports some of these goals, with an X11-like windowing system, a VFS based filesystem and network stack, and ELF file loading and virtual memory management for user programs.

I hope you enjoy this as much as I do,

Jeremy Soller

Newer posts → Home