Section 3: Project directions

This page collects some ideas about directions for Chickadee-based OS projects. THe course’s capstone is an individual project (or a group project substantive enough to support group work), and it’s time for you to start thinking about what you might do!

TCP/IP networking

Chickadee ships without support for networking, which is now a critical feature for every operating system. Adding networking support is an excellent project!

Full networking has at least three components: device support, kernel support for TCP/IP, and user-level support for networking system calls.

Device support

Networking support involves hardware devices that can send messages to, and receive messages from, a physical networking layer. You’ll need to teach the Chickadee kernel how to communicate with a networking device—that is, to write a networking device driver. This is useful practice; the vast majority of code in most kernels is given over to device drivers, since many hardware designers “innovate” their own device driver interfaces.

Some good choices for networking devices:

Different QEMU arguments can support networking via E1000 or virtio.

Kernel support for TCP/IP

The TCP/IP protocol suite is massive, but there are free implementations designed to be easy to port to different operating systems. We recommend lwIP (lightweight IP) or picoTCP. Lots of lwIP ports to teaching operating systems can be found online. You can also read about how to port lwIP to a new OS on the lwIP Fandom wiki.1

User-level support for networking system calls

With device support and kernel support, you can start testing networking. When your Chickadee boots, does it detect the networking card you chose? And then can it use lwIP’s DHCP to successfully obtain an IP address?

But the point of networking is to allow user processes to access the network. This requires support for new system calls—either ones based on the Unix system calls (socket, accept, listen, connect), or others, such as those from Plan 9, the self-styled successor to Unix. (For instance, see Section 5.)

Other forms of networking

It’s possible—and useful!—to implement networking without networking devices. Localhost networking uses networking system calls, and kernel support for TCP/IP, but allows processes on the same OS to communicate as if over a network. You might want to start with localhost networking and then add device support later!

Kernel extensibility

Operating systems’ primary extension mechanism is the system call API. A full system—kernel plus applications—can do a lot. But code running inside the kernel can implement powerful functions with low overhead.

Kernel extensibility saw a lot of research in the 1990s (e.g., SPIN, Flux), but in recent times, kernel extensibility has been transformed by eBPF, or the Extended Berkeley Packet Filter. eBPF is a programming language and program representation, coupled with a verifier: a kernel component capable of validating that an eBPF program is safe (e.g., accesses no invalid memory). User-level processes can specify eBPF programs that run in response to specific events. eBPF is extensively used in production for networking, security, and tracing and debugging, and several compilers can target the eBPF format for their output.

Integrating eBPF with Chickadee would be an interesting project. Port the eBPF verifier to Chickadee, and then provide a system call that allows eBPF programs to run in response to a hook. For example, you could implement a form of seccomp-bpf, where a process can restrict the system calls it or its children are allowed to make by specifying an eBPF program that runs in response to each system call request.

Kernel observability

Operating systems kernels must offer debugging and observability mechanisms so that OS users and administrators can figure out what’s going on. Current kernel offerings range from simple system calls that return system statistics to full VFS implementations that expose structured kernel information in the form of human-readable files. For instance:

A reasonably full-featured /proc would be a great project!

Security and isolation

As we saw in Section 2, security concerns are a major evolutionary pressure on modern operating systems. Many recent changes in Linux have aimed to improve Linux’s security, or to offer additional security and isolation guarantees to applications. There are a lot of project ideas along these lines!

  1. Implement a form of address space layout randomization for user programs.

  2. Implement a form of kernel address space layout randomization for the Chickadee kernel.

  3. Implement a version of the seccomp system call, or, even better, its seccomp-bpf variant (see kernel extensibility).

  4. Implement system calls that constrain processes in other ways, such as:

    • chroot: Limit a process and its children to accessing only a specific subdirectory.
    • FreeBSD jail.
    • Linux namespaces, such as process namespaces that present different views of the process IDs (also a feature of FreeBSD jail).
  5. Implement KAISER, which ensures most of the kernel is not mapped in process page tables. This should be coupled with other mitigations that can expose kernel information to user-level processes.

Inter-process communication (IPC)

Operating systems offer tons of ways for processes to communicate. The file system can perform this function, and pipes are designed for inter-process communication. But many other IPC mechanisms exist with different performance and permissions characteristics. You could port some of them Chickadee, or read the computing research literature for newer designs, or invent your own!

  1. Signals (Unix sigaction and kill system calls).

  2. Semaphores, which are synchronization objects managed by the kernel.

  3. Unix-domain sockets, which use versions of networking-style system calls to set up communication channels accessible via the file system.

  4. Shared-memory regions: named regions of memory that can be mapped into multiple processes simultaneously.

  5. Message queues, which let process exchange prioritized messages.

  6. For fun: L3 IPC!

Synchronization

The most efficient mechanisms for coordinating threads require kernel support: a user-level thread cannot safely block without it. The most fun system call providing user-level thread support is Linux’s futex, which has a bewildering array of useful options. (Wikipedia gives the basics; the manual page goes wild.) Can you provide Chickadee with an effective futex system call, and use it to provide true blocking to Chickadee user-level threads?

Architecture

Intel-compatible x86-64 is far from the only architecture these days, and most operating systems are portable to other architectures. The most important architectural alternatives are Arm and RISC-V; Arm is more commercially relevant (many of you are using M1 or M2 Macs, which are based on Arm chips). QEMU can emulate Arm and RISC-V chips as well as x86-64 (and many others). Can you produce a version of Chickadee that boots on an emulated Arm?

Graphics

Console graphics are amazing, but bitmapped graphics are amazing too. Can you extend Chickadee to access one of QEMU’s video modes? Maybe you could run DOOM. This will likely require changing Chickadee’s device detection to access UEFI information, which is how we believe QEMU exposes information about video modes. (See Section 12.9 of the UEFI specification.)

Programming languages

Everyone loves Rust, the programming language that supposedly avoids all errors! (5 minutes later) We regret to inform you that Rust does not avoid all errors, but it’s still pretty cool. People have used it to build operating systems, toy operating systems, and teaching operating systems.

Try porting some of Chickadee’s less architecture-specific code to Rust. You could first change Chickadee’s Makefiles so that Chickadee can build and run processes whose code is mostly in Rust. (This won’t happen automatically: you’ll need to teach Rust to compile binaries with a minimal support library, and write Rust versions of the functions in u-lib.hh.) Then start porting parts of Chickadee itself!

Don’t like Rust? Try another language. Operating systems have been written in Go, Modula-3, and more. Whole research papers have been written about experiences writing kernels in high-level languages.

Rather than port an OS to a whole new language, you could also improve Chickadee’s support for programming language technologies like debuggers and sanitizers. It would be so amazing to have a thread sanitizer, kernel address sanitizer, or kernel concurrency sanitizer working on Chickadee kernels. It would be amazing to run GDB on Chickadee user processes! (This requires new system call support as well as a porting exercise.)

File systems

Chickadee understands just ChickadeeFS. Can you help it understand another file system format, such as VFAT, so it can read MS-DOS images, or ISO 9660, so it can read CD-ROMs?

Or you could add more features to ChickadeeFS, such as symbolic links, special file types that correspond to kernel-implemented devices (e.g., /dev/null, /dev/zero, /dev/urandom), or mount points.

Games and fun

An excellent form of project is to make yourself happy. Can you make Chickadee perform the computer systems equivalent of a stupid pet trick? Implementing a simple computer game like Snake can lead you to interesting places as you add operating system features (think raw keyboard input modes).

Need inspiration? Check out the QEMU Advent Calendar and its previous versions!

Yet more


  1. ?!?!?!??! ↩︎