Initial commit

This commit is contained in:
2026-02-04 01:58:49 +01:00
commit 0e01345abf
117 changed files with 5076 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
#include <kernel/percpu.h>
#include <kernel/syscalls.h>
#include <asm.h>
#include <defs.h>
#include <gdt.h>
#include <stddef.h>
#include <string.h>
struct gdt main_gdt = {
.null = 0,
.kernel_code32 = 0x00cf9b000000ffff,
.kernel_data32 = 0x00cf93000000ffff,
.kernel_code64 = 0x00a09b0000000000,
.kernel_data64 = 0x0000930000000000,
.user_code = 0x00cffb000000ffff,
.user_data = 0x0000f30000000000,
.user_code64 = 0x0000fb0000000000,
.tss = {0x0000890000000000, 0},
};
void gdt_init() {
// Set initial values.
memcpy(&percpu_get()->arch.gdt, &main_gdt, sizeof(struct gdt));
struct gdtr gdtr = {
.limit = sizeof(struct gdt) - 1,
.base = &percpu_get()->arch.gdt,
};
asm volatile("lgdt [%0]" ::"r"(&gdtr));
// Save the contents of MSR_GS_BASE, as they get cleared by a write to `gs`.
virt_t gs = asm_rdmsr(MSR_GS_BASE);
asm volatile(
"push %0\n"
"lea rax, [rip + 1f]\n"
"push rax\n"
"retfq\n"
"1:\n"
"mov ax, %1\n"
"mov ds, ax\n"
"mov es, ax\n"
"mov fs, ax\n"
"mov gs, ax\n"
"mov ss, ax\n"
:
: "i"(offsetof(struct gdt, kernel_code64)), "i"(offsetof(struct gdt, kernel_data64))
: "rax", "memory"
);
asm_wrmsr(MSR_GS_BASE, gs);
}
+7
View File
@@ -0,0 +1,7 @@
#include <kernel/compiler.h>
#include <stdint.h>
struct [[__packed]] idtr {
uint16_t limit;
struct idt* base;
};
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <stdint.h>
static inline uint64_t asm_rdmsr(uint32_t msr) {
uint32_t eax;
uint32_t edx;
asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(msr) : "memory");
return ((uint64_t)edx << 32) | eax;
}
// Writes a 64-bit value to a given MSR.
static inline void asm_wrmsr(uint32_t msr, uint64_t val) {
uint32_t eax = (uint32_t)val;
uint32_t edx = val >> 32;
asm volatile("wrmsr" : : "a"(eax), "d"(edx), "c"(msr) : "memory");
}
// Writes a 64-bit value to a control register using XSETBV.
static inline void asm_wrxcr(uint32_t reg, uint64_t val) {
uint32_t eax = val;
uint32_t edx = val >> 32;
asm volatile("xsetbv" ::"a"(eax), "d"(edx), "c"(reg) : "memory");
}
// Saves the FPU state to a 512-byte region of memory using FXSAVE.
// Pointer must be 16-byte aligned.
static inline void asm_fxsave(void* mem) {
asm volatile("fxsave %0" : "+m"(*(uint8_t*)mem)::"memory");
}
// Restores the FPU state from a 512-byte region of memory using FXRSTOR.
// Pointer must be 16-byte aligned.
static inline void asm_fxrstor(void* mem) {
asm volatile("fxrstor %0" ::"m"(*(uint8_t*)mem) : "memory");
}
// Saves the FPU state to a region of memory using XSAVE.
// Pointer must be 16-byte aligned.
static inline void asm_xsave(void* mem) {
asm volatile("xsave %0" : "+m"(*(uint8_t*)mem)::"memory");
}
// Restores the FPU state from a region of memory using XRSTOR.
// Pointer must be 16-byte aligned.
static inline void asm_xrstor(void* mem) {
asm volatile("xrstor %0" ::"m"(*(uint8_t*)mem) : "memory");
}
static inline uint8_t asm_inb(uint16_t port) {
uint8_t result;
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
return result;
}
static inline uint16_t asm_inw(uint16_t port) {
uint16_t result;
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
return result;
}
static inline uint32_t asm_inl(uint16_t port) {
uint32_t result;
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
return result;
}
static inline void asm_outb(uint16_t port, uint8_t value) {
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
}
static inline void asm_outw(uint16_t port, uint16_t value) {
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
}
static inline void asm_outl(uint16_t port, uint32_t value) {
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#define ELF_ARCH_CLASS ELFCLASS64
#define ELF_ARCH_DATA ELFDATA2LSB
#define ELF_ARCH_MACHINE EM_X86_64
#define elf_hdr elf64_hdr
#define elf_phdr elf64_phdr
#define elf_dyn elf64_dyn
#define elf_addr elf64_addr
#define elf_off elf64_off
#define elf_nhdr elf64_nhdr
#define elf_auxv elf64_auxv
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <defs.h>
#include <stdint.h>
static inline bool arch_irq_get_state() {
uint64_t flags;
asm volatile("pushf; pop %0" : "=r"(flags)::"memory");
return flags & RFLAGS_IF;
}
static inline void arch_irq_set_state(bool state) {
if (state)
asm volatile("sti");
else
asm volatile("cli");
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <kernel/types.h>
#include <stddef.h>
#include <stdint.h>
static inline size_t arch_mem_page_bits() {
return 12;
}
static inline size_t arch_mem_level_bits() {
return 9;
}
static inline size_t arch_mem_num_levels() {
return 4;
}
static inline virt_t arch_mem_hhdm_addr() {
return 0xFFFF'8000'0000'0000;
}
static inline virt_t arch_mem_pfndb_addr() {
return 0xFFFF'A000'0000'0000;
}
static inline virt_t arch_mem_mapping_addr() {
return 0xFFFF'C000'0000'0000;
}
typedef uint64_t pte_t;
enum pte_flags;
enum cache_mode;
struct page_table;
void arch_mem_pte_clear(pte_t* pte);
pte_t arch_mem_pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache);
bool arch_mem_pte_is_present(pte_t* pte);
bool arch_mem_pte_is_dir(pte_t* pte);
phys_t arch_mem_pte_address(pte_t* pte);
void arch_mem_pt_set(struct page_table* pt);
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <gdt.h>
#include <stdint.h>
static inline struct percpu* arch_percpu_get() {
struct percpu* result;
asm volatile("mov %0, gs:0" : "=r"(result)::"memory");
return result;
}
struct arch_percpu {
uint32_t lapic_id;
struct gdt gdt;
struct tss tss;
};
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <stdint.h>
struct arch_task_context {
uint64_t rsp;
uint8_t* fpu_region;
uint16_t ds, es, fs, gs;
uint64_t fs_base, gs_base;
};
struct arch_context {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rsi;
uint64_t rdi;
uint64_t rbp;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
uint64_t isr; // Pushed onto the stack by the interrupt handler stubs.
uint64_t error; // Pushed onto the stack by the CPU if the interrupt has an error code.
uint64_t rip; // The rest is pushed onto the stack by the CPU during an interrupt.
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
};
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
typedef uint64_t phys_t;
typedef uint64_t virt_t;
typedef uint64_t reg_t;
@@ -0,0 +1,9 @@
#pragma once
#include <kernel/compiler.h>
#include <stddef.h>
#include <stdint.h>
bool arch_usercopy_read(uint8_t* dst, const __user uint8_t* src, size_t len);
bool arch_usercopy_write(__user uint8_t* dst, const uint8_t* src, size_t len);
bool arch_usercopy_strlen(const __user uint8_t* str, size_t max, size_t* len);
+191
View File
@@ -0,0 +1,191 @@
#pragma once
#define CPUID_1C_SSE3 (1 << 0)
#define CPUID_1C_PCLMUL (1 << 1)
#define CPUID_1C_DTES64 (1 << 2)
#define CPUID_1C_MONITOR (1 << 3)
#define CPUID_1C_DS_CPL (1 << 4)
#define CPUID_1C_VMX (1 << 5)
#define CPUID_1C_SMX (1 << 6)
#define CPUID_1C_EST (1 << 7)
#define CPUID_1C_TM2 (1 << 8)
#define CPUID_1C_SSSE3 (1 << 9)
#define CPUID_1C_CID (1 << 10)
#define CPUID_1C_SDBG (1 << 11)
#define CPUID_1C_FMA (1 << 12)
#define CPUID_1C_CX16 (1 << 13)
#define CPUID_1C_XTPR (1 << 14)
#define CPUID_1C_PDCM (1 << 15)
#define CPUID_1C_PCID (1 << 17)
#define CPUID_1C_DCA (1 << 18)
#define CPUID_1C_SSE4_1 (1 << 19)
#define CPUID_1C_SSE4_2 (1 << 20)
#define CPUID_1C_X2APIC (1 << 21)
#define CPUID_1C_MOVBE (1 << 22)
#define CPUID_1C_POPCNT (1 << 23)
#define CPUID_1C_TSC (1 << 24)
#define CPUID_1C_AES (1 << 25)
#define CPUID_1C_XSAVE (1 << 26)
#define CPUID_1C_OSXSAVE (1 << 27)
#define CPUID_1C_AVX (1 << 28)
#define CPUID_1C_F16C (1 << 29)
#define CPUID_1C_RDRAND (1 << 30)
#define CPUID_1C_HYPERVISOR (1 << 31)
#define CPUID_1D_FPU (1 << 0)
#define CPUID_1D_VME (1 << 1)
#define CPUID_1D_DE (1 << 2)
#define CPUID_1D_PSE (1 << 3)
#define CPUID_1D_TSC (1 << 4)
#define CPUID_1D_MSR (1 << 5)
#define CPUID_1D_PAE (1 << 6)
#define CPUID_1D_MCE (1 << 7)
#define CPUID_1D_CX8 (1 << 8)
#define CPUID_1D_APIC (1 << 9)
#define CPUID_1D_SEP (1 << 11)
#define CPUID_1D_MTRR (1 << 12)
#define CPUID_1D_PGE (1 << 13)
#define CPUID_1D_MCA (1 << 14)
#define CPUID_1D_CMOV (1 << 15)
#define CPUID_1D_PAT (1 << 16)
#define CPUID_1D_PSE36 (1 << 17)
#define CPUID_1D_PSN (1 << 18)
#define CPUID_1D_CLFLUSH (1 << 19)
#define CPUID_1D_DS (1 << 21)
#define CPUID_1D_ACPI (1 << 22)
#define CPUID_1D_MMX (1 << 23)
#define CPUID_1D_FXSR (1 << 24)
#define CPUID_1D_SSE (1 << 25)
#define CPUID_1D_SSE2 (1 << 26)
#define CPUID_1D_SS (1 << 27)
#define CPUID_1D_HTT (1 << 28)
#define CPUID_1D_TM (1 << 29)
#define CPUID_1D_IA64 (1 << 30)
#define CPUID_1D_PBE (1 << 31)
#define CPUID_7B_FSGSBASE (1 << 0)
#define CPUID_7B_SGX (1 << 2)
#define CPUID_7B_BMI (1 << 3)
#define CPUID_7B_HLE (1 << 4)
#define CPUID_7B_AVX2 (1 << 5)
#define CPUID_7B_SMEP (1 << 7)
#define CPUID_7B_BMI2 (1 << 8)
#define CPUID_7B_RTM (1 << 11)
#define CPUID_7B_AVX512F (1 << 16)
#define CPUID_7B_AVX512DQ (1 << 17)
#define CPUID_7B_RDSEED (1 << 18)
#define CPUID_7B_ADX (1 << 19)
#define CPUID_7B_SMAP (1 << 20)
#define CPUID_7B_AVX512IFMA (1 << 21)
#define CPUID_7B_CLFLUSHOPT (1 << 23)
#define CPUID_7B_CLWB (1 << 24)
#define CPUID_7B_AVX512CD (1 << 28)
#define CPUID_7B_SHA (1 << 29)
#define CPUID_7B_AVX512BW (1 << 30)
#define CPUID_7B_AVX512VL (1 << 31)
#define CPUID_7C_AVX512VBMI (1 << 1)
#define CPUID_7C_UMIP (1 << 2)
#define CPUID_7C_PKU (1 << 3)
#define CPUID_7C_OSPKE (1 << 4)
#define CPUID_7C_WAITPKG (1 << 5)
#define CPUID_7C_AVX512VBMI2 (1 << 6)
#define CPUID_7C_SHSTK (1 << 7)
#define CPUID_7C_GFNI (1 << 8)
#define CPUID_7C_VAES (1 << 9)
#define CPUID_7C_VPCLMULQDQ (1 << 10)
#define CPUID_7C_AVX512VNNI (1 << 11)
#define CPUID_7C_AVX512BITALG (1 << 12)
#define CPUID_7C_TME_EN (1 << 13)
#define CPUID_7C_AVX512VPOPCNTDQ (1 << 14)
#define CPUID_7C_RDPID (1 << 22)
#define CPUID_7C_KL (1 << 23)
#define CPUID_7C_CLDEMOTE (1 << 25)
#define CPUID_7C_MOVDIRI (1 << 27)
#define CPUID_7C_MOVDIR64B (1 << 28)
#define CPUID_7C_ENQCMD (1 << 29)
#define CPUID_7D_UINTR (1 << 5)
#define CPUID_7D_AVX512VP2INTERSECT (1 << 8)
#define CPUID_7D_SERIALIZE (1 << 14)
#define CPUID_7D_TSXLDTRK (1 << 16)
#define CPUID_7D_PCONFIG (1 << 18)
#define CPUID_7D_IBT (1 << 20)
#define CPUID_7D_AMX_BF16 (1 << 22)
#define CPUID_7D_AVX512FP16 (1 << 23)
#define CPUID_7D_AMX_TILE (1 << 24)
#define CPUID_7D_AMX_INT8 (1 << 25)
#define RFLAGS_CF (1u << 0)
#define RFLAGS_PF (1u << 2)
#define RFLAGS_AF (1u << 4)
#define RFLAGS_ZF (1u << 6)
#define RFLAGS_SF (1u << 7)
#define RFLAGS_TF (1u << 8)
#define RFLAGS_IF (1u << 9)
#define RFLAGS_DF (1u << 10)
#define RFLAGS_OF (1u << 11)
#define RFLAGS_IOPL (3u << 12)
#define RFLAGS_NT (1u << 14)
#define RFLAGS_RF (1u << 16)
#define RFLAGS_VM (1u << 17)
#define RFLAGS_AC (1u << 18)
#define RFLAGS_VIF (1u << 19)
#define RFLAGS_VIP (1u << 20)
#define RFLAGS_ID (1u << 21)
#define CR0_PE (1 << 0)
#define CR0_MP (1 << 1)
#define CR0_EM (1 << 2)
#define CR0_TS (1 << 3)
#define CR0_ET (1 << 4)
#define CR0_NE (1 << 5)
#define CR0_WP (1 << 16)
#define CR0_AM (1 << 18)
#define CR0_NW (1 << 29)
#define CR0_CD (1 << 30)
#define CR0_PG (1 << 31)
#define CR4_VME (1 << 0)
#define CR4_PVI (1 << 1)
#define CR4_TSD (1 << 2)
#define CR4_DE (1 << 3)
#define CR4_PSE (1 << 4)
#define CR4_PAE (1 << 5)
#define CR4_MCE (1 << 6)
#define CR4_PGE (1 << 7)
#define CR4_PCE (1 << 8)
#define CR4_OSFXSR (1 << 9)
#define CR4_OSXMMEXCPT (1 << 10)
#define CR4_UMIP (1 << 11)
#define CR4_VMXE (1 << 13)
#define CR4_SMXE (1 << 14)
#define CR4_FSGSBASE (1 << 16)
#define CR4_PCIDE (1 << 17)
#define CR4_OSXSAVE (1 << 18)
#define CR4_SMEP (1 << 20)
#define CR4_SMAP (1 << 21)
#define CR4_PKE (1 << 22)
#define CR4_CET (1 << 23)
#define CR4_PKS (1 << 24)
#define MSR_EFER_SCE (1 << 0)
#define MSR_EFER_LME (1 << 8)
#define MSR_EFER_LMA (1 << 10)
#define MSR_EFER_NXE (1 << 11)
#define MSR_EFER_SVME (1 << 12)
#define MSR_EFER_LMSLE (1 << 13)
#define MSR_EFER_FFXSR (1 << 14)
#define MSR_EFER_TCE (1 << 15)
#define MSR_EFER (0xC0000080)
#define MSR_STAR (0xC0000081)
#define MSR_LSTAR (0xC0000082)
#define MSR_CSTAR (0xC0000083)
#define MSR_SFMASK (0xC0000084)
#define MSR_FS_BASE (0xC0000100)
#define MSR_GS_BASE (0xC0000101)
#define MSR_KERNEL_GS_BASE (0xC0000102)
#define CPL_USER (0b11)
#define CPL_KERNEL (0b00)
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <kernel/compiler.h>
#include <stdint.h>
#include "tss.h"
struct [[__packed]] gdt {
uint64_t null;
uint64_t kernel_code32;
uint64_t kernel_data32;
uint64_t kernel_code64;
uint64_t kernel_data64;
uint64_t user_code;
uint64_t user_data;
uint64_t user_code64;
uint64_t tss[2];
};
struct [[__packed]] gdtr {
uint16_t limit;
struct gdt* base;
};
// Initializes a GDT on the local core.
void gdt_init();
// Sets the Task State Segment in the given GDT.
void gdt_set_tss(struct gdt* gdt, struct tss* tss);
+1
View File
@@ -0,0 +1 @@
#pragma once
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#define SMP_GDTR_OFFSET 80
#define SMP_FARJMP_OFFSET 90
#define SMP_TEMP_STACK_OFFSET 98
#define SMP_TEMP_CR3_OFFSET 102
#define SMP_ENTRY_OFFSET 106
#define SMP_HHDM_OFFSET 114
#define SMP_KERNEL32_DS 16
#define SMP_KERNEL64_CS 24
#define SMP_KERNEL64_DS 32
#define SMP_INFO_SIZE 127
#ifndef __ASSEMBLER__
#include <gdt.h>
#include <kernel/common.h>
#include <kernel/compiler.h>
#include <stddef.h>
#include <stdint.h>
struct [[__packed]] smp_info {
struct gdt gdt;
struct gdtr gdtr;
uint32_t farjmp_offset;
uint32_t farjmp_segment;
uint32_t temp_stack;
uint32_t temp_cr3;
uint64_t entry;
uint64_t hhdm_offset;
uint32_t lapic_id;
uint8_t booted;
};
static_assert(SMP_GDTR_OFFSET == offsetof(struct smp_info, gdtr));
static_assert(SMP_FARJMP_OFFSET == offsetof(struct smp_info, farjmp_offset));
static_assert(SMP_TEMP_STACK_OFFSET == offsetof(struct smp_info, temp_stack));
static_assert(SMP_TEMP_CR3_OFFSET == offsetof(struct smp_info, temp_cr3));
static_assert(SMP_ENTRY_OFFSET == offsetof(struct smp_info, entry));
static_assert(SMP_HHDM_OFFSET == offsetof(struct smp_info, hhdm_offset));
static_assert(SMP_KERNEL32_DS == offsetof(struct gdt, kernel_data32));
static_assert(SMP_KERNEL64_CS == offsetof(struct gdt, kernel_code64));
static_assert(SMP_KERNEL64_DS == offsetof(struct gdt, kernel_data64));
static_assert(SMP_INFO_SIZE == sizeof(struct smp_info));
// Initializes a LAPIC.
void x86_64_smp_init(uint32_t id);
#endif
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <kernel/compiler.h>
#include <stdint.h>
struct [[__packed, __aligned(0x10)]] tss {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint32_t reserved1;
uint32_t reserved2;
uint64_t ist1;
uint64_t ist2;
uint64_t ist3;
uint64_t ist4;
uint64_t ist5;
uint64_t ist6;
uint64_t ist7;
uint32_t reserved3;
uint32_t reserved4;
uint16_t reserved5;
uint16_t iopb;
};
+56
View File
@@ -0,0 +1,56 @@
#include <kernel/mem.h>
#include <kernel/types.h>
enum : pte_t {
ARCH_FLAG_PRESENT = 1 << 0,
ARCH_FLAG_READ_WRITE = 1 << 1,
ARCH_FLAG_USER_MODE = 1 << 2,
ARCH_FLAG_WRITE_THROUGH = 1 << 3,
ARCH_FLAG_CACHE_DISABLE = 1 << 4,
ARCH_FLAG_ACCESSED = 1 << 5,
ARCH_FLAG_DIRTY = 1 << 6,
ARCH_FLAG_SIZE = 1 << 7,
ARCH_FLAG_GLOBAL = 1 << 8,
ARCH_FLAG_AVAILABLE = 1 << 9,
ARCH_FLAG_ATTRIBUTE_TABLE = 1 << 10,
ARCH_FLAG_EXECUTE_DISABLE = 1LU << 63,
ARCH_PTE_ADDR_MASK = 0x000F'FFFF'FFFF'F000,
};
void arch_mem_pte_clear(pte_t* pte) {
*pte = 0;
}
pte_t arch_mem_pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache) {
pte_t result = ((pte_t)addr & ARCH_PTE_ADDR_MASK) | ARCH_FLAG_PRESENT;
if (flags & PTE_USER)
result |= ARCH_FLAG_USER_MODE;
if (flags & PTE_DIR)
result |= ARCH_FLAG_READ_WRITE;
else {
if (flags & PTE_WRITE)
result |= ARCH_FLAG_READ_WRITE;
if (!(flags & PTE_EXEC))
result |= ARCH_FLAG_EXECUTE_DISABLE;
}
return result;
}
bool arch_mem_pte_is_present(pte_t* pte) {
return *pte & ARCH_FLAG_PRESENT;
}
bool arch_mem_pte_is_dir(pte_t* pte) {
return *pte & ARCH_FLAG_SIZE;
}
phys_t arch_mem_pte_address(pte_t* pte) {
return (phys_t)(*pte & ARCH_PTE_ADDR_MASK);
}
void arch_mem_pt_set(struct page_table* pt) {
asm volatile("mov cr3, %0" ::"r"(pt->root) : "memory");
}
+17
View File
@@ -0,0 +1,17 @@
kernel_c_args += [
'-masm=intel',
'-mgeneral-regs-only',
'-mno-red-zone',
'-mcmodel=kernel',
]
kernel_sources += files(
'gdt.c',
'idt.c',
'mem.c',
'serial.c',
'smp.c',
'sys.c',
'trampoline.S',
'usercopy.c',
)
+47
View File
@@ -0,0 +1,47 @@
#include <asm.h>
#include <kernel/console.h>
#include <stdint.h>
constexpr uint16_t COM1_BASE = 0x3F8;
static bool is_ready() {
return asm_inb(COM1_BASE + 5) & 0x20;
}
static void serial_write(struct console* con, const char* buf, size_t count) {
for (size_t i = 0; i < count; i++) {
while (!is_ready())
asm volatile("pause");
asm_outb(COM1_BASE, buf[i]);
}
}
bool serial_setup(struct console* con) {
asm_outb(COM1_BASE + 1, 0x00); // Disable interrupts
asm_outb(COM1_BASE + 3, 0x80); // Enable DLAB (set baud rate divisor)
asm_outb(COM1_BASE, 0x01); // Set divisor low byte (115200 baud if 1)
asm_outb(COM1_BASE + 1, 0x00); // Set divisor high byte
asm_outb(COM1_BASE + 3, 0x03); // 8 bits, no parity, one stop bit (8n1)
asm_outb(COM1_BASE + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
asm_outb(COM1_BASE + 4, 0x0B); // IRQs enabled, RTS/DSR set
asm_outb(COM1_BASE + 4, 0x1E); // Set to loopback mode.
asm_outb(COM1_BASE, 0xAE); // Send a test byte.
// If we don't get the same value back, this serial port doesn't work.
if (asm_inb(COM1_BASE) != 0xAE) {
return false;
}
asm_outb(COM1_BASE + 4, 0x0F); // Disable loopback mode.
return true;
}
static struct console serial_con = {
.name = "com1",
.write = serial_write,
.init = serial_setup,
};
DEFINE_CONSOLE(&serial_con);
+17
View File
@@ -0,0 +1,17 @@
#include <kernel/compiler.h>
#include <kernel/types.h>
#include <smp.h>
extern uint8_t smp_trampoline_start[];
extern uint8_t smp_trampoline_end[];
[[noreturn]]
static void ap_entry(phys_t info) {
// TODO
while (1) {
asm volatile("hlt");
}
}
void x86_64_smp_init(uint32_t id) {}
+42
View File
@@ -0,0 +1,42 @@
#include <zinnia/archctl.h>
#include <zinnia/status.h>
#include <kernel/compiler.h>
#include <kernel/init.h>
#include <kernel/percpu.h>
#include <gdt.h>
#include "asm.h"
#include "defs.h"
[[__init, __naked]]
void _start() {
asm volatile(
"lea rsp, [rip + %0]\n"
"jmp %1"
:
: "i"(__ld_stack_top), "r"(kernel_entry)
);
}
void percpu_bsp_early_init() {
asm_wrmsr(MSR_GS_BASE, (uint64_t)&percpu_bsp);
asm_wrmsr(MSR_FS_BASE, 0);
asm_wrmsr(MSR_KERNEL_GS_BASE, 0);
gdt_init();
}
[[noreturn]]
void arch_panic() {
asm volatile("cli; hlt");
__unreachable();
}
zn_status_t arch_archctl(zn_archctl_t op, uintptr_t arg) {
switch (op) {
case ZN_ARCHCTL_SET_FSBASE:
asm_wrmsr(MSR_FS_BASE, arg);
return 0;
default:
return ZN_ERR_BAD_ARG;
}
}
+107
View File
@@ -0,0 +1,107 @@
#include <smp.h>
#include <defs.h>
.intel_syntax noprefix
.global smp_trampoline_start
.global smp_trampoline_end
.section .rodata
.code16
smp_trampoline_start:
cli
cld
jmp 1f
smp_trampoline_data:
.skip SMP_INFO_SIZE
.set data_offset, (smp_trampoline_data - smp_trampoline_start)
.set gdtr_offset, (data_offset + SMP_GDTR_OFFSET)
.set farjmp_offset, (data_offset + SMP_FARJMP_OFFSET)
.set temp_stack_offset, (data_offset + SMP_TEMP_STACK_OFFSET)
.set temp_cr3_offset, (data_offset + SMP_TEMP_CR3_OFFSET)
.set entry_offset, (data_offset + SMP_ENTRY_OFFSET)
.set hhdm_offset, (data_offset + SMP_HHDM_OFFSET)
1:
mov bx, cs
shl ebx, 4
.set idtr_offset, (invalid_idtr - smp_trampoline_start)
lidtd cs:idtr_offset
lgdtd cs:gdtr_offset
.set mode32_offset, (mode32 - smp_trampoline_start)
lea eax, [ebx + mode32_offset]
mov dword ptr cs:farjmp_offset, eax
mov eax, CR0_PE | CR0_ET
mov cr0, eax
jmp fword ptr cs:farjmp_offset
.code32
mode32:
mov ax, SMP_KERNEL32_DS
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
xor eax, eax
lldt ax
mov esp, dword ptr [ebx + temp_stack_offset]
xor eax, eax
mov cr4, eax
or eax, CR4_PAE
mov cr4, eax
mov ecx, MSR_EFER
mov eax, MSR_EFER_LME
xor edx, edx
wrmsr
mov eax, dword ptr [ebx + temp_cr3_offset]
mov cr3, eax
mov eax, cr0
or eax, CR0_PG
mov cr0, eax
.set mode64_offset, (mode64 - smp_trampoline_start)
lea eax, [ebx + mode64_offset]
push SMP_KERNEL64_CS
push eax
retf
.code64
mode64:
mov ax, SMP_KERNEL64_DS
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ecx, MSR_EFER
rdmsr
or eax, MSR_EFER_NXE
wrmsr
xor ebp, ebp
lea rdi, [rbx + data_offset]
mov rax, [rbx + entry_offset]
add rsp, [rbx + hhdm_offset]
jmp rax
invalid_idtr:
.word 0
.quad 0
smp_trampoline_end:
+128
View File
@@ -0,0 +1,128 @@
#include <kernel/compiler.h>
#include <kernel/usercopy.h>
extern void x86_64_read_start();
extern void x86_64_read_end();
extern void x86_64_read_fault();
static const struct usercopy_region read_region = {
.start_ip = x86_64_read_start,
.end_ip = x86_64_read_end,
.fault_ip = x86_64_read_fault,
};
[[__naked]]
bool arch_usercopy_read(uint8_t* dst, const __user uint8_t* src, size_t len) {
asm volatile(
// Setup regs for `rep movsb`.
"xchg rcx, rdx\n"
"mov rax, [rip + %0]\n"
"mov [rdx], rax\n"
".global x86_64_read_start\n"
"x86_64_read_start:\n"
"rep movsb\n"
".global x86_64_read_end\n"
"x86_64_read_end:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"mov rax, 1\n"
"ret\n"
".global x86_64_read_fault\n"
"x86_64_read_fault:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"ret\n"
:
: "i"(&read_region)
: "memory"
);
}
extern void x86_64_write_start();
extern void x86_64_write_end();
extern void x86_64_write_fault();
static const struct usercopy_region write_region = {
.start_ip = x86_64_write_start,
.end_ip = x86_64_write_end,
.fault_ip = x86_64_write_fault,
};
[[__naked]]
bool arch_usercopy_write(__user uint8_t* dst, const uint8_t* src, size_t len) {
asm volatile(
// Setup regs for `rep movsb`.
"xchg rcx, rdx\n"
"mov rax, [rip + %0]\n"
"mov [rdx], rax\n"
".global x86_64_write_start\n"
"x86_64_write_start:\n"
"rep movsb\n"
".global x86_64_write_end\n"
"x86_64_write_end:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"mov rax, 1\n"
"ret\n"
".global x86_64_write_fault\n"
"x86_64_write_fault:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"ret\n"
:
: "i"(&read_region)
: "memory"
);
}
extern void x86_64_strlen_start();
extern void x86_64_strlen_end();
extern void x86_64_strlen_fault();
static const struct usercopy_region strlen_region = {
.start_ip = x86_64_strlen_start,
.end_ip = x86_64_strlen_end,
.fault_ip = x86_64_strlen_fault,
};
[[__naked]]
bool arch_usercopy_strlen(const __user uint8_t* str, size_t max, size_t* len) {
asm volatile(
"mov rax, [rip + %0]\n"
"mov [rcx], rax\n"
".global x86_64_strlen_start\n"
"x86_64_strlen_start:\n"
"xor r8, r8\n"
".Lloop:\n"
"cmp byte ptr [rdi + r8], 0\n"
"je .Lleave\n"
"inc r8\n"
"cmp rsi, r8\n"
"jne .Lloop\n"
".Lleave:\n"
"mov [rdx], r8\n"
".global x86_64_strlen_end\n"
"x86_64_strlen_end:\n"
"xor rax, rax\n"
"mov [rcx], rax\n"
"mov rax, 1\n"
"ret\n"
".global x86_64_strlen_fault\n"
"x86_64_strlen_fault:\n"
"xor rax, rax\n"
"mov [rcx], rax\n"
"ret\n"
:
: "i"(&read_region)
: "memory"
);
}
+70
View File
@@ -0,0 +1,70 @@
#include <kernel/cmdline.h>
#include <kernel/init.h>
#include <stddef.h>
#include <string.h>
// Defined in linker script.
extern struct cmdline_option __ld_cmdline_start[];
extern struct cmdline_option __ld_cmdline_end[];
static char cmdline_buf[CMDLINE_MAX];
[[__init]]
void cmdline_parse(const char* cmdline) {
struct cmdline_option* opt = __ld_cmdline_start;
const size_t len = strnlen(cmdline, CMDLINE_MAX - 1);
size_t idx = 0;
memcpy(cmdline_buf, cmdline, len);
while (1) {
char* name = nullptr;
char* value = nullptr;
// Skip all leading spaces.
while (idx < len && cmdline_buf[idx] == ' ')
idx++;
if (idx >= len)
break;
size_t name_idx = idx;
name = cmdline_buf + name_idx;
// Find the next equal sign or space.
while (idx < len && cmdline_buf[idx] != '=' && cmdline_buf[idx] != ' ')
idx++;
if (idx > len)
break;
// Check if the option has a value (=foo).
char seperator = cmdline_buf[idx];
cmdline_buf[idx++] = 0;
if (seperator == '=') {
// Check if we need to escape the value.
char check;
if (cmdline_buf[idx] == '"') {
check = '"';
cmdline_buf[idx++] = 0;
} else {
check = ' ';
}
value = cmdline_buf + idx;
// Skip the value.
while (idx < len && cmdline_buf[idx] != check)
idx++;
if (idx > len)
break;
cmdline_buf[idx++] = 0;
}
// Find the corresponding option.
while (opt < __ld_cmdline_end) {
if (!strcmp(opt->name, name))
opt->func(value);
opt++;
}
if (idx >= len)
break;
}
}
+85
View File
@@ -0,0 +1,85 @@
#include <kernel/assert.h>
#include <kernel/cmdline.h>
#include <kernel/common.h>
#include <kernel/init.h>
#include <kernel/mem.h>
#include <kernel/print.h>
#include "limine.h"
[[__initdata_sorted("limine.0")]]
static volatile uint64_t limine_start[] = LIMINE_REQUESTS_START_MARKER;
[[__initdata_sorted("limine.1")]]
static volatile uint64_t limine_revision[] = LIMINE_BASE_REVISION(4);
[[__initdata_sorted("limine.2")]]
static volatile uint64_t limine_end[] = LIMINE_REQUESTS_END_MARKER;
#define LIMINE_REQUEST(request, tag, rev) \
[[__initdata_sorted("limine.1")]] \
static volatile struct limine_##request request = { \
.id = tag, \
.revision = rev, \
.response = nullptr, \
}
LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST_ID, 0);
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST_ID, 0);
LIMINE_REQUEST(executable_address_request, LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, 0);
LIMINE_REQUEST(executable_file_request, LIMINE_EXECUTABLE_FILE_REQUEST_ID, 0);
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST_ID, 1);
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST_ID, 0);
LIMINE_REQUEST(dtb_request, LIMINE_DTB_REQUEST_ID, 0);
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST_ID, 0);
[[__initdata]]
static struct phys_mem mem[128];
[[__initdata]]
static struct boot_file files[8];
[[__init]]
void kernel_entry() {
kernel_early_init();
struct boot_info info;
struct limine_memmap_response* mm_res = memmap_request.response;
struct limine_module_response* module_res = module_request.response;
struct limine_executable_address_response* exec_res = executable_address_request.response;
struct limine_executable_file_response* exec_file_res = executable_file_request.response;
info.cmdline = exec_file_res->executable_file->string;
info.num_mem_maps = MIN(ARRAY_SIZE(mem), mm_res->entry_count);
for (size_t i = 0; i < info.num_mem_maps; i++) {
mem[i].address = mm_res->entries[i]->base;
mem[i].length = mm_res->entries[i]->length;
switch (mm_res->entries[i]->type) {
case LIMINE_MEMMAP_USABLE:
mem[i].usage = PHYS_USABLE;
break;
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
mem[i].usage = PHYS_RECLAIMABLE;
break;
default:
mem[i].usage = PHYS_RESERVED;
break;
}
}
if (module_res) {
info.num_files = MIN(ARRAY_SIZE(files), module_res->module_count);
for (size_t i = 0; i < info.num_files; i++) {
files[i].data = module_res->modules[i]->address;
files[i].length = module_res->modules[i]->size;
files[i].path = module_res->modules[i]->path;
}
}
info.mem_map = mem;
info.phys_base = (phys_t)exec_res->physical_base;
info.virt_base = (virt_t)exec_res->virtual_base;
info.hhdm_base = (virt_t)hhdm_request.response->offset;
kernel_main(&info);
}
+576
View File
@@ -0,0 +1,576 @@
/* BSD Zero Clause License */
/* Copyright (C) 2022-2025 Mintsuki and contributors.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIMINE_H
#define LIMINE_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* Misc */
#ifdef LIMINE_NO_POINTERS
#define LIMINE_PTR(TYPE) uint64_t
#else
#define LIMINE_PTR(TYPE) TYPE
#endif
#define LIMINE_REQUESTS_START_MARKER {0xf6b8f4b39de7d1ae, 0xfab91a6940fcb9cf, 0x785c6ed015d3e316, 0x181e920a7852b9d9}
#define LIMINE_REQUESTS_END_MARKER {0xadc0e0531bb10d03, 0x9572709f31764c62}
#define LIMINE_BASE_REVISION(N) {0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N)}
#define LIMINE_BASE_REVISION_SUPPORTED(VAR) ((VAR)[2] == 0)
#define LIMINE_LOADED_BASE_REVISION_VALID(VAR) ((VAR)[1] != 0x6a7b384944536bdc)
#define LIMINE_LOADED_BASE_REVISION(VAR) ((VAR)[1])
#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b
struct limine_uuid {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
};
#define LIMINE_MEDIA_TYPE_GENERIC 0
#define LIMINE_MEDIA_TYPE_OPTICAL 1
#define LIMINE_MEDIA_TYPE_TFTP 2
struct limine_file {
uint64_t revision;
LIMINE_PTR(void*) address;
uint64_t size;
LIMINE_PTR(char*) path;
LIMINE_PTR(char*) string;
uint32_t media_type;
uint32_t unused;
uint32_t tftp_ip;
uint32_t tftp_port;
uint32_t partition_index;
uint32_t mbr_disk_id;
struct limine_uuid gpt_disk_uuid;
struct limine_uuid gpt_part_uuid;
struct limine_uuid part_uuid;
};
/* Boot info */
#define LIMINE_BOOTLOADER_INFO_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740}
struct limine_bootloader_info_response {
uint64_t revision;
LIMINE_PTR(char*) name;
LIMINE_PTR(char*) version;
};
struct limine_bootloader_info_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_bootloader_info_response*) response;
};
/* Executable command line */
#define LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x4b161536e598651e, 0xb390ad4a2f1f303a}
struct limine_executable_cmdline_response {
uint64_t revision;
LIMINE_PTR(char*) cmdline;
};
struct limine_executable_cmdline_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_executable_cmdline_response*) response;
};
/* Firmware type */
#define LIMINE_FIRMWARE_TYPE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x8c2f75d90bef28a8, 0x7045a4688eac00c3}
#define LIMINE_FIRMWARE_TYPE_X86BIOS 0
#define LIMINE_FIRMWARE_TYPE_EFI32 1
#define LIMINE_FIRMWARE_TYPE_EFI64 2
#define LIMINE_FIRMWARE_TYPE_SBI 3
struct limine_firmware_type_response {
uint64_t revision;
uint64_t firmware_type;
};
struct limine_firmware_type_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_firmware_type_response*) response;
};
/* Stack size */
#define LIMINE_STACK_SIZE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d}
struct limine_stack_size_response {
uint64_t revision;
};
struct limine_stack_size_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_stack_size_response*) response;
uint64_t stack_size;
};
/* HHDM */
#define LIMINE_HHDM_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b}
struct limine_hhdm_response {
uint64_t revision;
uint64_t offset;
};
struct limine_hhdm_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_hhdm_response*) response;
};
/* Framebuffer */
#define LIMINE_FRAMEBUFFER_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b}
#define LIMINE_FRAMEBUFFER_RGB 1
struct limine_video_mode {
uint64_t pitch;
uint64_t width;
uint64_t height;
uint16_t bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
};
struct limine_framebuffer {
LIMINE_PTR(void*) address;
uint64_t width;
uint64_t height;
uint64_t pitch;
uint16_t bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
uint8_t unused[7];
uint64_t edid_size;
LIMINE_PTR(void*) edid;
/* Response revision 1 */
uint64_t mode_count;
LIMINE_PTR(struct limine_video_mode**) modes;
};
struct limine_framebuffer_response {
uint64_t revision;
uint64_t framebuffer_count;
LIMINE_PTR(struct limine_framebuffer**) framebuffers;
};
struct limine_framebuffer_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_framebuffer_response*) response;
};
/* Paging mode */
#define LIMINE_PAGING_MODE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a}
#if defined(__x86_64__) || defined(__i386__)
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_X86_64_4LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
#elif defined(__aarch64__)
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_AARCH64_4LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
#elif defined(__riscv) && (__riscv_xlen == 64)
#define LIMINE_PAGING_MODE_RISCV_SV39 0
#define LIMINE_PAGING_MODE_RISCV_SV48 1
#define LIMINE_PAGING_MODE_RISCV_SV57 2
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_RISCV_SV39
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
#elif defined(__loongarch__) && (__loongarch_grlen == 64)
#define LIMINE_PAGING_MODE_LOONGARCH64_4LVL 0
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_LOONGARCH64_4LVL
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_LOONGARCH64_4LVL
#else
#error Unknown architecture
#endif
struct limine_paging_mode_response {
uint64_t revision;
uint64_t mode;
};
struct limine_paging_mode_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_paging_mode_response*) response;
uint64_t mode;
uint64_t max_mode;
uint64_t min_mode;
};
/* MP */
#define LIMINE_MP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0}
struct limine_mp_info;
typedef void (*limine_goto_address)(struct limine_mp_info*);
#if defined(__x86_64__) || defined(__i386__)
#define LIMINE_MP_X2APIC (1 << 0)
struct limine_mp_info {
uint32_t processor_id;
uint32_t lapic_id;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint32_t flags;
uint32_t bsp_lapic_id;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info**) cpus;
};
#elif defined(__aarch64__)
struct limine_mp_info {
uint32_t processor_id;
uint32_t reserved1;
uint64_t mpidr;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint64_t flags;
uint64_t bsp_mpidr;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info**) cpus;
};
#elif defined(__riscv) && (__riscv_xlen == 64)
struct limine_mp_info {
uint64_t processor_id;
uint64_t hartid;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint64_t flags;
uint64_t bsp_hartid;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info**) cpus;
};
#elif defined(__loongarch__) && (__loongarch_grlen == 64)
struct limine_mp_info {
uint64_t reserved;
};
struct limine_mp_response {
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info**) cpus;
};
#else
#error Unknown architecture
#endif
struct limine_mp_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_mp_response*) response;
uint64_t flags;
};
/* Memory map */
#define LIMINE_MEMMAP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62}
#define LIMINE_MEMMAP_USABLE 0
#define LIMINE_MEMMAP_RESERVED 1
#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2
#define LIMINE_MEMMAP_ACPI_NVS 3
#define LIMINE_MEMMAP_BAD_MEMORY 4
#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5
#define LIMINE_MEMMAP_EXECUTABLE_AND_MODULES 6
#define LIMINE_MEMMAP_FRAMEBUFFER 7
#define LIMINE_MEMMAP_ACPI_TABLES 8
struct limine_memmap_entry {
uint64_t base;
uint64_t length;
uint64_t type;
};
struct limine_memmap_response {
uint64_t revision;
uint64_t entry_count;
LIMINE_PTR(struct limine_memmap_entry**) entries;
};
struct limine_memmap_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_memmap_response*) response;
};
/* Entry point */
#define LIMINE_ENTRY_POINT_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a}
typedef void (*limine_entry_point)(void);
struct limine_entry_point_response {
uint64_t revision;
};
struct limine_entry_point_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_entry_point_response*) response;
LIMINE_PTR(limine_entry_point) entry;
};
/* Executable File */
#define LIMINE_EXECUTABLE_FILE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69}
struct limine_executable_file_response {
uint64_t revision;
LIMINE_PTR(struct limine_file*) executable_file;
};
struct limine_executable_file_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_executable_file_response*) response;
};
/* Module */
#define LIMINE_MODULE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee}
#define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0)
#define LIMINE_INTERNAL_MODULE_COMPRESSED (1 << 1)
struct limine_internal_module {
LIMINE_PTR(const char*) path;
LIMINE_PTR(const char*) string;
uint64_t flags;
};
struct limine_module_response {
uint64_t revision;
uint64_t module_count;
LIMINE_PTR(struct limine_file**) modules;
};
struct limine_module_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_module_response*) response;
/* Request revision 1 */
uint64_t internal_module_count;
LIMINE_PTR(struct limine_internal_module**) internal_modules;
};
/* RSDP */
#define LIMINE_RSDP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c}
struct limine_rsdp_response {
uint64_t revision;
LIMINE_PTR(void*) address;
};
struct limine_rsdp_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_rsdp_response*) response;
};
/* SMBIOS */
#define LIMINE_SMBIOS_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee}
struct limine_smbios_response {
uint64_t revision;
uint64_t entry_32;
uint64_t entry_64;
};
struct limine_smbios_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_smbios_response*) response;
};
/* EFI system table */
#define LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc}
struct limine_efi_system_table_response {
uint64_t revision;
uint64_t address;
};
struct limine_efi_system_table_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_efi_system_table_response*) response;
};
/* EFI memory map */
#define LIMINE_EFI_MEMMAP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8}
struct limine_efi_memmap_response {
uint64_t revision;
LIMINE_PTR(void*) memmap;
uint64_t memmap_size;
uint64_t desc_size;
uint64_t desc_version;
};
struct limine_efi_memmap_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_efi_memmap_response*) response;
};
/* Date at boot */
#define LIMINE_DATE_AT_BOOT_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893}
struct limine_date_at_boot_response {
uint64_t revision;
int64_t timestamp;
};
struct limine_date_at_boot_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_date_at_boot_response*) response;
};
/* Executable address */
#define LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487}
struct limine_executable_address_response {
uint64_t revision;
uint64_t physical_base;
uint64_t virtual_base;
};
struct limine_executable_address_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_executable_address_response*) response;
};
/* Device Tree Blob */
#define LIMINE_DTB_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7}
struct limine_dtb_response {
uint64_t revision;
LIMINE_PTR(void*) dtb_ptr;
};
struct limine_dtb_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_dtb_response*) response;
};
/* RISC-V Boot Hart ID */
#define LIMINE_RISCV_BSP_HARTID_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x1369359f025525f9, 0x2ff2a56178391bb6}
struct limine_riscv_bsp_hartid_response {
uint64_t revision;
uint64_t bsp_hartid;
};
struct limine_riscv_bsp_hartid_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_riscv_bsp_hartid_response*) response;
};
/* Bootloader Performance */
#define LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x6b50ad9bf36d13ad, 0xdc4c7e88fc759e17}
struct limine_bootloader_performance_response {
uint64_t revision;
uint64_t reset_usec;
uint64_t init_usec;
uint64_t exec_usec;
};
struct limine_bootloader_performance_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_bootloader_performance_response*) response;
};
#ifdef __cplusplus
}
#endif
#endif
+1
View File
@@ -0,0 +1 @@
kernel_sources += files('entry.c')
+42
View File
@@ -0,0 +1,42 @@
#include <kernel/assert.h>
#include <kernel/cmdline.h>
#include <kernel/compiler.h>
#include <kernel/console.h>
#include <kernel/init.h>
#include <kernel/irq.h>
#include <kernel/mem.h>
#include <kernel/percpu.h>
#include <kernel/print.h>
#include <config.h>
const char zinnia_banner[] =
"zinnia " ZINNIA_VERSION " (" ZINNIA_ARCH ", " ZINNIA_COMPILER_ID ", " ZINNIA_LINKER_ID ")";
[[__init]]
void kernel_early_init() {
percpu_bsp_early_init();
percpu_get()->online = true;
irq_lock();
}
[[noreturn]]
void kernel_main(struct boot_info* info) {
cmdline_parse(info->cmdline);
console_init();
kprintf("%s\n", zinnia_banner); // Say hello!
kprintf("Command line: \"%s\"\n", info->cmdline);
mem_init(info->mem_map, info->num_mem_maps, info->virt_base, info->phys_base, info->hhdm_base);
sched_init();
irq_unlock();
while (1) {}
}
[[noreturn]]
void kernel_main_task() {
while (1) {}
ASSERT(false, "Nothing to do");
}
+6
View File
@@ -0,0 +1,6 @@
kernel_sources += files(
'cmdline.c',
'main.c',
)
subdir('limine')
+1
View File
@@ -0,0 +1 @@
+44
View File
@@ -0,0 +1,44 @@
#include <kernel/cmdline.h>
#include <kernel/console.h>
#include <kernel/print.h>
#include <kernel/syscalls.h>
#include <string.h>
extern struct console* const __ld_console_start[];
extern struct console* const __ld_console_end[];
struct console* active = nullptr;
void console_write(const char* buf, size_t len) {
if (__likely(active))
active->write(active, buf, len);
}
static void console_setup(const char* value) {
if (!value)
return;
// Find a suitable boot console.
struct console* const* cur = __ld_console_start;
while (cur < __ld_console_end) {
if (!strcmp(value, (*cur)->name)) {
active = *cur;
}
cur++;
}
}
CMDLINE_OPTION("console", console_setup);
void console_init() {
// If we have no console set and at least one set, use that as a default.
if (!active && &__ld_console_start[0] != &__ld_console_end[0])
active = __ld_console_start[0];
if (active) {
if (active->init(active) == false)
active = nullptr;
else
kprintf("Logging on console \"%s\"\n", active->name);
}
}
+1
View File
@@ -0,0 +1 @@
#include <kernel/elf.h>
+16
View File
@@ -0,0 +1,16 @@
#include <kernel/irq.h>
#include <kernel/percpu.h>
#include <stdatomic.h>
void irq_lock() {
irq_set_state(false);
atomic_fetch_add_explicit(&percpu_get()->irq.level, 1, memory_order_acq_rel);
}
void irq_unlock() {
uint32_t old_level = atomic_fetch_sub_explicit(&percpu_get()->irq.level, 1, memory_order_acq_rel);
// If it was 1, the new IRQ level is now 0.
if (old_level == 1) {
irq_set_state(true);
}
}
+11
View File
@@ -0,0 +1,11 @@
kernel_sources += files(
'clock.c',
'console.c',
'elf.c',
'irq.c',
'panic.c',
'percpu.c',
'print.c',
'sched.c',
'spin.c',
)
+7
View File
@@ -0,0 +1,7 @@
#include <kernel/panic.h>
[[noreturn]]
void panic() {
// TODO
while (1) {}
}
+14
View File
@@ -0,0 +1,14 @@
#include <kernel/compiler.h>
#include <kernel/percpu.h>
[[__used, __section(".percpu")]]
struct percpu percpu_bsp = {
.self = &percpu_bsp,
.id = 0,
.online = false,
};
struct percpu* percpu_new() {
// TODO
return nullptr;
}
+372
View File
@@ -0,0 +1,372 @@
#include <kernel/console.h>
#include <kernel/panic.h>
#include <kernel/print.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
static void reverse(char* s) {
int i, j;
char c;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
static int pow(int x, unsigned int y) {
if (y == 0)
return 1;
else if ((y % 2) == 0)
return pow(x, y / 2) * pow(x, y / 2);
else
return x * pow(x, y / 2) * pow(x, y / 2);
}
long atol(const char* str, int base) {
size_t len = strlen(str);
int64_t result = 0;
size_t i = 0;
if (str[0] == '-')
i++;
for (; i < len; i++)
result += (str[i] - '0') * pow(base, len - i - 1);
if (str[0] == '-')
result *= -1;
return result;
};
unsigned long atolu(const char* str, int base) {
size_t len = strlen(str);
uint64_t result = 0;
size_t i = 0;
for (; i < len; i++)
result += (str[i] - '0') * pow(base, len - i - 1);
return result;
};
char* ltoa(int64_t value, char* str, int base) {
size_t i;
int64_t sign;
sign = value;
if (base == 10 && sign < 0)
value = -value;
i = 0;
do {
char c = value % base;
if (c > 9)
c += 7;
str[i++] = c + '0';
} while ((value /= base) > 0);
if (base == 10 && sign < 0)
str[i++] = '-';
str[i] = '\0';
reverse(str);
return str;
};
char* lutoa(uint64_t value, char* str, int base) {
size_t i = 0;
do {
char c = value % base;
if (c > 9)
c += 7;
str[i++] = c + '0';
} while ((value /= base) > 0);
str[i] = '\0';
reverse(str);
return str;
};
typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len);
static void kvprintf(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
// Amount of bytes written.
int32_t written = 0;
while (*fmt != '\0') {
size_t maxrem = INT32_MAX - written;
if (fmt[0] != '%' || fmt[1] == '%') {
if (fmt[0] == '%')
fmt++;
size_t amount = 1;
while (fmt[amount] && fmt[amount] != '%')
amount++;
if (maxrem < amount) {
return;
}
callback(ctx, fmt, amount);
fmt += amount;
written += amount;
continue;
}
const char* format_begun_at = fmt++;
bool force_sign = false;
bool blank_sign = false;
bool write_prefix = false;
bool pad_zero = false;
bool has_width = false;
size_t width = 0;
bool has_precision = false;
size_t precision = 0;
size_t size = sizeof(uint32_t);
char number[32];
check_fmt:
memset(number, 0, sizeof(number));
switch (*fmt) {
// Flags
case '+': {
force_sign = true;
fmt++;
goto check_fmt;
}
case ' ': {
blank_sign = true;
fmt++;
goto check_fmt;
}
case '#': {
write_prefix = true;
fmt++;
goto check_fmt;
}
case '0': {
pad_zero = true;
fmt++;
goto check_fmt;
}
// Width
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
has_width = true;
char number[10 + 1] = {0};
size_t idx = 0;
while (*fmt >= '0' && *fmt <= '9') {
number[idx++] = *fmt;
fmt++;
}
width = atolu(number, 10);
goto check_fmt;
}
// Precision
case '.': {
has_precision = true;
fmt++;
if (*fmt == '*') {
precision = (char)va_arg(args, int32_t);
} else {
char number[10 + 1] = {0};
size_t idx = 0;
while (*fmt >= '0' && *fmt <= '9') {
number[idx++] = *fmt;
fmt++;
}
precision = atolu(number, 10);
}
goto check_fmt;
}
// Length
case 'h': {
if (fmt[1] == 'h') {
size = sizeof(uint8_t);
fmt++;
} else
size = sizeof(uint16_t);
fmt++;
goto check_fmt;
}
case 'l': {
// There is no difference between `long int` and `long long int`.
if (fmt[1] == 'l')
fmt++;
fmt++;
size = sizeof(uint64_t);
goto check_fmt;
}
case 'z': {
size = sizeof(size_t);
fmt++;
goto check_fmt;
}
// Character
case 'c': {
char c = (char)va_arg(args, int32_t);
callback(ctx, &c, sizeof(c));
written++;
fmt++;
break;
}
// String of characters
case 's': {
const char* str = va_arg(args, const char*);
if (!str)
str = "(null)";
size_t len = 0;
if (has_width)
len = strnlen(str, width);
else
len = strlen(str);
callback(ctx, str, len);
written += len;
fmt++;
break;
}
// Signed decimal integer
case 'i':
case 'd': {
switch (size) {
case sizeof(int8_t):
ltoa((int8_t)va_arg(args, int32_t), number, 10);
break;
case sizeof(int16_t):
ltoa((int16_t)va_arg(args, int32_t), number, 10);
break;
case sizeof(int32_t):
ltoa((int32_t)va_arg(args, int32_t), number, 10);
break;
default:
ltoa(va_arg(args, int64_t), number, 10);
break;
}
goto print_num;
}
// Unsigned decimal integer
case 'u': {
switch (size) {
case sizeof(uint8_t):
lutoa((uint8_t)va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint16_t):
lutoa((uint16_t)va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint32_t):
lutoa(va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint64_t):
lutoa(va_arg(args, uint64_t), number, 10);
break;
}
goto print_num;
}
// Unsigned hexadecimal integer
case 'X':
case 'x': {
switch (size) {
case sizeof(uint8_t):
lutoa((uint8_t)va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint16_t):
lutoa((uint16_t)va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint32_t):
lutoa(va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint64_t):
lutoa(va_arg(args, uint64_t), number, 16);
break;
}
print_num:
size_t len = strlen(number);
if (has_precision) {
for (size_t i = 0; i < precision - len; i++)
callback(ctx, " ", 1);
}
if (has_width) {
char c = pad_zero ? '0' : ' ';
// We don't have to pad anything.
if (len < width) {
for (size_t i = 0; i < width - len; i++)
callback(ctx, &c, 1);
}
}
if (write_prefix) {
callback(ctx, "0x", 2);
}
if (force_sign) {
char c = '+';
if (number[0] != '-')
callback(ctx, &c, 1);
}
if (blank_sign) {
char c = ' ';
if (number[0] != '-')
callback(ctx, &c, 1);
}
callback(ctx, number, len);
written += len;
fmt++;
break;
}
// Pointer address
case 'p': {
const size_t num = va_arg(args, size_t);
const size_t buf_size = sizeof(size_t) * 2 + 1;
char str[buf_size];
lutoa(num, number, 0x10);
const size_t len = strlen(number); // Get the length of the final number.
for (size_t i = 0; i < buf_size; i++)
str[i] = '0';
size_t offset = sizeof(str) - len - 1;
callback(ctx, str, offset);
callback(ctx, number, len);
fmt++;
written += len;
break;
}
// The number of characters written so far is stored in the pointed location.
case 'n': {
int32_t* const ptr = va_arg(args, int32_t*);
*ptr = written;
fmt++;
break;
}
// No format, just normal text.
default: {
fmt = format_begun_at;
size_t len = strlen(fmt);
callback(ctx, fmt, len);
written += len;
fmt += len;
break;
}
}
}
}
static void printf_console(void*, const char* msg, size_t len) {
console_write(msg, len);
}
void kprintf(const char* message, ...) {
va_list args;
va_start(args, message);
kvprintf(printf_console, nullptr, message, args);
va_end(args);
}
+6
View File
@@ -0,0 +1,6 @@
#include <kernel/print.h>
#include <kernel/sched.h>
void sched_init() {
kprintf("Scheduler initialized\n");
}
+16
View File
@@ -0,0 +1,16 @@
#include <kernel/spin.h>
#include <stdatomic.h>
// TODO: Ticketing system
void spin_lock(struct spinlock* spin) {
while (atomic_exchange_explicit(&spin->locked, true, memory_order_acquire)) {
asm volatile("pause");
}
atomic_store_explicit(&spin->locked, true, memory_order_release);
}
void spin_unlock(struct spinlock* spin) {
atomic_store_explicit(&spin->locked, false, memory_order_release);
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <kernel/compiler.h>
#include <kernel/panic.h>
#include <kernel/print.h>
#define ASSERT(expr, msg, ...) \
({ \
if (__unlikely(!(expr))) { \
kprintf( \
"\e[31mKernel panic - Environment is unsound!\n" \
"In function \"%s\" (%s:%u):\n" \
"Assertion \"%s\" failed! " msg "\n", \
__FUNCTION__, \
__FILE__, \
__LINE__, \
#expr, \
##__VA_ARGS__ \
); \
panic(); \
} \
})
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <kernel/common.h>
#include <kernel/compiler.h>
#define CMDLINE_MAX 0x1000
#define CMDLINE_OPTION(opt_name, opt_func) \
[[__used, __section(".cmdline")]] \
static const struct cmdline_option UNIQUE_IDENT(__cmline_option_) = { \
.name = opt_name, \
.func = opt_func, \
}
struct cmdline_option {
// The name of this option.
const char* name;
// Gets called if this option is present on the command line.
// If an option is specified as `name=value`, then the `value` is passed as well.
// Otherwise, it's NULL.
void (*func)(const char* value);
};
// Parses the command line and invokes all options.
void cmdline_parse(const char* cmdline);
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#define ROUND_UP(value, to) (((value) + ((to) - 1)) / (to))
#define ALIGN_DOWN(value, align) (((value) / (align)) * (align))
#define ALIGN_UP(value, align) (ROUND_UP(value, align) * align)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define BIT_TEST(value, bit) (((value) & (1 << bit)) == (1 << bit))
#define CONTAINER_OF(value, p, field) ((p*)((char*)(&(value)) + offsetof(p, field)))
#define CONCAT(A, B) _CONCAT(A, B)
#define _CONCAT(A, B) A##B
// Creates a unique identifier for a compilation unit.
#define UNIQUE_IDENT(ident) CONCAT(__unique_##ident, __COUNTER__)
// Ensures that a type is properly defined at this point.
// This is used to ensure that e.g. architecture-specific structures are defined.
#define ASSERT_TYPE(type) static_assert(sizeof(type) == sizeof(type))
+46
View File
@@ -0,0 +1,46 @@
#pragma once
// These hints may improve code generation, but are optional and can be stubbed.
#if defined(__GNUC__) || defined(__clang__)
#define __cold gnu::cold
#define __hot gnu::hot
#define __used gnu::used
#define __unused gnu::unused
#define __format(like, ...) gnu::format(like, __VA_ARGS__)
#define __likely(x) __builtin_expect(!!(x), 1)
#define __unlikely(x) __builtin_expect(!!(x), 0)
#define __always_inline gnu::always_inline
#else
#define __cold
#define __hot
#define __used
#define __unused
#define __format(like, ...)
#define __likely(x) x
#define __unlikely(x) x
#define __always_inline
#endif
// Only clang has these hints, GCC will throw a warning here.
// They also don't support the C23 attribute format.
#ifdef __clang__
#define __user __attribute__((noderef, address_space(1)))
#define __phys __attribute__((noderef, address_space(2)))
#else
#define __user
#define __phys
#endif
// Attributes that affect data layout. Not optional.
#if defined(__GNUC__) || defined(__clang__)
#define __weak gnu::weak
#define __section(x) gnu::section(x)
#define __aligned(x) gnu::aligned(x)
#define __packed gnu::packed
#define __naked gnu::naked
#else
#error "Unsupported compiler!"
#endif
// Intrinsics
#define __unreachable() __builtin_unreachable()
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <kernel/common.h>
#include <kernel/compiler.h>
#include <stddef.h>
struct console {
const char* name;
void* private;
// Initializes the console.
bool (*init)(struct console* con);
// Writes to the console.
void (*write)(struct console* con, const char* buf, size_t count);
};
// Initializes a console for logging.
void console_init();
// Writes a message to a console.
void console_write(const char* buf, size_t len);
// Defines a new early console which can be used to log early boot messages.
// An early console must not make any heap allocations.
#define DEFINE_CONSOLE(con) \
[[__used, __section(".console")]] \
static struct console* UNIQUE_IDENT(console) = (con)
+227
View File
@@ -0,0 +1,227 @@
#pragma once
#include <bits/elf.h>
#include <stdint.h>
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
typedef uint64_t elf64_addr_t;
typedef uint64_t elf64_off_t;
typedef uint32_t elf32_addr_t;
typedef uint32_t elf32_off_t;
enum : uint8_t {
EI_MAG0 = 0,
EI_MAG1 = 1,
EI_MAG2 = 2,
EI_MAG3 = 3,
EI_CLASS = 4,
EI_DATA = 5,
EI_VERSION = 6,
EI_OSABI = 7,
EI_ABIVERSION = 8,
EI_PAD = 9,
EI_NIDENT = 16,
};
enum : uint16_t {
EM_X86_64 = 62,
EM_AARCH64 = 183,
EM_RISCV = 243,
EM_LOONGARCH = 258,
};
enum : uint32_t {
ELFCLASS32 = 1,
ELFCLASS64 = 2,
};
enum : uint32_t {
ELFDATA2LSB = 1,
ELFDATA2MSB = 2,
};
enum : uint32_t {
EV_NONE = 0,
EV_CURRENT = 1,
EV_NUM = 2,
};
enum : uint32_t {
ELFOSABI_SYSV = 0,
ELFOSABI_HPUX = 1,
ELFOSABI_STANDALONE = 255,
};
enum : uint32_t {
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
};
enum : uint32_t {
PT_NULL = 0x00000000,
PT_LOAD = 0x00000001,
PT_DYNAMIC = 0x00000002,
PT_INTERP = 0x00000003,
PT_NOTE = 0x00000004,
PT_SHLIB = 0x00000005,
PT_PHDR = 0x00000006,
PT_TLS = 0x00000007,
};
enum : uint32_t {
PF_X = 0x00000001,
PF_W = 0x00000002,
PF_R = 0x00000004,
};
enum : uint32_t {
DT_NULL = 0,
DT_NEEDED = 1,
DT_PLTRELSZ = 2,
DT_PLTGOT = 3,
DT_HASH = 4,
DT_STRTAB = 5,
DT_SYMTAB = 6,
DT_RELA = 7,
DT_RELASZ = 8,
DT_RELAENT = 9,
DT_STRSZ = 10,
DT_SYMENT = 11,
DT_INIT = 12,
DT_FINI = 13,
DT_SONAME = 14,
DT_RPATH = 15,
DT_SYMBOLIC = 16,
DT_REL = 17,
DT_RELSZ = 18,
DT_RELENT = 19,
DT_PLTREL = 20,
DT_DEBUG = 21,
DT_TEXTREL = 22,
DT_JMPREL = 23,
DT_BIND_NOW = 24,
DT_INIT_ARRAY = 25,
DT_FINI_ARRAY = 26,
DT_INIT_ARRAYSZ = 27,
DT_FINI_ARRAYSZ = 28,
DT_LOOS = 0x60000000,
DT_HIOS = 0x6FFFFFFF,
DT_LOPROC = 0x70000000,
DT_HIPROC = 0x7FFFFFFF,
};
enum : uint32_t {
AT_NULL = 0,
AT_IGNORE = 1,
AT_EXECFD = 2,
AT_PHDR = 3,
AT_PHENT = 4,
AT_PHNUM = 5,
AT_PAGESZ = 6,
AT_BASE = 7,
AT_FLAGS = 8,
AT_ENTRY = 9,
AT_NOTELF = 10,
AT_UID = 11,
AT_EUID = 12,
AT_GID = 13,
AT_EGID = 14,
};
struct elf64_ehdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf64_addr_t e_entry;
elf64_off_t e_phoff;
elf64_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct elf32_ehdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf32_addr_t e_entry;
elf32_off_t e_phoff;
elf32_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct elf64_phdr {
uint32_t p_type;
uint32_t p_flags;
elf64_off_t p_offset;
elf64_addr_t p_vaddr;
elf64_addr_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
};
struct elf32_phdr {
uint32_t p_type;
elf32_off_t p_offset;
elf32_addr_t p_vaddr;
elf32_addr_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct elf64_dyn {
int64_t d_tag;
union {
uint64_t d_val;
elf64_addr_t d_ptr;
} d_un;
};
struct elf32_dyn {
int32_t r_offset;
union {
uint32_t d_val;
elf32_addr_t d_ptr;
} d_un;
};
struct elf64_nhdr {
uint64_t n_namesz;
uint64_t n_descsz;
uint64_t n_type;
};
struct elf32_nhdr {
uint32_t n_namesz;
uint32_t n_descsz;
uint32_t n_type;
};
struct elf64_auxv {
uint64_t atype;
uint64_t avalue;
};
struct elf32_auxv {
uint32_t atype;
uint32_t avalue;
};
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include <zinnia/handle.h>
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <kernel/common.h>
#include <kernel/compiler.h>
#include <kernel/types.h>
#include <stddef.h>
#include <stdint.h>
#define __init __used, __section(".init.text"), __cold
#define __initdata __used, __section(".init.data")
#define __initdata_sorted(name) __used, __section(".init.data." name)
struct boot_file {
uint8_t* data;
size_t length;
const char* path;
};
struct boot_info {
char* cmdline;
struct phys_mem* mem_map;
size_t num_mem_maps;
phys_t phys_base;
virt_t virt_base;
virt_t hhdm_base;
struct boot_file* files;
size_t num_files;
};
extern uint8_t __ld_stack_top[];
extern uint8_t __ld_stack_bottom[];
// Entry point for the kernel after arch-specific setup has finished.
void kernel_entry();
// Initializes the early parts of the kernel.
void kernel_early_init();
[[noreturn]]
void kernel_main(struct boot_info* info);
[[noreturn]]
void kernel_main_task();
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include <bits/irq.h>
#include <kernel/compiler.h>
#include <kernel/list.h>
#include <kernel/vector.h>
#include <stdint.h>
enum irq_status : uint8_t {
IRQ_STATUS_HANDLED,
IRQ_STATUS_IGNORED,
};
enum irq_polarity : uint8_t {
IRQ_POLARITY_LOW,
IRQ_POLARITY_HIGH,
};
enum irq_trigger_mode : uint8_t {
IRQ_TRIGGER_EDGE,
IRQ_TRIGGER_LEVEL,
};
struct irq_handler {
enum irq_status (*handler)(void* self);
SLIST_LINK(struct irq_handler*) next;
};
struct irq_line {
int (*set_config)(enum irq_trigger_mode mode, enum irq_polarity polarity);
void (*mask)(struct irq_line* self);
void (*unmask)(struct irq_line* self);
void (*eoi)(struct irq_line* self);
SLIST_HEAD(struct irq_handler*) handlers;
bool is_busy;
};
void irq_line_attach(struct irq_line* self, struct irq_handler* handler);
struct irq_percpu {
uint32_t level;
};
void irq_lock();
void irq_unlock();
// Enables or disables interrupts on this CPU.
// Don't use this function directly.
static inline void irq_set_state(bool state) {
return arch_irq_set_state(state);
}
// Returns true if interrupts are enabled on this CPU.
// Don't use this function directly.
static inline bool irq_get_state() {
return arch_irq_get_state();
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#define SLIST_HEAD(type) \
struct { \
type sl_head; \
}
#define SLIST_LINK(type) \
struct { \
type sl_link; \
}
#define SLIST_EMPTY(head) ((head)->sl_head == NULL)
#define SLIST_FIRST(head) ((head)->sl_head)
#define SLIST_NEXT(elm, field) ((elm)->field.sl_link)
#define SLIST_INSERT_END(slistelm, elm, field) \
do { \
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
SLIST_NEXT((slistelm), field) = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) \
do { \
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
SLIST_FIRST((head)) = (elm); \
} while (0)
#define SLIST_FOREACH(var, head, field) for ((var) = SLIST_FIRST((head)); (var); (var) = SLIST_NEXT((var), field))
+180
View File
@@ -0,0 +1,180 @@
#pragma once
#include <zinnia/status.h>
#include <kernel/compiler.h>
#include <kernel/spin.h>
#include <kernel/types.h>
#include <bits/mem.h>
#include <stddef.h>
ASSERT_TYPE(pte_t);
enum pte_flags {
PTE_READ = 1 << 0, // Can read from this page.
PTE_WRITE = 1 << 1, // Can write to this page.
PTE_EXEC = 1 << 2, // Can execute code on this page.
PTE_USER = 1 << 3, // Can be accessed by the user.
PTE_DIR = 1 << 4, // Is a non-leaf page.
};
enum cache_mode {
CACHE_NONE,
CACHE_WRITE_COMBINE,
CACHE_WRITE_THROUGH,
CACHE_WRITE_BACK,
CACHE_MMIO,
};
struct page_table {
phys_t root;
struct spinlock lock;
};
enum alloc_flags {
ALLOC_NOZERO = 1 << 0, // Don't zero out the allocated memory.
ALLOC_MEM32 = 1 << 1, // Allocated memory needs to fit inside 32 bits.
ALLOC_MEM20 = 1 << 2, // Allocated memory needs to fit inside 20 bits.
};
enum phys_mem_usage {
PHYS_RESERVED,
PHYS_USABLE,
PHYS_RECLAIMABLE,
};
struct phys_mem {
phys_t address;
size_t length;
enum phys_mem_usage usage;
};
enum page_type {
PAGE_PHYS = 0, // Regular physical memory.
};
struct page {
enum page_type type;
int32_t flags;
size_t refcount;
union {
struct {
struct page* next; // Pointer to the next chunk.
size_t count; // Amount of free pages.
} freelist;
};
};
static_assert(0x1000 % sizeof(struct page) == 0, "struct must be a multiple of the page size!");
static_assert(sizeof(struct page) <= 64, "struct must be smaller than 64 bytes!");
struct address_space {
// TODO
};
extern struct page_table mem_kernel_table;
// Base address of the `struct page` array.
extern struct page* mem_pfndb;
// Base address of the HHDM.
extern virt_t mem_hhdm_base;
#define HHDM_PTR(paddr) (void*)((paddr) + mem_hhdm_base)
void mem_init(struct phys_mem* map, size_t length, virt_t kernel_virt, phys_t kernel_phys, virt_t tmp_hhdm);
void mem_phys_bootstrap(struct phys_mem* mem);
void mem_phys_init(struct phys_mem* map, size_t length);
// Allocates a region of memory which can be smaller than the page size.
// Returns `nullptr` if the allocator cannot provide an allocation for the
// given `length` + `flags`. Always returns `nullptr` if `length` is 0.
zn_status_t mem_alloc(size_t length, enum alloc_flags flags, void** out);
// Frees an allocation created by `mem_alloc`.
// Passing `nullptr` is a no-op.
zn_status_t mem_free(void* mem);
// Allocates an amount of contiguous physical pages.
zn_status_t mem_phys_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out);
// Frees an allocated region of physical pages.
zn_status_t mem_phys_free(phys_t start, size_t num_pages);
// Creates a new page table for the kernel.
zn_status_t mem_pt_new_kernel(struct page_table* pt, enum alloc_flags flags);
// Creates a new page table for a user process.
zn_status_t mem_pt_new_user(struct page_table* pt, enum alloc_flags flags);
// Sets a page table on the current processor.
static inline void mem_pt_set(struct page_table* pt) {
arch_mem_pt_set(pt);
}
// Maps a single page to a virtual address in the given page table.
zn_status_t mem_pt_map(struct page_table* pt, virt_t vaddr, phys_t paddr, enum pte_flags flags, enum cache_mode cache);
// Changes the protection of a page.
zn_status_t mem_pt_protect(struct page_table* pt, virt_t vaddr, enum pte_flags flags);
// Unmaps a page.
zn_status_t mem_pt_unmap(struct page_table* pt, virt_t vaddr);
// Base address used to access physical memory.
static inline virt_t mem_hhdm_addr() {
return arch_mem_hhdm_addr();
}
// Base address used to access the page array.
static inline virt_t mem_pfndb_addr() {
return arch_mem_pfndb_addr();
}
// Base address used to access kernel memory mappings.
static inline virt_t mem_mapping_addr() {
return arch_mem_mapping_addr();
}
// Bit shift in a page.
static inline size_t mem_page_bits() {
return arch_mem_page_bits();
}
// Gets the page size in bytes.
static inline size_t mem_page_size() {
return 1 << mem_page_bits();
}
// The amount of bits in a level.
static inline size_t mem_level_bits() {
return arch_mem_level_bits();
}
// The amount of levels in an address.
static inline size_t mem_num_levels() {
return arch_mem_num_levels();
}
// Clears out a page table entry.
static inline void mem_pte_clear(pte_t* pte) {
return arch_mem_pte_clear(pte);
}
// Builds a page table entry from the given info.
static inline pte_t mem_pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache) {
return arch_mem_pte_build(addr, flags, cache);
}
// Returns true if the given PTE is present and valid.
static inline bool mem_pte_is_present(pte_t* pte) {
return arch_mem_pte_is_present(pte);
}
// Returns true if the given PTE contains another level.
static inline bool mem_pte_is_dir(pte_t* pte) {
return arch_mem_pte_is_dir(pte);
}
// Returns the address component of the page table entry.
static inline phys_t mem_pte_address(pte_t* pte) {
return arch_mem_pte_address(pte);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <kernel/sched.h>
struct mutex {
struct task* owner;
bool flag;
};
void mutex_lock(struct mutex* mutex);
void mutex_unlock(struct mutex* mutex);
+5
View File
@@ -0,0 +1,5 @@
#pragma once
// Stop all execution upon panic.
[[noreturn]]
void panic();
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <bits/percpu.h>
#include <kernel/compiler.h>
#include <kernel/irq.h>
#include <kernel/sched.h>
#include <stddef.h>
ASSERT_TYPE(struct arch_percpu);
// CPU-relative information.
struct percpu {
struct percpu* self; // A pointer to this structure.
size_t id; // The virtual ID of this CPU.
bool online; // Whether this CPU is initialized and active.
virt_t kernel_stack; // The kernel mode stack.
virt_t user_stack; // The user mode stack.
struct arch_percpu arch; // Architecture-specific fields.
struct irq_percpu irq; // IRQ information.
struct sched_percpu sched; // Scheduler information.
};
// Per-CPU data for the bootstrap processor.
extern struct percpu percpu_bsp;
// Gets the per-CPU data on the current CPU.
static inline struct percpu* percpu_get() {
return arch_percpu_get();
}
// Allocates a block of memory for a new CPU.
struct percpu* percpu_new();
// Initializes the bootstrap processor.
void percpu_bsp_early_init();
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <kernel/compiler.h>
[[__format(printf, 1, 2)]]
void kprintf(const char* message, ...);
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <bits/sched.h>
#include <kernel/mem.h>
#include <kernel/types.h>
#include <stddef.h>
#include <stdint.h>
ASSERT_TYPE(struct arch_context);
ASSERT_TYPE(struct arch_task_context);
typedef size_t tid_t;
enum task_state {
TASK_STATE_RUNNING, // Task is active and running.
TASK_STATE_READY, // Task is active, but not currently scheduled.
TASK_STATE_BLOCKED, // Task is waiting on another object.
};
// A task is the smallest unit of the scheduler.
struct task {
tid_t id;
struct process* process;
enum task_state state;
struct arch_context context;
virt_t kernel_stack;
virt_t user_stack;
size_t time_slice;
int8_t priority;
};
// Per-CPU data for scheduling.
struct sched_percpu {
struct task* current;
};
void sched_init();
+13
View File
@@ -0,0 +1,13 @@
#pragma once
// Busy-waits in a loop until the lock is freed.
// Does not put the CPU to sleep.
struct spinlock {
bool locked;
};
// Attempts to lock a spinlock. If it's already locked, waits until it's freed.
void spin_lock(struct spinlock* spin);
// Unlocks a spinlock.
void spin_unlock(struct spinlock* spin);
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <zinnia/status.h>
#include <kernel/compiler.h>
#include <kernel/types.h>
#include <stddef.h>
#include <stdint.h>
zn_status_t syscall_dispatch(reg_t num, reg_t a0, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6);
[[noreturn]]
void syscall_panic(zn_status_t err);
zn_status_t syscall_log(__user const char* msg, size_t len);
zn_status_t syscall_archctl(uint32_t op, size_t value);
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include <bits/types.h>
#include <kernel/common.h>
ASSERT_TYPE(phys_t);
ASSERT_TYPE(virt_t);
ASSERT_TYPE(reg_t);
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <uapi/uio.h>
struct iovec_iter {};
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <bits/usercopy.h>
#include <kernel/compiler.h>
#include <stddef.h>
#include <stdint.h>
struct usercopy_region {
void (*start_ip)();
void (*end_ip)();
void (*fault_ip)();
};
// Copies a block of data from user to kernel memory.
bool usercopy_read(uint8_t* dst, const __user uint8_t* src, size_t len) {
return arch_usercopy_read(dst, src, len);
}
// Copies a block of data from kernel to user memory.
bool usercopy_write(__user uint8_t* dst, const uint8_t* src, size_t len) {
return arch_usercopy_write(dst, src, len);
}
// Performs a strlen() on a user string.
bool usercopy_strlen(const __user uint8_t* str, size_t max, size_t* len) {
return arch_usercopy_strlen(str, max, len);
}
+85
View File
@@ -0,0 +1,85 @@
#pragma once
#include <kernel/mem.h>
#include <stddef.h>
#include <string.h>
#define VEC(type) \
struct { \
type* items; \
size_t capacity; \
size_t length; \
}
// Initializes a vector that can hold `cap` entries.
#define VEC_INIT(vec, cap) \
do { \
*(vec) = (typeof(*(vec))){ \
.items = nullptr, \
.capacity = (cap), \
.length = 0, \
}; \
} while (0)
// Frees the memory associated with the `vec`.
#define VEC_FREE(vec) \
do { \
kfree((vec)->items); \
(vec)->items = NULL; \
(vec)->capacity = 0; \
(vec)->length = 0; \
} while (0)
// Pushes a new `item` to the `vec`.
#define VEC_PUSH(vec, item) \
do { \
auto __vec = (vec); \
/* Default capacity. */ \
if (__vec->capacity == 0) \
__vec->capacity = 16; \
/* If no container was allocated, do it now.*/ \
if (__vec->items == nullptr) { \
__vec->items = kmalloc(sizeof(typeof(*(__vec->items))) * __vec->capacity, KMF_ZEROED); \
__vec->length = 0; \
} \
/* If the new entry would overflow the buffer, double the capacity. */ \
if (__vec->length + 1 > __vec->capacity) { \
__vec->capacity *= 2; \
__vec->items = krealloc(__vec->items, sizeof(typeof(*(__vec->items))) * __vec->capacity); \
} \
__vec->items[__vec->length] = (item); \
__vec->length++; \
} while (0)
// Removes the element at index `idx` and moves all other members so the vec is contiguous again.
#define VEC_POP(vec, idx) \
do { \
auto __vec = (vec); \
if ((idx) >= __vec->length) \
break; \
/* Move all data back by one entry. */ \
memmove( \
__vec->items + (idx), \
__vec->items + (idx) + 1, \
sizeof(typeof(*(__vec->items))) * (__vec->length - (idx)) \
); \
__vec->length--; \
} while (0)
// Iterates over `vec` with iterator variable `var_name`.
#define VEC_ITER(vec, var_name) \
for (typeof(((vec)->items)) var_name = (vec)->items + 0; (var_name) < (vec)->items + (vec)->length; var_name++)
// Looks for a `value` in the `vec` and returns the index of the first match in `result`.
// If unsuccessful, returns -1.
#define VEC_FIND(vec, result, value) \
do { \
result = -1; \
for (usize __i = 0; __i < (vec)->length; __i++) { \
if ((vec)->items[__i] == value) { \
result = __i; \
break; \
} \
} \
true; \
} while (0)
+10
View File
@@ -0,0 +1,10 @@
#ifndef ZINNIA_STDARG_H
#define ZINNIA_STDARG_H
typedef __builtin_va_list va_list;
#define va_start(v, l) __builtin_va_start(v, l)
#define va_end(v) __builtin_va_end(v)
#define va_arg(v, T) __builtin_va_arg(v, T)
#define va_copy(d, s) __builtin_va_copy(d, s)
#endif
+37
View File
@@ -0,0 +1,37 @@
#ifndef ZINNIA_STDATOMIC_H
#define ZINNIA_STDATOMIC_H
typedef enum memory_order {
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST
} memory_order;
#define atomic_store_explicit(ptr, des, ord) __atomic_store_n(ptr, des, ord)
#define atomic_store(ptr, des) atomic_store_explicit(ptr, des, __ATOMIC_SEQ_CST)
#define atomic_load_explicit(ptr, ord) __atomic_load_n(ptr, ord)
#define atomic_load(ptr) atomic_load_explicit(ptr, __ATOMIC_SEQ_CST)
#define atomic_exchange_explicit(ptr, des, ord) __atomic_exchange_n(ptr, des, ord)
#define atomic_exchange(ptr, des) atomic_exchange_explicit(ptr, des, __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit(ptr, op, ord) __atomic_fetch_add(ptr, op, ord)
#define atomic_fetch_add(ptr, op) atomic_fetch_add_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit(ptr, op, ord) __atomic_fetch_sub(ptr, op, ord)
#define atomic_fetch_sub(ptr, op) atomic_fetch_sub_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit(ptr, op, ord) __atomic_fetch_or(ptr, op, ord)
#define atomic_fetch_or(ptr, op) atomic_fetch_or_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit(ptr, op, ord) __atomic_fetch_xor(ptr, op, ord)
#define atomic_fetch_xor(ptr, op) atomic_fetch_xor_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit(ptr, op, ord) __atomic_fetch_and(ptr, op, ord)
#define atomic_fetch_and(ptr, op) atomic_fetch_and_explicit(ptr, op, __ATOMIC_SEQ_CST)
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef ZINNIA_STDDEF_H
#define ZINNIA_STDDEF_H
typedef unsigned long size_t;
typedef signed long ssize_t;
#define offsetof(type, member) __builtin_offsetof(type, member)
#endif
+29
View File
@@ -0,0 +1,29 @@
#ifndef ZINNIA_STDINT_H
#define ZINNIA_STDINT_H
typedef __INT8_TYPE__ int8_t;
typedef __UINT8_TYPE__ uint8_t;
#define INT8_MAX __INT8_MAX__
#define UINT8_MAX __UINT8_MAX__
typedef __INT16_TYPE__ int16_t;
typedef __UINT16_TYPE__ uint16_t;
#define INT16_MAX __INT16_MAX__
#define UINT16_MAX __UINT16_MAX__
typedef __INT32_TYPE__ int32_t;
typedef __UINT32_TYPE__ uint32_t;
#define INT32_MAX __INT32_MAX__
#define UINT32_MAX __UINT32_MAX__
typedef __INT64_TYPE__ int64_t;
typedef __UINT64_TYPE__ uint64_t;
#define INT64_MAX __INT64_MAX__
#define UINT64_MAX __UINT64_MAX__
typedef __INTPTR_TYPE__ intptr_t;
typedef __UINTPTR_TYPE__ uintptr_t;
#define INTPTR_MAX __INTPTR_MAX__
#define UINTPTR_MAX __UINTPTR_MAX__
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef ZINNIA_STRING_H
#define ZINNIA_STRING_H
#include <stddef.h>
size_t strlen(const char* str);
size_t strnlen(const char* str, size_t len);
int memcmp(const void* s1, const void* s2, size_t size);
void* memcpy(void* restrict dest, const void* restrict src, size_t n);
void* memmove(void* dstptr, const void* srcptr, size_t size);
void* memset(void* dest, int value, size_t n);
char* strncpy(char* restrict dst, const char* restrict src, size_t len);
int strcmp(const char* str1, const char* str2);
int strncmp(const char* str1, const char* str2, size_t len);
char* strchr(const char* c, int s);
#endif
+105
View File
@@ -0,0 +1,105 @@
ENTRY(_start)
PHDRS {
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(6);
percpu PT_LOAD FLAGS(6);
}
SECTIONS
{
. = 0xffffffff80000000;
__ld_kernel_start = .;
.text ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_text_start = .;
KEEP(*(.init.text))
*(.text .text.*)
*(.plt .plt.*)
__ld_text_end = .;
} :text
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_rodata_start = .;
__ld_init_ctors_start = .;
KEEP(*(.initgraph.ctors))
__ld_init_ctors_end = .;
__ld_cmdline_start = .;
KEEP(*(.cmdline))
__ld_cmdline_end = .;
*(.rodata .rodata.*)
*(.got .got.*)
__ld_rodata_end = .;
} :rodata
.data ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_data_start = .;
__ld_init_start = .;
*(.initgraph.nodes)
__ld_init_end = .;
__ld_console_start = .;
KEEP(*(.console))
__ld_console_end = .;
KEEP(*(SORT_BY_NAME(.init.data.*)))
KEEP(*(.init.data))
*(.data .data.*)
} :data
.dynsym : {
__ld_dynsym_start = .;
*(.dynsym)
__ld_dynsym_end = .;
} :data
.dynstr : {
__ld_dynstr_start = .;
*(.dynstr)
__ld_dynstr_end = .;
} :data
.dynamic : {
*(.dynamic)
} :data :dynamic
.bss : {
/* Small stack to bootstrap the allocator */
__ld_stack_bottom = .;
. += 0x2000;
__ld_stack_top = .;
*(.*bss .*bss.*)
*(COMMON)
} :data
/* Per-CPU data block. MUST always come last. */
.percpu ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_percpu_start = .;
KEEP(*(.percpu))
. = ALIGN(CONSTANT(MAXPAGESIZE));
__ld_percpu_end = .;
__ld_data_end = .;
} :percpu
__ld_kernel_end = .;
/DISCARD/ :
{
*(.comment*)
*(.eh_frame*)
*(.rel.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.interp)
}
}
+3
View File
@@ -0,0 +1,3 @@
kernel_sources += files(
'string.c',
)
+164
View File
@@ -0,0 +1,164 @@
#include <kernel/common.h>
#include <kernel/compiler.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
[[__hot]]
size_t strlen(const char* str) {
size_t result = 0;
while (*str++) {
result++;
}
return result;
}
[[__hot]]
size_t strnlen(const char* str, size_t len) {
size_t result = 0;
while (result < len && *str++) {
result++;
}
return result;
}
[[__hot]]
int memcmp(const void* s1, const void* s2, size_t size) {
int diff = 0;
char* s1ptr = (char*)s1;
char* s2ptr = (char*)s2;
for (size_t i = 0; i < size; i++) {
if (s1ptr[i] != s2ptr[i])
diff++;
}
return diff;
}
[[__hot]]
void* memcpy(void* restrict dest, const void* restrict src, size_t n) {
if (__unlikely(n == 0))
return dest;
size_t d = (size_t)dest;
size_t s = (size_t)src;
if (d % sizeof(size_t) != 0 || s % sizeof(size_t) != 0) {
while (n && (d % sizeof(size_t) != 0) && (s % sizeof(size_t) != 0)) {
*((uint8_t*)d) = *((uint8_t*)s);
d++;
s++;
n--;
}
}
size_t* qword_dest = (size_t*)d;
const size_t* qword_src = (const size_t*)s;
const size_t word_count = n / sizeof(size_t);
for (size_t i = 0; i < word_count; i++) {
qword_dest[i] = qword_src[i];
}
size_t remaining_bytes = n % sizeof(size_t);
d = (size_t)(qword_dest + word_count);
s = (size_t)(qword_src + word_count);
while (remaining_bytes--) {
*((uint8_t*)d) = *((uint8_t*)s);
d++;
s++;
}
return dest;
}
[[__hot]]
void* memmove(void* dstptr, const void* srcptr, size_t size) {
uint8_t* dst = (uint8_t*)dstptr;
const uint8_t* src = (const uint8_t*)srcptr;
if (dst < src) {
for (size_t i = 0; i < size; i++)
dst[i] = src[i];
} else {
for (size_t i = size; i != 0; i--)
dst[i - 1] = src[i - 1];
}
return dstptr;
}
[[__hot]]
void* memset(void* dest, int value, size_t n) {
if (__unlikely(n == 0))
return dest;
size_t d = (size_t)dest;
if (d % sizeof(size_t) != 0) {
while (n && (d % sizeof(size_t) != 0)) {
*((uint8_t*)d) = value;
d++;
n--;
}
}
size_t* qword_dest = (size_t*)d;
size_t qword_value = value;
qword_value |= (qword_value << 8);
qword_value |= (qword_value << 16);
qword_value |= (qword_value << 32);
const size_t word_count = n / sizeof(size_t);
for (size_t i = 0; i < word_count; i++) {
qword_dest[i] = qword_value;
}
size_t remaining_bytes = n % sizeof(size_t);
d = (size_t)(qword_dest + word_count);
while (remaining_bytes--) {
*((uint8_t*)d) = value;
d++;
}
return dest;
}
char* strncpy(char* restrict dst, const char* restrict src, size_t len) {
size_t src_len = strnlen(src, len) + 1;
return memcpy(dst, src, MIN(len, src_len));
}
int strncmp(const char* str1, const char* str2, size_t len) {
while (len && *str1 && (*str1 == *str2)) {
++str1;
++str2;
--len;
}
if (len == 0) {
return 0;
} else {
return (*(unsigned char*)str1 - *(unsigned char*)str2);
}
}
int strcmp(const char* str1, const char* str2) {
while (*str1 && (*str1 == *str2)) {
++str1;
++str2;
}
return (*(unsigned char*)str1 - *(unsigned char*)str2);
}
char* strchr(const char* s, int c) {
char ch = (char)c;
while (true) {
char cur = *s;
if (cur == ch)
return (char*)s;
if (cur == 0)
return nullptr;
s++;
}
}
+147
View File
@@ -0,0 +1,147 @@
#include <kernel/assert.h>
#include <kernel/common.h>
#include <kernel/mem.h>
#include <kernel/panic.h>
#include <kernel/print.h>
#include <string.h>
extern uint8_t __ld_text_start[];
extern uint8_t __ld_text_end[];
extern uint8_t __ld_rodata_start[];
extern uint8_t __ld_rodata_end[];
extern uint8_t __ld_data_start[];
extern uint8_t __ld_data_end[];
extern uint8_t __ld_kernel_start[];
struct page_table mem_kernel_table = {0};
void mem_init(struct phys_mem* map, size_t map_len, virt_t kernel_virt, phys_t kernel_phys, virt_t tmp_hhdm) {
// This function creates a kernel page table and initializes all memory managers.
const size_t pgsz = mem_page_size();
kprintf("Memory map:\n");
for (size_t i = 0; i < map_len; i++) {
if (map[i].length == 0)
continue;
const char* label = nullptr;
switch (map[i].usage) {
case PHYS_RESERVED:
label = "Reserved";
break;
case PHYS_USABLE:
label = "Usable";
break;
case PHYS_RECLAIMABLE:
label = "Reclaimable";
break;
}
kprintf("[%p - %p] %s\n", (void*)map[i].address, (void*)(map[i].address + map[i].length - 1), label);
}
// Set up the bootstrap allocator.
struct phys_mem* largest = map;
for (size_t i = 0; i < map_len; i++) {
if (map[i].length > largest->length && map[i].usage == PHYS_USABLE)
largest = &map[i];
}
mem_phys_bootstrap(largest);
kprintf(
"Using region [%p - %p] for bootstrap allocator\n",
(void*)largest->address,
(void*)(largest->address + largest->length - 1)
);
// Set the HHDM base address to the address given by the loader.
// We must not keep any virtual addresses to this region,
// since we're likely going to map it at a different base address.
mem_hhdm_base = tmp_hhdm;
ASSERT(mem_pt_new_kernel(&mem_kernel_table, 0) == 0, "Unable to allocate the kernel page table");
// text
kprintf("Mapping text segment at %p\n", __ld_text_start);
for (uint8_t* p = __ld_text_start; p <= __ld_text_end; p += pgsz) {
zn_status_t status = mem_pt_map(
&mem_kernel_table,
(virt_t)p,
(phys_t)(p - __ld_kernel_start + kernel_phys),
PTE_READ | PTE_EXEC,
CACHE_NONE
);
ASSERT(!status, "Failed to map %p with error %i", p, status);
}
// rodata
kprintf("Mapping rodata segment at %p\n", __ld_rodata_start);
for (uint8_t* p = __ld_rodata_start; p < __ld_rodata_end; p += pgsz) {
zn_status_t status = mem_pt_map(
&mem_kernel_table,
(virt_t)p,
(phys_t)(p - __ld_kernel_start + kernel_phys),
PTE_READ,
CACHE_NONE
);
ASSERT(!status, "Failed to map %p with error %i", p, status);
}
// data
kprintf("Mapping data segment at %p\n", __ld_data_start);
for (uint8_t* p = __ld_data_start; p < __ld_data_end; p += pgsz) {
zn_status_t status = mem_pt_map(
&mem_kernel_table,
(virt_t)p,
(phys_t)(p - __ld_kernel_start + kernel_phys),
PTE_READ | PTE_WRITE,
CACHE_NONE
);
ASSERT(!status, "Failed to map %p with error %i", p, status);
}
// The kernel address space is divided into 3 segments (plus kernel).
// On x86_64 for example, they live at:
// HHDM FFFF'8000'0000'0000
// PFNDB FFFF'A000'0000'0000
// Mappings FFFF'C000'0000'0000
// Map all physical memory to the HHDM address.
tmp_hhdm = mem_hhdm_addr();
for (size_t i = 0; i < map_len; i++) {
for (size_t p = 0; p <= map[i].length; p += pgsz) {
virt_t vaddr = (virt_t)(map[i].address + p + tmp_hhdm);
phys_t paddr = (phys_t)(map[i].address + p);
zn_status_t status = mem_pt_map(&mem_kernel_table, vaddr, paddr, PTE_READ | PTE_WRITE, CACHE_NONE);
ASSERT(!status, "Failed to map HHDM page %p to %p with error %i", (void*)vaddr, (void*)paddr, status);
}
}
mem_hhdm_base = tmp_hhdm;
// Switch to our own page table.
mem_pt_set(&mem_kernel_table);
// We record metadata for every single page of available memory in a large array.
// This array is contiguous in virtual memory, but is sparsely populated.
// Only those array entries which represent usable memory are mapped.
for (size_t i = 0; i < map_len; i++) {
if (map[i].length == 0 || map[i].usage != PHYS_USABLE)
continue;
size_t length = ALIGN_UP((map[i].length / pgsz) * sizeof(struct page), pgsz);
virt_t vaddr = ALIGN_DOWN(mem_pfndb_addr() + (map[i].address / pgsz * sizeof(struct page)), pgsz);
for (size_t page = 0; page < length; page += pgsz) {
phys_t paddr;
ASSERT(!mem_phys_alloc(1, 0, &paddr), "Failed to allocate memory for PFNDB!");
mem_pt_map(&mem_kernel_table, vaddr + page, paddr, PTE_READ | PTE_WRITE, CACHE_NONE);
}
}
mem_pfndb = (struct page*)mem_pfndb_addr();
// We don't need the bootstrap allocator from this point on.
// Initialize the real page allocator.
mem_phys_init(map, map_len);
kprintf("Memory initialization complete\n");
}
+7
View File
@@ -0,0 +1,7 @@
kernel_sources += files(
'init.c',
'pmm.c',
'pt.c',
'slab.c',
'vmm.c',
)
+155
View File
@@ -0,0 +1,155 @@
#include <kernel/assert.h>
#include <kernel/compiler.h>
#include <kernel/mem.h>
#include <kernel/print.h>
#include <kernel/spin.h>
#include <stdint.h>
#include <string.h>
virt_t mem_hhdm_base = 0;
struct page* mem_pfndb = nullptr;
static zn_status_t (*alloc_fn)(size_t num_pages, enum alloc_flags flags, phys_t* out);
static zn_status_t (*free_fn)(phys_t start, size_t num_pages);
zn_status_t mem_phys_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out) {
return alloc_fn(num_pages, flags, out);
}
zn_status_t mem_phys_free(phys_t start, size_t num_pages) {
return free_fn(start, num_pages);
}
static struct phys_mem bump_mem = {0};
static struct phys_mem* bump_region = nullptr;
static zn_status_t bump_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out) {
const size_t bytes = num_pages * mem_page_size();
if (((intptr_t)bump_mem.length - bytes) <= 0)
return ZN_ERR_NO_MEMORY;
*out = bump_mem.address;
bump_mem.length -= bytes;
bump_mem.address += bytes;
if (!(flags & ALLOC_NOZERO)) {
memset(HHDM_PTR(*out), 0, bytes);
}
return 0;
}
static zn_status_t bump_free(phys_t addr, size_t num_pages) {
// We don't free bootstrap memory.
ASSERT(false, "Attempted to free bootstrap memory!");
}
void mem_phys_bootstrap(struct phys_mem* mem) {
alloc_fn = bump_alloc;
free_fn = bump_free;
// Save the region by reference and value.
// We can't directly modify the region by reference, because that
// interferes with e.g. HHDM mapping logic.
bump_mem = *mem;
bump_region = mem;
}
static struct page* pmm_head = nullptr;
static struct spinlock pmm_lock = {0};
static size_t pmm_total_free = 0;
static zn_status_t freelist_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out) {
spin_lock(&pmm_lock);
const size_t bytes = num_pages * mem_page_size();
phys_t limit = UINTPTR_MAX;
if (flags & ALLOC_MEM20) {
limit = (phys_t)1 << 20;
} else if (flags & ALLOC_MEM32) {
limit = (phys_t)1 << 32;
}
struct page* it = pmm_head;
struct page* prev_it = nullptr;
while (it) {
const phys_t page_addr = (((uintptr_t)it - (uintptr_t)mem_pfndb) / sizeof(struct page)) * mem_page_size();
// If the region is too high up or doesn't have enough pages left, continue searching.
if (page_addr + bytes >= limit || __unlikely(it->freelist.count < num_pages)) {
prev_it = it;
it = it->freelist.next;
continue;
}
// If the current block has exactly enough pages left, consume it entirely.
if (__unlikely(it->freelist.count == num_pages)) {
if (prev_it) {
prev_it->freelist.next = it->freelist.next;
} else {
pmm_head = it->freelist.next;
}
it->freelist.next = nullptr;
it->freelist.count = 0;
if (__likely(out))
*out = page_addr;
}
// If not, we can just shrink the block.
else {
it->freelist.count -= num_pages;
if (__likely(out))
*out = page_addr + (it->freelist.count * mem_page_size());
}
goto success;
}
spin_unlock(&pmm_lock);
return ZN_ERR_NO_MEMORY;
success:
// If the NOZERO flag is *not* specified, zero memory.
if (__likely(out) && __likely(!(flags & ALLOC_NOZERO))) {
memcpy(HHDM_PTR(*out), 0, bytes);
}
pmm_total_free -= num_pages;
spin_unlock(&pmm_lock);
return 0;
}
static zn_status_t freelist_free(phys_t addr, size_t num_pages) {
spin_lock(&pmm_lock);
struct page* page = &mem_pfndb[addr / mem_page_size()];
page->freelist.count = num_pages;
page->freelist.next = pmm_head;
pmm_head = page;
spin_unlock(&pmm_lock);
return 0;
}
void mem_phys_init(struct phys_mem* map, size_t length) {
alloc_fn = freelist_alloc;
free_fn = freelist_free;
bump_region->address = bump_mem.address;
bump_region->length = bump_mem.length;
for (size_t i = 0; i < length; i++) {
// Regions smaller than a page are useless.
if (map[i].length < mem_page_size() || map[i].usage != PHYS_USABLE)
continue;
struct page* page = &mem_pfndb[map[i].address / mem_page_size()];
page->freelist.count = map[i].length / mem_page_size();
page->freelist.next = pmm_head;
pmm_head = page;
pmm_total_free += page->freelist.count;
}
const size_t free_bytes = (pmm_total_free * mem_page_size());
kprintf("Total available memory: %zu MiB\n", free_bytes / 1024 / 1024);
}
+82
View File
@@ -0,0 +1,82 @@
#include <kernel/assert.h>
#include <kernel/mem.h>
#include <kernel/print.h>
#include <kernel/spin.h>
zn_status_t mem_pt_new_kernel(struct page_table* pt, enum alloc_flags flags) {
flags &= ~ALLOC_NOZERO;
phys_t addr;
zn_status_t status = mem_phys_alloc(1, flags, &addr);
if (status)
return status;
pt->root = addr;
return 0;
}
zn_status_t mem_pt_new_user(struct page_table* pt, enum alloc_flags flags) {
// TODO
return 0;
}
// Gets a reference to the PTE at the given virtual address.
// If `check_only` is set, only checks if the PTE exists,
// and doesn't allocate new levels if they don't already exist.
// If it can't allocate a page if it has to, returns `nullptr`.
static zn_status_t get_pte(struct page_table* pt, virt_t vaddr, bool is_user, bool check_only, pte_t** ret) {
pte_t* current_head = HHDM_PTR(pt->root);
size_t index = 0;
for (int8_t level = mem_num_levels() - 1; level >= 0; level--) {
const size_t addr_mask = (1 << mem_level_bits()) - 1;
const size_t addr_shift = mem_page_bits() + (mem_level_bits() * level);
const enum pte_flags level_flags = PTE_DIR | (is_user ? PTE_USER : 0);
index = (vaddr >> addr_shift) & addr_mask;
// The last level is used to access the actual PTE, so break the loop then.
// We still need to update the index beforehand, that's why we can't just end early.
if (level == 0)
break;
pte_t* pte = &current_head[index];
if (mem_pte_is_present(pte)) {
// Get the next level.
*pte = mem_pte_build(mem_pte_address(pte), level_flags, CACHE_NONE);
current_head = HHDM_PTR(mem_pte_address(pte));
} else {
// If the current level isn't present, we can skip the rest.
if (check_only)
return ZN_ERR_NO_MEMORY;
phys_t addr;
zn_status_t alloc_status = mem_phys_alloc(1, 0, &addr);
if (alloc_status)
return alloc_status;
*pte = mem_pte_build(addr, level_flags, CACHE_NONE);
current_head = HHDM_PTR(addr);
}
}
*ret = &current_head[index];
return 0;
}
zn_status_t mem_pt_map(struct page_table* pt, virt_t vaddr, phys_t paddr, enum pte_flags flags, enum cache_mode cache) {
spin_lock(&pt->lock);
pte_t* pte;
zn_status_t status = get_pte(pt, vaddr, flags & PTE_USER, false, &pte);
if (status) {
goto fail;
}
*pte = mem_pte_build(paddr, flags, cache);
fail:
spin_unlock(&pt->lock);
return status;
}
+1
View File
@@ -0,0 +1 @@
#include <kernel/mem.h>
View File
+45
View File
@@ -0,0 +1,45 @@
kernel_c_args = [
'-ffreestanding',
'-fno-stack-protector',
'-fno-omit-frame-pointer',
'-fno-pie',
'-nostdinc',
'-D__KERNEL__=1',
]
kernel_lds = meson.current_source_dir() / 'kernel.ld'
kernel_link_args = [
'-ffreestanding',
'-nostdlib',
'-Wl,-nostdlib',
'-static',
'-T' + kernel_lds,
]
kernel_includes = include_directories(
'arch' / target_machine.cpu_family() / 'include',
'include',
)
kernel_sources = []
subdir('arch' / target_machine.cpu_family())
subdir('boot')
subdir('klibc')
subdir('mem')
subdir('core')
subdir('syscalls')
configure_file(configuration: common_cfg, output: 'config.h')
executable(
'zinnia',
include_directories: [common_includes, kernel_includes],
c_args: kernel_c_args,
link_args: kernel_link_args,
link_depends: kernel_lds,
sources: kernel_sources,
install: true,
install_dir: get_option('datadir') / 'zinnia',
)
+21
View File
@@ -0,0 +1,21 @@
#include <zinnia/channel.h>
#include <zinnia/status.h>
#include <kernel/compiler.h>
zn_status_t syscall_channel_create(
enum zn_channel_flags flags,
__user zn_handle_t* endpoint0,
__user zn_handle_t* endpoint1
) {
return ZN_ERR_UNSUPPORTED;
}
zn_status_t syscall_channel_open(
zn_handle_t channel,
size_t num_handles,
size_t num_bytes,
__user zn_handle_t** out_handle_buf,
__user void** out_data_buf
) {
return ZN_ERR_UNSUPPORTED;
}
+18
View File
@@ -0,0 +1,18 @@
#include <zinnia/status.h>
#include <zinnia/syscall_numbers.h>
#include <kernel/compiler.h>
#include <kernel/syscalls.h>
#include <kernel/types.h>
zn_status_t syscall_dispatch(reg_t num, reg_t a0, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6) {
switch (num) {
case ZN_SYSCALL_PANIC:
syscall_panic((zn_status_t)a0);
case ZN_SYSCALL_LOG:
return syscall_log((__user const char*)a0, a1);
case ZN_SYSCALL_ARCHCTL:
return syscall_archctl(a0, a1);
}
return ZN_ERR_BAD_SYSCALL;
}
+22
View File
@@ -0,0 +1,22 @@
#include <zinnia/handle.h>
#include <zinnia/status.h>
#include <kernel/percpu.h>
#include <kernel/syscalls.h>
zn_status_t zn_handle_clone(zn_handle_t handle, zn_rights_t cloned_rights, zn_handle_t* cloned) {
struct task* current = percpu_get()->sched.current;
current->process;
return ZN_ERR_UNSUPPORTED;
}
zn_status_t zn_handle_drop(zn_handle_t handle) {
struct task* current = percpu_get()->sched.current;
return ZN_ERR_UNSUPPORTED;
}
zn_status_t zn_handle_validate(zn_handle_t handle) {
struct task* current = percpu_get()->sched.current;
return ZN_ERR_UNSUPPORTED;
}
View File
+6
View File
@@ -0,0 +1,6 @@
kernel_sources += files(
'channel.c',
'dispatch.c',
'handle.c',
'log.c',
)