diff --git a/include/kernel.h b/include/kernel.h index a46ce430265..1b69cab80b5 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -4178,6 +4178,9 @@ extern void k_poll_event_init(struct k_poll_event *event, u32_t type, * Before being reused for another call to k_poll(), the user has to reset the * state field to K_POLL_STATE_NOT_READY. * + * When called from user mode, a temporary memory allocation is required from + * the caller's resource pool. + * * @param events An array of pointers to events to be polled for. * @param num_events The number of events in the array. * @param timeout Waiting period for an event to be ready (in milliseconds), @@ -4186,10 +4189,12 @@ extern void k_poll_event_init(struct k_poll_event *event, u32_t type, * @retval 0 One or more events are ready. * @retval -EAGAIN Waiting period timed out. * @retval -EINTR Poller thread has been interrupted. + * @retval -ENOMEM Thread resource pool insufficient memory (user mode only) + * @retval -EINVAL Bad parameters (user mode only) */ -extern int k_poll(struct k_poll_event *events, int num_events, - s32_t timeout); +__syscall int k_poll(struct k_poll_event *events, int num_events, + s32_t timeout); /** * @brief Initialize a poll signal object. @@ -4201,7 +4206,32 @@ extern int k_poll(struct k_poll_event *events, int num_events, * @return N/A */ -extern void k_poll_signal_init(struct k_poll_signal *signal); +__syscall void k_poll_signal_init(struct k_poll_signal *signal); + +/* + * @brief Reset a poll signal object's state to unsignaled. + * + * @param signal A poll signal object + */ +__syscall void k_poll_signal_reset(struct k_poll_signal *signal); + +static inline void _impl_k_poll_signal_reset(struct k_poll_signal *signal) +{ + signal->signaled = 0; +} + +/** + * @brief Fetch the signaled state and resylt value of a poll signal + * + * @param signal A poll signal object + * @param signaled An integer buffer which will be written nonzero if the + * object was signaled + * @param result An integer destination buffer which will be written with the + * result value if the object was signaed, or an undefined + * value if it was not. + */ +__syscall void k_poll_signal_check(struct k_poll_signal *signal, + unsigned int *signaled, int *result); /** * @brief Signal a poll signal object. @@ -4211,9 +4241,10 @@ extern void k_poll_signal_init(struct k_poll_signal *signal); * made ready to run. A @a result value can be specified. * * The poll signal contains a 'signaled' field that, when set by - * k_poll_signal(), stays set until the user sets it back to 0. It thus has to - * be reset by the user before being passed again to k_poll() or k_poll() will - * consider it being signaled, and will return immediately. + * k_poll_signal(), stays set until the user sets it back to 0 with + * k_poll_signal_reset(). It thus has to be reset by the user before being + * passed again to k_poll() or k_poll() will consider it being signaled, and + * will return immediately. * * @param signal A poll signal. * @param result The value to store in the result field of the signal. @@ -4222,7 +4253,7 @@ extern void k_poll_signal_init(struct k_poll_signal *signal); * @retval -EAGAIN The polling thread's timeout is in the process of expiring. */ -extern int k_poll_signal(struct k_poll_signal *signal, int result); +__syscall int k_poll_signal(struct k_poll_signal *signal, int result); /** * @internal diff --git a/kernel/poll.c b/kernel/poll.c index 0b5f0e90017..581fd416f40 100644 --- a/kernel/poll.c +++ b/kernel/poll.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -189,7 +190,7 @@ static inline void set_event_ready(struct k_poll_event *event, u32_t state) event->state |= state; } -int k_poll(struct k_poll_event *events, int num_events, s32_t timeout) +int _impl_k_poll(struct k_poll_event *events, int num_events, s32_t timeout) { __ASSERT(!_is_in_isr(), ""); __ASSERT(events, "NULL events\n"); @@ -263,6 +264,80 @@ int k_poll(struct k_poll_event *events, int num_events, s32_t timeout) return swap_rc; } +#ifdef CONFIG_USERSPACE +Z_SYSCALL_HANDLER(k_poll, events, num_events, timeout) +{ + int ret, key; + struct k_poll_event *events_copy = NULL; + unsigned int bounds; + + /* Validate the events buffer and make a copy of it in an + * allocated kernel-side buffer. + */ + if (Z_SYSCALL_VERIFY(num_events > 0)) { + ret = -EINVAL; + goto out; + } + if (Z_SYSCALL_VERIFY_MSG( + !__builtin_umul_overflow(num_events, + sizeof(struct k_poll_event), + &bounds), "num_events too large")) { + ret = -EINVAL; + goto out; + } + events_copy = z_thread_malloc(bounds); + if (!events_copy) { + ret = -ENOMEM; + goto out; + } + + key = irq_lock(); + if (Z_SYSCALL_MEMORY_WRITE(events, bounds)) { + irq_unlock(key); + goto oops_free; + } + memcpy(events_copy, (void *)events, bounds); + irq_unlock(key); + + /* Validate what's inside events_copy */ + for (int i = 0; i < num_events; i++) { + struct k_poll_event *e = &events_copy[i]; + + if (Z_SYSCALL_VERIFY(e->mode == K_POLL_MODE_NOTIFY_ONLY)) { + ret = -EINVAL; + goto out_free; + } + + switch (e->type) { + case K_POLL_TYPE_IGNORE: + break; + case K_POLL_TYPE_SIGNAL: + Z_OOPS(Z_SYSCALL_OBJ(e->signal, K_OBJ_POLL_SIGNAL)); + break; + case K_POLL_TYPE_SEM_AVAILABLE: + Z_OOPS(Z_SYSCALL_OBJ(e->sem, K_OBJ_SEM)); + break; + case K_POLL_TYPE_DATA_AVAILABLE: + Z_OOPS(Z_SYSCALL_OBJ(e->queue, K_OBJ_QUEUE)); + break; + default: + ret = -EINVAL; + goto out_free; + } + } + + ret = k_poll(events_copy, num_events, timeout); + memcpy((void *)events, events_copy, bounds); +out_free: + k_free(events_copy); +out: + return ret; +oops_free: + k_free(events_copy); + Z_OOPS(1); +} +#endif + /* must be called with interrupts locked */ static int signal_poll_event(struct k_poll_event *event, u32_t state) { @@ -309,14 +384,44 @@ void _handle_obj_poll_events(sys_dlist_t *events, u32_t state) } } -void k_poll_signal_init(struct k_poll_signal *signal) +void _impl_k_poll_signal_init(struct k_poll_signal *signal) { sys_dlist_init(&signal->poll_events); signal->signaled = 0; /* signal->result is left unitialized */ + _k_object_init(signal); } -int k_poll_signal(struct k_poll_signal *signal, int result) +#ifdef CONFIG_USERSPACE +Z_SYSCALL_HANDLER(k_poll_signal_init, signal) +{ + Z_OOPS(Z_SYSCALL_OBJ_INIT(signal, K_OBJ_POLL_SIGNAL)); + _impl_k_poll_signal_init((struct k_poll_signal *)signal); + return 0; +} +#endif + +void _impl_k_poll_signal_check(struct k_poll_signal *signal, + unsigned int *signaled, int *result) +{ + *signaled = signal->signaled; + *result = signal->result; +} + +#ifdef CONFIG_USERSPACE +Z_SYSCALL_HANDLER(k_poll_signal_check, signal, signaled, result) +{ + Z_OOPS(Z_SYSCALL_OBJ(signal, K_OBJ_POLL_SIGNAL)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(signaled, sizeof(unsigned int))); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(result, sizeof(int))); + + _impl_k_poll_signal_check((struct k_poll_signal *)signal, + (unsigned int *)signaled, (int *)result); + return 0; +} +#endif + +int _impl_k_poll_signal(struct k_poll_signal *signal, int result) { unsigned int key = irq_lock(); struct k_poll_event *poll_event; @@ -335,3 +440,14 @@ int k_poll_signal(struct k_poll_signal *signal, int result) _reschedule(key); return rc; } + +#ifdef CONFIG_USERSPACE +Z_SYSCALL_HANDLER(k_poll_signal, signal, result) +{ + Z_OOPS(Z_SYSCALL_OBJ(signal, K_OBJ_POLL_SIGNAL)); + return _impl_k_poll_signal((struct k_poll_signal *)signal, result); +} +Z_SYSCALL_HANDLER1_SIMPLE_VOID(k_poll_signal_reset, K_OBJ_POLL_SIGNAL, + struct k_poll_signal *); +#endif + diff --git a/scripts/gen_kobject_list.py b/scripts/gen_kobject_list.py index 5e53db64f28..76a48bf62aa 100755 --- a/scripts/gen_kobject_list.py +++ b/scripts/gen_kobject_list.py @@ -17,6 +17,7 @@ kobjects = [ "k_mutex", "k_pipe", "k_queue", + "k_poll_signal", "k_sem", "k_stack", "k_thread", diff --git a/tests/kernel/poll/prj.conf b/tests/kernel/poll/prj.conf index e5b15316616..55f4bf81a7f 100644 --- a/tests/kernel/poll/prj.conf +++ b/tests/kernel/poll/prj.conf @@ -1,2 +1,3 @@ CONFIG_ZTEST=y CONFIG_POLL=y +CONFIG_DYNAMIC_OBJECTS=y diff --git a/tests/kernel/poll/src/main.c b/tests/kernel/poll/src/main.c index bdec83d09d9..b4f05efdefe 100644 --- a/tests/kernel/poll/src/main.c +++ b/tests/kernel/poll/src/main.c @@ -15,12 +15,19 @@ extern void test_poll_no_wait(void); extern void test_poll_wait(void); extern void test_poll_multi(void); +extern void test_poll_grant_access(void); + +K_MEM_POOL_DEFINE(test_pool, 128, 128, 4, 4); /*test case main entry*/ void test_main(void) { + test_poll_grant_access(); + + k_thread_resource_pool_assign(k_current_get(), &test_pool); + ztest_test_suite(poll_api, - ztest_unit_test(test_poll_no_wait), + ztest_user_unit_test(test_poll_no_wait), ztest_unit_test(test_poll_wait), ztest_unit_test(test_poll_multi)); ztest_run_test_suite(poll_api); diff --git a/tests/kernel/poll/src/test_poll.c b/tests/kernel/poll/src/test_poll.c index 1ce6dace284..f06b92ee028 100644 --- a/tests/kernel/poll/src/test_poll.c +++ b/tests/kernel/poll/src/test_poll.c @@ -29,13 +29,15 @@ struct fifo_msg { #define FIFO_MSG_VALUE 0xdeadbeef /* verify k_poll() without waiting */ -static struct k_sem no_wait_sem; -static struct k_fifo no_wait_fifo; -static struct k_poll_signal no_wait_signal; +static __kernel struct k_sem no_wait_sem; +static __kernel struct k_fifo no_wait_fifo; +static __kernel struct k_poll_signal no_wait_signal; void test_poll_no_wait(void) { struct fifo_msg msg = { NULL, FIFO_MSG_VALUE }, *msg_ptr; + unsigned int signaled; + int result; k_sem_init(&no_wait_sem, 1, 1); k_fifo_init(&no_wait_fifo); @@ -54,7 +56,7 @@ void test_poll_no_wait(void) }; /* test polling events that are already ready */ - k_fifo_put(&no_wait_fifo, &msg); + zassert_false(k_fifo_alloc_put(&no_wait_fifo, &msg), NULL); k_poll_signal(&no_wait_signal, SIGNAL_RESULT); zassert_equal(k_poll(events, ARRAY_SIZE(events), 0), 0, ""); @@ -69,14 +71,15 @@ void test_poll_no_wait(void) zassert_equal(msg_ptr->msg, FIFO_MSG_VALUE, ""); zassert_equal(events[2].state, K_POLL_STATE_SIGNALED, ""); - zassert_equal(no_wait_signal.signaled, 1, ""); - zassert_equal(no_wait_signal.result, SIGNAL_RESULT, ""); + k_poll_signal_check(&no_wait_signal, &signaled, &result); + zassert_not_equal(signaled, 0, ""); + zassert_equal(result, SIGNAL_RESULT, ""); /* verify events are not ready anymore (user has to clear them first) */ events[0].state = K_POLL_STATE_NOT_READY; events[1].state = K_POLL_STATE_NOT_READY; events[2].state = K_POLL_STATE_NOT_READY; - no_wait_signal.signaled = 0; + k_poll_signal_reset(&no_wait_signal); zassert_equal(k_poll(events, ARRAY_SIZE(events), 0), -EAGAIN, ""); zassert_equal(events[0].state, K_POLL_STATE_NOT_READY, ""); @@ -91,12 +94,12 @@ void test_poll_no_wait(void) static K_SEM_DEFINE(wait_sem, 0, 1); static K_FIFO_DEFINE(wait_fifo); -static struct k_poll_signal wait_signal = +static __kernel struct k_poll_signal wait_signal = K_POLL_SIGNAL_INITIALIZER(wait_signal); struct fifo_msg wait_msg = { NULL, FIFO_MSG_VALUE }; -static struct k_thread poll_wait_helper_thread; +static __kernel struct k_thread poll_wait_helper_thread; static K_THREAD_STACK_DEFINE(poll_wait_helper_stack, KB(1)); #define TAG_0 10 @@ -124,7 +127,7 @@ static void poll_wait_helper(void *use_fifo, void *p2, void *p3) k_sem_give(&wait_sem); if ((intptr_t)use_fifo) { - k_fifo_put(&wait_fifo, &wait_msg); + k_fifo_alloc_put(&wait_fifo, &wait_msg); } k_poll_signal(&wait_signal, SIGNAL_RESULT); @@ -139,6 +142,7 @@ void test_poll_wait(void) int old_prio = k_thread_priority_get(k_current_get()); + k_poll_signal_init(&wait_signal); /* * Wait for 3 non-ready events to become ready from a higher priority * thread. @@ -148,7 +152,7 @@ void test_poll_wait(void) k_thread_create(&poll_wait_helper_thread, poll_wait_helper_stack, K_THREAD_STACK_SIZEOF(poll_wait_helper_stack), poll_wait_helper, (void *)1, 0, 0, - main_low_prio - 1, 0, 0); + main_low_prio - 1, K_USER | K_INHERIT_PERMS, 0); rc = k_poll(wait_events, ARRAY_SIZE(wait_events), K_SECONDS(1)); @@ -235,7 +239,8 @@ void test_poll_wait(void) k_thread_create(&poll_wait_helper_thread, poll_wait_helper_stack, K_THREAD_STACK_SIZEOF(poll_wait_helper_stack), poll_wait_helper, - (void *)1, 0, 0, old_prio + 1, 0, 0); + (void *)1, 0, 0, old_prio + 1, + K_USER | K_INHERIT_PERMS, 0); /* semaphore */ rc = k_poll(wait_events, ARRAY_SIZE(wait_events), K_SECONDS(1)); @@ -303,7 +308,7 @@ void test_poll_wait(void) static K_SEM_DEFINE(multi_sem, 0, 1); static K_SEM_DEFINE(multi_reply, 0, 1); -static struct k_thread multi_thread; +static __kernel struct k_thread multi_thread; static K_THREAD_STACK_DEFINE(multi_stack, KB(1)); static void multi(void *p1, void *p2, void *p3) @@ -341,7 +346,8 @@ void test_poll_multi(void) k_thread_create(&multi_thread, multi_stack, K_THREAD_STACK_SIZEOF(multi_stack), - multi, 0, 0, 0, main_low_prio - 1, 0, 0); + multi, 0, 0, 0, main_low_prio - 1, + K_USER | K_INHERIT_PERMS, 0); rc = k_poll(events, ARRAY_SIZE(events), K_SECONDS(1)); @@ -357,3 +363,13 @@ void test_poll_multi(void) zassert_equal(rc, 0, ""); } + +void test_poll_grant_access(void) +{ + k_thread_access_grant(k_current_get(), &no_wait_sem, &no_wait_fifo, + &no_wait_signal, &wait_sem, &wait_fifo, + &wait_signal, &poll_wait_helper_thread, + &poll_wait_helper_stack, &multi_sem, + &multi_reply, &multi_thread, &multi_stack, + NULL); +} diff --git a/tests/kernel/poll/testcase.yaml b/tests/kernel/poll/testcase.yaml index 3067ea400b8..60a148dab04 100644 --- a/tests/kernel/poll/testcase.yaml +++ b/tests/kernel/poll/testcase.yaml @@ -1,3 +1,3 @@ tests: kernel.poll: - tags: kernel + tags: kernel userspace