[boot][kernel] Split programs into sections

To enable setting sections as NX or read-only, the boot program loader
now loads programs as lists of sections, and the kernel args are updated
accordingly. The kernel's loader now just takes a program pointer to
iterate the sections. Also enable NX in IA32_EFER in the bootloader.
This commit is contained in:
Justin C. Miller
2021-01-20 01:25:47 -08:00
parent 14aad62e02
commit cb612c36ea
13 changed files with 140 additions and 61 deletions

View File

@@ -37,8 +37,25 @@ find_acpi_table(uefi::system_table *st)
return reinterpret_cast<void*>(acpi1_table); return reinterpret_cast<void*>(acpi1_table);
} }
static uint64_t
rdmsr(uint32_t addr)
{
uint32_t low, high;
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
return (static_cast<uint64_t>(high) << 32) | low;
}
static void
wrmsr(uint32_t addr, uint64_t value)
{
uint32_t low = value & 0xffffffff;
uint32_t high = value >> 32;
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
}
void void
setup_cr4() setup_control_regs()
{ {
uint64_t cr4 = 0; uint64_t cr4 = 0;
asm volatile ( "mov %%cr4, %0" : "=r" (cr4) ); asm volatile ( "mov %%cr4, %0" : "=r" (cr4) );
@@ -49,6 +66,15 @@ setup_cr4()
0x020000 | // Enable PCIDs 0x020000 | // Enable PCIDs
0; 0;
asm volatile ( "mov %0, %%cr4" :: "r" (cr4) ); asm volatile ( "mov %0, %%cr4" :: "r" (cr4) );
// Set up IA32_EFER
constexpr uint32_t IA32_EFER = 0xC0000080;
uint64_t efer = rdmsr(IA32_EFER);
efer |=
0x0001 | // Enable SYSCALL
0x0800 | // Enable NX bit
0;
wrmsr(IA32_EFER, efer);
} }
} // namespace hw } // namespace hw

View File

@@ -13,8 +13,8 @@ namespace hw {
/// significant bit set to 1. /// significant bit set to 1.
void * find_acpi_table(uefi::system_table *st); void * find_acpi_table(uefi::system_table *st);
/// Enable CPU options in CR4 for the kernel starting state. /// Enable CPU options in CR4 etc for the kernel starting state.
void setup_cr4(); void setup_control_regs();
} // namespace hw } // namespace hw
} // namespace boot } // namespace boot

View File

@@ -87,6 +87,9 @@ load_program(
bs->set_mem(pages, total_size, 0); bs->set_mem(pages, total_size, 0);
program.base = prog_base;
program.total_size = total_size;
program.num_sections = 0;
for (int i = 0; i < header->ph_num; ++i) { for (int i = 0; i < header->ph_num; ++i) {
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize; ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
const elf::program_header *pheader = const elf::program_header *pheader =
@@ -95,14 +98,18 @@ load_program(
if (pheader->type != elf::PT_LOAD) if (pheader->type != elf::PT_LOAD)
continue; continue;
args::program_section &section = program.sections[program.num_sections++];
void *src_start = offset_ptr<void>(data.data, pheader->offset); void *src_start = offset_ptr<void>(data.data, pheader->offset);
void *dest_start = offset_ptr<void>(pages, pheader->vaddr - prog_base); void *dest_start = offset_ptr<void>(pages, pheader->vaddr - prog_base);
bs->copy_mem(dest_start, src_start, pheader->file_size); bs->copy_mem(dest_start, src_start, pheader->file_size);
section.phys_addr = reinterpret_cast<uintptr_t>(dest_start);
section.virt_addr = pheader->vaddr;
section.size = pheader->mem_size;
section.type = static_cast<args::section_flags>(pheader->flags);
} }
program.phys_addr = reinterpret_cast<uintptr_t>(pages);
program.size = total_size;
program.virt_addr = prog_base;
program.entrypoint = header->entrypoint; program.entrypoint = header->entrypoint;
} }

