kernel: userspace: reserve stack space to store local data

This enables reserving little space on the top of stack to store
data local to thread when CONFIG_USERSPACE. The first customer
of this is errno.

Note that ARC, due to how it lays out the user stack and
privilege stack, sets the pointer itself rather than
relying on the common way.

Fixes: #9067

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2018-08-16 15:42:28 -07:00 committed by Anas Nashif
commit fc182430c0
5 changed files with 54 additions and 10 deletions

View file

@ -71,6 +71,7 @@ void _new_thread(struct k_thread *thread, k_thread_stack_t *stack,
_ASSERT_VALID_PRIO(priority, pEntry);
char *stackEnd;
char *stackAdjEnd;
struct init_stack_frame *pInitCtx;
#if CONFIG_USERSPACE
@ -101,8 +102,18 @@ void _new_thread(struct k_thread *thread, k_thread_stack_t *stack,
#endif
_new_thread_init(thread, pStackMem, stackSize, priority, options);
stackAdjEnd = stackEnd;
#ifdef CONFIG_THREAD_USERSPACE_LOCAL_DATA
/* reserve stack space for the userspace local data struct */
stackAdjEnd = (char *)STACK_ROUND_DOWN(stackEnd
- sizeof(*thread->userspace_local_data));
thread->userspace_local_data =
(struct _thread_userspace_local_data *)stackAdjEnd;
#endif
/* carve the thread entry struct from the "base" of the stack */
pInitCtx = (struct init_stack_frame *)(STACK_ROUND_DOWN(stackEnd) -
pInitCtx = (struct init_stack_frame *)(STACK_ROUND_DOWN(stackAdjEnd) -
sizeof(struct init_stack_frame));
#if CONFIG_USERSPACE
if (options & K_USER) {

View file

@ -503,6 +503,12 @@ struct _mem_domain_info {
#endif /* CONFIG_USERSPACE */
#ifdef CONFIG_THREAD_USERSPACE_LOCAL_DATA
struct _thread_userspace_local_data {
int errno_var;
};
#endif
/**
* @ingroup thread_apis
* Thread Structure
@ -538,14 +544,12 @@ struct k_thread {
void *custom_data;
#endif
#ifdef CONFIG_THREAD_USERSPACE_LOCAL_DATA
struct _thread_userspace_local_data *userspace_local_data;
#endif
#ifdef CONFIG_ERRNO
#ifdef CONFIG_USERSPACE
/* Set to the lowest area in the thread stack since this needs to
* be directly read/writable by user mode. Not ideal, but best we
* can do until we have thread-local storage
*/
int *errno_location;
#else
#ifndef CONFIG_USERSPACE
/** per-thread errno variable */
int errno_var;
#endif

View file

@ -164,9 +164,19 @@ config THREAD_CUSTOM_DATA
This option allows each thread to store 32 bits of custom data,
which can be accessed using the k_thread_custom_data_xxx() APIs.
config THREAD_USERSPACE_LOCAL_DATA
bool
depends on USERSPACE
config THREAD_USERSPACE_LOCAL_DATA_ARCH_DEFER_SETUP
bool
depends on THREAD_USERSPACE_LOCAL_DATA
default y if ARCH="arc"
config ERRNO
bool "Enable errno support"
default y
select THREAD_USERSPACE_LOCAL_DATA if USERSPACE
help
Enable per-thread errno in the kernel. Application and library code must
include errno.h provided by the C library (libc) to use the errno

View file

@ -29,7 +29,7 @@ int *_impl_z_errno(void)
/* Initialized to the lowest address in the stack so the thread can
* directly read/write it
*/
return _current->errno_location;
return &_current->userspace_local_data->errno_var;
}
Z_SYSCALL_HANDLER0_SIMPLE(z_errno);

View file

@ -294,8 +294,28 @@ void _setup_new_thread(struct k_thread *new_thread,
{
stack_size = adjust_stack_size(stack_size);
#ifdef CONFIG_THREAD_USERSPACE_LOCAL_DATA
#ifndef CONFIG_THREAD_USERSPACE_LOCAL_DATA_ARCH_DEFER_SETUP
/* reserve space on top of stack for local data */
stack_size = STACK_ROUND_DOWN(stack_size
- sizeof(*new_thread->userspace_local_data));
#endif
#endif
_new_thread(new_thread, stack, stack_size, entry, p1, p2, p3,
prio, options);
#ifdef CONFIG_THREAD_USERSPACE_LOCAL_DATA
#ifndef CONFIG_THREAD_USERSPACE_LOCAL_DATA_ARCH_DEFER_SETUP
/* don't set again if the arch's own code in _new_thread() has
* already set the pointer.
*/
new_thread->userspace_local_data =
(struct _thread_userspace_local_data *)
(K_THREAD_STACK_BUFFER(stack) + stack_size);
#endif
#endif
#ifdef CONFIG_THREAD_MONITOR
new_thread->entry.pEntry = entry;
new_thread->entry.parameter1 = p1;
@ -312,7 +332,6 @@ void _setup_new_thread(struct k_thread *new_thread,
_k_object_init(new_thread);
_k_object_init(stack);
new_thread->stack_obj = stack;
new_thread->errno_location = (int *)K_THREAD_STACK_BUFFER(stack);
/* Any given thread has access to itself */
k_object_access_grant(new_thread, new_thread);