Kernel: Don't allocate userspace stacks in Thread

Thread only cares about stack's vaddr and size. Stacks are now allocated
before creating the threads.
This commit is contained in:
2026-07-02 19:38:55 +03:00
parent b19b7f064a
commit 6d1696b77e
4 changed files with 301 additions and 290 deletions

View File

@@ -44,10 +44,10 @@ namespace Kernel
~Process(); ~Process();
void cleanup_function(Thread*); void cleanup_function(Thread*);
void register_to_scheduler(); BAN::ErrorOr<vaddr_t> setup_initial_process_stack(MemoryBackedRegion&, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv);
void exit(int status, int signal); void exit(int status, int signal);
void add_thread(Thread*);
// returns true if thread was the last one // returns true if thread was the last one
bool on_thread_exit(Thread&); bool on_thread_exit(Thread&);

View File

@@ -41,22 +41,20 @@ namespace Kernel
// TODO: userspace stack size is hard limited, maybe make this dynamic? // TODO: userspace stack size is hard limited, maybe make this dynamic?
#if ARCH(x86_64) #if ARCH(x86_64)
static constexpr size_t userspace_stack_size { 32 << 20 }; static constexpr size_t userspace_stack_size { 32 << 20 };
static constexpr vaddr_t userspace_stack_base { 0x0000700000000000 };
#elif ARCH(i686) #elif ARCH(i686)
static constexpr size_t userspace_stack_size { 4 << 20 }; static constexpr size_t userspace_stack_size { 4 << 20 };
static constexpr vaddr_t userspace_stack_base { 0xB0000000 };
#endif #endif
public: public:
static BAN::ErrorOr<Thread*> create_kernel(entry_t, void*); static BAN::ErrorOr<Thread*> create_kernel(entry_t, void*);
static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&); static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&, vaddr_t userspace_stack_vaddr, size_t userspace_stack_size, vaddr_t entry_point, vaddr_t stack_pointer);
~Thread(); ~Thread();
BAN::ErrorOr<Thread*> thread_create(entry_t, void*);
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip); BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip);
void setup_process_cleanup(); void setup_process_cleanup();
BAN::ErrorOr<void> initialize_userspace(vaddr_t entry, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv);
// Returns true, if thread is going to trigger signal // Returns true, if thread is going to trigger signal
bool is_interrupted_by_signal(bool skip_stop_and_cont = false) const; bool is_interrupted_by_signal(bool skip_stop_and_cont = false) const;
@@ -112,8 +110,6 @@ namespace Kernel
vaddr_t kernel_stack_top() const { return m_kernel_stack->vaddr() + m_kernel_stack->size(); } vaddr_t kernel_stack_top() const { return m_kernel_stack->vaddr() + m_kernel_stack->size(); }
VirtualRange& kernel_stack() { return *m_kernel_stack; } VirtualRange& kernel_stack() { return *m_kernel_stack; }
MemoryBackedRegion& userspace_stack() { ASSERT(is_userspace() && m_userspace_stack); return *m_userspace_stack; }
static Thread& current(); static Thread& current();
static pid_t current_tid(); static pid_t current_tid();
@@ -157,8 +153,6 @@ namespace Kernel
private: private:
Thread(pid_t tid, Process*); Thread(pid_t tid, Process*);
void setup_exec(vaddr_t ip, vaddr_t sp);
static void on_exit_trampoline(Thread*); static void on_exit_trampoline(Thread*);
void on_exit(); void on_exit();
@@ -180,7 +174,6 @@ namespace Kernel
BAN::UniqPtr<PageTable> m_keep_alive_page_table; BAN::UniqPtr<PageTable> m_keep_alive_page_table;
BAN::UniqPtr<VirtualRange> m_kernel_stack; BAN::UniqPtr<VirtualRange> m_kernel_stack;
MemoryBackedRegion* m_userspace_stack { nullptr };
const pid_t m_tid { 0 }; const pid_t m_tid { 0 };
State m_state { State::NotStarted }; State m_state { State::NotStarted };
Process* m_process { nullptr }; Process* m_process { nullptr };
@@ -188,6 +181,9 @@ namespace Kernel
BAN::Atomic<bool> m_is_detached { false }; BAN::Atomic<bool> m_is_detached { false };
bool m_delete_process { false }; bool m_delete_process { false };
vaddr_t m_userspace_stack_vaddr { 0 };
size_t m_userspace_stack_size { 0 };
vaddr_t m_fsbase { 0 }; vaddr_t m_fsbase { 0 };
vaddr_t m_gsbase { 0 }; vaddr_t m_gsbase { 0 };

View File

