ipc and thread spawning
This commit is contained in:
parent
02e855c066
commit
4f9bf8afef
18 changed files with 681 additions and 32 deletions
1
disk/calcite/init.rc
Normal file
1
disk/calcite/init.rc
Normal file
|
|
@ -0,0 +1 @@
|
|||
root://calcite/apps/hello/hello.elf
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include <kernel-public/framebuffer.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/input.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
|
||||
[[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 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);
|
||||
|
|
|
|||
38
include/kernel-public/ipc.h
Normal file
38
include/kernel-public/ipc.h
Normal 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;
|
||||
|
|
@ -26,5 +26,11 @@ enum {
|
|||
SYSCALL_READ_FILE,
|
||||
SYSCALL_WAIT_FOR_MOUSE_PACKET,
|
||||
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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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_MAP_PAGES, (void *)&syscall_map_pages);
|
||||
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
|
||||
|
||||
|
|
@ -288,12 +294,12 @@ static const char *cmdline_look_up(const char *key) {
|
|||
|
||||
set_fs("root", root_fs);
|
||||
|
||||
//load hello and start it
|
||||
//load init and start it
|
||||
|
||||
init_scheduler();
|
||||
|
||||
if (!start_elf("root://calcite/apps/hello/hello.elf"))
|
||||
panic("could not start hello.elf")
|
||||
if (!start_elf("root://calcite/apps/init/init.elf"))
|
||||
panic("could not start init.elf")
|
||||
|
||||
resume_next_continuation();
|
||||
|
||||
|
|
|
|||
147
src/kernel/ipc-dgram.c
Normal file
147
src/kernel/ipc-dgram.c
Normal 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
58
src/kernel/ipc-dgram.h
Normal 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);
|
||||
|
|
@ -18,28 +18,19 @@
|
|||
bits 64
|
||||
default rel
|
||||
|
||||
extern running_thread
|
||||
|
||||
section .text
|
||||
|
||||
;referenced in process.c
|
||||
global thread_start
|
||||
thread_start:
|
||||
|
||||
mov rax, qword [running_thread]
|
||||
test rax, rax
|
||||
jnz .assert_fail
|
||||
|
||||
mov qword [running_thread], rbx
|
||||
|
||||
mov cr3, rbp
|
||||
mov rcx, r12
|
||||
mov r11, 0x200
|
||||
mov rdi, r13
|
||||
|
||||
xor rax, rax
|
||||
xor rbx, rbx
|
||||
xor rdx, rdx
|
||||
xor rdi, rdi
|
||||
xor rsi, rsi
|
||||
xor rbp, rbp
|
||||
xor r8, r8
|
||||
|
|
@ -51,7 +42,3 @@ thread_start:
|
|||
xor r15, r15
|
||||
|
||||
o64 sysret
|
||||
|
||||
.assert_fail:
|
||||
;could be nice to handle this more gracefully in the future
|
||||
ud2
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "ipc-dgram.h"
|
||||
#include "scheduler.h"
|
||||
#include "syscalls.h"
|
||||
#include "process.h"
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
#include "fs.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) {
|
||||
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->files = 0;
|
||||
process_out->ipc_dgram_handles = 0;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -334,11 +337,11 @@ int load_elf(
|
|||
}
|
||||
|
||||
//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
|
||||
// rsp = value of rsp when we jump here
|
||||
// rip = rcx = value of r12 when we jump here
|
||||
// rflags = r11 = 0x200 (IF)
|
||||
// rdi = value of r13 when we jump here
|
||||
// all other registers zeroed
|
||||
extern uint8_t thread_start;
|
||||
|
||||
|
|
@ -365,10 +368,11 @@ int start_elf(const char *uri) {
|
|||
|
||||
struct continuation_info ci;
|
||||
ci.rip = (uint64_t)&thread_start;
|
||||
ci.rbx = (uint64_t)thread;
|
||||
ci.rbp = thread->process->p4_physical_base;
|
||||
ci.rsp = (uint64_t)thread->stack_top;
|
||||
ci.r12 = entry;
|
||||
ci.r13 = 0;
|
||||
ci.running_thread = thread;
|
||||
|
||||
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) {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
if (process->p3_virtual_base[p3i]) {
|
||||
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();
|
||||
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);
|
||||
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")
|
||||
}
|
||||
|
||||
|
|
@ -626,3 +671,136 @@ void *syscall_map_pages(uint64_t count) {
|
|||
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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include <kernel-public/framebuffer.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
|
||||
struct ipc_dgram_box;
|
||||
|
||||
struct process_file_info {
|
||||
const struct fs_info *fs;
|
||||
|
|
@ -28,6 +31,13 @@ struct process_file_info {
|
|||
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 {
|
||||
|
||||
uint64_t p4_physical_base;
|
||||
|
|
@ -42,6 +52,9 @@ struct process {
|
|||
struct process_file_info *files;
|
||||
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.
|
||||
uint64_t *p2_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.
|
||||
int start_elf(const char *uri);
|
||||
|
||||
int syscall_start_elf(const char *uri);
|
||||
|
||||
void destroy_process(struct process *process);
|
||||
void destroy_thread(struct thread *thread);
|
||||
|
||||
[[noreturn]] void syscall_illegal_args();
|
||||
|
||||
//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);
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ bits 64
|
|||
default rel
|
||||
|
||||
extern resume_next_continuation
|
||||
extern running_thread
|
||||
extern add_to_queue
|
||||
|
||||
;referenced in scheduler.c
|
||||
global resume_continuation
|
||||
|
|
@ -32,15 +34,14 @@ resume_continuation:
|
|||
mov r13, qword [rdi + 40]
|
||||
mov r14, qword [rdi + 48]
|
||||
mov r15, qword [rdi + 56]
|
||||
mov rdi, qword [rdi + 64]
|
||||
mov qword [running_thread], rdi
|
||||
|
||||
jmp rax
|
||||
|
||||
ret:
|
||||
ret
|
||||
|
||||
global yield
|
||||
yield:
|
||||
mov qword [rdi], ret
|
||||
mov qword [rdi], .ret
|
||||
mov qword [rdi + 8], rbx
|
||||
mov qword [rdi + 16], rbp
|
||||
mov qword [rdi + 24], rsp
|
||||
|
|
@ -48,6 +49,8 @@ yield:
|
|||
mov qword [rdi + 40], r13
|
||||
mov qword [rdi + 48], r14
|
||||
mov qword [rdi + 56], r15
|
||||
mov qword rsi, qword [running_thread]
|
||||
mov qword [rdi + 64], rsi
|
||||
|
||||
jmp resume_next_continuation
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ yield:
|
|||
|
||||
global yield_sti
|
||||
yield_sti:
|
||||
mov qword [rdi], ret
|
||||
mov qword [rdi], .ret
|
||||
mov qword [rdi + 8], rbx
|
||||
mov qword [rdi + 16], rbp
|
||||
mov qword [rdi + 24], rsp
|
||||
|
|
@ -64,6 +67,8 @@ yield_sti:
|
|||
mov qword [rdi + 40], r13
|
||||
mov qword [rdi + 48], r14
|
||||
mov qword [rdi + 56], r15
|
||||
mov qword rsi, qword [running_thread]
|
||||
mov qword [rdi + 64], rsi
|
||||
|
||||
sti
|
||||
|
||||
|
|
@ -71,3 +76,24 @@ yield_sti:
|
|||
|
||||
.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
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@
|
|||
*/
|
||||
|
||||
#include "scheduler.h"
|
||||
#include "process.h"
|
||||
#include "utility.h"
|
||||
#include "panic.h"
|
||||
#include "heap.h"
|
||||
|
||||
struct continuation_queue ready_continuations;
|
||||
|
|
@ -30,11 +32,14 @@ void init_scheduler() {
|
|||
|
||||
[[noreturn]] void resume_next_continuation() {
|
||||
|
||||
struct continuation_info ci;
|
||||
running_thread = 0;
|
||||
|
||||
struct continuation_info ci;
|
||||
while (!take_from_queue(&ready_continuations, &ci))
|
||||
__asm__ ("hlt");
|
||||
|
||||
assert(running_thread == 0)
|
||||
assert(ci.running_thread != 0)
|
||||
resume_continuation(&ci);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "process.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct continuation_info {
|
||||
|
|
@ -28,6 +30,7 @@ struct continuation_info {
|
|||
uint64_t r13;
|
||||
uint64_t r14;
|
||||
uint64_t r15;
|
||||
struct thread *running_thread;
|
||||
};
|
||||
|
||||
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.
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ void register_syscall(
|
|||
uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3));
|
||||
|
||||
//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;
|
||||
|
||||
//also kind of a hack. switches to temporary stack and runs f with that stack.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "kernel-public/ipc.h"
|
||||
#include <calcite/syscalls.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))
|
||||
return;
|
||||
void main() {
|
||||
|
||||
map_framebuffer(&fb_info);
|
||||
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_y = fb_info.fb_height / 2;
|
||||
|
||||
|
|
|
|||
83
src/user-apps/init/init.c
Normal file
83
src/user-apps/init/init.c
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1
src/user-apps/init/libraries.txt
Normal file
1
src/user-apps/init/libraries.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
calcite
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <kernel-public/syscall-numbers.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
#include <calcite/syscalls.h>
|
||||
|
||||
//defined in syscalls.asm
|
||||
|
|
@ -64,3 +65,30 @@ void *map_pages(uint64_t count) {
|
|||
void sleep_ms(uint64_t ms_to_sleep) {
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue