Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9e732f0
refactor: remove use of .init_array and .finit_array
KaiNorberg Jan 13, 2026
d1b9c0b
chore(README): reorganize README
KaiNorberg Jan 13, 2026
c426309
build: add "-z noexecstack" flag
KaiNorberg Jan 13, 2026
b232656
chore(boot): remove duplicate rsdp_get
KaiNorberg Jan 13, 2026
454a412
fix(boot:rsdp): rsdp_get
KaiNorberg Jan 13, 2026
d0bb6b6
fix(Make): add AVX to CFLAGS_DISABLE_SIMD
KaiNorberg Jan 13, 2026
6f3c931
fix(kernel:start): define the early init stack in the linker not start.S
KaiNorberg Jan 13, 2026
289272a
build: downgrade gnu-efi to version 3.0.14
KaiNorberg Jan 13, 2026
190f1c4
fix(boot): dont use %p in Print
KaiNorberg Jan 13, 2026
bdba9b0
build: add "-fno-stack-protector" and "-fcf-protection=none"
KaiNorberg Jan 13, 2026
d9c9828
build: add ALIGN to linker script
KaiNorberg Jan 13, 2026
12eb94e
fix(boot): mark kernel data/code as EfiRuntimeServicesCode
KaiNorberg Jan 13, 2026
1b651ec
fix(boot): mark kernel data/code as EfiReservedMemoryType
KaiNorberg Jan 13, 2026
07981cc
build: add wildcard patterns to linker scripts
KaiNorberg Jan 13, 2026
1a2f494
build: add "-z noexecstack" to boot linking
KaiNorberg Jan 13, 2026
f857201
build: refactor bootloader build flags
KaiNorberg Jan 13, 2026
bcf959e
build: switch from gcc to clang
KaiNorberg Jan 13, 2026
96b7161
build: switch ar to llvm-ar
KaiNorberg Jan 13, 2026
0603bfc
build: add "-no-pie" to kernel LDFLAGS
KaiNorberg Jan 13, 2026
43d1764
build: add "llvm" as a dependency
KaiNorberg Jan 13, 2026
b5efa66
build: fix compilation error
KaiNorberg Jan 13, 2026
60651c7
build: add "-no-pie" to all LDFLAGS
KaiNorberg Jan 13, 2026
943a179
build: revert back to gcc
KaiNorberg Jan 13, 2026
000287c
fix(boot): load the kernel as EfiLoaderData
KaiNorberg Jan 13, 2026
0dbf23e
fix(boot): load the kernel as EfiReservedMemoryType
KaiNorberg Jan 13, 2026
f17635b
refactor(boot): new more robust bootloader
KaiNorberg Jan 14, 2026
4688c8f
chore(.gitignore): add gnu-efi
KaiNorberg Jan 14, 2026
496aeb4
refactor(boot): use gnu-efi makefiles for the bootloader
KaiNorberg Jan 14, 2026
5a43de0
refactor(boot): revert to previous makefile
KaiNorberg Jan 14, 2026
e4fb45a
chore(boot): add entry code logging
KaiNorberg Jan 14, 2026
9b1ff76
fix(boot): add "-fno-builtin"
KaiNorberg Jan 14, 2026
ebf3d0c
fix(boot): simplify string.h handling
KaiNorberg Jan 14, 2026
b8c6062
fix(libstd:elf): dont use string.h functions
KaiNorberg Jan 14, 2026
a9f3bba
fix(boot): inline elf64_load_segments
KaiNorberg Jan 14, 2026
a3ecd65
fix(boot): add "-fno-builtin-memcpy -fno-builtin-memset -fno-tree-loo…
KaiNorberg Jan 14, 2026
e3c8e5e
fix(boot): use volatile when copying kernel
KaiNorberg Jan 14, 2026
007798a
fix(workflow): use ubuntu-22.04 instead of ubuntu-latest
KaiNorberg Jan 14, 2026
c9df7e8
fix(kernel:rwmutex): fix "initializer element is not constant"
KaiNorberg Jan 14, 2026
d0a0fe9
fix(kernel:boot): fix "expected expression before ‘{’ token"
KaiNorberg Jan 14, 2026
68feb5b
build: add QEMU_NOGRAPHIC flag for testing
KaiNorberg Jan 14, 2026
0b8b80c
fix(libstd:elf): incorrect ET_REL handling
KaiNorberg Jan 14, 2026
19c3a27
fix(boot): add a bunch of debug info to kernel_load
KaiNorberg Jan 14, 2026
dde9049
build(workflow): add debug info to test workflow
KaiNorberg Jan 14, 2026
7c883a2
fix(modules): cleanup linker script
KaiNorberg Jan 14, 2026
7b3a9b4
fix: dont discard gnu.hash
KaiNorberg Jan 14, 2026
6853434
fix(modules): add dynamic linker stuff to the linker script
KaiNorberg Jan 14, 2026
1deeadd
fix: add expected symbols and padding to linker scripts
KaiNorberg Jan 14, 2026
a2c13e2
fix: undefined reference to `_testsEnd'
KaiNorberg Jan 14, 2026
2c96ed6
chore(boot): remove debug code
KaiNorberg Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 14 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,36 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: trues
submodules: true

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential qemu-system-x86 mtools iasl

- name: Setup PatchworkOS
run: make setup DEBUG=1 TESTING=1 QEMU_EXIT_ON_PANIC=1
sudo apt-get install -y clang llvm build-essential qemu-system-x86 mtools iasl

- name: Build PatchworkOS
run: make all DEBUG=1 TESTING=1 QEMU_EXIT_ON_PANIC=1

- name: Inspect kernel ELF
run: |
echo "=== Kernel ELF Program Headers ==="
readelf -l bin/kernel/kernel || true
echo "=== Kernel ELF Section Headers ==="
readelf -S bin/kernel/kernel || true
echo "=== Kernel file size ==="
ls -la bin/kernel/kernel

- name: Verify PatchworkOS.img exists
run: |
if [ ! -f bin/PatchworkOS.img ]; then
echo "Error: bin/PatchworkOS.img not found after build!"
exit 1
fi

- name: Run PatchworkOS with QEMU (test launch and graceful timeout)
- name: Run PatchworkOS with QEMU
id: qemu_test
run: | # We use this cheap trick to check if qemu runs properly, the idea is that we start qemu, and let it run for one minute and it does not crash thats considered a success, if it crashes, well thats bad. If it doesent we shut it down and call it a success, a bit simple but gets the job done.
setsid make run DEBUG=1 TESTING=1 QEMU_EXIT_ON_PANIC=1 &
run: |
setsid make run DEBUG=1 TESTING=1 QEMU_EXIT_ON_PANIC=1 QEMU_NOGRAPHIC=1 &
QEMU_PID=$!
echo "QEMU process started with PID=$QEMU_PID"
echo "QEMU_PID=$QEMU_PID" >> $GITHUB_OUTPUT
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@ include/kernel/version.h
lib/acpica
lib/acpica_tests
lib/argon2
lib/gnu-efi
include/argon2
temp.md
15 changes: 9 additions & 6 deletions Make.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ MKCWD = @mkdir -p $(@D)
CC = gcc
AS = gcc
LD = gcc
AR = gcc-ar
AR = ar
OBJCOPY = objcopy

CFLAGS := \
-std=gnu11 \
-ffreestanding \
-fno-builtin \
-march=x86-64 \
-fno-pie \
-fno-stack-protector \
-fcf-protection=none \
-mno-red-zone \
-nostdinc \
-Wall \
Expand All @@ -36,7 +38,8 @@ CFLAGS_DISABLE_SIMD := \
-mno-mmx -mno-3dnow \
-mno-80387 -mno-sse \
-mno-sse2 -mno-sse3 \
-mno-ssse3 -mno-sse4
-mno-ssse3 -mno-sse4 \
-mno-avx -mno-avx2

ASFLAGS := \
-nostdinc \
Expand All @@ -51,7 +54,9 @@ LDFLAGS := \
-nostdlib \
-Lbin/libstd \
-Lbin/libpatchwork \
-no-pie
-z noexecstack \
-Wno-unused-command-line-argument \
-no-pie

ifeq ($(TESTING),1)
CFLAGS += -D_TESTING_
Expand All @@ -61,10 +66,8 @@ endif
ifeq ($(DEBUG),1)
CFLAGS += -O0 -g3 -ggdb -fno-omit-frame-pointer -fno-inline
LDFLAGS += -g
DEBUG_LDFLAGS = -g
else
CFLAGS += -O2 -DNDEBUG
DEBUG_LDFLAGS =
endif

ifeq ($(NOSTDLIB),1)
Expand Down
38 changes: 1 addition & 37 deletions Make.rules
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,4 @@ $(BINDIR)/%.a: $(OBJ)
$(BINDIR)/%: $(OBJ)
$(MKCWD)
@echo " LD $@"
@$(LD) -o $@ $^ $(LDFLAGS) $(LDSTDLIB)

$(BINDIR)/%.efi: $(OBJ)
$(MKCWD)
@echo " LD $@ (EFI)"
@gcc -shared -nostdlib -nostartfiles -fPIC -fno-stack-protector \
$(DEBUG_LDFLAGS) \
-Wl,-shared,-Bsymbolic \
-Wl,-T,lib/gnu-efi/gnuefi/elf_x86_64_efi.lds \
-Llib/gnu-efi/x86_64/lib \
-Llib/gnu-efi/x86_64/gnuefi \
lib/gnu-efi/x86_64/gnuefi/crt0-efi-x86_64.o $^ \
-o $(BINDIR)/temp.so \
-lgnuefi -lefi

ifeq ($(DEBUG),1)
@echo " OBJCOPY $@ (with debug symbols)"
@$(OBJCOPY) --only-keep-debug $(BINDIR)/temp.so $(BINDIR)/$*.debug
@$(OBJCOPY) \
-j .text -j .sdata -j .data -j .dynamic -j .dynsym \
-j .rel -j .rela -j .rel.* -j .rela.* -j .reloc -j .eh_frame \
--target efi-app-x86_64 \
--subsystem=10 \
--add-gnu-debuglink=$(BINDIR)/$*.debug \
$(BINDIR)/temp.so $@
@$(OBJCOPY) --strip-debug $@
else
@echo " OBJCOPY $@"
@$(OBJCOPY) \
-j .text -j .sdata -j .data -j .dynamic -j .dynsym \
-j .rel -j .rela -j .rel.* -j .rela.* -j .reloc -j .eh_frame \
--target efi-app-x86_64 \
--subsystem=10 \
$(BINDIR)/temp.so $@
endif

@rm -f $(BINDIR)/temp.so
@$(LD) -o $@ $^ $(LDFLAGS) $(LDSTDLIB)
18 changes: 12 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ $(VERSION_HEADER): .FORCE | include/kernel
include/kernel:
@mkdir -p $@

lib/gnu-efi/.built: | lib/gnu-efi
lib/gnu-efi/.built:
@if [ ! -d "lib/gnu-efi" ]; then \
echo "CLONE gnu-efi"; \
git clone https://github.com/ncroxon/gnu-efi.git lib/gnu-efi >/dev/null 2>&1; \
fi
@echo "BUILD gnu-efi"
@$(MAKE) -C lib/gnu-efi >/dev/null 2>&1
@touch $@
Expand Down Expand Up @@ -248,9 +252,7 @@ clean_programs:

nuke: clean
@echo "NUKE all"
@$(MAKE) -C lib/gnu-efi clean 2>/dev/null || true
@rm -rf lib/doomgeneric-patchworkos lib/lua-5.4.7 lib/acpica lib/acpica_tests lib/argon2 include/argon2 meta/docs
@rm -rf lib/gnu-efi/.built lib/argon2/.built lib/acpica_tests/.built
@rm -rf lib/doomgeneric lib/lua lib/acpica lib/acpica_tests lib/argon2 include/argon2 meta/docs lib/gnu-efi

QEMU_MEMORY ?= 2G
QEMU_CPUS ?= $(shell nproc 2>/dev/null || echo 8)
Expand All @@ -259,15 +261,19 @@ QEMU_ARGS ?=

QEMU_FLAGS = \
-M $(QEMU_MACHINE) \
-display sdl \
-serial stdio \
-drive format=raw,file=$(IMAGE) \
-m $(QEMU_MEMORY) \
-smp $(QEMU_CPUS) \
-cpu qemu64 \
-drive if=pflash,format=raw,unit=0,file=lib/OVMFbin/OVMF_CODE-pure-efi.fd,readonly=on \
-drive if=pflash,format=raw,unit=1,file=lib/OVMFbin/OVMF_VARS-pure-efi.fd

ifeq ($(QEMU_NOGRAPHIC),1)
QEMU_FLAGS += -nographic
else
QEMU_FLAGS += -display sdl -serial stdio
endif

ifeq ($(DEBUG),1)
else
QEMU_FLAGS += -no-shutdown -no-reboot
Expand Down
55 changes: 29 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@

<img src="meta/screenshots/desktop.png" alt="Desktop Screenshot" />

**PatchworkOS** is a modular non-POSIX operating system for the x86_64 architecture that rigorously follows an "everything is a file" philosophy, in the style of Plan9. Built from scratch in C and assembly, it's intended to be an educational and experimental operating system.
**PatchworkOS** is a modular non-POSIX operating system for the x86_64 architecture that rigorously follows an "everything is a file" philosophy. Built from scratch in C and assembly.

While primarily a project made for fun, the goal is still to make a "real" operating system, one that runs on real hardware and has the performance one would expect from a modern operating system without jumping ahead to user space features or drivers, a floppy disk driver and a round-robin scheduler are not enough.
While primarily a project made for fun, the goal is still to make a "real" operating system, one that runs on real hardware and has the performance one would expect from a modern operating system without jumping ahead to user space features or drivers, a floppy disk driver with a round-robin scheduler is not enough.

PatchworkOS is not a UNIX clone, it's intended to be a (hopefully) interesting experiment in operating system design by attempting to use unique algorithms and designs over tried and tested ones. Sometimes this leads to bad results, and sometimes, with a bit of luck, good ones.

Expand Down Expand Up @@ -55,7 +55,7 @@ Will this project ever reach its goals? Probably not, but that's not the point.

- Preemptive and tickless [EEVDF scheduler](https://kainorberg.github.io/PatchworkOS/html/d7/d85/group__kernel__sched.html) based upon the [original paper](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=805acf7726282721504c8f00575d91ebfd750564) and implemented using an [Augmented Red-Black tree](https://kainorberg.github.io/PatchworkOS/html/da/d90/group__kernel__utils__rbtree.html) to achieve `O(log n)` worst case complexity. EEVDF is the same algorithm used in the modern Linux kernel, but ours is obviously **a lot** less mature.
- Multithreading and Symmetric Multi Processing with fine-grained locking.
- Physical and virtual memory management is `O(1)` per page and `O(n)` where `n` is the number of pages per allocation/mapping operation, see [benchmarks](#benchmarks) for more info.
- Optimized memory management, featuring object caching and `O(1)` per page physical and virtual memory managers. See [benchmarks](#benchmarks) for more info.
- File based IPC including [pipes](https://kainorberg.github.io/PatchworkOS/html/d7/d64/group__modules__ipc__pipe.html), [shared memory](https://kainorberg.github.io/PatchworkOS/html/df/d3f/group__modules__ipc__shmem.html), [sockets](https://kainorberg.github.io/PatchworkOS/html/d4/db0/group__kernel__fs__netfs.html) and Plan9 inspired "signals" called [notes](https://kainorberg.github.io/PatchworkOS/html/d8/db1/group__kernel__ipc__note.html).
- File based device API [abstractions](https://kainorberg.github.io/PatchworkOS/html/de/d7b/group__kernel__drivers__abstract.html), including framebuffers, input devices, etc.
- [Synchronization primitives](https://kainorberg.github.io/PatchworkOS/html/dd/d6b/group__kernel__sync.html) including Read-Copy-Update, mutexes, R/W locks, sequential locks, futexes and others.
Expand Down Expand Up @@ -92,6 +92,24 @@ Will this project ever reach its goals? Probably not, but that's not the point.
- Fully Asynchronous I/O and syscalls (io_uring?).
- USB support.

## Setup

```bash
# Install dependencies
sudo dnf install gcc make mtools qemu-system-x86 # For Fedora
sudo apt install build-essential mtools qemu-system-x86 # For Debian/Ubuntu