View File

@@ -192,16 +192,20 @@ efi_main(uefi::handle image, uefi::system_table *st)
status_bar status {con.fb()}; // Switch to fb status display status_bar status {con.fb()}; // Switch to fb status display
args::program &kernel = args->programs[0]; args::program &kernel = args->programs[0];
paging::map_pages(args, kernel.phys_addr, kernel.virt_addr, kernel.size); for (auto &section : kernel.sections)
if (section.size)
paging::map_section(args, section);
kernel::entrypoint kentry = kernel::entrypoint kentry =
reinterpret_cast<kernel::entrypoint>(kernel.entrypoint); reinterpret_cast<kernel::entrypoint>(kernel.entrypoint);
status.next(); status.next();
hw::setup_control_regs();
memory::virtualize(args->pml4, map, st->runtime_services); memory::virtualize(args->pml4, map, st->runtime_services);
status.next(); status.next();
change_pointer(args->pml4); change_pointer(args->pml4);
hw::setup_cr4();
status.next(); status.next();
kentry(args); kentry(args);

View File

@@ -15,7 +15,7 @@ using memory::page_size;
using ::memory::pml4e_kernel; using ::memory::pml4e_kernel;
using ::memory::table_entries; using ::memory::table_entries;
// Flags: 0 0 0 1 0 0 0 0 0 0 1 1 = 0x0103 // Flags: 0 0 0 1 0 0 0 0 0 0 0 1 = 0x0101
// IGN | | | | | | | | +- Present // IGN | | | | | | | | +- Present
// | | | | | | | +--- Writeable // | | | | | | | +--- Writeable
// | | | | | | +----- Usermode access (supervisor only) // | | | | | | +----- Usermode access (supervisor only)
@@ -26,7 +26,7 @@ using ::memory::table_entries;
// | +---------------- PAT (determining memory type for page) // | +---------------- PAT (determining memory type for page)
// +------------------- Global // +------------------- Global
/// Page table entry flags for entries pointing at a page /// Page table entry flags for entries pointing at a page
constexpr uint16_t page_flags = 0x103; constexpr uint64_t page_flags = 0x101;
// Flags: 0 0 0 0 1 1 0 0 0 1 0 1 1 = 0x018b // Flags: 0 0 0 0 1 1 0 0 0 1 0 1 1 = 0x018b
// | IGN | | | | | | | | +- Present // | IGN | | | | | | | | +- Present
@@ -40,7 +40,7 @@ constexpr uint16_t page_flags = 0x103;
// | +------------------- Global // | +------------------- Global
// +---------------------------- PAT (determining memory type for page) // +---------------------------- PAT (determining memory type for page)
/// Page table entry flags for entries pointing at a huge page /// Page table entry flags for entries pointing at a huge page
constexpr uint16_t huge_page_flags = 0x183; constexpr uint64_t huge_page_flags = 0x18b;
// Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003 // Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003
// IGNORED | | | | | | | +- Present // IGNORED | | | | | | | +- Present
@@ -52,7 +52,7 @@ constexpr uint16_t huge_page_flags = 0x183;
// | +-------------- Ignored // | +-------------- Ignored
// +---------------- Reserved 0 (Table pointer, not page) // +---------------- Reserved 0 (Table pointer, not page)
/// Page table entry flags for entries pointing at another table /// Page table entry flags for entries pointing at another table
constexpr uint16_t table_flags = 0x003; constexpr uint64_t table_flags = 0x003;
/// Iterator over page table entries. /// Iterator over page table entries.
template <unsigned D = 4> template <unsigned D = 4>
@@ -191,7 +191,7 @@ allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
status_line status(L"Allocating initial page tables"); status_line status(L"Allocating initial page tables");
static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs
static constexpr size_t extra_tables = 49; // number of extra pages static constexpr size_t extra_tables = 64; // number of extra pages
// number of pages for kernelspace PDs + PML4 // number of pages for kernelspace PDs + PML4
static constexpr size_t kernel_tables = pd_tables + 1; static constexpr size_t kernel_tables = pd_tables + 1;
@@ -223,23 +223,39 @@ allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count); console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count);
} }
template <typename E>
constexpr bool has_flag(E set, E flag) {
return
(static_cast<uint64_t>(set) & static_cast<uint64_t>(flag)) ==
static_cast<uint64_t>(flag);
}
void void
map_pages( map_section(
kernel::args::header *args, kernel::args::header *args,
uintptr_t phys, uintptr_t virt, const kernel::args::program_section &section)
size_t size)
{ {
paging::page_table *pml4 = paging::page_table *pml4 =
reinterpret_cast<paging::page_table*>(args->pml4); reinterpret_cast<paging::page_table*>(args->pml4);
size_t pages = memory::bytes_to_pages(size); size_t pages = memory::bytes_to_pages(section.size);
page_entry_iterator<4> iterator{ page_entry_iterator<4> iterator{
virt, pml4, section.virt_addr, pml4,
args->page_tables, args->page_tables,
args->table_count}; args->table_count};
using kernel::args::section_flags;
uint64_t flags = page_flags;
if (!has_flag(section.type, section_flags::execute))
flags |= (1ull << 63); // set NX bit
if (has_flag(section.type, section_flags::write))
flags |= 2;
uintptr_t phys = section.phys_addr;
while (true) { while (true) {
*iterator = phys | page_flags; *iterator = phys | flags;
if (--pages == 0) if (--pages == 0)
break; break;

View File

@@ -38,15 +38,14 @@ void allocate_tables(
/// tables in the current PML4. /// tables in the current PML4.
void add_current_mappings(page_table *new_pml4); void add_current_mappings(page_table *new_pml4);
/// Map a physical address to a virtual address in the given page tables. /// Map a program section in physical memory to its virtual address in the
/// given page tables.
/// \arg args The kernel args header, used for the page table cache and pml4 /// \arg args The kernel args header, used for the page table cache and pml4
/// \arg phys The phyiscal address to map in /// \arg section The program section to load
/// \arg virt The virtual address to map in void map_section(
/// \arg size The size in bytes of the mapping
void map_pages(
kernel::args::header *args, kernel::args::header *args,
uintptr_t phys, uintptr_t virt, const kernel::args::program_section &section);
size_t bytes);
} // namespace paging } // namespace paging
} // namespace boot } // namespace boot

View File

@@ -35,11 +35,26 @@ struct framebuffer {
fb_type type; fb_type type;
}; };
struct program { enum class section_flags : uint32_t {
none = 0,
execute = 1,
write = 2,
read = 4,
};
struct program_section {
uintptr_t phys_addr; uintptr_t phys_addr;
uintptr_t virt_addr; uintptr_t virt_addr;
uint32_t size;
section_flags type;
};
struct program {
uintptr_t entrypoint; uintptr_t entrypoint;
size_t size; uintptr_t base;
size_t total_size;
size_t num_sections;
program_section sections[3];
}; };
enum class mem_type : uint32_t { enum class mem_type : uint32_t {

View File

@@ -58,6 +58,11 @@ namespace memory {
return reinterpret_cast<T*>(a|page_offset); return reinterpret_cast<T*>(a|page_offset);
} }
/// Convert a physical address to a virtual one (in the offset-mapped area)
template <typename T> T * to_virtual(T *p) {
return to_virtual<T>(reinterpret_cast<uintptr_t>(p));
}
/// Get the number of pages needed for a given number of bytes. /// Get the number of pages needed for a given number of bytes.
/// \arg bytes The number of bytes desired /// \arg bytes The number of bytes desired
/// \returns The number of pages needed to contain the desired bytes /// \returns The number of pages needed to contain the desired bytes

View File

@@ -8,11 +8,7 @@ preloaded_process_init:
; create_process already pushed the arguments for load_process_image and ; create_process already pushed the arguments for load_process_image and
; the following iretq onto the stack for us ; the following iretq onto the stack for us
pop rdi ; the physical address of the program image pop rdi ; a pointer to the program descriptor
pop rsi ; the virtual address of the program image
pop rdx ; the size in bytes of the program image
pop rcx ; the address of this thread's TCB
call load_process_image call load_process_image
; user rsp is now in rax, put it in the right place for iret ; user rsp is now in rax, put it in the right place for iret

View File

@@ -192,8 +192,8 @@ kernel_main(args::header *header)
// Skip program 0, which is the kernel itself // Skip program 0, which is the kernel itself
for (size_t i = 1; i < header->num_programs; ++i) { for (size_t i = 1; i < header->num_programs; ++i) {
args::program &prog = header->programs[i]; args::program *prog = memory::to_virtual(&header->programs[i]);
thread *th = sched->load_process(prog.phys_addr, prog.virt_addr, prog.size, prog.entrypoint); thread *th = sched->load_process(*prog);
if (i == 2) { if (i == 2) {
//th->set_state(thread::state::constant); //th->set_state(thread::state::constant);
} }

View File

@@ -34,7 +34,7 @@ const uint64_t rflags_int = 0x202;
extern "C" { extern "C" {
void preloaded_process_init(); void preloaded_process_init();
uintptr_t load_process_image(uintptr_t phys, uintptr_t virt, size_t bytes, TCB *tcb); uintptr_t load_process_image(const kernel::args::program*);
}; };
extern uint64_t idle_stack_end; extern uint64_t idle_stack_end;
@@ -76,20 +76,28 @@ inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) {
} }
uintptr_t uintptr_t
load_process_image(uintptr_t phys, uintptr_t virt, size_t bytes, TCB *tcb) load_process_image(const kernel::args::program *program)
{ {
using memory::page_align_down; using memory::page_align_down;
using memory::page_align_up; using memory::page_align_up;
using kernel::args::section_flags;
// We're now in the process space for this process, allocate memory for the // We're now in the process space for this process, allocate memory for the
// process code and load it // process code and load it
process &proc = process::current(); process &proc = process::current();
thread &th = thread::current(); thread &th = thread::current();
TCB *tcb = th.tcb();
vm_space &space = proc.space(); vm_space &space = proc.space();
vm_area *vma = new vm_area_open(bytes, space, vm_flags::zero|vm_flags::write); for (const auto &sect : program->sections) {
space.add(virt, vma); vm_flags flags =
vma->commit(phys, 0, memory::page_count(bytes)); (bitfield_has(sect.type, section_flags::execute) ? vm_flags::exec : vm_flags::none) |
(bitfield_has(sect.type, section_flags::write) ? vm_flags::write : vm_flags::none);
vm_area *vma = new vm_area_open(sect.size, space, flags);
space.add(sect.virt_addr, vma);
vma->commit(sect.phys_addr, 0, memory::page_count(sect.size));
}
// double zero stack sentinel // double zero stack sentinel
*push<uint64_t>(tcb->rsp3) = 0; *push<uint64_t>(tcb->rsp3) = 0;
@@ -141,7 +149,8 @@ load_process_image(uintptr_t phys, uintptr_t virt, size_t bytes, TCB *tcb)
// Crazypants framebuffer part // Crazypants framebuffer part
if (fb) { if (fb) {
vma = new vm_area_open(fb->size, space, vm_flags::write|vm_flags::mmio|vm_flags::write_combine); vm_area *vma = new vm_area_open(fb->size, space,
vm_flags::write|vm_flags::mmio|vm_flags::write_combine);
space.add(0x100000000, vma); space.add(0x100000000, vma);
vma->commit(fb->phys_addr, 0, memory::page_count(fb->size)); vma->commit(fb->phys_addr, 0, memory::page_count(fb->size));
} }
@@ -167,7 +176,7 @@ scheduler::create_process(bool user)
} }
thread * thread *
scheduler::load_process(uintptr_t phys, uintptr_t virt, size_t size, uintptr_t entry) scheduler::load_process(kernel::args::program &program)
{ {
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0 uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
@@ -180,23 +189,20 @@ scheduler::load_process(uintptr_t phys, uintptr_t virt, size_t size, uintptr_t e
auto *tcb = th->tcb(); auto *tcb = th->tcb();
// Create an initial kernel stack space // Create an initial kernel stack space
uintptr_t *stack = reinterpret_cast<uintptr_t *>(tcb->rsp0) - 9; uintptr_t *stack = reinterpret_cast<uintptr_t *>(tcb->rsp0) - 6;
// Pass args to preloaded_process_init on the stack // Pass args to preloaded_process_init on the stack
stack[0] = reinterpret_cast<uintptr_t>(phys); stack[0] = reinterpret_cast<uintptr_t>(&program);
stack[1] = reinterpret_cast<uintptr_t>(virt);
stack[2] = reinterpret_cast<uintptr_t>(size);
stack[3] = reinterpret_cast<uintptr_t>(tcb);
tcb->rsp = reinterpret_cast<uintptr_t>(stack); tcb->rsp = reinterpret_cast<uintptr_t>(stack);
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(preloaded_process_init)); th->add_thunk_kernel(reinterpret_cast<uintptr_t>(preloaded_process_init));
// Arguments for iret - rip will be pushed on before these // Arguments for iret - rip will be pushed on before these
stack[4] = reinterpret_cast<uintptr_t>(entry); stack[1] = reinterpret_cast<uintptr_t>(program.entrypoint);
stack[5] = cs; stack[2] = cs;
stack[6] = rflags_int | (3 << 12); stack[3] = rflags_int | (3 << 12);
stack[7] = process::stacks_top; stack[4] = process::stacks_top;
stack[8] = ss; stack[5] = ss;
tcb->rsp3 = process::stacks_top; tcb->rsp3 = process::stacks_top;

View File

@@ -5,6 +5,11 @@
#include <stdint.h> #include <stdint.h>
#include "objects/thread.h" #include "objects/thread.h"
namespace kernel {
namespace args {
struct program;
}}
class lapic; class lapic;
class process; class process;
struct page_table; struct page_table;
@@ -41,12 +46,9 @@ public:
scheduler(lapic *apic); scheduler(lapic *apic);
/// Create a new process from a program image in memory. /// Create a new process from a program image in memory.
/// \arg phys Physical address of the loaded program image /// \arg program The descriptor of the pogram in memory
/// \arg virt Virtual address of the loaded program image
/// \arg size Size of the program image, in bytes
/// \arg entry Virtual address of the program entrypoint
/// \returns The main thread of the loaded process /// \returns The main thread of the loaded process
thread * load_process(uintptr_t phys, uintptr_t virt, size_t size, uintptr_t entry); thread * load_process(kernel::args::program &program);
/// Create a new kernel task /// Create a new kernel task
/// \arg proc Function to run as a kernel task /// \arg proc Function to run as a kernel task

View File

@@ -131,10 +131,13 @@ operator ! (E rhs)
} }
template <typename E> template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type constexpr bool
bitfield_has(E set, E flag) bitfield_has(E set, E flag)
{ {
return (set & flag) == flag; return
(static_cast<typename std::underlying_type<E>::type>(set) &
static_cast<typename std::underlying_type<E>::type>(flag)) ==
static_cast<typename std::underlying_type<E>::type>(flag);
} }
// Overload the logical-and operator to be 'bitwise-and, bool-cast' // Overload the logical-and operator to be 'bitwise-and, bool-cast'