118 lines
4.0 KiB
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 {};
|
|
} |