1 /* 2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #include <IOKit/IOSharedDataQueue.h> 30 #include <IOKit/IODataQueueShared.h> 31 #include <IOKit/IOLib.h> 32 #include <IOKit/IOMemoryDescriptor.h> 33 34 #ifdef enqueue 35 #undef enqueue 36 #endif 37 38 #ifdef dequeue 39 #undef dequeue 40 #endif 41 42 #define super IODataQueue 43 44 OSDefineMetaClassAndStructors(IOSharedDataQueue, IODataQueue) 45 46 IOSharedDataQueue *IOSharedDataQueue::withCapacity(UInt32 size) 47 { 48 IOSharedDataQueue *dataQueue = new IOSharedDataQueue; 49 50 if (dataQueue) { 51 if (!dataQueue->initWithCapacity(size)) { 52 dataQueue->release(); 53 dataQueue = 0; 54 } 55 } 56 57 return dataQueue; 58 } 59 60 IOSharedDataQueue *IOSharedDataQueue::withEntries(UInt32 numEntries, UInt32 entrySize) 61 { 62 IOSharedDataQueue *dataQueue = new IOSharedDataQueue; 63 64 if (dataQueue) { 65 if (!dataQueue->initWithEntries(numEntries, entrySize)) { 66 dataQueue->release(); 67 dataQueue = 0; 68 } 69 } 70 71 return dataQueue; 72 } 73 74 Boolean IOSharedDataQueue::initWithCapacity(UInt32 size) 75 { 76 IODataQueueAppendix * appendix; 77 vm_size_t allocSize; 78 79 if (!super::init()) { 80 return false; 81 } 82 83 _reserved = (ExpansionData *)IOMalloc(sizeof(struct ExpansionData)); 84 if (!_reserved) { 85 return false; 86 } 87 88 if (size > UINT32_MAX - DATA_QUEUE_MEMORY_HEADER_SIZE - DATA_QUEUE_MEMORY_APPENDIX_SIZE) { 89 return false; 90 } 91 92 allocSize = round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE); 93 94 if (allocSize < size) { 95 return false; 96 } 97 98 dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE); 99 if (dataQueue == 0) { 100 return false; 101 } 102 bzero(dataQueue, allocSize); 103 104 dataQueue->queueSize = size; 105 // dataQueue->head = 0; 106 // dataQueue->tail = 0; 107 108 if (!setQueueSize(size)) { 109 return false; 110 } 111 112 appendix = (IODataQueueAppendix *)((UInt8 *)dataQueue + size + DATA_QUEUE_MEMORY_HEADER_SIZE); 113 appendix->version = 0; 114 115 if (!notifyMsg) { 116 notifyMsg = IOMalloc(sizeof(mach_msg_header_t)); 117 if (!notifyMsg) 118 return false; 119 } 120 bzero(notifyMsg, sizeof(mach_msg_header_t)); 121 122 setNotificationPort(MACH_PORT_NULL); 123 124 return true; 125 } 126 127 void IOSharedDataQueue::free() 128 { 129 if (dataQueue) { 130 IOFreeAligned(dataQueue, round_page(getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE)); 131 dataQueue = NULL; 132 if (notifyMsg) { 133 IOFree(notifyMsg, sizeof(mach_msg_header_t)); 134 notifyMsg = NULL; 135 } 136 } 137 138 if (_reserved) { 139 IOFree (_reserved, sizeof(struct ExpansionData)); 140 _reserved = NULL; 141 } 142 143 super::free(); 144 } 145 146 IOMemoryDescriptor *IOSharedDataQueue::getMemoryDescriptor() 147 { 148 IOMemoryDescriptor *descriptor = 0; 149 150 if (dataQueue != 0) { 151 descriptor = IOMemoryDescriptor::withAddress(dataQueue, getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE, kIODirectionOutIn); 152 } 153 154 return descriptor; 155 } 156 157 158 IODataQueueEntry * IOSharedDataQueue::peek() 159 { 160 IODataQueueEntry *entry = 0; 161 162 if (dataQueue && (dataQueue->head != dataQueue->tail)) { 163 IODataQueueEntry * head = 0; 164 UInt32 headSize = 0; 165 UInt32 headOffset = dataQueue->head; 166 UInt32 queueSize = getQueueSize(); 167 168 if (headOffset >= queueSize) { 169 return NULL; 170 } 171 172 head = (IODataQueueEntry *)((char *)dataQueue->queue + headOffset); 173 headSize = head->size; 174 175 // Check if there's enough room before the end of the queue for a header. 176 // If there is room, check if there's enough room to hold the header and 177 // the data. 178 179 if ((headOffset > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) || 180 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize) || 181 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headSize) || 182 (headOffset + headSize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) { 183 // No room for the header or the data, wrap to the beginning of the queue. 184 // Note: wrapping even with the UINT32_MAX checks, as we have to support 185 // queueSize of UINT32_MAX 186 entry = dataQueue->queue; 187 } else { 188 entry = head; 189 } 190 } 191 192 return entry; 193 } 194 195 Boolean IOSharedDataQueue::enqueue(void * data, UInt32 dataSize) 196 { 197 const UInt32 head = dataQueue->head; // volatile 198 const UInt32 tail = dataQueue->tail; 199 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE; 200 IODataQueueEntry * entry; 201 202 // Check for overflow of entrySize 203 if (dataSize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) { 204 return false; 205 } 206 // Check for underflow of (getQueueSize() - tail) 207 if (getQueueSize() < tail || getQueueSize() < head) { 208 return false; 209 } 210 211 if ( tail >= head ) 212 { 213 // Is there enough room at the end for the entry? 214 if ((entrySize <= UINT32_MAX - tail) && 215 ((tail + entrySize) <= getQueueSize()) ) 216 { 217 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail); 218 219 entry->size = dataSize; 220 memcpy(&entry->data, data, dataSize); 221 222 // The tail can be out of bound when the size of the new entry 223 // exactly matches the available space at the end of the queue. 224 // The tail can range from 0 to dataQueue->queueSize inclusive. 225 226 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail); 227 } 228 else if ( head > entrySize ) // Is there enough room at the beginning? 229 { 230 // Wrap around to the beginning, but do not allow the tail to catch 231 // up to the head. 232 233 dataQueue->queue->size = dataSize; 234 235 // We need to make sure that there is enough room to set the size before 236 // doing this. The user client checks for this and will look for the size 237 // at the beginning if there isn't room for it at the end. 238 239 if ( ( getQueueSize() - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE ) 240 { 241 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize; 242 } 243 244 memcpy(&dataQueue->queue->data, data, dataSize); 245 OSCompareAndSwap(dataQueue->tail, entrySize, &dataQueue->tail); 246 } 247 else 248 { 249 return false; // queue is full 250 } 251 } 252 else 253 { 254 // Do not allow the tail to catch up to the head when the queue is full. 255 // That's why the comparison uses a '>' rather than '>='. 256 257 if ( (head - tail) > entrySize ) 258 { 259 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail); 260 261 entry->size = dataSize; 262 memcpy(&entry->data, data, dataSize); 263 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail); 264 } 265 else 266 { 267 return false; // queue is full 268 } 269 } 270 271 // Send notification (via mach message) that data is available. 272 273 if ( ( head == tail ) /* queue was empty prior to enqueue() */ 274 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */ 275 { 276 sendDataAvailableNotification(); 277 } 278 279 return true; 280 } 281 282 Boolean IOSharedDataQueue::dequeue(void *data, UInt32 *dataSize) 283 { 284 Boolean retVal = TRUE; 285 IODataQueueEntry * entry = 0; 286 UInt32 entrySize = 0; 287 UInt32 newHeadOffset = 0; 288 289 if (dataQueue) { 290 if (dataQueue->head != dataQueue->tail) { 291 IODataQueueEntry * head = 0; 292 UInt32 headSize = 0; 293 UInt32 headOffset = dataQueue->head; 294 UInt32 queueSize = getQueueSize(); 295 296 if (headOffset > queueSize) { 297 return false; 298 } 299 300 head = (IODataQueueEntry *)((char *)dataQueue->queue + headOffset); 301 headSize = head->size; 302 303 // we wrapped around to beginning, so read from there 304 // either there was not even room for the header 305 if ((headOffset > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) || 306 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize) || 307 // or there was room for the header, but not for the data 308 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headSize) || 309 (headOffset + headSize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) { 310 // Note: we have to wrap to the beginning even with the UINT32_MAX checks 311 // because we have to support a queueSize of UINT32_MAX. 312 entry = dataQueue->queue; 313 entrySize = entry->size; 314 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) || 315 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) { 316 return false; 317 } 318 newHeadOffset = entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE; 319 // else it is at the end 320 } else { 321 entry = head; 322 entrySize = entry->size; 323 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) || 324 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headOffset) || 325 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE + headOffset > queueSize)) { 326 return false; 327 } 328 newHeadOffset = headOffset + entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE; 329 } 330 } 331 332 if (entry) { 333 if (data) { 334 if (dataSize) { 335 if (entrySize <= *dataSize) { 336 memcpy(data, &(entry->data), entrySize); 337 OSCompareAndSwap( dataQueue->head, newHeadOffset, (SInt32 *)&dataQueue->head); 338 } else { 339 retVal = FALSE; 340 } 341 } else { 342 retVal = FALSE; 343 } 344 } else { 345 OSCompareAndSwap( dataQueue->head, newHeadOffset, (SInt32 *)&dataQueue->head); 346 } 347 348 if (dataSize) { 349 *dataSize = entrySize; 350 } 351 } else { 352 retVal = FALSE; 353 } 354 } else { 355 retVal = FALSE; 356 } 357 358 return retVal; 359 } 360 361 UInt32 IOSharedDataQueue::getQueueSize() 362 { 363 if (!_reserved) { 364 return 0; 365 } 366 return _reserved->queueSize; 367 } 368 369 Boolean IOSharedDataQueue::setQueueSize(UInt32 size) 370 { 371 if (!_reserved) { 372 return false; 373 } 374 _reserved->queueSize = size; 375 return true; 376 } 377 378 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 0); 379 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 1); 380 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 2); 381 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 3); 382 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 4); 383 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 5); 384 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 6); 385 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 7); 386