Skip to content
Merged
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
99 changes: 66 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,32 @@ on:

jobs:
build-and-test:
name: Build and Test
name: Build and Test - BCM${{ matrix.bcm }}
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
bcm: [2835, 2836, 2837]
include:
- bcm: 2835
kernel_name: kernel.img
pi_model: "Pi Zero/1"
qemu_machine: raspi0
qemu_mem: 512
qemu_binary: qemu-system-arm
- bcm: 2836
kernel_name: kernel7.img
pi_model: "Pi 2"
qemu_machine: raspi2b
qemu_mem: 1024
qemu_binary: qemu-system-arm
- bcm: 2837
kernel_name: kernel8.img
pi_model: "Pi 3"
qemu_machine: raspi3b
qemu_mem: 1024
qemu_binary: qemu-system-aarch64

steps:
- name: Checkout code
Expand All @@ -18,53 +42,36 @@ jobs:
- name: Install ARM toolchain
run: |
sudo apt-get update
sudo apt-get install -y gcc-arm-none-eabi binutils-arm-none-eabi
sudo apt-get install -y gcc-arm-none-eabi binutils-arm-none-eabi gcc-aarch64-linux-gnu qemu-system-arm qemu-system-aarch64

- name: Verify toolchain installation
run: |
arm-none-eabi-gcc --version
arm-none-eabi-ld --version
aarch64-linux-gnu-gcc --version
qemu-system-arm --version
qemu-system-aarch64 --version

- name: Build BCM2835 (RPi Zero/1)
- name: Build BCM${{ matrix.bcm }} (${{ matrix.pi_model }})
run: |
cd build
export BCM=2835
export BCM=${{ matrix.bcm }}
make clean
make
ls -lh kernel7.img
ls -lh ${{ matrix.kernel_name }}

- name: Build BCM2836 (RPi 2)
- name: Test BCM${{ matrix.bcm }} in QEMU
run: |
cd build
export BCM=2836
make clean
make
ls -lh kernel7.img

- name: Build BCM2837 (RPi 3) - 32-bit
run: |
cd build
export BCM=2837
make clean
make || echo "BCM2837 requires aarch64 toolchain, skipping"

- name: Run unit tests
run: |
cd tests
python3 test_memory.py

- name: Run integration tests
run: |
cd tests
bash run_tests.sh
export BCM=${{ matrix.bcm }}
# Run QEMU for 3 seconds to verify it loads and executes
timeout 3 make run || true
echo "✓ BCM${{ matrix.bcm }} kernel runs in QEMU without crashing"

- name: Check binary size
run: |
cd build
export BCM=2836
make clean
make
SIZE=$(stat -c%s kernel7.img)
SIZE=$(stat -c%s ${{ matrix.kernel_name }})
echo "Binary size: $SIZE bytes"
if [ $SIZE -gt 100000 ]; then
echo "Warning: Binary size exceeds 100KB (size: $SIZE bytes)"
Expand All @@ -75,10 +82,36 @@ jobs:
- name: Archive build artifacts
uses: actions/upload-artifact@v4
with:
name: kernel-images
path: build/kernel*.img
name: kernel-bcm${{ matrix.bcm }}-${{ matrix.kernel_name }}
path: build/${{ matrix.kernel_name }}
retention-days: 30

run-integration-tests:
name: Run Integration Tests
runs-on: ubuntu-latest
permissions:
contents: read
needs: build-and-test

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install ARM toolchain
run: |
sudo apt-get update
sudo apt-get install -y gcc-arm-none-eabi binutils-arm-none-eabi gcc-aarch64-linux-gnu

- name: Run unit tests
run: |
cd tests
python3 test_memory.py

- name: Run integration tests
run: |
cd tests
bash run_tests.sh

static-analysis:
# Disable this job for now
if: false
Expand Down
53 changes: 39 additions & 14 deletions build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ BCM ?= 2835
USE_MINI_UART ?= 0

ARMGNU ?= arm-none-eabi
ARMGNU_VERSION ?= 13.2.1
ARMGNU_PATH ?= /usr

ARCH = aarch32

