1// Copyright 2016 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <err.h>
8#include <inttypes.h>
9#include <trace.h>
10
11#include <kernel/event.h>
12#include <kernel/lockdep.h>
13#include <kernel/thread.h>
14#include <platform.h>
15
16#include <lib/ktrace.h>
17#include <lib/user_copy/user_ptr.h>
18
19#include <object/handle.h>
20#include <object/port_dispatcher.h>
21#include <object/process_dispatcher.h>
22#include <object/wait_state_observer.h>
23
24#include <fbl/inline_array.h>
25#include <fbl/ref_ptr.h>
26
27#include <zircon/types.h>
28
29#include "priv.h"
30
31#define LOCAL_TRACE 0
32
33// TODO(ZX-1349) Re-lower this to 8.
34constexpr uint32_t kMaxWaitHandleCount = 16u;
35
36// ensure public headers agree
37static_assert(ZX_WAIT_MANY_MAX_ITEMS == kMaxWaitHandleCount, "");
38
39zx_status_t sys_object_wait_one(zx_handle_t handle_value,
40                                zx_signals_t signals,
41                                zx_time_t deadline,
42                                user_out_ptr<zx_signals_t> observed) {
43    LTRACEF("handle %x\n", handle_value);
44
45    Event event;
46
47    zx_status_t result;
48    WaitStateObserver wait_state_observer;
49
50    auto up = ProcessDispatcher::GetCurrent();
51    {
52        Guard<fbl::Mutex> guard{up->handle_table_lock()};
53
54        Handle* handle = up->GetHandleLocked(handle_value);
55        if (!handle)
56            return ZX_ERR_BAD_HANDLE;
57        if (!handle->HasRights(ZX_RIGHT_WAIT))
58            return ZX_ERR_ACCESS_DENIED;
59
60        result = wait_state_observer.Begin(&event, handle, signals);
61        if (result != ZX_OK)
62            return result;
63    }
64
65    auto koid = static_cast<uint32_t>(up->GetKoidForHandle(handle_value));
66    ktrace(TAG_WAIT_ONE, koid, signals, (uint32_t)deadline, (uint32_t)(deadline >> 32));
67
68    // event_wait() will return ZX_OK if already signaled,
69    // even if the deadline has passed.  It will return ZX_ERR_TIMED_OUT
70    // after the deadline passes if the event has not been
71    // signaled.
72    {
73        ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_ONE);
74        result = event.Wait(deadline);
75    }
76
77    // Regardless of wait outcome, we must call End().
78    auto signals_state = wait_state_observer.End();
79
80    ktrace(TAG_WAIT_ONE_DONE, koid, signals_state, result, 0);
81
82    if (observed) {
83        zx_status_t status = observed.copy_to_user(signals_state);
84        if (status != ZX_OK)
85            return status;
86    }
87
88    if (signals_state & ZX_SIGNAL_HANDLE_CLOSED)
89        return ZX_ERR_CANCELED;
90
91    return result;
92}
93
94zx_status_t sys_object_wait_many(user_inout_ptr<zx_wait_item_t> user_items, size_t count, zx_time_t deadline) {
95    LTRACEF("count %zu\n", count);
96
97    if (!count) {
98        zx_status_t result = thread_sleep_etc(deadline, /*interruptable=*/true);
99        if (result != ZX_OK)
100            return result;
101        return ZX_ERR_TIMED_OUT;
102    }
103
104    if (count > kMaxWaitHandleCount)
105        return ZX_ERR_OUT_OF_RANGE;
106
107    zx_wait_item_t items[kMaxWaitHandleCount];
108    if (user_items.copy_array_from_user(items, count) != ZX_OK)
109        return ZX_ERR_INVALID_ARGS;
110
111    WaitStateObserver wait_state_observers[kMaxWaitHandleCount];
112    Event event;
113
114    // We may need to unwind (which can be done outside the lock).
115    zx_status_t result = ZX_OK;
116    size_t num_added = 0;
117    {
118        auto up = ProcessDispatcher::GetCurrent();
119        Guard<fbl::Mutex> guard{up->handle_table_lock()};
120
121        for (; num_added != count; ++num_added) {
122            Handle* handle = up->GetHandleLocked(items[num_added].handle);
123            if (!handle) {
124                result = ZX_ERR_BAD_HANDLE;
125                break;
126            }
127            if (!handle->HasRights(ZX_RIGHT_WAIT)) {
128                result = ZX_ERR_ACCESS_DENIED;
129                break;
130            }
131
132            result = wait_state_observers[num_added].Begin(&event, handle, items[num_added].waitfor);
133            if (result != ZX_OK)
134                break;
135        }
136    }
137    if (result != ZX_OK) {
138        for (size_t ix = 0; ix < num_added; ++ix)
139            wait_state_observers[ix].End();
140        return result;
141    }
142
143    // event_wait() will return ZX_OK if already signaled,
144    // even if deadline has passed.  It will return ZX_ERR_TIMED_OUT
145    // after the deadline passes if the event has not been
146    // signaled.
147    {
148        ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_MANY);
149        result = event.Wait(deadline);
150    }
151
152    // Regardless of wait outcome, we must call End().
153    zx_signals_t combined = 0;
154    for (size_t ix = 0; ix != count; ++ix) {
155        combined |= (items[ix].pending = wait_state_observers[ix].End());
156    }
157
158    if (user_items.copy_array_to_user(items, count) != ZX_OK)
159        return ZX_ERR_INVALID_ARGS;
160
161    if (combined & ZX_SIGNAL_HANDLE_CLOSED)
162        return ZX_ERR_CANCELED;
163
164    return result;
165}
166
167zx_status_t sys_object_wait_async(zx_handle_t handle_value, zx_handle_t port_handle,
168                                  uint64_t key, zx_signals_t signals, uint32_t options) {
169    LTRACEF("handle %x\n", handle_value);
170
171    auto up = ProcessDispatcher::GetCurrent();
172
173    fbl::RefPtr<PortDispatcher> port;
174    auto status = up->GetDispatcherWithRights(port_handle, ZX_RIGHT_WRITE, &port);
175    if (status != ZX_OK)
176        return status;
177
178    {
179        Guard<fbl::Mutex> guard{up->handle_table_lock()};
180        Handle* handle = up->GetHandleLocked(handle_value);
181        if (!handle)
182            return ZX_ERR_BAD_HANDLE;
183        if (!handle->HasRights(ZX_RIGHT_WAIT))
184            return ZX_ERR_ACCESS_DENIED;
185
186        return port->MakeObserver(options, handle, key, signals);
187    }
188}
189