# Clone this repository, you can also use the green Code button at the top of the Github.
git clone https://github.com/KaiNorberg/PatchworkOS
cd PatchworkOS

# Build (creates PatchworkOS.img in bin/)
make all

# Run using QEMU
make run
```

---

## Doxygen Documentation
Expand Down Expand Up @@ -635,7 +653,9 @@ The scheduler has not yet been properly benchmarked. However, testing using the

## Shell Utilities

PatchworkOS includes its own shell utilities designed around its [file flags](#file-flags) system, when file flags are used we also demonstrate the short form. Included is a brief overview with some usage examples. For convenience the shell utilities are named after their POSIX counterparts, however they are not drop-in replacements.
PatchworkOS includes its own shell utilities designed around its [file flags](#file-flags) system, allowing all shell utilities to be "dumb" and rely on the virtual file system to provide more complex behavior.

Included is a brief overview with some usage examples. For convenience the shell utilities are named after their POSIX counterparts, however they are not drop-in replacements. When file flags are used we also demonstrate the short form.

### touch

Expand Down Expand Up @@ -734,28 +754,7 @@ There are other utils available that work as expected, for example `symlink` and

---

## Setup

### Requirements

| Requirement | Details |
|:------------|:--------|
| **OS** | Linux (WSL might work, but I make no guarantees) |
| **Tools** | GCC, make, mtools, QEMU (optional) |

### Build and Run

```bash
# Clone this repository, you can also use the green Code button at the top of the Github.
git clone https://github.com/KaiNorberg/PatchworkOS
cd PatchworkOS

