Kernel: Add support for the NX bit

Not support, actually. We now REQUIRE it.
This commit is contained in:
apio 2022-11-02 18:34:57 +01:00
parent 534500cda0
commit 22740e69bf
12 changed files with 138 additions and 17 deletions

View File

@ -10,4 +10,5 @@ namespace CPU
uint64_t get_feature_bitmask();
uint64_t get_initial_apic_id();
bool has_feature(CPU::Features);
bool has_nx();
}

View File

@ -13,6 +13,8 @@ namespace MemoryManager
{
void init();
void protect_kernel_sections();
void* get_mapping(void* physicalAddress, int flags = MAP_READ_WRITE);
void release_mapping(void* mapping);

View File

@ -17,13 +17,17 @@ struct PageDirectoryEntry
bool larger_pages : 1;
bool ignore1 : 1;
uint8_t available : 3;
uint64_t address : 52;
uint64_t address : 48;
uint8_t available2 : 3;
bool no_execute : 1;
void set_address(uint64_t addr);
uint64_t get_address();
};
} __attribute__((packed));
struct PageTable
{
PageDirectoryEntry entries[512];
} __attribute__((aligned(PAGE_SIZE)));
static_assert(sizeof(PageDirectoryEntry) == 8UL);

View File

@ -33,6 +33,7 @@ namespace VMM
PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr);
void propagate_read_write(PageTable* root, uint64_t vaddr);
void propagate_no_execute(PageTable* root, uint64_t vaddr);
void propagate_user(PageTable* root, uint64_t vaddr);
void flush_tlb(uint64_t addr);

View File

@ -16,14 +16,19 @@ SECTIONS
kernel_start = .;
.text : {
KEEP(*(.text.boot)) *(.text .text.*) /* code */
. = ALIGN(0x1000);
start_of_kernel_rodata = .;
*(.rodata .rodata.*) /* data */
end_of_kernel_rodata = .;
. = ALIGN(0x1000);
start_of_kernel_data = .;
*(.data .data.*)
} :boot
.bss (NOLOAD) : { /* bss */
. = ALIGN(16);
*(.bss .bss.*)
*(COMMON)
} :boot
end_of_kernel_data = .;
kernel_end = .;
/DISCARD/ : { *(.eh_frame) *(.comment) }

View File

