1// Copyright 2017 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 <object/fifo_dispatcher.h> 8 9#include <string.h> 10 11#include <zircon/rights.h> 12#include <fbl/alloc_checker.h> 13#include <fbl/auto_lock.h> 14#include <object/handle.h> 15 16// static 17zx_status_t FifoDispatcher::Create(size_t count, size_t elemsize, uint32_t options, 18 fbl::RefPtr<Dispatcher>* dispatcher0, 19 fbl::RefPtr<Dispatcher>* dispatcher1, 20 zx_rights_t* rights) { 21 // count and elemsize must be nonzero 22 // count must be a power of two 23 // total size must be <= kMaxSizeBytes 24 if (!count || !elemsize || (count & (count - 1)) || 25 (count > kMaxSizeBytes) || (elemsize > kMaxSizeBytes) || 26 ((count * elemsize) > kMaxSizeBytes)) { 27 return ZX_ERR_OUT_OF_RANGE; 28 } 29 30 fbl::AllocChecker ac; 31 auto holder0 = fbl::AdoptRef(new (&ac) PeerHolder<FifoDispatcher>()); 32 if (!ac.check()) 33 return ZX_ERR_NO_MEMORY; 34 auto holder1 = holder0; 35 36 auto data0 = fbl::unique_ptr<uint8_t[]>(new (&ac) uint8_t[count * elemsize]); 37 if (!ac.check()) 38 return ZX_ERR_NO_MEMORY; 39 40 auto fifo0 = fbl::AdoptRef(new (&ac) FifoDispatcher(fbl::move(holder0), options, static_cast<uint32_t>(count), 41 static_cast<uint32_t>(elemsize), fbl::move(data0))); 42 if (!ac.check()) 43 return ZX_ERR_NO_MEMORY; 44 45 auto data1 = fbl::unique_ptr<uint8_t[]>(new (&ac) uint8_t[count * elemsize]); 46 if (!ac.check()) 47 return ZX_ERR_NO_MEMORY; 48 49 auto fifo1 = fbl::AdoptRef(new (&ac) FifoDispatcher(fbl::move(holder1), options, static_cast<uint32_t>(count), 50 static_cast<uint32_t>(elemsize), fbl::move(data1))); 51 if (!ac.check()) 52 return ZX_ERR_NO_MEMORY; 53 54 fifo0->Init(fifo1); 55 fifo1->Init(fifo0); 56 57 *rights = ZX_DEFAULT_FIFO_RIGHTS; 58 *dispatcher0 = fbl::move(fifo0); 59 *dispatcher1 = fbl::move(fifo1); 60 return ZX_OK; 61} 62 63FifoDispatcher::FifoDispatcher(fbl::RefPtr<PeerHolder<FifoDispatcher>> holder, 64 uint32_t /*options*/, uint32_t count, uint32_t elem_size, 65 fbl::unique_ptr<uint8_t[]> data) 66 : PeeredDispatcher(fbl::move(holder), ZX_FIFO_WRITABLE), 67 elem_count_(count), elem_size_(elem_size), mask_(count - 1), 68 head_(0u), tail_(0u), data_(fbl::move(data)) { 69} 70 71FifoDispatcher::~FifoDispatcher() { 72} 73 74// Thread safety analysis disabled as this happens during creation only, 75// when no other thread could be accessing the object. 76void FifoDispatcher::Init(fbl::RefPtr<FifoDispatcher> other) TA_NO_THREAD_SAFETY_ANALYSIS { 77 peer_ = fbl::move(other); 78 peer_koid_ = peer_->get_koid(); 79} 80 81zx_status_t FifoDispatcher::UserSignalSelfLocked(uint32_t clear_mask, uint32_t set_mask) { 82 canary_.Assert(); 83 UpdateStateLocked(clear_mask, set_mask); 84 return ZX_OK; 85} 86 87void FifoDispatcher::on_zero_handles_locked() { 88 canary_.Assert(); 89} 90 91void FifoDispatcher::OnPeerZeroHandlesLocked() { 92 canary_.Assert(); 93 94 UpdateStateLocked(ZX_FIFO_WRITABLE, ZX_FIFO_PEER_CLOSED); 95} 96 97zx_status_t FifoDispatcher::WriteFromUser(size_t elem_size, user_in_ptr<const uint8_t> ptr, 98 size_t count, size_t* actual) 99 TA_NO_THREAD_SAFETY_ANALYSIS { 100 canary_.Assert(); 101 102 Guard<fbl::Mutex> guard{get_lock()}; 103 if (!peer_) 104 return ZX_ERR_PEER_CLOSED; 105 return peer_->WriteSelfLocked(elem_size, ptr, count, actual); 106} 107 108zx_status_t FifoDispatcher::WriteSelfLocked(size_t elem_size, user_in_ptr<const uint8_t> ptr, 109 size_t count, size_t* actual) 110 TA_NO_THREAD_SAFETY_ANALYSIS { 111 canary_.Assert(); 112 113 if (elem_size != elem_size_) 114 return ZX_ERR_OUT_OF_RANGE; 115 if (count == 0) 116 return ZX_ERR_OUT_OF_RANGE; 117 118 uint32_t old_head = head_; 119 120 // total number of available empty slots in the fifo 121 size_t avail = elem_count_ - (head_ - tail_); 122 123 if (avail == 0) 124 return ZX_ERR_SHOULD_WAIT; 125 126 bool was_empty = (avail == elem_count_); 127 128 if (count > avail) 129 count = avail; 130 131 while (count > 0) { 132 uint32_t offset = (head_ & mask_); 133 134 // number of slots from target to end, inclusive 135 uint32_t n = elem_count_ - offset; 136 137 // number of slots we can actually copy 138 size_t to_copy = (count > n) ? n : count; 139 140 zx_status_t status = ptr.copy_array_from_user(&data_[offset * elem_size_], 141 to_copy * elem_size_); 142 if (status != ZX_OK) { 143 // roll back, in case this is the second copy 144 head_ = old_head; 145 return ZX_ERR_INVALID_ARGS; 146 } 147 148 // adjust head and count 149 // due to size limitations on fifo, to_copy will always fit in a u32 150 head_ += static_cast<uint32_t>(to_copy); 151 count -= to_copy; 152 ptr = ptr.byte_offset(to_copy * elem_size_); 153 } 154 155 // if was empty, we've become readable 156 if (was_empty) 157 UpdateStateLocked(0u, ZX_FIFO_READABLE); 158 159 // if now full, we're no longer writable 160 if (elem_count_ == (head_ - tail_)) 161 peer_->UpdateStateLocked(ZX_FIFO_WRITABLE, 0u); 162 163 *actual = (head_ - old_head); 164 return ZX_OK; 165} 166 167zx_status_t FifoDispatcher::ReadToUser(size_t elem_size, user_out_ptr<uint8_t> ptr, size_t count, 168 size_t* actual) 169 TA_NO_THREAD_SAFETY_ANALYSIS { 170 canary_.Assert(); 171 172 if (elem_size != elem_size_) 173 return ZX_ERR_OUT_OF_RANGE; 174 if (count == 0) 175 return ZX_ERR_OUT_OF_RANGE; 176 177 Guard<fbl::Mutex> guard{get_lock()}; 178 179 uint32_t old_tail = tail_; 180 181 // total number of available entries to read from the fifo 182 size_t avail = (head_ - tail_); 183 184 if (avail == 0) 185 return peer_ ? ZX_ERR_SHOULD_WAIT : ZX_ERR_PEER_CLOSED; 186 187 bool was_full = (avail == elem_count_); 188 189 if (count > avail) 190 count = avail; 191 192 while (count > 0) { 193 uint32_t offset = (tail_ & mask_); 194 195 // number of slots from target to end, inclusive 196 uint32_t n = elem_count_ - offset; 197 198 // number of slots we can actually copy 199 size_t to_copy = (count > n) ? n : count; 200 201 zx_status_t status = ptr.copy_array_to_user(&data_[offset * elem_size_], 202 to_copy * elem_size_); 203 if (status != ZX_OK) { 204 // roll back, in case this is the second copy 205 tail_ = old_tail; 206 return ZX_ERR_INVALID_ARGS; 207 } 208 209 // adjust tail and count 210 // due to size limitations on fifo, to_copy will always fit in a u32 211 tail_ += static_cast<uint32_t>(to_copy); 212 count -= to_copy; 213 ptr = ptr.byte_offset(to_copy * elem_size_); 214 } 215 216 // if we were full, we have become writable 217 if (was_full && peer_) 218 peer_->UpdateStateLocked(0u, ZX_FIFO_WRITABLE); 219 220 // if we've become empty, we're no longer readable 221 if ((head_ - tail_) == 0) 222 UpdateStateLocked(ZX_FIFO_READABLE, 0u); 223 224 *actual = (tail_ - old_tail); 225 return ZX_OK; 226} 227