# Build (creates PatchworkOS.img in bin/)
make all

# Run using QEMU
make run
```
## Development

### Additional commands

Expand Down Expand Up @@ -855,6 +854,10 @@ If you are unsure where to start, check the [Todo List](https://kainorberg.githu

Check out the [contribution guidelines](CONTRIBUTING.md) to get started.

## License

Distributed under the MIT License. See [LICENSE](https://github.com/KaiNorberg/PatchworkOS/blob/main/LICENSE) for more information.

## Nostalgia

[The first Reddit post and image of PatchworkOS](https://www.reddit.com/r/osdev/comments/18gbsng/a_little_over_2_years_ago_i_posted_a_screenshot/) from back when getting to user space was a massive milestone and the kernel was supposed to be a UNIX-like microkernel.
6 changes: 3 additions & 3 deletions include/boot/boot_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#include <kernel/mem/paging_types.h>

#include <gnu-efi/inc/efi.h>
#include <gnu-efi/inc/efilib.h>

#include <_internal/MAX_NAME.h>
#include <stdint.h>
#include <sys/defs.h>
#include <sys/elf.h>
#include <sys/io.h>
#include <sys/list.h>

/**
Expand All @@ -21,7 +21,7 @@
* @{
*/

static bool boot_is_mem_ram(EFI_MEMORY_TYPE type)
static UNUSED_FUNC bool boot_is_mem_ram(EFI_MEMORY_TYPE type)
{
switch (type)
{
Expand Down
4 changes: 2 additions & 2 deletions include/kernel/cpu/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
static inline void cli_push(void)
{
uint64_t rflags = rflags_read();
asm volatile("cli" ::: "memory");
ASM("cli" :: : "memory");

if (SELF->cli == 0)
{
Expand All @@ -46,7 +46,7 @@ static inline void cli_pop(void)
SELF->cli--;
if (SELF->cli == 0 && (SELF->oldRflags & RFLAGS_INTERRUPT_ENABLE))
{
asm volatile("sti" ::: "memory");
ASM("sti" :: : "memory");
}
}

Expand Down
30 changes: 28 additions & 2 deletions include/kernel/cpu/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#ifndef __ASSEMBLER__
#include <kernel/config.h>
#include <kernel/cpu/cpu_id.h>
#include <kernel/cpu/stack_pointer.h>
#include <kernel/cpu/tss.h>

Expand Down Expand Up @@ -38,6 +37,33 @@ typedef struct cpu cpu_t;

#ifndef __ASSEMBLER__

/**
* @brief The offset of the `id` member in the `cpu_t` structure.
*
* Needed to access the CPU ID from assembly code.
*/
#define CPU_OFFSET_ID 0x8

/**
* @brief Maximum number of CPUs supported.
*/
#define CPU_MAX UINT8_MAX

/**
* @brief ID of the bootstrap CPU.
*/
#define CPU_ID_BOOTSTRAP 0

/**
* @brief Invalid CPU ID.
*/
#define CPU_ID_INVALID UINT16_MAX

/**
* @brief Type used to identify a CPU.
*/
typedef uint16_t cpu_id_t;

/**
* @brief CPU stack canary value.
*
Expand Down Expand Up @@ -72,7 +98,7 @@ typedef struct cpu
uint8_t doubleFaultStackBuffer[CONFIG_INTERRUPT_STACK_PAGES * PAGE_SIZE] ALIGNED(PAGE_SIZE);
uint8_t nmiStackBuffer[CONFIG_INTERRUPT_STACK_PAGES * PAGE_SIZE] ALIGNED(PAGE_SIZE);
uint8_t interruptStackBuffer[CONFIG_INTERRUPT_STACK_PAGES * PAGE_SIZE] ALIGNED(PAGE_SIZE);
uint8_t percpu[CONFIG_PERCPU_SIZE] ALIGNED(PAGE_SIZE);
uint8_t percpu[CONFIG_PERCPU_SIZE] ALIGNED(PAGE_SIZE); ///< Buffer used for per-CPU data.
} cpu_t;

static_assert(offsetof(cpu_t, self) == CPU_OFFSET_SELF,
Expand Down
Loading