181 lines
4.7 KiB
Zig
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);
|
|
}
|
|
}
|
|
}
|