Simple ELF program loader

Now any initrd file is treated like a program image and passed to the
loader to load as a process. Very rudimentary elf loading just allocates
pages, copies sections, and sets the ELF's entrypoint as the RIP to
iretq to.
This commit is contained in:
Justin C. Miller
2018-09-06 01:31:47 -07:00
parent 3d0b262435
commit 585abe9a18
13 changed files with 321 additions and 51 deletions

View File

@@ -2,7 +2,6 @@ global _start
_start:
xor rbp, rbp ; Sentinel rbp
pop rsi ; My PID
mov rdi, 0 ; DEBUG syscall
.loop:

View File

@@ -209,17 +209,3 @@ ramdisk_process_loader:
add rsp, 16 ; because the ISRs add err/num
iretq
global taskA
taskA:
push rbp
mov rbp, rsp
mov rdi, 0
mov rax, 0xaaaaaaaaaaaaaaaa
.loop:
syscall
jmp .loop
global taskAend
taskAend:

View File

@@ -41,9 +41,9 @@ init_console()
log::init(cons);
log::enable(logs::apic, log::level::info);
log::enable(logs::device, log::level::debug);
log::enable(logs::device, log::level::info);
log::enable(logs::driver, log::level::debug);
log::enable(logs::memory, log::level::info);
log::enable(logs::memory, log::level::debug);
log::enable(logs::fs, log::level::debug);
log::enable(logs::task, log::level::debug);
}
@@ -142,7 +142,12 @@ kernel_main(popcorn_data *header)
syscall_enable();
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic());
sched->start();
for (auto &f : ird.files()) {
//if (f.executable())
sched->create_process(f.name(), f.data(), f.size());
}
sched->start();
g_console.puts("boogity!\n");
}

View File

@@ -34,6 +34,14 @@ public:
page_manager();
/// Helper to get the number of pages needed for a given number of bytes.
/// \arg bytes The number of bytes desired
/// \returns The number of pages needed to contain the desired bytes
static inline size_t page_count(size_t bytes)
{
return (bytes - 1) / page_size + 1;
}
/// Helper to read the PML4 table from CR3.
/// \returns A pointer to the current PML4 table.
static inline page_table * get_pml4()

View File

