Files
zing/src/arch/riscv64/vmm.zig
T

181 lines
4.7 KiB
Zig

const sync = @import("../../sync.zig");
const regs = @import("regs.zig");
const mem = @import("../../mem.zig");
const arch = @import("../../kernel.zig").arch;
const PhysicalAddress = mem.PhysicalAddress;
pub const KERNEL_VIRTUAL_BASE: usize = 0xFFFFFFF000000000;
pub const KERNEL_VIRTUAL_L1I: usize = (KERNEL_VIRTUAL_BASE >> L1.SHIFT) & 511;
// 16 GiB
const EARLY_MAPPING_SIZE: usize = 16;
pub const L1 = mem.TranslationLevel(30);
pub const L2 = mem.TranslationLevel(21);
pub const L3 = mem.TranslationLevel(12);
pub const RawEntry = packed struct(u64) {
// 0: Valid
v: bool = false,
// 1 : Read
r: bool = false,
// 2: Write
w: bool = false,
// 3: Execute
x: bool = false,
// 4: U-mode access
u: bool = false,
// 5: Global bit
g: bool = false,
// 6: Access bit
a: bool = false,
// 7: dirty bit
d: bool = false,
// 8..10 Unused bits
_pad0: u2 = 0,
// 10..49: Address
address: u39 = 0,
// 49..64: Unused bits
_pad1: u15 = 0,
pub fn make_union(self: @This(), other: @This()) @This() {
const lhs = @as(u64, @bitCast(self));
const rhs = @as(u64, @bitCast(other));
return @as(@This(), @bitCast(lhs | rhs));
}
pub fn clear(self: *@This(), mask: @This()) void {
const lhs = @as(*u64, @bitCast(self));
const rhs = @as(u64, @bitCast(mask));
lhs.* &= ~rhs;
}
};
pub fn TableEntry(comptime Level: type) type {
_ = Level;
return struct {
raw: RawEntry,
pub const INVALID: @This() = .{ .raw = .{} };
pub fn address(self: @This()) PhysicalAddress {
if (self.raw.v) {
return .{ .raw = self.raw.address << 12 };
} else {
return PhysicalAddress.NULL;
}
}
pub fn bits(self: @This()) u64 {
return @as(u64, @bitCast(self.raw));
}
pub fn page(addr: PhysicalAddress, flags: RawEntry) @This() {
return .{
.raw = flags.make_union(.{
.address = @as(u39, @intCast(addr.raw >> 12)),
.r = true,
.v = true,
.d = true,
.a = true,
}),
};
}
pub fn table(addr: PhysicalAddress, flags: RawEntry) @This() {
flags.clear(.{ .r = true, .w = true, .x = true });
return .{ .raw = flags.make_union(.{
.address = @as(u39, @intCast(addr.raw >> 12)),
.v = true,
}) };
}
};
}
pub fn Table(comptime Level: type) type {
return struct {
pub const Entry = TableEntry(Level);
entries: [512]Entry align(4096),
pub fn empty() @This() {
return .{ .entries = [_]Entry{.INVALID} ** 512 };
}
pub inline fn entry(self: *@This(), index: usize) *Entry {
return &self.entries[index];
}
};
}
var g_fixed = Table(L1).empty();
var g_fixed_lock: sync.Spinlock = .{};
pub fn virtualize_range() usize {
return EARLY_MAPPING_SIZE * L1.SIZE;
}
pub fn unmap_early() void {
// Make lower half mappings non-executable
const guard = g_fixed_lock.lock_irqsave();
defer guard.release();
for (0..EARLY_MAPPING_SIZE) |i| {
g_fixed.entry(i).* = .page(
.{ .raw = L1.address(i) },
.{ .r = true, .w = true },
);
}
}
pub fn map_early(real_address: usize) void {
const real_l1 = L1.index(real_address);
// Identity map first 16GiB of memory
for (0..EARLY_MAPPING_SIZE) |i| {
g_fixed.entry(i).* = .page(
.{ .raw = L1.address(i) },
.{ .r = true, .w = true, .x = true },
);
}
// Map 1GiB at KERNEL_VIRTUAL_BASE -> physical 1GiB where the kernel is loaded
g_fixed.entry(KERNEL_VIRTUAL_L1I).* = .page(
.{ .raw = L1.address(real_l1) },
.{ .r = true, .w = true, .x = true },
);
const address = @as(usize, @intFromPtr(&g_fixed));
regs.SATP.write(.{ .PPN = @intCast(address >> 12), .MODE = .sv39 });
}
pub inline fn flush_vma(page: usize) void {
asm volatile ("sfence.vma %[page], zero"
:
: [page] "r" (page),
: "memory"
);
}
pub inline fn flush_vma_asid(page: usize, asid: usize) void {
asm volatile ("sfence.vma %[page], %[asid]"
:
: [page] "r" (page),
[asid] "r" (asid),
);
}
pub fn flush_vma_range(start: usize, end: usize, asid: ?usize) void {
var i = L3.align_down(start);
const rend = L3.align_up(end);
if (asid) |a| {
while (i < rend) : (i += L3.SIZE) {
flush_vma_asid(i, a);
}
} else {
while (i < rend) : (i += L3.SIZE) {
flush_vma(i);
}
}
}