mirror of
https://github.com/rdmsr/zag.git
synced 2026-11-01 23:58:21 +00:00
all: add console and logging machinery
This commit is contained in:
@@ -0,0 +1,193 @@
|
|||||||
|
const r = @import("root");
|
||||||
|
const std = @import("std");
|
||||||
|
const pl = r.pl;
|
||||||
|
const ke = r.ke;
|
||||||
|
const ex = r.ex;
|
||||||
|
|
||||||
|
const PSF1_MAGIC: u16 = 0x0436;
|
||||||
|
const PSF2_MAGIC: u32 = 0x864ab572;
|
||||||
|
|
||||||
|
const Psf1Header = extern struct {
|
||||||
|
magic: u16,
|
||||||
|
mode: u8,
|
||||||
|
glyph_size: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Psf2Header = extern struct {
|
||||||
|
magic: u32,
|
||||||
|
version: u32,
|
||||||
|
header_size: u32,
|
||||||
|
flags: u32,
|
||||||
|
glyph_count: u32,
|
||||||
|
glyph_size: u32,
|
||||||
|
height: u32,
|
||||||
|
width: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const FontInfo = struct {
|
||||||
|
header_size: usize,
|
||||||
|
glyph_size: usize,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
const font_file = @embedFile("sun12x22.psfu");
|
||||||
|
var font: FontInfo = undefined;
|
||||||
|
var framebuffer: [*]u32 = undefined;
|
||||||
|
var framebuffer_width: usize = 0;
|
||||||
|
var framebuffer_height: usize = 0;
|
||||||
|
|
||||||
|
const bg_color: u32 = 0xFFFFFFFF;
|
||||||
|
const text_color: u32 = 0xFF000000;
|
||||||
|
const window_padding: usize = 10;
|
||||||
|
|
||||||
|
var content_x: usize = 0;
|
||||||
|
var content_y: usize = 0;
|
||||||
|
var content_w: usize = 0;
|
||||||
|
var content_h: usize = 0;
|
||||||
|
var visible_h: usize = 0;
|
||||||
|
|
||||||
|
fn load_font() void {
|
||||||
|
if (font_file.len >= @sizeOf(Psf2Header)) {
|
||||||
|
const h2: *const Psf2Header = @ptrCast(@alignCast(font_file.ptr));
|
||||||
|
if (h2.magic == PSF2_MAGIC) {
|
||||||
|
font = .{
|
||||||
|
.header_size = h2.header_size,
|
||||||
|
.glyph_size = h2.glyph_size,
|
||||||
|
.width = h2.width,
|
||||||
|
.height = h2.height,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (font_file.len >= @sizeOf(Psf1Header)) {
|
||||||
|
const h1: *const Psf1Header = @ptrCast(@alignCast(font_file.ptr));
|
||||||
|
if (h1.magic == PSF1_MAGIC) {
|
||||||
|
font = .{
|
||||||
|
.header_size = @sizeOf(Psf1Header),
|
||||||
|
.glyph_size = h1.glyph_size,
|
||||||
|
.width = 8,
|
||||||
|
.height = h1.glyph_size,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font = .{ .header_size = 0, .glyph_size = 0, .width = 0, .height = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plot_character(x: usize, y: usize, c: u8) void {
|
||||||
|
const bytes_per_row = font.glyph_size / font.height;
|
||||||
|
const glyph_offset = font.header_size + (@as(usize, c) * font.glyph_size);
|
||||||
|
const glyph_data = font_file[glyph_offset..(glyph_offset + font.glyph_size)];
|
||||||
|
for (0..font.height) |row| {
|
||||||
|
var row_data: u32 = 0;
|
||||||
|
for (0..bytes_per_row) |b| {
|
||||||
|
row_data = (row_data << 8) | @as(u32, glyph_data[row * bytes_per_row + b]);
|
||||||
|
}
|
||||||
|
const shift_base = bytes_per_row * 8;
|
||||||
|
for (0..font.width) |col| {
|
||||||
|
if ((row_data & (@as(u32, 1) << @intCast(shift_base - 1 - col))) != 0) {
|
||||||
|
framebuffer[(y + row) * framebuffer_width + (x + col)] = text_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cursor_x: usize = 0;
|
||||||
|
var cursor_y: usize = 0;
|
||||||
|
|
||||||
|
fn clear_content_rows(y: usize, height: usize) void {
|
||||||
|
for (0..height) |row| {
|
||||||
|
const dst_y = content_y + y + row;
|
||||||
|
if (dst_y >= content_y + visible_h) break;
|
||||||
|
|
||||||
|
for (0..content_w) |col| {
|
||||||
|
framebuffer[dst_y * framebuffer_width + content_x + col] = bg_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_up(pixels: usize) void {
|
||||||
|
if (pixels >= visible_h) {
|
||||||
|
clear_content_rows(0, visible_h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const remaining_h = visible_h - pixels;
|
||||||
|
for (0..remaining_h) |row| {
|
||||||
|
const dst_y = content_y + row;
|
||||||
|
const src_y = dst_y + pixels;
|
||||||
|
|
||||||
|
for (0..content_w) |col| {
|
||||||
|
framebuffer[dst_y * framebuffer_width + content_x + col] =
|
||||||
|
framebuffer[src_y * framebuffer_width + content_x + col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_content_rows(remaining_h, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_cursor_visible() void {
|
||||||
|
while (cursor_y + font.height > visible_h) {
|
||||||
|
scroll_up(font.height);
|
||||||
|
cursor_y -= font.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn newline() void {
|
||||||
|
cursor_x = 0;
|
||||||
|
cursor_y += font.height;
|
||||||
|
|
||||||
|
while (cursor_y > visible_h) {
|
||||||
|
scroll_up(font.height);
|
||||||
|
cursor_y -= font.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_char(c: u8) void {
|
||||||
|
if (font.width == 0 or font.height == 0 or content_w < font.width or visible_h < font.height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
newline();
|
||||||
|
} else if (c == '\r') {
|
||||||
|
cursor_x = 0;
|
||||||
|
} else {
|
||||||
|
if (cursor_x + font.width > content_w) {
|
||||||
|
newline();
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_cursor_visible();
|
||||||
|
plot_character(content_x + cursor_x, content_y + cursor_y, c);
|
||||||
|
cursor_x += font.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(_: ?*anyopaque, s: []const u8) void {
|
||||||
|
for (s) |c| {
|
||||||
|
write_char(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var console: ex.Console = .{ .ctx = null, .link = undefined, .write = write };
|
||||||
|
|
||||||
|
pub fn init(boot_info: *pl.BootInfo) void {
|
||||||
|
framebuffer = @ptrFromInt(boot_info.framebuffer.?.address);
|
||||||
|
framebuffer_width = boot_info.framebuffer.?.width;
|
||||||
|
framebuffer_height = boot_info.framebuffer.?.height;
|
||||||
|
|
||||||
|
load_font();
|
||||||
|
|
||||||
|
content_x = window_padding;
|
||||||
|
content_y = window_padding;
|
||||||
|
content_w = framebuffer_width - (window_padding * 2);
|
||||||
|
content_h = framebuffer_height - (window_padding * 2);
|
||||||
|
visible_h = if (font.height == 0) 0 else content_h - (content_h % font.height);
|
||||||
|
|
||||||
|
for (0..framebuffer_width * framebuffer_height) |i| {
|
||||||
|
framebuffer[i] = bg_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ex.console.register(&console);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,111 @@
|
|||||||
|
//! Logging (for now) consoles.
|
||||||
|
const std = @import("std");
|
||||||
|
const r = @import("root");
|
||||||
|
const rtl = @import("rtl");
|
||||||
|
const ke = r.ke;
|
||||||
|
const ps = r.ps;
|
||||||
|
const ex = r.ex;
|
||||||
|
|
||||||
|
pub const Console = struct {
|
||||||
|
link: rtl.List.Entry,
|
||||||
|
write: *const fn (ctx: ?*anyopaque, slice: []const u8) void,
|
||||||
|
ctx: ?*anyopaque,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConWriter = struct {
|
||||||
|
interface: std.Io.Writer,
|
||||||
|
console: *Console,
|
||||||
|
|
||||||
|
pub fn init(console: *Console) ConWriter {
|
||||||
|
return .{
|
||||||
|
.interface = .{
|
||||||
|
.vtable = &.{ .drain = drain },
|
||||||
|
.buffer = &.{},
|
||||||
|
.end = 0,
|
||||||
|
},
|
||||||
|
.console = console,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain(w: *std.Io.Writer, data: []const []const u8, _: usize) std.Io.Writer.Error!usize {
|
||||||
|
const self: *ConWriter = @fieldParentPtr("interface", w);
|
||||||
|
var total_written: usize = 0;
|
||||||
|
for (data) |slice| {
|
||||||
|
self.console.write(self.console.ctx, slice);
|
||||||
|
total_written += slice.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_written;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var console_list: rtl.List = undefined;
|
||||||
|
var console_lock: ke.Mutex = .init();
|
||||||
|
var last_seq: usize = 0;
|
||||||
|
|
||||||
|
fn write_on(buf: []u8, rec: anytype, console: *Console) void {
|
||||||
|
var writer = ConWriter.init(console);
|
||||||
|
writer.interface.writeAll(buf[0..rec.length]) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain_from_on(seq: usize, console: *Console) usize {
|
||||||
|
var cur_seq: usize = seq;
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
while (true) {
|
||||||
|
const rec = ke.log.ringbuffer.read(cur_seq, &buf) catch break;
|
||||||
|
|
||||||
|
write_on(&buf, rec, console);
|
||||||
|
|
||||||
|
cur_seq += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur_seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain_from_all(seq: usize) usize {
|
||||||
|
var cur_seq: usize = seq;
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
while (true) {
|
||||||
|
const rec = ke.log.ringbuffer.read(cur_seq, &buf) catch break;
|
||||||
|
|
||||||
|
console_lock.acquire();
|
||||||
|
|
||||||
|
var it = console_list.iterator();
|
||||||
|
|
||||||
|
while (it.next()) : (it.advance()) {
|
||||||
|
const console: *Console = @fieldParentPtr("link", it.get());
|
||||||
|
write_on(&buf, rec, console);
|
||||||
|
}
|
||||||
|
|
||||||
|
console_lock.release();
|
||||||
|
|
||||||
|
cur_seq += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur_seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worker(_: ?*anyopaque) void {
|
||||||
|
while (true) {
|
||||||
|
_ = ke.wait.wait_one(&ke.log.event.hdr, null) catch unreachable;
|
||||||
|
ke.log.event.reset();
|
||||||
|
|
||||||
|
last_seq = drain_from_all(last_seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(cons: *Console) void {
|
||||||
|
// Drain all logs until now.
|
||||||
|
_ = drain_from_on(0, cons);
|
||||||
|
|
||||||
|
console_lock.acquire();
|
||||||
|
console_list.insert_tail(&cons.link);
|
||||||
|
console_lock.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() void {
|
||||||
|
console_list.init();
|
||||||
|
|
||||||
|
const td = ps.thread.create_kernel(ke.Thread.Priority.default, worker, null) catch @panic("handle me");
|
||||||
|
ke.sched.enqueue(&td.kern);
|
||||||
|
}
|
||||||
+10
-7
@@ -5,8 +5,10 @@ const ke = r.ke;
|
|||||||
const mm = r.mm;
|
const mm = r.mm;
|
||||||
const ex = r.ex;
|
const ex = r.ex;
|
||||||
const ps = r.ps;
|
const ps = r.ps;
|
||||||
const amd64 = @import("arch");
|
const exp = ex.private;
|
||||||
const turnstile = ke.private.turnstile;
|
|
||||||
|
// XXX
|
||||||
|
const fbconsole = @import("../dev/fbconsole.zig");
|
||||||
|
|
||||||
pub fn init(boot_info: *pl.BootInfo) void {
|
pub fn init(boot_info: *pl.BootInfo) void {
|
||||||
mm.init(boot_info);
|
mm.init(boot_info);
|
||||||
@@ -15,14 +17,15 @@ pub fn init(boot_info: *pl.BootInfo) void {
|
|||||||
pl.late_init(boot_info);
|
pl.late_init(boot_info);
|
||||||
mm.late_init();
|
mm.late_init();
|
||||||
ke.sched.late_init();
|
ke.sched.late_init();
|
||||||
ex.private.workqueue.init() catch @panic("e");
|
exp.workqueue.init() catch @panic("e");
|
||||||
|
exp.console.init();
|
||||||
|
|
||||||
if (boot_info.framebuffer != null) {
|
if (boot_info.framebuffer != null) {
|
||||||
const t = ps.thread.create_kernel(ke.Thread.Priority.default, ex.fireworks.start, boot_info) catch @panic("oom");
|
fbconsole.init(boot_info);
|
||||||
ke.sched.enqueue(&t.kern);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {}
|
// const t = ps.thread.create_kernel(ke.Thread.Priority.default, ex.fireworks.start, boot_info) catch @panic("oom");
|
||||||
|
// ke.sched.enqueue(&t.kern);
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std.atomic.spinLoopHint();
|
std.atomic.spinLoopHint();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub const init_mod = @import("init.zig");
|
pub const init_mod = @import("init.zig");
|
||||||
pub const workqueue = @import("workqueue.zig");
|
pub const workqueue = @import("workqueue.zig");
|
||||||
pub const init = init_mod.init;
|
pub const init = init_mod.init;
|
||||||
|
pub const console = @import("console.zig");
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ pub const private = @import("private.zig");
|
|||||||
const p = private;
|
const p = private;
|
||||||
|
|
||||||
pub const WorkItem = p.workqueue.WorkItem;
|
pub const WorkItem = p.workqueue.WorkItem;
|
||||||
|
pub const Console = p.console.Console;
|
||||||
|
|
||||||
pub const workqueue = struct {
|
pub const workqueue = struct {
|
||||||
pub const enqueue = p.workqueue.enqueue;
|
pub const enqueue = p.workqueue.enqueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const console = struct {
|
||||||
|
pub const register = p.console.register;
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub fn init(boot_info: *pl.BootInfo) linksection(r.init) void {
|
|||||||
ki.impl.early_init();
|
ki.impl.early_init();
|
||||||
std.log.info("Welcome to the machine", .{});
|
std.log.info("Welcome to the machine", .{});
|
||||||
pl.early_init(boot_info);
|
pl.early_init(boot_info);
|
||||||
|
ki.log.init();
|
||||||
ki.cpu.init_cpu(0);
|
ki.cpu.init_cpu(0);
|
||||||
ki.turnstile.init_turnstiles();
|
ki.turnstile.init_turnstiles();
|
||||||
ki.shootdown.init();
|
ki.shootdown.init();
|
||||||
|
|||||||
+19
-3
@@ -39,6 +39,22 @@ const avg_msg_size_bits = 5;
|
|||||||
|
|
||||||
pub var ringbuffer = ki.log_ring.RingBuffer(config.log_buffer_shift, avg_msg_size_bits).init();
|
pub var ringbuffer = ki.log_ring.RingBuffer(config.log_buffer_shift, avg_msg_size_bits).init();
|
||||||
|
|
||||||
|
// Initialize manually because we need this ASAP.
|
||||||
|
pub var event: ke.Event = .{
|
||||||
|
.hdr = .{
|
||||||
|
.lock = .init(),
|
||||||
|
.signaled = 0,
|
||||||
|
.type = .Notification,
|
||||||
|
.waitblocks = .{
|
||||||
|
.head = .{ .next = &event.hdr.waitblocks.head, .prev = &event.hdr.waitblocks.head },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init() void {
|
||||||
|
event.init(.Notification);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn log(
|
pub fn log(
|
||||||
comptime level: std.log.Level,
|
comptime level: std.log.Level,
|
||||||
comptime scope: @EnumLiteral(),
|
comptime scope: @EnumLiteral(),
|
||||||
@@ -63,9 +79,9 @@ pub fn log(
|
|||||||
|
|
||||||
ringbuffer.publish(res);
|
ringbuffer.publish(res);
|
||||||
|
|
||||||
// TODO: for now, we print to the platform's debug output immediately.
|
// Signal whomever is waiting on logs to get published.
|
||||||
// In the future, we should instead set an event that would get triggered in Ex,
|
event.signal();
|
||||||
// which would consume the ringbuffer from multiple consoles in a separate thread.
|
|
||||||
var writer = DebugWriter.init();
|
var writer = DebugWriter.init();
|
||||||
|
|
||||||
const ipl = out_lock.acquire_at(.High);
|
const ipl = out_lock.acquire_at(.High);
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ pub const wait = struct {
|
|||||||
pub const log = struct {
|
pub const log = struct {
|
||||||
pub const register_console = p.log.register_console;
|
pub const register_console = p.log.register_console;
|
||||||
pub const log = p.log.log;
|
pub const log = p.log.log;
|
||||||
|
pub const ringbuffer = &p.log.ringbuffer;
|
||||||
|
pub const event = &p.log.event;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const cpu = struct {
|
pub const cpu = struct {
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ pub fn init() linksection(r.init) void {
|
|||||||
ki.impl.cpu_offsets = @ptrCast(mm.zone.gpa.alloc(usize, apic.apics.items.len + 1) catch @panic("Failed to allocate AP local data offsets"));
|
ki.impl.cpu_offsets = @ptrCast(mm.zone.gpa.alloc(usize, apic.apics.items.len + 1) catch @panic("Failed to allocate AP local data offsets"));
|
||||||
const percpu_size = @intFromPtr(&__percpu_end) - @intFromPtr(&__percpu_start);
|
const percpu_size = @intFromPtr(&__percpu_end) - @intFromPtr(&__percpu_start);
|
||||||
|
|
||||||
|
log.info("per-CPU data size: {} bytes", .{percpu_size});
|
||||||
|
|
||||||
ki.impl.cpu_offsets[0] = 0;
|
ki.impl.cpu_offsets[0] = 0;
|
||||||
|
|
||||||
cpu_id_to_apic_id[0] = apic.get_id();
|
cpu_id_to_apic_id[0] = apic.get_id();
|
||||||
|
|||||||
Reference in New Issue
Block a user