Skip to content

Commit

Permalink
pass-through RSDP/XSDT
Browse files Browse the repository at this point in the history
Signed-off-by: smallkirby <[email protected]>
  • Loading branch information
smallkirby committed Sep 7, 2024
1 parent 64a1f1a commit d558574
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 15 deletions.
21 changes: 21 additions & 0 deletions surtr/boot.zig
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,26 @@ pub fn main() uefi.Status {
}
log.info("Loaded guest kernel image @ 0x{X:0>16} ~ 0x{X:0>16}", .{ guest_start, guest_start + guest_size });

// Find RSDP.
const acpi_table_guid = uefi.Guid{
.time_low = 0x8868E871,
.time_mid = 0xE4F1,
.time_high_and_version = 0x11D3,
.clock_seq_high_and_reserved = 0xBC,
.clock_seq_low = 0x22,
.node = [_]u8{ 0x0, 0x80, 0xC7, 0x3C, 0x88, 0x81 },
};
const acpi_table = for (0..uefi.system_table.number_of_table_entries) |i| {
const guid = uefi.system_table.configuration_table[i].vendor_guid;
if (uefi.Guid.eql(acpi_table_guid, guid)) {
break uefi.system_table.configuration_table[i].vendor_table;
}
} else {
log.err("Failed to find ACPI table.", .{});
return .LoadError;
};
log.info("ACPI table @ 0x{X:0>16}", .{@intFromPtr(acpi_table)});

// Clean up memory.
status = boot_service.freePool(header_buffer);
if (status != .Success) {
Expand Down Expand Up @@ -329,6 +349,7 @@ pub fn main() uefi.Status {
.guest_image = @ptrFromInt(guest_start),
.guest_size = guest_size,
},
.acpi_table = acpi_table,
};
kernel_entry(boot_info);

Expand Down
1 change: 1 addition & 0 deletions surtr/defs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub const BootInfo = extern struct {
magic: usize = surtr_magic,
memory_map: MemoryMap,
guest_info: GuestInfo,
acpi_table: *anyopaque,
};

/// Memory map provided by UEFI.
Expand Down
210 changes: 210 additions & 0 deletions ymir/arch/x86/acpi.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
const std = @import("std");
const log = std.log.scoped(.acpi);

const ymir = @import("ymir");
const mem = ymir.mem;

const AcpiError = error{
InvalidSignature,
InvalidRevision,
InvalidChecksum,
InvalidExtendedChecksum,
};

/// Pointer to FADT.
/// You MUST call `init()` before using this pointer.
var fadt: ?*Fadt = null;
/// RSDP.
var rsdp_addr: ?mem.Phys = null;
/// XSDT
var xsdt_addr: ?mem.Phys = null;

pub fn getRsdpPhys() ?mem.Phys {
return rsdp_addr;
}

pub fn getXsdtPhys() ?mem.Phys {
return xsdt_addr;
}

/// Initialize ACPI.
pub fn init(_rsdp: *align(1) Rsdp) void {
// Copy RSDP to the stack, because the original address of RSDP might not be correctly aligned.
var rsdp: Rsdp = undefined;
@memcpy(std.mem.asBytes(&rsdp), std.mem.asBytes(_rsdp));

rsdp.valid() catch |e| switch (e) {
AcpiError.InvalidSignature => @panic("Invalid RSDP signature."),
AcpiError.InvalidRevision => @panic("Invalid RSDP revision."),
AcpiError.InvalidChecksum => @panic("Invalid RSDP checksum."),
AcpiError.InvalidExtendedChecksum => @panic("Invalid RSDP extended checksum."),
};

const xsdt: *Xsdt = @ptrFromInt(mem.phys2virt(rsdp.xsdt_address));
xsdt.header.valid("XSDT") catch |e| switch (e) {
AcpiError.InvalidSignature => @panic("Invalid XSDT signature."),
AcpiError.InvalidChecksum => @panic("Invalid XSDT checksum."),
else => unreachable,
};

var ix: usize = 0;
fadt = while (ix < xsdt.size()) : (ix += 1) {
const ent = xsdt.get(ix);
if (ent.valid("FACP")) |_| {
// The signature of FADT is "FACP".
break @as(*Fadt, @ptrCast(ent));
} else |_| {}
} else @panic("FADT not found.");

rsdp_addr = mem.virt2phys(_rsdp);
xsdt_addr = rsdp.xsdt_address;
log.debug("RSDP @ 0x{X:0>16}, XSDT @ 0x{X:0>16}", .{ rsdp_addr.?, xsdt_addr.? });
}