@@ -97,25 +97,19 @@ namespace Kernel
return process; return process;
} }
void Process::register_to_scheduler()
{
// FIXME: Allow failing...
{
SpinLockGuard _(s_process_lock);
MUST(s_processes.push_back(this));
}
for (auto* thread : m_threads)
MUST(Processor::scheduler().add_thread(thread));
}
BAN::ErrorOr<Process*> Process::create_userspace(const Credentials& credentials, BAN::StringView path, BAN::Span<BAN::StringView> arguments) BAN::ErrorOr<Process*> Process::create_userspace(const Credentials& credentials, BAN::StringView path, BAN::Span<BAN::StringView> arguments)
{ {
auto* process = create_process(credentials, 0); auto* process = create_process(credentials, 0);
BAN::ScopeGuard process_deleter([process] {
process->m_mapped_regions.clear();
process->m_page_table.clear();
delete process;
});
process->m_working_directory = VirtualFileSystem::get().root_file(); process->m_working_directory = VirtualFileSystem::get().root_file();
process->m_root_file = VirtualFileSystem::get().root_file(); process->m_root_file = VirtualFileSystem::get().root_file();
process->m_page_table = BAN::UniqPtr<PageTable>::adopt(MUST(PageTable::create_userspace())); process->m_page_table = BAN::UniqPtr<PageTable>::adopt(TRY(PageTable::create_userspace()));
TRY(process->m_cmdline.emplace_back()); TRY(process->m_cmdline.emplace_back());
TRY(process->m_cmdline.back().append(path)); TRY(process->m_cmdline.back().append(path));
@@ -125,17 +119,12 @@ namespace Kernel
TRY(process->m_cmdline.back().append(argument)); TRY(process->m_cmdline.back().append(argument));
} }
auto* thread = TRY(Thread::create_userspace(process, process->page_table()));
BAN::ScopeGuard thread_deleter([thread] { delete thread; });
LockGuard _(process->m_process_lock);
auto executable_file = TRY(process->find_file(AT_FDCWD, path.data(), O_EXEC)); auto executable_file = TRY(process->find_file(AT_FDCWD, path.data(), O_EXEC));
auto executable_inode = executable_file.inode; auto executable_inode = executable_file.inode;
auto executable = TRY(ELF::load_from_inode(process->m_root_file.inode, executable_inode, process->m_credentials, process->page_table())); auto executable = TRY(ELF::load_from_inode(process->m_root_file.inode, executable_inode, process->m_credentials, process->page_table()));
for (auto& region : executable.regions) for (auto& region : executable.regions)
TRY(process->add_mapped_region(BAN::move(region))); TRY(process->m_mapped_regions.push_back(BAN::move(region)));
executable.regions.clear(); executable.regions.clear();
TRY(process->m_executable.append(executable_file.canonical_path)); TRY(process->m_executable.append(executable_file.canonical_path));
@@ -145,6 +134,24 @@ namespace Kernel
if (executable_inode->mode().mode & +Inode::Mode::ISGID) if (executable_inode->mode().mode & +Inode::Mode::ISGID)
process->m_credentials.set_egid(executable_inode->gid()); process->m_credentials.set_egid(executable_inode->gid());
process->m_shared_page_vaddr = process->page_table().reserve_free_page(executable.interp_base.value_or(0x400000), USERSPACE_END);
if (process->m_shared_page_vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
process->page_table().map_page_at(
Processor::shared_page_paddr(),
process->m_shared_page_vaddr,
PageTable::UserSupervisor | PageTable::Present
);
auto userspace_stack = TRY(MemoryBackedRegion::create(
process->page_table(),
Thread::userspace_stack_size,
{ Thread::userspace_stack_base, USERSPACE_END },
MemoryRegion::Type::PRIVATE,
PageTable::UserSupervisor | PageTable::ReadWrite | PageTable::Present,
O_RDWR
));
BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector; BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector;
TRY(auxiliary_vector.reserve(5 + 2 * executable.interp_base.has_value())); TRY(auxiliary_vector.reserve(5 + 2 * executable.interp_base.has_value()));
@@ -161,15 +168,6 @@ namespace Kernel
})); }));
} }
process->m_shared_page_vaddr = process->page_table().reserve_free_page(executable.interp_base.value_or(0x400000), USERSPACE_END);
if (process->m_shared_page_vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
process->page_table().map_page_at(
Processor::shared_page_paddr(),
process->m_shared_page_vaddr,
PageTable::UserSupervisor | PageTable::Present
);
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_PAGESZ, .a_type = LibELF::AT_PAGESZ,
.a_un = { .a_val = PAGE_SIZE }, .a_un = { .a_val = PAGE_SIZE },
@@ -182,12 +180,12 @@ namespace Kernel
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_STACK_BASE, .a_type = LibELF::AT_STACK_BASE,
.a_un = { .a_ptr = reinterpret_cast<void*>(thread->userspace_stack().vaddr()) }, .a_un = { .a_ptr = reinterpret_cast<void*>(userspace_stack->vaddr()) },
})); }));
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_STACK_SIZE, .a_type = LibELF::AT_STACK_SIZE,
.a_un = { .a_ptr = reinterpret_cast<void*>(thread->userspace_stack().size()) }, .a_un = { .a_ptr = reinterpret_cast<void*>(userspace_stack->size()) },
})); }));
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
@@ -195,20 +193,39 @@ namespace Kernel
.a_un = { .a_val = 0 }, .a_un = { .a_val = 0 },
})); }));
BAN::Optional<vaddr_t> tls_addr; const vaddr_t initial_stack_pointer = TRY(process->setup_initial_process_stack(
if (executable.master_tls.has_value()) *userspace_stack,
{
auto tls_result = TRY(process->initialize_thread_local_storage(process->page_table(), *executable.master_tls));
TRY(process->add_mapped_region(BAN::move(tls_result.region)));
tls_addr = tls_result.addr;
}
TRY(thread->initialize_userspace(
executable.entry_point,
process->m_cmdline.span(), process->m_cmdline.span(),
process->m_environ.span(), process->m_environ.span(),
auxiliary_vector.span() auxiliary_vector.span()
)); ));
const vaddr_t userspace_stack_vaddr = userspace_stack->vaddr();
const vaddr_t userspace_stack_size = userspace_stack->size();
TRY(process->m_mapped_regions.push_back(BAN::move(userspace_stack)));
BAN::Optional<vaddr_t> tls_addr;
if (executable.master_tls.has_value())
{
auto tls_result = TRY(process->initialize_thread_local_storage(process->page_table(), *executable.master_tls));
TRY(process->m_mapped_regions.push_back(BAN::move(tls_result.region)));
tls_addr = tls_result.addr;
}
BAN::sort::sort(process->m_mapped_regions.begin(), process->m_mapped_regions.end(), [](const auto& a, const auto& b) {
return a->vaddr() < b->vaddr();
});
auto* thread = TRY(Thread::create_userspace(
process,
process->page_table(),
userspace_stack_vaddr,
userspace_stack_size,
executable.entry_point,
initial_stack_pointer
));
BAN::ScopeGuard thread_deleter([thread] { delete thread; });
if (tls_addr.has_value()) if (tls_addr.has_value())
{ {
#if ARCH(x86_64) #if ARCH(x86_64)
@@ -220,12 +237,118 @@ namespace Kernel
#endif #endif
} }
// NOTE: make sure the last two `MUST`s don't fail
TRY(process->m_threads.reserve(1));
TRY(Processor::scheduler().bind_thread_to_processor(thread, Processor::current_id()));
{
SpinLockGuard _(s_process_lock);
TRY(s_processes.push_back(process));
}
MUST(process->m_threads.push_back(thread));
MUST(Processor::scheduler().add_thread(thread));
process_deleter.disable();
thread_deleter.disable(); thread_deleter.disable();
process->add_thread(thread);
process->register_to_scheduler();
return process; return process;
} }
BAN::ErrorOr<vaddr_t> Process::setup_initial_process_stack(MemoryBackedRegion& stack_region, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv)
{
// System V ABI: Initial process stack
size_t needed_size = 0;
// argc
needed_size += sizeof(uintptr_t);
// argv
needed_size += (argv.size() + 1) * sizeof(uintptr_t);
for (auto arg : argv)
needed_size += arg.size() + 1;
// envp
needed_size += (envp.size() + 1) * sizeof(uintptr_t);
for (auto env : envp)
needed_size += env.size() + 1;
// auxv
needed_size += auxv.size() * sizeof(LibELF::AuxiliaryVector);
if (auto rem = needed_size % alignof(char*))
needed_size += alignof(char*) - rem;
if (needed_size > stack_region.size())
return BAN::Error::from_errno(ENOBUFS);
vaddr_t vaddr = stack_region.vaddr() + stack_region.size() - needed_size;
const size_t page_count = BAN::Math::div_round_up<size_t>(needed_size, PAGE_SIZE);
for (size_t i = 0; i < page_count; i++)
TRY(stack_region.allocate_page_containing(vaddr + i * PAGE_SIZE, true));
const auto stack_copy_buf =
[&stack_region](BAN::ConstByteSpan buffer, vaddr_t vaddr) -> void
{
ASSERT(vaddr >= stack_region.vaddr());
ASSERT(vaddr + buffer.size() <= stack_region.vaddr() + stack_region.size());
MUST(stack_region.copy_data_to_region(vaddr - stack_region.vaddr(), buffer.data(), buffer.size()));
};
const auto stack_push_buf =
[&stack_copy_buf, &vaddr](BAN::ConstByteSpan buffer) -> void
{
stack_copy_buf(buffer, vaddr);
vaddr += buffer.size();
};
const auto stack_push_uint =
[&stack_push_buf](uintptr_t value) -> void
{
stack_push_buf(BAN::ConstByteSpan::from(value));
};
const auto stack_push_str =
[&stack_push_buf](BAN::StringView string) -> void
{
const uint8_t* string_u8 = reinterpret_cast<const uint8_t*>(string.data());
stack_push_buf(BAN::ConstByteSpan(string_u8, string.size() + 1));
};
// argc
stack_push_uint(argv.size());
// argv
const vaddr_t argv_vaddr = vaddr;
vaddr += argv.size() * sizeof(uintptr_t);
stack_push_uint(0);
// envp
const vaddr_t envp_vaddr = vaddr;
vaddr += envp.size() * sizeof(uintptr_t);
stack_push_uint(0);
// auxv
for (auto aux : auxv)
stack_push_buf(BAN::ConstByteSpan::from(aux));
// information
for (size_t i = 0; i < argv.size(); i++)
{
stack_copy_buf(BAN::ConstByteSpan::from(vaddr), argv_vaddr + i * sizeof(uintptr_t));
stack_push_str(argv[i]);
}
for (size_t i = 0; i < envp.size(); i++)
{
stack_copy_buf(BAN::ConstByteSpan::from(vaddr), envp_vaddr + i * sizeof(uintptr_t));
stack_push_str(envp[i]);
}
return stack_region.vaddr() + stack_region.size() - needed_size;
}
Process::Process(const Credentials& credentials, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp) Process::Process(const Credentials& credentials, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp)
: m_credentials(credentials) : m_credentials(credentials)
, m_open_file_descriptors(m_credentials) , m_open_file_descriptors(m_credentials)
@@ -266,12 +389,6 @@ namespace Kernel
return valid_pgrp; return valid_pgrp;
} }
void Process::add_thread(Thread* thread)
{
LockGuard _(m_process_lock);
MUST(m_threads.push_back(thread));
}
void Process::cleanup_function(Thread* thread) void Process::cleanup_function(Thread* thread)
{ {
{ {
@@ -304,21 +421,14 @@ namespace Kernel
// NOTE: We must unmap ranges while the page table is still alive // NOTE: We must unmap ranges while the page table is still alive
m_mapped_regions.clear(); m_mapped_regions.clear();
// After we give our page table to the thread, we cannot get rescheduled
Processor::set_interrupt_state(InterruptState::Disabled);
thread->give_keep_alive_page_table(BAN::move(m_page_table)); thread->give_keep_alive_page_table(BAN::move(m_page_table));
} }
bool Process::on_thread_exit(Thread& thread) bool Process::on_thread_exit(Thread& thread)
{ {
{ // TODO: if main thread exists, should we delete its stack?
RWLockWRGuard _(m_memory_region_lock);
const size_t index = find_mapped_region(thread.userspace_stack().vaddr());
ASSERT(m_mapped_regions[index].ptr() == thread.m_userspace_stack);
m_mapped_regions.remove(index);
thread.m_userspace_stack = nullptr;
}
LockGuard _(m_process_lock); LockGuard _(m_process_lock);
@@ -830,12 +940,23 @@ namespace Kernel
BAN::String executable_path; BAN::String executable_path;
TRY(executable_path.append(executable_file.canonical_path)); TRY(executable_path.append(executable_file.canonical_path));
// This is ugly but thread insterts userspace stack to process' memory region const vaddr_t shared_page_vaddr = new_page_table->reserve_free_page(executable.interp_base.value_or(0x400000), USERSPACE_END);
BAN::swap(m_mapped_regions, new_mapped_regions); if (shared_page_vaddr == 0)
auto new_thread_or_error = Thread::create_userspace(this, *new_page_table); return BAN::Error::from_errno(ENOMEM);
BAN::swap(m_mapped_regions, new_mapped_regions); new_page_table->map_page_at(
auto* new_thread = TRY(new_thread_or_error); Processor::shared_page_paddr(),
BAN::ScopeGuard new_thread_deleter([new_thread] { delete new_thread; }); shared_page_vaddr,
PageTable::UserSupervisor | PageTable::Present
);
auto userspace_stack = TRY(MemoryBackedRegion::create(
*new_page_table,
Thread::userspace_stack_size,
{ Thread::userspace_stack_base, USERSPACE_END },
MemoryRegion::Type::PRIVATE,
PageTable::UserSupervisor | PageTable::ReadWrite | PageTable::Present,
O_RDWR
));
BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector; BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector;
TRY(auxiliary_vector.reserve(5 + 2 * executable.interp_base.has_value())); TRY(auxiliary_vector.reserve(5 + 2 * executable.interp_base.has_value()));
@@ -860,16 +981,6 @@ namespace Kernel
.a_un = { .a_ptr = reinterpret_cast<void*>(executable.interp_base.value()) }, .a_un = { .a_ptr = reinterpret_cast<void*>(executable.interp_base.value()) },
})); }));
} }
const vaddr_t shared_page_vaddr = new_page_table->reserve_free_page(executable.interp_base.value_or(0x400000), USERSPACE_END);
if (shared_page_vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
new_page_table->map_page_at(
Processor::shared_page_paddr(),
shared_page_vaddr,
PageTable::UserSupervisor | PageTable::Present
);
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_PAGESZ, .a_type = LibELF::AT_PAGESZ,
.a_un = { .a_val = PAGE_SIZE }, .a_un = { .a_val = PAGE_SIZE },
@@ -882,12 +993,12 @@ namespace Kernel
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_STACK_BASE, .a_type = LibELF::AT_STACK_BASE,
.a_un = { .a_ptr = reinterpret_cast<void*>(new_thread->userspace_stack().vaddr()) }, .a_un = { .a_ptr = reinterpret_cast<void*>(userspace_stack->vaddr()) },
})); }));
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_STACK_SIZE, .a_type = LibELF::AT_STACK_SIZE,
.a_un = { .a_ptr = reinterpret_cast<void*>(new_thread->userspace_stack().size()) }, .a_un = { .a_ptr = reinterpret_cast<void*>(userspace_stack->size()) },
})); }));
TRY(auxiliary_vector.push_back({ TRY(auxiliary_vector.push_back({
@@ -895,30 +1006,57 @@ namespace Kernel
.a_un = { .a_val = 0 }, .a_un = { .a_val = 0 },
})); }));
TRY(new_thread->initialize_userspace( const vaddr_t initial_stack_pointer = TRY(setup_initial_process_stack(
executable.entry_point, *userspace_stack,
str_argv.span(), str_argv.span(),
str_envp.span(), str_envp.span(),
auxiliary_vector.span() auxiliary_vector.span()
)); ));
const vaddr_t userspace_stack_vaddr = userspace_stack->vaddr();
const vaddr_t userspace_stack_size = userspace_stack->size();
TRY(new_mapped_regions.emplace_back(BAN::move(userspace_stack)));
BAN::Optional<vaddr_t> tls_addr;
if (executable.master_tls.has_value()) if (executable.master_tls.has_value())
{ {
auto tls_result = TRY(initialize_thread_local_storage(*new_page_table, *executable.master_tls)); auto tls_result = TRY(initialize_thread_local_storage(*new_page_table, *executable.master_tls));
TRY(new_mapped_regions.emplace_back(BAN::move(tls_result.region))); TRY(new_mapped_regions.emplace_back(BAN::move(tls_result.region)));
#if ARCH(x86_64) tls_addr = tls_result.addr;
new_thread->set_fsbase(tls_result.addr);
#elif ARCH(i686)
new_thread->set_gsbase(tls_result.addr);
#else
#error
#endif
} }
BAN::sort::sort(new_mapped_regions.begin(), new_mapped_regions.end(), [](auto& a, auto& b) { BAN::sort::sort(new_mapped_regions.begin(), new_mapped_regions.end(), [](auto& a, auto& b) {
return a->vaddr() < b->vaddr(); return a->vaddr() < b->vaddr();
}); });
auto* new_thread = TRY(Thread::create_userspace(
this,
*new_page_table,
userspace_stack_vaddr,
userspace_stack_size,
executable.entry_point,
initial_stack_pointer
));
if (tls_addr.has_value())
{
#if ARCH(x86_64)
new_thread->set_fsbase(tls_addr.value());
#elif ARCH(i686)
new_thread->set_gsbase(tls_addr.value());
#else
#error
#endif
}
// NOTE: bind new thread to this processor so it wont be rescheduled before end of this function
// and so that adding the thread to the scheduler cannot fail
if (auto ret = Scheduler::bind_thread_to_processor(new_thread, Processor::current_id()); ret.is_error())
{
Processor::set_interrupt_state(InterruptState::Enabled);
delete new_thread;
return ret.release_error();
}
RWLockWRGuard wr_guard(m_memory_region_lock); RWLockWRGuard wr_guard(m_memory_region_lock);
// NOTE: this is done before disabling interrupts and moving the threads as // NOTE: this is done before disabling interrupts and moving the threads as
@@ -929,13 +1067,6 @@ namespace Kernel
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled); ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
Processor::set_interrupt_state(InterruptState::Disabled); Processor::set_interrupt_state(InterruptState::Disabled);
// NOTE: bind new thread to this processor so it wont be rescheduled before end of this function
if (auto ret = Scheduler::bind_thread_to_processor(new_thread, Processor::current_id()); ret.is_error())
{
Processor::set_interrupt_state(InterruptState::Enabled);
return ret.release_error();
}
// after this point, everything is initialized and nothing can fail! // after this point, everything is initialized and nothing can fail!
ASSERT(m_threads.size() == 1); ASSERT(m_threads.size() == 1);
@@ -948,7 +1079,6 @@ namespace Kernel
m_threads.front()->m_process = nullptr; m_threads.front()->m_process = nullptr;
m_threads.front()->give_keep_alive_page_table(BAN::move(m_page_table)); m_threads.front()->give_keep_alive_page_table(BAN::move(m_page_table));
new_thread_deleter.disable();
MUST(Processor::scheduler().add_thread(new_thread)); MUST(Processor::scheduler().add_thread(new_thread));
m_threads.front() = new_thread; m_threads.front() = new_thread;
@@ -3402,13 +3532,43 @@ namespace Kernel
BAN::ErrorOr<long> Process::sys_thread_create(void (*entry)(void*), void* arg) BAN::ErrorOr<long> Process::sys_thread_create(void (*entry)(void*), void* arg)
{ {
auto userspace_stack = TRY(MemoryBackedRegion::create(
page_table(),
Thread::userspace_stack_size,
{ Thread::userspace_stack_base, USERSPACE_END },
MemoryRegion::Type::PRIVATE,
PageTable::UserSupervisor | PageTable::ReadWrite | PageTable::Present,
O_RDWR
));
const vaddr_t stack_vaddr = userspace_stack->vaddr();
const size_t stack_size = userspace_stack->size();
TRY(add_mapped_region(BAN::move(userspace_stack)));
const vaddr_t initial_stack_pointer = stack_vaddr + stack_size - sizeof(void*);
*reinterpret_cast<void**>(initial_stack_pointer) = arg;
auto* thread = TRY(Thread::create_userspace(
this,
page_table(),
stack_vaddr,
stack_size,
reinterpret_cast<vaddr_t>(entry),
initial_stack_pointer
));
thread->m_signal_block_mask = Thread::current().m_signal_block_mask;
LockGuard _(m_process_lock); LockGuard _(m_process_lock);
auto* new_thread = TRY(Thread::current().thread_create(entry, arg)); TRY(m_threads.push_back(thread));
MUST(m_threads.push_back(new_thread)); if (auto ret = Processor::scheduler().add_thread(thread); ret.is_error())
MUST(Processor::scheduler().add_thread(new_thread)); {
m_threads.pop_back();
delete thread;
return ret.release_error();
}
return new_thread->tid(); return thread->tid();
} }
BAN::ErrorOr<long> Process::sys_thread_exit(void* value) BAN::ErrorOr<long> Process::sys_thread_exit(void* value)

