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
21 changes: 6 additions & 15 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,36 +89,27 @@ jobs:
strategy:
matrix:
arch: [ riscv32, riscv64 ]
continue-on-error: true

steps:
- uses: actions/checkout@v6

- name: Install RISC-V toolchain (optional)
- name: Install RISC-V toolchain
run: |
sudo apt-get update || true
sudo apt-get update
sudo apt-get install -y \
gcc-riscv64-linux-gnu \
gcc-riscv32-linux-gnu \
binutils-riscv64-linux-gnu \
binutils-riscv32-linux-gnu || true
binutils-riscv64-linux-gnu

- name: Build ${{ matrix.arch }} (best effort)
- name: Build ${{ matrix.arch }}
run: |
if [ "${{ matrix.arch }}" == "riscv32" ]; then
make ARCH=riscv32 CROSS_COMPILE=riscv32-linux-gnu- 2>&1 || echo "riscv32 build skipped"
elif [ "${{ matrix.arch }}" == "riscv64" ]; then
make ARCH=riscv64 CROSS_COMPILE=riscv64-linux-gnu- 2>&1 || echo "riscv64 build skipped"
fi
make ARCH=${{ matrix.arch }} CROSS_COMPILE=riscv64-linux-gnu-

- name: Upload binary (if exists)
- name: Upload binary
uses: actions/upload-artifact@v6
with:
name: tach-${{ matrix.arch }}-kernel
path: src/build/${{ matrix.arch }}/tach.bin
retention-days: 30
if-no-files-found: ignore
if: always()

release:
needs: [build-x86, build-arm]
Expand Down
4 changes: 2 additions & 2 deletions Config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ ifeq ($(ARCH),aarch64)
LDFLAGS := -m aarch64elf -T $(CURDIR)/link/aarch64.ld
endif
ifeq ($(ARCH),riscv32)
CFLAGS += -march=rv32ima_zicsr -mabi=ilp32
CFLAGS += -march=rv32ima_zicsr -mabi=ilp32 -mcmodel=medany
LDFLAGS := -m elf32lriscv -T $(CURDIR)/link/riscv32.ld
endif
ifeq ($(ARCH),riscv64)
CFLAGS += -march=rv64ima_zicsr -mabi=lp64
CFLAGS += -march=rv64ima_zicsr -mabi=lp64 -mcmodel=medany
LDFLAGS := -m elf64lriscv -T $(CURDIR)/link/riscv64.ld
endif

Expand Down
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@ test: test-host

test-host:
@mkdir -p src/build/host
$(HOST_CC) -std=c11 -Wall -Wextra -Werror -fno-builtin -Iinclude \
$(HOST_CC) -std=gnu11 -Wall -Wextra -Werror -fno-builtin -DTACH_HOST_TEST -Iinclude \
tests/host/userspace_test.c \
src/lib/string.c src/lib/printf.c src/kernel/klog.c \
src/fs/vfs.c src/proc/fd.c src/proc/process.c \
src/proc/scheduler.c src/proc/syscall.c \
src/sync/atomic.c src/sync/spinlock.c src/hal/cpu.c src/ipc/sem.c \
src/term/tty.c src/term/vterm.c src/term/shell.c \
src/userland/runtime.c \
-o src/build/host/userspace-test
@src/build/host/userspace-test
@echo "Host userspace integration test passed."
$(HOST_CC) -std=gnu11 -Wall -Wextra -Werror -fno-builtin -DTACH_HOST_TEST -Iinclude \
tests/host/mm_elf_test.c src/lib/string.c src/mm/pmm.c \
src/proc/elf_loader.c src/sync/atomic.c src/sync/spinlock.c \
src/hal/cpu.c -o src/build/host/mm-elf-test
@src/build/host/mm-elf-test
@echo "Host userspace, PMM, ELF, scheduler, and semaphore tests passed."

clean:
rm -rf src/build/
Expand Down
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ tach/
- Timer wheel for delayed work

### Memory Management
- Physical Memory Manager (PMM)
- Virtual Memory Manager (VMM)
- Bitmap physical frame allocator populated from Multiboot memory maps
- Per-process VMM contexts backed by x86, ARM, AArch64, and RISC-V page tables
- Page allocation, fixed mappings, unmapping, address-space switching, and cleanup
- Kernel heap (kmalloc/kfree)
- Slab allocator
- W^X enforcement
Expand All @@ -193,18 +194,20 @@ tach/

### Userspace Foundation
- Static process table with PID/PPID, lifecycle state, credentials, and accounting
- Cooperative round-robin run queue ready for timer-driven preemption
- Cooperative round-robin run queue with real architecture context switches
- Per-process file descriptor tables with inherited console standard streams
- Stable syscall dispatcher with `exit`, `read`, `write`, `open`, `close`, and `getpid`
- Embedded PID 1 runtime that launches an interactive `sh`
- `/dev/console` and `/dev/tty` through the VFS character-device interface
- Interactive EN-US shell with quoting, command status, history, and line editing
- Serial and PS/2 keyboard input with mirrored serial/VGA output
- ELF32/ELF64 executable validation and `PT_LOAD` segment mapping
- FIFO semaphores that block and wake processes through the scheduler

