ipc and thread spawning

This commit is contained in:
Benji Dial 2025-12-29 16:55:53 -05:00
parent 02e855c066
commit 4f9bf8afef
18 changed files with 681 additions and 32 deletions

1
disk/calcite/init.rc Normal file
View file

@ -0,0 +1 @@
root://calcite/apps/hello/hello.elf

View file

@ -20,6 +20,7 @@
#include <kernel-public/framebuffer.h> #include <kernel-public/framebuffer.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/input.h> #include <kernel-public/input.h>
#include <kernel-public/ipc.h>
[[noreturn]] void end_thread(); [[noreturn]] void end_thread();
@ -38,3 +39,20 @@ void wait_for_mouse_packet(struct mouse_packet *packet_out);
void *map_pages(uint64_t count); void *map_pages(uint64_t count);
void sleep_ms(uint64_t ms_to_sleep); void sleep_ms(uint64_t ms_to_sleep);
//1 on success, 0 on failure.
int start_elf(const char *path);
enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out);
enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out);
//on entry, bytes is maximum accepted packet length.
//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length.
//actual packet length will always be positive.
enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes);
//bytes must be positive.
enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes);
//f should not return
void create_thread(void (*f)(uint64_t x), uint64_t x);

View file

@ -0,0 +1,38 @@
/* Calcite, include/kernel-public/ipc.h
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
enum ipc_dgram_result {
IPR_SUCCESS,
//on write, packet is bigger than kernel data structure.
//on read, packet is bigger than max requested size.
IPR_TOO_BIG,
IPR_BAD_HANDLE,
//on create receiver, someone is already receiving at that address.
IPR_IN_USE
};
typedef uint64_t ipc_dgram_receiver_handle_t;
typedef uint64_t ipc_dgram_sender_handle_t;

View file

@ -26,5 +26,11 @@ enum {
SYSCALL_READ_FILE, SYSCALL_READ_FILE,
SYSCALL_WAIT_FOR_MOUSE_PACKET, SYSCALL_WAIT_FOR_MOUSE_PACKET,
SYSCALL_MAP_PAGES, SYSCALL_MAP_PAGES,
SYSCALL_SLEEP_MS SYSCALL_SLEEP_MS,
SYSCALL_START_ELF,
SYSCALL_IPC_CREATE_DGRAM_RECEIVER,
SYSCALL_IPC_CREATE_DGRAM_SENDER,
SYSCALL_IPC_RECEIVE_DGRAM,
SYSCALL_IPC_SEND_DGRAM,
SYSCALL_CREATE_THREAD
}; };

View file

@ -265,6 +265,12 @@ static const char *cmdline_look_up(const char *key) {
register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet); register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet);
register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages); register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages);
register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms); register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms);
register_syscall(SYSCALL_START_ELF, (void *)&syscall_start_elf);
register_syscall(SYSCALL_IPC_CREATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_dgram_receiver);
register_syscall(SYSCALL_IPC_CREATE_DGRAM_SENDER, (void *)&syscall_ipc_create_dgram_sender);
register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram);
register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram);
register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread);
//probe for drives //probe for drives
@ -288,12 +294,12 @@ static const char *cmdline_look_up(const char *key) {
set_fs("root", root_fs); set_fs("root", root_fs);
//load hello and start it //load init and start it
init_scheduler(); init_scheduler();
if (!start_elf("root://calcite/apps/hello/hello.elf")) if (!start_elf("root://calcite/apps/init/init.elf"))
panic("could not start hello.elf") panic("could not start init.elf")
resume_next_continuation(); resume_next_continuation();

147
src/kernel/ipc-dgram.c Normal file
View file

@ -0,0 +1,147 @@
/* Calcite, src/kernel/ipc-dgram.c
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ipc-dgram.h"
#include "scheduler.h"
#include "utility.h"
#include "panic.h"
#include "heap.h"
#include <kernel-public/ipc.h>
struct ipc_dgram_address_book {
const char *address;
struct ipc_dgram_box box;
struct ipc_dgram_address_book *prev;
};
static struct ipc_dgram_address_book *last = 0;
#define INITIAL_SEND_BACKLOG 4
struct ipc_dgram_box *get_ipc_dgram_box(const char *address) {
for (struct ipc_dgram_address_book *book = last; book != 0; book = book->prev)
if (strequ(book->address, address))
return &book->box;
struct ipc_dgram_address_book *new = heap_alloc(sizeof(struct ipc_dgram_address_book));
int address_length = 0;
while (address[address_length] != 0)
++address_length;
char *new_address = heap_alloc(address_length + 1);
memcpy(new_address, address, address_length);
new_address[address_length] = 0;
new->address = new_address;
new->box.is_receiver_held = 0;
new->box.is_someone_waiting_to_receive = 0;
create_queue(&new->box.waiting_to_send, INITIAL_SEND_BACKLOG);
new->box.buffer_read_pointer = 0;
new->box.buffer_read_available = 0;
new->prev = last;
last = new;
return &new->box;
}
int get_ipc_dgram_size(struct ipc_dgram_box *box) {
if (box->is_someone_waiting_to_receive)
return -1;
while (box->buffer_read_available == 0) {
box->is_someone_waiting_to_receive = 1;
yield(&box->waiting_to_receive);
}
assert(box->buffer_read_available >= 4)
return *(int *)&box->buffer[box->buffer_read_pointer];
}
void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer) {
assert(box->is_someone_waiting_to_receive == 0)
assert(box->buffer_read_available >= 4)
int size = *(int *)&box->buffer[box->buffer_read_pointer];
box->buffer_read_available -= 4;
box->buffer_read_pointer = (box->buffer_read_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH;
int size_rounded_up = ((size - 1) / 4 + 1) * 4;
assert(box->buffer_read_available >= size_rounded_up)
int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_pointer;
if (size < first_block)
first_block = size;
memcpy(buffer, &box->buffer[box->buffer_read_pointer], first_block);
if (first_block != size)
memcpy(buffer + first_block, box->buffer, size - first_block);
box->buffer_read_available -= size_rounded_up;
box->buffer_read_pointer = (box->buffer_read_pointer + size_rounded_up) % IPC_DGRAM_BOX_BUFFER_LENGTH;
struct continuation_info ci;
while (take_from_queue(&box->waiting_to_send, &ci))
add_to_queue(&ready_continuations, &ci);
}
enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes) {
assert(bytes > 0)
int bytes_round_up = ((bytes - 1) / 4 + 1) * 4;
if (4 + bytes_round_up > IPC_DGRAM_BOX_BUFFER_LENGTH)
return IPR_TOO_BIG;
while (IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_available < 4 + bytes_round_up)
yield_to_queue(&box->waiting_to_send);
int write_pointer = (box->buffer_read_pointer + box->buffer_read_available) % IPC_DGRAM_BOX_BUFFER_LENGTH;
assert(write_pointer + 4 <= IPC_DGRAM_BOX_BUFFER_LENGTH)
*(int *)&box->buffer[write_pointer] = bytes;
write_pointer = (write_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH;
int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - write_pointer;
if (bytes < first_block)
first_block = bytes;
memcpy(&box->buffer[write_pointer], data, first_block);
if (first_block != bytes)
memcpy(box->buffer, data + first_block, bytes - first_block);
box->buffer_read_available += 4 + bytes_round_up;
if (box->is_someone_waiting_to_receive) {
box->is_someone_waiting_to_receive = 0;
add_to_queue(&ready_continuations, &box->waiting_to_receive);
}
return IPR_SUCCESS;
}

58
src/kernel/ipc-dgram.h Normal file
View file

@ -0,0 +1,58 @@
/* Calcite, src/kernel/ipc-dgram.h
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "scheduler.h"
#include <kernel-public/ipc.h>
//must be a multiple of 4
#define IPC_DGRAM_BOX_BUFFER_LENGTH 1024
struct ipc_dgram_box {
int is_receiver_held;
int is_someone_waiting_to_receive;
struct continuation_info waiting_to_receive;
struct continuation_queue waiting_to_send;
int buffer_read_pointer;
int buffer_read_available;
//packets are of the form:
// int size;
// uint8_t packet[size];
// uint8_t padding[size % 4 == 0 ? 0 : 4 - size % 4];
uint8_t buffer[IPC_DGRAM_BOX_BUFFER_LENGTH];
};
struct ipc_dgram_box *get_ipc_dgram_box(const char *address);
//waits until a packet is available, then returns the size of that packet.
//returns -1 if someone is already waiting.
int get_ipc_dgram_size(struct ipc_dgram_box *box);
//should only be called just after get_ipc_dgram_size
//(i.e. with no intervening continuation resumptions)
void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer);
//only returns IPR_SUCCESS or IPR_TOO_BIG
enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes);

View file

@ -18,28 +18,19 @@
bits 64 bits 64
default rel default rel
extern running_thread
section .text section .text
;referenced in process.c ;referenced in process.c
global thread_start global thread_start
thread_start: thread_start:
mov rax, qword [running_thread]
test rax, rax
jnz .assert_fail
mov qword [running_thread], rbx
mov cr3, rbp mov cr3, rbp
mov rcx, r12 mov rcx, r12
mov r11, 0x200 mov r11, 0x200
mov rdi, r13
xor rax, rax xor rax, rax
xor rbx, rbx xor rbx, rbx
xor rdx, rdx xor rdx, rdx
xor rdi, rdi
xor rsi, rsi xor rsi, rsi
xor rbp, rbp xor rbp, rbp
xor r8, r8 xor r8, r8
@ -51,7 +42,3 @@ thread_start:
xor r15, r15 xor r15, r15
o64 sysret o64 sysret
.assert_fail:
;could be nice to handle this more gracefully in the future
ud2

View file

@ -16,6 +16,7 @@
*/ */
#include "framebuffer.h" #include "framebuffer.h"
#include "ipc-dgram.h"
#include "scheduler.h" #include "scheduler.h"
#include "syscalls.h" #include "syscalls.h"
#include "process.h" #include "process.h"
@ -26,6 +27,7 @@
#include "fs.h" #include "fs.h"
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h>
struct process_file_info *get_file_info(struct process *process, file_handle_t handle) { struct process_file_info *get_file_info(struct process *process, file_handle_t handle) {
if (handle > (uint64_t)process->files_buffer_size || process->files[handle].fs == 0) if (handle > (uint64_t)process->files_buffer_size || process->files[handle].fs == 0)
@ -63,6 +65,7 @@ void create_process(struct process *process_out) {
process_out->n_threads = 0; process_out->n_threads = 0;
process_out->files = 0; process_out->files = 0;
process_out->ipc_dgram_handles = 0;
} }
@ -334,11 +337,11 @@ int load_elf(
} }
//defined in process.asm. enters user mode with: //defined in process.asm. enters user mode with:
// running_thread = value of rbx when we jump here
// cr3 = value of rbp when we jump here // cr3 = value of rbp when we jump here
// rsp = value of rsp when we jump here // rsp = value of rsp when we jump here
// rip = rcx = value of r12 when we jump here // rip = rcx = value of r12 when we jump here
// rflags = r11 = 0x200 (IF) // rflags = r11 = 0x200 (IF)
// rdi = value of r13 when we jump here
// all other registers zeroed // all other registers zeroed
extern uint8_t thread_start; extern uint8_t thread_start;
@ -365,10 +368,11 @@ int start_elf(const char *uri) {
struct continuation_info ci; struct continuation_info ci;
ci.rip = (uint64_t)&thread_start; ci.rip = (uint64_t)&thread_start;
ci.rbx = (uint64_t)thread;
ci.rbp = thread->process->p4_physical_base; ci.rbp = thread->process->p4_physical_base;
ci.rsp = (uint64_t)thread->stack_top; ci.rsp = (uint64_t)thread->stack_top;
ci.r12 = entry; ci.r12 = entry;
ci.r13 = 0;
ci.running_thread = thread;
add_to_queue(&ready_continuations, &ci); add_to_queue(&ready_continuations, &ci);
@ -376,6 +380,36 @@ int start_elf(const char *uri) {
} }
void syscall_create_thread(void (*f)(uint64_t), uint64_t x) {
assert(running_thread != 0)
struct thread *thread = heap_alloc(sizeof(struct thread));
create_thread(running_thread->process, thread);
struct continuation_info ci;
ci.rip = (uint64_t)&thread_start;
ci.rbp = thread->process->p4_physical_base;
ci.rsp = (uint64_t)thread->stack_top;
ci.r12 = (uint64_t)f;
ci.r13 = x;
ci.running_thread = thread;
add_to_queue(&ready_continuations, &ci);
}
int syscall_start_elf(const char *uri) {
assert(running_thread != 0)
if (!is_mapped_readable_string(running_thread->process, uri))
syscall_illegal_args();
return start_elf(uri);
}
void destroy_process(struct process *process) { void destroy_process(struct process *process) {
if (process->files) { if (process->files) {
@ -385,6 +419,15 @@ void destroy_process(struct process *process) {
heap_dealloc(process->files, process->files_buffer_size * sizeof(struct process_file_info)); heap_dealloc(process->files, process->files_buffer_size * sizeof(struct process_file_info));
} }
if (process->ipc_dgram_handles) {
for (int i = 0; i < process->ipc_dgram_handles_buffer_size; ++i)
if (process->ipc_dgram_handles[i].box != 0 && process->ipc_dgram_handles[i].is_receiver == 1)
process->ipc_dgram_handles[i].box->is_receiver_held = 0;
heap_dealloc(
process->ipc_dgram_handles,
process->ipc_dgram_handles_buffer_size * sizeof(struct process_ipc_dgram_handle_info));
}
for (int p3i = 0; p3i < 512; ++p3i) for (int p3i = 0; p3i < 512; ++p3i)
if (process->p3_virtual_base[p3i]) { if (process->p3_virtual_base[p3i]) {
for (int p2i = 0; p2i < 512; ++p2i) for (int p2i = 0; p2i < 512; ++p2i)
@ -441,7 +484,9 @@ void create_thread(struct process *process, struct thread *thread_out) {
uint64_t pma = take_free_physical_page(); uint64_t pma = take_free_physical_page();
map_page_for_process( map_page_for_process(
process, pma, stack_bottom_vma + i * 4096, 1, 0, 1); process, pma,
stack_bottom_vma + i * 4096,
1, 0, 1);
void *kvma = find_free_kernel_region(4096); void *kvma = find_free_kernel_region(4096);
map_in_kernel_page_table(pma, kvma, 1, 0); map_in_kernel_page_table(pma, kvma, 1, 0);
@ -458,7 +503,7 @@ void create_thread(struct process *process, struct thread *thread_out) {
} }
[[noreturn]] static void syscall_illegal_args() { [[noreturn]] void syscall_illegal_args() {
panic("bad syscall") panic("bad syscall")
} }
@ -626,3 +671,136 @@ void *syscall_map_pages(uint64_t count) {
return vma; return vma;
} }
#define INITIAL_IPC_DGRAM_HANDLE_COUNT 16
static uint64_t get_free_ipc_dgram_handle(struct process *process) {
if (process->ipc_dgram_handles == 0) {
process->ipc_dgram_handles =
heap_alloc(INITIAL_IPC_DGRAM_HANDLE_COUNT * sizeof(struct process_ipc_dgram_handle_info));
process->ipc_dgram_handles_buffer_size = INITIAL_IPC_DGRAM_HANDLE_COUNT;
for (int i = 0; i < INITIAL_IPC_DGRAM_HANDLE_COUNT; ++i)
process->ipc_dgram_handles[i].box = 0;
return 0;
}
for (int i = 0; i < process->ipc_dgram_handles_buffer_size; ++i)
if (process->ipc_dgram_handles[i].box == 0)
return i;
int old_size = process->ipc_dgram_handles_buffer_size;
int new_size = old_size * 2;
struct process_ipc_dgram_handle_info *new_buffer =
heap_alloc(new_size * sizeof(struct process_ipc_dgram_handle_info));
memcpy(
new_buffer, process->ipc_dgram_handles,
old_size * sizeof(struct process_ipc_dgram_handle_info));
heap_dealloc(
process->ipc_dgram_handles,
old_size * sizeof(struct process_ipc_dgram_handle_info));
process->ipc_dgram_handles = new_buffer;
process->ipc_dgram_handles_buffer_size = new_size;
for (int i = old_size; i < new_size; ++i)
new_buffer[i].box = 0;
return old_size;
}
enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
const char *address, ipc_dgram_receiver_handle_t *handle_out) {
assert(running_thread != 0)
if (!is_mapped_readable_string(running_thread->process, address) ||
!is_mapped_writable(
running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t)))
syscall_illegal_args();
struct ipc_dgram_box *box = get_ipc_dgram_box(address);
if (box->is_receiver_held)
return IPR_IN_USE;
box->is_receiver_held = 1;
*handle_out = get_free_ipc_dgram_handle(running_thread->process);
struct process_ipc_dgram_handle_info *info = &running_thread->process->ipc_dgram_handles[*handle_out];
info->box = box;
info->is_receiver = 1;
return IPR_SUCCESS;
}
enum ipc_dgram_result syscall_ipc_create_dgram_sender(
const char *address, ipc_dgram_sender_handle_t *handle_out) {
assert(running_thread != 0)
if (!is_mapped_readable_string(running_thread->process, address) ||
!is_mapped_writable(
running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t)))
syscall_illegal_args();
struct ipc_dgram_box *box = get_ipc_dgram_box(address);
*handle_out = get_free_ipc_dgram_handle(running_thread->process);
struct process_ipc_dgram_handle_info *info = &running_thread->process->ipc_dgram_handles[*handle_out];
info->box = box;
info->is_receiver = 0;
return IPR_SUCCESS;
}
enum ipc_dgram_result syscall_ipc_receive_dgram(
ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) {
assert(running_thread != 0)
if (!is_mapped_writable(running_thread->process, bytes, sizeof(uint64_t)) ||
!is_mapped_writable(running_thread->process, buffer, *bytes))
syscall_illegal_args();
if (running_thread->process->ipc_dgram_handles == 0 ||
handle >= (uint64_t)running_thread->process->ipc_dgram_handles_buffer_size ||
running_thread->process->ipc_dgram_handles[handle].box == 0 ||
running_thread->process->ipc_dgram_handles[handle].is_receiver != 1)
return IPR_BAD_HANDLE;
struct ipc_dgram_box *box = running_thread->process->ipc_dgram_handles[handle].box;
int actual_bytes = get_ipc_dgram_size(box);
assert(actual_bytes > 0);
if (actual_bytes > *bytes) {
*bytes = actual_bytes;
return IPR_TOO_BIG;
}
receive_ipc_dgram(box, buffer);
*bytes = actual_bytes;
return IPR_SUCCESS;
}
enum ipc_dgram_result syscall_ipc_send_dgram(
ipc_dgram_sender_handle_t handle, const void *data, int bytes) {
assert(running_thread != 0)
if (bytes <= 0 || !is_mapped_readable(running_thread->process, data, bytes))
syscall_illegal_args();
if (running_thread->process->ipc_dgram_handles == 0 ||
handle >= (uint64_t)running_thread->process->ipc_dgram_handles_buffer_size ||
running_thread->process->ipc_dgram_handles[handle].box == 0 ||
running_thread->process->ipc_dgram_handles[handle].is_receiver != 0)
return IPR_BAD_HANDLE;
struct ipc_dgram_box *box = running_thread->process->ipc_dgram_handles[handle].box;
return send_ipc_dgram(box, data, bytes);
}

View file

@ -21,6 +21,9 @@
#include <kernel-public/framebuffer.h> #include <kernel-public/framebuffer.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h>
struct ipc_dgram_box;
struct process_file_info { struct process_file_info {
const struct fs_info *fs; const struct fs_info *fs;
@ -28,6 +31,13 @@ struct process_file_info {
struct fs_stat stat; struct fs_stat stat;
}; };
struct process_ipc_dgram_handle_info {
//0 if this handle number is not used.
struct ipc_dgram_box *box;
//1 if this is a receiver handle, 0 if this is a sender handle.
int is_receiver;
};
struct process { struct process {
uint64_t p4_physical_base; uint64_t p4_physical_base;
@ -42,6 +52,9 @@ struct process {
struct process_file_info *files; struct process_file_info *files;
int files_buffer_size; int files_buffer_size;
struct process_ipc_dgram_handle_info *ipc_dgram_handles;
int ipc_dgram_handles_buffer_size;
//0 for missing levels. just bottom p3 of address space. //0 for missing levels. just bottom p3 of address space.
uint64_t *p2_virtual_bases[512]; uint64_t *p2_virtual_bases[512];
uint64_t **p1_virtual_bases[512]; uint64_t **p1_virtual_bases[512];
@ -97,9 +110,13 @@ int load_elf(
//starts user mode at the elf's entry point. //starts user mode at the elf's entry point.
int start_elf(const char *uri); int start_elf(const char *uri);
int syscall_start_elf(const char *uri);
void destroy_process(struct process *process); void destroy_process(struct process *process);
void destroy_thread(struct thread *thread); void destroy_thread(struct thread *thread);
[[noreturn]] void syscall_illegal_args();
//returs 1 if [start, start + length) is writable by process, otherwise 0. //returs 1 if [start, start + length) is writable by process, otherwise 0.
int is_mapped_writable(struct process *process, const void *start, uint64_t length); int is_mapped_writable(struct process *process, const void *start, uint64_t length);
@ -119,3 +136,22 @@ enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *byte
enum fs_access_result syscall_read_file(struct read_file_parameter *parameter); enum fs_access_result syscall_read_file(struct read_file_parameter *parameter);
void *syscall_map_pages(uint64_t count); void *syscall_map_pages(uint64_t count);
enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
const char *address, ipc_dgram_receiver_handle_t *handle_out);
enum ipc_dgram_result syscall_ipc_create_dgram_sender(
const char *address, ipc_dgram_sender_handle_t *handle_out);
//on entry, bytes is maximum accepted packet length.
//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length.
//actual packet length will always be positive.
enum ipc_dgram_result syscall_ipc_receive_dgram(
ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes);
//bytes must be positive.
enum ipc_dgram_result syscall_ipc_send_dgram(
ipc_dgram_sender_handle_t handle, const void *data, int bytes);
//f should not return.
void syscall_create_thread(void (*f)(uint64_t x), uint64_t x);

View file

@ -19,6 +19,8 @@ bits 64
default rel default rel
extern resume_next_continuation extern resume_next_continuation
extern running_thread
extern add_to_queue
;referenced in scheduler.c ;referenced in scheduler.c
global resume_continuation global resume_continuation
@ -32,15 +34,14 @@ resume_continuation:
mov r13, qword [rdi + 40] mov r13, qword [rdi + 40]
mov r14, qword [rdi + 48] mov r14, qword [rdi + 48]
mov r15, qword [rdi + 56] mov r15, qword [rdi + 56]
mov rdi, qword [rdi + 64]
mov qword [running_thread], rdi
jmp rax jmp rax
ret:
ret
global yield global yield
yield: yield:
mov qword [rdi], ret mov qword [rdi], .ret
mov qword [rdi + 8], rbx mov qword [rdi + 8], rbx
mov qword [rdi + 16], rbp mov qword [rdi + 16], rbp
mov qword [rdi + 24], rsp mov qword [rdi + 24], rsp
@ -48,6 +49,8 @@ yield:
mov qword [rdi + 40], r13 mov qword [rdi + 40], r13
mov qword [rdi + 48], r14 mov qword [rdi + 48], r14
mov qword [rdi + 56], r15 mov qword [rdi + 56], r15
mov qword rsi, qword [running_thread]
mov qword [rdi + 64], rsi
jmp resume_next_continuation jmp resume_next_continuation
@ -56,7 +59,7 @@ yield:
global yield_sti global yield_sti
yield_sti: yield_sti:
mov qword [rdi], ret mov qword [rdi], .ret
mov qword [rdi + 8], rbx mov qword [rdi + 8], rbx
mov qword [rdi + 16], rbp mov qword [rdi + 16], rbp
mov qword [rdi + 24], rsp mov qword [rdi + 24], rsp
@ -64,6 +67,8 @@ yield_sti:
mov qword [rdi + 40], r13 mov qword [rdi + 40], r13
mov qword [rdi + 48], r14 mov qword [rdi + 48], r14
mov qword [rdi + 56], r15 mov qword [rdi + 56], r15
mov qword rsi, qword [running_thread]
mov qword [rdi + 64], rsi
sti sti
@ -71,3 +76,24 @@ yield_sti:
.ret: .ret:
ret ret
global yield_to_queue
yield_to_queue:
mov qword [rsp - 72], .ret
mov qword [rsp - 64], rbx
mov qword [rsp - 56], rbp
mov qword [rsp - 48], rsp
mov qword [rsp - 40], r12
mov qword [rsp - 32], r13
mov qword [rsp - 24], r14
mov qword [rsp - 16], r15
mov rdx, qword [running_thread]
mov qword [rsp - 8], rdx
sub rsp, 72
mov rsi, rsp
call add_to_queue
jmp resume_continuation
.ret:
ret

View file

@ -16,7 +16,9 @@
*/ */
#include "scheduler.h" #include "scheduler.h"
#include "process.h"
#include "utility.h" #include "utility.h"
#include "panic.h"
#include "heap.h" #include "heap.h"
struct continuation_queue ready_continuations; struct continuation_queue ready_continuations;
@ -30,11 +32,14 @@ void init_scheduler() {
[[noreturn]] void resume_next_continuation() { [[noreturn]] void resume_next_continuation() {
struct continuation_info ci; running_thread = 0;
struct continuation_info ci;
while (!take_from_queue(&ready_continuations, &ci)) while (!take_from_queue(&ready_continuations, &ci))
__asm__ ("hlt"); __asm__ ("hlt");
assert(running_thread == 0)
assert(ci.running_thread != 0)
resume_continuation(&ci); resume_continuation(&ci);
} }

View file

@ -17,6 +17,8 @@
#pragma once #pragma once
#include "process.h"
#include <stdint.h> #include <stdint.h>
struct continuation_info { struct continuation_info {
@ -28,6 +30,7 @@ struct continuation_info {
uint64_t r13; uint64_t r13;
uint64_t r14; uint64_t r14;
uint64_t r15; uint64_t r15;
struct thread *running_thread;
}; };
void init_scheduler(); void init_scheduler();
@ -57,3 +60,6 @@ void yield(struct continuation_info *save_current_continuation_to);
//save as yield but enables interrupts between saving this continuation and resuming next ready one. //save as yield but enables interrupts between saving this continuation and resuming next ready one.
void yield_sti(struct continuation_info *save_current_continuation_to); void yield_sti(struct continuation_info *save_current_continuation_to);
//same as yield, but instead of saving continuation to a pointer, saves it to the end of the queue.
void yield_to_queue(struct continuation_queue *queue);

View file

@ -27,7 +27,8 @@ void register_syscall(
uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3)); uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3));
//the top of the syscall stack of the most recently started syscall. //the top of the syscall stack of the most recently started syscall.
//kind of a hack, used in the end_thread syscall to deallocate that stack. //kind of a hack, used in the end_thread syscall to deallocate that stack
//and in the fork_thread syscall to copy the stack.
extern void *most_recent_syscall_stack; extern void *most_recent_syscall_stack;
//also kind of a hack. switches to temporary stack and runs f with that stack. //also kind of a hack. switches to temporary stack and runs f with that stack.

View file

@ -15,6 +15,7 @@
* with this program. If not, see <https://www.gnu.org/licenses/>. * with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "kernel-public/ipc.h"
#include <calcite/syscalls.h> #include <calcite/syscalls.h>
#include <silver/pam.h> #include <silver/pam.h>
@ -59,14 +60,37 @@ static void copy_frame() {
} }
} }
void main() { [[noreturn]] static void panic() {
for (int y = 0; y < fbb->height; ++y)
for (int x = 0; x < fbb->width; ++x) {
struct pixel *pixel = &fbb->pixels[y * fbb->width + x];
pixel->r = (x + y) * 256 / (fbb->height + fbb->width);
pixel->g = 0;
pixel->b = 0;
}
copy_frame();
end_thread();
}
if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image)) void main() {
return;
map_framebuffer(&fb_info); map_framebuffer(&fb_info);
create_image(fb_info.fb_width, fb_info.fb_height, &fbb); create_image(fb_info.fb_width, fb_info.fb_height, &fbb);
ipc_dgram_receiver_handle_t receiver;
if (ipc_create_dgram_receiver("calcite-test", &receiver) != IPR_SUCCESS)
panic();
char buffer[5];
int bytes = 5;
if (ipc_receive_dgram(receiver, buffer, &bytes) != IPR_SUCCESS ||
bytes != 5 ||
buffer[0] != 'h' || buffer[1] != 'e' || buffer[2] != 'l' || buffer[3] != 'l' || buffer[4] != 'o')
panic();
if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image))
panic();
int cursor_x = fb_info.fb_width / 2; int cursor_x = fb_info.fb_width / 2;
int cursor_y = fb_info.fb_height / 2; int cursor_y = fb_info.fb_height / 2;