View File

@@ -18,12 +18,6 @@ namespace Kernel
static_assert(SYS_SIGPROCMASK == 78, "this is hard coded in arch/*/Signal.S"); static_assert(SYS_SIGPROCMASK == 78, "this is hard coded in arch/*/Signal.S");
static_assert(SIG_SETMASK == 3, "this is hard coded in arch/*/Signal.S"); static_assert(SIG_SETMASK == 3, "this is hard coded in arch/*/Signal.S");
#if ARCH(x86_64)
static constexpr vaddr_t s_user_stack_addr_start = 0x0000700000000000;
#elif ARCH(i686)
static constexpr vaddr_t s_user_stack_addr_start = 0xB0000000;
#endif
extern "C" [[noreturn]] void start_kernel_thread(); extern "C" [[noreturn]] void start_kernel_thread();
extern "C" [[noreturn]] void start_userspace_thread(); extern "C" [[noreturn]] void start_userspace_thread();
@@ -198,7 +192,7 @@ namespace Kernel
return thread; return thread;
} }
BAN::ErrorOr<Thread*> Thread::create_userspace(Process* process, PageTable& page_table) BAN::ErrorOr<Thread*> Thread::create_userspace(Process* process, PageTable& page_table, vaddr_t userspace_stack_vaddr, size_t userspace_stack_size, vaddr_t entry_point, vaddr_t stack_pointer)
{ {
ASSERT(process); ASSERT(process);
@@ -212,22 +206,32 @@ namespace Kernel
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range( thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
page_table, page_table,
{ s_user_stack_addr_start, USERSPACE_END }, { userspace_stack_base, USERSPACE_END },
kernel_stack_size, kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present, PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true true
)); ));
auto userspace_stack = TRY(MemoryBackedRegion::create( thread->m_userspace_stack_vaddr = userspace_stack_vaddr;
page_table, thread->m_userspace_stack_size = userspace_stack_size;
userspace_stack_size,
{ s_user_stack_addr_start, USERSPACE_END }, // Initialize stack for returning
MemoryRegion::Type::PRIVATE, PageTable::with_fast_page(thread->kernel_stack().paddr_of(thread->kernel_stack_top() - PAGE_SIZE), [=] {
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present, uintptr_t cur_sp = PageTable::fast_page() + PAGE_SIZE;
O_RDWR write_to_stack(cur_sp, 0x20 | 3);
)); write_to_stack(cur_sp, stack_pointer);
thread->m_userspace_stack = userspace_stack.ptr(); write_to_stack(cur_sp, 0x202);
TRY(process->add_mapped_region(BAN::move(userspace_stack))); #if ARCH(x86_64)
write_to_stack(cur_sp, 0x28 | 3);
#elif ARCH(i686)
write_to_stack(cur_sp, 0x18 | 3);
#endif
write_to_stack(cur_sp, entry_point);
});
thread->m_yield_registers = {};
thread->m_yield_registers.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
thread->m_yield_registers.sp = thread->kernel_stack_top() - 5 * sizeof(uintptr_t);
thread_deleter.disable(); thread_deleter.disable();
@@ -311,26 +315,6 @@ namespace Kernel
Processor::gdt().set_cpu_index(Processor::current_index()); Processor::gdt().set_cpu_index(Processor::current_index());
} }
BAN::ErrorOr<Thread*> Thread::thread_create(entry_t entry, void* arg)
{
auto* thread = TRY(create_userspace(m_process, m_process->page_table()));
if (Processor::get_current_sse_thread() == this)
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
TRY(thread->userspace_stack().copy_data_to_region(
thread->m_userspace_stack->size() - sizeof(void*),
reinterpret_cast<const uint8_t*>(&arg),
sizeof(void*)
));
const vaddr_t entry_addr = reinterpret_cast<vaddr_t>(entry);
thread->setup_exec(entry_addr, thread->userspace_stack().vaddr() + thread->userspace_stack().size() - sizeof(void*));
return thread;
}
BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip) BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip)
{ {
ASSERT(m_is_userspace); ASSERT(m_is_userspace);
@@ -345,7 +329,7 @@ namespace Kernel
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range( thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
new_process->page_table(), new_process->page_table(),
{ s_user_stack_addr_start, USERSPACE_END }, { userspace_stack_base, USERSPACE_END },
kernel_stack_size, kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present, PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true true
@@ -362,9 +346,8 @@ namespace Kernel
); );
}); });
const auto stack_index = new_process->find_mapped_region(m_userspace_stack->vaddr()); thread->m_userspace_stack_vaddr = m_userspace_stack_vaddr;
thread->m_userspace_stack = static_cast<MemoryBackedRegion*>(new_process->m_mapped_regions[stack_index].ptr()); thread->m_userspace_stack_size = m_userspace_stack_size;
ASSERT(thread->m_userspace_stack->vaddr() == m_userspace_stack->vaddr());
thread->m_fsbase = m_fsbase; thread->m_fsbase = m_fsbase;
thread->m_gsbase = m_gsbase; thread->m_gsbase = m_gsbase;
@@ -385,142 +368,13 @@ namespace Kernel
return thread; return thread;
} }
BAN::ErrorOr<void> Thread::initialize_userspace(vaddr_t entry, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv)
{
// System V ABI: Initial process stack
ASSERT(m_is_userspace);
ASSERT(m_userspace_stack);
size_t needed_size = 0;
// argc
needed_size += sizeof(uintptr_t);
// argv
needed_size += (argv.size() + 1) * sizeof(uintptr_t);
for (auto arg : argv)
needed_size += arg.size() + 1;
// envp
needed_size += (envp.size() + 1) * sizeof(uintptr_t);
for (auto env : envp)
needed_size += env.size() + 1;
// auxv
needed_size += auxv.size() * sizeof(LibELF::AuxiliaryVector);
if (auto rem = needed_size % alignof(char*))
needed_size += alignof(char*) - rem;
if (needed_size > m_userspace_stack->size())
return BAN::Error::from_errno(ENOBUFS);
vaddr_t vaddr = userspace_stack().vaddr() + userspace_stack().size() - needed_size;
const size_t page_count = BAN::Math::div_round_up<size_t>(needed_size, PAGE_SIZE);
for (size_t i = 0; i < page_count; i++)
TRY(m_userspace_stack->allocate_page_containing(vaddr + i * PAGE_SIZE, true));
const auto stack_copy_buf =
[this](BAN::ConstByteSpan buffer, vaddr_t vaddr) -> void
{
ASSERT(vaddr >= m_userspace_stack->vaddr());
ASSERT(vaddr + buffer.size() <= m_userspace_stack->vaddr() + m_userspace_stack->size());
MUST(m_userspace_stack->copy_data_to_region(vaddr - m_userspace_stack->vaddr(), buffer.data(), buffer.size()));
};
const auto stack_push_buf =
[&stack_copy_buf, &vaddr](BAN::ConstByteSpan buffer) -> void
{
stack_copy_buf(buffer, vaddr);
vaddr += buffer.size();
};
const auto stack_push_uint =
[&stack_push_buf](uintptr_t value) -> void
{
stack_push_buf(BAN::ConstByteSpan::from(value));
};
const auto stack_push_str =
[&stack_push_buf](BAN::StringView string) -> void
{
const uint8_t* string_u8 = reinterpret_cast<const uint8_t*>(string.data());
stack_push_buf(BAN::ConstByteSpan(string_u8, string.size() + 1));
};
// argc
stack_push_uint(argv.size());
// argv
const vaddr_t argv_vaddr = vaddr;
vaddr += argv.size() * sizeof(uintptr_t);
stack_push_uint(0);
// envp
const vaddr_t envp_vaddr = vaddr;
vaddr += envp.size() * sizeof(uintptr_t);
stack_push_uint(0);
// auxv
for (auto aux : auxv)
stack_push_buf(BAN::ConstByteSpan::from(aux));
// information
for (size_t i = 0; i < argv.size(); i++)
{
stack_copy_buf(BAN::ConstByteSpan::from(vaddr), argv_vaddr + i * sizeof(uintptr_t));
stack_push_str(argv[i]);
}
for (size_t i = 0; i < envp.size(); i++)
{
stack_copy_buf(BAN::ConstByteSpan::from(vaddr), envp_vaddr + i * sizeof(uintptr_t));
stack_push_str(envp[i]);
}
setup_exec(entry, m_userspace_stack->vaddr() + m_userspace_stack->size() - needed_size);
return {};
}
void Thread::setup_exec(vaddr_t ip, vaddr_t sp)
{
ASSERT(is_userspace());
m_state = State::NotStarted;
// Signal mask is inherited
// Initialize stack for returning
PageTable::with_fast_page(kernel_stack().paddr_of(kernel_stack_top() - PAGE_SIZE), [=] {
uintptr_t cur_sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(cur_sp, 0x20 | 3);
write_to_stack(cur_sp, sp);
write_to_stack(cur_sp, 0x202);
#if ARCH(x86_64)
write_to_stack(cur_sp, 0x28 | 3);
#elif ARCH(i686)
write_to_stack(cur_sp, 0x18 | 3);
#endif
write_to_stack(cur_sp, ip);
});
m_yield_registers = {};
m_yield_registers.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
m_yield_registers.sp = kernel_stack_top() - 5 * sizeof(uintptr_t);
}
void Thread::setup_process_cleanup() void Thread::setup_process_cleanup()
{ {
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled); ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
m_state = State::NotStarted; static entry_t entry = [](void* process_ptr) {
static entry_t entry(
[](void* process_ptr)
{
auto* thread = &Thread::current(); auto* thread = &Thread::current();
auto* process = static_cast<Process*>(process_ptr); auto* process = static_cast<Process*>(process_ptr);
ASSERT(thread->m_process == process); ASSERT(thread->m_process == process);
process->cleanup_function(thread); process->cleanup_function(thread);
@@ -528,8 +382,9 @@ namespace Kernel
thread->m_delete_process = true; thread->m_delete_process = true;
// will call on thread exit after return // will call on thread exit after return
} };
);
m_state = State::NotStarted;
m_signal_pending_mask = 0; m_signal_pending_mask = 0;
m_signal_block_mask = ~0ull; m_signal_block_mask = ~0ull;