/// Calculate the checksum of RSDP structure.
/// `checksum` and `extended_checksum` is set so that the sum of all bytes is 0.
fn checksum(data: []u8) u8 {
var sum: u8 = 0;
for (data) |byte| {
sum +%= byte;
}
return sum;
}

/// ACPI structure that contains information about ACPI fixed registers.
/// Most of fields are omitted here.
const Fadt = extern struct {
header: DescriptorHeader,

_reserved1: [76 - @sizeOf(DescriptorHeader)]u8,
pm_tmr_blk: u32,
_reserved2: [32]u8,
flags: u32,
_reserved3: [160]u8,

comptime {
if (@sizeOf(Fadt) != 276) {
@compileError("Invalid size of FADT.");
}
}
};

/// XSDT (Extended System Descriptor Table) structure of ACPI v2.0+..
/// XSDT starts with a header and followed by a list of 64-bit pointers to other tables.
const Xsdt = extern struct {
header: DescriptorHeader,
_pointer_entries: void,

/// Get the table entry at the specified index.
pub fn get(self: *Xsdt, index: usize) *DescriptorHeader {
// NOTE: 64bit pointer entries are 4 bytes aligned, not 8 bytes.
// Zig does not allow incorrect alignment cast, for example:
// `const ents: [*]*const DescriptorHeader = @alignCast(@ptrCast(&self._pointer_entries))`
// Therefore, we have to calculate the address from two 32bit integers.
const ents_start = @intFromPtr(&self._pointer_entries);
const first: *u32 = @ptrFromInt(ents_start + index * @sizeOf(u64));
const second: *u32 = @ptrFromInt(ents_start + index * @sizeOf(u64) + @sizeOf(u32));
return @ptrFromInt(mem.phys2virt((@as(u64, second.*) << 32) + first.*));
}

/// Number of table entries.
pub fn size(self: *Xsdt) usize {
return (self.header.length - @sizeOf(DescriptorHeader)) / @sizeOf(u64);
}

comptime {
if (@sizeOf(Xsdt) != 36) {
@compileError("Invalid size of XSDT.");
}
}
};

/// Descriptor header of ACPI structures.
const DescriptorHeader = extern struct {
signature: [4]u8,
length: u32,
revision: u8,
checksum: u8,
oem_id: [6]u8,
oem_table_id: [8]u8,
oem_revision: u32,
creator_id: u32,
creator_revision: u32,

_end_marker: void,

pub fn valid(self: *DescriptorHeader, signature: []const u8) AcpiError!void {
if (!std.mem.eql(u8, signature, &self.signature)) {
return AcpiError.InvalidSignature;
}
const ents: [*]u8 = @ptrCast(self);
if (checksum(ents[0..self.length]) != 0) {
return AcpiError.InvalidChecksum;
}
}

comptime {
if (@sizeOf(DescriptorHeader) != 36) {
@compileError("Invalid size of DescriptorHeader.");
}
}
};

/// RSDP (Root System Description Pointer) structure of ACPI v2.0+.
/// RSDP is used to find XSDT (Extended System Descriptor Table).
pub const Rsdp = extern struct {
const size_first_byte = @offsetOf(Rsdp, "length");
const size_extended_byte = @offsetOf(Rsdp, "_end_marker");

/// Signature.
/// It should be "RSD PTR ".
signature: [8]u8,
/// Checksum for the first 20 bytes.
checksum: u8,
/// OEM ID.
oem_id: [6]u8,
/// Revision.
/// 0 for ACPI 1.0, 2 for ACPI 2.0.
revision: u8,
/// RSDT physical address.
rsdt_address: u32,

/// Total length of RSDP.
length: u32,
/// XSDT (Extended System Descriptor Table) physical address.
xsdt_address: u64,
/// Checksum for entire RSDP.
extended_checksum: u8,
/// Reserved.
_reserved: [3]u8,

_end_marker: void,

comptime {
if (size_extended_byte != 36) {
@compileError("Invalid size of Rsdp.");
}
}

fn valid(self: *Rsdp) AcpiError!void {
if (!std.mem.eql(u8, &self.signature, "RSD PTR ")) {
return AcpiError.InvalidSignature;
}
if (self.revision != 2) {
return AcpiError.InvalidRevision;
}
if (checksum(std.mem.asBytes(self)[0..size_first_byte]) != 0) {
return AcpiError.InvalidChecksum;
}
if (checksum(std.mem.asBytes(self)[0..size_extended_byte]) != 0) {
return AcpiError.InvalidExtendedChecksum;
}
}
};