@@ -7,6 +7,9 @@
#include "page_manager.h"
#include "scheduler.h"
#include "elf/elf.h"
#include "kutil/assert.h"
scheduler scheduler::s_instance(nullptr);
//static const uint32_t quantum = 2000000;
static const uint32_t quantum = 20000000;
@@ -16,43 +19,83 @@ const uint64_t rflags_noint = 0x002;
const uint64_t rflags_int = 0x202;
extern "C" {
void taskA();
void taskAend();
void ramdisk_process_loader();
void load_process(void *copy_start, size_t butes, cpu_state state);
void load_process(const void *image_start, size_t butes, cpu_state state);
};
scheduler::scheduler(lapic *apic) :
m_apic(apic),
m_current(0)
m_current(0),
m_next_pid(0)
{
m_processes.ensure_capacity(50);
m_processes.append({m_next_pid++, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now
}
void
load_process(void *copy_start, size_t bytes, cpu_state state)
load_process(const void *image_start, size_t bytes, cpu_state state)
{
log::debug(logs::task, "Loading task! Start: %016lx [%d]", copy_start, bytes);
log::debug(logs::task, "New process state:");
// We're now in the process space for this process, allocate memory for the
// process code and load it
page_manager *pager = page_manager::get();
log::debug(logs::task, "Loading task! ELF: %016lx [%d]", image_start, bytes);
// TODO: Handle bad images gracefully
elf::elf image(image_start, bytes);
kassert(image.valid(), "Invalid ELF passed to load_process");
const unsigned program_count = image.program_count();
for (unsigned i = 0; i < program_count; ++i) {
const elf::program_header *header = image.program(i);
if (header->type != elf::segment_type::load)
continue;
addr_t aligned = header->vaddr & ~(page_manager::page_size - 1);
size_t size = (header->vaddr + header->mem_size) - aligned;
size_t pages = page_manager::page_count(size);
log::debug(logs::task, " Loadable segment %02d: vaddr %016lx size %016lx",
i, header->vaddr, header->mem_size);
log::debug(logs::task, " - aligned to: vaddr %016lx pages %d",
aligned, pages);
void *mapped = pager->map_pages(aligned, pages, true);
kassert(mapped, "Tried to map userspace pages and failed!");
kutil::memset(mapped, 0, pages * page_manager::page_size);
}
const unsigned section_count = image.section_count();
for (unsigned i = 0; i < section_count; ++i) {
const elf::section_header *header = image.section(i);
if (header->type != elf::section_type::progbits ||
!bitfield_has(header->flags, elf::section_flags::alloc))
continue;
log::debug(logs::task, " Loadable section %u: vaddr %016lx size %016lx",
i, header->addr, header->size);
void *dest = reinterpret_cast<void *>(header->addr);
const void *src = kutil::offset_pointer(image_start, header->offset);
kutil::memcpy(dest, src, header->size);
}
state.rip = image.entrypoint();
log::debug(logs::task, "Loaded! New process state:");
log::debug(logs::task, " CS: %d [%d]", state.cs >> 3, state.cs & 0x07);
log::debug(logs::task, " SS: %d [%d]", state.ss >> 3, state.ss & 0x07);
log::debug(logs::task, " RFLAGS: %08x", state.rflags);
log::debug(logs::task, " RIP: %016lx", state.rip);
log::debug(logs::task, " uRSP: %016lx", state.user_rsp);
// We're now in the process space for this process, allocate memory for the
// process code and load it
page_manager *pager = page_manager::get();
size_t count = ((bytes - 1) / page_manager::page_size) + 1;
void *loading = pager->map_pages(0x200000, count, true);
kutil::memcpy(loading, copy_start, bytes);
state.rip = reinterpret_cast<uint64_t>(loading);
}
static process
create_process(uint16_t pid)
void
scheduler::create_process(const char *name, const void *data, size_t size)
{
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
uint16_t cs = (5 << 3) | 3; // User CS is GDT entry 5, ring 3
@@ -90,29 +133,25 @@ create_process(uint16_t pid)
loader_state->rip = reinterpret_cast<uint64_t>(ramdisk_process_loader);
loader_state->user_rsp = reinterpret_cast<uint64_t>(state);
// TODO: replace with an actual ELF location in memory
loader_state->rax = reinterpret_cast<uint64_t>(taskA);
loader_state->rbx = reinterpret_cast<uint64_t>(taskAend) - reinterpret_cast<uint64_t>(taskA);
loader_state->rax = reinterpret_cast<uint64_t>(data);
loader_state->rbx = size;
log::debug(logs::task, "Creating PID %d:", pid);
uint16_t pid = m_next_pid++;
log::debug(logs::task, "Creating process %s:", name);
log::debug(logs::task, " PID %d", pid);
log::debug(logs::task, " RSP0 %016lx", state);
log::debug(logs::task, " RSP3 %016lx", state->user_rsp);
log::debug(logs::task, " PML4 %016lx", pml4);
log::debug(logs::task, " Loading %016lx [%d]", loader_state->rax, loader_state->rbx);
return {pid, reinterpret_cast<addr_t>(loader_state), pml4};
m_processes.append({pid, reinterpret_cast<addr_t>(loader_state), pml4});
}
void
scheduler::start()
{
log::info(logs::task, "Starting scheduler.");
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
m_processes.append({0, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now
m_processes.append(create_process(1));
//m_processes.append(create_process(2, &taskB));
}
addr_t

View File

@@ -24,6 +24,12 @@ public:
/// \arg apic Pointer to the local APIC object
scheduler(lapic *apic);
/// Create a new process from a program image in memory.
/// \arg name Name of the program image
/// \arg data Pointer to the image data
/// \arg size Size of the program image, in bytes
void create_process(const char *name, const void *data, size_t size);
/// Start the scheduler working. This may involve starting
/// timer interrupts or other preemption methods.
void start();
@@ -41,6 +47,7 @@ private:
lapic *m_apic;
kutil::vector<process> m_processes;
uint16_t m_current;
uint16_t m_next_pid;
static scheduler s_instance;
};

View File

@@ -15,7 +15,11 @@ def build(bld):
name = 'kernel',
includes = '.',
target = bld.env.KERNEL_FILENAME,
use = ['kutil', 'initrd'],
use = [
'kutil',
'initrd',
'elf',
],
linkflags = "-T {}".format(lds),
)

49
src/libraries/elf/elf.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "elf/elf.h"
#include "elf/headers.h"
static const uint32_t expected_magic = 0x464c457f; // "\x7f" "ELF"
namespace elf {
elf::elf(const void *data, size_t size) :
m_data(data),
m_size(size)
{
}
bool
elf::valid() const
{
const file_header *fheader = header();
return
fheader->magic == expected_magic &&
fheader->word_size == wordsize::bits64 &&
fheader->endianness == encoding::lsb &&
fheader->os_abi == osabi::sysV &&
fheader->file_type == filetype::executable &&
fheader->machine_type == machine::x64 &&
fheader->ident_version == 1 &&
fheader->version == 1;
}
const program_header *
elf::program(unsigned index) const
{
const file_header *fheader = header();
uint64_t off = fheader->ph_offset + (index * fheader->ph_entsize);
const void *pheader = kutil::offset_pointer(m_data, off);
return reinterpret_cast<const program_header *>(pheader);
}
const section_header *
elf::section(unsigned index) const
{
const file_header *fheader = header();
uint64_t off = fheader->sh_offset + (index * fheader->sh_entsize);
const void *sheader = kutil::offset_pointer(m_data, off);
return reinterpret_cast<const section_header *>(sheader);
}
} // namespace elf

61
src/libraries/elf/elf.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include <stddef.h>
#include "elf/headers.h"
#include "kutil/memory.h"
namespace elf {
class elf
{
public:
/// Constructor: Create an elf object out of ELF data in memory
/// \arg data The ELF data to read
/// \arg size Size of the ELF data, in bytes
elf(const void *data, size_t size);
/// Check the validity of the ELF data
/// \returns true for valid ELF data
bool valid() const;
/// Get the entrypoint address of the program image
/// \returns A pointer to the entrypoint of the program
inline addr_t entrypoint() const
{
return static_cast<addr_t>(header()->entrypoint);
}
/// Get the number of program sections in the image
/// \returns The number of program section entries
inline unsigned program_count() const
{
return header()->ph_num;
}
/// Get a program header
/// \arg index The index number of the program header
/// \returns A pointer to the program header data
const program_header * program(unsigned index) const;
/// Get the number of data sections in the image
/// \returns The number of section entries
inline unsigned section_count() const
{
return header()->sh_num;
}
/// Get a section header
/// \arg index The index number of the section header
/// \returns A pointer to the section header data
const section_header * section(unsigned index) const;
private:
inline const file_header *header() const
{
return reinterpret_cast<const file_header *>(m_data);
}
const void *m_data;
size_t m_size;
};
}

View File

@@ -0,0 +1,98 @@
#pragma once
#include <stdint.h>
#include "kutil/enum_bitfields.h"
namespace elf {
enum class wordsize : uint8_t { invalid, bits32, bits64 };
enum class encoding : uint8_t { invalid, lsb, msb };
enum class osabi : uint8_t { sysV, hpux, standalone = 255 };
enum class machine : uint16_t { none, x64 = 0x3e };
enum class filetype : uint16_t
{
none,
relocatable,
executable,
shared,
core
};
struct file_header
{
uint32_t magic;
wordsize word_size;
encoding endianness;
uint8_t ident_version;
osabi os_abi;
uint64_t reserved;
filetype file_type;
machine machine_type;
uint32_t version;
uint64_t entrypoint;
uint64_t ph_offset;
uint64_t sh_offset;
uint32_t flags;
uint16_t eh_size;
uint16_t ph_entsize;
uint16_t ph_num;
uint16_t sh_entsize;
uint16_t sh_num;
uint16_t sh_str_idx;
} __attribute__ ((packed));
enum class segment_type : uint32_t { null, load, dynamic, interpreter, note };
struct program_header
{
segment_type type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t file_size;
uint64_t mem_size;
uint64_t align;
} __attribute__ ((packed));
enum class section_type : uint32_t { null, progbits };
enum class section_flags : uint64_t
{
write = 0x01,
alloc = 0x02,
exec = 0x04,
};
struct section_header
{
uint32_t name_offset;
section_type type;
section_flags flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t align;
uint64_t entry_size;
} __attribute__ ((packed));
} // namespace elf
IS_BITFIELD(elf::section_flags);

14
src/libraries/elf/wscript Normal file
View File

@@ -0,0 +1,14 @@
def configure(ctx):
pass
def build(bld):
sources = bld.path.ant_glob("**/*.cpp")
bld.stlib(
source = sources,
name = 'elf',
target = 'elf',
)
# vim: ft=python et

View File

@@ -17,9 +17,9 @@ memset(void *s, uint8_t v, size_t n)
}
void *
memcpy(void *dest, void *src, size_t n)
memcpy(void *dest, const void *src, size_t n)
{
uint8_t *s = reinterpret_cast<uint8_t *>(src);
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
uint8_t *d = reinterpret_cast<uint8_t *>(dest);
for (size_t i = 0; i < n; ++i) d[i] = s[i];
return d;

View File

@@ -35,7 +35,7 @@ void * memset(void *p, uint8_t v, size_t n);
/// \src The memory to copy from
/// \n The number of bytes to copy
/// \returns A pointer to the destination memory
void * memcpy(void *dest, void *src, size_t n);
void * memcpy(void *dest, const void *src, size_t n);
/// Read a value of type T from a location in memory
/// \arg p The location in memory to read