diff --git a/boot/main.c b/boot/main.c index b3f78dd..519a6eb 100644 --- a/boot/main.c +++ b/boot/main.c @@ -76,12 +76,11 @@ void kernel_early_init() { [[noreturn]] void kernel_main(struct boot_info* info) { - // TODO: Initialize in-memory log ring. - kprintf("%s\n", zinnia_banner); // Say hello! - kprintf("Command line: %s\n", info->cmdline); - cmdline_parse(info->cmdline); + log("%s\n", zinnia_banner); // Say hello! + log("Command line: %s\n", info->cmdline); + initgraph_run(sched_stage, info); struct task* main_task; diff --git a/core/console.c b/core/console.c index fedbd86..68adfe8 100644 --- a/core/console.c +++ b/core/console.c @@ -13,10 +13,17 @@ void console_write(const char* buf, size_t 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; @@ -37,8 +44,4 @@ void console_unregister(struct console* con) { spin_unlock(&console_lock); } -[[__init]] -static void console_stage_init(struct boot_info*) { - kprintf("\ec"); -} -INIT_TASK(console_stage, "core.console", console_stage_init); +INIT_TASK(console_stage, "core.console"); diff --git a/core/fbcon.c b/core/fbcon.c index 31578a3..2a7460b 100644 --- a/core/fbcon.c +++ b/core/fbcon.c @@ -26,6 +26,7 @@ enum esc_state { static struct { struct boot_framebuffer fb; + uint32_t* back; uint32_t fg, bg; size_t cols, rows; size_t offset_x, offset_y; @@ -37,6 +38,15 @@ static inline uint8_t* fb_ptr() { return HHDM_PTR(fbcon.fb.base); } +static inline uint32_t* back_row(size_t y) { + return fbcon.back + y * fbcon.fb.width; +} + +static void flush_rect(size_t x, size_t y, size_t w, size_t h) { + for (size_t j = 0; j < h; j++) + memcpy((uint32_t*)(fb_ptr() + (y + j) * fbcon.fb.pitch) + x, back_row(y + j) + x, w * sizeof(uint32_t)); +} + static uint32_t pack_color(uint32_t rgb) { const struct boot_framebuffer* fb = &fbcon.fb; uint32_t r = (rgb >> 16) & 0xff; @@ -48,10 +58,11 @@ static uint32_t pack_color(uint32_t rgb) { 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; + uint32_t* row = back_row(y + j) + x; for (size_t i = 0; i < w; i++) row[i] = color; } + flush_rect(x, y, w, h); } static void clear_screen() { @@ -65,19 +76,25 @@ static void draw_glyph(size_t col, size_t row, uint8_t ch) { 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; + uint32_t* line = back_row(y + j) + x; for (size_t i = 0; i < FONT_WIDTH; i++) line[i] = (glyph[j] & (0x80 >> i)) ? fbcon.fg : fbcon.bg; } + flush_rect(x, y, FONT_WIDTH, FONT_HEIGHT); } static void scroll_up() { - const size_t pitch = fbcon.fb.pitch; + const size_t width = fbcon.fb.width; 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); + memmove(back_row(text_y), back_row(text_y + FONT_HEIGHT), (text_h - FONT_HEIGHT) * width * sizeof(uint32_t)); + for (size_t j = text_y + text_h - FONT_HEIGHT; j < text_y + text_h; j++) { + uint32_t* row = back_row(j); + for (size_t i = 0; i < width; i++) + row[i] = fbcon.bg; + } + flush_rect(0, text_y, width, text_h); } static void newline() { @@ -178,6 +195,10 @@ void fbcon_init(struct boot_info* info) { return; } + fbcon.back = mem_alloc(fb->width * fb->height * sizeof(uint32_t), ALLOC_NOZERO); + if (!fbcon.back) + return; + console_register(&fbcon_con); } INIT_TASK(fbcon_stage, "core.fbcon", fbcon_init); diff --git a/core/print.c b/core/print.c index e933985..f86aaff 100644 --- a/core/print.c +++ b/core/print.c @@ -430,13 +430,39 @@ static const char* const log_level_colors[] = { [LOG_PANIC] = "\e[1;31m", }; -static void log_console_printf(const char* fmt, ...) { +static void log_cb_printf(log_fn_t callback, void* ctx, const char* fmt, ...) { va_list args; va_start(args, fmt); - print_fmt(printf_console, nullptr, fmt, args); + 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] [%5lu] %s", + rec->time_ns / 1'000'000'000, + rec->time_ns / 1'000 % 1'000'000, + (uint64_t)rec->tid, + 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); } @@ -458,15 +484,7 @@ void log_vwrite_prefixed(enum log_level level, const char* prefix, const char* m 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); + log_emit(printf_console, nullptr, &rec, line.buf); spin_unlock(&print_lock); } diff --git a/include/zinnia/print.h b/include/zinnia/print.h index 6541ff3..e8e9f7d 100644 --- a/include/zinnia/print.h +++ b/include/zinnia/print.h @@ -34,6 +34,9 @@ void log_vwrite(enum log_level level, const char* message, va_list args); // Like log_vwrite(), but prepends a `prefix` to the message. void log_vwrite_prefixed(enum log_level level, const char* prefix, const char* message, va_list args); +// Passes every record currently in the log buffer to `callback`. +void log_replay(log_fn_t callback, void* ctx); + #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif