Kernel: Implement Process::exec()
This commit is contained in:
parent
5fb69300ca
commit
b48b239882
|
@ -47,6 +47,7 @@ namespace Kernel
|
||||||
pid_t pid() const { return m_pid; }
|
pid_t pid() const { return m_pid; }
|
||||||
|
|
||||||
BAN::ErrorOr<Process*> fork(uintptr_t rsp, uintptr_t rip);
|
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<int> open(BAN::StringView, int);
|
||||||
BAN::ErrorOr<void> close(int fd);
|
BAN::ErrorOr<void> close(int fd);
|
||||||
|
@ -85,6 +86,8 @@ namespace Kernel
|
||||||
static Process* create_process();
|
static Process* create_process();
|
||||||
static void register_process(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;
|
BAN::ErrorOr<BAN::String> absolute_path_of(BAN::StringView) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace Kernel
|
||||||
~Thread();
|
~Thread();
|
||||||
|
|
||||||
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t rsp, uintptr_t rip);
|
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t rsp, uintptr_t rip);
|
||||||
|
void setup_exec();
|
||||||
|
|
||||||
pid_t tid() const { return m_tid; }
|
pid_t tid() const { return m_tid; }
|
||||||
|
|
||||||
|
|
|
@ -46,49 +46,14 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<Process*> Process::create_userspace(BAN::StringView path)
|
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();
|
auto* process = create_process();
|
||||||
MUST(process->m_working_directory.push_back('/'));
|
MUST(process->m_working_directory.push_back('/'));
|
||||||
process->m_page_table = MUST(PageTable::create_userspace());
|
process->m_page_table = MUST(PageTable::create_userspace());
|
||||||
|
|
||||||
auto& elf_file_header = elf->file_header_native();
|
if (auto res = process->cleanup_and_load_elf(path); res.is_error())
|
||||||
for (size_t i = 0; i < elf_file_header.e_phnum; i++)
|
|
||||||
{
|
{
|
||||||
auto& elf_program_header = elf->program_header_native(i);
|
delete process;
|
||||||
|
return res.error();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char** argv = nullptr;
|
char** argv = nullptr;
|
||||||
|
@ -103,13 +68,9 @@ namespace Kernel
|
||||||
|
|
||||||
process->m_userspace_entry.argc = 1;
|
process->m_userspace_entry.argc = 1;
|
||||||
process->m_userspace_entry.argv = argv;
|
process->m_userspace_entry.argv = argv;
|
||||||
process->m_userspace_entry.entry = elf_file_header.e_entry;
|
|
||||||
|
|
||||||
auto* thread = MUST(Thread::create_userspace(process));
|
auto* thread = MUST(Thread::create_userspace(process));
|
||||||
process->add_thread(thread);
|
process->add_thread(thread);
|
||||||
|
|
||||||
delete elf;
|
|
||||||
|
|
||||||
register_process(process);
|
register_process(process);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
@ -225,6 +186,110 @@ namespace Kernel
|
||||||
return forked;
|
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)
|
BAN::ErrorOr<int> Process::open(BAN::StringView path, int flags)
|
||||||
{
|
{
|
||||||
if (flags & ~O_RDWR)
|
if (flags & ~O_RDWR)
|
||||||
|
|
|
@ -238,7 +238,6 @@ namespace Kernel
|
||||||
|
|
||||||
void Scheduler::set_current_thread_done()
|
void Scheduler::set_current_thread_done()
|
||||||
{
|
{
|
||||||
VERIFY_STI();
|
|
||||||
DISABLE_INTERRUPTS();
|
DISABLE_INTERRUPTS();
|
||||||
|
|
||||||
load_temp_stack();
|
load_temp_stack();
|
||||||
|
|
|
@ -69,25 +69,7 @@ namespace Kernel
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup registers and entry
|
thread->setup_exec();
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
@ -141,6 +123,30 @@ namespace Kernel
|
||||||
return thread;
|
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
|
void Thread::validate_stack() const
|
||||||
{
|
{
|
||||||
if (stack_base() <= m_rsp && m_rsp <= stack_base() + stack_size())
|
if (stack_base() <= m_rsp && m_rsp <= stack_base() + stack_size())
|
||||||
|
|
Loading…
Reference in New Issue