83
src/user-apps/init/init.c Normal file
View file

@ -0,0 +1,83 @@
/* Calcite, src/user-apps/init/init.c
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <calcite/file-streams.h>
#include <kernel-public/files.h>
#include <kernel-public/ipc.h>
#include <calcite/syscalls.h>
const char *hello = "hello";
[[noreturn]] void test(const char *message) {
ipc_dgram_sender_handle_t handle;
if (ipc_create_dgram_sender("calcite-test", &handle) == IPR_SUCCESS)
ipc_send_dgram(handle, message, 5);
end_thread();
}
#define MAX_LINE_LENGTH 1023
void main() {
create_thread((void *)&test, (uint64_t)hello);
struct file_stream *rc;
if (open_file_stream("root://calcite/init.rc", &rc) != FAR_SUCCESS)
return;
int at_file_end = 0;
while (!at_file_end) {
char line[MAX_LINE_LENGTH + 1];
int line_length = 0;
while (1) {
char ch = read_file_stream_byte(rc);
if (ch == -1) {
at_file_end = 1;
break;
}
if (ch == '\n')
break;
if (line_length == MAX_LINE_LENGTH) {
while (1) {
ch = read_file_stream_byte(rc);
if (ch == -1 || ch == '\n')
break;
}
break;
}
line[line_length++] = ch;
}
if (line_length == 0 || line[0] == '#')
continue;
line[line_length] = 0;
start_elf(line);
}
}

View file

@ -0,0 +1 @@
calcite

View file

@ -17,6 +17,7 @@
#include <kernel-public/syscall-numbers.h> #include <kernel-public/syscall-numbers.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h>
#include <calcite/syscalls.h> #include <calcite/syscalls.h>
//defined in syscalls.asm //defined in syscalls.asm
@ -64,3 +65,30 @@ void *map_pages(uint64_t count) {
void sleep_ms(uint64_t ms_to_sleep) { void sleep_ms(uint64_t ms_to_sleep) {
do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS); do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS);
} }
int start_elf(const char *uri) {
return do_syscall((uint64_t)uri, 0, 0, SYSCALL_START_ELF);
}
enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out) {
return do_syscall(
(uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_RECEIVER);
}
enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out) {
return do_syscall(
(uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_SENDER);
}
enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) {
return do_syscall(
handle, (uint64_t)buffer, (uint64_t)bytes, SYSCALL_IPC_RECEIVE_DGRAM);
}
enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes) {
return do_syscall(handle, (uint64_t)data, bytes, SYSCALL_IPC_SEND_DGRAM);
}
void create_thread(void (*f)(uint64_t x), uint64_t x) {
do_syscall((uint64_t)f, x, 0, SYSCALL_CREATE_THREAD);
}