325 lines
7.0 KiB
C
325 lines
7.0 KiB
C
/*
|
|
* 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>
|
|
#include <ksched.h>
|
|
#include <syscall.h>
|
|
#include <syscall_handler.h>
|
|
|
|
#define MAX_THREAD_BITS (CONFIG_MAX_THREAD_BYTES * 8)
|
|
|
|
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) {
|
|
/* Core kernel objects */
|
|
case K_OBJ_ALERT:
|
|
return "k_alert";
|
|
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__THREAD_STACK_ELEMENT:
|
|
return "k_thread_stack_t";
|
|
|
|
/* Driver subsystems */
|
|
case K_OBJ_DRIVER_ADC:
|
|
return "adc driver";
|
|
case K_OBJ_DRIVER_AIO_CMP:
|
|
return "aio comparator driver";
|
|
case K_OBJ_DRIVER_COUNTER:
|
|
return "counter driver";
|
|
case K_OBJ_DRIVER_CRYPTO:
|
|
return "crypto driver";
|
|
case K_OBJ_DRIVER_DMA:
|
|
return "dma driver";
|
|
case K_OBJ_DRIVER_FLASH:
|
|
return "flash driver";
|
|
case K_OBJ_DRIVER_GPIO:
|
|
return "gpio driver";
|
|
case K_OBJ_DRIVER_I2C:
|
|
return "i2c driver";
|
|
case K_OBJ_DRIVER_I2S:
|
|
return "i2s driver";
|
|
case K_OBJ_DRIVER_IPM:
|
|
return "ipm driver";
|
|
case K_OBJ_DRIVER_PINMUX:
|
|
return "pinmux driver";
|
|
case K_OBJ_DRIVER_PWM:
|
|
return "pwm driver";
|
|
case K_OBJ_DRIVER_ENTROPY:
|
|
return "entropy driver";
|
|
case K_OBJ_DRIVER_RTC:
|
|
return "realtime clock driver";
|
|
case K_OBJ_DRIVER_SENSOR:
|
|
return "sensor driver";
|
|
case K_OBJ_DRIVER_SPI:
|
|
return "spi driver";
|
|
case K_OBJ_DRIVER_UART:
|
|
return "uart driver";
|
|
default:
|
|
return "?";
|
|
}
|
|
#else
|
|
ARG_UNUSED(otype);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
struct perm_ctx {
|
|
int parent_id;
|
|
int child_id;
|
|
struct k_thread *parent;
|
|
};
|
|
|
|
static int thread_index_get(struct k_thread *t)
|
|
{
|
|
struct _k_object *ko;
|
|
|
|
ko = _k_object_find(t);
|
|
|
|
if (!ko) {
|
|
return -1;
|
|
}
|
|
|
|
return ko->data;
|
|
}
|
|
|
|
static void wordlist_cb(struct _k_object *ko, void *ctx_ptr)
|
|
{
|
|
struct perm_ctx *ctx = (struct perm_ctx *)ctx_ptr;
|
|
|
|
if (sys_bitfield_test_bit((mem_addr_t)&ko->perms, ctx->parent_id) &&
|
|
(struct k_thread *)ko->name != ctx->parent) {
|
|
sys_bitfield_set_bit((mem_addr_t)&ko->perms, ctx->child_id);
|
|
}
|
|
}
|
|
|
|
void _thread_perms_inherit(struct k_thread *parent, struct k_thread *child)
|
|
{
|
|
struct perm_ctx ctx = {
|
|
thread_index_get(parent),
|
|
thread_index_get(child),
|
|
parent
|
|
};
|
|
|
|
if ((ctx.parent_id != -1) && (ctx.child_id != -1)) {
|
|
_k_object_wordlist_foreach(wordlist_cb, &ctx);
|
|
}
|
|
}
|
|
|
|
void _thread_perms_set(struct _k_object *ko, struct k_thread *thread)
|
|
{
|
|
int index = thread_index_get(thread);
|
|
|
|
if (index != -1) {
|
|
sys_bitfield_set_bit((mem_addr_t)&ko->perms, index);
|
|
}
|
|
}
|
|
|
|
void _thread_perms_clear(struct _k_object *ko, struct k_thread *thread)
|
|
{
|
|
int index = thread_index_get(thread);
|
|
|
|
if (index != -1) {
|
|
sys_bitfield_clear_bit((mem_addr_t)&ko->perms, index);
|
|
}
|
|
}
|
|
|
|
static void clear_perms_cb(struct _k_object *ko, void *ctx_ptr)
|
|
{
|
|
int id = (int)ctx_ptr;
|
|
|
|
sys_bitfield_clear_bit((mem_addr_t)&ko->perms, id);
|
|
}
|
|
|
|
void _thread_perms_all_clear(struct k_thread *thread)
|
|
{
|
|
int index = thread_index_get(thread);
|
|
|
|
if (index != -1) {
|
|
_k_object_wordlist_foreach(clear_perms_cb, (void *)index);
|
|
}
|
|
}
|
|
|
|
static int thread_perms_test(struct _k_object *ko)
|
|
{
|
|
int index;
|
|
|
|
if (ko->flags & K_OBJ_FLAG_PUBLIC) {
|
|
return 1;
|
|
}
|
|
|
|
index = thread_index_get(_current);
|
|
if (index != -1) {
|
|
return sys_bitfield_test_bit((mem_addr_t)&ko->perms, index);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void dump_permission_error(struct _k_object *ko)
|
|
{
|
|
int index = thread_index_get(_current);
|
|
printk("thread %p (%d) does not have permission on %s %p [",
|
|
_current, index,
|
|
otype_to_str(ko->type), ko->name);
|
|
for (int i = CONFIG_MAX_THREAD_BYTES - 1; i >= 0; i--) {
|
|
printk("%02x", ko->perms[i]);
|
|
}
|
|
printk("]\n");
|
|
}
|
|
|
|
void _dump_object_error(int retval, void *obj, struct _k_object *ko,
|
|
enum k_objects otype)
|
|
{
|
|
switch (retval) {
|
|
case -EBADF:
|
|
printk("%p is not a valid %s\n", obj, otype_to_str(otype));
|
|
break;
|
|
case -EPERM:
|
|
dump_permission_error(ko);
|
|
break;
|
|
case -EINVAL:
|
|
printk("%p used before initialization\n", obj);
|
|
break;
|
|
case -EADDRINUSE:
|
|
printk("%p %s in use\n", obj, otype_to_str(otype));
|
|
}
|
|
}
|
|
|
|
void _impl_k_object_access_grant(void *object, struct k_thread *thread)
|
|
{
|
|
struct _k_object *ko = _k_object_find(object);
|
|
|
|
if (ko) {
|
|
_thread_perms_set(ko, thread);
|
|
}
|
|
}
|
|
|
|
void _impl_k_object_access_revoke(void *object, struct k_thread *thread)
|
|
{
|
|
struct _k_object *ko = _k_object_find(object);
|
|
|
|
if (ko) {
|
|
_thread_perms_clear(ko, thread);
|
|
}
|
|
}
|
|
|
|
void k_object_access_all_grant(void *object)
|
|
{
|
|
struct _k_object *ko = _k_object_find(object);
|
|
|
|
if (ko) {
|
|
ko->flags |= K_OBJ_FLAG_PUBLIC;
|
|
}
|
|
}
|
|
|
|
int _k_object_validate(struct _k_object *ko, enum k_objects otype,
|
|
enum _obj_init_check init)
|
|
{
|
|
if (unlikely(!ko || (otype != K_OBJ_ANY && ko->type != otype))) {
|
|
return -EBADF;
|
|
}
|
|
|
|
/* Manipulation of any kernel objects by a user thread requires that
|
|
* thread be granted access first, even for uninitialized objects
|
|
*/
|
|
if (unlikely(!thread_perms_test(ko))) {
|
|
return -EPERM;
|
|
}
|
|
|
|
/* Initialization state checks. _OBJ_INIT_ANY, we don't care */
|
|
if (likely(init == _OBJ_INIT_TRUE)) {
|
|
/* Object MUST be intialized */
|
|
if (unlikely(!(ko->flags & K_OBJ_FLAG_INITIALIZED))) {
|
|
return -EINVAL;
|
|
}
|
|
} else if (init < _OBJ_INIT_TRUE) { /* _OBJ_INIT_FALSE case */
|
|
/* Object MUST NOT be initialized */
|
|
if (unlikely(ko->flags & K_OBJ_FLAG_INITIALIZED)) {
|
|
return -EADDRINUSE;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Allows non-initialization system calls to be made on this object */
|
|
ko->flags |= K_OBJ_FLAG_INITIALIZED;
|
|
}
|
|
|
|
void _k_object_uninit(void *object)
|
|
{
|
|
struct _k_object *ko;
|
|
|
|
/* See comments in _k_object_init() */
|
|
ko = _k_object_find(object);
|
|
if (!ko) {
|
|
return;
|
|
}
|
|
|
|
ko->flags &= ~K_OBJ_FLAG_INITIALIZED;
|
|
}
|
|
|
|
static u32_t handler_bad_syscall(u32_t bad_id, u32_t arg2, u32_t arg3,
|
|
u32_t arg4, u32_t arg5, u32_t arg6, void *ssf)
|
|
{
|
|
printk("Bad system call id %u invoked\n", bad_id);
|
|
_arch_syscall_oops(ssf);
|
|
CODE_UNREACHABLE;
|
|
}
|
|
|
|
static u32_t handler_no_syscall(u32_t arg1, u32_t arg2, u32_t arg3,
|
|
u32_t arg4, u32_t arg5, u32_t arg6, void *ssf)
|
|
{
|
|
printk("Unimplemented system call\n");
|
|
_arch_syscall_oops(ssf);
|
|
CODE_UNREACHABLE;
|
|
}
|
|
|
|
#include <syscall_dispatch.c>
|
|
|