diff --git a/kernel/elf.cpp b/kernel/elf.cpp
index 5c6acca7..af05c7d9 100644
--- a/kernel/elf.cpp
+++ b/kernel/elf.cpp
@@ -116,8 +116,11 @@ addr_t Construct32(Process* process, const uint8_t* file, size_t filelen,
uintptr_t desc = note + 12 + namesz_aligned;
if ( strcmp(name, "Sortix") == 0 )
{
- (void) desc;
- (void) type;
+ if ( type == ELF_NOTE_SORTIX_UTHREAD_SIZE )
+ {
+ aux->uthread_size = *(uint32_t*) (desc + 0);
+ aux->uthread_align = *(uint32_t*) (desc + 4);
+ }
}
}
continue;
@@ -318,8 +321,15 @@ addr_t Construct64(Process* process, const uint8_t* file, size_t filelen,
uintptr_t desc = note + 12 + namesz_aligned;
if ( strcmp(name, "Sortix") == 0 )
{
- (void) desc;
- (void) type;
+ if ( type == ELF_NOTE_SORTIX_UTHREAD_SIZE )
+ {
+ uint64_t size_low = *((uint32_t*) (desc + 0));
+ uint64_t size_high = *((uint32_t*) (desc + 4));
+ aux->uthread_size = size_low << 0 | size_high << 32;
+ uint64_t align_low = *((uint32_t*) (desc + 8));
+ uint64_t align_high = *((uint32_t*) (desc + 12));
+ aux->uthread_align = align_low << 0 | align_high << 32;
+ }
}
}
continue;
diff --git a/kernel/elf.h b/kernel/elf.h
index 4448c6ec..d3a13bd3 100644
--- a/kernel/elf.h
+++ b/kernel/elf.h
@@ -40,6 +40,8 @@ struct Auxiliary
size_t tls_file_size;
size_t tls_mem_size;
size_t tls_mem_align;
+ size_t uthread_size;
+ size_t uthread_align;
};
struct Header
diff --git a/kernel/include/sortix/elf-note.h b/kernel/include/sortix/elf-note.h
index 6d11a46f..37cf9266 100644
--- a/kernel/include/sortix/elf-note.h
+++ b/kernel/include/sortix/elf-note.h
@@ -29,6 +29,8 @@
__BEGIN_DECLS
+#define ELF_NOTE_SORTIX_UTHREAD_SIZE 0x10
+
__END_DECLS
#endif
diff --git a/kernel/include/sortix/kernel/process.h b/kernel/include/sortix/kernel/process.h
index 6fc35508..c09434a7 100644
--- a/kernel/include/sortix/kernel/process.h
+++ b/kernel/include/sortix/kernel/process.h
@@ -50,6 +50,7 @@ struct ProcessSegment;
struct ProcessTimer;
struct ioctx_struct;
typedef struct ioctx_struct ioctx_t;
+struct segment;
struct Symbol;
class Process
@@ -168,6 +169,8 @@ public:
void AddChildProcess(Process* child);
void ScheduleDeath();
void AbortConstruction();
+ bool MapSegment(struct segment* result, void* hint, size_t size, int flags,
+ int prot);
public:
Process* Fork();
diff --git a/kernel/include/sortix/uthread.h b/kernel/include/sortix/uthread.h
new file mode 100644
index 00000000..a2049741
--- /dev/null
+++ b/kernel/include/sortix/uthread.h
@@ -0,0 +1,61 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2014.
+
+ This file is part of Sortix.
+
+ Sortix is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or (at your option) any later
+ version.
+
+ Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ details.
+
+ You should have received a copy of the GNU General Public License along with
+ Sortix. If not, see .
+
+ sortix/uthread.h
+ Header for user-space thread structures.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_SORTIX_UTHREAD_H
+#define INCLUDE_SORTIX_UTHREAD_H
+
+#include
+
+#include
+
+#ifndef __size_t_defined
+#define __size_t_defined
+#define __need_size_t
+#include
+#endif
+
+__BEGIN_DECLS
+
+#define UTHREAD_FLAG_INITIAL (1UL << 0UL)
+
+struct uthread
+{
+ struct uthread* uthread_pointer;
+ size_t uthread_size;
+ unsigned long uthread_flags;
+ void* tls_master_mmap;
+ size_t tls_master_size;
+ size_t tls_master_align;
+ void* tls_mmap;
+ size_t tls_size;
+ void* stack_mmap;
+ size_t stack_size;
+ void* arg_mmap;
+ size_t arg_size;
+ size_t __uthread_reserved[4];
+};
+
+__END_DECLS
+
+#endif
diff --git a/kernel/process.cpp b/kernel/process.cpp
index a37e9bc9..4984a3db 100644
--- a/kernel/process.cpp
+++ b/kernel/process.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -37,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
@@ -61,6 +63,10 @@
#include "elf.h"
#include "initrd.h"
+#if defined(__i386__) || defined(__x86_64__)
+#include "x86-family/gdt.h"
+#endif
+
namespace Sortix {
Process::Process()
@@ -736,11 +742,38 @@ void Process::ResetForExecute()
ResetAddressSpace();
}
+bool Process::MapSegment(struct segment* result, void* hint, size_t size,
+ int flags, int prot)
+{
+ // process->segment_lock is held at this point.
+
+ if ( !size )
+ {
+ result->addr = 0x0;
+ result->size = 0x0;
+ result->prot = prot;
+ return true;
+ }
+
+ if ( !PlaceSegment(result, this, hint, size, flags) )
+ return false;
+ if ( !Memory::MapMemory(this, result->addr, result->size, result->prot = prot) )
+ {
+ // The caller is expected to self-destruct in this case, so the
+ // segment just created is not removed.
+ return false;
+ }
+ Memory::Flush();
+ return true;
+}
+
int Process::Execute(const char* programname, const uint8_t* program,
size_t programsize, int argc, const char* const* argv,
int envc, const char* const* envp,
CPU::InterruptRegisters* regs)
{
+ assert(argc != INT_MAX);
+ assert(envc != INT_MAX);
assert(CurrentProcess() == this);
char* programname_clone = String::Clone(programname);
@@ -759,70 +792,142 @@ int Process::Execute(const char* programname, const uint8_t* program,
size_t userspace_size;
Memory::GetUserVirtualArea(&userspace_addr, &userspace_size);
- const size_t DEFAULT_STACK_SIZE = 512UL * 1024UL;
- void* const PREFERRED_STACK_LOCATION =
- (void*) (userspace_addr + userspace_size - DEFAULT_STACK_SIZE);
- const int STACK_PROTECTION = PROT_READ | PROT_WRITE | PROT_KREAD |
- PROT_KWRITE | PROT_FORK;
+ const size_t stack_size = 512UL * 1024UL;
+ void* stack_hint = (void*) (userspace_addr + userspace_size - stack_size);
+ const int stack_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK;
- // Attempt to allocate a stack for the new process.
- kthread_mutex_lock(&segment_lock);
+ if ( !aux.tls_mem_align )
+ aux.tls_mem_align = 1;
+ if ( Page::Size() < aux.tls_mem_align )
+ return errno = EINVAL, -1;
+ if ( !aux.uthread_align )
+ aux.uthread_align = 1;
+ if ( Page::Size() < aux.uthread_align )
+ return errno = EINVAL, -1;
+ if ( aux.uthread_size < sizeof(struct uthread) )
+ aux.uthread_size = sizeof(struct uthread);
+
+ size_t raw_tls_size = aux.tls_mem_size;
+ size_t raw_tls_size_aligned = -(-raw_tls_size & ~(aux.tls_mem_align-1));
+ if ( raw_tls_size && raw_tls_size_aligned == 0 /* overflow */ )
+ return errno = EINVAL, -1;
+ int raw_tls_prot = PROT_READ | PROT_KREAD | PROT_KWRITE | PROT_FORK;
+ void* raw_tls_hint = stack_hint;
+
+ size_t tls_size = raw_tls_size_aligned + aux.uthread_size;
+ size_t tls_offset_tls = 0;
+ size_t tls_offset_uthread = raw_tls_size_aligned;
+ if ( aux.tls_mem_align < aux.uthread_align )
+ {
+ size_t more_aligned = -(-raw_tls_size_aligned & ~(aux.uthread_align-1));
+ if ( raw_tls_size_aligned && more_aligned == 0 /* overflow */ )
+ return errno = EINVAL, -1;
+ size_t difference = more_aligned - raw_tls_size_aligned;
+ tls_size += difference;
+ tls_offset_tls += difference;
+ tls_offset_uthread += difference;
+ }
+ assert((tls_offset_tls & (aux.tls_mem_align-1)) == 0);
+ assert((tls_offset_uthread & (aux.uthread_align-1)) == 0);
+ int tls_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK;
+ void* tls_hint = stack_hint;
+
+ size_t arg_size = 0;
+
+ size_t argv_size = sizeof(char*) * (argc + 1);
+ size_t envp_size = sizeof(char*) * (envc + 1);
+
+ arg_size += argv_size;
+ arg_size += envp_size;
+
+ for ( int i = 0; i < argc; i++ )
+ arg_size += strlen(argv[i]) + 1;
+ for ( int i = 0; i < envc; i++ )
+ arg_size += strlen(envp[i]) + 1;
+
+ struct segment arg_segment;
struct segment stack_segment;
- if ( !PlaceSegment(&stack_segment, this, PREFERRED_STACK_LOCATION, DEFAULT_STACK_SIZE, 0) ||
- !Memory::MapMemory(this, stack_segment.addr, stack_segment.size, stack_segment.prot = STACK_PROTECTION) )
+ struct segment raw_tls_segment;
+ struct segment tls_segment;
+
+ kthread_mutex_lock(&segment_lock);
+
+ if ( !(MapSegment(&arg_segment, stack_hint, arg_size, 0, stack_prot) &&
+ MapSegment(&stack_segment, stack_hint, stack_size, 0, stack_prot) &&
+ MapSegment(&raw_tls_segment, raw_tls_hint, raw_tls_size, 0, raw_tls_prot) &&
+ MapSegment(&tls_segment, tls_hint, tls_size, 0, tls_prot)) )
{
kthread_mutex_unlock(&segment_lock);
ResetForExecute();
return errno = ENOMEM, -1;
}
+
kthread_mutex_unlock(&segment_lock);
- addr_t stackpos = stack_segment.addr + stack_segment.size;
+ char** target_argv = (char**) (arg_segment.addr + 0);
+ char** target_envp = (char**) (arg_segment.addr + argv_size);
+ char* target_strings = (char*) (arg_segment.addr + argv_size + envp_size);
+ size_t target_strings_offset = 0;
- // Alright, move argv onto the new stack! First figure out exactly how
- // big argv actually is.
- addr_t argvpos = stackpos - sizeof(char*) * (argc+1);
- char** stackargv = (char**) argvpos;
-
- size_t argvsize = 0;
for ( int i = 0; i < argc; i++ )
{
- size_t len = strlen(argv[i]) + 1;
- argvsize += len;
- char* dest = ((char*) argvpos) - argvsize;
- stackargv[i] = dest;
- memcpy(dest, argv[i], len);
+ const char* arg = argv[i];
+ size_t arg_len = strlen(arg);
+ char* target_arg = target_strings + target_strings_offset;
+ strcpy(target_arg, arg);
+ target_argv[i] = target_arg;
+ target_strings_offset += arg_len + 1;
}
+ target_argv[argc] = (char*) NULL;
- stackargv[argc] = NULL;
-
- if ( argvsize % 16UL )
- argvsize += 16 - argvsize % 16UL;
-
- // And then move envp onto the stack.
- addr_t envppos = argvpos - argvsize - sizeof(char*) * (envc+1);
- char** stackenvp = (char**) envppos;
-
- size_t envpsize = 0;
for ( int i = 0; i < envc; i++ )
{
- size_t len = strlen(envp[i]) + 1;
- envpsize += len;
- char* dest = ((char*) envppos) - envpsize;
- stackenvp[i] = dest;
- memcpy(dest, envp[i], len);
+ const char* env = envp[i];
+ size_t env_len = strlen(env);
+ char* target_env = target_strings + target_strings_offset;
+ strcpy(target_env, env);
+ target_envp[i] = target_env;
+ target_strings_offset += env_len + 1;
}
+ target_envp[envc] = (char*) NULL;
- stackenvp[envc] = NULL;
+ const uint8_t* file_raw_tls = program + aux.tls_file_offset;
- if ( envpsize % 16UL )
- envpsize += 16 - envpsize % 16UL;
+ uint8_t* target_raw_tls = (uint8_t*) raw_tls_segment.addr;
+ memcpy(target_raw_tls, file_raw_tls, aux.tls_file_size);
+ memset(target_raw_tls + aux.tls_file_size, 0, aux.tls_mem_size - aux.tls_file_size);
- stackpos = envppos - envpsize;
+ uint8_t* target_tls = (uint8_t*) (tls_segment.addr + tls_offset_tls);
+ assert((((uintptr_t) target_tls) & (aux.tls_mem_align-1)) == 0);
+ memcpy(target_tls, file_raw_tls, aux.tls_file_size);
+ memset(target_tls + aux.tls_file_size, 0, aux.tls_mem_size - aux.tls_file_size);
+
+ struct uthread* uthread = (struct uthread*) (tls_segment.addr + tls_offset_uthread);
+ assert((((uintptr_t) uthread) & (aux.uthread_align-1)) == 0);
+ memset(uthread, 0, sizeof(*uthread));
+ uthread->uthread_pointer = uthread;
+ uthread->uthread_size = aux.uthread_size;
+ uthread->uthread_flags = UTHREAD_FLAG_INITIAL;
+ uthread->tls_master_mmap = (void*) raw_tls_segment.addr;
+ uthread->tls_master_size = aux.tls_mem_size;
+ uthread->tls_master_align = aux.tls_mem_align;
+ uthread->tls_mmap = (void*) tls_segment.addr;
+ uthread->tls_size = tls_size;
+ uthread->stack_mmap = (void*) stack_segment.addr;
+ uthread->stack_size = stack_segment.size;
+ uthread->arg_mmap = (void*) arg_segment.addr;
+ uthread->arg_size = arg_segment.size;
+ memset(uthread + 1, 0, aux.uthread_size - sizeof(struct uthread));
+
+#if defined(__i386__)
+ GDT::SetGSBase((uint32_t) uthread);
+#elif defined(__x86_64__)
+ wrmsr(MSRID_FSBASE, (uint64_t) uthread);
+#endif
dtable->OnExecute();
- ExecuteCPU(argc, stackargv, envc, stackenvp, stackpos, entry, regs);
+ ExecuteCPU(argc, target_argv, envc, target_envp, stack_segment.addr + stack_segment.size, entry, regs);
return 0;
}
diff --git a/kernel/segment.cpp b/kernel/segment.cpp
index 67c6a42e..97a0e58d 100644
--- a/kernel/segment.cpp
+++ b/kernel/segment.cpp
@@ -210,9 +210,11 @@ bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr,
{
// process->segment_lock is held at this point.
+ assert(size);
assert(!(flags & MAP_FIXED));
uintptr_t addr = (uintptr_t) addr_ptr;
+ size = Page::AlignUp(size);
bool found_any = false;
size_t best_distance = 0;
struct segment best;