The current userspace runtime is embedded in the kernel image. It exercises the
process, file descriptor, VFS, TTY, and syscall boundaries, but does not claim
ring-3 isolation yet. ELF execution and isolated address spaces depend on the
still-in-progress PMM/VMM implementations.
The embedded PID 1 and shell remain the default boot payload. Processes now have
separate page-table roots and the ELF loader can populate them, but user-mode
privilege transitions and a filesystem-backed `/sbin/init` are still future
work; ELF entry points currently execute at kernel privilege.

### Hardware Support
- VGA text mode console
Expand Down
13 changes: 13 additions & 0 deletions include/boot/multiboot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* boot/multiboot.h - Normalized Multiboot memory-map handoff
* Copyright 2026 Tervia Interactive™
* Licensed under Apache License 2.0
*/
#ifndef _BOOT_MULTIBOOT_H
#define _BOOT_MULTIBOOT_H

#include <kernel/types.h>

int multiboot_parse(uint32_t magic, uintptr_t info);

#endif /* _BOOT_MULTIBOOT_H */
17 changes: 17 additions & 0 deletions include/boot/multiboot1.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

#define MULTIBOOT1_MAGIC 0x1BADB002
#define MULTIBOOT1_BOOTLOADER_MAGIC 0x2BADB002
#define MULTIBOOT1_INFO_MEMORY (1u << 0)
#define MULTIBOOT1_INFO_MODULES (1u << 3)
#define MULTIBOOT1_INFO_MMAP (1u << 6)

