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:
@@ -2,7 +2,6 @@ global _start
|
||||
_start:
|
||||
xor rbp, rbp ; Sentinel rbp
|
||||
|
||||
pop rsi ; My PID
|
||||
mov rdi, 0 ; DEBUG syscall
|
||||
|
||||
.loop:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
49
src/libraries/elf/elf.cpp
Normal 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
61
src/libraries/elf/elf.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
98
src/libraries/elf/headers.h
Normal file
98
src/libraries/elf/headers.h
Normal 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
14
src/libraries/elf/wscript
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user