Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions .github/workflows/kernel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: Kernel CI

on:
push:
branches: [main, master]
paths:
- 'kernel/**'
- 'src/**'
- 'compiler/**'
- 'flow'
- 'requirements.txt'
- 'requirements.lock'
- 'pyproject.toml'
- '.github/actions/**'
- '.github/workflows/kernel.yml'
pull_request:
branches: [main, master]
paths:
- 'kernel/**'
- 'src/**'
- 'compiler/**'
- 'flow'
- 'requirements.txt'
- 'requirements.lock'
- 'pyproject.toml'
- '.github/actions/**'
- '.github/workflows/kernel.yml'
workflow_dispatch:

concurrency:
group: kernel-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
kernel:
name: x86_64 freestanding kernel
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- uses: actions/checkout@v7

- uses: ./.github/actions/setup-python

- name: Install freestanding and boot-test toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang \
lld \
grub-common \
grub-pc-bin \
xorriso \
qemu-system-x86

- name: Parse kernel source
env:
PYTHONPATH: src
run: pytest tests/unit/test_kernel_base.py -v

- name: Build freestanding x86_64 ELF
env:
PYTHONPATH: src
FLOW_HOST: python
run: |
chmod +x ./flow kernel/x86_64/build.sh
kernel/x86_64/build.sh /tmp/flow-kernel-build
test -s /tmp/flow-kernel-build/flow-kernel.elf
grub-file --is-x86-multiboot2 /tmp/flow-kernel-build/flow-kernel.elf
llvm-readelf -h /tmp/flow-kernel-build/flow-kernel.elf
llvm-nm /tmp/flow-kernel-build/flow-kernel.elf | grep -F 'flow_export_kernel_main'

- name: Build bootable GRUB image
run: |
mkdir -p /tmp/flow-kernel-iso/boot/grub
cp /tmp/flow-kernel-build/flow-kernel.elf /tmp/flow-kernel-iso/boot/flow-kernel.elf
cp kernel/x86_64/grub.cfg /tmp/flow-kernel-iso/boot/grub/grub.cfg
grub-mkrescue -o /tmp/flow-kernel.iso /tmp/flow-kernel-iso
test -s /tmp/flow-kernel.iso

- name: Boot smoke under QEMU
run: |
set +e
timeout 10s qemu-system-x86_64 \
-machine accel=tcg \
-m 128M \
-cdrom /tmp/flow-kernel.iso \
-serial file:/tmp/flow-kernel-serial.log \
-display none \
-no-reboot \
-no-shutdown
rc=$?
set -e

cat /tmp/flow-kernel-serial.log
if [[ "$rc" -ne 0 && "$rc" -ne 124 ]]; then
echo "QEMU exited unexpectedly with status $rc" >&2
exit "$rc"
fi

grep -F 'Flow kernel: entry' /tmp/flow-kernel-serial.log
grep -F 'Flow kernel: boot contract accepted' /tmp/flow-kernel-serial.log

- name: Upload kernel artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: flow-kernel-x86_64
path: |
/tmp/flow-kernel-build/flow-kernel.elf
/tmp/flow-kernel.iso
/tmp/flow-kernel-serial.log
if-no-files-found: warn
retention-days: 7
23 changes: 23 additions & 0 deletions kernel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Flow kernel

This directory is the freestanding kernel substrate for Flow. The first target is x86_64 booted through Multiboot2. Flow owns the kernel entry contract and policy-facing primitives; the architecture layer is restricted to the CPU operations that cannot be expressed portably.

The current base boots in 32-bit Multiboot2 mode, establishes an identity-mapped 1 GiB long-mode address space with 2 MiB pages, enters x86_64 long mode, installs a 64 KiB kernel stack, and calls the stable Flow C ABI entry `flow_export_kernel_main`. The Flow entry validates the Multiboot2 contract and reports boot state over COM1 serial.

Build the ELF with:

```bash
bash kernel/x86_64/build.sh
```

The build requires Flow's normal transpiler dependencies plus `clang` and `ld.lld`. If `grub-file` is installed, the build also validates the resulting ELF as Multiboot2.

Boot it in QEMU with:

```bash
bash kernel/x86_64/run.sh
```

That additionally requires `grub-mkrescue` and `qemu-system-x86_64`. Successful boot reaches the serial message `Flow kernel: boot contract accepted`.

The next kernel layers should remain above this boundary: Multiboot2 memory-map ingestion, a physical page allocator, interrupt/exception tables, timer-driven scheduling, syscall entry, virtual memory ownership, and then the eBPF verifier/interpreter/JIT hooks. eBPF should consume explicit kernel hook surfaces rather than becoming part of the boot substrate.
116 changes: 116 additions & 0 deletions kernel/x86_64/boot.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
.section .multiboot,"a"
.align 8
multiboot_header:
.long 0xe85250d6
.long 0
.long multiboot_header_end - multiboot_header
.long -(0xe85250d6 + 0 + (multiboot_header_end - multiboot_header))
.short 0
.short 0
.long 8
multiboot_header_end:

.section .boot.data,"aw"
.align 8
boot_magic:
.long 0
boot_info:
.long 0

.align 4096
pml4:
.skip 4096
.align 4096
pdpt:
.skip 4096
.align 4096
pd:
.skip 4096

.section .boot.bss,"aw",@nobits
.align 16
stack_bottom:
.skip 65536
stack_top:

.section .boot.text,"ax"
.code32
.global _start
.type _start, @function
_start:
cli
movl %eax, boot_magic
movl %ebx, boot_info
movl $stack_top, %esp

