From bbd67d065d74daebe75016f4444a00b9be3e2d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gr=C3=A9goire?= Date: Thu, 3 Sep 2026 21:05:20 -0400 Subject: [PATCH] fbconsole -> bootvid --- .../fbconsole.zig => bootvid/bootvid.zig} | 167 ++++++++---------- src/bootvid/sun12x22-ascii.psf | Bin 0 -> 5664 bytes src/dev/sun12x22.psfu | Bin 12087 -> 0 bytes src/ex/init.zig | 7 - src/kern/init.zig | 2 + src/kern/log.zig | 2 + src/kern/panic.zig | 7 +- src/root.zig | 1 + 8 files changed, 84 insertions(+), 102 deletions(-) rename src/{dev/fbconsole.zig => bootvid/bootvid.zig} (52%) create mode 100644 src/bootvid/sun12x22-ascii.psf delete mode 100644 src/dev/sun12x22.psfu diff --git a/src/dev/fbconsole.zig b/src/bootvid/bootvid.zig similarity index 52% rename from src/dev/fbconsole.zig rename to src/bootvid/bootvid.zig index 5d9bf90..12f5e0c 100644 --- a/src/dev/fbconsole.zig +++ b/src/bootvid/bootvid.zig @@ -1,9 +1,9 @@ //! Very bad and slow framebuffer console. 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; @@ -28,25 +28,33 @@ const Psf2Header = extern struct { const FontInfo = struct { header_size: usize, glyph_size: usize, - width: usize, - height: usize, + width: u16, + height: u16, }; -const font_file = @embedFile("sun12x22.psfu"); +const Cursor = struct { + x: u16 = 0, + y: u16 = 0, +}; + +const font_file = @embedFile("sun12x22-ascii.psf"); +const text_color: u32 = 0xFFFFFFFF; +const bg_color: u32 = 0xFF000000; +const crash_bg_color: u32 = 0xFF0000FF; +const window_padding: usize = 10; + 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 cursor: Cursor = .{}; +var console_width: u16 = 0; +var console_height: u16 = 0; -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; +var enabled = true; fn load_font() void { if (font_file.len >= @sizeOf(Psf2Header)) { @@ -61,6 +69,7 @@ fn load_font() void { return; } } + if (font_file.len >= @sizeOf(Psf1Header)) { const h1: *const Psf1Header = @ptrCast(@alignCast(font_file.ptr)); if (h1.magic == PSF1_MAGIC) { @@ -73,121 +82,95 @@ fn load_font() void { return; } } - font = .{ .header_size = 0, .glyph_size = 0, .width = 0, .height = 0 }; + + 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)]; + const start_x = window_padding + x; + const start_y = window_padding + y; + 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; + framebuffer[(start_y + row) * framebuffer_width + (start_x + col)] = text_color; } } } } -var cursor_x: usize = 0; -var cursor_y: usize = 0; +fn scroll() void { + const rows_to_shift = (console_height - 1) * font.height; -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; + // Move all lines up. + for (0..rows_to_shift) |row| { + const dst_y = window_padding + row; + const src_y = dst_y + font.height; for (0..content_w) |col| { - framebuffer[dst_y * framebuffer_width + content_x + col] = bg_color; + framebuffer[dst_y * framebuffer_width + window_padding + col] = + framebuffer[src_y * framebuffer_width + window_padding + col]; + } + } + + // Clear the last line. + for (0..font.height) |row| { + const dst_y = window_padding + (console_height - 1) * font.height + row; + + for (0..content_w) |col| { + framebuffer[dst_y * framebuffer_width + window_padding + 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); +pub fn disable() void { + enabled = false; } -fn ensure_cursor_visible() void { - while (cursor_y + font.height > visible_h) { - scroll_up(font.height); - cursor_y -= font.height; - } -} +pub fn crash() void { + cursor.x = 0; + cursor.y = 0; -fn newline() void { - cursor_x = 0; - cursor_y += font.height; - - while (cursor_y > visible_h) { - scroll_up(font.height); - cursor_y -= font.height; + for (0..framebuffer_width * framebuffer_height) |i| { + framebuffer[i] = crash_bg_color; } } pub 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; - } - - // Backspace. - if (c == '\x08') { - if (cursor_x >= font.width) { - cursor_x -= font.width; - ensure_cursor_visible(); - for (0..font.height) |row| { - const dst_y = content_y + cursor_y + row; - for (0..font.width) |col| { - framebuffer[dst_y * framebuffer_width + content_x + cursor_x + col] = bg_color; - } - } - } - return; - } + if (!enabled) return; if (c == '\n') { - newline(); - } else if (c == '\r') { - cursor_x = 0; - } else { - if (cursor_x + font.width > content_w) { - newline(); + cursor.x = 0; + cursor.y += 1; + + if (cursor.y >= console_height) { + cursor.y = console_height - 1; + scroll(); } - - ensure_cursor_visible(); - plot_character(content_x + cursor_x, content_y + cursor_y, c); - cursor_x += font.width; + } else if (c == '\r') { + cursor.x = 0; + } else { + plot_character(cursor.x * font.width, cursor.y * font.height, c); + cursor.x += 1; } } -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: *r.BootInfo) void { framebuffer = @ptrFromInt(boot_info.framebuffer.?.address); framebuffer_width = boot_info.framebuffer.?.width; @@ -195,15 +178,17 @@ pub fn init(boot_info: *r.BootInfo) void { 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); + + console_width = @intCast(content_w / font.width); + console_height = std.math.divCeil( + u16, + @intCast(content_h), + font.height, + ) catch unreachable; for (0..framebuffer_width * framebuffer_height) |i| { framebuffer[i] = bg_color; } - - ex.console.register(&console); } diff --git a/src/bootvid/sun12x22-ascii.psf b/src/bootvid/sun12x22-ascii.psf new file mode 100644 index 0000000000000000000000000000000000000000..8f3e78d769e2a37e49274de739bd8ad87dd38398 GIT binary patch literal 5664 zcmbVQy>8q_5T5fXM=L&yQWMBG0meJh9!pGJk9m zUf6^aj!a}i3TeTyJs)4o87I$04vJMqQ$~q z;Q`i{Hwm{z%Sg$+OyA?&;hS8*W;WxPV`-)T=490Ilq)#7V>32jWog@X@a4Y9US!YX z=drZiazh*1p6%;_p3pXMW@bj(Nk@?Cyf;4W|Su!ftq#al>-VaKSTBI=nQWNtx^{{s@ui<%RbxrVam7pD|6Reqkf^|kS_8ieMxPr zE%xO~Mjx?YXeN;Sgzo5>o$<^hcA|aWAx(g?=ON-{-5+f9dZt{to}XlHAF<>!k08&D z^f6juxsK0U_9R2j^^tof8N05D8^P9cinJ$E{I+4%`|O*CoLR5|vJM^OmeMJ%8b(1T>r#*Uu#VWLnIA&hFwU|ga}nbgYW-%M z!4t4Uv8>K)qH%7tvtB^EzPhzuJqGG@jipA`IUFsX>^z6M#{q5PR?YAXsK?Oe3@H6t zw#!&GO%X+X1Ao}QSwpWWR;sccR$Px97vquxx*$YOi($BZ_3+s};J($xeGyupTlRVZ zxC$>I>M={PN>jvy&3#UyW70;m;LI$9DuItplNWKOl~UMAg$l7gO2ZcKG0jA36VjP^ z+Z0O+j#D7^5}U-trl25KPEnJ22`}azG+C*s?=06^=~$0+Ti+aGB`j=7Nt?H97MW&V zDErOxM(}Yt*4?fkGRpchw=;8x=5!Bxk^1ie?b96tJK8*JZceJ!4(zm<&9oHrz#p~X zxB5Ham%x8od*e*2OdY5n)NOT3eXVY)8_!Y;)V5UfR27_hi7YYFDJXbyWSVE5P{$%a z!1h4CYU-MI_^m^3oKDjS=P062|LoaQ6%fBOiCRHa$YbPZ9Vtt#**KNIesc0GTc_nYf zxF@BSsj8XcZo_IwFEx;=w5)RcRz`e~uYk+=2GQ)lf1jRwy_WOj<`XLgCNTZ-oIjd36A1ODFlyj3s4J+D|7^YE>hw(t2Ir9SnFN@_XT(=vEzer9dO z%LSwvvSt}cr{R7wIaC|<*wIrPxnPwE4d!3W7d+*&^LnWh{+naus*@ZnUT{%Rj=NUA zLWhWzdW;R#S{fLiuj(CIKXP#1bT#TzE(gk8B^lG@OrN9b9cL~NBLf3n2ChbZ>UA#m z{}?e9(1oJXebum3#gCc;IosDk_Iw_Z*9;7tNqd9)CV=EknyL(^w2~xL-&8R;L{;JP ss(Xhz)PsuiY)U3=QpJO>QFfWoz1#Gpy3VG$uJ`$-=@|OouUroP7ru^Ug#Z8m literal 0 HcmV?d00001 diff --git a/src/dev/sun12x22.psfu b/src/dev/sun12x22.psfu deleted file mode 100644 index 937a96cd93f7ed3b80dd4f2828b1e79e53fe1226..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12087 zcmbVSYmi(;74BuiP6C_E##C9eu-6c5|<7!9yJ241Uf<@=?SgE`Nwn)4sgczy5&quK`GZ>wzu4FKarF zR87~-EuEVh&CYFrW~b5E(W#q!T<$-j>bhy_x~^-grYfQ$26M=sAj+bmDypWLKL2v5 zEKbP56{sx(P7qbS-mEvLXxlrVhe3?UBQqCGvsw>ge6FeMUeeqWV;AvD9SiSuIGBcM zXkYuBa{0Y(tHp^&uyjY%hytqSw09j`x^Fh#Y#jBE`kdy|t(b}#FeN!C$HW9+T1_iX z$8xlQ$B)P6^=Dv-aGDJjGbD#3enH2Db>ddBL-?rSjk6iwCCkg`UKGIQHNdtgz!ze~glQ4@7wu0XD->#BxyiGw4=xXJ@Y;`$7-sCCxq zTO(MfNA*w&MokZHM8U^8)}mZZ45$In90wmCB(OF&Q`C#+<>K)eE-tS_0$CoCmLylS zYH{DCqADgu(-1hEM1E=9kdqGZ{55#&G|hlUJ_jeiir26X&B$+E292KrT>{-HJ))N7 zqeZn#q7`AiV9j9NXzC(#ZetwA z=6piT0NgMr*c#55895_1AYayla!}4lo@cLpnADf*3)Qu1ttdr24vFRZ6nTm_;f-sT zE?e}fW~it!P~I*!%V{&Mr&ZmINf&oWi~)GgL(rFDw=q*1Q1QrV`^0@q&?OhS3VODs zgw`11aa`QE#1?W!ZZjjGHKMj*UD$pW1*XNoyDZKZ;kxzx&6(-C{f=L8j$eysgx2AOfJ}fWH2TR-c zdgsWVfM)^;)=XJfbY$~oAZ5Ti&kdLp;6%VOz)HaB&@fmN%r>hJ70?=netF;p`f5NQ z0J_rGHRaMRTkfLu*`l`Uh5;HvBZmh*rDTAkMhhi)TrnU@SRG!UtbM46mCCMcRV_#R z$*FQy&mak`Lo}>xn7YKik#_aUVx7xws+87&bUuZW%BC@Fo+X*P59NR^i&31a4g8kH zfCe|m39($`v4GPIsjAq%W(om){QB z;?Oj4j?+%hn$=X+F2v>ReiM*!+?Jm%fo8=1XU9yd&0HY=>7=`^yF zW3M#_a@Wi>Bs=qft0co^@^ZihfPeSQO*f?1@OF4tdY5_^c^7+|yp8(;1CS>;u*wpx3B0=Q(oAq3Z{eK@Iz;CeFNjz$~x;{hem5Wq^l# z{57i?>>dyEgPj=@Wre+tc?D9!yG|(DT+>|S-V*bGHNuI;_SafqmJa0BBV&tOUBFG|*(2?Us*ol6{%Pn~Bagj0W?P3hb4}UXGh`iyQbBa4PN~ zQoHpZro^|`xF4T$NR;9Rd(Ug6~VO_%g$)1fnhH1I_45fm1^F&v3qn})j=6vxbbA*1s?n3j5v(Z8j(({!I|jzXnLn%jC5d0aIoHW zqP^_(M-Ay8)+dA#ry1}t(jjB1+kYZZ173RBkY){@XOd|=sBPU$s@X+n}hQ7hz zMv{WJ3uSSR+PUaD#QdZ^D)!}gPDSBub)%Msr5uHz0jn8n(t^5oJ6F7wY2A^Nre>9u zJ7kb=R_EzSeSEtf^MtoK4O`WOlvb;jHE;jYlmTh0oDR1t#0n!4zK@d(5bRGIr4Ph$*fd@51#`z+vS1h zycC+3RSfJ&vr(5+8I(uGq}RYMO(=1$q!Jw*f>&Yfs;z?`>VyRI_&M%0DiR#rMzyts zwrWnBT>1s>O`vr9FEJ;y3@^!nn9J^mzCAzCFu? zwV|d~>$5e#N-}H(?ReR@xvJRUHO;h6V+z4c#@|?&x{cNX zyO`Nq)`VE2_zTK}KW=tpsKTo^cu$Quw!e5`(&_R)a1Qz10T%3}TFj@#=R3vv-p_tZ zaQo4-nC{~GFdEeLnTlCo+>f%j@xAwI?{dEN?%P;!=jAUP1F8(Y;O`p~>NG7x_EnI_ z4F^uUFNka>f+=WDa;`-v-JiO~TM*-+vx18a-U|a4moSD zN`B+#*Zm2s?L+=jZ(4b(pLBcU3~!Hml_2cqIGcT zS}gA2aF)+|#BUJ~d31|-aL2Oz7V+T9%~I@-t{tTaw}@YNJX>;cZHei+^C&JA$=Dq$ z(X+hq+`0tUrBTc;^H9v8+fp<)cPz#ljP%T{FD`u$8&$O5c+`HuU9&`PdHw$<`iD}d zf5;mvkFRW9MXoZZYx|lhbwj~JlRY@_SFG1JuGYXyj9;b3)%Gl^;Q#iBzKTS?ob~A9 z-yY;{1%G?wn@Jj6T*kniLB?^NHz8PI=w_c_90)T!xQ{ z|MrUB%}ThR#b2q9|DMY1PwB0g=VAY_KO?uwDczltn`rwLWvzp|KDq(&y4euFuJM~^ zRn*YRIbIijd2H zy8}@nK7;ryx*eqXxtHhXKw+<%-@A+Ecel{%Ahlnn_LFFP4mD39-h+4_;=PD>)BK)? zk09(| zx{|J<@6dPYYWg01pMLQ6zFj0~ny#VE^h3IqW@rojh<;2zp`X%qbUocbTd6@e(oJ+T z{fvH2+h{xOpq+FJ{eo_#+vs+hzv~VPe?#Fx3ZJI%Q3@ZYa36(FQ1~Q;&rtX*g-=oV zXA1vB;d2!3qVU)AyHI&2h5ISoP2r0azD(f@6uwH~-zhvq;T(kzQu|SAKSpf`=8h-n z@Vyj1Lg61N{0oJzQ20EB_fY#8YQIA5Ltx=$bEDarN`*#uc-afk%M$}7aiFj-cLvH_w@X({d5FO?sWw$3jP(vJ=8t`qz9;t(jH9Wmxrj$+PLrSL$4A2 EA1a-r-T(jq diff --git a/src/ex/init.zig b/src/ex/init.zig index 7f4a959..8879a89 100644 --- a/src/ex/init.zig +++ b/src/ex/init.zig @@ -8,9 +8,6 @@ const ps = r.ps; const exp = ex.private; const rtl = @import("rtl"); -// XXX -const fbconsole = @import("../dev/fbconsole.zig"); - pub fn init() void { mm.init(); ps.init(); @@ -34,8 +31,4 @@ pub fn init() void { } } } - - if (r.boot_info.framebuffer != null) { - // fbconsole.init(r.boot_info); - } } diff --git a/src/kern/init.zig b/src/kern/init.zig index e6275fd..6eceb75 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -5,6 +5,7 @@ const ke = r.ke; const arch = r.arch; const kep = ke.private; const ex = r.ex; +const bv = r.bv; var thread0: ke.Thread = undefined; @@ -31,6 +32,7 @@ fn init() linksection(r.init) void { ke.ncpus = 1; kep.impl.early_init(); kep.tunable.init(); + bv.init(r.boot_info); std.log.info("Welcome to the machine", .{}); pl.early_init(); kep.log.init(); diff --git a/src/kern/log.zig b/src/kern/log.zig index f8b3cb9..6ff337b 100644 --- a/src/kern/log.zig +++ b/src/kern/log.zig @@ -5,6 +5,7 @@ const r = @import("root"); const pl = r.pl; const ke = r.ke; const kep = ke.private; +const bv = r.bv; var out_lock: ke.SpinLock = .init(); @@ -29,6 +30,7 @@ const DebugWriter = struct { var total_written: usize = 0; for (data) |slice| { for (slice) |byte| { + bv.write_char(byte); pl.impl.debug_write(byte); } total_written += slice.len; diff --git a/src/kern/panic.zig b/src/kern/panic.zig index 6cdc393..680f1cd 100644 --- a/src/kern/panic.zig +++ b/src/kern/panic.zig @@ -4,6 +4,7 @@ const r = @import("root"); const ksyms = @import("ksyms"); const ke = r.ke; const kep = ke.private; +const bv = r.bv; extern var text_start_addr: u8; extern var text_end_addr: u8; @@ -78,8 +79,7 @@ pub fn panic_with_frame( frame: usize, ) noreturn { _ = kep.impl.disable_interrupts(); - - std.log.info("Crash count is {} on cpu {}", .{ crash_count.load(.monotonic), ke.cpu.current() }); + bv.crash(); if (crash_count.fetchAdd(1, .monotonic) != 0) { std.log.err("Recursive panic from CPU {}", .{ke.cpu.current()}); @@ -97,10 +97,9 @@ pub fn panic_with_frame( _ = ke.ipl.raise(.High); - std.log.err("KERNEL PANIC: {s} on CPU {}, curthread is {*}", .{ + std.log.err("KERNEL PANIC: {s} on CPU {}", .{ msg, ke.cpu.current(), - kep.sched.percpu.local().current_thread.?, }); std.log.err("Stack trace:", .{}); diff --git a/src/root.zig b/src/root.zig index 96a067d..36609a5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -10,6 +10,7 @@ pub const ps = @import("proc/root.zig"); pub const BootInfo = @import("loader/info.zig"); pub const tests = @import("tests/root.zig"); pub const rcu = @import("rcu/root.zig"); +pub const bv = @import("bootvid/bootvid.zig"); const config = @import("config");