struct multiboot1_info {
uint32_t flags;
Expand All @@ -29,4 +32,18 @@ struct multiboot1_info {
uint32_t apm_table;
} __attribute__((packed));

struct multiboot1_mmap_entry {
uint32_t size;
uint64_t addr;
uint64_t len;
uint32_t type;
} __attribute__((packed));

struct multiboot1_module {
uint32_t mod_start;
uint32_t mod_end;
uint32_t string;
uint32_t reserved;
} __attribute__((packed));

#endif /* _BOOT_MULTIBOOT1_H */
9 changes: 9 additions & 0 deletions include/boot/multiboot2.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
#define MULTIBOOT2_TAG_TYPE_END 0
#define MULTIBOOT2_TAG_TYPE_MODULE 3
#define MULTIBOOT2_TAG_TYPE_MMAP 6
#define MULTIBOOT2_TAG_TYPE_FRAMEBUFFER 5

Expand Down Expand Up @@ -40,4 +41,12 @@ struct multiboot2_tag_mmap {
struct multiboot2_mmap_entry entries[];
} __attribute__((packed));

struct multiboot2_tag_module {
uint32_t type;
uint32_t size;
uint32_t mod_start;
uint32_t mod_end;
char string[];
} __attribute__((packed));

#endif /* _BOOT_MULTIBOOT2_H */
3 changes: 3 additions & 0 deletions include/hal/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ void hal_mmu_init(void);
int hal_mmu_map(uintptr_t virt, uintptr_t phys, size_t size, uint32_t flags);
int hal_mmu_unmap(uintptr_t virt, size_t size);
int hal_get_memmap(mem_region_t* regions, int max_regions);
void hal_memmap_reset(void);
int hal_memmap_add(uint64_t base, uint64_t size, uint32_t type);
void hal_memmap_use_platform_fallback(void);

#endif /* _HAL_MM_H */
20 changes: 20 additions & 0 deletions include/hal/paging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* hal/paging.h - Architecture page-table interface
* Copyright 2026 Tervia Interactive™
* Licensed under Apache License 2.0
*/
#ifndef _HAL_PAGING_H
#define _HAL_PAGING_H

#include <kernel/types.h>

int arch_paging_init(void);
void* arch_paging_kernel_root(void);
void* arch_paging_create_root(void);
void arch_paging_destroy_root(void* root);
void arch_paging_switch(void* root);
int arch_paging_map(void* root, uintptr_t virt, phys_addr_t phys,
uint32_t flags);
int arch_paging_unmap(void* root, uintptr_t virt);

#endif /* _HAL_PAGING_H */
7 changes: 7 additions & 0 deletions include/ipc/sem.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
#define _IPC_SEM_H

#include <kernel/types.h>
#include <kernel/spinlock.h>
#include <proc/process.h>
#include <stdint.h>

/* Semaphore structure */
typedef struct {
int32_t value;
int32_t waiters;
struct process* wait_queue[MAX_PROCESSES];
size_t wait_head;
size_t wait_tail;
size_t wait_count;
spinlock_t lock;
} sem_t;

/* Initialize a semaphore with initial value */
Expand Down
2 changes: 2 additions & 0 deletions include/mm/mprotect.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include <kernel/types.h>

struct vmm_context;

#define MPROT_NONE 0x0
#define MPROT_READ 0x1
#define MPROT_WRITE 0x2
Expand Down
7 changes: 6 additions & 1 deletion include/mm/pmm.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@
#define _MM_PMM_H

#include <kernel/types.h>
#include <hal/mm.h>

#define PAGE_SIZE 4096
#define PAGE_SHIFT 12

void pmm_init(void* memory_map, size_t map_size);
void pmm_init(const mem_region_t* memory_map, size_t region_count);
phys_addr_t pmm_alloc_frame(void);
void pmm_free_frame(phys_addr_t frame);
void* pmm_alloc_page(void);
void pmm_free_page(void* page);
void* pmm_alloc_pages(size_t count);
void* pmm_alloc_aligned_pages(size_t count, size_t alignment_pages);
void pmm_free_pages(void* pages, size_t count);
size_t pmm_get_free_pages(void);
size_t pmm_get_total_pages(void);
void pmm_mark_region_used(phys_addr_t start, size_t size);
void pmm_mark_region_free(phys_addr_t start, size_t size);

Expand Down
5 changes: 5 additions & 0 deletions include/mm/vmm.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@ struct process;
struct vmm_context {
void* page_table;
pid_t owner;
uintptr_t next_free;
};

void vmm_init(void);
struct vmm_context* vmm_create_context(void);
void vmm_destroy_context(struct vmm_context* ctx);
void vmm_switch_context(struct vmm_context* ctx);
int vmm_map(struct vmm_context* ctx, void* virt, phys_addr_t phys, uint32_t flags);
int vmm_map_allocated(struct vmm_context* ctx, void* virt, uint32_t flags,
phys_addr_t* phys_out);
int vmm_unmap(struct vmm_context* ctx, void* virt);
void* vmm_alloc_page(struct vmm_context* ctx, uint32_t flags);
void vmm_free_page(struct vmm_context* ctx, void* virt);
int vmm_handle_fault(uintptr_t addr, int is_write);
phys_addr_t vmm_resolve(struct vmm_context* ctx, const void* virt);
struct vmm_context* vmm_kernel_context(void);

#endif /* _MM_VMM_H */
72 changes: 56 additions & 16 deletions include/proc/elf.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* proc/elf.h - ELF parser (loads userland binaries)
* proc/elf.h - Minimal ELF32/ELF64 executable loader
* Copyright 2026 Tervia Interactive™
* Licensed under Apache License 2.0
*/
Expand All @@ -9,41 +9,81 @@
#include <kernel/types.h>
#include <proc/process.h>

#define ELF_MAGIC "\x7fELF"
#define ELF_MAGIC "\x7f" "ELF"
#define ELF_CLASS_32 1
#define ELF_CLASS_64 2
#define ELF_DATA_LSB 1
#define ELF_VERSION_CURRENT 1
#define ELF_TYPE_EXEC 2
#define ELF_TYPE_DYN 3
#define ELF_MACHINE_X86 3
#define ELF_MACHINE_X86_64 62
#define ELF_MACHINE_ARM 40
#define ELF_MACHINE_X86_64 62
#define ELF_MACHINE_AARCH64 183
#define ELF_MACHINE_RISCV 243
#define ELF_PT_LOAD 1
#define ELF_PF_X 1
#define ELF_PF_W 2
#define ELF_PF_R 4

struct elf32_header {
uint8_t ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint32_t entry;
uint32_t phoff;
uint32_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
} __attribute__((packed));

struct elf_header {
uint8_t magic[4];
uint8_t class;
uint8_t endian;
uint8_t version;
uint8_t osabi;
uint8_t padding[8];
struct elf64_header {
uint8_t ident[16];
uint16_t type;
uint16_t machine;
uint32_t elf_version;
uint32_t version;
uint64_t entry;
uint64_t phoff;
uint64_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
} __attribute__((packed));

struct elf_program_header {
struct elf32_program_header {
uint32_t type;
uint32_t offset;
uint32_t vaddr;
uint32_t paddr;
uint32_t filesz;
uint32_t memsz;
uint32_t flags;
uint32_t align;
} __attribute__((packed));

struct elf64_program_header {
uint32_t type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t filesz;
uint64_t memsz;
uint32_t flags;
uint32_t align;
uint64_t align;
} __attribute__((packed));

int elf_validate(const void* data);
int elf_load(const void* data, struct process* proc);
int elf_validate(const void* data, size_t size);
int elf_load(const void* data, size_t size, struct process* proc);
void* elf_get_entry(const void* data);

#endif /* _PROC_ELF_H */
Loading