2017-08-22 22:15:23 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Intel Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <misc/printk.h>
|
|
|
|
#include <kernel_structs.h>
|
|
|
|
#include <sys_io.h>
|
2017-08-30 23:17:44 +02:00
|
|
|
#include <ksched.h>
|
userspace: flesh out internal syscall interface
* Instead of a common system call entry function, we instead create a
table mapping system call ids to handler skeleton functions which are
invoked directly by the architecture code which receives the system
call.
* system call handler prototype specified. All but the most trivial
system calls will implement one of these. They validate all the
arguments, including verifying kernel/device object pointers, ensuring
that the calling thread has appropriate access to any memory buffers
passed in, and performing other parameter checks that the base system
call implementation does not check, or only checks with __ASSERT().
It's only possible to install a system call implementation directly
inside this table if the implementation has a return value and requires
no validation of any of its arguments.
A sample handler implementation for k_mutex_unlock() might look like:
u32_t _syscall_k_mutex_unlock(u32_t mutex_arg, u32_t arg2, u32_t arg3,
u32_t arg4, u32_t arg5, void *ssf)
{
struct k_mutex *mutex = (struct k_mutex *)mutex_arg;
_SYSCALL_ARG1;
_SYSCALL_IS_OBJ(mutex, K_OBJ_MUTEX, 0, ssf);
_SYSCALL_VERIFY(mutex->lock_count > 0, ssf);
_SYSCALL_VERIFY(mutex->owner == _current, ssf);
k_mutex_unlock(mutex);
return 0;
}
* the x86 port modified to work with the system call table instead of
calling a common handler function. fixed an issue where registers being
changed could confuse the compiler has been fixed; all registers, even
ones used for parameters, must be preserved across the system call.
* a new arch API for producing a kernel oops when validating system call
arguments added. The debug information reported will be from the system
call site and not inside the handler function.
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-09-14 03:04:21 +02:00
|
|
|
#include <syscall.h>
|
2017-08-22 22:15:23 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Kernel object validation function
|
|
|
|
*
|
|
|
|
* Retrieve metadata for a kernel object. This function is implemented in
|
|
|
|
* the gperf script footer, see gen_kobject_list.py
|
|
|
|
*
|
|
|
|
* @param obj Address of kernel object to get metadata
|
|
|
|
* @return Kernel object's metadata, or NULL if the parameter wasn't the
|
|
|
|
* memory address of a kernel object
|
|
|
|
*/
|
|
|
|
extern struct _k_object *_k_object_find(void *obj);
|
|
|
|
|
|
|
|
const char *otype_to_str(enum k_objects otype)
|
|
|
|
{
|
|
|
|
/* -fdata-sections doesn't work right except in very very recent
|
|
|
|
* GCC and these literal strings would appear in the binary even if
|
|
|
|
* otype_to_str was omitted by the linker
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_PRINTK
|
|
|
|
switch (otype) {
|
|
|
|
case K_OBJ_ALERT:
|
|
|
|
return "k_alert";
|
|
|
|
case K_OBJ_DELAYED_WORK:
|
|
|
|
return "k_delayed_work";
|
|
|
|
case K_OBJ_MEM_SLAB:
|
|
|
|
return "k_mem_slab";
|
|
|
|
case K_OBJ_MSGQ:
|
|
|
|
return "k_msgq";
|
|
|
|
case K_OBJ_MUTEX:
|
|
|
|
return "k_mutex";
|
|
|
|
case K_OBJ_PIPE:
|
|
|
|
return "k_pipe";
|
|
|
|
case K_OBJ_SEM:
|
|
|
|
return "k_sem";
|
|
|
|
case K_OBJ_STACK:
|
|
|
|
return "k_stack";
|
|
|
|
case K_OBJ_THREAD:
|
|
|
|
return "k_thread";
|
|
|
|
case K_OBJ_TIMER:
|
|
|
|
return "k_timer";
|
|
|
|
case K_OBJ_WORK:
|
|
|
|
return "k_work";
|
|
|
|
case K_OBJ_WORK_Q:
|
|
|
|
return "k_work_q";
|
|
|
|
default:
|
|
|
|
return "?";
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
ARG_UNUSED(otype);
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stub functions, to be filled in forthcoming patch sets */
|
|
|
|
|
|
|
|
static void set_thread_perms(struct _k_object *ko, struct k_thread *thread)
|
|
|
|
{
|
2017-08-30 23:31:03 +02:00
|
|
|
if (thread->base.perm_index < 8 * CONFIG_MAX_THREAD_BYTES) {
|
|
|
|
sys_bitfield_set_bit((mem_addr_t)&ko->perms,
|
|
|
|
thread->base.perm_index);
|
|
|
|
}
|
2017-08-22 22:15:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int test_thread_perms(struct _k_object *ko)
|
|
|
|
{
|
2017-08-30 23:31:03 +02:00
|
|
|
if (_current->base.perm_index < 8 * CONFIG_MAX_THREAD_BYTES) {
|
|
|
|
return sys_bitfield_test_bit((mem_addr_t)&ko->perms,
|
|
|
|
_current->base.perm_index);
|
|
|
|
}
|
|
|
|
return 0;
|
2017-08-22 22:15:23 +02:00
|
|
|
}
|
|
|
|
|
2017-08-30 23:31:03 +02:00
|
|
|
|
2017-08-22 22:15:23 +02:00
|
|
|
void k_object_grant_access(void *object, struct k_thread *thread)
|
|
|
|
{
|
|
|
|
struct _k_object *ko = _k_object_find(object);
|
|
|
|
|
|
|
|
if (!ko) {
|
|
|
|
if (_is_thread_user()) {
|
|
|
|
printk("granting access to non-existent kernel object %p\n",
|
|
|
|
object);
|
|
|
|
k_oops();
|
|
|
|
} else {
|
|
|
|
/* Supervisor threads may at times instantiate objects
|
|
|
|
* that ignore rules on where they can live. Such
|
|
|
|
* objects won't ever be usable from userspace, but
|
|
|
|
* we shouldn't explode.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* userspace can't grant access to objects unless it already has
|
|
|
|
* access to that object
|
|
|
|
*/
|
|
|
|
if (_is_thread_user() && !test_thread_perms(ko)) {
|
|
|
|
printk("insufficient permissions in current thread %p\n",
|
|
|
|
_current);
|
|
|
|
printk("Cannot grant access to %s %p for thread %p\n",
|
|
|
|
otype_to_str(ko->type), object, thread);
|
|
|
|
k_oops();
|
|
|
|
}
|
|
|
|
set_thread_perms(ko, thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int _k_object_validate(void *obj, enum k_objects otype, int init)
|
|
|
|
{
|
|
|
|
struct _k_object *ko;
|
|
|
|
|
|
|
|
ko = _k_object_find(obj);
|
|
|
|
|
|
|
|
if (!ko || ko->type != otype) {
|
|
|
|
printk("%p is not a %s\n", obj, otype_to_str(otype));
|
|
|
|
return -EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Uninitialized objects are not owned by anyone. However if an
|
|
|
|
* object is initialized, and the caller is from userspace, then
|
|
|
|
* we need to assert that the user thread has sufficient permissions
|
|
|
|
* to re-initialize.
|
|
|
|
*/
|
|
|
|
if (ko->flags & K_OBJ_FLAG_INITIALIZED && _is_thread_user() &&
|
|
|
|
!test_thread_perms(ko)) {
|
|
|
|
printk("thread %p does not have permission on %s %p\n",
|
|
|
|
_current, otype_to_str(otype), obj);
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we are not initializing an object, and the object is not
|
|
|
|
* initialized, we should freak out
|
|
|
|
*/
|
|
|
|
if (!init && !(ko->flags & K_OBJ_FLAG_INITIALIZED)) {
|
|
|
|
printk("%p used before initialization\n", obj);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _k_object_init(void *object)
|
|
|
|
{
|
|
|
|
struct _k_object *ko;
|
|
|
|
|
|
|
|
/* By the time we get here, if the caller was from userspace, all the
|
|
|
|
* necessary checks have been done in _k_object_validate(), which takes
|
|
|
|
* place before the object is initialized.
|
|
|
|
*
|
|
|
|
* This function runs after the object has been initialized and
|
|
|
|
* finalizes it
|
|
|
|
*/
|
|
|
|
|
|
|
|
ko = _k_object_find(object);
|
|
|
|
if (!ko) {
|
|
|
|
/* Supervisor threads can ignore rules about kernel objects
|
|
|
|
* and may declare them on stacks, etc. Such objects will never
|
|
|
|
* be usable from userspace, but we shouldn't explode.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(ko->perms, 0, CONFIG_MAX_THREAD_BYTES);
|
|
|
|
set_thread_perms(ko, _current);
|
|
|
|
|
|
|
|
ko->flags |= K_OBJ_FLAG_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
userspace: flesh out internal syscall interface
* Instead of a common system call entry function, we instead create a
table mapping system call ids to handler skeleton functions which are
invoked directly by the architecture code which receives the system
call.
* system call handler prototype specified. All but the most trivial
system calls will implement one of these. They validate all the
arguments, including verifying kernel/device object pointers, ensuring
that the calling thread has appropriate access to any memory buffers
passed in, and performing other parameter checks that the base system
call implementation does not check, or only checks with __ASSERT().
It's only possible to install a system call implementation directly
inside this table if the implementation has a return value and requires
no validation of any of its arguments.
A sample handler implementation for k_mutex_unlock() might look like:
u32_t _syscall_k_mutex_unlock(u32_t mutex_arg, u32_t arg2, u32_t arg3,
u32_t arg4, u32_t arg5, void *ssf)
{
struct k_mutex *mutex = (struct k_mutex *)mutex_arg;
_SYSCALL_ARG1;
_SYSCALL_IS_OBJ(mutex, K_OBJ_MUTEX, 0, ssf);
_SYSCALL_VERIFY(mutex->lock_count > 0, ssf);
_SYSCALL_VERIFY(mutex->owner == _current, ssf);
k_mutex_unlock(mutex);
return 0;
}
* the x86 port modified to work with the system call table instead of
calling a common handler function. fixed an issue where registers being
changed could confuse the compiler has been fixed; all registers, even
ones used for parameters, must be preserved across the system call.
* a new arch API for producing a kernel oops when validating system call
arguments added. The debug information reported will be from the system
call site and not inside the handler function.
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-09-14 03:04:21 +02:00
|
|
|
static u32_t _syscall_bad_handler(u32_t bad_id, u32_t arg2, u32_t arg3,
|
|
|
|
u32_t arg4, u32_t arg5, void *ssf)
|
2017-09-08 21:10:12 +02:00
|
|
|
{
|
userspace: flesh out internal syscall interface
* Instead of a common system call entry function, we instead create a
table mapping system call ids to handler skeleton functions which are
invoked directly by the architecture code which receives the system
call.
* system call handler prototype specified. All but the most trivial
system calls will implement one of these. They validate all the
arguments, including verifying kernel/device object pointers, ensuring
that the calling thread has appropriate access to any memory buffers
passed in, and performing other parameter checks that the base system
call implementation does not check, or only checks with __ASSERT().
It's only possible to install a system call implementation directly
inside this table if the implementation has a return value and requires
no validation of any of its arguments.
A sample handler implementation for k_mutex_unlock() might look like:
u32_t _syscall_k_mutex_unlock(u32_t mutex_arg, u32_t arg2, u32_t arg3,
u32_t arg4, u32_t arg5, void *ssf)
{
struct k_mutex *mutex = (struct k_mutex *)mutex_arg;
_SYSCALL_ARG1;
_SYSCALL_IS_OBJ(mutex, K_OBJ_MUTEX, 0, ssf);
_SYSCALL_VERIFY(mutex->lock_count > 0, ssf);
_SYSCALL_VERIFY(mutex->owner == _current, ssf);
k_mutex_unlock(mutex);
return 0;
}
* the x86 port modified to work with the system call table instead of
calling a common handler function. fixed an issue where registers being
changed could confuse the compiler has been fixed; all registers, even
ones used for parameters, must be preserved across the system call.
* a new arch API for producing a kernel oops when validating system call
arguments added. The debug information reported will be from the system
call site and not inside the handler function.
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-09-14 03:04:21 +02:00
|
|
|
printk("Bad system call id %u invoked\n", bad_id);
|
|
|
|
_arch_syscall_oops(ssf);
|
|
|
|
CODE_UNREACHABLE;
|
2017-09-08 21:10:12 +02:00
|
|
|
}
|
|
|
|
|
userspace: flesh out internal syscall interface
* Instead of a common system call entry function, we instead create a
table mapping system call ids to handler skeleton functions which are
invoked directly by the architecture code which receives the system
call.
* system call handler prototype specified. All but the most trivial
system calls will implement one of these. They validate all the
arguments, including verifying kernel/device object pointers, ensuring
that the calling thread has appropriate access to any memory buffers
passed in, and performing other parameter checks that the base system
call implementation does not check, or only checks with __ASSERT().
It's only possible to install a system call implementation directly
inside this table if the implementation has a return value and requires
no validation of any of its arguments.
A sample handler implementation for k_mutex_unlock() might look like:
u32_t _syscall_k_mutex_unlock(u32_t mutex_arg, u32_t arg2, u32_t arg3,
u32_t arg4, u32_t arg5, void *ssf)
{
struct k_mutex *mutex = (struct k_mutex *)mutex_arg;
_SYSCALL_ARG1;
_SYSCALL_IS_OBJ(mutex, K_OBJ_MUTEX, 0, ssf);
_SYSCALL_VERIFY(mutex->lock_count > 0, ssf);
_SYSCALL_VERIFY(mutex->owner == _current, ssf);
k_mutex_unlock(mutex);
return 0;
}
* the x86 port modified to work with the system call table instead of
calling a common handler function. fixed an issue where registers being
changed could confuse the compiler has been fixed; all registers, even
ones used for parameters, must be preserved across the system call.
* a new arch API for producing a kernel oops when validating system call
arguments added. The debug information reported will be from the system
call site and not inside the handler function.
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-09-14 03:04:21 +02:00
|
|
|
/* This table will eventually be generated by a script, placeholder for now */
|
|
|
|
const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT] = {
|
|
|
|
[K_SYSCALL_BAD] = _syscall_bad_handler,
|
|
|
|
};
|