mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
riscv64: initial risc-v port
This commit is contained in:
@@ -223,6 +223,10 @@ fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz
|
||||
kernel.root_module.addAssemblyFile(b.path("src/kern/amd64/locore.s"));
|
||||
kernel.root_module.addAssemblyFile(b.path("src/platform/pc/ap.s"));
|
||||
},
|
||||
.riscv64 => {
|
||||
kernel.root_module.addAssemblyFile(b.path("src/kern/riscv64/locore.s"));
|
||||
kernel.root_module.addAssemblyFile(b.path("src/platform/riscv64/trap.s"));
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
@@ -248,7 +252,11 @@ fn addLoader(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz
|
||||
loader.root_module.addImport("arch", arch_module);
|
||||
loader.root_module.addImport("config", config_module);
|
||||
loader.root_module.addImport("rtl", rtl);
|
||||
loader.root_module.addAssemblyFile(b.path("src/loader/jump.s"));
|
||||
switch (plat.arch) {
|
||||
.x86_64 => loader.root_module.addAssemblyFile(b.path("src/loader/x86_64/jump.s")),
|
||||
.riscv64 => loader.root_module.addAssemblyFile(b.path("src/loader/riscv64/jump.s")),
|
||||
else => {},
|
||||
}
|
||||
|
||||
loader.use_llvm = true;
|
||||
loader.use_lld = true;
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@ pub const Platform = struct {
|
||||
const ArchEnum = enum {
|
||||
amd64,
|
||||
aarch64,
|
||||
riscv64,
|
||||
};
|
||||
|
||||
const BootloaderEnum = enum {
|
||||
@@ -36,11 +37,12 @@ pub fn getPlatform(config: Config) !Platform {
|
||||
.arch = switch (config.arch) {
|
||||
.amd64 => .x86_64,
|
||||
.aarch64 => .aarch64,
|
||||
.riscv64 => .riscv64,
|
||||
},
|
||||
.os = .freestanding,
|
||||
};
|
||||
|
||||
if (ret.os == .freestanding and (ret.arch == .x86_64 or ret.arch == .aarch64)) {
|
||||
if (ret.os == .freestanding and (ret.arch == .x86_64 or ret.arch == .aarch64 or ret.arch == .riscv64)) {
|
||||
ret.bootloader = switch (config.bootloader) {
|
||||
.limine => .Limine,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
OUTPUT_FORMAT(elf64-littleriscv)
|
||||
|
||||
ENTRY(kmain)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
limine_requests PT_LOAD;
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
|
||||
.limine_requests : {
|
||||
KEEP(*(.limine_requests_start))
|
||||
KEEP(*(.limine_requests))
|
||||
KEEP(*(.limine_requests_end))
|
||||
} :limine_requests
|
||||
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
|
||||
|
||||
text_start_addr = .;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
text_end_addr = .;
|
||||
rodata_start_addr = .;
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
|
||||
.note.gnu.build-id : {
|
||||
*(.note.gnu.build-id)
|
||||
} :rodata
|
||||
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
rodata_end_addr = .;
|
||||
|
||||
data_start_addr = .;
|
||||
|
||||
.data.percpu : {
|
||||
__percpu_start = .;
|
||||
KEEP(*(.data.percpu .data.percpu.*))
|
||||
__percpu_end = .;
|
||||
} :data
|
||||
|
||||
.data.tunable : {
|
||||
__tunables_start = .;
|
||||
KEEP(*(.data.tunable .data.tunable.*))
|
||||
__tunables_end = .;
|
||||
} :data
|
||||
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
|
||||
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
data_end_addr = .;
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
OUTPUT_FORMAT(elf64-littleriscv)
|
||||
ENTRY(loader_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
limine_requests PT_LOAD;
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0xffffffffe0000000;
|
||||
|
||||
.limine_requests : {
|
||||
KEEP(*(.limine_requests_start))
|
||||
KEEP(*(.limine_requests))
|
||||
KEEP(*(.limine_requests_end))
|
||||
} :limine_requests
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
text_start_addr = .;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
text_end_addr = .;
|
||||
|
||||
rodata_start_addr = .;
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
.note.gnu.build-id : {
|
||||
*(.note.gnu.build-id)
|
||||
} :rodata
|
||||
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
rodata_end_addr = .;
|
||||
data_start_addr = .;
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
data_end_addr = .;
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -62,8 +62,8 @@ pub fn addRun(b: *std.Build, kernel: *std.Build.Step.Compile, loader: *std.Build
|
||||
|
||||
if (!debug and builtin.target.os.tag == .linux and builtin.cpu.arch == .x86_64) qemu.addArgs(&.{ "-enable-kvm", "-cpu", "host,+invtsc" });
|
||||
},
|
||||
.aarch64 => qemu.addArgs(&.{ "-machine", "virt", "-cpu", "cortex-a57" }),
|
||||
.riscv64 => qemu.addArgs(&.{ "-machine", "virt" }),
|
||||
.aarch64 => qemu.addArgs(&.{ "-machine", "virt", "-cpu", "cortex-a57", "-device", "ramfb" }),
|
||||
.riscv64 => qemu.addArgs(&.{ "-machine", "virt", "-m", "1G", "-device", "ramfb" }),
|
||||
else => {},
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
.options = .{
|
||||
.{ .value = "amd64" },
|
||||
.{ .value = "aarch64" },
|
||||
.{ .value = "riscv64" },
|
||||
},
|
||||
},
|
||||
.{
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
pub const name: []const u8 = "riscv64";
|
||||
pub const BootInfo = struct {};
|
||||
|
||||
pub inline fn read_csr(comptime csr: []const u8) u64 {
|
||||
return asm volatile ("csrr %[out], " ++ csr
|
||||
: [out] "=r" (-> u64),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn write_csr(comptime csr: []const u8, value: u64) void {
|
||||
asm volatile ("csrw " ++ csr ++ ", %[value]"
|
||||
:
|
||||
: [value] "r" (value),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn sfence_vma() void {
|
||||
asm volatile ("sfence.vma" ::: .{ .memory = true });
|
||||
}
|
||||
|
||||
pub inline fn sfence_vma_page(va: usize) void {
|
||||
asm volatile ("sfence.vma %[va], zero"
|
||||
:
|
||||
: [va] "r" (va),
|
||||
: .{ .memory = true });
|
||||
}
|
||||
@@ -144,6 +144,7 @@ pub fn disable() void {
|
||||
}
|
||||
|
||||
pub fn crash() void {
|
||||
if (!enabled) return;
|
||||
cursor.x = 0;
|
||||
cursor.y = 0;
|
||||
|
||||
@@ -172,9 +173,13 @@ pub fn write_char(c: u8) void {
|
||||
}
|
||||
|
||||
pub fn init(boot_info: *r.BootInfo) void {
|
||||
framebuffer = @ptrFromInt(boot_info.framebuffer.?.address);
|
||||
framebuffer_width = boot_info.framebuffer.?.width;
|
||||
framebuffer_height = boot_info.framebuffer.?.height;
|
||||
const fb = boot_info.framebuffer orelse {
|
||||
enabled = false;
|
||||
return;
|
||||
};
|
||||
framebuffer = @ptrFromInt(fb.address);
|
||||
framebuffer_width = fb.width;
|
||||
framebuffer_height = fb.height;
|
||||
|
||||
load_font();
|
||||
|
||||
|
||||
+5
-2
@@ -57,7 +57,7 @@ fn is_kernel_text(addr: usize) bool {
|
||||
}
|
||||
|
||||
fn walk_stack_frame(base: usize) void {
|
||||
var frame: ?*StackFrame = @ptrFromInt(base);
|
||||
var frame: ?*StackFrame = @ptrFromInt(if (config.arch == .riscv64) base - 16 else base);
|
||||
var depth: usize = 0;
|
||||
while (frame) |f| : (depth += 1) {
|
||||
if (depth > 64) break;
|
||||
@@ -67,7 +67,10 @@ fn walk_stack_frame(base: usize) void {
|
||||
}
|
||||
const sym = get_symbol_name(ret_addr) orelse Symbol{ .name = "???", .offset = 0 };
|
||||
std.log.err(" #{d} {s}+0x{x} - 0x{x}", .{ depth, sym.name, sym.offset, ret_addr });
|
||||
frame = f.prev;
|
||||
frame = if (config.arch == .riscv64 and f.prev != null)
|
||||
@ptrFromInt(@intFromPtr(f.prev.?) - 16)
|
||||
else
|
||||
f.prev;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ const ke = r.ke;
|
||||
|
||||
pub const impl = switch (config.arch) {
|
||||
.amd64 => @import("amd64/impl.zig"),
|
||||
.riscv64 => @import("riscv64/impl.zig"),
|
||||
else => @compileError("unsupported architecture"),
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
const r = @import("root");
|
||||
const ke = r.ke;
|
||||
const rv64 = r.arch;
|
||||
|
||||
pub const tlb_max_pages = 32;
|
||||
|
||||
pub const ThreadContext = extern struct {
|
||||
sp: usize,
|
||||
ra: usize = 0,
|
||||
s: [12]usize = .{0} ** 12,
|
||||
|
||||
pub fn init_with_stack(stack: r.VAddr) @This() {
|
||||
return .{ .sp = stack };
|
||||
}
|
||||
|
||||
pub fn reset(self: *@This(), stack_top: r.VAddr, entry: *const fn (?*anyopaque) void, arg: ?*anyopaque) @This() {
|
||||
self.sp = stack_top & ~@as(usize, 15);
|
||||
self.ra = @intFromPtr(&thread_start);
|
||||
self.s = .{0} ** 12;
|
||||
self.s[1] = @intFromPtr(entry);
|
||||
self.s[2] = @intFromPtr(arg);
|
||||
return self.*;
|
||||
}
|
||||
|
||||
pub fn load(self: *@This()) noreturn {
|
||||
riscv_context_load(self);
|
||||
}
|
||||
};
|
||||
|
||||
extern fn riscv_context_load(ctx: *ThreadContext) callconv(.c) noreturn;
|
||||
extern fn riscv_context_switch(old: *ThreadContext, new: *ThreadContext, lock: *u8, switching: *bool) callconv(.c) void;
|
||||
extern fn riscv_context_switch_cont(old: *ke.Thread.Context, new: *ThreadContext, lock: *u8, switching: *bool) callconv(.c) void;
|
||||
extern fn thread_start() callconv(.c) noreturn;
|
||||
|
||||
export fn riscv_thread_entry(entry_addr: usize, arg_addr: usize) callconv(.c) noreturn {
|
||||
const entry: *const fn (?*anyopaque) void = @ptrFromInt(entry_addr);
|
||||
entry(@ptrFromInt(arg_addr));
|
||||
@panic("Thread entry returned");
|
||||
}
|
||||
|
||||
export fn riscv_free_old_stack(stack: usize) callconv(.c) void {
|
||||
ke.private.thread.stack_cache_free(stack);
|
||||
}
|
||||
|
||||
export fn riscv_run_continuation(func_addr: usize, arg: ?*anyopaque) callconv(.c) noreturn {
|
||||
ke.ipl.lower(.Passive);
|
||||
const func: *const fn (?*anyopaque) void = @ptrFromInt(func_addr);
|
||||
func(arg);
|
||||
@panic("Continuation returned");
|
||||
}
|
||||
|
||||
pub fn switch_normal_to_normal(old: *ke.Thread.Context, new: *ke.Thread.Context) void {
|
||||
const thread: *ke.Thread = @alignCast(@fieldParentPtr("context", old));
|
||||
riscv_context_switch(&old.impl, &new.impl, &thread.lock.inner.locked.raw, &thread.switching.raw);
|
||||
}
|
||||
|
||||
pub fn switch_cont_to_normal(old: *ke.Thread.Context, new: *ke.Thread.Context) void {
|
||||
const thread: *ke.Thread = @alignCast(@fieldParentPtr("context", old));
|
||||
riscv_context_switch_cont(old, &new.impl, &thread.lock.inner.locked.raw, &thread.switching.raw);
|
||||
}
|
||||
|
||||
pub fn call_continuation(ctx: *ke.Thread.Context, continuation: ke.Continuation) noreturn {
|
||||
const sp = ctx.stack_top & ~@as(usize, 15);
|
||||
asm volatile (
|
||||
\\mv sp, %[stack]
|
||||
\\mv a0, %[func]
|
||||
\\mv a1, %[arg]
|
||||
\\tail riscv_run_continuation
|
||||
:
|
||||
: [stack] "r" (sp),
|
||||
[func] "r" (continuation.func),
|
||||
[arg] "r" (continuation.arg),
|
||||
: .{ .memory = true });
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) {
|
||||
return variable;
|
||||
}
|
||||
|
||||
pub inline fn percpu_ptr_other(variable: anytype, id: u32) @TypeOf(variable) {
|
||||
// TODO: start up harts
|
||||
if (id != 0) @panic("secondary harts are not supported");
|
||||
return variable;
|
||||
}
|
||||
|
||||
pub fn early_init() void {
|
||||
_ = disable_interrupts();
|
||||
}
|
||||
|
||||
pub inline fn raise_software_ipl(new: ke.Ipl, old: *ke.Ipl) bool {
|
||||
const current = ke.private.ipl.cpu_ipl.local();
|
||||
old.* = current.*;
|
||||
current.* = new;
|
||||
return new.value() < old.value();
|
||||
}
|
||||
|
||||
pub inline fn lower_software_ipl(new: ke.Ipl, old: *ke.Ipl) bool {
|
||||
const current = ke.private.ipl.cpu_ipl.local();
|
||||
old.* = current.*;
|
||||
current.* = new;
|
||||
return new.value() > old.value();
|
||||
}
|
||||
|
||||
pub fn set_hardware_ipl(level: ke.Ipl) void {
|
||||
if (level.value() > ke.Ipl.Dispatch.value()) {
|
||||
asm volatile ("csrc sie, %[mask]"
|
||||
:
|
||||
: [mask] "r" (@as(usize, 32)),
|
||||
: .{ .memory = true });
|
||||
} else {
|
||||
asm volatile ("csrs sie, %[mask]"
|
||||
:
|
||||
: [mask] "r" (@as(usize, 32)),
|
||||
: .{ .memory = true });
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn disable_interrupts() bool {
|
||||
const old = asm volatile ("csrrc %[old], sstatus, %[mask]"
|
||||
: [old] "=r" (-> usize),
|
||||
: [mask] "r" (@as(usize, 2)),
|
||||
: .{ .memory = true });
|
||||
return old & 2 != 0;
|
||||
}
|
||||
|
||||
pub inline fn enable_interrupts() void {
|
||||
asm volatile ("csrsi sstatus, 2" ::: .{ .memory = true });
|
||||
}
|
||||
|
||||
pub inline fn restore_interrupts(state: bool) void {
|
||||
if (state) enable_interrupts() else _ = disable_interrupts();
|
||||
}
|
||||
|
||||
pub inline fn halt() void {
|
||||
asm volatile ("wfi");
|
||||
}
|
||||
|
||||
pub inline fn flush_full_tlb() void {
|
||||
rv64.sfence_vma();
|
||||
}
|
||||
|
||||
pub inline fn flush_tlb(va: r.VAddr) void {
|
||||
rv64.sfence_vma_page(va);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
.section .text
|
||||
.global riscv_context_switch
|
||||
.type riscv_context_switch, @function
|
||||
riscv_context_switch:
|
||||
sd sp, 0(a0)
|
||||
sd ra, 8(a0)
|
||||
sd s0, 16(a0)
|
||||
sd s1, 24(a0)
|
||||
sd s2, 32(a0)
|
||||
sd s3, 40(a0)
|
||||
sd s4, 48(a0)
|
||||
sd s5, 56(a0)
|
||||
sd s6, 64(a0)
|
||||
sd s7, 72(a0)
|
||||
sd s8, 80(a0)
|
||||
sd s9, 88(a0)
|
||||
sd s10, 96(a0)
|
||||
sd s11, 104(a0)
|
||||
mv a0, a1
|
||||
j riscv_context_resume
|
||||
|
||||
.global riscv_context_load
|
||||
.type riscv_context_load, @function
|
||||
riscv_context_load:
|
||||
mv a2, zero
|
||||
mv a3, zero
|
||||
j riscv_context_resume
|
||||
|
||||
riscv_context_resume:
|
||||
ld sp, 0(a0)
|
||||
ld ra, 8(a0)
|
||||
ld s0, 16(a0)
|
||||
ld s1, 24(a0)
|
||||
ld s2, 32(a0)
|
||||
ld s3, 40(a0)
|
||||
ld s4, 48(a0)
|
||||
ld s5, 56(a0)
|
||||
ld s6, 64(a0)
|
||||
ld s7, 72(a0)
|
||||
ld s8, 80(a0)
|
||||
ld s9, 88(a0)
|
||||
ld s10, 96(a0)
|
||||
ld s11, 104(a0)
|
||||
beqz a3, 1f
|
||||
sb zero, 0(a3)
|
||||
1:
|
||||
beqz a2, 2f
|
||||
sb zero, 0(a2)
|
||||
2:
|
||||
ret
|
||||
|
||||
.global riscv_context_switch_cont
|
||||
.type riscv_context_switch_cont, @function
|
||||
riscv_context_switch_cont:
|
||||
mv s2, a2
|
||||
mv s3, a3
|
||||
mv s4, a1
|
||||
ld a0, 112(a0)
|
||||
ld sp, 0(s4)
|
||||
call riscv_free_old_stack
|
||||
mv a0, s4
|
||||
mv a2, s2
|
||||
mv a3, s3
|
||||
j riscv_context_resume
|
||||
|
||||
.global thread_start
|
||||
.type thread_start, @function
|
||||
thread_start:
|
||||
mv a0, s1
|
||||
mv a1, s2
|
||||
tail riscv_thread_entry
|
||||
@@ -0,0 +1,122 @@
|
||||
const riscv64 = @import("arch");
|
||||
const r = @import("root");
|
||||
const pmap = @import("../pmap.zig");
|
||||
|
||||
pub const levels = [_]pmap.PMapLevel{
|
||||
.{ .shift = 12, .mask = 0x1ff, .leaf = true }, // 4K
|
||||
.{ .shift = 21, .mask = 0x1ff, .leaf = true }, // 2M
|
||||
.{ .shift = 30, .mask = 0x1ff, .leaf = true }, // 1G
|
||||
.{ .shift = 39, .mask = 0x1ff, .leaf = false }, // root
|
||||
};
|
||||
|
||||
pub const virtual_bits: usize = 48;
|
||||
|
||||
pub const Pte = packed struct(u64) {
|
||||
present: bool,
|
||||
read: bool,
|
||||
write: bool,
|
||||
exec: bool,
|
||||
user: bool,
|
||||
global: bool,
|
||||
access: bool,
|
||||
dirty: bool,
|
||||
rsw: u2,
|
||||
addr: u44,
|
||||
reserved: u7,
|
||||
pbmt: u2,
|
||||
n: bool,
|
||||
|
||||
pub fn address(self: Pte) usize {
|
||||
return @as(usize, self.addr) << 12;
|
||||
}
|
||||
|
||||
pub fn is_present(self: Pte) bool {
|
||||
return self.present;
|
||||
}
|
||||
|
||||
pub fn load(table: *Pte) Pte {
|
||||
return @atomicLoad(Pte, table, .monotonic);
|
||||
}
|
||||
|
||||
pub fn zero() Pte {
|
||||
return Pte{
|
||||
.present = false,
|
||||
.read = false,
|
||||
.write = false,
|
||||
.exec = false,
|
||||
.user = false,
|
||||
.global = false,
|
||||
.access = false,
|
||||
.dirty = false,
|
||||
.rsw = 0,
|
||||
.addr = 0,
|
||||
.reserved = 0,
|
||||
.pbmt = 0,
|
||||
.n = false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn make_table_pte(pa: usize) Pte {
|
||||
return Pte{
|
||||
.present = true,
|
||||
.read = false,
|
||||
.write = false,
|
||||
.exec = false,
|
||||
.user = false,
|
||||
.global = false,
|
||||
.access = false,
|
||||
.dirty = false,
|
||||
.rsw = 0,
|
||||
.addr = @truncate(pa >> 12),
|
||||
.reserved = 0,
|
||||
.pbmt = 0,
|
||||
.n = false,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn make_leaf_pte(pa: usize, flags: r.mem.MapFlags, level: usize) Pte {
|
||||
_ = level;
|
||||
return Pte{
|
||||
.present = true,
|
||||
.read = flags.read or flags.write,
|
||||
.write = flags.write,
|
||||
.exec = flags.execute,
|
||||
.user = flags.user,
|
||||
.global = flags.global,
|
||||
.access = true,
|
||||
.dirty = flags.write,
|
||||
.rsw = 0,
|
||||
.addr = @truncate(pa >> 12),
|
||||
.reserved = 0,
|
||||
.pbmt = 0,
|
||||
.n = false,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn activate(root_pa: usize) void {
|
||||
riscv64.write_csr("satp", (@as(u64, 9) << 60) | (root_pa >> 12));
|
||||
riscv64.sfence_vma();
|
||||
}
|
||||
|
||||
pub inline fn flush(va: usize) void {
|
||||
riscv64.sfence_vma_page(va);
|
||||
}
|
||||
|
||||
pub fn is_leaf_level_enabled(level: usize) bool {
|
||||
if (!levels[level].leaf) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn debug_write(c: u8) void {
|
||||
// OpenSBI debug ext
|
||||
asm volatile ("ecall"
|
||||
:
|
||||
: [char] "{a0}" (@as(usize, c)),
|
||||
[extension] "{a7}" (@as(usize, 1)),
|
||||
: .{ .memory = true });
|
||||
}
|
||||
|
||||
pub fn init() void {
|
||||
r.loader_info.arch_info = .{};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.section .text
|
||||
.global jump_to_kernel
|
||||
.type jump_to_kernel, @function
|
||||
jump_to_kernel:
|
||||
mv t0, a1
|
||||
mv sp, a2
|
||||
jr t0
|
||||
@@ -82,6 +82,7 @@ pub const ImplSchema = struct {
|
||||
|
||||
pub const arch = switch (config.arch) {
|
||||
.amd64 => @import("amd64/impl.zig"),
|
||||
.riscv64 => @import("riscv64/impl.zig"),
|
||||
else => @compileError("unsupported architecture"),
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ pub const PMapLevel = struct {
|
||||
|
||||
pub const impl = switch (config.arch) {
|
||||
.amd64 => @import("amd64/impl.zig"),
|
||||
.riscv64 => @import("riscv64/impl.zig"),
|
||||
else => @compileError("unsupported architecture"),
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
const rv64 = @import("arch");
|
||||
const r = @import("root");
|
||||
const mm = r.mm;
|
||||
const mmp = mm.private;
|
||||
|
||||
pub const hhdm_minimum_max_address = r.gib(4);
|
||||
|
||||
pub fn phys_to_virt(addr: r.PAddr) r.VAddr {
|
||||
return addr + 0xffff800000000000;
|
||||
}
|
||||
|
||||
pub fn virt_to_phys(vaddr: r.VAddr) usize {
|
||||
return vaddr - 0xffff800000000000;
|
||||
}
|
||||
|
||||
pub const hhdm_base = 0xffff800000000000;
|
||||
pub const kernel_heap_base = 0xffffc00000000000;
|
||||
pub const pfndb_base = 0xffffd00000000000;
|
||||
|
||||
pub const levels = [_]mmp.PMapLevel{
|
||||
.{ .shift = 12, .mask = 0x1ff, .leaf = true }, // 4K
|
||||
.{ .shift = 21, .mask = 0x1ff, .leaf = true }, // 2M
|
||||
.{ .shift = 30, .mask = 0x1ff, .leaf = true }, // 1G
|
||||
.{ .shift = 39, .mask = 0x1ff, .leaf = false }, // root
|
||||
};
|
||||
|
||||
pub const Pte = packed struct(u64) {
|
||||
present: bool,
|
||||
read: bool,
|
||||
write: bool,
|
||||
exec: bool,
|
||||
user: bool,
|
||||
global: bool,
|
||||
access: bool,
|
||||
dirty: bool,
|
||||
rsw: u2,
|
||||
addr: u44,
|
||||
reserved: u7,
|
||||
pbmt: u2,
|
||||
n: bool,
|
||||
|
||||
pub fn address(self: Pte) r.PAddr {
|
||||
return @as(r.PAddr, self.addr) << 12;
|
||||
}
|
||||
|
||||
pub fn is_present(self: Pte) bool {
|
||||
return self.present;
|
||||
}
|
||||
|
||||
pub fn load(table: *Pte) Pte {
|
||||
return @atomicLoad(Pte, table, .monotonic);
|
||||
}
|
||||
|
||||
pub fn zero() Pte {
|
||||
return Pte{
|
||||
.present = false,
|
||||
.read = false,
|
||||
.write = false,
|
||||
.exec = false,
|
||||
.user = false,
|
||||
.global = false,
|
||||
.access = false,
|
||||
.dirty = false,
|
||||
.rsw = 0,
|
||||
.addr = 0,
|
||||
.reserved = 0,
|
||||
.pbmt = 0,
|
||||
.n = false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn make_table_pte(pa: r.PAddr) Pte {
|
||||
return Pte{
|
||||
.present = true,
|
||||
.read = false,
|
||||
.write = false,
|
||||
.exec = false,
|
||||
.user = false,
|
||||
.global = false,
|
||||
.access = false,
|
||||
.dirty = false,
|
||||
.rsw = 0,
|
||||
.addr = @truncate(pa >> 12),
|
||||
.reserved = 0,
|
||||
.pbmt = 0,
|
||||
.n = false,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn make_leaf_pte(pa: r.PAddr, flags: mm.MapFlags, level: usize) Pte {
|
||||
_ = level;
|
||||
return Pte{
|
||||
.present = true,
|
||||
.read = flags.read or flags.write,
|
||||
.write = flags.write,
|
||||
.exec = flags.execute,
|
||||
.user = flags.user,
|
||||
.global = flags.global,
|
||||
.access = true,
|
||||
.dirty = flags.write,
|
||||
.rsw = 0,
|
||||
.addr = @truncate(pa >> 12),
|
||||
.reserved = 0,
|
||||
.pbmt = 0,
|
||||
.n = false,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn activate(root_pa: r.PAddr) void {
|
||||
rv64.write_csr("satp", (@as(u64, 9) << 60) | (root_pa >> 12));
|
||||
rv64.sfence_vma();
|
||||
}
|
||||
|
||||
pub fn is_leaf_level_enabled(level: usize) bool {
|
||||
if (!levels[level].leaf) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn init_kernel() void {
|
||||
mmp.kernel_space.pmap.root_pa = (rv64.read_csr("satp") & ((@as(u64, 1) << 44) - 1)) << 12;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
const r = @import("root");
|
||||
const rv64 = r.arch;
|
||||
const ke = r.ke;
|
||||
const std = @import("std");
|
||||
extern fn riscv_trap() callconv(.c) void;
|
||||
|
||||
// for QEMU virt. for real HW this needs to be parsed from the DTB.
|
||||
const timebase_hz = 10_000_000;
|
||||
var time_counter: ke.TimeCounter = .{
|
||||
.read_count = read_time,
|
||||
.frequency = timebase_hz,
|
||||
.name = "riscv time",
|
||||
.quality = 100,
|
||||
.mask = std.math.maxInt(u64),
|
||||
.p = 0,
|
||||
.n = 0,
|
||||
};
|
||||
|
||||
fn read_time() u64 {
|
||||
return rv64.read_csr("time");
|
||||
}
|
||||
|
||||
pub const name: []const u8 = "QEMU virt";
|
||||
|
||||
pub fn early_init() void {
|
||||
rv64.write_csr("stvec", @intFromPtr(&riscv_trap));
|
||||
}
|
||||
|
||||
pub fn late_init() void {
|
||||
ke.time.register_source(&time_counter);
|
||||
asm volatile ("csrs sie, %[mask]"
|
||||
:
|
||||
: [mask] "r" (@as(usize, 32)),
|
||||
: .{ .memory = true });
|
||||
}
|
||||
|
||||
export fn riscv_trap_handler(cause: usize, pc: usize) callconv(.c) void {
|
||||
if (cause == ((@as(usize, 1) << 63) | 5)) {
|
||||
const old = ke.ipl.set_hardware(.Device);
|
||||
set_timer(std.math.maxInt(u64));
|
||||
ke.private.timer.clock();
|
||||
_ = ke.ipl.set_hardware(old);
|
||||
if (old == .Passive and ke.private.ipl.is_softint_pending(.Dispatch)) {
|
||||
ke.private.dpc.dispatch();
|
||||
}
|
||||
return;
|
||||
}
|
||||
std.debug.panic("trap: cause = 0x{x}, pc = 0x{x}, tval = 0x{x}", .{ cause, pc, rv64.read_csr("stval") });
|
||||
}
|
||||
|
||||
fn set_timer(deadline: u64) void {
|
||||
asm volatile ("ecall"
|
||||
:
|
||||
: [deadline] "{a0}" (deadline),
|
||||
[function] "{a6}" (@as(usize, 0)),
|
||||
[extension] "{a7}" (@as(usize, 0x54494d45)),
|
||||
: .{ .memory = true });
|
||||
}
|
||||
|
||||
pub fn debug_write(c: u8) void {
|
||||
// SBI legacy console putchar.
|
||||
asm volatile ("ecall"
|
||||
:
|
||||
: [char] "{a0}" (@as(usize, c)),
|
||||
[extension] "{a7}" (@as(usize, 1)),
|
||||
: .{ .memory = true });
|
||||
}
|
||||
|
||||
pub fn debug_read() u8 {
|
||||
while (true) {
|
||||
const c = asm volatile ("ecall"
|
||||
: [char] "={a0}" (-> usize),
|
||||
: [extension] "{a7}" (@as(usize, 2)),
|
||||
: .{ .memory = true });
|
||||
if (c != std.math.maxInt(usize)) return @truncate(c);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_ipi(_: u32) void {}
|
||||
pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||
const ticks = @max(1, @as(u64, @intCast((@as(u128, ns.value) * timebase_hz) / std.time.ns_per_s)));
|
||||
set_timer(read_time() +% ticks);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
.section .text
|
||||
.global riscv_trap
|
||||
.type riscv_trap, @function
|
||||
riscv_trap:
|
||||
addi sp, sp, -272
|
||||
sd ra, 0(sp)
|
||||
sd gp, 8(sp)
|
||||
sd tp, 16(sp)
|
||||
sd t0, 24(sp)
|
||||
sd t1, 32(sp)
|
||||
sd t2, 40(sp)
|
||||
sd s0, 48(sp)
|
||||
sd s1, 56(sp)
|
||||
sd a0, 64(sp)
|
||||
sd a1, 72(sp)
|
||||
sd a2, 80(sp)
|
||||
sd a3, 88(sp)
|
||||
sd a4, 96(sp)
|
||||
sd a5, 104(sp)
|
||||
sd a6, 112(sp)
|
||||
sd a7, 120(sp)
|
||||
sd s2, 128(sp)
|
||||
sd s3, 136(sp)
|
||||
sd s4, 144(sp)
|
||||
sd s5, 152(sp)
|
||||
sd s6, 160(sp)
|
||||
sd s7, 168(sp)
|
||||
sd s8, 176(sp)
|
||||
sd s9, 184(sp)
|
||||
sd s10, 192(sp)
|
||||
sd s11, 200(sp)
|
||||
sd t3, 208(sp)
|
||||
sd t4, 216(sp)
|
||||
sd t5, 224(sp)
|
||||
sd t6, 232(sp)
|
||||
csrr t0, sepc
|
||||
sd t0, 240(sp)
|
||||
csrr a0, scause
|
||||
mv a1, t0
|
||||
call riscv_trap_handler
|
||||
ld t0, 240(sp)
|
||||
csrw sepc, t0
|
||||
ld ra, 0(sp)
|
||||
ld gp, 8(sp)
|
||||
ld tp, 16(sp)
|
||||
ld t0, 24(sp)
|
||||
ld t1, 32(sp)
|
||||
ld t2, 40(sp)
|
||||
ld s0, 48(sp)
|
||||
ld s1, 56(sp)
|
||||
ld a0, 64(sp)
|
||||
ld a1, 72(sp)
|
||||
ld a2, 80(sp)
|
||||
ld a3, 88(sp)
|
||||
ld a4, 96(sp)
|
||||
ld a5, 104(sp)
|
||||
ld a6, 112(sp)
|
||||
ld a7, 120(sp)
|
||||
ld s2, 128(sp)
|
||||
ld s3, 136(sp)
|
||||
ld s4, 144(sp)
|
||||
ld s5, 152(sp)
|
||||
ld s6, 160(sp)
|
||||
ld s7, 168(sp)
|
||||
ld s8, 176(sp)
|
||||
ld s9, 184(sp)
|
||||
ld s10, 192(sp)
|
||||
ld s11, 200(sp)
|
||||
ld t3, 208(sp)
|
||||
ld t4, 216(sp)
|
||||
ld t5, 224(sp)
|
||||
ld t6, 232(sp)
|
||||
addi sp, sp, 272
|
||||
sret
|
||||
@@ -7,6 +7,7 @@ const rtl = @import("rtl");
|
||||
|
||||
pub const impl = switch (config.arch) {
|
||||
.amd64 => @import("pc/impl.zig"),
|
||||
.riscv64 => @import("riscv64/impl.zig"),
|
||||
else => @compileError("unsupported architecture"),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user