stuff
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <bits/mem.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum alloc_flags {
|
||||
ALLOC_NOZERO = 1 << 0, // Don't zero out the allocated memory.
|
||||
ALLOC_MEM32 = 1 << 1, // Allocated memory needs to fit inside 32 bits.
|
||||
ALLOC_MEM20 = 1 << 2, // Allocated memory needs to fit inside 20 bits.
|
||||
};
|
||||
|
||||
enum phys_mem_usage {
|
||||
PHYS_RESERVED,
|
||||
PHYS_USABLE,
|
||||
PHYS_STATIC,
|
||||
PHYS_RECLAIMABLE,
|
||||
};
|
||||
|
||||
struct phys_mem {
|
||||
phys_t address;
|
||||
size_t length;
|
||||
enum phys_mem_usage usage;
|
||||
};
|
||||
|
||||
// Base address of the HHDM.
|
||||
extern uintptr_t mem_hhdm_base;
|
||||
#define HHDM_PTR(paddr) (void*)(mem_hhdm_base + (paddr))
|
||||
|
||||
// Allocates a region of memory which can be smaller than the page size.
|
||||
// Returns `nullptr` if the allocator cannot provide an allocation for the given `length` + `flags`.
|
||||
// Always returns `nullptr` if `length` is 0.
|
||||
void* mem_alloc(size_t length, enum alloc_flags flags);
|
||||
|
||||
// Frees an allocation created by `mem_alloc`.
|
||||
// Passing `nullptr` is a no-op.
|
||||
void mem_free(void* mem);
|
||||
|
||||
zn_status_t mem_phys_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out);
|
||||
zn_status_t mem_phys_free(phys_t start, size_t num_pages);
|
||||
|
||||
void mem_init(struct phys_mem* map, size_t length, uintptr_t kernel_virt, phys_t kernel_phys, uintptr_t tmp_hhdm);
|
||||
void slab_init();
|
||||
void mem_phys_bootstrap(struct phys_mem* mem);
|
||||
void mem_phys_init(struct phys_mem* map, size_t length);
|
||||
|
||||
uintptr_t arch_mem_hhdm_addr();
|
||||
uintptr_t arch_vm_pfndb_addr();
|
||||
uintptr_t arch_mem_mapping_addr();
|
||||
size_t arch_mem_page_bits();
|
||||
size_t arch_mem_page_size();
|
||||
size_t arch_mem_level_bits();
|
||||
size_t arch_mem_num_levels();
|
||||
|
||||
static inline size_t mem_high_shift() {
|
||||
return arch_mem_level_bits() * arch_mem_num_levels() + arch_mem_page_bits();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/panic.h>
|
||||
|
||||
#define ASSERT(expr, msg, ...) \
|
||||
({ \
|
||||
if (__unlikely(!(expr))) { \
|
||||
panic( \
|
||||
"In function \"%s\" (%s:%u):\n" \
|
||||
"Assertion \"%s\" failed!\n" msg, \
|
||||
__FUNCTION__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
#expr, \
|
||||
##__VA_ARGS__ \
|
||||
); \
|
||||
} \
|
||||
})
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEBUG_ASSERT(...) ASSERT(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_ASSERT(...) ((void)0)
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <kernel/tailq.h>
|
||||
#include <kernel/wait.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct namespace_desc;
|
||||
|
||||
struct channel_msg {
|
||||
void* data;
|
||||
size_t num_bytes;
|
||||
struct namespace_desc* descs;
|
||||
size_t num_handles;
|
||||
TAILQ_LINK(struct channel_msg) next;
|
||||
};
|
||||
|
||||
struct channel {
|
||||
struct channel* peer;
|
||||
struct spinlock lock;
|
||||
TAILQ_HEAD(struct channel_msg) messages;
|
||||
struct wait_queue waiters;
|
||||
};
|
||||
|
||||
// Creates a linked pair of channel endpoints.
|
||||
zn_status_t channel_create(struct channel** ep0, struct channel** ep1);
|
||||
|
||||
// Writes a message to the peer channel.
|
||||
// On success, ownership of `data` and `descs` transfers to the message.
|
||||
// On failure, the caller retains ownership and must free them.
|
||||
zn_status_t channel_write(
|
||||
struct channel* ch,
|
||||
void* data,
|
||||
size_t num_bytes,
|
||||
struct namespace_desc* descs,
|
||||
size_t num_handles
|
||||
);
|
||||
|
||||
// Non-blocking dequeue of the next message.
|
||||
zn_status_t channel_read(struct channel* ch, struct channel_msg** msg);
|
||||
|
||||
// Non-blocking check for message availability.
|
||||
zn_status_t channel_poll(struct channel* ch);
|
||||
|
||||
// Frees a channel message and its associated data and descriptors.
|
||||
void channel_msg_free(struct channel_msg* msg);
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct clock {
|
||||
const char* name;
|
||||
uint8_t priority;
|
||||
|
||||
void (*reset)(struct clock* c);
|
||||
uint64_t (*get_elapsed_ns)(struct clock* c);
|
||||
};
|
||||
|
||||
// Attempts to set a new clock. Returns true if the clock was changed.
|
||||
bool clock_switch(struct clock* clock);
|
||||
|
||||
// Returns true, if a clock is available.
|
||||
bool clock_available();
|
||||
|
||||
// Gets the current timestamp in nanoseconds since the last reset.
|
||||
uint64_t clock_get_elapsed_ns();
|
||||
|
||||
// Spin for `ns` nanoseconds.
|
||||
void clock_spin_ns(uint64_t ns);
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <common/utils.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define _CMDLINE_OPTION(x, opt_name, opt_func) \
|
||||
static const struct cmdline_option x = { \
|
||||
.name = opt_name, \
|
||||
.option_type = _Generic( \
|
||||
(opt_func), \
|
||||
void (*)(const char*): CMDLINE_STRING, \
|
||||
void (*)(bool): CMDLINE_BOOL, \
|
||||
void (*)(intptr_t): CMDLINE_INTPTR, \
|
||||
void (*)(uintptr_t): CMDLINE_UINTPTR \
|
||||
), \
|
||||
.func = (void*)(opt_func), \
|
||||
}; \
|
||||
[[__used, __section(".cmdline")]] \
|
||||
static const struct cmdline_option* CONCAT(x, _ptr) = &x;
|
||||
|
||||
#define CMDLINE_OPTION(opt_name, opt_func) _CMDLINE_OPTION(UNIQUE_IDENT(cmdline_option), opt_name, opt_func)
|
||||
|
||||
enum cmdline_type {
|
||||
CMDLINE_STRING,
|
||||
CMDLINE_BOOL,
|
||||
CMDLINE_INTPTR,
|
||||
CMDLINE_UINTPTR,
|
||||
};
|
||||
|
||||
struct cmdline_option {
|
||||
// The name of this option.
|
||||
const char* name;
|
||||
// Gets called if this option is present on the command line.
|
||||
union {
|
||||
void (*func_str)(const char* value);
|
||||
void (*func_bool)(bool value);
|
||||
void (*func_intptr)(intptr_t value);
|
||||
void (*func_uintptr)(uintptr_t value);
|
||||
void* func;
|
||||
};
|
||||
enum cmdline_type option_type;
|
||||
};
|
||||
|
||||
// Parses the command line and invokes all options.
|
||||
void cmdline_parse(const char* cmdline);
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <common/utils.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct console {
|
||||
const char* name;
|
||||
void* private;
|
||||
|
||||
// Initializes the console.
|
||||
bool (*init)(struct console* con);
|
||||
// Writes to the console.
|
||||
void (*write)(struct console* con, const char* buf, size_t count);
|
||||
};
|
||||
|
||||
// Initializes a console for logging.
|
||||
void console_init();
|
||||
|
||||
// Writes a message to a console.
|
||||
void console_write(const char* buf, size_t len);
|
||||
|
||||
// Defines a new early console which can be used to log early boot messages.
|
||||
// An early console must not make any heap allocations.
|
||||
#define DEFINE_CONSOLE(con) \
|
||||
[[__used, __section(".console")]] \
|
||||
static struct console* UNIQUE_IDENT(console) = (con)
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/namespace.h>
|
||||
#include <kernel/sched.h>
|
||||
#include <kernel/vas.h>
|
||||
#include <kernel/vmo.h>
|
||||
|
||||
struct exec_info {
|
||||
struct vas* space;
|
||||
struct vmo* file_obj;
|
||||
struct namespace* ns;
|
||||
const char** argv;
|
||||
const char** envp;
|
||||
struct namespace_desc* init_handle;
|
||||
};
|
||||
|
||||
zn_status_t elf_load(struct exec_info* info, struct task** out);
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/wait.h>
|
||||
|
||||
// Initializes the global futex table.
|
||||
void futex_init(void);
|
||||
|
||||
// Blocks the calling task if *addr == expected.
|
||||
zn_status_t futex_wait(__user int* addr, int expected);
|
||||
|
||||
// Wakes up to `count` tasks waiting on the given address.
|
||||
// Returns the number of tasks woken.
|
||||
zn_status_t futex_wake(__user int* addr, int count);
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
@@ -0,0 +1,223 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// FNV-1a hash over raw bytes.
|
||||
static inline uint64_t hashmap_hash_default(const void* key, size_t len) {
|
||||
const uint8_t* data = (const uint8_t*)key;
|
||||
uint64_t hash = 0xcbf29ce484222325;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
hash ^= data[i];
|
||||
hash *= 0x100000001b3;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Byte-wise equality.
|
||||
static inline bool hashmap_eq_default(const void* a, const void* b, size_t len) {
|
||||
return memcmp(a, b, len) == 0;
|
||||
}
|
||||
|
||||
// Ensure a hash is never zero (zero is the empty sentinel).
|
||||
#define _HASHMAP_FIXHASH(h) ((h) == 0 ? (uint64_t)1 : (h))
|
||||
|
||||
#define HASHMAP(K, V) \
|
||||
struct { \
|
||||
K* keys; \
|
||||
V* values; \
|
||||
uint64_t* hashes; \
|
||||
size_t capacity; \
|
||||
size_t count; \
|
||||
}
|
||||
|
||||
#define HASHMAP_INIT(map) \
|
||||
do { \
|
||||
(map)->keys = nullptr; \
|
||||
(map)->values = nullptr; \
|
||||
(map)->hashes = nullptr; \
|
||||
(map)->capacity = 0; \
|
||||
(map)->count = 0; \
|
||||
} while (0)
|
||||
|
||||
#define HASHMAP_DESTROY(map) \
|
||||
do { \
|
||||
mem_free((map)->keys); \
|
||||
mem_free((map)->values); \
|
||||
mem_free((map)->hashes); \
|
||||
(map)->keys = nullptr; \
|
||||
(map)->values = nullptr; \
|
||||
(map)->hashes = nullptr; \
|
||||
(map)->capacity = 0; \
|
||||
(map)->count = 0; \
|
||||
} while (0)
|
||||
|
||||
#define HASHMAP_COUNT(map) ((map)->count)
|
||||
|
||||
// Internal: probe distance from ideal position.
|
||||
#define _HASHMAP_PROBE_DIST(hash, pos, cap) (((pos) - ((hash) & ((cap) - 1))) & ((cap) - 1))
|
||||
|
||||
// Internal: grow and rehash.
|
||||
#define _HASHMAP_GROW(map, hash_fn, eq_fn) \
|
||||
do { \
|
||||
size_t _old_cap = (map)->capacity; \
|
||||
size_t _new_cap = _old_cap == 0 ? 16 : _old_cap * 2; \
|
||||
typeof((map)->keys) _old_keys = (map)->keys; \
|
||||
typeof((map)->values) _old_values = (map)->values; \
|
||||
uint64_t* _old_hashes = (map)->hashes; \
|
||||
(map)->keys = mem_alloc(_new_cap * sizeof(*(map)->keys), ALLOC_NOZERO); \
|
||||
(map)->values = mem_alloc(_new_cap * sizeof(*(map)->values), ALLOC_NOZERO); \
|
||||
(map)->hashes = mem_alloc(_new_cap * sizeof(uint64_t), 0); \
|
||||
(map)->capacity = _new_cap; \
|
||||
(map)->count = 0; \
|
||||
for (size_t _i = 0; _i < _old_cap; _i++) { \
|
||||
if (_old_hashes[_i] != 0) { \
|
||||
_HASHMAP_INSERT_INNER(map, _old_keys[_i], _old_values[_i], _old_hashes[_i]); \
|
||||
} \
|
||||
} \
|
||||
mem_free(_old_keys); \
|
||||
mem_free(_old_values); \
|
||||
mem_free(_old_hashes); \
|
||||
} while (0)
|
||||
|
||||
// Internal: insert with a precomputed hash. Used by grow and the public INSERT macro.
|
||||
#define _HASHMAP_INSERT_INNER(map, k, v, h) \
|
||||
do { \
|
||||
size_t _cap = (map)->capacity; \
|
||||
size_t _pos = (h) & (_cap - 1); \
|
||||
typeof(*(map)->keys) _ik = (k); \
|
||||
typeof(*(map)->values) _iv = (v); \
|
||||
uint64_t _ih = (h); \
|
||||
for (;;) { \
|
||||
if ((map)->hashes[_pos] == 0) { \
|
||||
(map)->keys[_pos] = _ik; \
|
||||
(map)->values[_pos] = _iv; \
|
||||
(map)->hashes[_pos] = _ih; \
|
||||
(map)->count++; \
|
||||
break; \
|
||||
} \
|
||||
size_t _existing_dist = _HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap); \
|
||||
size_t _insert_dist = _HASHMAP_PROBE_DIST(_ih, _pos, _cap); \
|
||||
if (_insert_dist > _existing_dist) { \
|
||||
typeof(*(map)->keys) _tk = (map)->keys[_pos]; \
|
||||
typeof(*(map)->values) _tv = (map)->values[_pos]; \
|
||||
uint64_t _th = (map)->hashes[_pos]; \
|
||||
(map)->keys[_pos] = _ik; \
|
||||
(map)->values[_pos] = _iv; \
|
||||
(map)->hashes[_pos] = _ih; \
|
||||
_ik = _tk; \
|
||||
_iv = _tv; \
|
||||
_ih = _th; \
|
||||
} \
|
||||
_pos = (_pos + 1) & (_cap - 1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Insert or update a key-value pair. Returns ZN_OK or ZN_ERR_NO_MEMORY.
|
||||
#define HASHMAP_INSERT(map, key, val, hash_fn, eq_fn) \
|
||||
({ \
|
||||
zn_status_t _status = ZN_OK; \
|
||||
typeof(*(map)->keys) _k = (key); \
|
||||
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
|
||||
/* Check for existing key first. */ \
|
||||
bool _found = false; \
|
||||
if ((map)->capacity > 0) { \
|
||||
size_t _cap = (map)->capacity; \
|
||||
size_t _pos = _h & (_cap - 1); \
|
||||
for (size_t _d = 0; _d < _cap; _d++) { \
|
||||
if ((map)->hashes[_pos] == 0) \
|
||||
break; \
|
||||
if (_HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap) < _d) \
|
||||
break; \
|
||||
if ((map)->hashes[_pos] == _h && (eq_fn)(&(map)->keys[_pos], &_k, sizeof(_k))) { \
|
||||
(map)->values[_pos] = (val); \
|
||||
_found = true; \
|
||||
break; \
|
||||
} \
|
||||
_pos = (_pos + 1) & (_cap - 1); \
|
||||
} \
|
||||
} \
|
||||
if (!_found) { \
|
||||
if ((map)->count * 4 >= (map)->capacity * 3 || (map)->capacity == 0) { \
|
||||
_HASHMAP_GROW(map, hash_fn, eq_fn); \
|
||||
if ((map)->keys == nullptr || (map)->values == nullptr || (map)->hashes == nullptr) { \
|
||||
_status = ZN_ERR_NO_MEMORY; \
|
||||
} \
|
||||
} \
|
||||
if (_status == ZN_OK) { \
|
||||
_HASHMAP_INSERT_INNER(map, _k, (val), _h); \
|
||||
} \
|
||||
} \
|
||||
_status; \
|
||||
})
|
||||
|
||||
// Look up a key. Returns a pointer to the value, or nullptr if not found.
|
||||
#define HASHMAP_GET(map, key, hash_fn, eq_fn) \
|
||||
({ \
|
||||
typeof(*(map)->values)* _result = nullptr; \
|
||||
if ((map)->capacity > 0) { \
|
||||
typeof(*(map)->keys) _k = (key); \
|
||||
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
|
||||
size_t _cap = (map)->capacity; \
|
||||
size_t _pos = _h & (_cap - 1); \
|
||||
for (size_t _d = 0; _d < _cap; _d++) { \
|
||||
if ((map)->hashes[_pos] == 0) \
|
||||
break; \
|
||||
if (_HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap) < _d) \
|
||||
break; \
|
||||
if ((map)->hashes[_pos] == _h && (eq_fn)(&(map)->keys[_pos], &_k, sizeof(_k))) { \
|
||||
_result = &(map)->values[_pos]; \
|
||||
break; \
|
||||
} \
|
||||
_pos = (_pos + 1) & (_cap - 1); \
|
||||
} \
|
||||
} \
|
||||
_result; \
|
||||
})
|
||||
|
||||
// Remove a key. Returns true if the key was found and removed.
|
||||
// Uses backward-shift deletion to avoid tombstones.
|
||||
#define HASHMAP_REMOVE(map, key, hash_fn, eq_fn) \
|
||||
({ \
|
||||
bool _removed = false; \
|
||||
if ((map)->capacity > 0) { \
|
||||
typeof(*(map)->keys) _k = (key); \
|
||||
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
|
||||
size_t _cap = (map)->capacity; \
|
||||
size_t _pos = _h & (_cap - 1); \
|
||||
for (size_t _d = 0; _d < _cap; _d++) { \
|
||||
if ((map)->hashes[_pos] == 0) \
|
||||
break; \
|
||||
if (_HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap) < _d) \
|
||||
break; \
|
||||
if ((map)->hashes[_pos] == _h && (eq_fn)(&(map)->keys[_pos], &_k, sizeof(_k))) { \
|
||||
(map)->hashes[_pos] = 0; \
|
||||
(map)->count--; \
|
||||
/* Backward-shift: move subsequent displaced entries back. */ \
|
||||
size_t _empty = _pos; \
|
||||
size_t _next = (_pos + 1) & (_cap - 1); \
|
||||
while ((map)->hashes[_next] != 0 && _HASHMAP_PROBE_DIST((map)->hashes[_next], _next, _cap) > 0) { \
|
||||
(map)->keys[_empty] = (map)->keys[_next]; \
|
||||
(map)->values[_empty] = (map)->values[_next]; \
|
||||
(map)->hashes[_empty] = (map)->hashes[_next]; \
|
||||
(map)->hashes[_next] = 0; \
|
||||
_empty = _next; \
|
||||
_next = (_next + 1) & (_cap - 1); \
|
||||
} \
|
||||
_removed = true; \
|
||||
break; \
|
||||
} \
|
||||
_pos = (_pos + 1) & (_cap - 1); \
|
||||
} \
|
||||
} \
|
||||
_removed; \
|
||||
})
|
||||
|
||||
// Iterate over all occupied entries. `idx` is a size_t loop variable.
|
||||
// Access keys and values via (map)->keys[idx] and (map)->values[idx].
|
||||
#define HASHMAP_FOREACH(map, idx) \
|
||||
for (size_t idx = 0; idx < (map)->capacity; idx++) \
|
||||
if ((map)->hashes[idx] != 0)
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <common/utils.h>
|
||||
#include <kernel/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define __init __used, __section(".init.text"), __cold
|
||||
#define __initdata __used, __section(".init.data")
|
||||
#define __initdata_sorted(name) __used, __section(".init.data." name)
|
||||
|
||||
struct boot_file {
|
||||
phys_t data;
|
||||
size_t length;
|
||||
char path[128];
|
||||
};
|
||||
|
||||
struct boot_info {
|
||||
char* cmdline;
|
||||
struct phys_mem* mem_map;
|
||||
size_t num_mem_maps;
|
||||
phys_t phys_base;
|
||||
uintptr_t virt_base;
|
||||
uintptr_t hhdm_base;
|
||||
struct boot_file* files;
|
||||
size_t num_files;
|
||||
phys_t rsdp;
|
||||
};
|
||||
|
||||
extern uint8_t __ld_stack_top[];
|
||||
extern uint8_t __ld_stack_bottom[];
|
||||
|
||||
extern phys_t rsdp_addr;
|
||||
|
||||
// Entry point for the kernel after arch-specific setup has finished.
|
||||
void kernel_entry();
|
||||
|
||||
// Initializes the early parts of the kernel.
|
||||
void kernel_early_init();
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main(struct boot_info* info);
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/list.h>
|
||||
#include <bits/irq.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum irq_status : uint8_t {
|
||||
IRQ_STATUS_HANDLED,
|
||||
IRQ_STATUS_IGNORED,
|
||||
};
|
||||
|
||||
enum irq_polarity : uint8_t {
|
||||
IRQ_POLARITY_LOW,
|
||||
IRQ_POLARITY_HIGH,
|
||||
};
|
||||
|
||||
enum irq_trigger_mode : uint8_t {
|
||||
IRQ_TRIGGER_EDGE,
|
||||
IRQ_TRIGGER_LEVEL,
|
||||
};
|
||||
|
||||
struct irq_handler {
|
||||
enum irq_status (*handler)(void* self);
|
||||
SLIST_LINK(struct irq_handler*) next;
|
||||
};
|
||||
|
||||
struct irq_line {
|
||||
int (*set_config)(enum irq_trigger_mode mode, enum irq_polarity polarity);
|
||||
void (*mask)(struct irq_line* self);
|
||||
void (*unmask)(struct irq_line* self);
|
||||
void (*eoi)(struct irq_line* self);
|
||||
|
||||
SLIST_HEAD(struct irq_handler*) handlers;
|
||||
bool is_busy;
|
||||
};
|
||||
|
||||
void irq_line_attach(struct irq_line* self, struct irq_handler* handler);
|
||||
|
||||
struct irq_percpu {
|
||||
uint32_t level;
|
||||
bool in_interrupt;
|
||||
};
|
||||
|
||||
void irq_lock();
|
||||
void irq_unlock();
|
||||
bool irq_set_interrupted(bool is_interrupted);
|
||||
|
||||
void arch_irq_set_state(bool state);
|
||||
bool arch_irq_get_state();
|
||||
void arch_irq_wait();
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#define SLIST_HEAD(type) \
|
||||
struct { \
|
||||
type sl_head; \
|
||||
}
|
||||
|
||||
#define SLIST_LINK(type) \
|
||||
struct { \
|
||||
type sl_link; \
|
||||
}
|
||||
|
||||
#define SLIST_EMPTY(head) ((head)->sl_head == NULL)
|
||||
#define SLIST_FIRST(head) ((head)->sl_head)
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sl_link)
|
||||
|
||||
#define SLIST_INSERT_END(slistelm, elm, field) \
|
||||
do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) for ((var) = SLIST_FIRST((head)); (var); (var) = SLIST_NEXT((var), field))
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void* mmio_new(phys_t addr, size_t length);
|
||||
void mmio_free(void* ptr, size_t length);
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
struct task;
|
||||
|
||||
struct mutex {
|
||||
struct task* owner;
|
||||
bool flag;
|
||||
};
|
||||
|
||||
void mutex_lock(struct mutex* mutex);
|
||||
void mutex_unlock(struct mutex* mutex);
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
#include <kernel/channel.h>
|
||||
#include <kernel/hashmap.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <kernel/vas.h>
|
||||
#include <kernel/vmo.h>
|
||||
|
||||
enum namespace_desc_type {
|
||||
NAMESPACE_DESC_NS,
|
||||
NAMESPACE_DESC_TASK,
|
||||
NAMESPACE_DESC_VAS,
|
||||
NAMESPACE_DESC_VMO,
|
||||
NAMESPACE_DESC_CHANNEL,
|
||||
};
|
||||
|
||||
struct namespace_desc {
|
||||
enum namespace_desc_type type;
|
||||
zn_rights_t rights;
|
||||
union {
|
||||
struct namespace* namespace;
|
||||
struct task* task;
|
||||
struct channel* channel;
|
||||
struct vas* vas;
|
||||
struct vmo* vmo;
|
||||
} value;
|
||||
};
|
||||
|
||||
// Translates between handles and objects.
|
||||
struct namespace {
|
||||
zn_handle_t next_handle;
|
||||
struct mutex mutex;
|
||||
HASHMAP(zn_handle_t, struct namespace_desc) descriptors;
|
||||
};
|
||||
|
||||
// Creates a new namespace with no contents.
|
||||
zn_status_t namespace_new(struct namespace** out);
|
||||
|
||||
// Adds a descriptor to this namespace with an auto-allocated handle.
|
||||
zn_status_t namespace_add_desc(struct namespace* namespace, struct namespace_desc desc, zn_handle_t* out);
|
||||
|
||||
// Adds a descriptor to this namespace at a specific handle value.
|
||||
zn_status_t namespace_add_desc_at(struct namespace* namespace, struct namespace_desc desc, zn_handle_t handle);
|
||||
|
||||
// Gets the descriptor referenced by the given handle.
|
||||
zn_status_t namespace_get(struct namespace* namespace, zn_handle_t handle, struct namespace_desc* out);
|
||||
|
||||
// Deletes a descriptor from this namespace.
|
||||
zn_status_t namespace_del_desc(struct namespace* namespace, zn_handle_t handle);
|
||||
|
||||
// Clones a descriptor with optionally restricted rights.
|
||||
// If cloned_rights has ZN_RIGHTS_IDENTICAL set, the clone keeps the original rights.
|
||||
// Otherwise, cloned_rights must be a subset of the original descriptor's rights.
|
||||
zn_status_t namespace_clone_desc(
|
||||
struct namespace* ns,
|
||||
struct namespace_desc* desc,
|
||||
zn_rights_t cloned_rights,
|
||||
zn_handle_t* out
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct pager {
|
||||
zn_status_t (*try_get_page)(struct pager* pager, uintptr_t offset);
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <common/compiler.h>
|
||||
|
||||
// Stop all execution upon panic.
|
||||
[[noreturn, __format(printf, 1, 2)]]
|
||||
void panic(const char* msg, ...);
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/irq.h>
|
||||
#include <kernel/sched.h>
|
||||
#include <kernel/usercopy.h>
|
||||
#include <bits/percpu.h>
|
||||
#include <stddef.h>
|
||||
|
||||
ASSERT_TYPE(struct arch_percpu);
|
||||
|
||||
// CPU-relative information.
|
||||
struct percpu {
|
||||
struct percpu* self; // A pointer to this structure.
|
||||
size_t id; // The virtual ID of this CPU.
|
||||
bool online; // Whether this CPU is initialized and active.
|
||||
uintptr_t kernel_stack; // The kernel mode stack.
|
||||
uintptr_t user_stack; // The user mode stack.
|
||||
struct arch_percpu arch; // Architecture-specific fields.
|
||||
struct irq_percpu irq; // IRQ information.
|
||||
struct sched_percpu sched; // Scheduler information.
|
||||
};
|
||||
|
||||
// Per-CPU data for the bootstrap processor.
|
||||
extern struct percpu percpu_bsp;
|
||||
|
||||
// Allocates a block of memory for a new CPU.
|
||||
struct percpu* percpu_new();
|
||||
|
||||
// Gets the per-CPU data on the current CPU.
|
||||
static inline struct percpu* percpu_get() {
|
||||
return arch_percpu_get();
|
||||
}
|
||||
|
||||
// Initializes the bootstrap processor.
|
||||
static inline void percpu_bsp_init() {
|
||||
arch_percpu_bsp_init();
|
||||
}
|
||||
|
||||
// Initializes all processors.
|
||||
static inline void percpu_init() {
|
||||
arch_percpu_init();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <common/utils.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <bits/mmu.h>
|
||||
|
||||
ASSERT_TYPE(pte_t);
|
||||
|
||||
enum pte_flags {
|
||||
PTE_READ = 1 << 0, // Can read from this page.
|
||||
PTE_WRITE = 1 << 1, // Can write to this page.
|
||||
PTE_EXEC = 1 << 2, // Can execute code on this page.
|
||||
PTE_USER = 1 << 3, // Can be accessed by the user.
|
||||
PTE_DIR = 1 << 4, // Is a non-leaf page.
|
||||
};
|
||||
|
||||
enum cache_mode {
|
||||
CACHE_NONE,
|
||||
CACHE_WRITE_COMBINE,
|
||||
CACHE_WRITE_THROUGH,
|
||||
CACHE_WRITE_BACK,
|
||||
CACHE_MMIO,
|
||||
};
|
||||
|
||||
struct pmap {
|
||||
phys_t root;
|
||||
struct spinlock lock;
|
||||
bool is_user;
|
||||
};
|
||||
|
||||
zn_status_t pmap_new_kernel(struct pmap* pt, enum alloc_flags flags);
|
||||
zn_status_t pmap_new_user(struct pmap* pt, enum alloc_flags flags);
|
||||
zn_status_t pmap_map(struct pmap* pt, uintptr_t vaddr, phys_t paddr, enum pte_flags flags, enum cache_mode cache);
|
||||
zn_status_t pmap_protect(struct pmap* pt, uintptr_t vaddr, enum pte_flags flags);
|
||||
zn_status_t pmap_unmap(struct pmap* pt, uintptr_t vaddr);
|
||||
void pmap_set(struct pmap* pt);
|
||||
|
||||
void pte_clear(pte_t* pte);
|
||||
pte_t pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache);
|
||||
bool pte_is_present(pte_t* pte);
|
||||
bool pte_is_dir(pte_t* pte);
|
||||
phys_t pte_address(pte_t* pte);
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
[[__format(printf, 1, 2)]]
|
||||
void kprintf(const char* message, ...);
|
||||
|
||||
void kvprintf(const char* message, va_list args);
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/namespace.h>
|
||||
#include <kernel/tailq.h>
|
||||
#include <kernel/types.h>
|
||||
#include <kernel/wait.h>
|
||||
#include <bits/sched.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
ASSERT_TYPE(struct arch_context);
|
||||
ASSERT_TYPE(struct arch_task_context);
|
||||
|
||||
enum task_state {
|
||||
TASK_STATE_RUNNING, // Task is active and running.
|
||||
TASK_STATE_READY, // Task is active, but not currently scheduled.
|
||||
TASK_STATE_BLOCKED, // Task is waiting on another object.
|
||||
TASK_STATE_DEAD, // Task is killed.
|
||||
};
|
||||
|
||||
#define TASK_NAME_MAX 128
|
||||
#define KERNEL_STACK_SIZE 0x4000
|
||||
|
||||
// A task is the smallest unit of the scheduler.
|
||||
struct task {
|
||||
size_t id;
|
||||
char name[TASK_NAME_MAX];
|
||||
struct vas* space;
|
||||
struct namespace* namespace;
|
||||
enum task_state state;
|
||||
struct arch_task_context context;
|
||||
struct usercopy_region* usercopy_region;
|
||||
uintptr_t kernel_stack;
|
||||
uintptr_t user_stack;
|
||||
size_t time_slice;
|
||||
int8_t priority;
|
||||
|
||||
TAILQ_LINK(struct task) next;
|
||||
};
|
||||
|
||||
// Per-CPU data for scheduling.
|
||||
struct sched_percpu {
|
||||
struct task* current;
|
||||
struct task* idle_task;
|
||||
size_t preempt_level;
|
||||
TAILQ_HEAD(struct task) run_queue;
|
||||
};
|
||||
|
||||
// Blocks the current task on the given wait queue.
|
||||
// The caller must NOT hold any spinlocks. Returns after the task is woken.
|
||||
void sched_block(struct sched_percpu* sched, struct wait_queue* wq);
|
||||
|
||||
// Wakes the first task blocked on the given wait queue, if any.
|
||||
void sched_wake(struct sched_percpu* sched, struct wait_queue* wq);
|
||||
|
||||
// Wakes all tasks blocked on the given wait queue.
|
||||
void sched_wake_all(struct sched_percpu* sched, struct wait_queue* wq);
|
||||
|
||||
typedef void (*task_fn_t)(uintptr_t arg0);
|
||||
|
||||
zn_status_t task_create(
|
||||
const char* name,
|
||||
struct vas* space,
|
||||
struct namespace* ns,
|
||||
task_fn_t entry,
|
||||
uintptr_t arg0,
|
||||
struct task** out
|
||||
);
|
||||
[[noreturn]]
|
||||
void task_entry(task_fn_t entry, uintptr_t arg0);
|
||||
|
||||
void sched_init(struct sched_percpu* sched);
|
||||
void sched_reschedule(struct sched_percpu* sched);
|
||||
void sched_yield(struct sched_percpu* sched);
|
||||
void sched_add_task(struct sched_percpu* sched, struct task* task);
|
||||
[[noreturn]]
|
||||
void sched_to_user(uintptr_t ctx);
|
||||
[[noreturn]]
|
||||
void sched_to_user_context(uintptr_t ctx);
|
||||
|
||||
// Info block for sched_to_user. Heap-allocated by the caller, freed by sched_to_user_arg.
|
||||
struct task_start_info {
|
||||
uintptr_t ip;
|
||||
uintptr_t sp;
|
||||
uintptr_t arg;
|
||||
};
|
||||
|
||||
void arch_sched_preempt_disable();
|
||||
bool arch_sched_preempt_enable();
|
||||
void arch_sched_switch(struct task* from, struct task* to);
|
||||
zn_status_t arch_task_init(
|
||||
struct arch_task_context* context,
|
||||
void* entry,
|
||||
uintptr_t arg0,
|
||||
uintptr_t stack_start,
|
||||
bool is_user
|
||||
);
|
||||
[[noreturn]]
|
||||
void arch_sched_jump_to_context(struct arch_context* context);
|
||||
[[noreturn]]
|
||||
void arch_sched_jump_to_user(uintptr_t ip, uintptr_t sp, uintptr_t arg);
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Busy-waits in a loop until the lock is freed.
|
||||
// Does not put the CPU to sleep.
|
||||
struct spinlock {
|
||||
bool locked;
|
||||
};
|
||||
|
||||
// Attempts to lock a spinlock. If it's already locked, waits until it's freed.
|
||||
void spin_lock(struct spinlock* spin);
|
||||
|
||||
// Unlocks a spinlock.
|
||||
void spin_unlock(struct spinlock* spin);
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/sched.h>
|
||||
|
||||
typedef zn_status_t (*syscall_fn_t)(struct arch_context* ctx);
|
||||
|
||||
void syscall_dispatch(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_log(struct arch_context* ctx);
|
||||
zn_status_t syscall_archctl(struct arch_context* ctx);
|
||||
zn_status_t syscall_page_size(struct arch_context* ctx);
|
||||
zn_status_t syscall_time(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_handle_validate(struct arch_context* ctx);
|
||||
zn_status_t syscall_handle_drop(struct arch_context* ctx);
|
||||
zn_status_t syscall_handle_clone(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_vas_create(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_vmo_create(struct arch_context* ctx);
|
||||
zn_status_t syscall_vmo_create_phys(struct arch_context* ctx);
|
||||
zn_status_t syscall_vmo_map(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_channel_create(struct arch_context* ctx);
|
||||
zn_status_t syscall_channel_wait(struct arch_context* ctx);
|
||||
zn_status_t syscall_channel_read(struct arch_context* ctx);
|
||||
zn_status_t syscall_channel_write(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_thread_create(struct arch_context* ctx);
|
||||
zn_status_t syscall_thread_start(struct arch_context* ctx);
|
||||
zn_status_t syscall_thread_exit(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_random_get(struct arch_context* ctx);
|
||||
zn_status_t syscall_random_entropy(struct arch_context* ctx);
|
||||
|
||||
zn_status_t syscall_futex_wait(struct arch_context* ctx);
|
||||
zn_status_t syscall_futex_wake(struct arch_context* ctx);
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#define TAILQ_HEAD(type) \
|
||||
struct { \
|
||||
type* tqh_first; \
|
||||
type** tqh_last; \
|
||||
}
|
||||
|
||||
#define TAILQ_LINK(type) \
|
||||
struct { \
|
||||
type* tqe_next; \
|
||||
type** tqe_prev; \
|
||||
}
|
||||
|
||||
#define TAILQ_INIT(head) \
|
||||
do { \
|
||||
(head)->tqh_first = nullptr; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == nullptr)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.tqe_next = nullptr; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != nullptr) \
|
||||
(head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next) != nullptr) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/utils.h>
|
||||
#include <bits/types.h>
|
||||
|
||||
ASSERT_TYPE(phys_t);
|
||||
ASSERT_TYPE(uintptr_t);
|
||||
ASSERT_TYPE(reg_t);
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/percpu.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct usercopy_region {
|
||||
void (*start_ip)();
|
||||
void (*end_ip)();
|
||||
void (*fault_ip)();
|
||||
};
|
||||
|
||||
// Copies a block of data from user to kernel memory.
|
||||
bool usercopy_read(void* dst, const __user void* src, size_t len);
|
||||
|
||||
// Copies a block of data from kernel to user memory.
|
||||
bool usercopy_write(__user void* dst, const void* src, size_t len);
|
||||
|
||||
// Performs a strlen() on a user string.
|
||||
bool usercopy_strlen(const __user char* str, size_t max, size_t* len);
|
||||
|
||||
bool arch_usercopy_read(void* dst, const __user void* src, size_t len, struct usercopy_region** region);
|
||||
bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct usercopy_region** region);
|
||||
bool arch_usercopy_strlen(const __user char* str, size_t max, size_t* len, struct usercopy_region** region);
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/mem.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/pmap.h>
|
||||
#include <kernel/vmo.h>
|
||||
|
||||
// Virtual address space.
|
||||
struct vas {
|
||||
// Architecture-specific virtual memory map implementation.
|
||||
struct pmap pmap;
|
||||
};
|
||||
|
||||
// Try to create a new, empty virtual address space.
|
||||
zn_status_t vma_new(struct vas** out);
|
||||
|
||||
// Delete a virtual address space.
|
||||
void vma_delete(struct vas* vm);
|
||||
|
||||
zn_status_t vma_map_vmo(
|
||||
struct vas* vm,
|
||||
struct vmo* vmo,
|
||||
uintptr_t addr,
|
||||
size_t len,
|
||||
enum zn_vm_flags flags,
|
||||
uintptr_t vmo_offset
|
||||
);
|
||||
|
||||
// Remove any mappings in the given address range.
|
||||
zn_status_t vma_unmap(struct vas* vm, uintptr_t vaddr, size_t size);
|
||||
|
||||
// Change the protection of an existing mapping.
|
||||
zn_status_t vma_protect(struct vas* vm, uintptr_t vaddr, size_t size, enum zn_vm_flags flags);
|
||||
|
||||
// Try to handle a page fault.
|
||||
bool vma_page_fault(struct vas* vm, uintptr_t vaddr, enum zn_vm_flags access_type);
|
||||
|
||||
extern struct vas kernel_vas;
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern uint8_t __ld_vdso_start[];
|
||||
extern uint8_t __ld_vdso_end[];
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#define VEC(T) \
|
||||
struct { \
|
||||
T* data; \
|
||||
size_t length; \
|
||||
size_t capacity; \
|
||||
}
|
||||
|
||||
#define VEC_INIT(vec) \
|
||||
do { \
|
||||
(vec)->data = nullptr; \
|
||||
(vec)->length = 0; \
|
||||
(vec)->capacity = 0; \
|
||||
} while (0)
|
||||
|
||||
#define VEC_DESTROY(vec) \
|
||||
do { \
|
||||
mem_free((vec)->data); \
|
||||
(vec)->data = nullptr; \
|
||||
(vec)->length = 0; \
|
||||
(vec)->capacity = 0; \
|
||||
} while (0)
|
||||
|
||||
#define VEC_LENGTH(vec) ((vec)->length)
|
||||
#define VEC_CAPACITY(vec) ((vec)->capacity)
|
||||
#define VEC_EMPTY(vec) ((vec)->length == 0)
|
||||
#define VEC_AT(vec, i) ((vec)->data[i])
|
||||
|
||||
// Internal: grow to at least `needed` capacity.
|
||||
#define _VEC_GROW(vec, needed) \
|
||||
do { \
|
||||
size_t _new_cap = (vec)->capacity == 0 ? 8 : (vec)->capacity; \
|
||||
while (_new_cap < (needed)) \
|
||||
_new_cap *= 2; \
|
||||
typeof((vec)->data) _new = mem_alloc(_new_cap * sizeof(*(vec)->data), ALLOC_NOZERO); \
|
||||
if (_new != nullptr && (vec)->length > 0) \
|
||||
memcpy(_new, (vec)->data, (vec)->length * sizeof(*(vec)->data)); \
|
||||
mem_free((vec)->data); \
|
||||
(vec)->data = _new; \
|
||||
(vec)->capacity = _new_cap; \
|
||||
} while (0)
|
||||
|
||||
// Append an element. Returns ZN_OK or ZN_ERR_NO_MEMORY.
|
||||
#define VEC_PUSH(vec, val) \
|
||||
({ \
|
||||
zn_status_t _status = ZN_OK; \
|
||||
if ((vec)->length >= (vec)->capacity) { \
|
||||
_VEC_GROW(vec, (vec)->length + 1); \
|
||||
if ((vec)->data == nullptr) \
|
||||
_status = ZN_ERR_NO_MEMORY; \
|
||||
} \
|
||||
if (_status == ZN_OK) \
|
||||
(vec)->data[(vec)->length++] = (val); \
|
||||
_status; \
|
||||
})
|
||||
|
||||
// Remove and return the last element. Undefined if empty.
|
||||
#define VEC_POP(vec) ((vec)->data[--(vec)->length])
|
||||
|
||||
// Insert at index, shifting subsequent elements right. Returns ZN_OK or ZN_ERR_NO_MEMORY.
|
||||
#define VEC_INSERT(vec, i, val) \
|
||||
({ \
|
||||
zn_status_t _status = ZN_OK; \
|
||||
size_t _idx = (i); \
|
||||
if ((vec)->length >= (vec)->capacity) { \
|
||||
_VEC_GROW(vec, (vec)->length + 1); \
|
||||
if ((vec)->data == nullptr) \
|
||||
_status = ZN_ERR_NO_MEMORY; \
|
||||
} \
|
||||
if (_status == ZN_OK) { \
|
||||
memmove(&(vec)->data[_idx + 1], &(vec)->data[_idx], ((vec)->length - _idx) * sizeof(*(vec)->data)); \
|
||||
(vec)->data[_idx] = (val); \
|
||||
(vec)->length++; \
|
||||
} \
|
||||
_status; \
|
||||
})
|
||||
|
||||
// Remove element at index, shifting subsequent elements left.
|
||||
#define VEC_REMOVE(vec, i) \
|
||||
do { \
|
||||
size_t _idx = (i); \
|
||||
(vec)->length--; \
|
||||
if (_idx < (vec)->length) \
|
||||
memmove(&(vec)->data[_idx], &(vec)->data[_idx + 1], ((vec)->length - _idx) * sizeof(*(vec)->data)); \
|
||||
} while (0)
|
||||
|
||||
// Remove element at index by swapping with the last element (O(1), unordered).
|
||||
#define VEC_SWAP_REMOVE(vec, i) \
|
||||
do { \
|
||||
size_t _idx = (i); \
|
||||
(vec)->length--; \
|
||||
if (_idx < (vec)->length) \
|
||||
(vec)->data[_idx] = (vec)->data[(vec)->length]; \
|
||||
} while (0)
|
||||
|
||||
// Clear all elements without freeing memory.
|
||||
#define VEC_CLEAR(vec) ((vec)->length = 0)
|
||||
|
||||
#define VEC_FOREACH(vec, idx) for (size_t idx = 0; idx < (vec)->length; idx++)
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum page_type : uint32_t {
|
||||
PAGE_PHYS = 0, // Regular physical memory.
|
||||
};
|
||||
|
||||
struct page {
|
||||
enum page_type type;
|
||||
uint32_t flags;
|
||||
size_t refcount;
|
||||
union {
|
||||
struct {
|
||||
struct page* next; // Pointer to the next chunk.
|
||||
size_t count; // Amount of free pages.
|
||||
} freelist;
|
||||
};
|
||||
};
|
||||
static_assert(0x1000 % sizeof(struct page) == 0, "struct must fit cleanly inside a page!");
|
||||
static_assert(sizeof(struct page) <= 64, "struct must be smaller than 64 bytes!");
|
||||
|
||||
void* vm_alloc(size_t length);
|
||||
void vm_free(void* addr, size_t length);
|
||||
|
||||
// Base address of the `struct page` array.
|
||||
extern struct page* vm_pfndb;
|
||||
|
||||
uintptr_t vm_page_addr(struct page* page);
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/list.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <kernel/virt.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct vmo {
|
||||
zn_status_t (*get_page)(struct vmo* vmo, uintptr_t offset_idx, struct page** out);
|
||||
};
|
||||
|
||||
zn_status_t vmo_read(struct vmo* obj, uintptr_t offset, void* buf, size_t len, size_t* actual_read);
|
||||
zn_status_t vmo_write(struct vmo* obj, uintptr_t offset, const void* buf, size_t len, size_t* actual_written);
|
||||
zn_status_t vmo_copy(
|
||||
struct vmo* target,
|
||||
uintptr_t target_offset,
|
||||
struct vmo* src,
|
||||
uintptr_t src_offset,
|
||||
size_t len,
|
||||
size_t* actual_copied
|
||||
);
|
||||
|
||||
struct pager_ops {
|
||||
zn_status_t (*get_page)(struct pager_ops* pager, uintptr_t offset_idx, struct page** out);
|
||||
zn_status_t (*put_page)(struct pager_ops* pager, uintptr_t offset_idx, struct page* page);
|
||||
};
|
||||
|
||||
struct page_list {
|
||||
uintptr_t offset;
|
||||
struct page* value;
|
||||
SLIST_LINK(struct page_list*) next;
|
||||
};
|
||||
|
||||
struct paged_vmo {
|
||||
struct vmo object;
|
||||
struct pager_ops source;
|
||||
SLIST_HEAD(struct page_list*) cache;
|
||||
struct spinlock lock;
|
||||
};
|
||||
|
||||
zn_status_t vmo_new_paged(struct pager_ops ops, struct paged_vmo** out);
|
||||
zn_status_t vmo_new_phys(struct paged_vmo** out);
|
||||
|
||||
// Creates a VMO backed by a specific physical address range (e.g. for MMIO).
|
||||
zn_status_t vmo_new_phys_range(uintptr_t addr, size_t length, struct vmo** out);
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/spin.h>
|
||||
#include <kernel/tailq.h>
|
||||
|
||||
struct task;
|
||||
|
||||
// A queue of tasks waiting for an event.
|
||||
struct wait_queue {
|
||||
TAILQ_HEAD(struct task) waiters;
|
||||
struct spinlock lock;
|
||||
};
|
||||
|
||||
void wait_queue_init(struct wait_queue* wq);
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef ZINNIA_STDARG_H
|
||||
#define ZINNIA_STDARG_H
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
#define va_start(v, l) __builtin_va_start(v, l)
|
||||
#define va_end(v) __builtin_va_end(v)
|
||||
#define va_arg(v, T) __builtin_va_arg(v, T)
|
||||
#define va_copy(d, s) __builtin_va_copy(d, s)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef ZINNIA_STDATOMIC_H
|
||||
#define ZINNIA_STDATOMIC_H
|
||||
|
||||
typedef enum memory_order {
|
||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||
memory_order_consume = __ATOMIC_CONSUME,
|
||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
||||
memory_order_release = __ATOMIC_RELEASE,
|
||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
||||
memory_order_seq_cst = __ATOMIC_SEQ_CST
|
||||
} memory_order;
|
||||
|
||||
#define atomic_store_explicit(ptr, des, ord) __atomic_store_n(ptr, des, ord)
|
||||
#define atomic_store(ptr, des) atomic_store_explicit(ptr, des, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_load_explicit(ptr, ord) __atomic_load_n(ptr, ord)
|
||||
#define atomic_load(ptr) atomic_load_explicit(ptr, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_exchange_explicit(ptr, des, ord) __atomic_exchange_n(ptr, des, ord)
|
||||
#define atomic_exchange(ptr, des) atomic_exchange_explicit(ptr, des, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_add_explicit(ptr, op, ord) __atomic_fetch_add(ptr, op, ord)
|
||||
#define atomic_fetch_add(ptr, op) atomic_fetch_add_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_sub_explicit(ptr, op, ord) __atomic_fetch_sub(ptr, op, ord)
|
||||
#define atomic_fetch_sub(ptr, op) atomic_fetch_sub_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_or_explicit(ptr, op, ord) __atomic_fetch_or(ptr, op, ord)
|
||||
#define atomic_fetch_or(ptr, op) atomic_fetch_or_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_xor_explicit(ptr, op, ord) __atomic_fetch_xor(ptr, op, ord)
|
||||
#define atomic_fetch_xor(ptr, op) atomic_fetch_xor_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_and_explicit(ptr, op, ord) __atomic_fetch_and(ptr, op, ord)
|
||||
#define atomic_fetch_and(ptr, op) atomic_fetch_and_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_thread_fence(ord) __atomic_thread_fence(ord)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef ZINNIA_STDDEF_H
|
||||
#define ZINNIA_STDDEF_H
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef signed long ssize_t;
|
||||
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef ZINNIA_STDINT_H
|
||||
#define ZINNIA_STDINT_H
|
||||
|
||||
typedef __INT8_TYPE__ int8_t;
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
#define INT8_MAX __INT8_MAX__
|
||||
#define UINT8_MAX __UINT8_MAX__
|
||||
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
typedef __UINT16_TYPE__ uint16_t;
|
||||
#define INT16_MAX __INT16_MAX__
|
||||
#define UINT16_MAX __UINT16_MAX__
|
||||
|
||||
typedef __INT32_TYPE__ int32_t;
|
||||
typedef __UINT32_TYPE__ uint32_t;
|
||||
#define INT32_MAX __INT32_MAX__
|
||||
#define UINT32_MAX __UINT32_MAX__
|
||||
|
||||
typedef __INT64_TYPE__ int64_t;
|
||||
typedef __UINT64_TYPE__ uint64_t;
|
||||
#define INT64_MAX __INT64_MAX__
|
||||
#define UINT64_MAX __UINT64_MAX__
|
||||
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
#define INTPTR_MAX __INTPTR_MAX__
|
||||
#define UINTPTR_MAX __UINTPTR_MAX__
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef ZINNIA_STDLIB_H
|
||||
#define ZINNIA_STDLIB_H
|
||||
|
||||
long atol(const char* str, int base);
|
||||
unsigned long atolu(const char* str, int base);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef ZINNIA_STRING_H
|
||||
#define ZINNIA_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
size_t strlen(const char* str);
|
||||
size_t strnlen(const char* str, size_t len);
|
||||
int memcmp(const void* s1, const void* s2, size_t size);
|
||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n);
|
||||
void* memmove(void* dstptr, const void* srcptr, size_t size);
|
||||
void* memset(void* dest, int value, size_t n);
|
||||
char* strncpy(char* restrict dst, const char* restrict src, size_t len);
|
||||
int strcmp(const char* str1, const char* str2);
|
||||
int strncmp(const char* str1, const char* str2, size_t len);
|
||||
char* strchr(const char* c, int s);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t uacpi_u8;
|
||||
typedef uint16_t uacpi_u16;
|
||||
typedef uint32_t uacpi_u32;
|
||||
typedef uint64_t uacpi_u64;
|
||||
|
||||
typedef int8_t uacpi_i8;
|
||||
typedef int16_t uacpi_i16;
|
||||
typedef int32_t uacpi_i32;
|
||||
typedef int64_t uacpi_i64;
|
||||
|
||||
#define UACPI_TRUE true
|
||||
#define UACPI_FALSE false
|
||||
typedef bool uacpi_bool;
|
||||
|
||||
#define UACPI_NULL nullptr
|
||||
|
||||
typedef uintptr_t uacpi_uintptr;
|
||||
typedef uacpi_uintptr uacpi_virt_addr;
|
||||
typedef size_t uacpi_size;
|
||||
|
||||
typedef va_list uacpi_va_list;
|
||||
#define uacpi_va_start va_start
|
||||
#define uacpi_va_end va_end
|
||||
#define uacpi_va_arg va_arg
|
||||
|
||||
typedef char uacpi_char;
|
||||
|
||||
#define uacpi_offsetof offsetof
|
||||
|
||||
#define UACPI_PRIu64 "llu"
|
||||
#define UACPI_PRIx64 "llx"
|
||||
#define UACPI_PRIX64 "llX"
|
||||
#define UACPI_FMT64(val) ((unsigned long long)(val))
|
||||
Reference in New Issue
Block a user