wip: IPC and Runtime

This commit is contained in:
karina
2026-05-03 21:57:20 +04:00
parent 5f343c991b
commit 39b2af7626
25 changed files with 371 additions and 66 deletions
+1
View File
@@ -6,3 +6,4 @@ compile_commands.json
ide-swift-toolchain.txt
.cache
.zed
Runtime/.clangd
+14 -4
View File
@@ -2,6 +2,7 @@
// Copyright (c) 2026 0xKSor
#pragma once
#include "Arch/Exceptions.h"
#include <Types.h>
static inline void CPUYield() {
@@ -63,7 +64,7 @@ static inline void CPUEnableMMU(Address l0PhysicalAddress) {
// MAIR_EL1 (Memory Attribute Indirection Register)
// kPTENormalMem is index 0 and kPTEDeviceMem is index 1
// 0xFF = Normal, 0x00 = Device
UInt64 mair = (0xFFULL << 0) | (0x00ULL << 8);
UInt64 mair = (0xFFULL << 0) | (0x00ULL << 8);
// TCR_EL1 (Translation Control Register)
// configures the mmu for 4kb pages and 48bit virtual addresses
@@ -82,11 +83,11 @@ static inline void CPUEnableMMU(Address l0PhysicalAddress) {
"msr tcr_el1, %1\n"
"msr ttbr0_el1, %2\n" // set userspace root
"msr ttbr1_el1, %2\n" // set kernelspace root
"tlbi vmalle1is\n"
"tlbi vmalle1is\n"
"isb\n" // Instruction Synchronization Barrier
:: "r"(mair), "r"(tcr), "r"(l0PhysicalAddress) : "memory"
);
// turn on the MMU in SCTLR_EL1 (System Control Register)
// Bit 0 = M (MMU Enable), Bit 2 = C (Data Cache Enable), Bit 12 = I (Instruction Cache Enable)
UInt64 sctlr;
@@ -111,4 +112,13 @@ static inline void CPUSwitchAddressSpace(Address l0Physical) {
);
}
#define CPUException(number) __asm__ volatile ("svc %0" :: "i" (number) : "memory")
static inline void CPUCopyIPCRegisters(ExceptionsContext* source, ExceptionsContext* destination) {
destination->x2 = source->x2;
destination->x3 = source->x3;
destination->x4 = source->x4;
destination->x5 = source->x5;
destination->x6 = source->x6;
destination->x7 = source->x7;
}
#define CPUException(number) __asm__ volatile ("svc %0" :: "i" (number) : "memory")
+1 -1
View File
@@ -14,4 +14,4 @@ static inline UInt32 IOAddressRead32(Address address) {
UInt32 value = *(volatile UInt32*)address;
__asm__ volatile ("dsb ld" ::: "memory"); // wait till my read is finished physically
return value;
}
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "Arch/Exceptions.h"
#include <Types.h>
#include <OS/Scheduler.h>
UInt64 IPCSend(OSTask* sender, UInt64 handleID, UInt64 data);
void IPCReceive(ExceptionsContext* receiverFrame);
+2 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <Types.h>
#include "../Common/bootinfo.h"
#include <OS/Scheduler.h>
void ModuleLoad(BootModule* module);
OSTask* ModuleLoad(BootModule* module);
+28 -6
View File
@@ -4,12 +4,31 @@
#pragma once
#include <Types.h>
typedef enum {
kOSHandleTypeNone = 0,
kOSHandleTypeTask
} OSHandleType;
typedef struct {
OSHandleType type;
Pointer object;
} OSHandle;
enum {
kOSSchedulerTaskStackSize = 16 * 1024,
kOSSchedulerExceptionNumber = 0xF0F0,
kOSMaxHandlesPerProcess = 256,
};
typedef enum OSTaskState {
OSTaskStateDead,
OSTaskStateRunning,
OSTaskStateReady,
OSTaskStateBlocked,
OSTaskStateSleeping,
OSTaskStateBlockedReceive,
OSTaskStateBlockedSend,
} OSTaskState;
typedef struct OSProcess {
@@ -20,11 +39,12 @@ typedef struct OSProcess {
Address heapCurrent;
struct OSProcess* parent;
ASCII name[32];
OSHandle handles[kOSMaxHandlesPerProcess];
} OSProcess;
typedef struct OSTask {
Address stackPointer;
struct OSTask* next;
UInt32 id;
UInt32 sleepTicks;
OSTaskState state;
@@ -32,12 +52,10 @@ typedef struct OSTask {
Pointer kernelStackBase;
OSProcess* process;
Int32 waitingForProcessId;
} OSTask;
enum {
kOSSchedulerTaskStackSize = 16 * 1024,
kOSSchedulerExceptionNumber = 0xF0F0
};
struct OSTask* next;
struct OSTask* senderWaiting;
} OSTask;
extern UInt32 gOSSchedulerNextProcessID;
@@ -46,3 +64,7 @@ OSTask* SchedulerSpawn(void(*entryPoint)(), OSProcess* owner, Boolean isUser, Ad
Address SchedulerNext(Address stackPointer);
void SchedulerYield(UInt64 ticks);
UInt64 SchedulerGetNextProcessID();
void SchedulerBlockCurrentTask(UInt32 newState);
OSTask* SchedulerGetCurrentTask();
Int32 SchedulerProcessAllocateHandle(OSProcess* process, OSHandleType type, Pointer object);
Pointer SchedulerProcessGetHandle(OSProcess* process, UInt32 handleId, OSHandleType expectedType);
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <Types.h>
#include <Arch/Exceptions.h>
enum Syscalls {
kSyscallSend,
kSyscallReceive,
};
Address SyscallDispatch(ExceptionsContext* frame);
+4 -8
View File
@@ -5,6 +5,7 @@
#include <Arch/GIC.h>
#include <OS/Panic.h>
#include <OS/Scheduler.h>
#include <OS/Syscall.h>
Address ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) {
if (type == ExceptionsIRQEl1h || type == ExceptionsIRQEl064) return GICDispatch(frame, type);
@@ -13,14 +14,9 @@ Address ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) {
UInt32 class = (esr >> 26) & 0x3F;
UInt32 syndrome = esr & 0x1FFFFFF;
if (class == 0x11 || class == 0x15) {
if (syndrome == kOSSchedulerExceptionNumber) {
return SchedulerNext((Address)frame);
}
}
if (class == 0x15 && syndrome == 0) {
OSPanic("Hi from userspace!!");
if (class == 0x15) {
if (syndrome == kOSSchedulerExceptionNumber) return SchedulerNext((Address)frame);
if (syndrome == 0) return SyscallDispatch(frame);
}
}
OSPanicException(frame);
+1 -2
View File
@@ -40,8 +40,7 @@ void KernelMain(Bootinfo* bootinfo) {
SchedulerInitialize();
for (UInt32 i = 0; i < bootinfo->moduleCount; i++) {
ModuleLoad(&bootinfo->modules[i]); // TODO: make some sort of priority of loading modules
// like init first then uart then fb ...
ModuleLoad(&bootinfo->modules[i]); // TODO: priority
}
OSLog("Kernel initialized.\n");
+46
View File
@@ -0,0 +1,46 @@
#include <OS/IPC.h>
#include <OS/Scheduler.h>
#include <Arch/CPU.h>
#include <Arch/Exceptions.h>
UInt64 IPCSend(OSTask* sender, UInt64 handleID, UInt64 data) {
if (handleID == 0) return -1;
OSProcess* currentProcess = sender->process;
OSTask* targetTask = (OSTask*)SchedulerProcessGetHandle(currentProcess, handleID, kOSHandleTypeTask);
if (!targetTask) return -1;
if (targetTask->state == OSTaskStateBlockedReceive) {
ExceptionsContext* receiverContext = (ExceptionsContext*)targetTask->stackPointer;
receiverContext->x1 = data;
receiverContext->x0 = currentProcess->id;
targetTask->state = OSTaskStateRunning;
return 0;
}
if (targetTask->senderWaiting != nullptr) return -2;
targetTask->senderWaiting = sender;
SchedulerBlockCurrentTask(OSTaskStateBlockedSend);
return 0;
}
void IPCReceive(ExceptionsContext* receiverFrame) {
OSTask* receiver = SchedulerGetCurrentTask();
if (receiver->senderWaiting != nullptr) {
OSTask* sender = receiver->senderWaiting;
ExceptionsContext* senderContext = (ExceptionsContext*)sender->stackPointer;
receiverFrame->x1 = senderContext->x1; // Data
receiverFrame->x0 = sender->process->id; // Sender ID
receiver->senderWaiting = nullptr;
sender->state = OSTaskStateRunning;
return;
}
SchedulerBlockCurrentTask(OSTaskStateBlockedReceive);
}
+3 -3
View File
@@ -7,7 +7,7 @@
#include <Arch/CPU.h>
#include "../Common/bootinfo.h"
void ModuleLoad(BootModule* module) {
OSTask* ModuleLoad(BootModule* module) {
OSProcess* userProc = HeapAllocate(sizeof(OSProcess));
MemorySet(userProc, 0, sizeof(OSProcess));
userProc->id = SchedulerGetNextProcessID();
@@ -56,8 +56,8 @@ void ModuleLoad(BootModule* module) {
if ((Address)userEntryPoint == 0x0) {
OSLog("Skipping module %s: entry point is 0x0.\n", module->name);
return;
return nullptr;
}
SchedulerSpawn(userEntryPoint, userProc, true, userStackVirt + kVMPageSize);
return SchedulerSpawn(userEntryPoint, userProc, true, userStackVirt + kVMPageSize);
}
+28
View File
@@ -69,6 +69,7 @@ OSTask* SchedulerSpawn(void(*entryPoint)(), OSProcess* owner, Boolean isUser, Ad
task->waitingForProcessId = -1;
task->next = gOSSchedulerCurrentTask->next;
task->senderWaiting = nullptr;
gOSSchedulerCurrentTask->next = task;
return task;
@@ -126,3 +127,30 @@ void SchedulerYield(UInt64 ticks) {
UInt64 SchedulerGetNextProcessID() {
return gOSSchedulerNextProcessID++;
}
void SchedulerBlockCurrentTask(UInt32 newState) {
gOSSchedulerCurrentTask->state = newState;
CPUException(kOSSchedulerExceptionNumber);
}
OSTask* SchedulerGetCurrentTask() {
return gOSSchedulerCurrentTask;
}
Int32 SchedulerProcessAllocateHandle(OSProcess* process, OSHandleType type, Pointer object) {
for (Int32 i = 1; i < kOSMaxHandlesPerProcess; i++) {
if (process->handles[i].type == kOSHandleTypeNone) {
process->handles[i].type = type;
process->handles[i].object = object;
return i;
}
}
return -1;
}
Pointer SchedulerProcessGetHandle(OSProcess* process, UInt32 handleId, OSHandleType expectedType) {
if (handleId == 0 || handleId >= kOSMaxHandlesPerProcess) return nullptr;
OSHandle* handle = &process->handles[handleId];
if (handle->type != expectedType) return nullptr;
return handle->object;
}
+25
View File
@@ -0,0 +1,25 @@
#include <OS/Syscall.h>
#include <OS/IPC.h>
#include <OS/Scheduler.h>
#include <OS/Panic.h>
#include <Arch/Exceptions.h>
Address SyscallDispatch(ExceptionsContext* frame) {
UInt64 syscallNumber = frame->x8;
OSTask* current = SchedulerGetCurrentTask();
switch (syscallNumber) {
case kSyscallSend:
frame->x0 = IPCSend(current, frame->x0, frame->x1);
break;
case kSyscallReceive:
IPCReceive(frame);
break;
default:
frame->x0 = -1;
break;
}
if (current->state != OSTaskStateRunning) return SchedulerNext((Address)frame);
return (Address)frame;
}
+4
View File
@@ -56,3 +56,7 @@ find_program(LLVM_OBJCOPY NAMES llvm-objcopy objcopy
/opt/homebrew/bin
REQUIRED
)
set(CMAKE_C_FLAGS_INIT "-ffreestanding -mgeneral-regs-only -std=c23")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-nostdlib -static -no-pie -z max-page-size=0x1000 --no-dynamic-linker -T ${CMAKE_CURRENT_LIST_DIR}/linker.ld -Ttext=0x400000")
+29
View File
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2026 0xKSor
set(KSOS_SYSROOT_SRC "${CMAKE_CURRENT_LIST_DIR}/..")
set(BUILD_DIR $ENV{BUILD_DIR})
if(NOT BUILD_DIR)
set(BUILD_DIR "${CMAKE_CURRENT_LIST_DIR}/../../.build")
endif()
set(KSOS_LIB_DIR "${BUILD_DIR}/Runtime/System/libksOS")
function(add_ksos_executable TARGET_NAME)
add_executable(${TARGET_NAME}
"${KSOS_SYSROOT_SRC}/System/libksOS/Source/crt0.S"
${ARGN}
)
target_include_directories(${TARGET_NAME} PRIVATE
"${KSOS_SYSROOT_SRC}/System/libksOS/Include"
)
target_link_directories(${TARGET_NAME} PRIVATE
"${KSOS_LIB_DIR}"
)
target_link_libraries(${TARGET_NAME} PRIVATE
ksOS
)
endfunction()
+3 -31
View File
@@ -4,44 +4,16 @@
cmake_minimum_required(VERSION 3.20)
project(ksOS_init LANGUAGES C ASM)
include("${CMAKE_CURRENT_SOURCE_DIR}/../../Common/ksOS_SDK.cmake")
file(GLOB_RECURSE INIT_SOURCES CMAKE_CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Source/*.S
${CMAKE_CURRENT_SOURCE_DIR}/Source/*.c
)
add_executable(init ${INIT_SOURCES})
add_ksos_executable(Init ${INIT_SOURCES})
target_include_directories(init PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source
${CMAKE_CURRENT_SOURCE_DIR}/../../Common
)
target_compile_options(init PRIVATE
$<$<COMPILE_LANGUAGE:C>:
-std=c23
-fno-stack-protector
-fno-builtin
-Wall
-Wextra
>
-ffreestanding
-g
-mgeneral-regs-only
)
target_link_options(init PRIVATE
-nostdlib
-static
-no-pie
-T "${CMAKE_CURRENT_SOURCE_DIR}/../../Common/linker.ld"
-Ttext=0x400000
-e _start
-z max-page-size=0x1000
--no-dynamic-linker
)
set_target_properties(init PROPERTIES
set_target_properties(Init PROPERTIES
OUTPUT_NAME "Init"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
)
+5 -5
View File
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKSor
void main(void) {
__asm__ volatile(
"svc #0\n"
"b .\n"
);
#include <ksOS/Syscall.h>
int main(void) {
SysSend(1, 0xa0a0a0a0a);
while (1) {}
}
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2026 0xKSor
cmake_minimum_required(VERSION 3.20)
project(ksOS_test LANGUAGES C ASM)
include("${CMAKE_CURRENT_SOURCE_DIR}/../../Common/ksOS_SDK.cmake")
file(GLOB_RECURSE INIT_SOURCES CMAKE_CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Source/*.S
${CMAKE_CURRENT_SOURCE_DIR}/Source/*.c
)
add_ksos_executable(Init ${INIT_SOURCES})
set_target_properties(Init PROPERTIES
OUTPUT_NAME "Test"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
)
+9
View File
@@ -0,0 +1,9 @@
#include <ksOS/Syscall.h>
int main() {
IPCMessage message = SysRecive();
if (message.data == 0xa0a0a0a0a) {
Sys228();
}
}
+17
View File
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-3.0-or-later
cmake_minimum_required(VERSION 3.20)
project(libksOS LANGUAGES C ASM)
file(GLOB_RECURSE LIB_SOURCES CMAKE_CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.c
)
add_library(ksOS STATIC ${LIB_SOURCES})
target_include_directories(ksOS PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/Include
)
target_compile_options(ksOS PRIVATE
-std=c23 -fno-stack-protector -fno-builtin -Wall -Wextra
)
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKSor
#pragma once
#include <ksOS/Types.h>
enum {
kSyscallSend = 1,
kSyscallReceive,
};
typedef struct IPCMessage {
UInt64 senderID;
UInt64 data;
} IPCMessage;
static inline UInt64 SysSend(UInt64 handleID, UInt64 data) {
register UInt64 x8 __asm__("x8") = kSyscallSend;
register UInt64 x0 __asm__("x0") = handleID;
register UInt64 x1 __asm__("x1") = data;
__asm__ volatile(
"svc #0\n"
: "+r" (x0)
: "r" (x1), "r" (x8)
: "memory", "cc"
);
return x0;
}
static inline IPCMessage SysRecive(void) {
register UInt64 x8 __asm__("x8") = kSyscallReceive;
register UInt64 x0 __asm__("x0");
register UInt64 x1 __asm__("x1");
__asm__ volatile(
"svc #0\n"
: "=r" (x0), "=r" (x1)
: "r" (x8)
: "memory", "cc"
);
struct IPCMessage msg = { x0, x1 };
return msg;
}
static inline void Sys228(void) {
register UInt64 x8 __asm__("x8") = 228;
__asm__ volatile(
"svc #0\n"
:
: "r" (x8)
: "memory", "cc"
);
}
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKSor
#pragma once
#if defined(NDEBUG) || defined(__OPTIMIZE__)
#define IS_RELEASE 1
#else
#define IS_RELEASE 0
#endif
typedef unsigned char UInt8;
typedef unsigned short UInt16;
typedef unsigned int UInt32;
typedef unsigned long long UInt64;
typedef unsigned long long UInt;
typedef void* Pointer;
typedef UInt Address;
typedef UInt32 Address32;
typedef UInt8* BytePointer;
typedef UInt8* MemoryPointer;
typedef signed char Int8;
typedef signed short Int16;
typedef signed int Int32;
typedef signed long long Int64;
typedef int Int;
typedef UInt64 Size;
typedef char ASCII;
typedef _Bool Boolean;
+2
View File
@@ -0,0 +1,2 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKSor
@@ -4,14 +4,9 @@
.section .text.entry, "ax"
.global _start
_start:
// Set up stack pointer (8KB stack)
adrp x0, stack_top
add sp, x0, :lo12:stack_top
// Jump to C main
bl main
// If main returns, loop forever
b .
.section .bss, "aw", @nobits
+25
View File
@@ -95,8 +95,22 @@ _default:
exit 0
fi
# libksOS MUST be built first — everyone depends on it
for dir in "${projects[@]}"; do
name=$(basename "${dir}")
if [ "${name}" = "libksOS" ]; then
echo ""
echo " 📦 ${CATEGORY}/${name}"
just --quiet _build_cmake "${dir}" "${CATEGORY}/${name}"
break
fi
done
for dir in "${projects[@]}"; do
name=$(basename "${dir}")
if [ "${name}" = "libksOS" ]; then
continue
fi
echo ""
echo " 📦 ${CATEGORY}/${name}"
@@ -111,6 +125,7 @@ _default:
@build:
@echo "🛠️ Building Runtime..."
@just --quiet clangd
@mkdir -p {{ RUNTIME_BUILD_DIR }}/System
@mkdir -p {{ RUNTIME_BUILD_DIR }}/Apps
@just --quiet _discover_and_build System
@@ -122,3 +137,13 @@ _default:
@rm -rf {{ TEMP_DIR }}/Runtime
@rm -rf {{ RUNTIME_BUILD_DIR }}
@echo "🧹 Runtime cleaned"
@clangd:
#!/usr/bin/env bash
set -e
RUNTIME_DIR="{{ justfile_directory() }}"
INCLUDE_DIR="${RUNTIME_DIR}/System/libksOS/Include"
printf 'CompileFlags:\n Add:\n - -xc\n - --target=aarch64-none-elf\n - -ffreestanding\n - -I%s\n' "${INCLUDE_DIR}" > "${RUNTIME_DIR}/.clangd"
echo "✅ Generated ${RUNTIME_DIR}/.clangd"