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; }
|
||||
|
||||
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:
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -238,7 +238,6 @@ namespace Kernel
|
|||
|
||||
void Scheduler::set_current_thread_done()
|
||||
{
|
||||
VERIFY_STI();
|
||||
DISABLE_INTERRUPTS();
|
||||
|
||||
load_temp_stack();
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue