diff --git a/build.zig b/build.zig index 7f0e50f..6613c21 100644 --- a/build.zig +++ b/build.zig @@ -38,10 +38,11 @@ pub fn build(b: *std.Build) void { return; }; - const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .Debug }); + const optimize = b.standardOptimizeOption(.{}); const config_module = b.createModule(.{ .root_source_file = config_wf.getDirectory().path(b, "config.gen.zig"), + .optimize = optimize, }); // first pass @@ -50,10 +51,12 @@ pub fn build(b: *std.Build) void { const empty_zig = empty_cmd.addOutputFileArg("ksyms_empty.gen.zig"); const empty_ksyms_module = b.createModule(.{ .root_source_file = empty_zig, + .optimize = optimize, }); const rtl_module = b.createModule(.{ .root_source_file = b.path("src/rtl/root.zig"), + .optimize = optimize, }); rtl_module.addImport("rtl", rtl_module); @@ -64,6 +67,7 @@ pub fn build(b: *std.Build) void { .{ .name = "config", .module = config_module }, .{ .name = "rtl", .module = rtl_module }, }, + .optimize = optimize, }); base_module.addImport("base", base_module); @@ -81,6 +85,7 @@ pub fn build(b: *std.Build) void { const ksyms_zig = ksyms.addOutputFileArg("ksyms.gen.zig"); const ksyms_module = b.createModule(.{ .root_source_file = ksyms_zig, + .optimize = optimize, }); // second pass @@ -214,6 +219,9 @@ fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz } else { kernel.linker_script = b.path("build/linker-scripts/uml.lds"); kernel.root_module.link_libc = true; + kernel.linker_allow_shlib_undefined = true; + kernel.root_module.linkSystemLibrary("sdl2-compat", .{ .use_pkg_config = .force }); + kernel.root_module.addLibraryPath(.{ .cwd_relative = "/usr/lib64" }); } return kernel; diff --git a/src/kern/init.zig b/src/kern/init.zig index c00d425..47d3626 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -14,13 +14,13 @@ fn handler(_: ?*anyopaque) void { while (true) {} } -fn make_thread(entry: *const fn (?*anyopaque) void, td: *ke.Thread) void { +fn make_thread(entry: *const fn (?*anyopaque) void, td: *ke.Thread, arg: ?*anyopaque) void { const _stack = std.heap.page_allocator.alloc(u8, 16384) catch @panic("wtf"); - td.init(@intFromPtr(_stack.ptr), 16384, entry, null); + td.init(@intFromPtr(_stack.ptr), 16384, entry, arg); } -var threads: [10]ke.Thread = undefined; +var threads: [1]ke.Thread = undefined; pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { pl.early_init(); @@ -37,14 +37,21 @@ pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { ki.sched.late_init(); - _ = ke.ipl.raise(.Dispatch); + if (boot_info.framebuffer == null) { + const ipl = ke.ipl.raise(.Dispatch); - for (0..threads.len) |i| { - make_thread(handler, &threads[i]); - ke.sched.enqueue(&threads[i]); + for (&threads) |*td| { + make_thread(&handler, td, null); + td.priority = ke.Thread.Priority.default; + td.priority_class = .Batch; + ke.sched.enqueue(td); + } + + ke.ipl.lower(ipl); + } else { + make_thread(&b.ex.fireworks.start, &threads[0], boot_info); + ke.sched.enqueue(&threads[0]); } - ke.ipl.lower(.Passive); - while (true) {} } diff --git a/src/platform/root.zig b/src/platform/root.zig index 087da67..bd24b9d 100644 --- a/src/platform/root.zig +++ b/src/platform/root.zig @@ -59,6 +59,14 @@ pub const BootInfo = struct { entries: [256]Entry, }; + pub const Framebuffer = struct { + address: usize, + width: u32, + height: u32, + pitch: u32, + bpp: u8, + }; + pub const KernelAddress = struct { physical_base: usize, virtual_base: usize }; /// RSDP on ACPI machines @@ -70,6 +78,9 @@ pub const BootInfo = struct { /// Memory map memory_map: MemMap, + /// Framebuffer info, if available + framebuffer: ?Framebuffer, + /// Kernel address kernel_address: KernelAddress, }; diff --git a/src/platform/um/entry.zig b/src/platform/um/entry.zig index 2c704a4..6c50e1e 100644 --- a/src/platform/um/entry.zig +++ b/src/platform/um/entry.zig @@ -4,6 +4,12 @@ pub const std_options = b.std_options; pub const rtl = @import("rtl"); +const c = @cImport({ + @cInclude("SDL.h"); + @cInclude("signal.h"); + @cInclude("pthread.h"); +}); + const std = @import("std"); const ke = b.ke; const pl = b.pl; @@ -27,6 +33,98 @@ fn parse_mem_size(str: []const u8) !usize { return size * mul; } +var gui_started = std.atomic.Value(bool).init(false); + +fn gui_thread() void { + if (c.SDL_Init(c.SDL_INIT_VIDEO) != 0) { + std.debug.panic("Failed to initialize SDL: {s}\n", .{c.SDL_GetError()}); + } + + const window = c.SDL_CreateWindow( + "Zag (UM)", + c.SDL_WINDOWPOS_UNDEFINED, + c.SDL_WINDOWPOS_UNDEFINED, + 800, + 600, + c.SDL_WINDOW_SHOWN, + ); + + if (window == null) { + std.debug.panic("Failed to create window: {s}\n", .{c.SDL_GetError()}); + } + + const renderer = c.SDL_CreateRenderer(window.?, -1, c.SDL_RENDERER_ACCELERATED); + if (renderer == null) { + std.debug.panic("Failed to create renderer: {s}\n", .{c.SDL_GetError()}); + } + + const fb_surface: ?*c.SDL_Surface = c.SDL_CreateRGBSurface(0, 800, 600, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0); + if (fb_surface == null) { + std.debug.panic("Failed to create surface: {s}\n", .{c + .SDL_GetError()}); + } + + const surface = fb_surface.?; + + const texture = c.SDL_CreateTexture( + renderer.?, + c.SDL_PIXELFORMAT_RGB888, + c.SDL_TEXTUREACCESS_STREAMING, + 800, + 600, + ); + + pl.impl.global_state.boot_info.framebuffer = .{ + .address = @intFromPtr(surface.pixels), + .width = 800, + .height = 600, + .pitch = @intCast(surface.pitch), + .bpp = 32, + }; + + gui_started.store(true, .release); + + // Mask all signals. + var sigset: c.sigset_t = undefined; + _ = c.sigfillset(&sigset); + _ = c.pthread_sigmask(c.SIG_BLOCK, &sigset, null); + + var event: c.SDL_Event = undefined; + var quit = false; + + while (!quit) { + const frame_start = c.SDL_GetTicks(); + + while (c.SDL_PollEvent(&event) != 0) { + if (event.type == c.SDL_QUIT) { + quit = true; + } + } + + // Copy the framebuffer (surface) to a texture and render it. + _ = c.SDL_UpdateTexture(texture, null, surface.pixels, surface.pitch); + _ = c.SDL_RenderClear(renderer.?); + _ = c.SDL_RenderCopy(renderer.?, texture, null, null); + _ = c.SDL_RenderPresent(renderer.?); + + const frame_time = c.SDL_GetTicks() - frame_start; + + if (frame_time < 16) { + c.SDL_Delay(16 - frame_time); + } + } + + _ = c.SDL_DestroyTexture(texture); + + // Exit. + c.SDL_Quit(); + std.process.exit(0); +} + +fn start_gui() void { + _ = std.Thread.spawn(.{ .allocator = std.heap.page_allocator }, gui_thread, .{}) catch unreachable; +} + pub fn main(init: std.process.Init) !void { var args = try init.minimal.args.iterateAllocator(init.gpa); defer args.deinit(); @@ -36,6 +134,7 @@ pub fn main(init: std.process.Init) !void { var mem_size: usize = default_mem_size; var cmdline: ?[]const u8 = null; var ncpus: usize = 1; + var gui = false; while (args.next()) |arg| { if (std.mem.eql(u8, arg, "-m") or std.mem.eql(u8, arg, "--mem-size")) { @@ -46,6 +145,8 @@ pub fn main(init: std.process.Init) !void { } else if (std.mem.eql(u8, arg, "-t") or std.mem.eql(u8, arg, "--threads")) { const val = args.next() orelse fatal("--threads requires a value", .{}); ncpus = std.fmt.parseInt(usize, val, 10) catch fatal("invalid thread count", .{}); + } else if (std.mem.eql(u8, arg, "-g") or std.mem.eql(u8, arg, "--gui")) { + gui = true; } else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { var stderr = std.Io.File.stderr().writerStreaming(init.io, &.{}); var w = &stderr.interface; @@ -55,6 +156,7 @@ pub fn main(init: std.process.Init) !void { \\ -m, --mem-size SIZE Set the memory size \\ -c, --cmdline STR Set the kernel cmdline \\ -t, --threads COUNT Set the number of threads + \\ -g, --gui, Enable graphics mode using SDL2 \\ , .{exe}); std.process.exit(0); @@ -72,6 +174,13 @@ pub fn main(init: std.process.Init) !void { pl.impl.global_state.boot_info.rsdp = 0; pl.impl.global_state.boot_info.cmdline = cmdline; + if (gui) { + start_gui(); + while (gui_started.load(.monotonic) != true) { + std.atomic.spinLoopHint(); + } + } + ke.ncpus = ncpus; ke.private.init(&pl.impl.global_state.boot_info);