| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <inttypes.h> |
| #include <lib/ktrace.h> |
| #include <lib/syscalls/forward.h> |
| #include <lib/user_copy/user_ptr.h> |
| #include <platform.h> |
| #include <trace.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/inline_array.h> |
| #include <fbl/ref_ptr.h> |
| #include <kernel/event.h> |
| #include <kernel/lockdep.h> |
| #include <kernel/thread.h> |
| #include <object/dispatcher.h> |
| #include <object/handle.h> |
| #include <object/port_dispatcher.h> |
| #include <object/process_dispatcher.h> |
| #include <object/wait_signal_observer.h> |
| |
| #define LOCAL_TRACE 0 |
| |
| constexpr uint32_t kMaxWaitHandleCount = ZX_WAIT_MANY_MAX_ITEMS; |
| |
| static_assert(ZX_WAIT_MANY_MAX_ITEMS == ZX_CHANNEL_MAX_MSG_HANDLES); |
| |
| // zx_status_t zx_object_wait_one |
| zx_status_t sys_object_wait_one(zx_handle_t handle_value, zx_signals_t signals, |
| zx_instant_mono_t deadline, user_out_ptr<zx_signals_t> observed) { |
| LTRACEF("handle %x\n", handle_value); |
| |
| Event event; |
| |
| zx_status_t result; |
| WaitSignalObserver wait_signal_observer; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| { |
| Guard<BrwLockPi, BrwLockPi::Reader> guard{up->handle_table().get_lock()}; |
| |
| Handle* handle = up->handle_table().GetHandleLocked(*up, handle_value); |
| if (!handle) |
| return ZX_ERR_BAD_HANDLE; |
| if (!handle->HasRights(ZX_RIGHT_WAIT)) |
| return ZX_ERR_ACCESS_DENIED; |
| |
| result = wait_signal_observer.Begin(&event, handle, signals); |
| if (result != ZX_OK) |
| return result; |
| } |
| |
| const TimerSlack slack = up->GetTimerSlackPolicy(); |
| const Deadline slackDeadline(deadline, slack); |
| |
| // Event::Wait() will return ZX_OK if already signaled, |
| // even if the deadline has passed. It will return ZX_ERR_TIMED_OUT |
| // after the deadline passes if the event has not been |
| // signaled. |
| { |
| ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_ONE); |
| result = event.Wait(slackDeadline); |
| } |
| |
| // Regardless of wait outcome, we must call End(). |
| auto signals_state = wait_signal_observer.End(); |
| |
| if (observed) { |
| zx_status_t status = observed.copy_to_user(signals_state); |
| if (status != ZX_OK) |
| return status; |
| } |
| |
| if (signals_state & ZX_SIGNAL_HANDLE_CLOSED) |
| return ZX_ERR_CANCELED; |
| |
| return result; |
| } |
| |
| // zx_status_t zx_object_wait_many |
| zx_status_t sys_object_wait_many(user_inout_ptr<zx_wait_item_t> user_items, size_t count, |
| zx_instant_mono_t deadline) { |
| LTRACEF("count %zu\n", count); |
| |
| const auto up = ProcessDispatcher::GetCurrent(); |
| const Deadline slackDeadline(deadline, up->GetTimerSlackPolicy()); |
| |
| if (!count) { |
| const zx_instant_mono_t now = current_time(); |
| { |
| ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_MANY); |
| zx_status_t result = Thread::Current::SleepEtc(slackDeadline, Interruptible::Yes, now); |
| if (result != ZX_OK) { |
| return result; |
| } |
| } |
| return ZX_ERR_TIMED_OUT; |
| } |
| |
| if (count > kMaxWaitHandleCount) |
| return ZX_ERR_OUT_OF_RANGE; |
| |
| zx_wait_item_t items[kMaxWaitHandleCount]; |
| if (user_items.copy_array_from_user(items, count) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| // WaitSignalObserver is heavier than it looks so make sure we know how |
| // much stack InlineArray is going to use, given limited kernel stack size. |
| static_assert(sizeof(WaitSignalObserver) * 8 < 640); |
| |
| fbl::AllocChecker ac; |
| fbl::InlineArray<WaitSignalObserver, 8u> wait_signal_observers(&ac, count); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| Event event; |
| |
| // We may need to unwind (which can be done outside the lock). |
| zx_status_t result = ZX_OK; |
| size_t num_added = 0; |
| { |
| Guard<BrwLockPi, BrwLockPi::Reader> guard{up->handle_table().get_lock()}; |
| |
| for (; num_added != count; ++num_added) { |
| Handle* handle = up->handle_table().GetHandleLocked(*up, items[num_added].handle); |
| if (!handle) { |
| result = ZX_ERR_BAD_HANDLE; |
| break; |
| } |
| if (!handle->HasRights(ZX_RIGHT_WAIT)) { |
| result = ZX_ERR_ACCESS_DENIED; |
| break; |
| } |
| |
| result = wait_signal_observers[num_added].Begin(&event, handle, items[num_added].waitfor); |
| if (result != ZX_OK) |
| break; |
| } |
| } |
| if (result != ZX_OK) { |
| for (size_t ix = 0; ix < num_added; ++ix) |
| wait_signal_observers[ix].End(); |
| return result; |
| } |
| |
| // Event::Wait() will return ZX_OK if already signaled, |
| // even if deadline has passed. It will return ZX_ERR_TIMED_OUT |
| // after the deadline passes if the event has not been |
| // signaled. |
| { |
| ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_MANY); |
| result = event.Wait(slackDeadline); |
| } |
| |
| // Regardless of wait outcome, we must call End(). |
| zx_signals_t combined = 0; |
| for (size_t ix = 0; ix != count; ++ix) { |
| combined |= (items[ix].pending = wait_signal_observers[ix].End()); |
| } |
| |
| if (user_items.copy_array_to_user(items, count) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| if (combined & ZX_SIGNAL_HANDLE_CLOSED) |
| return ZX_ERR_CANCELED; |
| |
| return result; |
| } |
| |
| // zx_status_t zx_object_wait_async |
| zx_status_t sys_object_wait_async(zx_handle_t handle_value, zx_handle_t port_handle_value, |
| uint64_t key, zx_signals_t signals, uint32_t options) { |
| LTRACEF("handle %x\n", handle_value); |
| |
| if ((options & ~(ZX_WAIT_ASYNC_TIMESTAMP | ZX_WAIT_ASYNC_BOOT_TIMESTAMP | ZX_WAIT_ASYNC_EDGE)) != |
| 0u) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // It is invalid to request both a monotonic and boot timestamp. |
| if ((options & ZX_WAIT_ASYNC_TIMESTAMP) != 0 && (options & ZX_WAIT_ASYNC_BOOT_TIMESTAMP) != 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| { |
| Guard<BrwLockPi, BrwLockPi::Reader> guard{up->handle_table().get_lock()}; |
| |
| // Note, we're doing this all while holding the handle table lock for two reasons. |
| // |
| // First, this thread may be racing with another thread that's closing the last handle to |
| // the port. By holding the lock we can ensure that this syscall behaves as if the port was |
| // closed just *before* the syscall started or closed just *after* it has completed. |
| // |
| // Second, MakeObserver takes a Handle. By holding the lock we ensure the Handle isn't |
| // destroyed out from under it. |
| |
| Handle* port_handle = up->handle_table().GetHandleLocked(*up, port_handle_value); |
| if (!port_handle) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| fbl::RefPtr<Dispatcher> disp = port_handle->dispatcher(); |
| fbl::RefPtr<PortDispatcher> port = DownCastDispatcher<PortDispatcher>(&disp); |
| if (!port) { |
| return ZX_ERR_WRONG_TYPE; |
| } |
| if (!port_handle->HasRights(ZX_RIGHT_WRITE)) { |
| return ZX_ERR_ACCESS_DENIED; |
| } |
| |
| Handle* handle = up->handle_table().GetHandleLocked(*up, handle_value); |
| if (!handle) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| if (!handle->HasRights(ZX_RIGHT_WAIT)) { |
| return ZX_ERR_ACCESS_DENIED; |
| } |
| |
| return port->MakeObserver(options, handle, key, signals); |
| } |
| } |