test "Size of structures" {
std.testing.refAllDecls(@This());
try std.testing.expectEqual(36, @sizeOf(DescriptorHeader));
try std.testing.expectEqual(36, @sizeOf(Xsdt));
try std.testing.expectEqual(276, @sizeOf(Fadt));
}
9 changes: 9 additions & 0 deletions ymir/arch/x86/arch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const std = @import("std");
const log = std.log.scoped(.arch);

const ymir = @import("ymir");
const mem = ymir.mem;

pub const gdt = @import("gdt.zig");
pub const intr = @import("interrupt.zig");
pub const page = @import("page.zig");
Expand All @@ -11,6 +14,7 @@ pub const serial = @import("serial.zig");
pub const apic = @import("apic.zig");

const cpuid = @import("cpuid.zig");
const acpi = @import("acpi.zig");
const am = @import("asm.zig");

/// Pause a CPU for a short period of time.
Expand Down Expand Up @@ -102,6 +106,11 @@ pub fn enableXstateFeature() void {
am.xsetbv(0, max_features); // XCR0 enabled mask
}

/// Initialize ACPI.
pub fn initAcpi(rsdp: *anyopaque) void {
acpi.init(@ptrFromInt(mem.phys2virt(rsdp)));
}

test {
const testing = @import("std").testing;
testing.refAllDeclsRecursive(@This());
Expand Down
19 changes: 17 additions & 2 deletions ymir/arch/x86/vmx.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const linux = ymir.linux;
const BootParams = linux.boot.BootParams;

const am = @import("asm.zig");
const acpi = @import("acpi.zig");
const apic = @import("apic.zig");
const gdt = @import("gdt.zig");
const serial = @import("serial.zig");
Expand Down Expand Up @@ -183,6 +184,22 @@ pub const Vcpu = struct {
}
}

/// Maps 4KiB page to EPT of this vCPU.
fn map4k(self: *Self, host: Phys, guest: Phys, allocator: Allocator) VmxError!void {
const lv4tbl = self.eptp.getLv4();
try ept.map4k(guest, host, lv4tbl, allocator);
}

/// Maps Ymir's physical pages where RSDP and XSDT are located to pass-through.
pub fn mapRsdpRegion(self: *Self, guest: Phys, allocator: Allocator) void {
// Map RSDP region.
const rsdp_page = @as(u64, acpi.getRsdpPhys().?) & ~mem.page_mask_4k;
self.map4k(rsdp_page, guest, allocator) catch @panic("Failed to map RDSP region.");
// Map XSDT region.
const xsdt_page = @as(u64, acpi.getXsdtPhys().?) & ~mem.page_mask_4k;
self.map4k(xsdt_page, xsdt_page, allocator) catch @panic("Failed to map XSDT region.");
}

// Enter VMX non-root operation.
fn vmentry(self: *Self) VmxError!void {
const success = self.asmVmEntry() == 0;
Expand All @@ -201,8 +218,6 @@ pub const Vcpu = struct {

/// Handle the VM-exit.
fn handleExit(self: *Self, exit_info: ExitInformation) VmxError!void {
log.debug("VM-exit: {?}", .{exit_info.basic_reason});

switch (exit_info.basic_reason) {
.invpcid => {
pg.invalidateEpt(self, .single_context);
Expand Down
Loading

0 comments on commit d558574

Please sign in to comment.