@ -66,6 +66,14 @@ uint64_t CPU::get_feature_bitmask()
return bitmask;
}
bool CPU::has_nx()
{
unsigned int unused;
unsigned int edx;
__get_cpuid(0x80000001, &unused, &unused, &unused, &edx);
return edx & (1 << 20);
}
static bool _has_feature(int feature)
{
return (CPU::get_feature_bitmask() & (uint64_t)(1 << feature)) > 0;

View File

@ -18,6 +18,7 @@
#include "std/assert.h"
#include "std/string.h"
#include "utils/Time.h"
#include "misc/MSR.h"
extern BOOTBOOT bootboot;
extern "C" char environment[4096];
@ -39,6 +40,40 @@ void Init::disable_smp()
extern "C" void asm_enable_sse();
extern void clock_init();
extern void panic_prepare_keyboard_triple_fault();
#define NO_EXECUTE_ENABLED (1 << 11)
static void check_and_enable_nx()
{
if(!CPU::has_nx()) {
kerrorln("This machine does not support the NX feature, which is required to continue booting.");
kerrorln("On most cases, this means your machine is too old and not supported.");
kinfoln("Press any key to restart and select an OS that is suitable for your CPU.");
panic_prepare_keyboard_triple_fault();
while(1) halt();
}
kdbgln("nx supported");
MSR efer(IA32_EFER_MSR);
uint64_t value = efer.read();
if(value & NO_EXECUTE_ENABLED)
{
kdbgln("nx already enabled");
return;
}
kdbgln("nx not enabled, enabling it");
efer.write(value | NO_EXECUTE_ENABLED);
}
void Init::early_init()
{
Interrupts::disable();
@ -48,8 +83,12 @@ void Init::early_init()
framebuffer0.init((void*)&fb, bootboot.fb_type, bootboot.fb_scanline, bootboot.fb_width, bootboot.fb_height);
check_and_enable_nx();
MemoryManager::init();
MemoryManager::protect_kernel_sections();
if (strstr(environment, "quiet=1"))
{
KernelLog::toggle_log_level(LogLevel::DEBUG);

View File

@ -8,6 +8,7 @@
#include "memory/PMM.h"
#include "memory/VMM.h"
#include "std/assert.h"
#include "misc/utils.h"
void MemoryManager::init()
{
@ -17,6 +18,17 @@ void MemoryManager::init()
PMM::map_bitmap_to_virtual();
}
extern char start_of_kernel_rodata[1];
extern char end_of_kernel_rodata[1];
extern char start_of_kernel_data[1];
extern char end_of_kernel_data[1];
void MemoryManager::protect_kernel_sections()
{
protect(start_of_kernel_rodata, Utilities::get_blocks_from_size(PAGE_SIZE, end_of_kernel_rodata - start_of_kernel_rodata), 0);
protect(start_of_kernel_data, Utilities::get_blocks_from_size(PAGE_SIZE, end_of_kernel_data - start_of_kernel_data), MAP_READ_WRITE);
}
void* MemoryManager::get_mapping(void* physicalAddress, int flags)
{
uint64_t virtualAddress = KernelHeap::request_virtual_page();

View File

@ -13,6 +13,8 @@ static PageTable* kernel_pml4;
static PageTable* current_pml4;
static AddressSpace* user_address_space;
// FIXME: Switch to recursive paging instead of naively assuming the physical address space is identity mapped.
void VMM::switch_back_to_kernel_address_space()
{
if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
@ -85,6 +87,9 @@ void VMM::remap(uint64_t vaddr, int flags)
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else
pde->read_write = false;
if(flags & Execute) pde->no_execute = false;
else
pde->no_execute = true;
flush_tlb(vaddr);
}
@ -104,6 +109,7 @@ uint64_t VMM::get_flags(uint64_t vaddr)
uint64_t flags = 0;
if (pde->user) flags |= User;
if (pde->read_write) flags |= ReadWrite;
if(!pde->no_execute) flags |= Execute;
return flags;
}
@ -130,6 +136,9 @@ void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
else
pde->read_write = false;
if(flags & Execute) pde->no_execute = false;
else
pde->no_execute = true;
if (will_flush_tlb) flush_tlb(vaddr);
}
@ -246,6 +255,34 @@ void VMM::propagate_user(PageTable* root, uint64_t vaddr)
pde->user = true;
}
void VMM::propagate_no_execute(PageTable* root, uint64_t vaddr)
{
uint64_t page_index;
PageDirectoryEntry* pde;
PageTable* pt = root;
uint64_t indexes[3];
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
for (int i = 0; i < 3; i++)
{
pde = &pt->entries[indexes[i]];
if (!pde->present) return;
else
{
pde->no_execute = true;
if (pde->larger_pages) return;
pt = (PageTable*)pde->get_address();
}
}
pde = &pt->entries[page_index];
if (!pde->present) return;
else
pde->no_execute = true;
}
void VMM::flush_tlb(uint64_t addr)
{
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");

View File

@ -16,7 +16,7 @@
static bool g_is_in_panic = false;
static bool g_is_in_double_panic = false;
static void panic_prepare_keyboard_triple_fault()
void panic_prepare_keyboard_triple_fault()
{
PIC::enable_master(0b11111101); // enable keyboard only
PIC::enable_slave(0b11111111);

View File

@ -105,7 +105,11 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
VMM::apply_address_space();
VMM::switch_to_previous_user_address_space();
MemoryManager::protect(buffer, pages, phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER);
int new_flags = MAP_USER;
if(phdr.p_flags & 2) new_flags |= MAP_READ_WRITE;
if(phdr.p_flags & 1) new_flags |= MAP_EXEC;
MemoryManager::protect(buffer, pages, new_flags);
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
(image->section_count + 1) * sizeof(ELFSection));

View File

@ -10,27 +10,35 @@
#include "thread/Scheduler.h"
#include <stddef.h>
#define MAP_READ 1
#define MAP_WRITE 2
#define MAP_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_NONE 0
#define PROT_EXEC 4
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
static const char* format_prot(int prot)
{
static char prot_string[3];
prot_string[2] = 0;
prot_string[0] = ((prot & MAP_READ) > 0) ? 'r' : '-';
prot_string[1] = ((prot & MAP_WRITE) > 0) ? 'w' : '-';
static char prot_string[4];
prot_string[3] = 0;
prot_string[0] = ((prot & PROT_READ) > 0) ? 'r' : '-';
prot_string[1] = ((prot & PROT_WRITE) > 0) ? 'w' : '-';
prot_string[2] = ((prot & PROT_WRITE) > 0) ? 'x' : '-';
return prot_string;
}
static int mman_flags_from_prot(int prot)
{
prot &= 0b11;
if (prot == MAP_NONE) return 0;
if ((prot & MAP_WRITE) > 0) return MAP_USER | MAP_READ_WRITE;
return MAP_USER;
prot &= 0b111;
int flags = MAP_USER;
if (prot == PROT_NONE) return 0;
if ((prot & PROT_WRITE) > 0) {
flags |= MAP_READ_WRITE;
}
if ((prot & PROT_EXEC) > 0) {
flags |= MAP_EXEC;
}
return flags;
}
void sys_mmap(Context* context, void* address, size_t size, int prot)