movl $pdpt, %eax
orl $0x3, %eax
movl %eax, pml4

movl $pd, %eax
orl $0x3, %eax
movl %eax, pdpt

xorl %ecx, %ecx
1:
movl %ecx, %eax
shll $21, %eax
orl $0x83, %eax
movl %eax, pd(,%ecx,8)
movl $0, pd+4(,%ecx,8)
incl %ecx
cmpl $512, %ecx
jne 1b

movl %cr4, %eax
orl $(1 << 5), %eax
movl %eax, %cr4

movl $pml4, %eax
movl %eax, %cr3

movl $0xc0000080, %ecx
rdmsr
orl $(1 << 8), %eax
wrmsr

movl %cr0, %eax
orl $(1 << 31), %eax
movl %eax, %cr0

lgdt gdt64_ptr
ljmp $0x08, $long_mode_start

.code64
long_mode_start:
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
xorw %ax, %ax
movw %ax, %fs
movw %ax, %gs

movq $stack_top, %rsp
xorq %rbp, %rbp

movl boot_magic(%rip), %edi
movl boot_info(%rip), %esi
call flow_export_kernel_main

2:
cli
hlt
jmp 2b
.size _start, . - _start

.align 8
gdt64:
.quad 0x0000000000000000
.quad 0x00af9a000000ffff
.quad 0x00cf92000000ffff
gdt64_end:

gdt64_ptr:
.word gdt64_end - gdt64 - 1
.long gdt64
49 changes: 49 additions & 0 deletions kernel/x86_64/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
BUILD="${1:-$HERE/build}"

mkdir -p "$BUILD"

"$ROOT/flow" transpile "$HERE/kernel.flow" \
--c \
--export kernel_main kernel_abi_version kernel_page_size kernel_boot_magic_valid kernel_page_count \
-o "$BUILD/kernel.c"

clang \
-target x86_64-unknown-none-elf \
-std=c11 \
-ffreestanding \
-fno-builtin \
-fno-stack-protector \
-fno-pic \
-mno-red-zone \
-ffunction-sections \
-fdata-sections \
-O2 \
-c "$BUILD/kernel.c" \
-o "$BUILD/kernel-flow.o"

clang \
-target x86_64-unknown-none-elf \
-ffreestanding \
-fno-pic \
-mno-red-zone \
-c "$HERE/boot.S" \
-o "$BUILD/boot.o"

ld.lld \
-nostdlib \
--gc-sections \
-T "$HERE/linker.ld" \
"$BUILD/boot.o" \
"$BUILD/kernel-flow.o" \
-o "$BUILD/flow-kernel.elf"

if command -v grub-file >/dev/null 2>&1; then
grub-file --is-x86-multiboot2 "$BUILD/flow-kernel.elf"
fi

printf '%s\n' "$BUILD/flow-kernel.elf"
7 changes: 7 additions & 0 deletions kernel/x86_64/grub.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
set timeout=0
set default=0

menuentry "Flow kernel" {
multiboot2 /boot/flow-kernel.elf
boot
}
77 changes: 77 additions & 0 deletions kernel/x86_64/kernel.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Flow base kernel core.
#
# Phase one deliberately keeps the architecture shim tiny: Flow owns boot
# validation and kernel policy; the shim owns privileged CPU/serial operations.

@cEmbed("
#include <stdint.h>

static inline void flow_kernel_outb(uint16_t port, uint8_t value) {
__asm__ volatile (\"outb %0, %1\" : : \"a\"(value), \"Nd\"(port));
}

void flow_kernel_serial_init(void) {
flow_kernel_outb(0x3f8 + 1, 0x00);
flow_kernel_outb(0x3f8 + 3, 0x80);
flow_kernel_outb(0x3f8 + 0, 0x03);
flow_kernel_outb(0x3f8 + 1, 0x00);
flow_kernel_outb(0x3f8 + 3, 0x03);
flow_kernel_outb(0x3f8 + 2, 0xc7);
flow_kernel_outb(0x3f8 + 4, 0x0b);
}

void flow_kernel_serial_write(const char* text) {
while (*text) {
flow_kernel_outb(0x3f8, (uint8_t)*text++);
}
}

__attribute__((noreturn)) void flow_kernel_halt(void) {
for (;;) {
__asm__ volatile (\"cli; hlt\");
}
}
")

extern {
function flow_kernel_serial_init() -> void
function flow_kernel_serial_write(text: string) -> void
function flow_kernel_halt() -> void
}

export function kernel_abi_version() -> u32 {
return 1
}

export function kernel_page_size() -> u64 {
return 4096
}

export function kernel_boot_magic_valid(magic: u32) -> bool {
return magic == 920085129
}

export function kernel_page_count(bytes: u64) -> u64 {
if bytes == 0 {
return 0
}
return ((bytes - 1) / 4096) + 1
}

export function kernel_main(multiboot_magic: u32, multiboot_info: ptr<void>) -> i32 {
flow_kernel_serial_init()
flow_kernel_serial_write("Flow kernel: entry\n")

if not kernel_boot_magic_valid(multiboot_magic) {
flow_kernel_serial_write("Flow kernel: invalid Multiboot2 magic\n")
return 1
}

if multiboot_info == null {
flow_kernel_serial_write("Flow kernel: missing Multiboot2 info\n")
return 2
}

flow_kernel_serial_write("Flow kernel: boot contract accepted\n")
return 0
}
Loading
Loading