initgraph, whole lot of refactor

This commit is contained in:
2026-10-12 15:36:48 +02:00
parent 41cc7fea77
commit eabce70866
79 changed files with 3700 additions and 302 deletions
+4 -2
View File
@@ -1,3 +1,5 @@
#define pr_fmt(fmt) "clock: " fmt
#include <zinnia/clock.h>
#include <zinnia/compiler.h>
#include <zinnia/print.h>
@@ -47,14 +49,14 @@ bool clock_switch(struct clock* clock) {
spin_unlock(&lock);
log("clock: Switching to clock source \"%s\"\n", clock->name);
log("Switching to clock source \"%s\"\n", clock->name);
return true;
}
void clock_spin_ns(uint64_t ns) {
if (!clock_available()) {
log("clock: Unable to sleep for %lu ns, no clock available!\n", ns);
log_err("Unable to sleep for %lu ns, no clock available!\n", ns);
return;
}
+31 -32
View File
@@ -1,45 +1,44 @@
#include <zinnia/cmdline.h>
#include <zinnia/console.h>
#include <zinnia/init.h>
#include <zinnia/print.h>
#include <zinnia/string.h>
#include <zinnia/spin.h>
extern struct console* const __ld_console_start[];
extern struct console* const __ld_console_end[];
struct console* active = nullptr;
static struct console* registered = nullptr;
static struct spinlock console_lock;
void console_write(const char* buf, size_t len) {
if (__likely(active))
active->write(active, buf, 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_setup(const char* value) {
if (!value)
return;
bool console_register(struct console* con) {
if (!con->init(con))
return false;
// Find a suitable boot console.
struct console* const* cur = __ld_console_start;
while (cur < __ld_console_end) {
if (!strcmp(value, (*cur)->name)) {
active = *cur;
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;
}
cur++;
}
spin_unlock(&console_lock);
}
CMDLINE_OPTION("console", "Set a default console", console_setup);
void console_init() {
// If we have no console set and at least one set, use that as a default.
if (!active && &__ld_console_start[0] != &__ld_console_end[0])
active = __ld_console_start[0];
if (active) {
if (active->init(active) == false) {
active = nullptr;
return;
}
log("console: Logging on console \"%s\"\n", active->name);
}
[[__init]]
static void console_stage_init(struct boot_info*) {
kprintf("\ec");
}
INIT_TASK(console_stage, "core.console", console_stage_init);
+184
View File
@@ -0,0 +1,184 @@
#include <zinnia/alloc.h>
#include <zinnia/assert.h>
#include <zinnia/console.h>
#include <zinnia/init.h>
#include <zinnia/pmap.h>
#include <zinnia/sched.h>
#include <zinnia/utils.h>
#include <zinnia/vma.h>
#include <stdint.h>
#include <string.h>
static const uint8_t fbcon_font[] = {
#embed "fbcon_font.bin"
};
constexpr size_t FONT_WIDTH = 8;
constexpr size_t FONT_HEIGHT = 12;
constexpr uint32_t FG_COLOR = 0xaaaaaa;
constexpr uint32_t BG_COLOR = 0x000000;
enum esc_state {
ESC_NONE,
ESC_START,
ESC_CSI,
};
static struct {
struct boot_framebuffer fb;
uint32_t fg, bg;
size_t cols, rows;
size_t offset_x, offset_y;
size_t cx, cy;
enum esc_state esc;
} fbcon;
static inline uint8_t* fb_ptr() {
return HHDM_PTR(fbcon.fb.base);
}
static uint32_t pack_color(uint32_t rgb) {
const struct boot_framebuffer* fb = &fbcon.fb;
uint32_t r = (rgb >> 16) & 0xff;
uint32_t g = (rgb >> 8) & 0xff;
uint32_t b = rgb & 0xff;
return ((r >> (8 - fb->red.size)) << fb->red.shift) | ((g >> (8 - fb->green.size)) << fb->green.shift) |
((b >> (8 - fb->blue.size)) << fb->blue.shift);
}
static void fill_rect(size_t x, size_t y, size_t w, size_t h, uint32_t color) {
for (size_t j = 0; j < h; j++) {
uint32_t* row = (uint32_t*)(fb_ptr() + (y + j) * fbcon.fb.pitch) + x;
for (size_t i = 0; i < w; i++)
row[i] = color;
}
}
static void clear_screen() {
fill_rect(0, 0, fbcon.fb.width, fbcon.fb.height, fbcon.bg);
fbcon.cx = fbcon.cy = 0;
}
static void draw_glyph(size_t col, size_t row, uint8_t ch) {
const uint8_t* glyph = &fbcon_font[ch * FONT_HEIGHT];
size_t x = fbcon.offset_x + col * FONT_WIDTH;
size_t y = fbcon.offset_y + row * FONT_HEIGHT;
for (size_t j = 0; j < FONT_HEIGHT; j++) {
uint32_t* line = (uint32_t*)(fb_ptr() + (y + j) * fbcon.fb.pitch) + x;
for (size_t i = 0; i < FONT_WIDTH; i++)
line[i] = (glyph[j] & (0x80 >> i)) ? fbcon.fg : fbcon.bg;
}
}
static void scroll_up() {
const size_t pitch = fbcon.fb.pitch;
const size_t text_y = fbcon.offset_y;
const size_t text_h = fbcon.rows * FONT_HEIGHT;
memmove(fb_ptr() + text_y * pitch, fb_ptr() + (text_y + FONT_HEIGHT) * pitch, (text_h - FONT_HEIGHT) * pitch);
fill_rect(0, text_y + text_h - FONT_HEIGHT, fbcon.fb.width, FONT_HEIGHT, fbcon.bg);
}
static void newline() {
fbcon.cx = 0;
if (++fbcon.cy >= fbcon.rows) {
fbcon.cy = fbcon.rows - 1;
scroll_up();
}
}
static void put_char(uint8_t ch) {
switch (fbcon.esc) {
case ESC_START:
fbcon.esc = ch == '[' ? ESC_CSI : ESC_NONE;
if (ch == 'c')
clear_screen();
return;
case ESC_CSI:
if (ch >= 0x40 && ch <= 0x7e)
fbcon.esc = ESC_NONE;
return;
case ESC_NONE:
break;
}
switch (ch) {
case 0x1b:
fbcon.esc = ESC_START;
break;
case '\n':
newline();
break;
case '\r':
fbcon.cx = 0;
break;
case '\b':
if (fbcon.cx > 0)
fbcon.cx--;
break;
case '\t':
fbcon.cx = (fbcon.cx + 8) & ~(size_t)7;
if (fbcon.cx >= fbcon.cols)
newline();
break;
default:
if (fbcon.cx >= fbcon.cols)
newline();
draw_glyph(fbcon.cx++, fbcon.cy, ch);
break;
}
}
static void fbcon_write(struct console* con, const char* buf, size_t count) {
for (size_t i = 0; i < count; i++)
put_char((uint8_t)buf[i]);
}
static bool fbcon_setup(struct console* con) {
const struct boot_framebuffer* fb = &fbcon.fb;
if (fb->bytes_per_pixel != 4 || fb->red.size > 8 || fb->green.size > 8 || fb->blue.size > 8)
return false;
fbcon.fg = pack_color(FG_COLOR);
fbcon.bg = pack_color(BG_COLOR);
fbcon.cols = fb->width / FONT_WIDTH;
fbcon.rows = fb->height / FONT_HEIGHT;
fbcon.offset_x = (fb->width % FONT_WIDTH) / 2;
fbcon.offset_y = (fb->height % FONT_HEIGHT) / 2;
if (!fbcon.cols || !fbcon.rows)
return false;
clear_screen();
return true;
}
static struct console fbcon_con = {
.name = "fbcon",
.init = fbcon_setup,
.write = fbcon_write,
};
[[__init]]
void fbcon_init(struct boot_info* info) {
const struct boot_framebuffer* fb = &info->fb;
if (!fb->base)
return;
fbcon.fb = *fb;
const size_t pgsz = arch_mem_page_size();
const phys_t start = ALIGN_DOWN(fb->base, pgsz);
const phys_t end = ALIGN_UP(fb->base + fb->pitch * fb->height, pgsz);
for (phys_t p = start; p < end; p += pgsz) {
errno_t status =
pmap_map(&kernel_vma.pmap, (uintptr_t)HHDM_PTR(p), p, PTE_READ | PTE_WRITE, CACHE_WRITE_COMBINE);
if (status)
return;
}
console_register(&fbcon_con);
}
INIT_TASK(fbcon_stage, "core.fbcon", fbcon_init);
INIT_DEPENDS(fbcon_stage, sched_stage);
Binary file not shown.
+168
View File
@@ -0,0 +1,168 @@
#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;
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);
switch (node->action) {
case INIT_EMPTY:
break;
case INIT_CALLBACK:
node->callback(info);
break;
case INIT_GATE:
if (!node->gate(info))
node->disabled = true;
break;
}
}
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;
kprintf("\tn%p [label=\"%s\"];\n", node, node->name);
for (struct init_edge* edge = node->in_edges; edge; edge = edge->next_in)
kprintf("\tn%p -> n%p;\n", edge->source, edge->target);
}
kprintf("}\n");
}
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++) {
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();
}
}
+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);
+2 -2
View File
@@ -1,6 +1,6 @@
#include <stdint.h>
#include <zinnia/stdlib.h>
#include <zinnia/string.h>
#include <stdint.h>
#include <string.h>
static void reverse(char* s) {
int i, j;
-1
View File
@@ -1,5 +1,4 @@
#include <zinnia/compiler.h>
#include <zinnia/string.h>
#include <zinnia/utils.h>
#include <stddef.h>
#include <stdint.h>
+17 -8
View File
@@ -1,9 +1,11 @@
#define pr_fmt(fmt) "mem: " fmt
#include <zinnia/alloc.h>
#include <zinnia/assert.h>
#include <zinnia/console.h>
#include <zinnia/init.h>
#include <zinnia/panic.h>
#include <zinnia/print.h>
#include <zinnia/string.h>
#include <zinnia/utils.h>
#include <zinnia/vma.h>
@@ -23,7 +25,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
const size_t pgsz = arch_mem_page_size();
log("mem: Memory map:\n");
log("Memory map:\n");
for (size_t i = 0; i < map_len; i++) {
if (map[i].length == 0)
continue;
@@ -44,7 +46,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
break;
}
log("mem: [%p - %p] %s\n", (void*)map[i].address, (void*)(map[i].address + map[i].length - 1), label);
log("[%p - %p] %s\n", (void*)map[i].address, (void*)(map[i].address + map[i].length - 1), label);
}
// Set up the bootstrap allocator.
@@ -55,7 +57,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
}
assert(largest && largest->length >= pgsz, "No usable memory for bootstrap allocator");
mem_phys_bootstrap(largest);
log("mem: Using region [%p - %p] for bootstrap allocator\n",
log("Using region [%p - %p] for bootstrap allocator\n",
(void*)largest->address,
(void*)(largest->address + largest->length - 1));
@@ -68,7 +70,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
tailq_init(&kernel_vma.mappings);
// text
log("mem: Mapping text segment at %p\n", __ld_text_start);
log("Mapping text segment at %p\n", __ld_text_start);
for (uint8_t* p = __ld_text_start; p < __ld_text_end; p += pgsz) {
errno_t status = pmap_map(
&kernel_vma.pmap,
@@ -81,7 +83,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
}
// rodata
log("mem: Mapping rodata segment at %p\n", __ld_rodata_start);
log("Mapping rodata segment at %p\n", __ld_rodata_start);
for (uint8_t* p = __ld_rodata_start; p < __ld_rodata_end; p += pgsz) {
errno_t status = pmap_map(
&kernel_vma.pmap,
@@ -94,7 +96,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
}
// data
log("mem: Mapping data segment at %p\n", __ld_data_start);
log("Mapping data segment at %p\n", __ld_data_start);
for (uint8_t* p = __ld_data_start; p < __ld_data_end; p += pgsz) {
errno_t status = pmap_map(
&kernel_vma.pmap,
@@ -165,5 +167,12 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
slab_init();
log("mem: Memory initialization complete\n");
log("Memory initialization complete\n");
}
[[__init]]
static void mem_stage_init(struct boot_info* info) {
mem_init(info->mem_map, info->num_mem_maps, info->virt_base, info->phys_base, info->hhdm_base);
}
INIT_TASK(mem_stage, "core.mem", mem_stage_init);
INIT_DEPENDS(mem_stage, console_stage);
-1
View File
@@ -2,7 +2,6 @@
#include <zinnia/errno.h>
#include <zinnia/pmap.h>
#include <zinnia/spin.h>
#include <zinnia/string.h>
#include <zinnia/vma.h>
#include <config.h>
-1
View File
@@ -3,7 +3,6 @@
#include <zinnia/panic.h>
#include <zinnia/print.h>
#include <zinnia/spin.h>
#include <zinnia/string.h>
#include <zinnia/virt.h>
#include <stdint.h>
+1 -1
View File
@@ -2,7 +2,7 @@
#include <zinnia/compiler.h>
#include <zinnia/errno.h>
#include <zinnia/spin.h>
#include <zinnia/string.h>
#include <string.h>
typedef struct {
size_t num_pages; // Amount of pages connected to this slab.
-1
View File
@@ -1,7 +1,6 @@
#include <zinnia/alloc.h>
#include <zinnia/errno.h>
#include <zinnia/list.h>
#include <zinnia/string.h>
#include <zinnia/utils.h>
#include <zinnia/vmo.h>
+3 -1
View File
@@ -1,13 +1,15 @@
kernel_sources += files(
'clock.c',
'console.c',
'fbcon.c',
'initgraph.c',
'irq.c',
'ksyms.c',
'module.c',
'panic.c',
'percpu.c',
'print.c',
'spin.c',
'wait.c',
)
subdir('lib')
+603 -2
View File
@@ -1,12 +1,613 @@
#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*);
void module_init() {
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(struct boot_info*) {
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 == EOK, "Built-in entry point returned %i", mod_status);
assert(mod_status == 0, "Built-in entry point returned %i", mod_status);
}
}
INIT_TASK(module_stage, "core.module", module_init);
INIT_DEPENDS(module_stage, sched_stage, ksyms_stage);
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);
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;
}
+36 -2
View File
@@ -1,11 +1,42 @@
#include <zinnia/alloc.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.
@@ -15,12 +46,14 @@ void panic(const char* msg, ...) {
struct task* current = cpu->sched.current;
kprintf(
"[ !!!! ] \e[31m"
"Kernel panic on CPU %zu (T%zu, \"%s\"): ",
"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);
@@ -28,9 +61,10 @@ void panic(const char* msg, ...) {
kprintf("\e[0m");
print_backtrace();
panic_log("End of panic message\n");
while (1) {
while (true) {
irq_wait();
}
}
+3 -2
View File
@@ -2,7 +2,6 @@
#include <zinnia/cmdline.h>
#include <zinnia/compiler.h>
#include <zinnia/percpu.h>
#include <zinnia/string.h>
#include <zinnia/utils.h>
#include <arch/percpu.h>
#include <stdatomic.h>
@@ -55,6 +54,8 @@ void percpu_bsp_init() {
arch_percpu_bsp_init();
}
void percpu_init() {
void percpu_init(struct boot_info*) {
arch_percpu_init();
}
INIT_TASK(percpu_stage, "core.percpu", percpu_init);
INIT_DEPENDS(percpu_stage, mem_stage);
+149 -6
View File
@@ -1,22 +1,22 @@
#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 <zinnia/string.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len);
#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);
}
static void do_print(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
void print_fmt(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
// Amount of bytes written.
int32_t written = 0;
@@ -320,16 +320,159 @@ void kprintf(const char* message, ...) {
va_list args;
va_start(args, message);
do_print(printf_console, nullptr, message, args);
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);
do_print(printf_console, nullptr, message, args);
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_console_printf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
print_fmt(printf_console, nullptr, fmt, args);
va_end(args);
}
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_console_printf(
"[%5lu.%06lu] [%5lu] %s",
rec.time_ns / 1'000'000'000,
rec.time_ns / 1'000 % 1'000'000,
(uint64_t)rec.tid,
log_level_colors[level]
);
console_write(line.buf, line.len);
console_write("\e[0m\n", 5);
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);
}
+29 -2
View File
@@ -9,7 +9,6 @@
#include <zinnia/sched.h>
#include <zinnia/spin.h>
#include <zinnia/stack.h>
#include <zinnia/string.h>
#include <zinnia/tailq.h>
#include <zinnia/vma.h>
#include <arch/sched.h>
@@ -54,6 +53,7 @@ void sched_init(struct scheduler* sched) {
void sched_add_task(struct scheduler* sched, struct task* task) {
assert(task != nullptr, "Tried to add NULL to the run queue\n");
task->state = TASK_STATE_READY;
tailq_insert_tail(&sched->run_queue, task, next);
}
@@ -64,6 +64,7 @@ static struct task* next_task(struct scheduler* sched) {
return sched->idle_task;
tailq_remove(&sched->run_queue, t, next);
t->state = TASK_STATE_RUNNING;
return t;
}
@@ -110,6 +111,25 @@ void sched_yield(struct scheduler* sched) {
do_reschedule(sched);
}
void sched_block(struct scheduler* sched) {
irq_lock();
if (atomic_exchange_explicit(&sched->current->wake_pending, false, memory_order_acq_rel)) {
irq_unlock();
return;
}
sched->current->state = TASK_STATE_BLOCKED;
do_reschedule(sched);
}
void sched_wake(struct task* task) {
irq_lock();
if (task->state == TASK_STATE_BLOCKED)
sched_add_task(&percpu_get()->sched, task);
else
atomic_store_explicit(&task->wake_pending, true, memory_order_release);
irq_unlock();
}
errno_t task_create(const char* name, struct vma* space, task_fn_t entry, uintptr_t arg0, struct task** out) {
if (!out)
return EINVAL;
@@ -118,7 +138,7 @@ errno_t task_create(const char* name, struct vma* space, task_fn_t entry, uintpt
if (new_task == nullptr)
return ENOMEM;
static _Atomic(size_t) last_tid = 0;
static atomic_size_t last_tid = 0;
new_task->id = atomic_fetch_add_explicit(&last_tid, 1, memory_order_acquire);
new_task->space = space;
strncpy(new_task->name, name, sizeof(new_task->name));
@@ -165,3 +185,10 @@ void task_entry(task_fn_t entry, uintptr_t arg0, struct task* prev) {
sched_yield(sched);
panic("unreachable");
}
[[__init]]
static void sched_stage_init(struct boot_info*) {
sched_init(&percpu_get()->sched);
}
INIT_TASK(sched_stage, "core.sched", sched_stage_init);
INIT_DEPENDS(sched_stage, percpu_stage);