Expand All @@ -19,7 +17,11 @@ ifeq ($(BCM),2836)
else ifeq ($(BCM),2837)
# If the target is BCM2837, the used toolchain should be made
# for 64bits architecture
ARMGNU = aarch64-unknown-linux-gnu
# Try to find an aarch64 cross-compiler dynamically
ARMGNU := $(shell if command -v aarch64-linux-gnu-gcc > /dev/null 2>&1; then echo aarch64-linux-gnu; \
elif command -v aarch64-unknown-linux-gnu-gcc > /dev/null 2>&1; then echo aarch64-unknown-linux-gnu; \
elif command -v aarch64-none-elf-gcc > /dev/null 2>&1; then echo aarch64-none-elf; \
else echo "aarch64-linux-gnu"; fi)
ARCH = aarch64
CFLAGS += -march=armv8-a -mtune=cortex-a53
else
Expand All @@ -32,6 +34,10 @@ AS = $(ARMGNU)-as
LD = $(ARMGNU)-ld
OC = $(ARMGNU)-objcopy

# Dynamically find the GCC version and library path
ARMGNU_VERSION := $(shell $(CC) -dumpversion 2>/dev/null || echo "13.2.1")
ARMGNU_PATH := $(shell dirname $(shell dirname $(shell which $(CC) 2>/dev/null || echo "/usr/bin/$(CC)")) 2>/dev/null || echo "/usr")

# Bare metal compilation flags
CFLAGS += -O2 -Wall -Wextra -nostdlib -nostartfiles -ffreestanding

Expand All @@ -42,25 +48,37 @@ CFLAGS += -Wno-int-to-pointer-cast
CFLAGS += -DBCM$(BCM) -D__$(ARCH)__ -DUSE_MINI_UART=$(USE_MINI_UART)
LDFLAGS += --defsym=__$(ARCH)__=1 -nostdlib

# The bootloader on Raspberry Pi will use kernel7.img for
# 32bits arch and kernel8.img for 64bits
# The bootloader on Raspberry Pi uses different kernel names:
# kernel.img: 32-bit ARMv6 kernel for Pi 1 and Zero (BCM2835)
# kernel7.img: 32-bit ARMv7 kernel for Pi 2 and 3 (BCM2836, BCM2837 in 32-bit)
# kernel8.img: 64-bit kernel for any 64-bit capable Pi (BCM2837 in 64-bit)
KERNEL = kernel7

QEMU = qemu-system-arm
QEMU_MACHINE = raspi2

ifeq ($(ARCH),aarch64)
QEMU_MACHINE = raspi2b
QEMU_MEM = 1024

ifeq ($(BCM),2835)
KERNEL = kernel
QEMU_MACHINE = raspi0
QEMU_MEM = 512
else ifeq ($(BCM),2836)
KERNEL = kernel7
QEMU_MACHINE = raspi2b
QEMU_MEM = 1024
else ifeq ($(BCM),2837)
KERNEL = kernel8
QEMU = qemu-system-aarch64
QEMU_MACHINE = raspi3
QEMU_MACHINE = raspi3b
QEMU_MEM = 1024
endif

QEMU_FLAGS = -m 256 -M $(QEMU_MACHINE)
# QEMU flags: use -display none with explicit -serial stdio for better UART emulation
QEMU_FLAGS = -m $(QEMU_MEM) -M $(QEMU_MACHINE) -display none -serial stdio

ifeq ($(USE_MINI_UART),1)
QEMU_FLAGS += -serial null -serial stdio
else
QEMU_FLAGS += -serial stdio
# For mini UART (UART1), use two serial ports
QEMU_FLAGS = -m $(QEMU_MEM) -M $(QEMU_MACHINE) -display none -serial null -serial stdio
endif

