Kernel: Implement Process::exec()

This commit is contained in:
Bananymous 2023-05-31 20:56:29 +03:00
parent 5fb69300ca
commit b48b239882
5 changed files with 136 additions and 62 deletions

View File

@ -47,6 +47,7 @@ namespace Kernel
pid_t pid() const { return m_pid; }
BAN::ErrorOr<Process*> fork(uintptr_t rsp, uintptr_t rip);
BAN::ErrorOr<void> exec(BAN::StringView path, const char* const* argv, const char* const* envp);
BAN::ErrorOr<int> open(BAN::StringView, int);
BAN::ErrorOr<void> close(int fd);
@ -85,6 +86,8 @@ namespace Kernel
static Process* create_process();
static void register_process(Process*);
BAN::ErrorOr<void> cleanup_and_load_elf(BAN::StringView);
BAN::ErrorOr<BAN::String> absolute_path_of(BAN::StringView) const;
private:

View File

@ -32,6 +32,7 @@ namespace Kernel
~Thread();
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t rsp, uintptr_t rip);
void setup_exec();
pid_t tid() const { return m_tid; }

View File

@ -46,49 +46,14 @@ namespace Kernel
BAN::ErrorOr<Process*> Process::create_userspace(BAN::StringView path)
{
auto* elf = TRY(LibELF::ELF::load_from_file(path));
if (!elf->is_native())
{
derrorln("ELF has invalid architecture");
return BAN::Error::from_errno(EINVAL);
}
auto* process = create_process();
MUST(process->m_working_directory.push_back('/'));
process->m_page_table = MUST(PageTable::create_userspace());
auto& elf_file_header = elf->file_header_native();
for (size_t i = 0; i < elf_file_header.e_phnum; i++)
if (auto res = process->cleanup_and_load_elf(path); res.is_error())
{
auto& elf_program_header = elf->program_header_native(i);
switch (elf_program_header.p_type)
{
case LibELF::PT_NULL:
break;
case LibELF::PT_LOAD:
{
// TODO: Do some relocations or map kernel to higher half?
ASSERT(process->page_table().is_range_free(elf_program_header.p_vaddr, elf_program_header.p_memsz));
uint8_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (elf_program_header.p_flags & LibELF::PF_W)
flags |= PageTable::Flags::ReadWrite;
size_t page_start = elf_program_header.p_vaddr / PAGE_SIZE;
size_t page_end = BAN::Math::div_round_up<size_t>(elf_program_header.p_vaddr + elf_program_header.p_memsz, PAGE_SIZE);
size_t page_count = page_end - page_start + 1;
MUST(process->m_mapped_ranges.push_back(VirtualRange::create(process->page_table(), page_start * PAGE_SIZE, page_count * PAGE_SIZE, flags)));
{
PageTableScope _(process->page_table());
memcpy((void*)elf_program_header.p_vaddr, elf->data() + elf_program_header.p_offset, elf_program_header.p_filesz);
memset((void*)(elf_program_header.p_vaddr + elf_program_header.p_filesz), 0, elf_program_header.p_memsz - elf_program_header.p_filesz);
}
break;
}
default:
ASSERT_NOT_REACHED();
}
delete process;
return res.error();
}
char** argv = nullptr;
@ -103,13 +68,9 @@ namespace Kernel
process->m_userspace_entry.argc = 1;
process->m_userspace_entry.argv = argv;
process->m_userspace_entry.entry = elf_file_header.e_entry;
auto* thread = MUST(Thread::create_userspace(process));
process->add_thread(thread);
delete elf;
register_process(process);
return process;
}
@ -225,6 +186,110 @@ namespace Kernel
return forked;
}
BAN::ErrorOr<void> Process::exec(BAN::StringView path, const char* const* argv, const char* const* envp)
{
if (argv == nullptr)
return BAN::Error::from_errno(EFAULT);
// FIXME: implement environment variables
(void)envp;
LockGuard lock_guard(m_lock);
MUST(cleanup_and_load_elf(path));
ASSERT(m_threads.size() == 1);
ASSERT(&Process::current() == this);
int argc = 0;
while (argv[argc])
argc++;
{
PageTableScope _(page_table());
m_userspace_entry.argv = (char**)MUST(allocate(sizeof(char**) * (argc + 1)));
for (int i = 0; i < argc; i++)
{
size_t len = strlen(argv[i]);
m_userspace_entry.argv[i] = (char*)MUST(allocate(len + 1));
memcpy(m_userspace_entry.argv[i], argv[i], len + 1);
}
m_userspace_entry.argv[argc] = nullptr;
}
m_userspace_entry.argc = argc;
CriticalScope _;
lock_guard.~LockGuard();
m_threads.front()->setup_exec();
Scheduler::get().execute_current_thread();
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> Process::cleanup_and_load_elf(BAN::StringView path)
{
auto* elf = TRY(LibELF::ELF::load_from_file(path));
if (!elf->is_native())
{
derrorln("ELF has invalid architecture");
return BAN::Error::from_errno(EINVAL);
}
for (auto* allocator : m_fixed_width_allocators)
delete allocator;
m_fixed_width_allocators.clear();
if (m_general_allocator)
delete m_general_allocator;
m_general_allocator = nullptr;
for (auto* range : m_mapped_ranges)
delete range;
m_mapped_ranges.clear();
m_open_files.clear();
auto& elf_file_header = elf->file_header_native();
for (size_t i = 0; i < elf_file_header.e_phnum; i++)
{
auto& elf_program_header = elf->program_header_native(i);
switch (elf_program_header.p_type)
{
case LibELF::PT_NULL:
break;
case LibELF::PT_LOAD:
{
ASSERT(page_table().is_range_free(elf_program_header.p_vaddr, elf_program_header.p_memsz));
uint8_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (elf_program_header.p_flags & LibELF::PF_W)
flags |= PageTable::Flags::ReadWrite;
size_t page_start = elf_program_header.p_vaddr / PAGE_SIZE;
size_t page_end = BAN::Math::div_round_up<size_t>(elf_program_header.p_vaddr + elf_program_header.p_memsz, PAGE_SIZE);
size_t page_count = page_end - page_start + 1;
MUST(m_mapped_ranges.push_back(VirtualRange::create(page_table(), page_start * PAGE_SIZE, page_count * PAGE_SIZE, flags)));
{
PageTableScope _(page_table());
memcpy((void*)elf_program_header.p_vaddr, elf->data() + elf_program_header.p_offset, elf_program_header.p_filesz);
memset((void*)(elf_program_header.p_vaddr + elf_program_header.p_filesz), 0, elf_program_header.p_memsz - elf_program_header.p_filesz);
}
break;
}
default:
ASSERT_NOT_REACHED();
}
}
m_userspace_entry.entry = elf_file_header.e_entry;
delete elf;
return {};
}
BAN::ErrorOr<int> Process::open(BAN::StringView path, int flags)
{
if (flags & ~O_RDWR)

View File

@ -238,7 +238,6 @@ namespace Kernel
void Scheduler::set_current_thread_done()
{
VERIFY_STI();
DISABLE_INTERRUPTS();
load_temp_stack();

View File

@ -69,25 +69,7 @@ namespace Kernel
return BAN::Error::from_errno(ENOMEM);
}
// Setup registers and entry
static entry_t entry_trampoline(
[](void*)
{
const auto& entry = Process::current().userspace_entry();
thread_userspace_trampoline(Thread::current().rsp(), entry.entry, entry.argc, entry.argv);
ASSERT_NOT_REACHED();
}
);
thread->m_rsp = thread->stack_base() + thread->stack_size();
thread->m_rip = (uintptr_t)entry_trampoline;
// Setup stack for returning
{
PageTableScope _(process->page_table());
write_to_stack<sizeof(void*)>(thread->m_rsp, thread);
write_to_stack<sizeof(void*)>(thread->m_rsp, &Thread::on_exit);
write_to_stack<sizeof(void*)>(thread->m_rsp, nullptr);
}
thread->setup_exec();
return thread;
}
@ -141,6 +123,30 @@ namespace Kernel
return thread;
}
void Thread::setup_exec()
{
ASSERT(is_userspace());
m_state = State::NotStarted;
static entry_t entry_trampoline(
[](void*)
{
const auto& entry = Process::current().userspace_entry();
thread_userspace_trampoline(Thread::current().rsp(), entry.entry, entry.argc, entry.argv);
ASSERT_NOT_REACHED();
}
);
m_rsp = stack_base() + stack_size();
m_rip = (uintptr_t)entry_trampoline;
// Setup stack for returning
{
PageTableScope _(m_process->page_table());
write_to_stack<sizeof(void*)>(m_rsp, this);
write_to_stack<sizeof(void*)>(m_rsp, &Thread::on_exit);
write_to_stack<sizeof(void*)>(m_rsp, nullptr);
}
}
void Thread::validate_stack() const
{
if (stack_base() <= m_rsp && m_rsp <= stack_base() + stack_size())