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.
34 constexpr uint32_t kMaxWaitHandleCount = 16u;
35 
36 // ensure public headers agree
37 static_assert(ZX_WAIT_MANY_MAX_ITEMS == kMaxWaitHandleCount, "");
38 
39 // zx_status_t zx_object_wait_one
sys_object_wait_one(zx_handle_t handle_value,zx_signals_t signals,zx_time_t deadline,user_out_ptr<zx_signals_t> observed)40 zx_status_t sys_object_wait_one(zx_handle_t handle_value,
41                                 zx_signals_t signals,
42                                 zx_time_t deadline,
43                                 user_out_ptr<zx_signals_t> observed) {
44     LTRACEF("handle %x\n", handle_value);
45 
46     Event event;
47 
48     zx_status_t result;
49     WaitStateObserver wait_state_observer;
50 
51     auto up = ProcessDispatcher::GetCurrent();
52     {
53         Guard<fbl::Mutex> guard{up->handle_table_lock()};
54 
55         Handle* handle = up->GetHandleLocked(handle_value);
56         if (!handle)
57             return ZX_ERR_BAD_HANDLE;
58         if (!handle->HasRights(ZX_RIGHT_WAIT))
59             return ZX_ERR_ACCESS_DENIED;
60 
61         result = wait_state_observer.Begin(&event, handle, signals);
62         if (result != ZX_OK)
63             return result;
64     }
65 
66     auto koid = static_cast<uint32_t>(up->GetKoidForHandle(handle_value));
67     ktrace(TAG_WAIT_ONE, koid, signals, (uint32_t)deadline, (uint32_t)(deadline >> 32));
68 
69     const TimerSlack slack = up->GetTimerSlackPolicy();
70 
71     // event_wait() will return ZX_OK if already signaled,
72     // even if the deadline has passed.  It will return ZX_ERR_TIMED_OUT
73     // after the deadline passes if the event has not been
74     // signaled.
75     {
76         ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_ONE);
77         result = event.Wait(deadline, slack);
78     }
79 
80     // Regardless of wait outcome, we must call End().
81     auto signals_state = wait_state_observer.End();
82 
83     ktrace(TAG_WAIT_ONE_DONE, koid, signals_state, result, 0);
84 
85     if (observed) {
86         zx_status_t status = observed.copy_to_user(signals_state);
87         if (status != ZX_OK)
88             return status;
89     }
90 
91     if (signals_state & ZX_SIGNAL_HANDLE_CLOSED)
92         return ZX_ERR_CANCELED;
93 
94     return result;
95 }
96 
97 // zx_status_t zx_object_wait_many
sys_object_wait_many(user_inout_ptr<zx_wait_item_t> user_items,size_t count,zx_time_t deadline)98 zx_status_t sys_object_wait_many(user_inout_ptr<zx_wait_item_t> user_items, size_t count, zx_time_t deadline) {
99     LTRACEF("count %zu\n", count);
100 
101     if (!count) {
102         // TODO(maniscalco): Replace this call with a call to thread_sleep_etc using the timer slack
103         // from the job policy (ZX-931).
104         zx_status_t result = thread_sleep_interruptable(deadline);
105         if (result != ZX_OK)
106             return result;
107         return ZX_ERR_TIMED_OUT;
108     }
109 
110     if (count > kMaxWaitHandleCount)
111         return ZX_ERR_OUT_OF_RANGE;
112 
113     zx_wait_item_t items[kMaxWaitHandleCount];
114     if (user_items.copy_array_from_user(items, count) != ZX_OK)
115         return ZX_ERR_INVALID_ARGS;
116 
117     WaitStateObserver wait_state_observers[kMaxWaitHandleCount];
118     Event event;
119 
120     auto up = ProcessDispatcher::GetCurrent();
121 
122     // We may need to unwind (which can be done outside the lock).
123     zx_status_t result = ZX_OK;
124     size_t num_added = 0;
125     {
126         Guard<fbl::Mutex> guard{up->handle_table_lock()};
127 
128         for (; num_added != count; ++num_added) {
129             Handle* handle = up->GetHandleLocked(items[num_added].handle);
130             if (!handle) {
131                 result = ZX_ERR_BAD_HANDLE;
132                 break;
133             }
134             if (!handle->HasRights(ZX_RIGHT_WAIT)) {
135                 result = ZX_ERR_ACCESS_DENIED;
136                 break;
137             }
138 
139             result = wait_state_observers[num_added].Begin(&event, handle, items[num_added].waitfor);
140             if (result != ZX_OK)
141                 break;
142         }
143     }
144     if (result != ZX_OK) {
145         for (size_t ix = 0; ix < num_added; ++ix)
146             wait_state_observers[ix].End();
147         return result;
148     }
149 
150     const TimerSlack slack = up->GetTimerSlackPolicy();
151 
152     // event_wait() will return ZX_OK if already signaled,
153     // even if deadline has passed.  It will return ZX_ERR_TIMED_OUT
154     // after the deadline passes if the event has not been
155     // signaled.
156     {
157         ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::WAIT_MANY);
158         result = event.Wait(deadline, slack);
159     }
160 
161     // Regardless of wait outcome, we must call End().
162     zx_signals_t combined = 0;
163     for (size_t ix = 0; ix != count; ++ix) {
164         combined |= (items[ix].pending = wait_state_observers[ix].End());
165     }
166 
167     if (user_items.copy_array_to_user(items, count) != ZX_OK)
168         return ZX_ERR_INVALID_ARGS;
169 
170     if (combined & ZX_SIGNAL_HANDLE_CLOSED)
171         return ZX_ERR_CANCELED;
172 
173     return result;
174 }
175 
176 // zx_status_t zx_object_wait_async
sys_object_wait_async(zx_handle_t handle_value,zx_handle_t port_handle,uint64_t key,zx_signals_t signals,uint32_t options)177 zx_status_t sys_object_wait_async(zx_handle_t handle_value, zx_handle_t port_handle,
178                                   uint64_t key, zx_signals_t signals, uint32_t options) {
179     LTRACEF("handle %x\n", handle_value);
180 
181     auto up = ProcessDispatcher::GetCurrent();
182 
183     fbl::RefPtr<PortDispatcher> port;
184     auto status = up->GetDispatcherWithRights(port_handle, ZX_RIGHT_WRITE, &port);
185     if (status != ZX_OK)
186         return status;
187 
188     {
189         Guard<fbl::Mutex> guard{up->handle_table_lock()};
190         Handle* handle = up->GetHandleLocked(handle_value);
191         if (!handle)
192             return ZX_ERR_BAD_HANDLE;
193         if (!handle->HasRights(ZX_RIGHT_WAIT))
194             return ZX_ERR_ACCESS_DENIED;
195 
196         return port->MakeObserver(options, handle, key, signals);
197     }
198 }
199