diff --git a/surtr/arch/x86/arch.zig b/surtr/arch/x86/arch.zig index f3e39e3..9c972d2 100644 --- a/surtr/arch/x86/arch.zig +++ b/surtr/arch/x86/arch.zig @@ -1 +1,7 @@ pub const page = @import("page.zig"); + +/// Enable NX-bit. +pub fn enableNxBit() void { + const efer_reg: *volatile u64 = @ptrFromInt(0xC000_0080); + efer_reg.* = efer_reg.* | (1 << 11); +} diff --git a/surtr/arch/x86/page.zig b/surtr/arch/x86/page.zig index 450d33f..73ab92b 100644 --- a/surtr/arch/x86/page.zig +++ b/surtr/arch/x86/page.zig @@ -1,6 +1,7 @@ const std = @import("std"); const log = std.log.scoped(.archp); const uefi = std.os.uefi; +const elf = std.elf; const BootServices = uefi.tables.BootServices; const am = @import("asm.zig"); @@ -58,8 +59,16 @@ pub const Virt = u64; const phys_virt_offset = 0x0; pub const PageAttribute = enum { - ReadOnly, - ReadWrite, + /// RO + read_only, + /// RW + read_write, + /// RX + executable, + + pub fn fromFlags(flags: u32) PageAttribute { + return if (flags & elf.PF_X != 0) .executable else if (flags & elf.PF_W != 0) .read_write else .read_only; + } }; /// Make level-4 page table writable. @@ -104,9 +113,10 @@ pub fn changeMap(virt: Virt, attr: PageAttribute) PageError!void { if (!lv1_entry.present) return PageError.NotPresent; lv1_entry.rw = switch (attr) { - PageAttribute.ReadOnly => false, - PageAttribute.ReadWrite => true, + .read_only, .executable => false, + .read_write => true, }; + lv1_entry.xd = attr != .executable; flushTlbSingle(virt); } @@ -170,12 +180,13 @@ pub fn mapTo(virt: Virt, phys: Phys, attr: PageAttribute, bs: *BootServices) Pag lv1_entry.* = Lv1PageTableEntry{ .present = true, .rw = switch (attr) { - PageAttribute.ReadOnly => false, - PageAttribute.ReadWrite => true, + .read_only, .executable => false, + .read_write => true, }, .us = false, .pat = false, .phys_addr = @truncate(phys >> page_shift), + .xd = attr != .executable, }; } @@ -374,7 +385,10 @@ const Lv2PageTableEntry = packed struct(u64) { restart: bool = false, /// When the entry maps a 2MiB page, physical address of the 2MiB page. /// When the entry references a Page Table, 4KB aligned address of the Page Table. - phys_pt: u52, + phys_pt: u51, + /// If the entry maps a 2MiB page and the bit is set, the 2MiB page is not executable. + /// If the entry references a Page Table, the bit must be unset. + xd: bool = false, /// Get a new PDT entry with the present bit set to false. pub fn new_nopresent() Lv2PageTableEntry { @@ -449,7 +463,10 @@ const Lv1PageTableEntry = packed struct(u64) { /// Ignored except for HLAT paging. restart: bool = false, /// Physical address of the 4KiB page. - phys_addr: u52, + phys_addr: u51, + /// If the entry maps a 4KiB page and the bit is set, the 4KiB page is not executable. + /// If the entry references a Page Table, the bit must be unset. + xd: bool = false, /// Get the physical address pointed by this entry. pub inline fn address(self: Lv1PageTableEntry) Phys { diff --git a/surtr/boot.zig b/surtr/boot.zig index 044a424..b072804 100644 --- a/surtr/boot.zig +++ b/surtr/boot.zig @@ -130,7 +130,7 @@ pub fn main() uefi.Status { } log.info("Allocated memory for kernel image @ 0x{X:0>16} ~ 0x{X:0>16}", .{ kernel_start, kernel_start + pages_4kib * 4096 }); - // Map memory for kernel image + // Map memory for kernel image. arch.page.setLv4PageTableWritable(boot_service) catch |err| { log.err("Failed to set page table writable: {?}", .{err}); return .LoadError; @@ -141,7 +141,7 @@ pub fn main() uefi.Status { arch.page.mapTo( kernel_start_virt + 4096 * i, kernel_start + 4096 * i, - .ReadWrite, + .read_write, boot_service, ) catch |err| { log.err("Failed to map memory for kernel image: {?}", .{err}); @@ -191,10 +191,11 @@ pub fn main() uefi.Status { const page_start = phdr.p_vaddr & ~(@as(u64, 0xFFF)); const page_end = (phdr.p_vaddr + phdr.p_memsz + 0xFFF) & ~(@as(u64, 0xFFF)); const size = (page_end - page_start) / 4096; + const attribute = arch.page.PageAttribute.fromFlags(phdr.p_flags); for (0..size) |i| { arch.page.changeMap( page_start + 4096 * i, - if (phdr.p_flags & elf.PF_W != 0) .ReadWrite else .ReadOnly, + attribute, ) catch |err| { log.err("Failed to change memory protection: {?}", .{err}); return .LoadError; @@ -202,6 +203,9 @@ pub fn main() uefi.Status { } } + // Enable NX-bit. + arch.enableNxBit(); + // Clean up memory. status = boot_service.freePool(header_buffer); if (status != .Success) {