Files
YukiOS/arch/x86_64/boot/early_serial_console.zig

118 lines
4.0 KiB
Zig

const std = @import("std");
const Cmdline = @import("cmdline.zig").Cmdline;
const io = @import("arch").io;
const PREDEFINED_PORTS: [2]u16 = .{
0x3F8, // ttyS0
0x2F8, // ttyS1
};
var serial_writer: ?SerialWriter = null;
// Parse earlyprintk argument from cmdline
// Format: earlyprintk=serial,port,baudrate, port can either be ioport address (e.g., 0x3F8) or
// a predefined name (e.g., ttyS0/ttyS1).
fn parseEarlyprintk(cmdline: *const Cmdline) ?struct { port: u16, baudrate: u32 } {
var buf = [_]u8{0} ** 32;
const len = cmdline.parseArg("earlyprintk", &buf) orelse 0;
if (len == 0) return null;
const arg = buf[0..len];
var it = std.mem.splitAny(u8, arg, ",");
if(std.mem.eql(u8, it.next() orelse "", "serial") == false) {
return null;
}
const port_str = it.next() orelse return null;
var port: u16 = undefined;
if (port_str.len == 5 and std.mem.startsWith(u8, port_str, "ttyS")) {
const port_idx = std.fmt.parseInt(u8, port_str[4..5], 10) catch return null;
if (port_idx >= PREDEFINED_PORTS.len) {
return null;
}
port = PREDEFINED_PORTS[port_idx];
} else if (std.fmt.parseInt(u16, port_str, 16) catch null) |port_val| {
port = port_val;
} else {
return null;
}
const baudrate_str = it.next() orelse return null;
const baudrate = std.fmt.parseInt(u32, baudrate_str, 10) catch return null;
return .{ .port = port, .baudrate = baudrate };
}
fn serialInit(port: u16, baudrate: u32) void {
const divisor: u16 = @intCast(115200 / baudrate);
// Disable interrupts
io.outb(port + 1, 0x00);
// Disable FIFO
io.outb(port + 2, 0x00);
// Set 8 data bits, 1 stop bit, no parity
io.outb(port + 3, 0x80);
const c = io.inb(port + 3);
// Enable DLAB
io.outb(port + 3, c | 0x80);
// Set divisor low byte
io.outb(port + 0, @intCast(divisor & 0x00FF));
// Set divisor high byte
io.outb(port + 1, @intCast((divisor >> 8) & 0x00FF));
io.outb(port + 3, c & ~@as(u8, 0x80));
}
pub fn earlyConsoleInit(cmdline: *const Cmdline) void {
const result = parseEarlyprintk(cmdline) orelse return;
serialInit(result.port, result.baudrate);
serial_writer = SerialWriter.init(result.port);
}
const SerialWriter = struct {
port: u16,
interface: std.Io.Writer,
var WRITER_VTABLE: std.Io.Writer.VTable = undefined;
pub fn init(port: u16) SerialWriter {
WRITER_VTABLE = .{
.drain = SerialWriter.drain,
};
var sw = SerialWriter{
.port = port,
.interface = undefined,
};
sw.interface = .{
.buffer = &[0]u8{},
.vtable = @call(.never_inline, getWriterVTable, .{}),
};
return sw;
}
pub fn writer(self: *SerialWriter) *std.Io.Writer {
return &self.interface;
}
fn getWriterVTable() *const std.Io.Writer.VTable {
return &WRITER_VTABLE;
}
// fn getWriterDrain() *const @TypeOf(SerialWriter.drain) {
// return SerialWriter.drain;
// }
fn drain(self: *std.Io.Writer, buf: []const []const u8, splat: usize) error{}!usize {
const sw: *SerialWriter = @fieldParentPtr("interface", self);
var written: usize = 0;
for (buf, 0..buf.len) |part, i| {
const repeat = if (i == buf.len - 1) splat else 1;
for (0..repeat) |_| {
for (part) |c| {
SerialWriter.putchar(sw, c);
written += 1;
}
}
}
return written;
}
fn putchar(self: *const SerialWriter, c: u8) void {
var timeout: usize = 0xffff;
while(io.inb(self.port + 5) & 0x20 == 0 and timeout > 0) {
timeout -= 1;
asm volatile ("pause"
::: .{ .memory = true }
);
}
io.outb(self.port, c);
}
};
pub fn dprint(comptime fmt: []const u8, args: anytype) void {
var sw = serial_writer orelse return;
_ = sw.writer().print(fmt, args) catch {};
}