commit 4d7c062fbfa6373e41c75637079dd1a72c57f150 from: Onana Onana Xavier Manuel date: Fri Sep 18 17:26:25 2026 UTC Very primitive message passing through shared buffers. Example includes alpha and xhci server. commit - 7b1f1314a4618908b79db56b86bae5c9306dc65f commit + 4d7c062fbfa6373e41c75637079dd1a72c57f150 blob - 328bcaa3fc244f7ebb9b149b20ea0db74ae89291 blob + 47b91394c55e97eb3f122110d63b1915f8c86dcc --- README +++ README @@ -27,10 +27,10 @@ memory (FDM) the program needs. The fake memory is add runtime. Hence, memory allocation is based on that memory chunk and not a syscall to request pages from the kernel. This will force you to tell how much RAM your program -really needs instead of pretending to be lightweight. Hence, -when a program does a `malloc` it takes a seat with the -allocation server which reads the FDM and returns either the -part needed or and error to say that there is no memory left. +really needs instead of pretending to be lightweight. The +memory block is handed at the entry point and the programs, +thread has to manage it. There is no malloc, there is no +free, you are on your own. The layout of the operating system image is : @@ -68,6 +68,55 @@ threads. I call this the seat protocol. The Root Task (alpha) goal is to strip away the header and load executable in memory. Essentialy the root task is the loader. +Opening a file: A text editor reserves a set amount of memory in +its contract for viewport. A policy is made so he can request +a driver to read the part needed from file and another file is +opened to save history at a maximum amount of bytes. + +Tasks spawning other task is not done like unix fork. The way +a task spawns another is is by cutting a part of its memory and +requesting the root task to use it to create another one. For +example, a windom manager could request a memory chunk of 512MB +and use that 512 MB to spawn other tasks. This way resource +exhaustion is impossible as the only resources used by the a +task are his own. + +Filesystem: The filesystem is tag based, meaning that there are +no directories, sub-directories, etc. Files are layout through +tags and adding a file adds it to the tag. Doing something like +tags in chell will give you a list. i.e: + +> # +#home +#games +#system + +Accessing a file is done with for instance home:record.mkv + +Files can also have multiple tags. For instance + +> ::record.mkv + +#home +#system + +Permissions are managed via groups and users just like in Unix. +That was actually a good design so we keep it. You can do something +like. + +> #home useradd nanga rwx +> #home groupadd mygroup rwx + +When doing this it adds these to all objects with this tag. There +are no directories, only tags and files. + +Basically + +#[Tags]:[Group/User]:[File] + +The file system server when started is fed by the root task the +offset before reading the boot drive files. + Features: - Buggy blob - 2f9e39b4b18e87434300955e12d385bf415864ec blob + f1b07c8fdf60905a2aaecad352ca1ff7f0c9d782 --- TODO +++ TODO @@ -1,14 +1,5 @@ -- kalloc and ualloc which checks if memory P_USER is set before - giving frames. +- File System (tag based) -- Add bss to module size for real binary sizes - -- Syscalls - -- IPC - -- File System - - a.out executable format - PIC @@ -16,6 +7,3 @@ - Lua C449 compiler, assembler and linker - Rewrite in C449 - -- Create ring buffer to allow asynchronous requests but drop on - full (no blocking or overwritting) blob - 6574b6b771e87848e935b310dd24595b5227bcd1 blob + 2de5594659a2333bdff30deac6500e8780a602e7 --- alpha/libc.c +++ alpha/libc.c @@ -39,3 +39,32 @@ outputh(u64 i) sysc.echo.number = i; sys_send(&sysc); } + +void +yield() +{ + struct sysc sysc; + sysc.type = SYSC_YIELD; + sys_send(&sysc); +} + +u8* +listen(u8 *block) +{ + struct sysc sysc; + sysc.type = SYSC_LISTEN; + sysc.listen.shared_block = block; + sys_send(&sysc); + return nil; +} + +u8* +seat(u64 threadid) +{ + struct sysc sysc; + sysc.type = SYSC_SEAT; + sysc.seat.threadid = threadid; + sysc.seat.ret_block = nil; + sys_send(&sysc); + return sysc.seat.ret_block; +} blob - e3c44299d8e2701bebb331312b659d8ccf0b5a63 blob + 84979facb56c87769474f78b410905a831d50d5b --- alpha/libc.h +++ alpha/libc.h @@ -2,13 +2,17 @@ #include "types.h" -// Resources given to a thread on initialization +#define MAX_SHARED_BUFFERS 24 + +// Resources given to a thread on initialization. Dynamic memory allocation +// does not exist hence don't expect malloc/calloc/realloc/free/etc. The +// kernel hands your memory and you're on your own. struct contract { u8 *memBlock; // This is guaranteed to be zero'd at entry point u64 memSize; - u8 *sharedBlock; // Guaranteed to be zero'd at entry point as well u64 sharedCount; + u8 *sharedBlocks[MAX_SHARED_BUFFERS]; }; // If you come from Unix-like this is kinda @@ -20,4 +24,19 @@ void outputi(u64); void outputh(u64); -// Memory allocation +void yield(void); // Tells the OS to pause the thread until something wakes it up + +// Shared Blocks syscalls +// +// This sets of functions including yield() allows for communication between threads. +// doing listen opens a seat for a sender and using yield after tells the kernel to +// pause the thread and wakes it up if its buffer was written to. This allows for +// non-blocking and blocking to be implemented without flags. + +u8 *seat(u64 thread_id); + +void getup(u64 seat_id); + +u64 speak(u8 *msg); + +u8 *listen(u8 *block); // Tells the OS to open the seat for a sender. blob - 1a2e9694bafd83253434326ace1ffd76de2b7ae1 blob + d4a5bc809b1263c230055cff194d0832934ecd6b --- alpha/main.c +++ alpha/main.c @@ -24,6 +24,7 @@ void alpha_main(struct mmap *km) { struct task *t; + u8 *block = nil; pci_init(); t = task_new((void *)XHCI_ENTRY); @@ -32,6 +33,9 @@ alpha_main(struct mmap *km) output("alpha: failed to create task!\n"); output("alpha: running\n"); + while ((block = seat(t->thread)) == nil); + output("alpha: found a seat\n"); + block[0] = 0x55; for (;;); } @@ -44,7 +48,7 @@ task_new(void *entry) sysc.type = SYSC_NEW_THREAD; sysc.new_thread.entry = entry; sysc.new_thread.memsize = PAGE_SIZE; - sysc.new_thread.seats = 0; + sysc.new_thread.nseats = 1; for (int i = 0; i < MAX_TASKS; i++) { if (tasks[i].assigned == 0) { blob - 4bbea456c1fc5e1c47816117ea97cf496fac0d68 blob + b1f53ded69a92ac0773586058d6d5b0783e71569 --- c449 +++ c449 @@ -52,6 +52,7 @@ defines a thread right away and that the root task can properly: thread 0; +core 0; static u8 *name="First"; @@ -62,6 +63,7 @@ main() } thread 1; +core 0; void main() blob - 15ee22e44c08b1d58a85ee5ed5ae33d5294522ee blob + 42b61e061e1a2e50515816aba016e6ecc8b18f25 --- kernel/idt.c +++ kernel/idt.c @@ -2,6 +2,7 @@ #include "types.h" #include "kpic.h" #include "kthreads.h" +#include "syscalls.h" #define APIC_EOI_REG ((volatile u32*)0xFEE000B0) @@ -119,6 +120,8 @@ timer_handler_c() { struct thread *next; + syscall_check_seats(); + next = thread_next(); if (next != nil) { blob - 5b711f5a26c269a6ec68fd7615236c460ef62d09 blob + 47337eab319ec3977f8806b72a51a4c7508e41f1 --- kernel/kpic.c +++ kernel/kpic.c @@ -67,7 +67,7 @@ static void apic_setup_timer() { // Clock divider - *APIC_TIMER_DIV = 0x03; + *APIC_TIMER_DIV = 0x01; // Setup register *APIC_TIMER_LVT = APIC_LVT_PERIODIC | TIMER_VECTOR; blob - f6fa1af4a7b0b7ba0d65afb136dece90e87f1654 blob + b6ffdf4fe737cc85c7e1aa463b2b93a93f7081fd --- kernel/kthreads.c +++ kernel/kthreads.c @@ -3,6 +3,7 @@ #include "kmmap.h" #include "kdebug.h" #include "kthreads.h" +#include "syscalls.h" #include "libc.h" #define MAX_USER_THREADS 64 @@ -15,6 +16,7 @@ thread_create(void (*entry)(void), struct sysc_new_thr { struct thread *thread = nil; u64 *sp, usp; + u64 tid; for (int i = 0; i < MAX_USER_THREADS; i++) { struct thread *th = &uthreads[i]; @@ -22,6 +24,7 @@ thread_create(void (*entry)(void), struct sysc_new_thr if (th->assigned == 0) { th->assigned = 1; thread = th; + tid = i; break; } } @@ -32,8 +35,12 @@ thread_create(void (*entry)(void), struct sysc_new_thr thread->base = allocate_memory(PAGE_SIZE * 2); thread->con->memBlock = nil; + thread->con->sharedCount = 0; thread->con->memSize = 0; + for (int i = 0; i < MAX_SHARED_BUFFERS; i++) + thread->con->sharedBlocks[i] = nil; + if (thread->base == nil) { thread->assigned = 0; return nil; @@ -43,8 +50,16 @@ thread_create(void (*entry)(void), struct sysc_new_thr thread->con->memBlock = (u8 *)allocate_memory(nthread->memsize); thread->con->memSize = nthread->memsize; - for (int i = 0; i < thread->con->memSize; i++) - thread->con->memBlock[i] = 0; + // Zero everything + if (nthread->memsize > 0) { + for (int i = 0; i < thread->con->memSize; i++) + thread->con->memBlock[i] = 0; + } + + if (nthread->nseats > 0) { + for (int i = 0; i < nthread->nseats; i++) + seat_new(tid); + } } sp = (u64 *)((u8*)thread->base + PAGE_SIZE * 2); blob - 19fbabd1b9e9124fcbbe564306ffed98b5414247 blob + 237e566703ea9144a6cec990175303c4f4c6641f --- kernel/kthreads.h +++ kernel/kthreads.h @@ -7,6 +7,7 @@ enum thread_state { THREAD_READY, THREAD_RUNNING, + THREAD_YIELDING, }; struct thread { blob - af6e2abed99462469afc6841fd8300cd54b9c97c blob + 1aee040fdee868f814c9300a4f6431f44d9c08ad --- kernel/syscalls.c +++ kernel/syscalls.c @@ -4,13 +4,15 @@ #include "kthreads.h" #include "syscalls/syscalls.h" -#define MAX_SEATS 256 +#define MAX_SEATS 24 #define MAX_SYSC_QUEUE 64 #define THREADID_NONE -1 struct seatmap { s64 send; // Sender Thread s64 recv; // Receiver Thread + u8 *buff; + u8 open; }; static struct seatmap seatmaps[MAX_SEATS]; @@ -19,9 +21,18 @@ static u8 *sbuffers; s8 seat_new(u64 threadid) { + struct thread *thread = thread_get_uthread(threadid); + for (int i = 0; i < MAX_SEATS; i++) { if (seatmaps[i].recv == THREADID_NONE) { + thread->con->sharedBlocks[thread->con->sharedCount++] = seatmaps[i].buff; seatmaps[i].recv = threadid; + + // Zero'd + for (int j = 0; j < PAGE_SIZE; j++) + seatmaps[i].buff[j] = 0; + + kprint("Shared block mapped to thread(id="); kprinti(threadid); kprint(")\n"); return 0; } } @@ -47,6 +58,7 @@ void syscall_init() for (int i = 0; i < MAX_SEATS; i++) { seatmaps[i].send = THREADID_NONE; seatmaps[i].recv = THREADID_NONE; + seatmaps[i].buff = sbuffers + (i * PAGE_SIZE); } // movabs rax, addr @@ -91,14 +103,6 @@ syscall_new_thread_h(struct sysc *call) if (th == nil) kprint("failed to create thread from syscall!\n"); - /* - if (seat_new(threadid) == -1) { - kprint("Failed to create new seat\n"); - repeat: - goto repeat; - } - */ - threadid = thread_getuid(th); nthread->ret_id = threadid; th->state = THREAD_RUNNING; @@ -107,6 +111,29 @@ syscall_new_thread_h(struct sysc *call) } void +sysc_listen_h(struct sysc_listen *listen) +{ + for (int i = 0; i < MAX_SEATS; i++) { + if (listen->shared_block == seatmaps[i].buff) { + seatmaps[i].open = 1; + return; + } + } +} + +void +sysc_seat_h(struct sysc_seat *sysc_seat) +{ + for (int i = 0; i < MAX_SEATS; i++) { + if (sysc_seat->threadid == seatmaps[i].recv && seatmaps[i].open == 1) { + sysc_seat->ret_block = seatmaps[i].buff; + seatmaps[i].send = thread_getuid(cthread); + break; + } + } +} + +void syscall_handler(struct sysc *call) { switch (call->type) { @@ -116,7 +143,32 @@ syscall_handler(struct sysc *call) case SYSC_NEW_THREAD: syscall_new_thread_h(call); break; + case SYSC_YIELD: + cthread->state = THREAD_YIELDING; + while (cthread->state != THREAD_RUNNING); + break; + case SYSC_LISTEN: + sysc_listen_h(&call->listen); + break; + case SYSC_SEAT: + sysc_seat_h(&call->seat); + break; default: break; } } + + +void +syscall_check_seats() +{ + for (int i = 0; i < MAX_SEATS; i++) { + struct seatmap *map = &seatmaps[i]; + + if (map->open == 1 && map->recv != THREADID_NONE && map->send != THREADID_NONE) { + struct thread *th = thread_get_uthread(map->recv); + + if (map->buff[0] != 0) th->state = THREAD_RUNNING; + } + } +} blob - 95ffef1019aeafa48a48857106ea098ee698ec70 blob + 79963268d0622d014efb9531ad3f6aaee5af8213 --- kernel/syscalls.h +++ kernel/syscalls.h @@ -1,3 +1,5 @@ #pragma once void syscall_init(void); +s8 seat_new(u64 threadid); +void syscall_check_seats(void); blob - 355b272393886987af96ba873b5cab4a4589205e blob + 9b5e3be626986c9a9b0d22f5a4d9aaeaebfb8046 --- servers/xhci/main.c +++ servers/xhci/main.c @@ -10,10 +10,12 @@ xhci_main(struct contract *con) { output("xhci: running\n"); - output("xhci: memory size is "); outputi(con->memSize); - output(" bytes\n"); + output("xhci: listening\n"); + listen(con->sharedBlocks[0]); + yield(); - output("Memory block location is : "); outputh((u64)con->memBlock[0]); output("\n"); + output("xhci: listen stop\n"); + output("xhci: first byte of shared block is "); outputh(con->sharedBlocks[0][0]); output("\n"); for (;;); } blob - 4814d52593d770131e8ced32171fbab5cca710de blob + e8ba6f6be4b57f7df4b5a9b831a5e2d68e19acd0 --- syscalls/syscalls.h +++ syscalls/syscalls.h @@ -5,6 +5,9 @@ enum sysc_type { SYSC_ECHO, + SYSC_SEAT, + SYSC_YIELD, + SYSC_LISTEN, SYSC_NEW_THREAD, SYSC_FREE_THREAD, }; @@ -27,16 +30,28 @@ struct sysc_echo { struct sysc_new_thread { void *entry; // Entry point u64 memsize; - u64 seats; + u64 nseats; s64 ret_id; }; +struct sysc_listen { + u8 *shared_block; +}; + +struct sysc_seat { + u64 threadid; + + u8 *ret_block; +}; + struct sysc { enum sysc_type type; union { struct sysc_echo echo; struct sysc_new_thread new_thread; + struct sysc_listen listen; + struct sysc_seat seat; }; };