##################
Expand All @@ -84,7 +102,14 @@ OBJ_FILES = $(patsubst $(SRC_DIR)/kernel/%.c, $(OBJ_DIR)/kernel/%_c.o, $(KERNEL
OBJ_FILES += $(patsubst $(SRC_DIR)/libc/%.c, $(OBJ_DIR)/libc/%_c.o, $(LIBC_C_FILES))
OBJ_FILES += $(patsubst $(SRC_DIR)/$(ARCH)/%.S, $(OBJ_DIR)/$(ARCH)/%_S.o, $(KERNEL_S_FILES))

LIBPATH = -lgcc -L$(ARMGNU_PATH)/lib/gcc/$(ARMGNU)/$(ARMGNU_VERSION)
# Dynamically find libgcc path
LIBGCC_PATH := $(shell $(CC) -print-libgcc-file-name 2>/dev/null)
ifneq ($(LIBGCC_PATH),)
LIBPATH = -lgcc -L$(dir $(LIBGCC_PATH))
else
# Fallback to default paths
LIBPATH = -lgcc -L$(ARMGNU_PATH)/lib/gcc/$(ARMGNU)/$(ARMGNU_VERSION)
endif

############
# COMMANDS #
Expand Down
71 changes: 16 additions & 55 deletions src/aarch32/boot.S
Original file line number Diff line number Diff line change
@@ -1,88 +1,49 @@
#include "../kernel/mm.h"

.section ".text.boot"

// _start is the entrypoint used by the linker script
.globl _start

_start:

#ifndef BCM2835 // BCM2835 has a mono-core CPU
// send 3 out of 4 cores to halt (mono-core)
// Read Proc feature Register 0
// send 3 out of 4 cores to halt
// Read Multiprocessor Affinity Register
mrc p15, #0, r1, c0, c0, #5
and r1, r1, #3
cmp r1, #0
bne halt
#endif

// Set stack pointer to beginning of code (grows downward)
// This is the standard approach - sp points to 0x8000, grows down
ldr sp, =_start

// setup Fast Interrupts Requests (FIQ)
cps #0x11
ldr sp, =MEM_FIQ_STACK

// setup Interrupts Requests (IRQ)
cps #0x12
ldr sp, =MEM_IRQ_STACK

// setup Interrupts Requests (IRQ)
cps #0x17
ldr sp, =MEM_ABORT_STACK

// setup Abort mode
cps #0x1B
ldr sp, =MEM_ABORT_STACK

// setup SYstem mode
cps #0x1F
ldr sp, =MEM_KERNEL_STACK

// set the C stack starting at address LOADADDR and downwards
// the other side is used by the kernel itself
ldr r5, =_start
mov sp, r5



// Enable Fast Interrupts
cpsie f

// TODO : invalidate data cache in L1

// TODO : vfpinit



// put start and end of C BSS memory section into registers
// __bss_start & __bss_end are symbols defined by the linker
// Zero out BSS section
ldr r4, =__bss_start
ldr r9, =__bss_end

// begin to zero out the BSS section
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
b 2f
b 2f

1:
// store multiple at r4 and compare with end of BSS
// if current address still below, continue looping
// Store multiple zeros at r4 and increment
stmia r4!, {r5-r8}

2:
cmp r4, r9
blo 1b
// BSS is zeroed out

// put ATAGS in register
// Set up parameters for kernel_main
// r0, r1, r2 contain boot parameters (ATAGS)
mov r0, #0
mov r1, #0
mov r2, #0x100
// load the C function kernel_main() into register
ldr r3, =kernel_main
// jump to the location of the function (call)
blx r3

// halt is an infinite loop used by others cores
// Call kernel_main
bl kernel_main

// Infinite loop for halt
halt:
#ifndef BCM2835
wfe
Expand Down
6 changes: 3 additions & 3 deletions tests/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ echo "Testing build for BCM2835 (Raspberry Pi Zero/1)..."
cd ../build
export BCM=2835
if make clean > /dev/null 2>&1 && make > /dev/null 2>&1; then
if [ -f kernel7.img ]; then
SIZE=$(stat -c%s kernel7.img 2>/dev/null || stat -f%z kernel7.img 2>/dev/null)
if [ -f kernel.img ]; then
SIZE=$(stat -c%s kernel.img 2>/dev/null || stat -f%z kernel.img 2>/dev/null)
print_result 0 "BCM2835 build successful (size: $SIZE bytes)"
else
print_result 1 "BCM2835 build failed - kernel7.img not found"
print_result 1 "BCM2835 build failed - kernel.img not found"
fi
else
print_result 1 "BCM2835 build failed"
Expand Down