Rename dirs

This commit is contained in:
2026-10-19 19:57:05 +02:00
parent 299ee1dfa8
commit 4384d36246
41 changed files with 13 additions and 20 deletions
+64
View File
@@ -0,0 +1,64 @@
#define pr_fmt(fmt) "clock: " fmt
#include <zinnia/atomic.h>
#include <zinnia/clock.h>
#include <zinnia/compiler.h>
#include <zinnia/print.h>
#include <arch/cpu.h>
#include <stdint.h>
static _Atomic(struct clock*) active_clock;
static _Atomic(int64_t) realtime_offset_ns;
static uint64_t clock_read_elapsed(struct clock* clock) {
if (__unlikely(!clock))
return 0;
return clock->base + clock->get_elapsed_ns(clock);
}
bool clock_available() {
return atomic_load(&active_clock, ATOMIC_ACQUIRE) != nullptr;
}
uint64_t clock_get_elapsed_ns() {
return clock_read_elapsed(atomic_load(&active_clock, ATOMIC_ACQUIRE));
}
uint64_t clock_get_realtime_ns() {
int64_t ns = (int64_t)clock_get_elapsed_ns() + atomic_load(&realtime_offset_ns, ATOMIC_RELAXED);
return ns < 0 ? 0 : ns;
}
void clock_set_realtime_ns(uint64_t ns) {
atomic_store(&realtime_offset_ns, (int64_t)ns - (int64_t)clock_get_elapsed_ns(), ATOMIC_RELAXED);
}
bool clock_switch(struct clock* clock) {
struct clock* old = atomic_load(&active_clock, ATOMIC_ACQUIRE);
do {
if (old && clock->priority <= old->priority)
return false;
uint64_t elapsed = clock_read_elapsed(old);
clock->reset(clock);
clock->base = elapsed;
} while (!atomic_compare_exchange(&active_clock, &old, clock, ATOMIC_RELEASE, ATOMIC_ACQUIRE));
log("Switching to clock source \"%s\"\n", clock->name);
return true;
}
void clock_spin_ns(uint64_t ns) {
if (!clock_available()) {
log_err("Unable to sleep for %lu ns, no clock available!\n", ns);
return;
}
uint64_t target = clock_get_elapsed_ns() + ns;
while (clock_get_elapsed_ns() < target) {
arch_cpu_pause();
}
}
+47
View File
@@ -0,0 +1,47 @@
#include <zinnia/console.h>
#include <zinnia/init.h>
#include <zinnia/print.h>
#include <zinnia/spin.h>
static struct console* registered = nullptr;
static struct spinlock console_lock;
void console_write(const char* buf, size_t len) {
spin_lock(&console_lock);
for (struct console* con = registered; con; con = con->next)
con->write(con, buf, len);
spin_unlock(&console_lock);
}
static void console_replay_write(void* ctx, const char* buf, size_t len) {
struct console* con = ctx;
con->write(con, buf, len);
}
bool console_register(struct console* con) {
if (!con->init(con))
return false;
con->write(con, "\ec", 2);
log_replay(console_replay_write, con);
spin_lock(&console_lock);
con->next = registered;
registered = con;
spin_unlock(&console_lock);
log("console: Registered console \"%s\"\n", con->name);
return true;
}
void console_unregister(struct console* con) {
spin_lock(&console_lock);
for (struct console** link = &registered; *link; link = &(*link)->next) {
if (*link == con) {
*link = con->next;
break;
}
}
spin_unlock(&console_lock);
}
INIT_TASK(console_stage, "core.console");
+3
View File
@@ -0,0 +1,3 @@
#include <zinnia/dpc.h>
// TODO
+31
View File
@@ -0,0 +1,31 @@
#include <zinnia/guid.h>
#include <zinnia/print.h>
#include <string.h>
bool guid_is_zero(const struct guid* guid) {
static const struct guid zero = {};
return guid_equal(guid, &zero);
}
bool guid_equal(const struct guid* a, const struct guid* b) {
return memcmp(a, b, sizeof(struct guid)) == 0;
}
void guid_format(const struct guid* guid, char out[static GUID_STR_LEN]) {
ksnprintf(
out,
GUID_STR_LEN,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
guid->time_low,
guid->time_mid,
guid->time_hi_version,
guid->clock_seq[0],
guid->clock_seq[1],
guid->node[0],
guid->node[1],
guid->node[2],
guid->node[3],
guid->node[4],
guid->node[5]
);
}
+194
View File
@@ -0,0 +1,194 @@
#define pr_fmt(fmt) "init: " fmt
#include <zinnia/assert.h>
#include <zinnia/cmdline.h>
#include <zinnia/initgraph.h>
#include <zinnia/print.h>
extern struct init_node* const __ld_initgraph_nodes_start[];
extern struct init_node* const __ld_initgraph_nodes_end[];
extern const struct init_edges* const __ld_initgraph_edges_start[];
extern const struct init_edges* const __ld_initgraph_edges_end[];
static bool dump_graph = false;
static void initgraph_option(bool value) {
dump_graph = value;
}
CMDLINE_OPTION("initgraph", "Print the init graph in DOT format after running", initgraph_option);
static bool edges_linked = false;
static struct init_node* failed_node = nullptr;
struct init_queue {
struct init_node* head;
struct init_node* tail;
};
static void queue_push(struct init_queue* queue, struct init_node* node) {
node->queue_next = nullptr;
if (queue->tail)
queue->tail->queue_next = node;
else
queue->head = node;
queue->tail = node;
}
static struct init_node* queue_pop(struct init_queue* queue) {
struct init_node* node = queue->head;
if (node) {
queue->head = node->queue_next;
if (!queue->head)
queue->tail = nullptr;
node->queue_next = nullptr;
}
return node;
}
static void link_edges() {
for (auto cur = __ld_initgraph_edges_start; cur < __ld_initgraph_edges_end; cur++) {
const struct init_edges* group = *cur;
for (size_t i = 0; i < group->count; i++) {
struct init_edge* edge = &group->edges[i];
edge->source = group->depends ? group->nodes[i] : group->node;
edge->target = group->depends ? group->node : group->nodes[i];
edge->propagates_disable = group->depends;
edge->next_out = edge->source->out_edges;
edge->source->out_edges = edge;
edge->next_in = edge->target->in_edges;
edge->target->in_edges = edge;
edge->target->unsatisfied++;
}
}
edges_linked = true;
}
static void mark_wanted(struct init_node* goal) {
if (!goal) {
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++)
(*cur)->wanted = true;
return;
}
struct init_queue queue = {};
if (!goal->wanted) {
goal->wanted = true;
queue_push(&queue, goal);
}
struct init_node* node;
while ((node = queue_pop(&queue))) {
for (struct init_edge* edge = node->in_edges; edge; edge = edge->next_in) {
if (!edge->source->wanted) {
edge->source->wanted = true;
queue_push(&queue, edge->source);
}
}
}
}
static bool is_ready(struct init_node* node) {
return node->wanted && !node->done && node->unsatisfied == 0;
}
static void reach_node(struct init_node* node, struct boot_info* info) {
if (node->disabled) {
log_status("Skipping stage \"%s\"\n", node->name);
} else {
log_status("Running stage \"%s\"\n", node->name);
failed_node = node;
switch (node->action) {
case INIT_EMPTY:
break;
case INIT_CALLBACK:
node->callback(info);
break;
case INIT_GATE:
if (!node->gate(info)) {
node->disabled = true;
node->failed = true;
}
break;
}
failed_node = nullptr;
node->executed = true;
}
node->done = true;
}
static void dump() {
kprintf("digraph initgraph {\n");
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++) {
struct init_node* node = *cur;
const char* style = "";
if (node->failed || node == failed_node)
style = ", color=red, fontcolor=red";
else if (!node->executed)
style = ", color=gray, fontcolor=gray";
kprintf("\tn%p [label=\"%s\"%s];\n", node, node->name, style);
for (struct init_edge* edge = node->in_edges; edge; edge = edge->next_in)
kprintf(
"\tn%p -> n%p%s;\n",
edge->source,
edge->target,
edge->source->executed && edge->target->executed ? "" : " [color=gray]"
);
}
kprintf("}\n");
}
void initgraph_panic() {
static bool dumping = false;
if (!dump_graph || !failed_node || dumping)
return;
dumping = true;
dump();
}
void initgraph_run(struct init_node* goal, struct boot_info* info) {
if (!edges_linked)
link_edges();
mark_wanted(goal);
struct init_queue pending = {};
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++) {
if (is_ready(*cur))
queue_push(&pending, *cur);
}
struct init_node* node;
while ((node = queue_pop(&pending))) {
reach_node(node, info);
for (struct init_edge* edge = node->out_edges; edge; edge = edge->next_out) {
struct init_node* successor = edge->target;
assert(successor->unsatisfied != 0, "Stage \"%s\" has no unsatisfied dependencies left", successor->name);
successor->unsatisfied--;
if (node->disabled && edge->propagates_disable)
successor->disabled = true;
if (is_ready(successor))
queue_push(&pending, successor);
}
}
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++) {
if ((*cur)->wanted && !(*cur)->done)
failed_node = *cur;
assert(
!(*cur)->wanted || (*cur)->done,
"The dependencies for stage \"%s\" could not be resolved",
(*cur)->name
);
}
if (!goal) {
log_status("All stages are complete!\n");
if (dump_graph)
dump();
}
}
+69
View File
@@ -0,0 +1,69 @@
#include <zinnia/iovec.h>
#include <zinnia/usercopy.h>
#include <zinnia/utils.h>
#include <string.h>
// Copies between `buf` and the iterator. If `to_iter` is set, data flows from `buf` into the iovecs.
static errno_t iovec_iter_copy(iovec_iter_t* iter, void* buf, size_t len, bool to_iter, size_t* out_copied) {
size_t progress = 0;
errno_t status = 0;
while (progress < len && iter->current_idx < iter->iovecs_len) {
iovec_t* vec = &iter->iovecs[iter->current_idx];
size_t remaining = vec->len - iter->current_offset;
if (remaining == 0) {
iter->current_idx++;
iter->current_offset = 0;
continue;
}
size_t chunk = MIN(remaining, len - progress);
void* ptr = (void*)(vec->base + iter->current_offset);
uint8_t* kbuf = (uint8_t*)buf + progress;
if (iter->user) {
bool ok = to_iter ? usercopy_write((__user void*)ptr, kbuf, chunk)
: usercopy_read(kbuf, (const __user void*)ptr, chunk);
if (!ok) {
status = EFAULT;
break;
}
} else if (to_iter) {
memcpy(ptr, kbuf, chunk);
} else {
memcpy(kbuf, ptr, chunk);
}
progress += chunk;
iter->current_offset += chunk;
iter->total_offset += chunk;
}
if (out_copied)
*out_copied = progress;
return status;
}
iovec_iter_t iovec_iter_kernel(iovec_t* iovecs, size_t count) {
size_t total = 0;
for (size_t i = 0; i < count; i++)
total += iovecs[i].len;
return (iovec_iter_t){
.iovecs = iovecs,
.iovecs_len = count,
.total_len = total,
.total_offset = 0,
.current_idx = 0,
.current_offset = 0,
.user = false,
};
}
errno_t iovec_iter_copy_to(iovec_iter_t* iter, const void* src, size_t len, size_t* out_copied) {
return iovec_iter_copy(iter, (void*)src, len, true, out_copied);
}
errno_t iovec_iter_copy_from(iovec_iter_t* iter, void* dst, size_t len, size_t* out_copied) {
return iovec_iter_copy(iter, dst, len, false, out_copied);
}
+69
View File
@@ -0,0 +1,69 @@
#include <zinnia/assert.h>
#include <zinnia/irq.h>
#include <zinnia/list.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <arch/irq.h>
[[__percpu]]
struct irq_percpu irq_data;
bool irq_set_state(bool state) {
return arch_irq_set_state(state);
}
bool irq_get_state() {
return arch_irq_get_state();
}
void irq_wait() {
arch_irq_wait();
}
void irq_lock() {
bool prev = arch_irq_set_state(false);
struct irq_percpu* state = percpu_ref(irq_data);
if (state->depth++ == 0)
state->saved_state = prev;
}
void irq_unlock() {
struct irq_percpu* state = percpu_ref(irq_data);
if (--state->depth == 0 && state->saved_state)
arch_irq_set_state(true);
}
void irq_line_program(struct irq_line* line, enum irq_trigger_mode trigger, enum irq_polarity polarity) {
irq_lock();
line->mode = line->set_config(line, trigger, polarity);
irq_unlock();
}
void irq_line_raise(struct irq_line* line) {
assert(!arch_irq_get_state(), "Raised an IRQ line with interrupts enabled");
irq_lock();
line->is_busy = true;
bool claimed = false;
struct irq_handler* handler;
list_foreach(handler, &line->handlers, next) {
if (handler->raise(handler) == IRQ_STATUS_HANDLED)
claimed = true;
}
if (!claimed)
log("irq: Spurious interrupt\n");
if ((line->mode & IRQ_MODE_EOI) && line->eoi)
line->eoi(line);
line->is_busy = false;
irq_unlock();
}
void irq_line_attach(struct irq_line* line, struct irq_handler* handler) {
irq_lock();
list_insert_head(&line->handlers, handler, next);
irq_unlock();
}
+135
View File
@@ -0,0 +1,135 @@
#include <zinnia/alloc.h>
#include <zinnia/assert.h>
#include <zinnia/compiler.h>
#include <zinnia/elf.h>
#include <zinnia/hashmap.h>
#include <zinnia/init.h>
#include <zinnia/ksyms.h>
#include <zinnia/print.h>
#include <zinnia/utils.h>
#include <bits/elf.h>
#include <string.h>
[[__visibility("hidden")]]
extern const elf_sym_t __ld_dynsym_start[];
[[__visibility("hidden")]]
extern const elf_sym_t __ld_dynsym_end[];
[[__visibility("hidden")]]
extern const char __ld_dynstr_start[];
[[__visibility("hidden")]]
extern const char __ld_dynstr_end[];
[[__visibility("hidden")]]
extern const elf_dyn_t _DYNAMIC[];
static uintptr_t slide;
static bool slide_known;
static const char* kernel_name;
static hashmap(const char*, uintptr_t) symbols;
static uint64_t sym_hash(const void* key, size_t) {
const char* name = *(const char* const*)key;
return hashmap_hash_default(name, strlen(name));
}
static bool sym_eq(const void* a, const void* b, size_t) {
return !strcmp(*(const char* const*)a, *(const char* const*)b);
}
static size_t num_syms() {
return __ld_dynsym_end - __ld_dynsym_start;
}
static const char* dynstr(uint64_t offset) {
if (offset >= (size_t)(__ld_dynstr_end - __ld_dynstr_start))
return nullptr;
return &__ld_dynstr_start[offset];
}
static uintptr_t kernel_slide() {
if (slide_known)
return slide;
for (size_t i = 0; i < num_syms(); i++) {
const char* name = dynstr(__ld_dynsym_start[i].st_name);
if (name != nullptr && !strcmp(name, "ksym_lookup")) {
slide = (uintptr_t)ksym_lookup - __ld_dynsym_start[i].st_value;
break;
}
}
slide_known = true;
return slide;
}
void ksym_init() {
const uintptr_t base = kernel_slide();
for (const elf_dyn_t* dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == DT_SONAME)
kernel_name = dynstr(dyn->d_un.d_val);
}
hashmap_init(&symbols);
size_t count = 0;
for (size_t i = 0; i < num_syms(); i++) {
const elf_sym_t* sym = &__ld_dynsym_start[i];
if (sym->st_shndx == SHN_UNDEF)
continue;
const char* name = dynstr(sym->st_name);
if (name == nullptr || *name == 0)
continue;
const errno_t status = hashmap_insert(&symbols, name, sym->st_value + base, sym_hash, sym_eq);
assert(status == 0, "Failed to register kernel symbol \"%s\" (%i)", name, status);
count++;
}
log("ksyms: Registered %zu kernel symbols\n", count);
}
bool ksym_find(const char* name, uintptr_t* addr) {
uintptr_t* value = hashmap_get(&symbols, name, sym_hash, sym_eq);
if (value == nullptr)
return false;
*addr = *value;
return true;
}
const char* ksym_kernel_name() {
return kernel_name;
}
bool ksym_lookup(uintptr_t addr, const char** name, size_t* offset) {
addr -= kernel_slide();
for (size_t i = 0; i < num_syms(); i++) {
const elf_sym_t* sym = &__ld_dynsym_start[i];
if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_shndx == SHN_UNDEF)
continue;
if (addr >= sym->st_value && addr - sym->st_value < sym->st_size) {
const char* sym_name = dynstr(sym->st_name);
if (sym_name == nullptr)
return false;
*name = sym_name;
*offset = addr - sym->st_value;
return true;
}
}
return false;
}
void ksym_format(uintptr_t addr, char* buf, size_t size) {
const char* name;
size_t offset;
if (ksym_lookup(addr, &name, &offset))
ksnprintf(buf, size, "%s+0x%zx", name, offset);
else
ksnprintf(buf, size, "<\?\?\?>");
}
[[__init]]
static void ksyms_stage_init(struct boot_info*) {
ksym_init();
}
INIT_TASK(ksyms_stage, "core.ksyms", ksyms_stage_init);
INIT_DEPENDS(ksyms_stage, mem_stage);
+20
View File
@@ -0,0 +1,20 @@
kernel_sources += files(
'clock.c',
'console.c',
'guid.c',
'initgraph.c',
'iovec.c',
'irq.c',
'ksyms.c',
'module.c',
'panic.c',
'percpu.c',
'print.c',
'radix.c',
'random.c',
'ringbuf.c',
'runtime.c',
'spin.c',
'stdlib.c',
'string.c',
)
+615
View File
@@ -0,0 +1,615 @@
#define pr_fmt(fmt) "module: " fmt
#include <zinnia/alloc.h>
#include <zinnia/assert.h>
#include <zinnia/cmdline.h>
#include <zinnia/elf.h>
#include <zinnia/ksyms.h>
#include <zinnia/module.h>
#include <zinnia/pmap.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/spin.h>
#include <zinnia/utils.h>
#include <zinnia/virt.h>
#include <zinnia/vma.h>
#include <bits/elf.h>
#include <stdckdint.h>
#include <string.h>
extern errno_t (*__ld_mod_entry_start[])(struct module*);
extern errno_t (*__ld_mod_entry_end[])(struct module*);
static struct spinlock module_lock;
static tailq_head(struct module) module_list;
static bool in_range(size_t total, uint64_t offset, uint64_t size) {
return offset <= total && size <= total - offset;
}
static void* image_ptr(struct module* mod, uint64_t vaddr, uint64_t size) {
uintptr_t addr, end;
if (ckd_add(&addr, mod->bias, vaddr) || ckd_add(&end, addr, size))
return nullptr;
for (size_t i = 0; i < mod->num_segments; i++) {
const struct module_segment* seg = &mod->segments[i];
if (addr >= seg->virt && end <= seg->virt + seg->size)
return (void*)addr;
}
return nullptr;
}
static const char* image_cstr(struct module* mod, uint64_t vaddr, uint64_t size) {
const char* str = image_ptr(mod, vaddr, size);
if (str == nullptr || size == 0 || strnlen(str, size) >= size)
return nullptr;
return str;
}
static const char* table_str(const char* table, size_t size, uint64_t offset) {
if (offset >= size)
return nullptr;
const char* str = table + offset;
if (strnlen(str, size - offset) >= size - offset)
return nullptr;
return str;
}
static const char* sym_name(struct module* mod, uint64_t offset) {
return table_str(mod->strtab, mod->strtab_size, offset);
}
static struct module* find_locked(const char* name) {
for (struct module* m = tailq_first(&module_list); m != nullptr; m = tailq_next(m, link)) {
if (!strcmp(m->name, name))
return m;
}
return nullptr;
}
void module_init() {
spin_init(&module_lock);
tailq_init(&module_list);
for (auto start = __ld_mod_entry_start; start < __ld_mod_entry_end; start += 1) {
errno_t mod_status = (*start)(nullptr);
assert(mod_status == 0, "Built-in entry point returned %i", mod_status);
}
}
static errno_t resolve_symbol(struct module* mod, uint32_t index, uintptr_t* out) {
if (index >= mod->num_syms)
return ENOEXEC;
const elf_sym_t* sym = &((const elf_sym_t*)mod->symtab)[index];
if (sym->st_shndx != SHN_UNDEF) {
*out = mod->bias + sym->st_value;
return 0;
}
const char* name = sym_name(mod, sym->st_name);
if (name == nullptr)
return ENOEXEC;
if (ksym_find(name, out))
return 0;
if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
*out = 0;
return 0;
}
log_err("%s: Unresolved symbol \"%s\"\n", mod->name, name);
return ENOENT;
}
static errno_t store_word(struct module* mod, uint64_t vaddr, uintptr_t value) {
uintptr_t* where = image_ptr(mod, vaddr, sizeof(uintptr_t));
if (where == nullptr)
return ENOEXEC;
*where = value;
return 0;
}
static errno_t relocate_relative(struct module* mod, uint64_t vaddr) {
uintptr_t* where = image_ptr(mod, vaddr, sizeof(uintptr_t));
if (where == nullptr)
return ENOEXEC;
*where += mod->bias;
return 0;
}
static errno_t apply_rela(struct module* mod, uint64_t addr, uint64_t size, uint64_t entsize) {
if (entsize == 0)
entsize = sizeof(elf_rela_t);
if (entsize < sizeof(elf_rela_t))
return ENOEXEC;
const uint8_t* table = image_ptr(mod, addr, size);
if (table == nullptr)
return ENOEXEC;
for (uint64_t off = 0; off + sizeof(elf_rela_t) <= size; off += entsize) {
elf_rela_t rela;
memcpy(&rela, table + off, sizeof(rela));
errno_t status = 0;
uintptr_t value;
switch (ELF_R_TYPE(rela.r_info)) {
case R_NONE:
break;
case R_64:
case R_GLOB_DAT:
case R_JUMP_SLOT:
status = resolve_symbol(mod, ELF_R_SYM(rela.r_info), &value);
if (status == 0)
status = store_word(mod, rela.r_offset, value + rela.r_addend);
break;
case R_RELATIVE:
status = store_word(mod, rela.r_offset, mod->bias + rela.r_addend);
break;
default:
log_err("%s: Unsupported relocation type %u\n", mod->name, ELF_R_TYPE(rela.r_info));
status = ENOEXEC;
break;
}
if (status)
return status;
}
return 0;
}
static errno_t apply_relr(struct module* mod, uint64_t addr, uint64_t size, uint64_t entsize) {
if (entsize != 0 && entsize != sizeof(elf_relr_t))
return ENOEXEC;
const elf_relr_t* table = image_ptr(mod, addr, size);
if (table == nullptr)
return ENOEXEC;
uint64_t where = 0;
for (size_t i = 0; i < size / sizeof(elf_relr_t); i++) {
elf_relr_t entry = table[i];
errno_t status;
if ((entry & 1) == 0) {
status = relocate_relative(mod, entry);
if (status)
return status;
where = entry + sizeof(elf_relr_t);
} else {
for (size_t bit = 0; (entry >>= 1) != 0; bit++) {
if ((entry & 1) == 0)
continue;
status = relocate_relative(mod, where + bit * sizeof(elf_relr_t));
if (status)
return status;
}
where += (sizeof(elf_relr_t) * 8 - 1) * sizeof(elf_relr_t);
}
}
return 0;
}
static void module_free(struct module* mod) {
const size_t page_size = arch_mem_page_size();
mem_free(mod->cmdline);
for (size_t i = 0; i < mod->num_segments; i++) {
const struct module_segment* seg = &mod->segments[i];
for (size_t off = 0; off < seg->size; off += page_size)
pmap_unmap(&kernel_vma.pmap, seg->virt + off);
}
pmap_shootdown(&kernel_vma.pmap);
for (size_t i = 0; i < mod->num_segments; i++) {
const struct module_segment* seg = &mod->segments[i];
mem_phys_free(seg->phys, seg->size / page_size);
}
mem_free(mod->segments);
if (mod->base != 0)
vm_free(mod->base, mod->size);
mem_free(mod);
}
static enum pte_flags prot_from_phdr(const elf_phdr_t* phdr) {
enum pte_flags prot = 0;
if (phdr->p_flags & PF_R)
prot |= PTE_READ;
if (phdr->p_flags & PF_W)
prot |= PTE_WRITE;
if (phdr->p_flags & PF_X)
prot |= PTE_EXEC;
return prot;
}
static errno_t read_headers(const void* data, size_t length, elf_ehdr_t* ehdr, elf_phdr_t** out) {
const uint8_t* file = data;
if (data == nullptr || length < sizeof(elf_ehdr_t))
return ENOEXEC;
memcpy(ehdr, file, sizeof(*ehdr));
if (memcmp(ehdr->e_ident, ELF_MAG, sizeof(ELF_MAG)) != 0 || ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
ehdr->e_ident[EI_CLASS] != ELF_ARCH_CLASS || ehdr->e_ident[EI_DATA] != ELF_ARCH_DATA ||
ehdr->e_machine != ELF_ARCH_MACHINE || ehdr->e_type != ET_DYN)
return ENOEXEC;
if (ehdr->e_phentsize < sizeof(elf_phdr_t) || ehdr->e_phnum == 0 ||
!in_range(length, ehdr->e_phoff, (uint64_t)ehdr->e_phnum * ehdr->e_phentsize))
return ENOEXEC;
elf_phdr_t* phdrs = mem_alloc(ehdr->e_phnum * sizeof(elf_phdr_t), 0);
if (phdrs == nullptr)
return ENOMEM;
for (size_t i = 0; i < ehdr->e_phnum; i++)
memcpy(&phdrs[i], file + ehdr->e_phoff + i * ehdr->e_phentsize, sizeof(elf_phdr_t));
*out = phdrs;
return 0;
}
errno_t module_load(const void* data, size_t length, const char* cmdline, struct module** out) {
const uint8_t* file = data;
const size_t page_size = arch_mem_page_size();
elf_ehdr_t ehdr;
elf_phdr_t* phdrs;
errno_t header_status = read_headers(data, length, &ehdr, &phdrs);
if (header_status)
return header_status;
struct module* mod = mem_alloc(sizeof(struct module), 0);
if (mod == nullptr) {
mem_free(phdrs);
return ENOMEM;
}
mod->version = "";
mod->author = "";
mod->desc = "";
mod->license = "";
errno_t status = ENOEXEC;
bool listed = false;
uintptr_t load_min = UINTPTR_MAX;
uintptr_t load_end = 0;
size_t num_loads = 0;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
const elf_phdr_t* phdr = &phdrs[i];
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0)
continue;
if (phdr->p_filesz > phdr->p_memsz || !in_range(length, phdr->p_offset, phdr->p_filesz))
goto fail;
uintptr_t end;
if (ckd_add(&end, phdr->p_vaddr, phdr->p_memsz) || end > UINTPTR_MAX - page_size)
goto fail;
const uintptr_t start = ALIGN_DOWN(phdr->p_vaddr, page_size);
if (start < load_end)
goto fail;
if (load_min == UINTPTR_MAX)
load_min = start;
load_end = ALIGN_UP(end, page_size);
num_loads++;
}
if (num_loads == 0)
goto fail;
status = ENOMEM;
mod->segments = mem_alloc(num_loads * sizeof(struct module_segment), 0);
if (mod->segments == nullptr)
goto fail;
mod->base = vm_alloc(load_end - load_min);
if (mod->base == 0)
goto fail;
mod->size = load_end - load_min;
mod->bias = mod->base - load_min;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
const elf_phdr_t* phdr = &phdrs[i];
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0)
continue;
const uintptr_t start = ALIGN_DOWN(phdr->p_vaddr, page_size);
const size_t size = ALIGN_UP(phdr->p_vaddr + phdr->p_memsz, page_size) - start;
phys_t phys;
status = mem_phys_alloc(size / page_size, 0, &phys);
if (status)
goto fail;
struct module_segment* seg = &mod->segments[mod->num_segments++];
*seg = (struct module_segment){.phys = phys, .virt = mod->bias + start, .size = size};
for (size_t off = 0; off < size; off += page_size) {
status = pmap_map(&kernel_vma.pmap, seg->virt + off, phys + off, PTE_READ | PTE_WRITE, CACHE_WRITE_BACK);
if (status)
goto fail;
}
memcpy((void*)(mod->bias + phdr->p_vaddr), file + phdr->p_offset, phdr->p_filesz);
}
status = ENOEXEC;
const elf_dyn_t* dyn = nullptr;
size_t num_dyn = 0;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
if (phdrs[i].p_type != PT_DYNAMIC)
continue;
dyn = image_ptr(mod, phdrs[i].p_vaddr, phdrs[i].p_memsz);
num_dyn = phdrs[i].p_memsz / sizeof(elf_dyn_t);
break;
}
if (dyn == nullptr)
goto fail;
uint64_t dt_strtab = 0, dt_strsz = 0, dt_symtab = 0, dt_hash = 0, dt_soname = 0;
uint64_t dt_rela = 0, dt_relasz = 0, dt_relaent = 0;
uint64_t dt_jmprel = 0, dt_pltrelsz = 0, dt_pltrel = DT_RELA;
uint64_t dt_relr = 0, dt_relrsz = 0, dt_relrent = 0;
bool has_strtab = false, has_symtab = false, has_hash = false, has_soname = false;
bool has_rela = false, has_jmprel = false, has_relr = false, has_needed = false;
for (size_t i = 0; i < num_dyn && dyn[i].d_tag != DT_NULL; i++) {
const uint64_t val = dyn[i].d_un.d_val;
switch (dyn[i].d_tag) {
case DT_STRTAB:
dt_strtab = val;
has_strtab = true;
break;
case DT_STRSZ:
dt_strsz = val;
break;
case DT_SYMTAB:
dt_symtab = val;
has_symtab = true;
break;
case DT_HASH:
dt_hash = val;
has_hash = true;
break;
case DT_SONAME:
dt_soname = val;
has_soname = true;
break;
case DT_NEEDED:
has_needed = true;
break;
case DT_RELA:
dt_rela = val;
has_rela = true;
break;
case DT_RELASZ:
dt_relasz = val;
break;
case DT_RELAENT:
dt_relaent = val;
break;
case DT_JMPREL:
dt_jmprel = val;
has_jmprel = true;
break;
case DT_PLTRELSZ:
dt_pltrelsz = val;
break;
case DT_PLTREL:
dt_pltrel = val;
break;
case DT_RELR:
dt_relr = val;
has_relr = true;
break;
case DT_RELRSZ:
dt_relrsz = val;
break;
case DT_RELRENT:
dt_relrent = val;
break;
default:
break;
}
}
if (!has_strtab || !has_symtab || !has_hash || !has_soname || dt_strsz == 0 || dt_pltrel != DT_RELA)
goto fail;
const elf_hash_t* hash = image_ptr(mod, dt_hash, sizeof(elf_hash_t));
if (hash == nullptr)
goto fail;
mod->num_syms = hash->n_chain;
mod->symtab = image_ptr(mod, dt_symtab, (uint64_t)mod->num_syms * sizeof(elf_sym_t));
mod->strtab = image_ptr(mod, dt_strtab, dt_strsz);
mod->strtab_size = dt_strsz;
if (mod->symtab == nullptr || mod->strtab == nullptr) {
mod->num_syms = 0;
goto fail;
}
mod->name = sym_name(mod, dt_soname);
if (mod->name == nullptr || *mod->name == 0)
goto fail;
for (size_t i = 0; has_needed && i < num_dyn && dyn[i].d_tag != DT_NULL; i++) {
if (dyn[i].d_tag != DT_NEEDED)
continue;
// TODO: Modules can technically claim they have dependencies on other modules, but we don't support that yet.
const char* needed = sym_name(mod, dyn[i].d_un.d_val);
const char* kernel_name = ksym_kernel_name();
if (needed == nullptr || kernel_name == nullptr || strcmp(needed, kernel_name) != 0) {
log_err("%s: Modules can only depend on the kernel, not \"%s\"\n", mod->name, needed ? needed : "?");
goto fail;
}
}
if (has_relr) {
status = apply_relr(mod, dt_relr, dt_relrsz, dt_relrent);
if (status)
goto fail;
}
if (has_rela) {
status = apply_rela(mod, dt_rela, dt_relasz, dt_relaent);
if (status)
goto fail;
}
if (has_jmprel) {
status = apply_rela(mod, dt_jmprel, dt_pltrelsz, dt_relaent);
if (status)
goto fail;
}
spin_lock(&module_lock);
if (find_locked(mod->name) != nullptr) {
spin_unlock(&module_lock);
status = EEXIST;
goto fail;
}
tailq_insert_tail(&module_list, mod, link);
listed = true;
spin_unlock(&module_lock);
const struct cmdline_option* const* opts_start = nullptr;
const struct cmdline_option* const* opts_end = nullptr;
status = ENOEXEC;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
const elf_phdr_t* phdr = &phdrs[i];
const char* str;
switch (phdr->p_type) {
case PT_MOD_VERSION:
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
mod->version = str;
break;
case PT_MOD_AUTHOR:
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
mod->author = str;
break;
case PT_MOD_DESC:
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
mod->desc = str;
break;
case PT_MOD_LICENSE:
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
mod->license = str;
break;
case PT_MOD_CMDLINE:
if (phdr->p_memsz < sizeof(void*))
break;
opts_start = image_ptr(mod, phdr->p_vaddr, phdr->p_memsz);
if (opts_start == nullptr)
goto fail;
opts_end = opts_start + (phdr->p_memsz / sizeof(void*));
break;
case PT_MOD_EXIT: {
if (phdr->p_memsz < sizeof(void*))
break;
const void* slot = image_ptr(mod, phdr->p_vaddr, sizeof(void*));
if (slot == nullptr)
goto fail;
memcpy(&mod->exit, slot, sizeof(void*));
break;
}
default:
break;
}
}
if (image_ptr(mod, ehdr.e_entry, 1) == nullptr)
goto fail;
size_t seg_idx = 0;
for (size_t i = 0; i < ehdr.e_phnum; i++) {
const elf_phdr_t* phdr = &phdrs[i];
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0)
continue;
const struct module_segment* seg = &mod->segments[seg_idx++];
const enum pte_flags prot = prot_from_phdr(phdr);
for (size_t off = 0; off < seg->size; off += page_size) {
pmap_unmap(&kernel_vma.pmap, seg->virt + off);
status = pmap_map(&kernel_vma.pmap, seg->virt + off, seg->phys + off, prot, CACHE_WRITE_BACK);
if (status)
goto fail;
}
}
if (cmdline == nullptr)
cmdline = "";
const size_t cmdline_len = strlen(cmdline);
mod->cmdline = mem_alloc(cmdline_len + 1, 0);
if (mod->cmdline == nullptr) {
status = ENOMEM;
goto fail;
}
memcpy(mod->cmdline, cmdline, cmdline_len);
if (opts_start != nullptr)
cmdline_parse_buf(mod->cmdline, cmdline_len, opts_start, opts_end);
log("Loaded module \"%s\" (%s, %s) at %p\n", mod->name, mod->desc, mod->version, (void*)mod->base);
errno_t (*entry)(struct module*) = (errno_t (*)(struct module*))(mod->bias + ehdr.e_entry);
status = entry(mod);
if (status) {
log_err("%s: Entry point returned %i\n", mod->name, status);
goto fail;
}
mem_free(phdrs);
if (out != nullptr)
*out = mod;
return 0;
fail:
if (listed) {
spin_lock(&module_lock);
tailq_remove(&module_list, mod, link);
spin_unlock(&module_lock);
}
module_free(mod);
mem_free(phdrs);
return status;
}
errno_t module_unload(struct module* mod) {
if (mod == nullptr)
return EINVAL;
if (mod->exit == nullptr)
return ENOTSUP;
spin_lock(&module_lock);
tailq_remove(&module_list, mod, link);
spin_unlock(&module_lock);
errno_t status = mod->exit(mod);
if (status) {
log_err("%s: Exit function returned %i\n", mod->name, status);
spin_lock(&module_lock);
tailq_insert_tail(&module_list, mod, link);
spin_unlock(&module_lock);
return status;
}
log("Unloaded module \"%s\"\n", mod->name);
module_free(mod);
return 0;
}
struct module* module_find(const char* name) {
spin_lock(&module_lock);
struct module* mod = find_locked(name);
spin_unlock(&module_lock);
return mod;
}
+73
View File
@@ -0,0 +1,73 @@
#include <zinnia/alloc.h>
#include <zinnia/initgraph.h>
#include <zinnia/irq.h>
#include <zinnia/ksyms.h>
#include <zinnia/panic.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <stdarg.h>
#include <stdint.h>
#define panic_log(msg, ...) kprintf("[ !!!! ] \e[31m" msg "\e[0m", ##__VA_ARGS__)
static void print_backtrace() {
constexpr size_t max_depth = 32;
void** fp = __builtin_frame_address(0);
panic_log("Backtrace:\n");
for (size_t i = 0; i < max_depth; i++) {
if (!fp || (uintptr_t)fp < mem_hhdm_base || (uintptr_t)fp % alignof(void*))
break;
void* ret = fp[1];
if (!ret)
break;
const char* name;
size_t offset;
// The return address may already point past the end of a noreturn call.
if (ksym_lookup((uintptr_t)ret - 1, &name, &offset))
panic_log(" #%zu [%p] %s+0x%zx\n", i, ret, name, offset + 1);
else
panic_log(" #%zu [%p] <\?\?\?>\n", i, ret);
void** next = fp[0];
if (next <= fp)
break;
fp = next;
}
}
[[noreturn]]
void panic(const char* msg, ...) {
// TODO: Stop all other CPUs with IPI.
irq_set_state(false);
struct percpu* cpu = percpu_get();
struct task* current = cpu->sched.current;
kprintf(
"[ !!!! ] \e[31m"
"Kernel panic on CPU %zu (T%zu, \"%s\"):\e[0m\n",
cpu->id,
current ? current->id : 0,
current ? current->name : nullptr
);
kprintf("[ !!!! ] \e[31m");
va_list args;
va_start(args, msg);
kvprintf(msg, args);
va_end(args);
kprintf("\e[0m");
print_backtrace();
panic_log("End of panic message\n");
initgraph_panic();
while (true) {
irq_wait();
}
}
+77
View File
@@ -0,0 +1,77 @@
#include <zinnia/alloc.h>
#include <zinnia/atomic.h>
#include <zinnia/cmdline.h>
#include <zinnia/compiler.h>
#include <zinnia/percpu.h>
#include <zinnia/pmap.h>
#include <zinnia/utils.h>
#include <zinnia/vma.h>
#include <arch/percpu.h>
static _Atomic(size_t) num_cpus = 1;
static size_t max_cpus = SIZE_MAX;
static void smp_option(uintptr_t value) {
max_cpus = value;
}
CMDLINE_OPTION("smp", "Maximum number of CPUs to use", smp_option);
[[__used, __section(".percpu.init")]]
struct percpu percpu_bsp = {
.self = &percpu_bsp,
.id = 0,
.online = false,
};
struct percpu* percpu_new() {
const size_t pgsz = arch_mem_page_size();
const size_t stride = percpu_stride();
const size_t id = atomic_load(&num_cpus, ATOMIC_RELAXED);
if (id >= (UINTPTR_MAX - (uintptr_t)__ld_percpu_start) / stride)
return nullptr;
phys_t mem;
if (mem_phys_alloc(stride / pgsz, 0, &mem))
return nullptr;
const uintptr_t base = (uintptr_t)percpu_cpu(id);
for (size_t offset = 0; offset < stride; offset += pgsz) {
if (pmap_map(&kernel_vma.pmap, base + offset, mem + offset, PTE_READ | PTE_WRITE, CACHE_ANY)) {
while (offset) {
offset -= pgsz;
pmap_unmap(&kernel_vma.pmap, base + offset);
}
mem_phys_free(mem, stride / pgsz);
return nullptr;
}
}
struct percpu* cpu = (struct percpu*)base;
cpu->self = cpu;
cpu->id = id;
cpu->online = false;
atomic_store(&num_cpus, id + 1, ATOMIC_RELEASE);
return cpu;
}
size_t percpu_max_cpus() {
return max_cpus;
}
size_t percpu_count() {
return atomic_load(&num_cpus, ATOMIC_RELAXED);
}
struct percpu* percpu_get() {
return arch_percpu_get();
}
void percpu_bsp_init() {
arch_percpu_bsp_init();
}
void percpu_init(struct boot_info*) {
arch_percpu_init();
}
INIT_TASK(percpu_stage, "core.percpu", percpu_init);
INIT_DEPENDS(percpu_stage, mem_stage);
+495
View File
@@ -0,0 +1,495 @@
#include <zinnia/clock.h>
#include <zinnia/console.h>
#include <zinnia/panic.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/spin.h>
#include <zinnia/stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
static void print_pad(log_fn_t callback, void* ctx, char c, size_t count) {
for (size_t i = 0; i < count; i++)
callback(ctx, &c, 1);
}
void print_fmt(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
// Amount of bytes written.
int32_t written = 0;
while (*fmt != '\0') {
size_t maxrem = INT32_MAX - written;
if (fmt[0] != '%' || fmt[1] == '%') {
if (fmt[0] == '%')
fmt++;
size_t amount = 1;
while (fmt[amount] && fmt[amount] != '%')
amount++;
if (maxrem < amount) {
return;
}
callback(ctx, fmt, amount);
fmt += amount;
written += amount;
continue;
}
const char* format_begun_at = fmt++;
bool force_sign = false;
bool blank_sign = false;
bool write_prefix = false;
bool pad_zero = false;
bool left_align = false;
size_t width = 0;
bool has_precision = false;
size_t precision = 0;
size_t size = sizeof(uint32_t);
char number[32];
check_fmt:
memset(number, 0, sizeof(number));
switch (*fmt) {
// Flags
case '+': {
force_sign = true;
fmt++;
goto check_fmt;
}
case ' ': {
blank_sign = true;
fmt++;
goto check_fmt;
}
case '#': {
write_prefix = true;
fmt++;
goto check_fmt;
}
case '0': {
pad_zero = true;
fmt++;
goto check_fmt;
}
case '-': {
left_align = true;
fmt++;
goto check_fmt;
}
// Width
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
width = 0;
while (*fmt >= '0' && *fmt <= '9') {
width = width * 10 + (*fmt - '0');
fmt++;
}
goto check_fmt;
}
// Precision
case '.': {
has_precision = true;
fmt++;
precision = 0;
if (*fmt == '*') {
const int32_t arg = va_arg(args, int32_t);
if (arg < 0)
has_precision = false;
else
precision = arg;
fmt++;
} else {
while (*fmt >= '0' && *fmt <= '9') {
precision = precision * 10 + (*fmt - '0');
fmt++;
}
}
goto check_fmt;
}
// Length
case 'h': {
if (fmt[1] == 'h') {
size = sizeof(uint8_t);
fmt++;
} else
size = sizeof(uint16_t);
fmt++;
goto check_fmt;
}
case 'l': {
// There is no difference between `long int` and `long long int`.
if (fmt[1] == 'l')
fmt++;
fmt++;
size = sizeof(uint64_t);
goto check_fmt;
}
case 'z': {
size = sizeof(size_t);
fmt++;
goto check_fmt;
}
// Character
case 'c': {
char c = (char)va_arg(args, int32_t);
const size_t pad = width > 1 ? width - 1 : 0;
if (!left_align)
print_pad(callback, ctx, ' ', pad);
callback(ctx, &c, sizeof(c));
if (left_align)
print_pad(callback, ctx, ' ', pad);
written += 1 + pad;
fmt++;
break;
}
// String of characters
case 's': {
const char* str = va_arg(args, const char*);
if (!str)
str = "(null)";
size_t len = 0;
if (has_precision)
len = strnlen(str, precision);
else
len = strlen(str);
const size_t pad = width > len ? width - len : 0;
if (!left_align)
print_pad(callback, ctx, ' ', pad);
callback(ctx, str, len);
if (left_align)
print_pad(callback, ctx, ' ', pad);
written += len + pad;
fmt++;
break;
}
// Signed decimal integer
case 'i':
case 'd': {
switch (size) {
case sizeof(int8_t):
ltoa((int8_t)va_arg(args, int32_t), number, 10);
break;
case sizeof(int16_t):
ltoa((int16_t)va_arg(args, int32_t), number, 10);
break;
case sizeof(int32_t):
ltoa((int32_t)va_arg(args, int32_t), number, 10);
break;
default:
ltoa(va_arg(args, int64_t), number, 10);
break;
}
goto print_num;
}
// Unsigned decimal integer
case 'u': {
switch (size) {
case sizeof(uint8_t):
lutoa((uint8_t)va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint16_t):
lutoa((uint16_t)va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint32_t):
lutoa(va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint64_t):
lutoa(va_arg(args, uint64_t), number, 10);
break;
}
goto print_num;
}
// Unsigned hexadecimal integer
case 'X':
case 'x': {
switch (size) {
case sizeof(uint8_t):
lutoa((uint8_t)va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint16_t):
lutoa((uint16_t)va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint32_t):
lutoa(va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint64_t):
lutoa(va_arg(args, uint64_t), number, 16);
break;
}
print_num:
const char* digits = number;
char sign = 0;
if (digits[0] == '-') {
sign = '-';
digits++;
} else if (force_sign) {
sign = '+';
} else if (blank_sign) {
sign = ' ';
}
const size_t len = strlen(digits);
size_t zeros = (has_precision && precision > len) ? precision - len : 0;
const size_t total = (sign ? 1 : 0) + (write_prefix ? 2 : 0) + zeros + len;
size_t pad = width > total ? width - total : 0;
if (pad_zero && !left_align && !has_precision) {
zeros += pad;
pad = 0;
}
if (!left_align)
print_pad(callback, ctx, ' ', pad);
if (sign)
callback(ctx, &sign, 1);
if (write_prefix)
callback(ctx, "0x", 2);
print_pad(callback, ctx, '0', zeros);
callback(ctx, digits, len);
if (left_align)
print_pad(callback, ctx, ' ', pad);
written += width > total ? width : total;
fmt++;
break;
}
// Pointer address
case 'p': {
const size_t num = va_arg(args, size_t);
const size_t buf_size = sizeof(size_t) * 2 + 1;
char str[buf_size];
lutoa(num, number, 0x10);
const size_t len = strlen(number); // Get the length of the final number.
for (size_t i = 0; i < buf_size; i++)
str[i] = '0';
size_t offset = sizeof(str) - len - 1;
callback(ctx, str, offset);
callback(ctx, number, len);
fmt++;
written += len;
break;
}
// The number of characters written so far is stored in the pointed location.
case 'n': {
int32_t* const ptr = va_arg(args, int32_t*);
*ptr = written;
fmt++;
break;
}
// No format, just normal text.
default: {
size_t len = fmt - format_begun_at;
if (*fmt)
len++;
callback(ctx, format_begun_at, len);
written += len;
fmt = format_begun_at + len;
break;
}
}
}
}
static void printf_console(void*, const char* msg, size_t len) {
console_write(msg, len);
}
static struct spinlock print_lock = {};
void kprintf(const char* message, ...) {
spin_lock(&print_lock);
va_list args;
va_start(args, message);
print_fmt(printf_console, nullptr, message, args);
va_end(args);
spin_unlock(&print_lock);
}
struct snprintf_ctx {
char* buf;
size_t size;
size_t len;
};
static void snprintf_write(void* ctx, const char* str, size_t len) {
struct snprintf_ctx* c = ctx;
// Always keep room for the terminator.
if (c->len < c->size) {
size_t room = c->size - c->len - 1;
memcpy(c->buf + c->len, str, len < room ? len : room);
}
c->len += len;
}
size_t kvsnprintf(char* buf, size_t size, const char* format, va_list args) {
struct snprintf_ctx ctx = {.buf = buf, .size = size};
print_fmt(snprintf_write, &ctx, format, args);
if (size > 0)
buf[ctx.len < size ? ctx.len : size - 1] = '\0';
return ctx.len;
}
size_t ksnprintf(char* buf, size_t size, const char* format, ...) {
va_list args;
va_start(args, format);
size_t len = kvsnprintf(buf, size, format, args);
va_end(args);
return len;
}
void kvprintf(const char* message, va_list args) {
spin_lock(&print_lock);
print_fmt(printf_console, nullptr, message, args);
spin_unlock(&print_lock);
}
struct log_record {
uint64_t time_ns;
uint32_t tid;
uint16_t len;
uint8_t level;
};
#define LOG_BUFFER_SIZE (64 * 1024)
#define LOG_MESSAGE_MAX 255
static uint8_t log_buffer[LOG_BUFFER_SIZE];
// Protected by print_lock.
static size_t log_head = 0;
static size_t log_tail = 0;
static void log_ring_copy_in(const void* src, size_t len) {
for (size_t i = 0; i < len; i++)
log_buffer[(log_head + i) % LOG_BUFFER_SIZE] = ((const uint8_t*)src)[i];
log_head += len;
}
static void log_ring_peek(size_t offset, void* dst, size_t len) {
for (size_t i = 0; i < len; i++)
((uint8_t*)dst)[i] = log_buffer[(offset + i) % LOG_BUFFER_SIZE];
}
static void log_store(const struct log_record* rec, const char* msg) {
const size_t total = sizeof(*rec) + rec->len;
// Drop the oldest records until the new one fits.
while (LOG_BUFFER_SIZE - (log_head - log_tail) < total) {
struct log_record old;
log_ring_peek(log_tail, &old, sizeof(old));
log_tail += sizeof(old) + old.len;
}
log_ring_copy_in(rec, sizeof(*rec));
log_ring_copy_in(msg, rec->len);
}
struct log_line {
char buf[LOG_MESSAGE_MAX];
size_t len;
};
static void log_line_append(void* ctx, const char* str, size_t len) {
struct log_line* line = ctx;
if (len > sizeof(line->buf) - line->len)
len = sizeof(line->buf) - line->len;
memcpy(line->buf + line->len, str, len);
line->len += len;
}
// ANSI colors for each level.
static const char* const log_level_colors[] = {
[LOG_DEBUG] = "\e[90m",
[LOG_STATUS] = "\e[32m",
[LOG_INFO] = "",
[LOG_WARN] = "\e[33m",
[LOG_ERR] = "\e[31m",
[LOG_PANIC] = "\e[1;31m",
};
static void log_cb_printf(log_fn_t callback, void* ctx, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
print_fmt(callback, ctx, fmt, args);
va_end(args);
}
// Formats a single record and passes it to `callback`.
static void log_emit(log_fn_t callback, void* ctx, const struct log_record* rec, const char* msg) {
log_cb_printf(
callback,
ctx,
"[%5lu.%06lu] %s",
rec->time_ns / 1'000'000'000,
rec->time_ns / 1'000 % 1'000'000,
log_level_colors[rec->level]
);
callback(ctx, msg, rec->len);
callback(ctx, "\e[0m\n", 5);
}
void log_replay(log_fn_t callback, void* ctx) {
char msg[LOG_MESSAGE_MAX];
for (size_t offset = log_tail; offset != log_head;) {
struct log_record rec;
log_ring_peek(offset, &rec, sizeof(rec));
log_ring_peek(offset + sizeof(rec), msg, rec.len);
log_emit(callback, ctx, &rec, msg);
offset += sizeof(rec) + rec.len;
}
}
void log_vwrite(enum log_level level, const char* message, va_list args) {
log_vwrite_prefixed(level, "", message, args);
}
void log_vwrite_prefixed(enum log_level level, const char* prefix, const char* message, va_list args) {
struct log_line line = {};
log_line_append(&line, prefix, strlen(prefix));
print_fmt(log_line_append, &line, message, args);
if (line.len > 0 && line.buf[line.len - 1] == '\n')
line.len--;
auto cur = percpu_get()->sched.current;
const struct log_record rec = {
.time_ns = clock_get_elapsed_ns(),
.tid = cur ? cur->id : 0,
.len = line.len,
.level = level,
};
spin_lock(&print_lock);
log_store(&rec, line.buf);
log_emit(printf_console, nullptr, &rec, line.buf);
spin_unlock(&print_lock);
}
void log_write(enum log_level level, const char* message, ...) {
va_list args;
va_start(args, message);
log_vwrite(level, message, args);
va_end(args);
}
+255
View File
@@ -0,0 +1,255 @@
#include <zinnia/alloc.h>
#include <zinnia/radix.h>
static bool covers(const struct radix_node* node, uint64_t index) {
return node->shift + RADIX_BITS >= 64 || (index >> (node->shift + RADIX_BITS)) == 0;
}
static size_t slot_of(const struct radix_node* node, uint64_t index) {
return (index >> node->shift) & (RADIX_SLOTS - 1);
}
static struct radix_node* node_alloc(uint8_t shift, struct radix_node* parent, uint8_t offset) {
struct radix_node* node = mem_alloc(sizeof(struct radix_node), 0);
if (!node)
return nullptr;
node->shift = shift;
node->parent = parent;
node->offset = offset;
return node;
}
static void free_subtree(struct radix_node* node) {
if (node->shift > 0) {
for (size_t i = 0; i < RADIX_SLOTS; i++) {
if (node->slots[i])
free_subtree(node->slots[i]);
}
}
mem_free(node);
}
void radix_init(struct radix_tree* tree) {
tree->root = nullptr;
}
void radix_destroy(struct radix_tree* tree) {
if (tree->root)
free_subtree(tree->root);
tree->root = nullptr;
}
bool radix_empty(struct radix_tree* tree) {
return tree->root == nullptr;
}
static struct radix_node* leaf_of(struct radix_tree* tree, uint64_t index) {
struct radix_node* node = tree->root;
if (!node || !covers(node, index))
return nullptr;
while (node->shift > 0) {
node = node->slots[slot_of(node, index)];
if (!node)
return nullptr;
}
return node;
}
void* radix_lookup(struct radix_tree* tree, uint64_t index) {
struct radix_node* leaf = leaf_of(tree, index);
return leaf ? leaf->slots[slot_of(leaf, index)] : nullptr;
}
void** radix_slot(struct radix_tree* tree, uint64_t index) {
struct radix_node* leaf = leaf_of(tree, index);
return leaf ? &leaf->slots[slot_of(leaf, index)] : nullptr;
}
static errno_t grow(struct radix_tree* tree, uint64_t index) {
if (!tree->root) {
uint8_t shift = 0;
while (shift + RADIX_BITS < 64 && (index >> (shift + RADIX_BITS)) != 0)
shift += RADIX_BITS;
tree->root = node_alloc(shift, nullptr, 0);
return tree->root ? 0 : ENOMEM;
}
while (!covers(tree->root, index)) {
struct radix_node* old = tree->root;
struct radix_node* top = node_alloc(old->shift + RADIX_BITS, nullptr, 0);
if (!top)
return ENOMEM;
top->slots[0] = old;
top->count = 1;
for (size_t t = 0; t < RADIX_NUM_TAGS; t++) {
if (old->tags[t])
top->tags[t] = 1;
}
old->parent = top;
old->offset = 0;
tree->root = top;
}
return 0;
}
errno_t radix_insert(struct radix_tree* tree, uint64_t index, void* value) {
errno_t status = grow(tree, index);
if (status)
return status;
struct radix_node* node = tree->root;
while (node->shift > 0) {
const size_t slot = slot_of(node, index);
struct radix_node* child = node->slots[slot];
if (!child) {
child = node_alloc(node->shift - RADIX_BITS, node, slot);
if (!child)
return ENOMEM;
node->slots[slot] = child;
node->count++;
}
node = child;
}
const size_t slot = slot_of(node, index);
if (node->slots[slot])
return EEXIST;
node->slots[slot] = value;
node->count++;
return 0;
}
static void clear_tag_upwards(struct radix_node* node, size_t slot, enum radix_tag tag) {
while (node) {
node->tags[tag] &= ~((uint64_t)1 << slot);
if (node->tags[tag])
return;
slot = node->offset;
node = node->parent;
}
}
void* radix_remove(struct radix_tree* tree, uint64_t index) {
struct radix_node* node = leaf_of(tree, index);
if (!node)
return nullptr;
size_t slot = slot_of(node, index);
void* value = node->slots[slot];
if (!value)
return nullptr;
for (size_t t = 0; t < RADIX_NUM_TAGS; t++) {
if (node->tags[t] & ((uint64_t)1 << slot))
clear_tag_upwards(node, slot, t);
}
node->slots[slot] = nullptr;
node->count--;
while (node->count == 0) {
struct radix_node* parent = node->parent;
if (!parent) {
tree->root = nullptr;
mem_free(node);
break;
}
slot = node->offset;
parent->slots[slot] = nullptr;
parent->count--;
mem_free(node);
node = parent;
}
return value;
}
void radix_tag_set(struct radix_tree* tree, uint64_t index, enum radix_tag tag) {
struct radix_node* node = leaf_of(tree, index);
if (!node || !node->slots[slot_of(node, index)])
return;
size_t slot = slot_of(node, index);
while (node) {
const uint64_t bit = (uint64_t)1 << slot;
if (node->tags[tag] & bit)
return;
node->tags[tag] |= bit;
slot = node->offset;
node = node->parent;
}
}
void radix_tag_clear(struct radix_tree* tree, uint64_t index, enum radix_tag tag) {
struct radix_node* node = leaf_of(tree, index);
if (!node)
return;
const size_t slot = slot_of(node, index);
if (node->tags[tag] & ((uint64_t)1 << slot))
clear_tag_upwards(node, slot, tag);
}
bool radix_tag_get(struct radix_tree* tree, uint64_t index, enum radix_tag tag) {
struct radix_node* node = leaf_of(tree, index);
return node && (node->tags[tag] & ((uint64_t)1 << slot_of(node, index)));
}
bool radix_tagged(struct radix_tree* tree, enum radix_tag tag) {
return tree->root && tree->root->tags[tag];
}
struct gang {
size_t max;
size_t found;
void** out;
uint64_t* out_index;
int tag;
};
static void gang_walk(struct radix_node* node, uint64_t base, uint64_t first, struct gang* g) {
size_t start = 0;
if (first > base) {
const uint64_t delta = (first - base) >> node->shift;
if (delta >= RADIX_SLOTS)
return;
start = delta;
}
for (size_t i = start; i < RADIX_SLOTS && g->found < g->max; i++) {
if (!node->slots[i])
continue;
if (g->tag >= 0 && !(node->tags[g->tag] & ((uint64_t)1 << i)))
continue;
const uint64_t index = base + ((uint64_t)i << node->shift);
if (node->shift == 0) {
if (g->out)
g->out[g->found] = node->slots[i];
if (g->out_index)
g->out_index[g->found] = index;
g->found++;
} else {
gang_walk(node->slots[i], index, first > index ? first : index, g);
}
}
}
static size_t gang_lookup(struct radix_tree* tree, uint64_t first, int tag, size_t max, void** out, uint64_t* idx) {
struct radix_node* root = tree->root;
if (!root || max == 0 || !covers(root, first))
return 0;
struct gang g = {.max = max, .out = out, .out_index = idx, .tag = tag};
gang_walk(root, 0, first, &g);
return g.found;
}
size_t radix_gang_lookup(struct radix_tree* tree, uint64_t first, size_t max, void** out, uint64_t* out_index) {
return gang_lookup(tree, first, -1, max, out, out_index);
}
size_t radix_gang_lookup_tag(
struct radix_tree* tree,
uint64_t first,
enum radix_tag tag,
size_t max,
void** out,
uint64_t* out_index
) {
return gang_lookup(tree, first, tag, max, out, out_index);
}
+61
View File
@@ -0,0 +1,61 @@
#define pr_fmt(fmt) "random: " fmt
#include <zinnia/atomic.h>
#include <zinnia/clock.h>
#include <zinnia/print.h>
#include <zinnia/random.h>
#include <zinnia/spin.h>
#include <string.h>
static _Atomic(uint64_t) state = 0x9E37'79B9'7F4A'7C15;
static bool dumb_random(struct random_source*, uint64_t* out) {
uint64_t z = atomic_fetch_add(&state, 0x9E37'79B9'7F4A'7C15, ATOMIC_RELAXED);
z ^= clock_get_elapsed_ns() * 0x2545'F491'4F6C'DD1D;
z = (z ^ (z >> 30)) * 0xBF58'476D'1CE4'E5B9;
z = (z ^ (z >> 27)) * 0x94D0'49BB'1331'11EB;
*out = z ^ (z >> 31);
return true;
}
static struct random_source fallback_source = {
.name = "fallback",
.quality = 0,
.get_u64 = dumb_random,
};
static _Atomic(struct random_source*) active_source = &fallback_source;
static struct spinlock lock = {};
bool random_register(struct random_source* source) {
spin_lock(&lock);
struct random_source* active = atomic_load(&active_source, ATOMIC_RELAXED);
if (source->quality <= active->quality) {
spin_unlock(&lock);
return false;
}
atomic_store(&active_source, source, ATOMIC_RELEASE);
spin_unlock(&lock);
log("Switching to random source \"%s\"\n", source->name);
return true;
}
void random_fill(void* buf, size_t len) {
struct random_source* source = atomic_load(&active_source, ATOMIC_ACQUIRE);
uint8_t* out = buf;
while (len) {
uint64_t value;
source->get_u64(source, &value);
const size_t chunk = len < sizeof(value) ? len : sizeof(value);
memcpy(out, &value, chunk);
out += chunk;
len -= chunk;
}
}
INIT_TASK(random_stage, "core.random");
+94
View File
@@ -0,0 +1,94 @@
#include <zinnia/alloc.h>
#include <zinnia/ringbuf.h>
#include <zinnia/utils.h>
#include <string.h>
errno_t ringbuf_init(struct ringbuf* rb, size_t capacity) {
rb->data = mem_alloc(capacity, ALLOC_NOZERO);
if (!rb->data)
return ENOMEM;
rb->capacity = capacity;
rb->head = 0;
rb->len = 0;
return 0;
}
void ringbuf_fini(struct ringbuf* rb) {
mem_free(rb->data);
rb->data = nullptr;
rb->capacity = 0;
rb->len = 0;
}
static size_t tail(const struct ringbuf* rb) {
return (rb->head + rb->len) % rb->capacity;
}
static size_t write_chunk(const struct ringbuf* rb, size_t len) {
return MIN(len, MIN(ringbuf_space(rb), rb->capacity - tail(rb)));
}
static size_t read_chunk(const struct ringbuf* rb, size_t len) {
return MIN(len, MIN(rb->len, rb->capacity - rb->head));
}
static void advance_read(struct ringbuf* rb, size_t len) {
rb->head = (rb->head + len) % rb->capacity;
rb->len -= len;
if (rb->len == 0)
rb->head = 0;
}
size_t ringbuf_write(struct ringbuf* rb, const void* src, size_t len) {
size_t done = 0;
size_t chunk;
while ((chunk = write_chunk(rb, len - done))) {
memcpy(rb->data + tail(rb), (const uint8_t*)src + done, chunk);
rb->len += chunk;
done += chunk;
}
return done;
}
size_t ringbuf_read(struct ringbuf* rb, void* dst, size_t len) {
size_t done = 0;
size_t chunk;
while ((chunk = read_chunk(rb, len - done))) {
memcpy((uint8_t*)dst + done, rb->data + rb->head, chunk);
advance_read(rb, chunk);
done += chunk;
}
return done;
}
errno_t ringbuf_write_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_written) {
size_t done = 0;
errno_t status = 0;
size_t chunk;
while (!status && (chunk = write_chunk(rb, len - done))) {
size_t copied;
status = iovec_iter_copy_from(iter, rb->data + tail(rb), chunk, &copied);
rb->len += copied;
done += copied;
if (copied < chunk)
break;
}
*out_written = done;
return done ? 0 : status;
}
errno_t ringbuf_read_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_read) {
size_t done = 0;
errno_t status = 0;
size_t chunk;
while (!status && (chunk = read_chunk(rb, len - done))) {
size_t copied;
status = iovec_iter_copy_to(iter, rb->data + rb->head, chunk, &copied);
advance_read(rb, copied);
done += copied;
if (copied < chunk)
break;
}
*out_read = done;
return done ? 0 : status;
}
+93
View File
@@ -0,0 +1,93 @@
// Compiler runtime support.
#include <stdint.h>
uint64_t __udivdi3(uint64_t dividend, uint64_t divisor) {
if (divisor == 0)
return 0;
if (dividend < divisor)
return 0;
unsigned shift = __builtin_clzll(divisor) - __builtin_clzll(dividend);
divisor <<= shift;
uint64_t quotient = 0;
for (;;) {
if (dividend >= divisor) {
dividend -= divisor;
quotient |= (uint64_t)1 << shift;
}
if (shift == 0)
break;
shift--;
divisor >>= 1;
}
return quotient;
}
static unsigned clz128(__uint128_t value) {
uint64_t high = (uint64_t)(value >> 64);
if (high != 0)
return (unsigned)__builtin_clzll(high);
return 64 + (unsigned)__builtin_clzll((uint64_t)value);
}
__uint128_t __udivmodti4(__uint128_t dividend, __uint128_t divisor, __uint128_t* remainder) {
if (divisor == 0) {
if (remainder)
*remainder = dividend;
return 0;
}
if (dividend < divisor) {
if (remainder)
*remainder = dividend;
return 0;
}
unsigned shift = clz128(divisor) - clz128(dividend);
divisor <<= shift;
__uint128_t quotient = 0;
for (;;) {
if (dividend >= divisor) {
dividend -= divisor;
quotient |= (__uint128_t)1 << shift;
}
if (shift == 0)
break;
shift--;
divisor >>= 1;
}
if (remainder)
*remainder = dividend;
return quotient;
}
__uint128_t __udivti3(__uint128_t dividend, __uint128_t divisor) {
return __udivmodti4(dividend, divisor, nullptr);
}
__uint128_t __umodti3(__uint128_t dividend, __uint128_t divisor) {
__uint128_t remainder;
__udivmodti4(dividend, divisor, &remainder);
return remainder;
}
+34
View File
@@ -0,0 +1,34 @@
#include <zinnia/atomic.h>
#include <zinnia/irq.h>
#include <zinnia/spin.h>
#include <arch/cpu.h>
void spin_init(struct spinlock* spin) {
spin->next = 0;
spin->owner = 0;
}
void spin_lock(struct spinlock* spin) {
irq_lock();
uint32_t my = atomic_fetch_add(&spin->next, 1, ATOMIC_RELAXED);
while (atomic_load(&spin->owner, ATOMIC_ACQUIRE) != my) {
arch_cpu_pause();
}
}
bool spin_trylock(struct spinlock* spin) {
irq_lock();
uint32_t owner = atomic_load(&spin->owner, ATOMIC_RELAXED);
uint32_t expected = owner;
if (atomic_compare_exchange(&spin->next, &expected, owner + 1, ATOMIC_ACQUIRE, ATOMIC_RELAXED))
return true;
irq_unlock();
return false;
}
void spin_unlock(struct spinlock* spin) {
uint32_t val = atomic_load(&spin->owner, ATOMIC_RELAXED);
atomic_store(&spin->owner, val + 1, ATOMIC_RELEASE);
irq_unlock();
}
+102
View File
@@ -0,0 +1,102 @@
#include <zinnia/stdlib.h>
#include <stdint.h>
#include <string.h>
static void reverse(char* s) {
int i, j;
char c;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
static int char_to_digit(char c) {
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'z')
return c - 'a' + 10;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 10;
return -1;
}
unsigned long atolu(const char* str, int base) {
unsigned long result = 0;
if (base == 16 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
str += 2;
}
while (*str) {
int digit = char_to_digit(*str++);
if (digit < 0 || digit >= base)
break;
result = result * base + digit;
}
return result;
}
long atol(const char* str, int base) {
int neg = 0;
if (*str == '-') {
neg = 1;
str++;
}
unsigned long val = atolu(str, base);
return neg ? -(long)val : (long)val;
}
char* lutoa(uint64_t value, char* str, int base) {
if (base < 2 || base > 36)
return nullptr;
size_t i = 0;
do {
uint64_t digit = value % base;
str[i++] = (digit < 10) ? '0' + digit : 'a' + (digit - 10);
value /= base;
} while (value);
str[i] = '\0';
reverse(str);
return str;
}
char* ltoa(int64_t value, char* str, int base) {
if (base < 2 || base > 36)
return nullptr;
uint64_t magnitude;
int negative = 0;
if (value < 0) {
negative = 1;
magnitude = (uint64_t)(-(value + 1)) + 1;
// avoids INT64_MIN overflow
} else {
magnitude = (uint64_t)value;
}
size_t i = 0;
do {
uint64_t digit = magnitude % base;
str[i++] = (digit < 10) ? '0' + digit : 'a' + (digit - 10);
magnitude /= base;
} while (magnitude);
if (negative)
str[i++] = '-';
str[i] = '\0';
reverse(str);
return str;
}
+141
View File
@@ -0,0 +1,141 @@
#include <zinnia/compiler.h>
#include <zinnia/utils.h>
#include <stddef.h>
#include <stdint.h>
[[__hot]]
size_t strlen(const char* str) {
size_t result = 0;
while (*str++) {
result++;
}
return result;
}
[[__hot]]
size_t strnlen(const char* str, size_t len) {
size_t result = 0;
while (result < len && *str++) {
result++;
}
return result;
}
[[__hot]]
int memcmp(const void* s1, const void* s2, size_t size) {
const uint8_t* s1ptr = s1;
const uint8_t* s2ptr = s2;
for (size_t i = 0; i < size; i++) {
if (s1ptr[i] != s2ptr[i])
return (int)s1ptr[i] - (int)s2ptr[i];
}
return 0;
}
typedef size_t word_t [[__may_alias]]
;
[[__hot]]
void* memcpy(void* restrict dest, const void* restrict src, size_t n) {
uint8_t* d = dest;
const uint8_t* s = src;
// Word copies are only possible if both pointers can be aligned at the same time.
if (((uintptr_t)d - (uintptr_t)s) % sizeof(word_t) == 0) {
while (n && (uintptr_t)d % sizeof(word_t) != 0) {
*d++ = *s++;
n--;
}
word_t* word_dest = (word_t*)d;
const word_t* word_src = (const word_t*)s;
for (; n >= sizeof(word_t); n -= sizeof(word_t))
*word_dest++ = *word_src++;
d = (uint8_t*)word_dest;
s = (const uint8_t*)word_src;
}
while (n--)
*d++ = *s++;
return dest;
}
[[__hot]]
void* memmove(void* dstptr, const void* srcptr, size_t size) {
uint8_t* dst = (uint8_t*)dstptr;
const uint8_t* src = (const uint8_t*)srcptr;
if ((uintptr_t)dst < (uintptr_t)src) {
for (size_t i = 0; i < size; i++)
dst[i] = src[i];
} else {
for (size_t i = size; i != 0; i--)
dst[i - 1] = src[i - 1];
}
return dstptr;
}
[[__hot]]
void* memset(void* dest, int value, size_t n) {
uint8_t* d = dest;
const uint8_t byte = (uint8_t)value;
while (n && (uintptr_t)d % sizeof(word_t) != 0) {
*d++ = byte;
n--;
}
const word_t word_value = (SIZE_MAX / 0xFF) * byte;
word_t* word_dest = (word_t*)d;
for (; n >= sizeof(word_t); n -= sizeof(word_t))
*word_dest++ = word_value;
d = (uint8_t*)word_dest;
while (n--)
*d++ = byte;
return dest;
}
char* strncpy(char* restrict dst, const char* restrict src, size_t len) {
size_t src_len = strnlen(src, len);
memcpy(dst, src, src_len);
memset(dst + src_len, 0, len - src_len);
return dst;
}
int strncmp(const char* str1, const char* str2, size_t len) {
while (len && *str1 && (*str1 == *str2)) {
++str1;
++str2;
--len;
}
if (len == 0) {
return 0;
} else {
return (*(unsigned char*)str1 - *(unsigned char*)str2);
}
}
int strcmp(const char* str1, const char* str2) {
while (*str1 && (*str1 == *str2)) {
++str1;
++str2;
}
return (*(unsigned char*)str1 - *(unsigned char*)str2);
}
char* strchr(const char* s, int c) {
char ch = (char)c;
while (true) {
char cur = *s;
if (cur == ch)
return (char*)s;
if (cur == 0)
return nullptr;
s++;
}
}
+17
View File
@@ -0,0 +1,17 @@
#include <zinnia/waitq.h>
void waitq_init(struct waitq* wq) {
tailq_init(&wq->waiters);
}
void waitq_wait(struct waitq* wq) {
// TODO
}
void waitq_wake_one(struct waitq* wq) {
// TODO
}
void waitq_wake_all(struct waitq* wq) {
// TODO
}