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