This commit is contained in:
2026-07-15 22:09:13 +02:00
commit cf33867e68
152 changed files with 9663 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
#pragma once
// These hints may improve code generation, but are optional and can be stubbed.
#if defined(__GNUC__) || defined(__clang__)
#define __cold gnu::cold
#define __hot gnu::hot
#define __used gnu::used
#define __unused gnu::unused
#define __format(like, ...) gnu::format(like, __VA_ARGS__)
#define __likely(x) __builtin_expect(!!(x), 1)
#define __unlikely(x) __builtin_expect(!!(x), 0)
#define __always_inline gnu::always_inline
#else
#define __cold
#define __hot
#define __used
#define __unused
#define __format(like, ...)
#define __likely(x) x
#define __unlikely(x) x
#define __always_inline
#endif
// Only clang has these hints, GCC will throw a warning here.
// They also don't support the C23 attribute format.
#ifdef __clang__
#define __user __attribute__((noderef, address_space(1)))
#define __phys __attribute__((noderef, address_space(2)))
#else
#define __user
#define __phys
#endif
// Attributes that affect data layout. Not optional.
#if defined(__GNUC__) || defined(__clang__)
#define __weak gnu::weak
#define __section(x) gnu::section(x)
#define __aligned(x) gnu::aligned(x)
#define __packed gnu::packed
#define __naked gnu::naked
#define __alias(x) gnu::alias(x)
#else
#error "Unsupported compiler!"
#endif
// Intrinsics
#define __unreachable() __builtin_unreachable()
+371
View File
@@ -0,0 +1,371 @@
#pragma once
#include <stdint.h>
#ifdef __x86_64__
#define ELF_ARCH_CLASS ELFCLASS64
#define ELF_ARCH_DATA ELFDATA2LSB
#define ELF_ARCH_MACHINE EM_X86_64
#define ELF_R_SYM ELF64_R_SYM
#define ELF_R_TYPE ELF64_R_TYPE
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
#define elf_ehdr elf64_ehdr
#define elf_phdr elf64_phdr
#define elf_dyn elf64_dyn
#define elf_addr elf64_addr
#define elf_off elf64_off
#define elf_nhdr elf64_nhdr
#define elf_auxv elf64_auxv
#define elf_sym elf64_sym
#define elf_rela elf64_rela
#define elf_rel elf64_rel
enum {
R_NONE = 0,
R_COPY = 5,
R_GLOB_DAT = 6,
R_JUMP_SLOT = 7,
R_RELATIVE = 8,
R_IRELATIVE = 37,
};
#endif
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
typedef uint64_t elf64_addr_t;
typedef uint64_t elf64_off_t;
typedef uint32_t elf32_addr_t;
typedef uint32_t elf32_off_t;
enum : uint8_t {
EI_MAG0 = 0,
EI_MAG1 = 1,
EI_MAG2 = 2,
EI_MAG3 = 3,
EI_CLASS = 4,
EI_DATA = 5,
EI_VERSION = 6,
EI_OSABI = 7,
EI_ABIVERSION = 8,
EI_PAD = 9,
EI_NIDENT = 16,
};
enum : uint16_t {
EM_X86_64 = 62,
EM_AARCH64 = 183,
EM_RISCV = 243,
EM_LOONGARCH = 258,
};
enum : uint32_t {
ELFCLASS32 = 1,
ELFCLASS64 = 2,
};
enum : uint32_t {
ELFDATA2LSB = 1,
ELFDATA2MSB = 2,
};
enum : uint32_t {
EV_NONE = 0,
EV_CURRENT = 1,
EV_NUM = 2,
};
enum : uint32_t {
ELFOSABI_SYSV = 0,
ELFOSABI_HPUX = 1,
ELFOSABI_STANDALONE = 255,
};
enum : uint32_t {
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
};
enum : uint32_t {
PT_NULL = 0x00000000,
PT_LOAD = 0x00000001,
PT_DYNAMIC = 0x00000002,
PT_INTERP = 0x00000003,
PT_NOTE = 0x00000004,
PT_SHLIB = 0x00000005,
PT_PHDR = 0x00000006,
PT_TLS = 0x00000007,
};
enum : uint32_t {
PF_X = 0x00000001,
PF_W = 0x00000002,
PF_R = 0x00000004,
};
enum : uint32_t {
DT_NULL = 0,
DT_NEEDED = 1,
DT_PLTRELSZ = 2,
DT_PLTGOT = 3,
DT_HASH = 4,
DT_STRTAB = 5,
DT_SYMTAB = 6,
DT_RELA = 7,
DT_RELASZ = 8,
DT_RELAENT = 9,
DT_STRSZ = 10,
DT_SYMENT = 11,
DT_INIT = 12,
DT_FINI = 13,
DT_SONAME = 14,
DT_RPATH = 15,
DT_SYMBOLIC = 16,
DT_REL = 17,
DT_RELSZ = 18,
DT_RELENT = 19,
DT_PLTREL = 20,
DT_DEBUG = 21,
DT_TEXTREL = 22,
DT_JMPREL = 23,
DT_BIND_NOW = 24,
DT_INIT_ARRAY = 25,
DT_FINI_ARRAY = 26,
DT_INIT_ARRAYSZ = 27,
DT_FINI_ARRAYSZ = 28,
DT_LOOS = 0x60000000,
DT_HIOS = 0x6FFFFFFF,
DT_LOPROC = 0x70000000,
DT_HIPROC = 0x7FFFFFFF,
};
enum : uint32_t {
AT_NULL = 0,
AT_IGNORE = 1,
AT_EXECFD = 2,
AT_PHDR = 3,
AT_PHENT = 4,
AT_PHNUM = 5,
AT_PAGESZ = 6,
AT_BASE = 7,
AT_FLAGS = 8,
AT_ENTRY = 9,
AT_NOTELF = 10,
AT_UID = 11,
AT_EUID = 12,
AT_GID = 13,
AT_EGID = 14,
AT_HWCAP = 16,
AT_SECURE = 23,
AT_RANDOM = 25,
AT_EXECFN = 31,
AT_SYSINFO_EHDR = 33,
AT_INIT_HANDLE = 0x1000,
};
struct elf64_ehdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf64_addr_t e_entry;
elf64_off_t e_phoff;
elf64_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct elf32_ehdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf32_addr_t e_entry;
elf32_off_t e_phoff;
elf32_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct elf64_phdr {
uint32_t p_type;
uint32_t p_flags;
elf64_off_t p_offset;
elf64_addr_t p_vaddr;
elf64_addr_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
};
struct elf32_phdr {
uint32_t p_type;
elf32_off_t p_offset;
elf32_addr_t p_vaddr;
elf32_addr_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct elf64_dyn {
int64_t d_tag;
union {
uint64_t d_val;
elf64_addr_t d_ptr;
} d_un;
};
struct elf32_dyn {
int32_t r_offset;
union {
uint32_t d_val;
elf32_addr_t d_ptr;
} d_un;
};
struct elf64_nhdr {
uint64_t n_namesz;
uint64_t n_descsz;
uint64_t n_type;
};
struct elf32_nhdr {
uint32_t n_namesz;
uint32_t n_descsz;
uint32_t n_type;
};
struct elf64_auxv {
uint64_t atype;
uint64_t avalue;
};
struct elf32_auxv {
uint32_t atype;
uint32_t avalue;
};
struct elf_hash {
uint32_t n_buckets;
uint32_t n_chain;
};
struct elf32_sym {
elf32_off_t st_name;
elf32_addr_t st_value;
elf32_off_t st_size;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
};
struct elf64_sym {
uint32_t st_name;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
uint64_t st_value;
uint64_t st_size;
};
static uint8_t ELF64_ST_BIND(uint8_t info) {
return info >> 4;
}
static inline uint8_t ELF64_ST_TYPE(uint8_t info) {
return info & 0x0F;
}
static inline uint8_t ELF64_ST_INFO(uint8_t bind, uint8_t type) {
return (bind << 4) | type;
}
typedef struct {
uint16_t si_boundto;
uint16_t si_flags;
} elf64_syminfo;
static inline uint8_t ELF32_ST_BIND(uint8_t info) {
return info >> 4;
}
static inline uint8_t ELF32_ST_TYPE(uint8_t info) {
return info & 0xF;
}
static inline uint8_t ELF32_ST_INFO(uint8_t bind, uint8_t type) {
return (bind << 4) | (type & 0xF);
}
enum {
STB_GLOBAL = 1,
STB_WEAK = 2,
STB_GNU_UNIQUE = 10,
STB_LOPROC = 13,
STB_HIPROC = 15
};
enum {
STT_NOTYPE = 0,
STT_OBJECT = 1,
STT_FUNC = 2,
STT_TLS = 6,
STT_GNU_IFUNC = 10,
STT_LOPROC = 13,
STT_HIPROC = 15
};
struct elf32_rela {
elf32_addr_t r_offset;
uint32_t r_info;
int32_t r_addend;
};
struct elf64_rela {
elf64_addr_t r_offset;
uint64_t r_info;
int64_t r_addend;
};
struct elf32_rel {
elf32_addr_t r_offset;
uint32_t r_info;
};
struct elf64_rel {
elf64_addr_t r_offset;
uint64_t r_info;
};
static inline uint32_t ELF32_R_SYM(uint32_t info) {
return info >> 8;
}
static inline uint32_t ELF32_R_TYPE(uint32_t info) {
return info & 0xF;
}
static inline uint32_t ELF64_R_SYM(uint64_t info) {
return info >> 32;
}
static inline uint32_t ELF64_R_TYPE(uint64_t info) {
return info & 0xFFFFFFFF;
}
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#define ROUND_UP(value, to) (((value) + ((to) - 1)) / (to))
#define ALIGN_DOWN(value, align) (((value) / (align)) * (align))
#define ALIGN_UP(value, align) (ROUND_UP(value, align) * align)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define BIT_TEST(value, bit) (((value) & (1 << bit)) == (1 << bit))
#if defined(__has_builtin)
#if __has_builtin(__builtin_types_compatible_p)
// Get pointer to parent object given pointer to a struct or union member.
#define CONTAINER_OF(ptr, type, member) \
({ \
_Static_assert( \
__builtin_types_compatible_p(__typeof__(*(ptr)), __typeof__(((type*)0)->member)), \
"Incompatible types for CONTAINER_OF" \
); \
(type*)((size_t)ptr - offsetof(type, member)); \
})
#endif
#endif
#ifndef CONTAINER_OF
// Get pointer to parent object given pointer to a struct or union member.
#define CONTAINER_OF(ptr, type, member) (type*)((size_t)ptr - offsetof(type, member))
#endif
#define CONCAT(A, B) _CONCAT(A, B)
#define _CONCAT(A, B) A##B
// Creates a unique identifier for a compilation unit.
#define UNIQUE_IDENT(ident) CONCAT(__unique_##ident, __COUNTER__)
// Ensures that a type is properly defined at this point.
// This is used to ensure that e.g. architecture-specific structures are defined.
#define ASSERT_TYPE(type) static_assert(sizeof(type) == sizeof(type))
+29
View File
@@ -0,0 +1,29 @@
#ifndef ZINNIA_ARCHCTL_H
#define ZINNIA_ARCHCTL_H
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
// Does nothing.
ZN_ARCHCTL_NONE = 0,
// On x86_64, sets the FSBASE MSR to the value.
ZN_ARCHCTL_SET_FSBASE = 1,
// On x86_64, sets the GSBASE MSR to the value.
ZN_ARCHCTL_SET_GSBASE = 2,
} zn_archctl_t;
// Performs an architecture-dependent operation identified by `op`.
static inline zn_status_t zn_archctl(zn_archctl_t op, void* value) {
return zn_syscall2(op, value, ZN_SYSCALL_ARCHCTL);
}
#ifdef __cplusplus
}
#endif
#endif
+64
View File
@@ -0,0 +1,64 @@
#ifndef ZINNIA_CHANNEL_H
#define ZINNIA_CHANNEL_H
#include <zinnia/handle.h>
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
enum zn_channel_flags {
ZN_CHANNEL_NONE = 0,
};
// Creates a new channel.
static inline zn_status_t zn_channel_create(
enum zn_channel_flags flags,
zn_handle_t* endpoint0,
zn_handle_t* endpoint1
) {
return zn_syscall3(flags, endpoint0, endpoint1, ZN_SYSCALL_CHANNEL_CREATE);
}
// Notifies the peer that there is new data available.
// The kernel only copies the first `num_handles` handles and `num_bytes` bytes.
static inline zn_status_t zn_channel_write(
zn_handle_t channel,
const zn_handle_t* handles,
const void* bytes,
size_t num_handles,
size_t num_bytes
) {
return zn_syscall5(channel, handles, bytes, num_handles, num_bytes, ZN_SYSCALL_CHANNEL_WRITE);
}
// Waits for a new message to appear in the channel.
static inline zn_status_t zn_channel_read(
zn_handle_t channel,
zn_handle_t* handles,
void* bytes,
size_t num_handles,
size_t num_bytes,
size_t* read_handles,
size_t* read_bytes
) {
return zn_syscall7(
channel,
handles,
bytes,
num_handles,
num_bytes,
read_handles,
read_bytes,
ZN_SYSCALL_CHANNEL_READ
);
}
#ifdef __cplusplus
}
#endif
#endif
+26
View File
@@ -0,0 +1,26 @@
#ifndef ZINNIA_FUTEX_H
#define ZINNIA_FUTEX_H
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#ifdef __cplusplus
extern "C" {
#endif
// Blocks the calling thread if *addr == expected.
// Returns immediately if the value at addr has changed.
static inline zn_status_t zn_futex_wait(int* addr, int expected) {
return zn_syscall2(addr, expected, ZN_SYSCALL_FUTEX_WAIT);
}
// Wakes up to `count` threads waiting on the given futex address.
static inline zn_status_t zn_futex_wake(int* addr, int count) {
return zn_syscall2(addr, count, ZN_SYSCALL_FUTEX_WAKE);
}
#ifdef __cplusplus
}
#endif
#endif
+45
View File
@@ -0,0 +1,45 @@
#ifndef ZINNIA_HANDLE_H
#define ZINNIA_HANDLE_H
#include <zinnia/rights.h>
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// A generic object handle.
typedef int32_t zn_handle_t;
#define ZN_HANDLE_INVALID ((zn_handle_t)(0))
#define ZN_HANDLE_THIS_NS ((zn_handle_t)(-1))
#define ZN_HANDLE_THIS_VAS ((zn_handle_t)(-2))
#define ZN_HANDLE_THIS_THREAD ((zn_handle_t)(-3))
#define ZN_HANDLE_ZERO_VMO ((zn_handle_t)(-4))
// Checks an object handle for validity.
static inline zn_status_t zn_handle_validate(zn_handle_t handle) {
return zn_syscall1(handle, ZN_SYSCALL_HANDLE_VALIDATE);
}
// Drops an object handle.
// All further references using this handle are invalid. The numerical value
// may become valid again, but it is an error to keep using it.
// The underlying object gets freed only once no other handle references it.
static inline zn_status_t zn_handle_drop(zn_handle_t handle) {
return zn_syscall1(handle, ZN_SYSCALL_HANDLE_DROP);
}
// Creates a new handle pointing to the same object as pointed to
// by the original handle. The new handle may obtain new rights.
static inline zn_status_t zn_handle_clone(zn_handle_t handle, zn_rights_t cloned_rights, zn_handle_t* cloned) {
return zn_syscall3(handle, cloned_rights, cloned, ZN_SYSCALL_HANDLE_CLONE);
}
#ifdef __cplusplus
}
#endif
#endif
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <zinnia/handle.h>
#include <stddef.h>
#include <stdint.h>
#define IPC_GET_STRING(data, str, buf) \
do { \
memcpy(buf, (data) + (str)->offset, (str)->len); \
} while (0)
#define IPC_SET_STRING(cursor, data, field, string) \
do { \
const size_t _len = strlen(string); \
(field)->offset = cursor; \
(field)->len = _len; \
memcpy(data + cursor, string, _len); \
cursor += _len; \
} while (0)
enum ipc_msg_id {
IPC_MSG_FOO,
IPC_MSG_BAR,
IPC_MSG_BAZ,
};
struct ipc_array {
uint16_t offset;
uint16_t len;
};
struct ipc_option {
uint16_t offset;
};
struct ipc_init_register_rq {
enum ipc_msg_id id;
struct ipc_array name;
char data[];
};
struct ipc_kernel_msg {
enum ipc_msg_id id;
struct ipc_array name;
char data[];
};
+64
View File
@@ -0,0 +1,64 @@
#ifndef ZINNIA_MEM_H
#define ZINNIA_MEM_H
#include <zinnia/handle.h>
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Virtual memory flags.
enum zn_vm_flags {
// Mapping is readable.
ZN_VM_MAP_READ = 1 << 0,
// Mapping is writable.
ZN_VM_MAP_WRITE = 1 << 1,
// Mapping is executable.
ZN_VM_MAP_EXEC = 1 << 2,
// Shared memory (not cloned on calls to fork).
ZN_VM_MAP_SHARED = 1 << 3,
// Map at a fixed address, replacing whatever is in the way.
ZN_VM_MAP_FIXED = 1 << 8,
// Map at a fixed address, failing if anything is in the way.
ZB_VM_MAP_FIXED_NOREPLACE = 1 << 9,
// Map the entire range immediately.
ZN_VM_MAP_POPULATE = 1 << 10,
};
// Creates a new virtual memory object.
static inline zn_status_t zn_vmo_create(size_t length, zn_handle_t* out) {
return zn_syscall2(length, out, ZN_SYSCALL_VMO_CREATE);
}
// Creates a new virtual memory object which points to a contiguous phyiscal memory region.
static inline zn_status_t zn_vmo_create_phys(uintptr_t phys_addr, size_t length, zn_handle_t* out) {
return zn_syscall3(phys_addr, length, out, ZN_SYSCALL_VMO_CREATE_PHYS);
}
// Maps `bytes` of `vmo` at `vmo_offset` to `addr` in `vas`.
static inline zn_status_t zn_vmo_map(
zn_handle_t vmo,
zn_handle_t vas,
uintptr_t vmo_offset,
uintptr_t* addr,
size_t bytes,
enum zn_vm_flags flags
) {
return zn_syscall6(vmo, vas, vmo_offset, addr, bytes, flags, ZN_SYSCALL_VMO_MAP);
}
// Creates a new virtual address space.
static inline zn_status_t zn_vas_create(zn_handle_t* out) {
return zn_syscall1(out, ZN_SYSCALL_VAS_CREATE);
}
#ifdef __cplusplus
}
#endif
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef ZINNIA_NAMESPACE_H
#define ZINNIA_NAMESPACE_H
#include <zinnia/handle.h>
#include <zinnia/status.h>
#ifdef __cplusplus
extern "C" {
#endif
// Creates a new namespace.
static inline zn_status_t zn_ns_create(zn_handle_t* out) {
return ZN_ERR_UNSUPPORTED;
}
static inline zn_status_t zn_ns_move(zn_handle_t handle, zn_handle_t target_namespace) {
return ZN_ERR_UNSUPPORTED;
}
#ifdef __cplusplus
}
#endif
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef ZINNIA_RANDOM_H
#define ZINNIA_RANDOM_H
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
enum {
ZN_RANDOM_MAX_ENTROPY = 0x1000
};
// Stores random bytes in a buffer.
static inline zn_status_t zn_random_get(void* addr, size_t len) {
return zn_syscall2(addr, len, ZN_SYSCALL_RANDOM_GET);
}
// Adds entropy to the RNG.
static inline zn_status_t zn_random_entropy(const void* addr, size_t len) {
return zn_syscall2(addr, len, ZN_SYSCALL_RANDOM_ENTROPY);
}
#ifdef __cplusplus
}
#endif
#endif
+35
View File
@@ -0,0 +1,35 @@
#ifndef ZINNIA_RIGHTS_H
#define ZINNIA_RIGHTS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
// Can be read from.
ZN_RIGHT_READ = 1u << 0,
// Can be written to.
ZN_RIGHT_WRITE = 1u << 1,
// Can be executed.
ZN_RIGHT_EXECUTE = 1u << 2,
// Can be mapped in an address space.
ZN_RIGHT_MAP = 1u << 3,
// Can be moved to another universe.
ZN_RIGHT_MOVE = 1u << 4,
// Can be cloned.
ZN_RIGHT_CLONE = 1u << 5,
// Can be deleted.
ZN_RIGHT_DELETE = 1u << 6,
// When cloning, use the same rights as the original.
ZN_RIGHTS_IDENTICAL = 1u << 31,
ZN_RIGHTS_COMMON = (ZN_RIGHT_MOVE | ZN_RIGHT_CLONE),
ZN_RIGHTS_RW = (ZN_RIGHT_READ | ZN_RIGHT_WRITE),
ZN_RIGHTS_ALL = ~0u,
} zn_rights_t;
#ifdef __cplusplus
}
#endif
#endif
+72
View File
@@ -0,0 +1,72 @@
#ifndef ZINNIA_STATUS_H
#define ZINNIA_STATUS_H
#ifdef __cplusplus
extern "C" {
#endif
// Status values that may be returned by syscalls or other calls.
// A status value of 0 means no error occured.
// A negative value indicates a common error, usually from the kernel.
typedef enum {
ZN_OK = 0,
// An internal error occured.
ZN_ERR_INTERNAL = -1,
// Syscall number is not a recognized syscall.
ZN_ERR_BAD_SYSCALL = -2,
// This operation is not supported or implemented yet.
ZN_ERR_UNSUPPORTED = -3,
// System does not have enough free memory for this operation.
ZN_ERR_NO_MEMORY = -4,
// Process can not own any more handles.
ZN_ERR_NO_HANDLES = -5,
// One or more of the provided arguments is not valid.
ZN_ERR_INVALID = -6,
// Argument is outside of the range for valid values.
ZN_ERR_BAD_RANGE = -7,
// Object handle does not name a valid object or correct type.
ZN_ERR_BAD_HANDLE = -8,
// Object has insufficient permissions for this operation.
ZN_ERR_BAD_PERMS = -9,
// Buffer is not large enough or doesn't point to a valid memory region.
ZN_ERR_BAD_BUFFER = -10,
// The resource already exists.
ZN_ERR_ALREADY_EXISTS = -11,
} zn_status_t;
static inline const char* zn_status_to_string(zn_status_t err) {
switch (err) {
case ZN_OK:
return "No error (OK)";
case ZN_ERR_INTERNAL:
return "An internal error occured (INTERNAL)";
case ZN_ERR_BAD_SYSCALL:
return "Syscall number is not a recognized syscall (BAD_SYSCALL)";
case ZN_ERR_UNSUPPORTED:
return "This operation is not supported or implemented yet (UNSUPPORTED)";
case ZN_ERR_NO_MEMORY:
return "System does not have enough free memory for this operation (NO_MEMORY)";
case ZN_ERR_NO_HANDLES:
return "Process can not own any more handles (NO_HANDLES)";
case ZN_ERR_INVALID:
return "One or more of the provided arguments is not valid (INVALID)";
case ZN_ERR_BAD_RANGE:
return "Argument is outside of the range for valid values (BAD_RANGE)";
case ZN_ERR_BAD_HANDLE:
return "Object handle does not name a valid object (BAD_HANDLE)";
case ZN_ERR_BAD_PERMS:
return "Object has insufficient permissions for this operation (BAD_PERMS)";
case ZN_ERR_BAD_BUFFER:
return "Buffer is not large enough or doesn't point to a valid memory region (BAD_BUFFER)";
case ZN_ERR_ALREADY_EXISTS:
return "The resource already exists (ALREADY_EXISTS)";
}
return "Unknown error code";
}
#ifdef __cplusplus
}
#endif
#endif
+36
View File
@@ -0,0 +1,36 @@
#ifndef ZINNIA_SYSCALL_NUMBERS_H
#define ZINNIA_SYSCALL_NUMBERS_H
typedef enum {
ZN_SYSCALL_LOG,
ZN_SYSCALL_ARCHCTL,
ZN_SYSCALL_PAGE_SIZE,
ZN_SYSCALL_TIME,
ZN_SYSCALL_HANDLE_VALIDATE,
ZN_SYSCALL_HANDLE_DROP,
ZN_SYSCALL_HANDLE_CLONE,
ZN_SYSCALL_VAS_CREATE,
ZN_SYSCALL_VMO_CREATE,
ZN_SYSCALL_VMO_CREATE_PHYS,
ZN_SYSCALL_VMO_MAP,
ZN_SYSCALL_CHANNEL_CREATE,
ZN_SYSCALL_CHANNEL_WAIT,
ZN_SYSCALL_CHANNEL_READ,
ZN_SYSCALL_CHANNEL_WRITE,
ZN_SYSCALL_THREAD_CREATE,
ZN_SYSCALL_THREAD_START,
ZN_SYSCALL_THREAD_EXIT,
ZN_SYSCALL_RANDOM_GET,
ZN_SYSCALL_RANDOM_ENTROPY,
ZN_SYSCALL_FUTEX_WAIT,
ZN_SYSCALL_FUTEX_WAKE,
} zn_syscall_t;
#endif
+252
View File
@@ -0,0 +1,252 @@
#ifndef ZINNIA_SYSCALL_STUBS_H
#define ZINNIA_SYSCALL_STUBS_H
#include <zinnia/status.h>
#include <zinnia/syscall_numbers.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __x86_64__
typedef uint64_t zn_arg_t;
#elif defined(__aarch64__)
typedef uint64_t zn_arg_t;
#elif defined(__riscv) && (__riscv_xlen == 64)
typedef uint64_t zn_arg_t;
#elif defined(__loongarch64)
typedef uint64_t zn_arg_t;
#else
#error "Unsupported architecture!"
#endif
#ifdef __x86_64__
#define ASM_REG_NUM "rax"
#define ASM_REG_RET "rax"
#define ASM_REG_A0 "rdi"
#define ASM_REG_A1 "rsi"
#define ASM_REG_A2 "rdx"
#define ASM_REG_A3 "r9"
#define ASM_REG_A4 "r8"
#define ASM_REG_A5 "r10"
#define ASM_REG_A6 "r12"
#define ASM_REG_A7 "r13"
#define ASM_SYSCALL "syscall"
#define ASM_CLOBBER "rcx", "r11"
#elif defined(__aarch64__)
#define ASM_REG_NUM "x8"
#define ASM_REG_RET "x0"
#define ASM_REG_A0 "x0"
#define ASM_REG_A1 "x1"
#define ASM_REG_A2 "x2"
#define ASM_REG_A3 "x3"
#define ASM_REG_A4 "x4"
#define ASM_REG_A5 "x5"
#define ASM_REG_A6 "x6"
#define ASM_REG_A7 "x7"
#define ASM_SYSCALL "svc 0"
#define ASM_CLOBBER
#elif defined(__riscv) && (__riscv_xlen == 64)
#define ASM_REG_NUM "t3"
#define ASM_REG_RET "a0"
#define ASM_REG_A0 "a0"
#define ASM_REG_A1 "a1"
#define ASM_REG_A2 "a2"
#define ASM_REG_A3 "a3"
#define ASM_REG_A4 "a4"
#define ASM_REG_A5 "a5"
#define ASM_REG_A5 "a6"
#define ASM_REG_A5 "a7"
#define ASM_SYSCALL "ecall"
#define ASM_CLOBBER
#elif defined(__loongarch64)
#define ASM_REG_NUM "a7"
#define ASM_REG_RET "a0"
#define ASM_REG_A0 "a0"
#define ASM_REG_A1 "a1"
#define ASM_REG_A2 "a2"
#define ASM_REG_A3 "a3"
#define ASM_REG_A4 "a4"
#define ASM_REG_A5 "a5"
// TODO: Define ABI
#define ASM_REG_A6 TODO
#define ASM_REG_A7 TODO
#define ASM_SYSCALL "syscall 0"
#define ASM_CLOBBER
#else
#error "Unsupported architecture!"
#endif
static inline zn_status_t zn_syscall0(zn_syscall_t num) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
asm volatile(ASM_SYSCALL : "=r"(value) : "r"(rnum) : "memory", ASM_CLOBBER);
return value;
}
static inline zn_status_t zn_syscall1(zn_arg_t a0, zn_syscall_t num) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
asm volatile(ASM_SYSCALL : "=r"(value) : "r"(rnum), "r"(r0) : "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall1(a0, num) zn_syscall1((zn_arg_t)a0, num)
static inline zn_status_t zn_syscall2(zn_arg_t a0, zn_arg_t a1, zn_syscall_t num) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
asm volatile(ASM_SYSCALL : "=r"(value) : "r"(rnum), "r"(r0), "r"(r1) : "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall2(a0, a1, num) zn_syscall2((zn_arg_t)a0, (zn_arg_t)a1, num)
static inline zn_status_t zn_syscall3(zn_arg_t a0, zn_arg_t a1, zn_arg_t a2, zn_syscall_t num) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
register zn_arg_t r2 asm(ASM_REG_A2) = a2;
asm volatile(ASM_SYSCALL : "=r"(value) : "r"(rnum), "r"(r0), "r"(r1), "r"(r2) : "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall3(a0, a1, a2, num) zn_syscall3((zn_arg_t)a0, (zn_arg_t)a1, (zn_arg_t)a2, num)
static inline zn_status_t zn_syscall4(zn_arg_t a0, zn_arg_t a1, zn_arg_t a2, zn_arg_t a3, zn_syscall_t num) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
register zn_arg_t r2 asm(ASM_REG_A2) = a2;
register zn_arg_t r3 asm(ASM_REG_A3) = a3;
asm volatile(ASM_SYSCALL : "=r"(value) : "r"(rnum), "r"(r0), "r"(r1), "r"(r2), "r"(r3) : "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall4(a0, a1, a2, a3, num) zn_syscall4((zn_arg_t)a0, (zn_arg_t)a1, (zn_arg_t)a2, (zn_arg_t)a3, num)
static inline zn_status_t zn_syscall5(
zn_arg_t a0,
zn_arg_t a1,
zn_arg_t a2,
zn_arg_t a3,
zn_arg_t a4,
zn_syscall_t num
) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
register zn_arg_t r2 asm(ASM_REG_A2) = a2;
register zn_arg_t r3 asm(ASM_REG_A3) = a3;
register zn_arg_t r4 asm(ASM_REG_A4) = a4;
asm volatile(ASM_SYSCALL
: "=r"(value)
: "r"(rnum), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4)
: "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall5(a0, a1, a2, a3, a4, num) \
zn_syscall5((zn_arg_t)a0, (zn_arg_t)a1, (zn_arg_t)a2, (zn_arg_t)a3, (zn_arg_t)a4, num)
static inline zn_status_t zn_syscall6(
zn_arg_t a0,
zn_arg_t a1,
zn_arg_t a2,
zn_arg_t a3,
zn_arg_t a4,
zn_arg_t a5,
zn_syscall_t num
) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
register zn_arg_t r2 asm(ASM_REG_A2) = a2;
register zn_arg_t r3 asm(ASM_REG_A3) = a3;
register zn_arg_t r4 asm(ASM_REG_A4) = a4;
register zn_arg_t r5 asm(ASM_REG_A5) = a5;
asm volatile(ASM_SYSCALL
: "=r"(value)
: "r"(rnum), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5)
: "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall6(a0, a1, a2, a3, a4, a5, num) \
zn_syscall6((zn_arg_t)a0, (zn_arg_t)a1, (zn_arg_t)a2, (zn_arg_t)a3, (zn_arg_t)a4, (zn_arg_t)a5, num)
static inline zn_status_t zn_syscall7(
zn_arg_t a0,
zn_arg_t a1,
zn_arg_t a2,
zn_arg_t a3,
zn_arg_t a4,
zn_arg_t a5,
zn_arg_t a6,
zn_syscall_t num
) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
register zn_arg_t r2 asm(ASM_REG_A2) = a2;
register zn_arg_t r3 asm(ASM_REG_A3) = a3;
register zn_arg_t r4 asm(ASM_REG_A4) = a4;
register zn_arg_t r5 asm(ASM_REG_A5) = a5;
register zn_arg_t r6 asm(ASM_REG_A6) = a6;
asm volatile(ASM_SYSCALL
: "=r"(value)
: "r"(rnum), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5), "r"(r6)
: "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall7(a0, a1, a2, a3, a4, a5, a6, num) \
zn_syscall7((zn_arg_t)a0, (zn_arg_t)a1, (zn_arg_t)a2, (zn_arg_t)a3, (zn_arg_t)a4, (zn_arg_t)a5, (zn_arg_t)a6, num)
static inline zn_status_t zn_syscall8(
zn_arg_t a0,
zn_arg_t a1,
zn_arg_t a2,
zn_arg_t a3,
zn_arg_t a4,
zn_arg_t a5,
zn_arg_t a6,
zn_arg_t a7,
zn_syscall_t num
) {
register zn_syscall_t rnum asm(ASM_REG_NUM) = num;
register zn_status_t value asm(ASM_REG_RET);
register zn_arg_t r0 asm(ASM_REG_A0) = a0;
register zn_arg_t r1 asm(ASM_REG_A1) = a1;
register zn_arg_t r2 asm(ASM_REG_A2) = a2;
register zn_arg_t r3 asm(ASM_REG_A3) = a3;
register zn_arg_t r4 asm(ASM_REG_A4) = a4;
register zn_arg_t r5 asm(ASM_REG_A5) = a5;
register zn_arg_t r6 asm(ASM_REG_A6) = a6;
register zn_arg_t r7 asm(ASM_REG_A7) = a7;
asm volatile(ASM_SYSCALL
: "=r"(value)
: "r"(rnum), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5), "r"(r6), "r"(r7)
: "memory", ASM_CLOBBER);
return value;
}
#define zn_syscall8(a0, a1, a2, a3, a4, a5, a6, a7, num) \
zn_syscall8( \
(zn_arg_t)a0, \
(zn_arg_t)a1, \
(zn_arg_t)a2, \
(zn_arg_t)a3, \
(zn_arg_t)a4, \
(zn_arg_t)a5, \
(zn_arg_t)a6, \
(zn_arg_t)a7, \
num \
)
#ifdef __cplusplus
}
#endif
#endif
+37
View File
@@ -0,0 +1,37 @@
#ifndef ZINNIA_SYSTEM_H
#define ZINNIA_SYSTEM_H
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void zn_log(const char* message, size_t len) {
zn_syscall2(message, len, ZN_SYSCALL_LOG);
}
// Returns the page size of the system.
static inline size_t zn_page_size() {
size_t size = 0;
zn_syscall1(&size, ZN_SYSCALL_PAGE_SIZE);
return size;
}
static inline zn_status_t zn_powerctl() {
return ZN_ERR_UNSUPPORTED;
}
static inline uint64_t zn_time() {
uint64_t time = 0;
zn_syscall1(&time, ZN_SYSCALL_TIME);
return time;
}
#ifdef __cplusplus
}
#endif
#endif
+43
View File
@@ -0,0 +1,43 @@
#ifndef ZINNIA_THREAD_H
#define ZINNIA_THREAD_H
#include <zinnia/handle.h>
#include <zinnia/status.h>
#include <zinnia/syscall_stubs.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
enum zn_thread_flags {
ZN_THREAD_NONE = 0,
};
// Creates a new thread in the given namespace.
static inline zn_status_t zn_thread_create(
zn_handle_t ns,
const char* name,
size_t name_len,
enum zn_thread_flags flags,
zn_handle_t* out
) {
return zn_syscall5(ns, name, name_len, flags, out, ZN_SYSCALL_THREAD_CREATE);
}
// Starts executing a thread.
static inline zn_status_t zn_thread_start(zn_handle_t thread, uintptr_t ip, uintptr_t sp, uintptr_t arg) {
return zn_syscall4(thread, ip, sp, arg, ZN_SYSCALL_THREAD_START);
}
// Stops execution of the calling thread.
static inline void zn_thread_exit(void) {
zn_syscall0(ZN_SYSCALL_THREAD_EXIT);
}
#ifdef __cplusplus
}
#endif
#endif