xref: /xnu-12377.81.4/iokit/IOKit/IOCircularDataQueueImplementation.h (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1*043036a2SApple OSS Distributions /*
2*043036a2SApple OSS Distributions  * Copyright (c) 2024 Apple Inc. All rights reserved.
3*043036a2SApple OSS Distributions  *
4*043036a2SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*043036a2SApple OSS Distributions  *
6*043036a2SApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*043036a2SApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*043036a2SApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*043036a2SApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*043036a2SApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*043036a2SApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*043036a2SApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*043036a2SApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*043036a2SApple OSS Distributions  *
15*043036a2SApple OSS Distributions  * Please obtain a copy of the License at
16*043036a2SApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*043036a2SApple OSS Distributions  *
18*043036a2SApple OSS Distributions  * The Original Code and all software distributed under the License are
19*043036a2SApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*043036a2SApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*043036a2SApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*043036a2SApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*043036a2SApple OSS Distributions  * Please see the License for the specific language governing rights and
24*043036a2SApple OSS Distributions  * limitations under the License.
25*043036a2SApple OSS Distributions  *
26*043036a2SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*043036a2SApple OSS Distributions  */
28*043036a2SApple OSS Distributions 
29*043036a2SApple OSS Distributions #include <IOKit/IOCircularDataQueue.h>
30*043036a2SApple OSS Distributions 
31*043036a2SApple OSS Distributions __BEGIN_DECLS
32*043036a2SApple OSS Distributions 
33*043036a2SApple OSS Distributions /*!
34*043036a2SApple OSS Distributions  *  @header IOCircularDataQueueMemory
35*043036a2SApple OSS Distributions  *
36*043036a2SApple OSS Distributions  *  This header contains the memory layout for a circular data queue.
37*043036a2SApple OSS Distributions  *
38*043036a2SApple OSS Distributions  *  A circular data queue supports a single producer and zero or more consumers.
39*043036a2SApple OSS Distributions  *
40*043036a2SApple OSS Distributions  *
41*043036a2SApple OSS Distributions  *  The producer does not wait for consumers to read the data.  If a
42*043036a2SApple OSS Distributions  *  consumer falls behind, it will miss data.
43*043036a2SApple OSS Distributions  *
44*043036a2SApple OSS Distributions  *  The queue can be configured to support either fixed or variable sized
45*043036a2SApple OSS Distributions  *  entries.
46*043036a2SApple OSS Distributions  *  Currently only fixed is supported.
47*043036a2SApple OSS Distributions  */
48*043036a2SApple OSS Distributions 
49*043036a2SApple OSS Distributions /*
50*043036a2SApple OSS Distributions  *  Fixed sized entry circular queue
51*043036a2SApple OSS Distributions  *
52*043036a2SApple OSS Distributions  +------------+
53*043036a2SApple OSS Distributions  |    Queue   |
54*043036a2SApple OSS Distributions  |   Header   |
55*043036a2SApple OSS Distributions  +------------+ <--- First Data Entry
56*043036a2SApple OSS Distributions  |Entry Header|
57*043036a2SApple OSS Distributions  +------------+
58*043036a2SApple OSS Distributions  |            |
59*043036a2SApple OSS Distributions  |    Entry   |
60*043036a2SApple OSS Distributions  |    Data    |
61*043036a2SApple OSS Distributions  |            |
62*043036a2SApple OSS Distributions  +------------+ <--- Second Data Entry
63*043036a2SApple OSS Distributions  |Entry Header|
64*043036a2SApple OSS Distributions  +------------+
65*043036a2SApple OSS Distributions  |            |
66*043036a2SApple OSS Distributions  |            |
67*043036a2SApple OSS Distributions  |     ...    |
68*043036a2SApple OSS Distributions  |     ...    |
69*043036a2SApple OSS Distributions  |            |
70*043036a2SApple OSS Distributions  |            |
71*043036a2SApple OSS Distributions  |            |
72*043036a2SApple OSS Distributions  +------------+ <--- Last Data Entry
73*043036a2SApple OSS Distributions  |Entry Header|
74*043036a2SApple OSS Distributions  +------------+
75*043036a2SApple OSS Distributions  |            |
76*043036a2SApple OSS Distributions  |    Entry   |
77*043036a2SApple OSS Distributions  |    Data    |
78*043036a2SApple OSS Distributions  |            |
79*043036a2SApple OSS Distributions  +------------+
80*043036a2SApple OSS Distributions  |
81*043036a2SApple OSS Distributions  */
82*043036a2SApple OSS Distributions 
83*043036a2SApple OSS Distributions #if defined(__STDC_VERSION__) && __STDC_VERSION__ < 201112L
84*043036a2SApple OSS Distributions 	#define _STATIC_ASSERT_OVERLOADED_MACRO(_1, _2, NAME, ...) NAME
85*043036a2SApple OSS Distributions 	#define static_assert(...) _STATIC_ASSERT_OVERLOADED_MACRO(__VA_ARGS__, _static_assert_2_args, _static_assert_1_arg)(__VA_ARGS__)
86*043036a2SApple OSS Distributions 
87*043036a2SApple OSS Distributions 	#define _static_assert_2_args(ex, str) _Static_assert((ex), str)
88*043036a2SApple OSS Distributions 	#define _static_assert_1_arg(ex) _Static_assert((ex), #ex)
89*043036a2SApple OSS Distributions #endif
90*043036a2SApple OSS Distributions 
91*043036a2SApple OSS Distributions #define HEADER_16BYTE_ALIGNED 1 // do the entry and entry headers need to be 16 byte aligned for perf/correctness ?
92*043036a2SApple OSS Distributions 
93*043036a2SApple OSS Distributions /*!
94*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueueEntryHeaderInfo
95*043036a2SApple OSS Distributions  * @abstract The state of an entry in the  circular data queue. The state is part of  each entry header in the queue.
96*043036a2SApple OSS Distributions  * @discussion The entry state has the sequence number, data size, generation and current state of the entry. The state
97*043036a2SApple OSS Distributions  * is read/updated atomically.
98*043036a2SApple OSS Distributions  * @field seqNum A unique sequence number for this entry. The sequence number is monotonically increased on each enqueue
99*043036a2SApple OSS Distributions  * to the queue. Each entry in the queue has a unique sequence number.
100*043036a2SApple OSS Distributions  * @field dataSize The size of the data at this entry.
101*043036a2SApple OSS Distributions  * @field generation The queue generation which is copied from the queue header memory into the entry state on an
102*043036a2SApple OSS Distributions  * enqueue.
103*043036a2SApple OSS Distributions  * @field `_reserved` Unused
104*043036a2SApple OSS Distributions  * @field wrStatus Represents if the queue entry is currently being written to or not.
105*043036a2SApple OSS Distributions  */
106*043036a2SApple OSS Distributions 
107*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_SIZE 1
108*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_GENERATION_SIZE 30
109*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_DATATSIZE_SIZE 32
110*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_SEQNUM_SIZE 64
111*043036a2SApple OSS Distributions // #define IOCIRCULARDATAQUEUE_ENTRY_STATE_RESERVED_SIZE   1
112*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_RESERVED_SIZE                                                                  \
113*043036a2SApple OSS Distributions     ((8 * sizeof(__uint128_t)) - IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_SIZE                                            \
114*043036a2SApple OSS Distributions      - IOCIRCULARDATAQUEUE_ENTRY_STATE_GENERATION_SIZE - IOCIRCULARDATAQUEUE_ENTRY_STATE_DATATSIZE_SIZE                \
115*043036a2SApple OSS Distributions      - IOCIRCULARDATAQUEUE_ENTRY_STATE_SEQNUM_SIZE)
116*043036a2SApple OSS Distributions 
117*043036a2SApple OSS Distributions typedef union {
118*043036a2SApple OSS Distributions 	__uint128_t val;
119*043036a2SApple OSS Distributions 	struct {
120*043036a2SApple OSS Distributions 		__uint128_t seqNum : IOCIRCULARDATAQUEUE_ENTRY_STATE_SEQNUM_SIZE; // Sequence Number
121*043036a2SApple OSS Distributions 		__uint128_t dataSize : IOCIRCULARDATAQUEUE_ENTRY_STATE_DATATSIZE_SIZE; // datasize
122*043036a2SApple OSS Distributions 		__uint128_t generation : IOCIRCULARDATAQUEUE_ENTRY_STATE_GENERATION_SIZE; // generation
123*043036a2SApple OSS Distributions 		__uint128_t _reserved : IOCIRCULARDATAQUEUE_ENTRY_STATE_RESERVED_SIZE; // reserved, currently not used
124*043036a2SApple OSS Distributions 		__uint128_t wrStatus : IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_SIZE; // queue writing status
125*043036a2SApple OSS Distributions 	} fields;
126*043036a2SApple OSS Distributions } IOCircularDataQueueEntryHeaderInfo;
127*043036a2SApple OSS Distributions 
128*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS (1)
129*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_COMPLETE (0)
130*043036a2SApple OSS Distributions 
131*043036a2SApple OSS Distributions static_assert(IOCIRCULARDATAQUEUE_ENTRY_STATE_RESERVED_SIZE > 0, "unexpected reserved field size");
132*043036a2SApple OSS Distributions 
133*043036a2SApple OSS Distributions /*!
134*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueueEntryHeader
135*043036a2SApple OSS Distributions  * @abstract An entry in the  circular data queue. The entry header is written at the beginning of each entry in the
136*043036a2SApple OSS Distributions  * queue.
137*043036a2SApple OSS Distributions  * @discussion The entry has the current state, sentinel, followed by the data at the enty.
138*043036a2SApple OSS Distributions  * @field info The info of the queue entry. This  includes the size, sequence number, generation and write status of the
139*043036a2SApple OSS Distributions  * data at this entry.
140*043036a2SApple OSS Distributions  * @field sentinel unique value written to the queue entry. This is copied from the sentinel in the queue header memory
141*043036a2SApple OSS Distributions  * when an entry is written.
142*043036a2SApple OSS Distributions  * @field data Represents the beginning of the data region.  The address of the data field is a pointer to the start of
143*043036a2SApple OSS Distributions  * the data region.
144*043036a2SApple OSS Distributions  */
145*043036a2SApple OSS Distributions typedef struct {
146*043036a2SApple OSS Distributions 	union {
147*043036a2SApple OSS Distributions 		volatile _Atomic __uint128_t headerInfoVal;
148*043036a2SApple OSS Distributions 		IOCircularDataQueueEntryHeaderInfo __headerInfo;         // for clarity, unused
149*043036a2SApple OSS Distributions 	};
150*043036a2SApple OSS Distributions 	volatile uint64_t sentinel;
151*043036a2SApple OSS Distributions 	uint64_t _pad; // pad for 16 byte aligment of data that follows
152*043036a2SApple OSS Distributions #if HEADER_16BYTE_ALIGNED
153*043036a2SApple OSS Distributions 	uint8_t data[16]; // Entry data begins.  Aligned to 16 bytes.
154*043036a2SApple OSS Distributions #else
155*043036a2SApple OSS Distributions 	uint8_t data[8]; // Entry data begins.  Aligned to 8 bytes.
156*043036a2SApple OSS Distributions #endif
157*043036a2SApple OSS Distributions } IOCircularDataQueueEntryHeader;
158*043036a2SApple OSS Distributions 
159*043036a2SApple OSS Distributions #if HEADER_16BYTE_ALIGNED
160*043036a2SApple OSS Distributions #define CIRCULAR_DATA_QUEUE_ENTRY_HEADER_SIZE (sizeof(IOCircularDataQueueEntryHeader) - 16)
161*043036a2SApple OSS Distributions #else
162*043036a2SApple OSS Distributions #define CIRCULAR_DATA_QUEUE_ENTRY_HEADER_SIZE (sizeof(IOCircularDataQueueEntryHeader) - 8)
163*043036a2SApple OSS Distributions #endif
164*043036a2SApple OSS Distributions 
165*043036a2SApple OSS Distributions /*!
166*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueueState
167*043036a2SApple OSS Distributions  * @abstract The current state of the circular data queue.
168*043036a2SApple OSS Distributions  * @discussion The queue state is part of the queue memory header. It has the current sequence number, next writing
169*043036a2SApple OSS Distributions  * index, generation and current reset and writing state off the queue. The queue state is read/updated atomically.
170*043036a2SApple OSS Distributions  * @field seqNum A monotonically increasing sequence number which is incremented for each enqueue.
171*043036a2SApple OSS Distributions  * @field wrIndex The next write position into the queue.
172*043036a2SApple OSS Distributions  * @field generation The generation of the queue. It is a monotonically increasing number, which is incremented on each
173*043036a2SApple OSS Distributions  * queue reset.
174*043036a2SApple OSS Distributions  * @field rstStatus The queue reset state. The bit is set if the queue is currently being reset.
175*043036a2SApple OSS Distributions  * @field wrStatus The queue writing state. The bit is set if an enqueue is in progress.
176*043036a2SApple OSS Distributions  */
177*043036a2SApple OSS Distributions // Fahad : I dont think we need a reset bit, since we are doing everything in one atomic op.
178*043036a2SApple OSS Distributions 
179*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_WRITE_SIZE 1
180*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_RESET_SIZE 1
181*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_GENERATION_SIZE 30
182*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_WRITEINDEX_SIZE 32
183*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_SEQNUM_SIZE 64
184*043036a2SApple OSS Distributions //#define IOCIRCULARDATAQUEUE_STATE_RESERVED_SIZE                                                                  \
185*043036a2SApple OSS Distributions //    ((8 * sizeof(__uint128_t)) - IOCIRCULARDATAQUEUE_STATE_WRITE_SIZE                                            \
186*043036a2SApple OSS Distributions //     - IOCIRCULARDATAQUEUE_STATE_GENERATION_SIZE - IOCIRCULARDATAQUEUE_STATE_WRITEINDEX_SIZE                \
187*043036a2SApple OSS Distributions //     - IOCIRCULARDATAQUEUE_STATE_SEQNUM_SIZE)
188*043036a2SApple OSS Distributions 
189*043036a2SApple OSS Distributions typedef union {
190*043036a2SApple OSS Distributions 	__uint128_t val;
191*043036a2SApple OSS Distributions 	struct {
192*043036a2SApple OSS Distributions 		__uint128_t seqNum : IOCIRCULARDATAQUEUE_STATE_SEQNUM_SIZE; // Sequence Number
193*043036a2SApple OSS Distributions 		__uint128_t wrIndex : IOCIRCULARDATAQUEUE_STATE_WRITEINDEX_SIZE; // write index
194*043036a2SApple OSS Distributions 		__uint128_t generation : IOCIRCULARDATAQUEUE_STATE_GENERATION_SIZE; // generation
195*043036a2SApple OSS Distributions 		// Fahad: We may not need reset.
196*043036a2SApple OSS Distributions 		__uint128_t rstStatus : IOCIRCULARDATAQUEUE_STATE_RESET_SIZE; // queue reset status
197*043036a2SApple OSS Distributions 		//        __uint128_t _rsvd : IOCIRCULARDATAQUEUE_STATE_RESERVED_SIZE; // reserved
198*043036a2SApple OSS Distributions 		__uint128_t wrStatus : IOCIRCULARDATAQUEUE_STATE_WRITE_SIZE; // queue writing status
199*043036a2SApple OSS Distributions 	} fields;
200*043036a2SApple OSS Distributions } IOCircularDataQueueState;
201*043036a2SApple OSS Distributions 
202*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_WRITE_INPROGRESS (1)
203*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_WRITE_COMPLETE (0)
204*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS (1)
205*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_RESET_COMPLETE (0)
206*043036a2SApple OSS Distributions 
207*043036a2SApple OSS Distributions // #define IOCircularDataQueueStateGeneration              (((uint32_t)1 << 30) - 1)
208*043036a2SApple OSS Distributions #define IOCIRCULARDATAQUEUE_STATE_GENERATION_MAX (((uint32_t)1 << 30))
209*043036a2SApple OSS Distributions 
210*043036a2SApple OSS Distributions // static_assert(IOCIRCULARDATAQUEUE_STATE_RESERVED_SIZE > 0,
211*043036a2SApple OSS Distributions //               "unexpected reserved field size");
212*043036a2SApple OSS Distributions 
213*043036a2SApple OSS Distributions static_assert(IOCIRCULARDATAQUEUE_STATE_GENERATION_SIZE == IOCIRCULARDATAQUEUE_ENTRY_STATE_GENERATION_SIZE,
214*043036a2SApple OSS Distributions     "mismatched generation sizes");
215*043036a2SApple OSS Distributions static_assert(IOCIRCULARDATAQUEUE_STATE_SEQNUM_SIZE == IOCIRCULARDATAQUEUE_ENTRY_STATE_SEQNUM_SIZE,
216*043036a2SApple OSS Distributions     "mismatched sequenece number sizes");
217*043036a2SApple OSS Distributions 
218*043036a2SApple OSS Distributions /*!
219*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueueMemory
220*043036a2SApple OSS Distributions  * @abstract The queue memory header present at the start of  queue shared memory region.
221*043036a2SApple OSS Distributions  * @discussion The queue memory header contains the queue info and state and is followed by the data region of the
222*043036a2SApple OSS Distributions  * queue.
223*043036a2SApple OSS Distributions  * @field sentinel unique value when the queue was created.
224*043036a2SApple OSS Distributions  * @field allocMemSize the allocated memory size of the queue including the queue header and the entries
225*043036a2SApple OSS Distributions  * @field memorySize  the memory size of the queue excluding the queue header
226*043036a2SApple OSS Distributions  * @field entryDataSize size of each entry in the queue including the entry header. The size is a multiple of 8 bytes
227*043036a2SApple OSS Distributions  * @field dataSize size of each entry in the queue excluding the entry header.
228*043036a2SApple OSS Distributions  * @field numEntries the number of fixed entries in the queue
229*043036a2SApple OSS Distributions  * @field `_padding` memory padding for alingment.
230*043036a2SApple OSS Distributions  * @field state the current state of the queue.
231*043036a2SApple OSS Distributions  * @field entries Represents the beginning of the data region.  The address of the data field is a pointer to the start
232*043036a2SApple OSS Distributions  * of the queue data region.
233*043036a2SApple OSS Distributions  */
234*043036a2SApple OSS Distributions 
235*043036a2SApple OSS Distributions typedef struct IOCircularDataQueueMemory {
236*043036a2SApple OSS Distributions 	uint64_t sentinel;
237*043036a2SApple OSS Distributions 	uint64_t _padding; // since we want it to be 16 bytes aligned below this
238*043036a2SApple OSS Distributions 	union {
239*043036a2SApple OSS Distributions 		volatile _Atomic __uint128_t queueStateVal;           // needs to be 16 bytes aligned.
240*043036a2SApple OSS Distributions 		IOCircularDataQueueState  __queueState;               // for clarity, unused
241*043036a2SApple OSS Distributions 	};
242*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeader entries[1]; // Entries begin.  Aligned to 16 bytes.
243*043036a2SApple OSS Distributions } IOCircularDataQueueMemory;
244*043036a2SApple OSS Distributions 
245*043036a2SApple OSS Distributions #define CIRCULAR_DATA_QUEUE_MEMORY_HEADER_SIZE                                                                         \
246*043036a2SApple OSS Distributions     (sizeof(IOCircularDataQueueMemory) - sizeof(IOCircularDataQueueEntryHeader))
247*043036a2SApple OSS Distributions 
248*043036a2SApple OSS Distributions /*!
249*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueueMemoryCursor
250*043036a2SApple OSS Distributions  * @abstract The circular data queue cursor struct.
251*043036a2SApple OSS Distributions  * @discussion This struct represents a readers reference to a position in the queue. Each client holds an instance of
252*043036a2SApple OSS Distributions  * this in its process indicating its current reading position in the queue. The cursor holds uniqely identifying
253*043036a2SApple OSS Distributions  * information for the queue entry.
254*043036a2SApple OSS Distributions  * @field generation  the generation for the entry data at the position in the queue. This generation is only changed
255*043036a2SApple OSS Distributions  * when the queue is reset.
256*043036a2SApple OSS Distributions  * @field position the position in the queue  the cursor is at
257*043036a2SApple OSS Distributions  * @field sequenceNum  The unique number for the data at the  cursor position. The sequence number is unique for each
258*043036a2SApple OSS Distributions  * entry in the queue.
259*043036a2SApple OSS Distributions  *
260*043036a2SApple OSS Distributions  */
261*043036a2SApple OSS Distributions typedef struct IOCircularDataQueueMemoryCursor {
262*043036a2SApple OSS Distributions 	uint32_t generation; // uint32_t seems a little excessive right now, since we dont expect these many resets. but
263*043036a2SApple OSS Distributions 	                     // lets leave it for now.
264*043036a2SApple OSS Distributions 	uint32_t position;
265*043036a2SApple OSS Distributions 	uint64_t sequenceNum;
266*043036a2SApple OSS Distributions } IOCircularDataQueueMemoryCursor;
267*043036a2SApple OSS Distributions 
268*043036a2SApple OSS Distributions 
269*043036a2SApple OSS Distributions /*!
270*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueueDescription
271*043036a2SApple OSS Distributions  * @abstract The circular data queue header shadow struct.
272*043036a2SApple OSS Distributions  * @discussion This struct represents the queue header shadow. Each client has a copy of this struct in its process .
273*043036a2SApple OSS Distributions  * This is used to detect any memory corruption of the shared memory queue header. This struct needs to be shared from
274*043036a2SApple OSS Distributions  * the creator of the queue to the clients via an out of band mechanism.
275*043036a2SApple OSS Distributions  * @field sentinel unique value written to the queue header memory and each queue entry.
276*043036a2SApple OSS Distributions  * @field allocMemSize the allocated memory size of the queue including the queue header
277*043036a2SApple OSS Distributions  * @field entryDataSize size of each entry in the queue including the entry header. The size is a multiple of 8 bytes
278*043036a2SApple OSS Distributions  * @field memorySize  the memory size of the queue excluding the queue header
279*043036a2SApple OSS Distributions  * @field numEntries the number of fixed entries in the queue
280*043036a2SApple OSS Distributions  * IOCircularDataQueueDescription
281*043036a2SApple OSS Distributions  */
282*043036a2SApple OSS Distributions typedef struct IOCircularDataQueueDescription {
283*043036a2SApple OSS Distributions 	uint64_t sentinel;
284*043036a2SApple OSS Distributions 	uint32_t allocMemSize; // total allocated size of the queue including the queue header.
285*043036a2SApple OSS Distributions 	uint32_t entryDataSize; // size of each queue entry including the per entry header.
286*043036a2SApple OSS Distributions 	uint32_t memorySize; // memory size of the queue (excluding the queue header)
287*043036a2SApple OSS Distributions 	uint32_t numEntries;
288*043036a2SApple OSS Distributions 	uint32_t dataSize; // the client provided data size excluding the per entry header.
289*043036a2SApple OSS Distributions 	uint32_t padding;
290*043036a2SApple OSS Distributions } IOCircularDataQueueDescription;
291*043036a2SApple OSS Distributions 
292*043036a2SApple OSS Distributions #define kIOCircularQueueDescriptionKey  "IOCircularQueueDescription"
293*043036a2SApple OSS Distributions 
294*043036a2SApple OSS Distributions 
295*043036a2SApple OSS Distributions #if !KERNEL
296*043036a2SApple OSS Distributions /*
297*043036a2SApple OSS Distributions  * IORound and IOTrunc convenience functions, in the spirit
298*043036a2SApple OSS Distributions  * of vm's round_page() and trunc_page().
299*043036a2SApple OSS Distributions  */
300*043036a2SApple OSS Distributions #define IORound(value, multiple) ((((value) + (multiple)-1) / (multiple)) * (multiple))
301*043036a2SApple OSS Distributions 
302*043036a2SApple OSS Distributions #define IONew(type, count) (type *)calloc(count, sizeof(type))
303*043036a2SApple OSS Distributions #define IODelete(p, type, count) free(p)
304*043036a2SApple OSS Distributions 
305*043036a2SApple OSS Distributions // libkern/os/base.h
306*043036a2SApple OSS Distributions #if __has_feature(ptrauth_calls)
307*043036a2SApple OSS Distributions #include <ptrauth.h>
308*043036a2SApple OSS Distributions #define OS_PTRAUTH_SIGNED_PTR(type) __ptrauth(ptrauth_key_process_independent_data, 1, ptrauth_string_discriminator(type))
309*043036a2SApple OSS Distributions #define OS_PTRAUTH_SIGNED_PTR_AUTH_NULL(type) __ptrauth(ptrauth_key_process_independent_data, 1, ptrauth_string_discriminator(type), "authenticates-null-values")
310*043036a2SApple OSS Distributions #define OS_PTRAUTH_DISCRIMINATOR(str) ptrauth_string_discriminator(str)
311*043036a2SApple OSS Distributions #define __ptrauth_only
312*043036a2SApple OSS Distributions #else //  __has_feature(ptrauth_calls)
313*043036a2SApple OSS Distributions #define OS_PTRAUTH_SIGNED_PTR(type)
314*043036a2SApple OSS Distributions #define OS_PTRAUTH_SIGNED_PTR_AUTH_NULL(type)
315*043036a2SApple OSS Distributions #define OS_PTRAUTH_DISCRIMINATOR(str) 0
316*043036a2SApple OSS Distributions #define __ptrauth_only __unused
317*043036a2SApple OSS Distributions #endif // __has_feature(ptrauth_calls)
318*043036a2SApple OSS Distributions #endif /* !KERNEL */
319*043036a2SApple OSS Distributions 
320*043036a2SApple OSS Distributions #pragma mark - Debugging
321*043036a2SApple OSS Distributions 
322*043036a2SApple OSS Distributions #define QUEUE_FORMAT "Queue(%" PRIu64 " gen:%" PRIu64 " pos:%" PRIu64 " next:%" PRIu64 ")"
323*043036a2SApple OSS Distributions #define QUEUE_ARGS(q) q->guard, q->generation, q->fixed.latestIndex, q->fixed.writingIndex
324*043036a2SApple OSS Distributions 
325*043036a2SApple OSS Distributions #define CURSOR_FORMAT "Cursor(%p gen:%" PRIu64 " pos:%" PRIu64 ")"
326*043036a2SApple OSS Distributions #define CURSOR_ARGS(c) c, c->generation, c->position
327*043036a2SApple OSS Distributions 
328*043036a2SApple OSS Distributions #define ENTRY_FORMAT "Entry(%" PRIu64 " gen:%" PRIu64 " pos:%" PRIu64 ")"
329*043036a2SApple OSS Distributions #define ENTRY_ARGS(e) e->guard, e->generation, e->position
330*043036a2SApple OSS Distributions 
331*043036a2SApple OSS Distributions #if 1
332*043036a2SApple OSS Distributions #define queue_debug_error(fmt, ...)
333*043036a2SApple OSS Distributions #define queue_debug_note(fmt, ...)
334*043036a2SApple OSS Distributions #define queue_debug_trace(fmt, ...)
335*043036a2SApple OSS Distributions #else
336*043036a2SApple OSS Distributions #define queue_debug_error(fmt, ...)                                                                                    \
337*043036a2SApple OSS Distributions     {                                                                                                                  \
338*043036a2SApple OSS Distributions 	os_log_debug(LOG_QUEUE, "#ERROR %s:%d %s " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__);                  \
339*043036a2SApple OSS Distributions     }
340*043036a2SApple OSS Distributions #define queue_debug_note(fmt, ...)                                                                                     \
341*043036a2SApple OSS Distributions     {                                                                                                                  \
342*043036a2SApple OSS Distributions 	os_log_debug(LOG_QUEUE, "#NOTE %s:%d %s " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__);                   \
343*043036a2SApple OSS Distributions     }
344*043036a2SApple OSS Distributions #define queue_debug_trace(fmt, ...)                                                                                    \
345*043036a2SApple OSS Distributions     {                                                                                                                  \
346*043036a2SApple OSS Distributions 	os_log_debug(LOG_QUEUE, "#TRACE %s:%d %s " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__);                  \
347*043036a2SApple OSS Distributions     }
348*043036a2SApple OSS Distributions #endif
349*043036a2SApple OSS Distributions 
350*043036a2SApple OSS Distributions #if HEADER_16BYTE_ALIGNED
351*043036a2SApple OSS Distributions static_assert(offsetof(IOCircularDataQueueEntryHeader, data) % sizeof(__uint128_t) == 0,
352*043036a2SApple OSS Distributions     "IOCircularDataQueueEntryHeader.data is not 16-byte aligned!");
353*043036a2SApple OSS Distributions #else
354*043036a2SApple OSS Distributions static_assert(offsetof(IOCircularDataQueueEntryHeader, data) % sizeof(uint64_t) == 0,
355*043036a2SApple OSS Distributions     "IOCircularDataQueueEntryHeader.data is not 8-byte aligned!");
356*043036a2SApple OSS Distributions #endif
357*043036a2SApple OSS Distributions 
358*043036a2SApple OSS Distributions static_assert(sizeof(IOCircularDataQueueState) == sizeof(__uint128_t), "Unexpected padding");
359*043036a2SApple OSS Distributions static_assert(offsetof(IOCircularDataQueueMemory, queueStateVal) % sizeof(__uint128_t) == 0,
360*043036a2SApple OSS Distributions     "IOCircularDataQueueMemory.entries is not 16-byte aligned!");
361*043036a2SApple OSS Distributions 
362*043036a2SApple OSS Distributions #if HEADER_16BYTE_ALIGNED
363*043036a2SApple OSS Distributions static_assert(offsetof(IOCircularDataQueueMemory, entries) % sizeof(__uint128_t) == 0,
364*043036a2SApple OSS Distributions     "IOCircularDataQueueMemory.entries is not 16-byte aligned!");
365*043036a2SApple OSS Distributions #else
366*043036a2SApple OSS Distributions static_assert(offsetof(IOCircularDataQueueMemory, entries) % sizeof(uint64_t) == 0,
367*043036a2SApple OSS Distributions     "IOCircularDataQueueMemory.entries is not 8-byte aligned!");
368*043036a2SApple OSS Distributions #endif
369*043036a2SApple OSS Distributions 
370*043036a2SApple OSS Distributions /*!
371*043036a2SApple OSS Distributions  * @typedef IOCircularDataQueue
372*043036a2SApple OSS Distributions  * @abstract A fixed entry size circular queue that supports multiple concurrent readers and a single writer.
373*043036a2SApple OSS Distributions  * @discussion The queue currently supports fixed size entries. The queue memory size is configured at init when the
374*043036a2SApple OSS Distributions  * number of entries and size of each entry is specifiied and cannot be resized later. Since the queue is a circular
375*043036a2SApple OSS Distributions  * buffer, the writer can potentially overwrite an entry while a reader is still reading it. The queue provides facility
376*043036a2SApple OSS Distributions  * to check for data integrity after reading the entry is complete. There is no support for sending notifications to
377*043036a2SApple OSS Distributions  * readers when data is enqueued into an empty queue by the writer. The queue supports a "pull model" for reading data
378*043036a2SApple OSS Distributions  * from the queue. The queue can be used for passing data from user space to kernel and vice-versa.
379*043036a2SApple OSS Distributions  * @field queueHeaderShadow    The queue header shadow
380*043036a2SApple OSS Distributions  * @field queueCursor    The queue cursor
381*043036a2SApple OSS Distributions  * @field isQueueMemoryAllocated    Represents if the queue memory is allocated or if the queue uses a previously
382*043036a2SApple OSS Distributions  * created queue memory region.
383*043036a2SApple OSS Distributions  * @field queueMemory    Pointer to the queue shared memory region
384*043036a2SApple OSS Distributions  */
385*043036a2SApple OSS Distributions typedef struct IOCircularDataQueue {
386*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor queueCursor;
387*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory * OS_PTRAUTH_SIGNED_PTR("IOCircularDataQueue.queueMemory") queueMemory;
388*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription queueHeaderShadow;
389*043036a2SApple OSS Distributions #if KERNEL
390*043036a2SApple OSS Distributions 	IOBufferMemoryDescriptor * OS_PTRAUTH_SIGNED_PTR("IOCircularDataQueue.iomd") iomd;
391*043036a2SApple OSS Distributions #else /* KERNEL */
392*043036a2SApple OSS Distributions 	io_connect_t connect;
393*043036a2SApple OSS Distributions 	uint32_t memoryType;
394*043036a2SApple OSS Distributions #endif /* !KERNEL */
395*043036a2SApple OSS Distributions } IOCircularDataQueue;
396*043036a2SApple OSS Distributions 
397*043036a2SApple OSS Distributions 
398*043036a2SApple OSS Distributions #if defined(__arm64__) && !KERNEL
399*043036a2SApple OSS Distributions #define ATTR_LSE2 __attribute__((target("lse2")))
400*043036a2SApple OSS Distributions #else
401*043036a2SApple OSS Distributions #define ATTR_LSE2
402*043036a2SApple OSS Distributions #endif /* defined(__arm64__) && !KERNEL */
403*043036a2SApple OSS Distributions 
404*043036a2SApple OSS Distributions #pragma mark - Queue
405*043036a2SApple OSS Distributions 
406*043036a2SApple OSS Distributions static bool ATTR_LSE2
_isQueueMemoryCorrupted(IOCircularDataQueue * queue)407*043036a2SApple OSS Distributions _isQueueMemoryCorrupted(IOCircularDataQueue *queue)
408*043036a2SApple OSS Distributions {
409*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
410*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
411*043036a2SApple OSS Distributions 
412*043036a2SApple OSS Distributions 	const size_t queueSentinel = queueMemory->sentinel;
413*043036a2SApple OSS Distributions 	if (os_unlikely(queueSentinel != queueHeaderShadow->sentinel)) {
414*043036a2SApple OSS Distributions 		return true;
415*043036a2SApple OSS Distributions 	}
416*043036a2SApple OSS Distributions 	return false;
417*043036a2SApple OSS Distributions }
418*043036a2SApple OSS Distributions 
419*043036a2SApple OSS Distributions inline static bool ATTR_LSE2
_isCursorPositionInvalid(IOCircularDataQueue * queue)420*043036a2SApple OSS Distributions _isCursorPositionInvalid(IOCircularDataQueue *queue)
421*043036a2SApple OSS Distributions {
422*043036a2SApple OSS Distributions //	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
423*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
424*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor const *cursor = &queue->queueCursor;
425*043036a2SApple OSS Distributions 
426*043036a2SApple OSS Distributions 	if (os_unlikely(cursor->position >= queueHeaderShadow->numEntries)) {
427*043036a2SApple OSS Distributions 		return true;
428*043036a2SApple OSS Distributions 	}
429*043036a2SApple OSS Distributions 
430*043036a2SApple OSS Distributions 	return false;
431*043036a2SApple OSS Distributions }
432*043036a2SApple OSS Distributions 
433*043036a2SApple OSS Distributions inline __unused static bool ATTR_LSE2
_isEntryOutOfBounds(IOCircularDataQueue * queue,IOCircularDataQueueEntryHeader * entry)434*043036a2SApple OSS Distributions _isEntryOutOfBounds(IOCircularDataQueue *queue, IOCircularDataQueueEntryHeader *entry)
435*043036a2SApple OSS Distributions {
436*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
437*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
438*043036a2SApple OSS Distributions //	IOCircularDataQueueMemoryCursor const *cursor = &queue->queueCursor;
439*043036a2SApple OSS Distributions 
440*043036a2SApple OSS Distributions 	bool ret = false;
441*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeader *firstEntry = (IOCircularDataQueueEntryHeader *)(&queueMemory->entries[0]);
442*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeader *lastEntry
443*043036a2SApple OSS Distributions 	        = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0]
444*043036a2SApple OSS Distributions 	    + ((queueHeaderShadow->numEntries - 1) * queueHeaderShadow->entryDataSize));
445*043036a2SApple OSS Distributions 
446*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
447*043036a2SApple OSS Distributions 	// within the queueMemory allocation before we begin writing.
448*043036a2SApple OSS Distributions 	if (os_unlikely(entry < firstEntry || entry > lastEntry)) {
449*043036a2SApple OSS Distributions 		ret = true;
450*043036a2SApple OSS Distributions 	}
451*043036a2SApple OSS Distributions 
452*043036a2SApple OSS Distributions 	return ret;
453*043036a2SApple OSS Distributions }
454*043036a2SApple OSS Distributions 
455*043036a2SApple OSS Distributions 
456*043036a2SApple OSS Distributions #if !KERNEL
457*043036a2SApple OSS Distributions /*!
458*043036a2SApple OSS Distributions  * @function isQueueMemoryValid
459*043036a2SApple OSS Distributions  * Verify if the queue header shadow matches the queue header in shared memory.
460*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
461*043036a2SApple OSS Distributions  * @return `true` if the queue header shadow matches the queue header in shared memory, else `false`.
462*043036a2SApple OSS Distributions  *
463*043036a2SApple OSS Distributions  */
464*043036a2SApple OSS Distributions 
465*043036a2SApple OSS Distributions static bool ATTR_LSE2
isQueueMemoryValid(IOCircularDataQueue * queue)466*043036a2SApple OSS Distributions isQueueMemoryValid(IOCircularDataQueue *queue)
467*043036a2SApple OSS Distributions {
468*043036a2SApple OSS Distributions 	return _isQueueMemoryCorrupted(queue) == false;
469*043036a2SApple OSS Distributions }
470*043036a2SApple OSS Distributions #endif /* KERNEL */
471*043036a2SApple OSS Distributions 
472*043036a2SApple OSS Distributions /*!
473*043036a2SApple OSS Distributions  * @function destroyQueueMem
474*043036a2SApple OSS Distributions  * @abstract Function that destroys a previously created IOCircularDataQueueMemory instance.
475*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
476*043036a2SApple OSS Distributions  *  @return
477*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the queue was succesfully destroyed.
478*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid queue was provided.
479*043036a2SApple OSS Distributions  */
480*043036a2SApple OSS Distributions 
481*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
destroyQueueMem(IOCircularDataQueue * queue)482*043036a2SApple OSS Distributions destroyQueueMem(IOCircularDataQueue *queue)
483*043036a2SApple OSS Distributions {
484*043036a2SApple OSS Distributions 	IOReturn ret = kIOReturnBadArgument;
485*043036a2SApple OSS Distributions 	if (queue != NULL) {
486*043036a2SApple OSS Distributions #if KERNEL
487*043036a2SApple OSS Distributions 		OSSafeReleaseNULL(queue->iomd);
488*043036a2SApple OSS Distributions #else /* !KERNEL */
489*043036a2SApple OSS Distributions 		IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
490*043036a2SApple OSS Distributions 		IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
491*043036a2SApple OSS Distributions 		if (queueMemory) {
492*043036a2SApple OSS Distributions 			ret = IOConnectUnmapMemory(queue->connect, queue->memoryType,
493*043036a2SApple OSS Distributions 			    mach_task_self(), (mach_vm_address_t) queueMemory);
494*043036a2SApple OSS Distributions //			assert(KERN_SUCCESS == ret);
495*043036a2SApple OSS Distributions 			queue->queueMemory = NULL;
496*043036a2SApple OSS Distributions 		}
497*043036a2SApple OSS Distributions #endif
498*043036a2SApple OSS Distributions 		ret = kIOReturnSuccess;
499*043036a2SApple OSS Distributions 	}
500*043036a2SApple OSS Distributions 
501*043036a2SApple OSS Distributions 	return ret;
502*043036a2SApple OSS Distributions }
503*043036a2SApple OSS Distributions 
504*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
_reset(IOCircularDataQueue * queue)505*043036a2SApple OSS Distributions _reset(IOCircularDataQueue *queue)
506*043036a2SApple OSS Distributions {
507*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
508*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
509*043036a2SApple OSS Distributions 
510*043036a2SApple OSS Distributions 	if (queueMemory == NULL || queueHeaderShadow == NULL) {
511*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
512*043036a2SApple OSS Distributions 	}
513*043036a2SApple OSS Distributions 
514*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
515*043036a2SApple OSS Distributions 	if (!queueEntryDataSize) {
516*043036a2SApple OSS Distributions 		return kIOReturnUnsupported;
517*043036a2SApple OSS Distributions 	}
518*043036a2SApple OSS Distributions 
519*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
520*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
521*043036a2SApple OSS Distributions 
522*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.wrStatus & IOCIRCULARDATAQUEUE_STATE_WRITE_INPROGRESS)) {
523*043036a2SApple OSS Distributions 		// Another thread is modifying the queue
524*043036a2SApple OSS Distributions 		return kIOReturnBusy;
525*043036a2SApple OSS Distributions 	}
526*043036a2SApple OSS Distributions 
527*043036a2SApple OSS Distributions 	uint32_t currGeneration = currState.fields.generation;
528*043036a2SApple OSS Distributions 	uint32_t newGen = (currGeneration + 1) % IOCIRCULARDATAQUEUE_STATE_GENERATION_MAX;
529*043036a2SApple OSS Distributions 
530*043036a2SApple OSS Distributions 	IOCircularDataQueueState newState;
531*043036a2SApple OSS Distributions 	newState.fields.generation = newGen;
532*043036a2SApple OSS Distributions 	newState.fields.wrIndex = 0;
533*043036a2SApple OSS Distributions 	newState.fields.seqNum = UINT64_MAX; // since we first increment the seq num on an enqueue.
534*043036a2SApple OSS Distributions 
535*043036a2SApple OSS Distributions 	if (!atomic_compare_exchange_strong(&queueMemory->queueStateVal, &currState.val, newState.val)) {
536*043036a2SApple OSS Distributions 		return kIOReturnBusy;
537*043036a2SApple OSS Distributions 	}
538*043036a2SApple OSS Distributions 
539*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
540*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
541*043036a2SApple OSS Distributions 	}
542*043036a2SApple OSS Distributions 
543*043036a2SApple OSS Distributions 	queue_debug_trace("Reset " QUEUE_FORMAT, QUEUE_ARGS(queueMemory));
544*043036a2SApple OSS Distributions 	return kIOReturnSuccess;
545*043036a2SApple OSS Distributions }
546*043036a2SApple OSS Distributions 
547*043036a2SApple OSS Distributions /*!
548*043036a2SApple OSS Distributions  * @function _enqueueInternal
549*043036a2SApple OSS Distributions  * @abstract Internal function for enqueuing a new entry on the queue.
550*043036a2SApple OSS Distributions  * @discussion This method adds a new data entry of dataSize to the queue.  It sets the size parameter of the entry
551*043036a2SApple OSS Distributions  * pointed to by the tail value and copies the memory pointed to by the data parameter in place in the queue.  Once that
552*043036a2SApple OSS Distributions  * is done, it moves the tail to the next available location.  When attempting to add a new entry towards the end of the
553*043036a2SApple OSS Distributions  * queue and there isn't enough space at the end, it wraps back to the beginning.<br>
554*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
555*043036a2SApple OSS Distributions  * @param data Pointer to the data to be added to the queue.
556*043036a2SApple OSS Distributions  * @param dataSize Size of the data pointed to by data.
557*043036a2SApple OSS Distributions  * @param earlyExitForTesting ealy exit flag used for testing only.
558*043036a2SApple OSS Distributions  *  @return
559*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` on success.
560*043036a2SApple OSS Distributions  *  - Other values indicate an error.
561*043036a2SApple OSS Distributions  */
562*043036a2SApple OSS Distributions 
563*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
_enqueueInternal(IOCircularDataQueue * queue,const void * data,size_t dataSize,int earlyExitForTesting)564*043036a2SApple OSS Distributions _enqueueInternal(IOCircularDataQueue *queue,
565*043036a2SApple OSS Distributions     const void *data,
566*043036a2SApple OSS Distributions     size_t dataSize,
567*043036a2SApple OSS Distributions     int earlyExitForTesting)
568*043036a2SApple OSS Distributions {
569*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
570*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
571*043036a2SApple OSS Distributions //	IOCircularDataQueueMemoryCursor const *cursor = &queue->queueCursor;
572*043036a2SApple OSS Distributions 
573*043036a2SApple OSS Distributions 	if (queueMemory == NULL || data == NULL || dataSize == 0 || queueHeaderShadow == NULL) {
574*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
575*043036a2SApple OSS Distributions 	}
576*043036a2SApple OSS Distributions 
577*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
578*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
579*043036a2SApple OSS Distributions 	}
580*043036a2SApple OSS Distributions 
581*043036a2SApple OSS Distributions 	if (os_unlikely(dataSize > queueHeaderShadow->dataSize)) {
582*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
583*043036a2SApple OSS Distributions 	}
584*043036a2SApple OSS Distributions 
585*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
586*043036a2SApple OSS Distributions 
587*043036a2SApple OSS Distributions 	if (!queueEntryDataSize) {
588*043036a2SApple OSS Distributions 		return kIOReturnUnsupported;
589*043036a2SApple OSS Distributions 	}
590*043036a2SApple OSS Distributions 
591*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
592*043036a2SApple OSS Distributions 	const uint32_t queueNumEntries = queueHeaderShadow->numEntries;
593*043036a2SApple OSS Distributions 
594*043036a2SApple OSS Distributions 	// Do not allow instruction re-ordering prior to the header check.
595*043036a2SApple OSS Distributions 	os_compiler_barrier();
596*043036a2SApple OSS Distributions 
597*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
598*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
599*043036a2SApple OSS Distributions 
600*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.wrStatus & IOCIRCULARDATAQUEUE_STATE_WRITE_INPROGRESS)) {
601*043036a2SApple OSS Distributions 		// Another thread is modifying the queue
602*043036a2SApple OSS Distributions 		return kIOReturnBusy;
603*043036a2SApple OSS Distributions 	}
604*043036a2SApple OSS Distributions 
605*043036a2SApple OSS Distributions 	//            size_t queueEntriesBufferSize = queueMemory->allocMemSize - CIRCULAR_DATA_QUEUE_MEMORY_HEADER_SIZE;
606*043036a2SApple OSS Distributions 	uint32_t writeIndex = currState.fields.wrIndex;
607*043036a2SApple OSS Distributions 	uint64_t nextWriteIndex = (writeIndex + 1) % queueNumEntries;
608*043036a2SApple OSS Distributions 	uint64_t nextSeqNum = currState.fields.seqNum + 1;
609*043036a2SApple OSS Distributions 	if (os_unlikely(nextSeqNum == UINT64_MAX)) {
610*043036a2SApple OSS Distributions 		// End of the world. How many enqueues are you trying to do !!!
611*043036a2SApple OSS Distributions //        abort();
612*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
613*043036a2SApple OSS Distributions 	}
614*043036a2SApple OSS Distributions 
615*043036a2SApple OSS Distributions 	__auto_type entry
616*043036a2SApple OSS Distributions 	        = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0] + (writeIndex * queueEntryDataSize));
617*043036a2SApple OSS Distributions 	//                printf("entry=%p\n", (void *)entry);
618*043036a2SApple OSS Distributions 
619*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
620*043036a2SApple OSS Distributions 	// within the queueMemory allocation before we begin writing.
621*043036a2SApple OSS Distributions 	if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
622*043036a2SApple OSS Distributions 	    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
623*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
624*043036a2SApple OSS Distributions 	}
625*043036a2SApple OSS Distributions 
626*043036a2SApple OSS Distributions 	//            if (os_unlikely(_isEntryOutOfBounds(queueHeaderShadow, queueMemory, entry) )) {
627*043036a2SApple OSS Distributions 	//                ret = kIOReturnBadArgument;
628*043036a2SApple OSS Distributions 	//                break;
629*043036a2SApple OSS Distributions 	//            }
630*043036a2SApple OSS Distributions 
631*043036a2SApple OSS Distributions 	os_compiler_barrier();
632*043036a2SApple OSS Distributions 
633*043036a2SApple OSS Distributions 	// All checks passed. Set the write bit.
634*043036a2SApple OSS Distributions 
635*043036a2SApple OSS Distributions 	IOCircularDataQueueState newState = currState;
636*043036a2SApple OSS Distributions 	newState.fields.wrStatus = IOCIRCULARDATAQUEUE_STATE_WRITE_INPROGRESS;
637*043036a2SApple OSS Distributions 	// lets not change the writeIndex and seq num here.
638*043036a2SApple OSS Distributions 	//            newState.fields.wrIndex = nextWriteIndex;
639*043036a2SApple OSS Distributions 	//    newState.fields.seqNum = currState.fields.seqNum + 1; // its ok even if we ever rollover UINT64_MAX!!
640*043036a2SApple OSS Distributions 
641*043036a2SApple OSS Distributions 	if (!atomic_compare_exchange_strong(&queueMemory->queueStateVal, &currState.val, newState.val)) {
642*043036a2SApple OSS Distributions 		// someone else is modifying the queue
643*043036a2SApple OSS Distributions 		return kIOReturnBusy;
644*043036a2SApple OSS Distributions 	}
645*043036a2SApple OSS Distributions 
646*043036a2SApple OSS Distributions 	// Update the entry header info
647*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
648*043036a2SApple OSS Distributions 	enHeaderInfo.val = 0;
649*043036a2SApple OSS Distributions 	enHeaderInfo.fields.wrStatus = IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS;
650*043036a2SApple OSS Distributions 	enHeaderInfo.fields.generation = currState.fields.generation;
651*043036a2SApple OSS Distributions 	//    enHeaderInfo.fields.seqNum = newState.fields.seqNum;
652*043036a2SApple OSS Distributions 	enHeaderInfo.fields.seqNum = nextSeqNum;
653*043036a2SApple OSS Distributions 	enHeaderInfo.fields.dataSize = dataSize;
654*043036a2SApple OSS Distributions 	atomic_store_explicit(&entry->headerInfoVal, enHeaderInfo.val, memory_order_release);
655*043036a2SApple OSS Distributions 
656*043036a2SApple OSS Distributions 	entry->sentinel = queueHeaderShadow->sentinel;
657*043036a2SApple OSS Distributions 	memcpy(entry->data, data, dataSize);
658*043036a2SApple OSS Distributions 	enHeaderInfo.fields.wrStatus = IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_COMPLETE;
659*043036a2SApple OSS Distributions 	atomic_store_explicit(&entry->headerInfoVal, enHeaderInfo.val, memory_order_release);
660*043036a2SApple OSS Distributions 
661*043036a2SApple OSS Distributions 	IOCircularDataQueueState finalState = newState;
662*043036a2SApple OSS Distributions 	finalState.fields.wrStatus = IOCIRCULARDATAQUEUE_STATE_WRITE_COMPLETE;
663*043036a2SApple OSS Distributions 	// Lets actually update the write index and seq num
664*043036a2SApple OSS Distributions 	finalState.fields.wrIndex = nextWriteIndex;
665*043036a2SApple OSS Distributions 	finalState.fields.seqNum = nextSeqNum;
666*043036a2SApple OSS Distributions 	atomic_store_explicit(&queueMemory->queueStateVal, finalState.val, memory_order_release);
667*043036a2SApple OSS Distributions 
668*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
669*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
670*043036a2SApple OSS Distributions 	}
671*043036a2SApple OSS Distributions 
672*043036a2SApple OSS Distributions 	return kIOReturnSuccess;
673*043036a2SApple OSS Distributions }
674*043036a2SApple OSS Distributions 
675*043036a2SApple OSS Distributions /*!
676*043036a2SApple OSS Distributions  * @function enqueueQueueMem
677*043036a2SApple OSS Distributions  * @abstract Enqueues a new entry on the queue.
678*043036a2SApple OSS Distributions  * @discussion This method adds a new data entry of dataSize to the queue.  It sets the size parameter of the entry
679*043036a2SApple OSS Distributions  * pointed to by the write index  and copies the memory pointed to by the data parameter in place in the queue.  Once
680*043036a2SApple OSS Distributions  * that is done, it moves the write index to the next index.
681*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
682*043036a2SApple OSS Distributions  * @param data Pointer to the data to be added to the queue.
683*043036a2SApple OSS Distributions  * @param dataSize Size of the data pointed to by data.
684*043036a2SApple OSS Distributions  *  @return
685*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` on success.
686*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
687*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid queue was provided.
688*043036a2SApple OSS Distributions  *  - `kIOReturnBusy` if another thread is enqueing concurrently
689*043036a2SApple OSS Distributions  *  - `kIOReturnUnsupported` if the queue has not been configured to support fixed size entries. Variable size is
690*043036a2SApple OSS Distributions  * currently not supported
691*043036a2SApple OSS Distributions  *  - Other values indicate an error.
692*043036a2SApple OSS Distributions  */
693*043036a2SApple OSS Distributions 
694*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
enqueueQueueMem(IOCircularDataQueue * queue,const void * data,size_t dataSize)695*043036a2SApple OSS Distributions enqueueQueueMem(IOCircularDataQueue *queue,
696*043036a2SApple OSS Distributions     const void *data,
697*043036a2SApple OSS Distributions     size_t dataSize)
698*043036a2SApple OSS Distributions {
699*043036a2SApple OSS Distributions 	return _enqueueInternal(queue, data, dataSize, 0);
700*043036a2SApple OSS Distributions }
701*043036a2SApple OSS Distributions 
702*043036a2SApple OSS Distributions /*!
703*043036a2SApple OSS Distributions  * @function isDataEntryValidInQueueMem
704*043036a2SApple OSS Distributions  * Verify if the data at the cursor position is still valid. Call this function after having read the data from the
705*043036a2SApple OSS Distributions  * queue, since the buffer could potentially have been overwritten while being read. <br>
706*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
707*043036a2SApple OSS Distributions  *  @return
708*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the data at the cursor position was valid.
709*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer valid and is
710*043036a2SApple OSS Distributions  *     potentially overwritten. Call getLatestInQueueMem to get the latest data and cursor position.
711*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid, possibly due to a reset of the queue.
712*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid param was passed.
713*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queueMemory is corrupted.
714*043036a2SApple OSS Distributions  *
715*043036a2SApple OSS Distributions  */
716*043036a2SApple OSS Distributions 
717*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
isDataEntryValidInQueueMem(IOCircularDataQueue * queue)718*043036a2SApple OSS Distributions isDataEntryValidInQueueMem(IOCircularDataQueue *queue)
719*043036a2SApple OSS Distributions {
720*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
721*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
722*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor const *cursor = &queue->queueCursor;
723*043036a2SApple OSS Distributions 
724*043036a2SApple OSS Distributions 	if (os_unlikely(queueMemory == NULL || queueHeaderShadow == NULL)) {
725*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
726*043036a2SApple OSS Distributions 	}
727*043036a2SApple OSS Distributions 
728*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
729*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
730*043036a2SApple OSS Distributions 	}
731*043036a2SApple OSS Distributions 
732*043036a2SApple OSS Distributions 	if (os_unlikely(_isCursorPositionInvalid(queue))) {
733*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
734*043036a2SApple OSS Distributions 	}
735*043036a2SApple OSS Distributions 
736*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
737*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
738*043036a2SApple OSS Distributions 
739*043036a2SApple OSS Distributions 	// Fahad: We may remove this filed since we don't actually use it. Instead just use generation check below.
740*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.rstStatus & IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS)) {
741*043036a2SApple OSS Distributions 		// Another thread is resetting the queue
742*043036a2SApple OSS Distributions 		return kIOReturnBusy;
743*043036a2SApple OSS Distributions 	}
744*043036a2SApple OSS Distributions 
745*043036a2SApple OSS Distributions 	uint32_t queueGeneration = currState.fields.generation;
746*043036a2SApple OSS Distributions 	if (queueGeneration != cursor->generation) {
747*043036a2SApple OSS Distributions 		//        return kIOReturnOverrun;
748*043036a2SApple OSS Distributions 		return kIOReturnAborted;
749*043036a2SApple OSS Distributions 	}
750*043036a2SApple OSS Distributions 
751*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
752*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
753*043036a2SApple OSS Distributions 	__auto_type entry = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0]
754*043036a2SApple OSS Distributions 	    + (cursor->position * queueEntryDataSize));
755*043036a2SApple OSS Distributions 
756*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
757*043036a2SApple OSS Distributions 	// within the queueMemory entries buffer before we begin writing.
758*043036a2SApple OSS Distributions 	if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
759*043036a2SApple OSS Distributions 	    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
760*043036a2SApple OSS Distributions 		queue_debug_error("Out of Bounds! " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT, QUEUE_ARGS(queueMemory),
761*043036a2SApple OSS Distributions 		    CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
762*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
763*043036a2SApple OSS Distributions 	}
764*043036a2SApple OSS Distributions 
765*043036a2SApple OSS Distributions 	os_compiler_barrier();
766*043036a2SApple OSS Distributions 
767*043036a2SApple OSS Distributions 	if (os_unlikely(entry->sentinel != queueHeaderShadow->sentinel)) {
768*043036a2SApple OSS Distributions 		queue_debug_error("entry->sentinel != queueMemory->sentinel " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
769*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
770*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
771*043036a2SApple OSS Distributions 	}
772*043036a2SApple OSS Distributions 
773*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
774*043036a2SApple OSS Distributions 	enHeaderInfo.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
775*043036a2SApple OSS Distributions 	uint32_t entryGeneration = enHeaderInfo.fields.generation;
776*043036a2SApple OSS Distributions 	if (os_unlikely(entryGeneration != queueGeneration)) {
777*043036a2SApple OSS Distributions 		queue_debug_note("entryGeneration != queueGeneration " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
778*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
779*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
780*043036a2SApple OSS Distributions 	}
781*043036a2SApple OSS Distributions 
782*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.wrStatus == IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS
783*043036a2SApple OSS Distributions 	    || enHeaderInfo.fields.seqNum != cursor->sequenceNum)) {
784*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
785*043036a2SApple OSS Distributions 	}
786*043036a2SApple OSS Distributions 
787*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
788*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
789*043036a2SApple OSS Distributions 	}
790*043036a2SApple OSS Distributions 
791*043036a2SApple OSS Distributions 	return kIOReturnSuccess;
792*043036a2SApple OSS Distributions }
793*043036a2SApple OSS Distributions 
794*043036a2SApple OSS Distributions /*!
795*043036a2SApple OSS Distributions  * @function setCursorLatestInQueueMem
796*043036a2SApple OSS Distributions  * Set the current cursor position to the latest entry in the queue. This only updates the cursor and does not read the
797*043036a2SApple OSS Distributions  * data from the queue. If nothing has been enqueued into the queue yet, this returns an error.
798*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
799*043036a2SApple OSS Distributions  *  @return
800*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated to the latest.
801*043036a2SApple OSS Distributions  *  - `kIOReturnUnderrun` if nothing has ever been enqueued into the queue since there is no latest entry.
802*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the queue is in an irrecoverable state.
803*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
804*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
805*043036a2SApple OSS Distributions  *  - Other values indicate an error.
806*043036a2SApple OSS Distributions  *
807*043036a2SApple OSS Distributions  */
808*043036a2SApple OSS Distributions 
809*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
setCursorLatestInQueueMem(IOCircularDataQueue * queue)810*043036a2SApple OSS Distributions setCursorLatestInQueueMem(IOCircularDataQueue *queue)
811*043036a2SApple OSS Distributions {
812*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
813*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
814*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor *cursor = &queue->queueCursor;
815*043036a2SApple OSS Distributions 
816*043036a2SApple OSS Distributions 	if (queueMemory == NULL || queueHeaderShadow == NULL) {
817*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
818*043036a2SApple OSS Distributions 	}
819*043036a2SApple OSS Distributions 
820*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
821*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
822*043036a2SApple OSS Distributions 	}
823*043036a2SApple OSS Distributions 
824*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
825*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
826*043036a2SApple OSS Distributions 
827*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
828*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
829*043036a2SApple OSS Distributions 
830*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.rstStatus & IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS)) {
831*043036a2SApple OSS Distributions 		// Another thread is resetting the queue
832*043036a2SApple OSS Distributions 		return kIOReturnBusy;
833*043036a2SApple OSS Distributions 	}
834*043036a2SApple OSS Distributions 
835*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.seqNum == UINT64_MAX)) {
836*043036a2SApple OSS Distributions 		// Nothing has ever been written to the queue yet.
837*043036a2SApple OSS Distributions 		return kIOReturnUnderrun;
838*043036a2SApple OSS Distributions 	}
839*043036a2SApple OSS Distributions 
840*043036a2SApple OSS Distributions 	uint32_t queueGeneration = currState.fields.generation;
841*043036a2SApple OSS Distributions 	uint32_t readIndex
842*043036a2SApple OSS Distributions 	        = (currState.fields.wrIndex > 0) ? (currState.fields.wrIndex - 1) : (queueHeaderShadow->numEntries - 1);
843*043036a2SApple OSS Distributions 
844*043036a2SApple OSS Distributions 	__auto_type entry
845*043036a2SApple OSS Distributions 	        = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0] + (readIndex * queueEntryDataSize));
846*043036a2SApple OSS Distributions 
847*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
848*043036a2SApple OSS Distributions 	// within the queueMemory entries buffer before we begin writing.
849*043036a2SApple OSS Distributions 	if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
850*043036a2SApple OSS Distributions 	    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
851*043036a2SApple OSS Distributions 		queue_debug_error("Out of Bounds! " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT, QUEUE_ARGS(queueMemory),
852*043036a2SApple OSS Distributions 		    CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
853*043036a2SApple OSS Distributions 		return kIOReturnAborted;
854*043036a2SApple OSS Distributions 	}
855*043036a2SApple OSS Distributions 
856*043036a2SApple OSS Distributions 	os_compiler_barrier();
857*043036a2SApple OSS Distributions 
858*043036a2SApple OSS Distributions 	if (os_unlikely(entry->sentinel != queueHeaderShadow->sentinel)) {
859*043036a2SApple OSS Distributions 		queue_debug_error("entry->sentinel != queueMemory->sentinel " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
860*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
861*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
862*043036a2SApple OSS Distributions 	}
863*043036a2SApple OSS Distributions 
864*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
865*043036a2SApple OSS Distributions 	enHeaderInfo.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
866*043036a2SApple OSS Distributions 	uint32_t entryGeneration = enHeaderInfo.fields.generation;
867*043036a2SApple OSS Distributions 	if (os_unlikely(entryGeneration != queueGeneration)) {
868*043036a2SApple OSS Distributions 		queue_debug_note("entryGeneration != queueGeneration " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
869*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
870*043036a2SApple OSS Distributions 		return kIOReturnAborted;
871*043036a2SApple OSS Distributions 	}
872*043036a2SApple OSS Distributions 
873*043036a2SApple OSS Distributions 	cursor->position = readIndex;
874*043036a2SApple OSS Distributions 	cursor->generation = entryGeneration;
875*043036a2SApple OSS Distributions 	cursor->sequenceNum = enHeaderInfo.fields.seqNum;
876*043036a2SApple OSS Distributions 
877*043036a2SApple OSS Distributions 	return kIOReturnSuccess;
878*043036a2SApple OSS Distributions }
879*043036a2SApple OSS Distributions 
880*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
_getLatestInQueueMemInternal(IOCircularDataQueue * queue,void ** data,size_t * size,bool copyMem)881*043036a2SApple OSS Distributions _getLatestInQueueMemInternal(IOCircularDataQueue *queue,
882*043036a2SApple OSS Distributions     void **data,
883*043036a2SApple OSS Distributions     size_t *size,
884*043036a2SApple OSS Distributions     bool copyMem)
885*043036a2SApple OSS Distributions {
886*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
887*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
888*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor *cursor = &queue->queueCursor;
889*043036a2SApple OSS Distributions 
890*043036a2SApple OSS Distributions 	IOReturn ret = kIOReturnTimeout;
891*043036a2SApple OSS Distributions 	if (queueMemory == NULL || data == NULL || size == NULL || queueHeaderShadow == NULL) {
892*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
893*043036a2SApple OSS Distributions 	}
894*043036a2SApple OSS Distributions 
895*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
896*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
897*043036a2SApple OSS Distributions 	}
898*043036a2SApple OSS Distributions 
899*043036a2SApple OSS Distributions 	const size_t kNumRetries = 5; // Number of retries if the latest index data gets overwritten by a writer.
900*043036a2SApple OSS Distributions 	size_t retry = kNumRetries;
901*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
902*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
903*043036a2SApple OSS Distributions 	size_t inSize;
904*043036a2SApple OSS Distributions 
905*043036a2SApple OSS Distributions 	inSize = *size;
906*043036a2SApple OSS Distributions 	do {
907*043036a2SApple OSS Distributions 		*size = 0;
908*043036a2SApple OSS Distributions 		retry--;
909*043036a2SApple OSS Distributions 		IOCircularDataQueueState currState;
910*043036a2SApple OSS Distributions 		currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_consume);
911*043036a2SApple OSS Distributions 
912*043036a2SApple OSS Distributions 		if (os_unlikely(currState.fields.rstStatus & IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS)) {
913*043036a2SApple OSS Distributions 			// Another thread is resetting the queue
914*043036a2SApple OSS Distributions 			return kIOReturnBusy;
915*043036a2SApple OSS Distributions 		}
916*043036a2SApple OSS Distributions 
917*043036a2SApple OSS Distributions 		if (os_unlikely(currState.fields.seqNum == UINT64_MAX)) {
918*043036a2SApple OSS Distributions 			// Nothing has ever been written to the queue yet.
919*043036a2SApple OSS Distributions 			return kIOReturnUnderrun;
920*043036a2SApple OSS Distributions 		}
921*043036a2SApple OSS Distributions 
922*043036a2SApple OSS Distributions 		uint32_t queueGeneration = currState.fields.generation;
923*043036a2SApple OSS Distributions 		uint32_t readIndex
924*043036a2SApple OSS Distributions 		        = (currState.fields.wrIndex > 0) ? (currState.fields.wrIndex - 1) : (queueHeaderShadow->numEntries - 1);
925*043036a2SApple OSS Distributions 
926*043036a2SApple OSS Distributions 		__auto_type entry = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0]
927*043036a2SApple OSS Distributions 		    + (readIndex * queueEntryDataSize));
928*043036a2SApple OSS Distributions 
929*043036a2SApple OSS Distributions 		// SANITY CHECK - Final check to ensure the 'entry' pointer is
930*043036a2SApple OSS Distributions 		// within the queueMemory entries buffer before we begin writing.
931*043036a2SApple OSS Distributions 		if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
932*043036a2SApple OSS Distributions 		    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
933*043036a2SApple OSS Distributions 			queue_debug_error("Out of Bounds! " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
934*043036a2SApple OSS Distributions 			    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
935*043036a2SApple OSS Distributions 			return kIOReturnBadArgument;
936*043036a2SApple OSS Distributions 		}
937*043036a2SApple OSS Distributions 
938*043036a2SApple OSS Distributions 		os_compiler_barrier();
939*043036a2SApple OSS Distributions 
940*043036a2SApple OSS Distributions 		if (os_unlikely(entry->sentinel != queueHeaderShadow->sentinel)) {
941*043036a2SApple OSS Distributions 			queue_debug_error("entry->sentinel != queueMemory->sentinel " QUEUE_FORMAT " " CURSOR_FORMAT
942*043036a2SApple OSS Distributions 			    " " ENTRY_FORMAT,
943*043036a2SApple OSS Distributions 			    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
944*043036a2SApple OSS Distributions 			return kIOReturnBadMedia;
945*043036a2SApple OSS Distributions 		}
946*043036a2SApple OSS Distributions 
947*043036a2SApple OSS Distributions 		IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
948*043036a2SApple OSS Distributions 		enHeaderInfo.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
949*043036a2SApple OSS Distributions 		uint32_t entryGeneration = enHeaderInfo.fields.generation;
950*043036a2SApple OSS Distributions 		/* Since the time we read the queue header, was the queue
951*043036a2SApple OSS Distributions 		 *   - reset
952*043036a2SApple OSS Distributions 		 *   - the entry is being overwritten
953*043036a2SApple OSS Distributions 		 *   - the entry was overwritten and hence the seq numbers don't match anymore.
954*043036a2SApple OSS Distributions 		 *
955*043036a2SApple OSS Distributions 		 *  Lets retry in such a case
956*043036a2SApple OSS Distributions 		 */
957*043036a2SApple OSS Distributions 		if (os_unlikely(entryGeneration != queueGeneration
958*043036a2SApple OSS Distributions 		    || enHeaderInfo.fields.wrStatus == IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS
959*043036a2SApple OSS Distributions 		    || currState.fields.seqNum != enHeaderInfo.fields.seqNum)) {
960*043036a2SApple OSS Distributions 			continue;
961*043036a2SApple OSS Distributions 		}
962*043036a2SApple OSS Distributions 
963*043036a2SApple OSS Distributions 		cursor->position = readIndex;
964*043036a2SApple OSS Distributions 		cursor->generation = entryGeneration;
965*043036a2SApple OSS Distributions 		cursor->sequenceNum = enHeaderInfo.fields.seqNum;
966*043036a2SApple OSS Distributions 
967*043036a2SApple OSS Distributions 		if (os_unlikely(enHeaderInfo.fields.dataSize > queueHeaderShadow->entryDataSize)) {
968*043036a2SApple OSS Distributions 			ret = kIOReturnOverrun;
969*043036a2SApple OSS Distributions 			break;
970*043036a2SApple OSS Distributions 		}
971*043036a2SApple OSS Distributions 		*size = enHeaderInfo.fields.dataSize;
972*043036a2SApple OSS Distributions 
973*043036a2SApple OSS Distributions 		if (!copyMem) {
974*043036a2SApple OSS Distributions 			*data = entry->data;
975*043036a2SApple OSS Distributions 			ret = kIOReturnSuccess;
976*043036a2SApple OSS Distributions 			break; // break out, we're done
977*043036a2SApple OSS Distributions 		} else {
978*043036a2SApple OSS Distributions 			if (os_unlikely(enHeaderInfo.fields.dataSize > inSize)) {
979*043036a2SApple OSS Distributions 				return kIOReturnOverrun;
980*043036a2SApple OSS Distributions 			}
981*043036a2SApple OSS Distributions 			memcpy(*data, entry->data, enHeaderInfo.fields.dataSize);
982*043036a2SApple OSS Distributions 			// Lets re-verify after the memcpy if the buffer is/has been overwritten.
983*043036a2SApple OSS Distributions 
984*043036a2SApple OSS Distributions 			IOCircularDataQueueEntryHeaderInfo enHeaderInfoAfter;
985*043036a2SApple OSS Distributions 			enHeaderInfoAfter.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
986*043036a2SApple OSS Distributions 			// Did something change ?
987*043036a2SApple OSS Distributions 			if (enHeaderInfo.val == enHeaderInfoAfter.val) {
988*043036a2SApple OSS Distributions 				ret = kIOReturnSuccess;
989*043036a2SApple OSS Distributions 				break;
990*043036a2SApple OSS Distributions 			} else {
991*043036a2SApple OSS Distributions 				// we failed so we'll retry.
992*043036a2SApple OSS Distributions 				*size = 0;
993*043036a2SApple OSS Distributions 			}
994*043036a2SApple OSS Distributions 		}
995*043036a2SApple OSS Distributions 	} while (retry);
996*043036a2SApple OSS Distributions 
997*043036a2SApple OSS Distributions 	if ((kIOReturnSuccess == ret) && os_unlikely(_isQueueMemoryCorrupted(queue))) {
998*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
999*043036a2SApple OSS Distributions 	}
1000*043036a2SApple OSS Distributions 
1001*043036a2SApple OSS Distributions 	return ret;
1002*043036a2SApple OSS Distributions }
1003*043036a2SApple OSS Distributions 
1004*043036a2SApple OSS Distributions /*!
1005*043036a2SApple OSS Distributions  * @function getLatestInQueueMem
1006*043036a2SApple OSS Distributions  * Access the latest entry data, also update the cursor position to the latest. No copy is made of the data. <br> Caller
1007*043036a2SApple OSS Distributions  * is supposed to call isDataEntryValidInQueueMem() to check data integrity after reading the data is complete.
1008*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1009*043036a2SApple OSS Distributions  * @param data A pointer to the data memory region for the latest entry data in the queue.
1010*043036a2SApple OSS Distributions  * @param size A pointer to the size of the data parameter.  On return, this contains the actual size of the data
1011*043036a2SApple OSS Distributions  * pointed to by data param.
1012*043036a2SApple OSS Distributions  *  @return
1013*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated.
1014*043036a2SApple OSS Distributions  *  - `kIOReturnUnderrun` if nothing has ever been enqueued into the queue
1015*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1016*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid queue was provided.
1017*043036a2SApple OSS Distributions  *  - `kIOReturnTimeout` if the reader timed out when trying to read. This is possible if the writer overwrites the
1018*043036a2SApple OSS Distributions  * latest index a reader is about to read. The function times out if the read is unsuccessful after multiple retries.
1019*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1020*043036a2SApple OSS Distributions  *
1021*043036a2SApple OSS Distributions  */
1022*043036a2SApple OSS Distributions 
1023*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
getLatestInQueueMem(IOCircularDataQueue * queue,void ** data,size_t * size)1024*043036a2SApple OSS Distributions getLatestInQueueMem(IOCircularDataQueue *queue,
1025*043036a2SApple OSS Distributions     void **data,
1026*043036a2SApple OSS Distributions     size_t *size)
1027*043036a2SApple OSS Distributions {
1028*043036a2SApple OSS Distributions 	return _getLatestInQueueMemInternal(queue, data, size, false);
1029*043036a2SApple OSS Distributions }
1030*043036a2SApple OSS Distributions 
1031*043036a2SApple OSS Distributions /*!
1032*043036a2SApple OSS Distributions  * @function copyLatestInQueueMem
1033*043036a2SApple OSS Distributions  *  Access the latest entry data and copy into the provided buffer. Also update the cursor position to the latest.
1034*043036a2SApple OSS Distributions  * Function gaurantees that the new data returned is always valid hence no need to call isDataEntryValidInQueueMem().
1035*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1036*043036a2SApple OSS Distributions  * @param data Pointer to memory into which the latest data from the queue is copied. Lifetime of this memory is
1037*043036a2SApple OSS Distributions  * controlled by the caller.
1038*043036a2SApple OSS Distributions  * @param size Size of the data buffer provided for copying. On return, this contains the actual size of the data
1039*043036a2SApple OSS Distributions  * pointed to by data param.
1040*043036a2SApple OSS Distributions  *  @return
1041*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated.
1042*043036a2SApple OSS Distributions  *  - `kIOReturnUnderrun` if nothing has ever been enqueued into the queue
1043*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if the buffer provided to copy the data is NULL or  if an invalid queue was provided..
1044*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1045*043036a2SApple OSS Distributions  *  - `kIOReturnTimeout` if the reader timed out when trying to copy the latest data. This is possible if the writer
1046*043036a2SApple OSS Distributions  * overwrites the latest index a reader is about to copy. The function times out if the copy is unsuccessful after
1047*043036a2SApple OSS Distributions  * multiple retries.
1048*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1049*043036a2SApple OSS Distributions  *
1050*043036a2SApple OSS Distributions  */
1051*043036a2SApple OSS Distributions 
1052*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
copyLatestInQueueMem(IOCircularDataQueue * queue,void * data,size_t * size)1053*043036a2SApple OSS Distributions copyLatestInQueueMem(IOCircularDataQueue *queue,
1054*043036a2SApple OSS Distributions     void *data,
1055*043036a2SApple OSS Distributions     size_t *size)
1056*043036a2SApple OSS Distributions {
1057*043036a2SApple OSS Distributions 	return _getLatestInQueueMemInternal(queue, &data, size, true);
1058*043036a2SApple OSS Distributions }
1059*043036a2SApple OSS Distributions 
1060*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
_getNextInQueueMemInternal(IOCircularDataQueue * queue,void ** data,size_t * size,bool copyMem)1061*043036a2SApple OSS Distributions _getNextInQueueMemInternal(IOCircularDataQueue *queue,
1062*043036a2SApple OSS Distributions     void **data,
1063*043036a2SApple OSS Distributions     size_t *size,
1064*043036a2SApple OSS Distributions     bool copyMem)
1065*043036a2SApple OSS Distributions {
1066*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
1067*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
1068*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor *cursor = &queue->queueCursor;
1069*043036a2SApple OSS Distributions 
1070*043036a2SApple OSS Distributions 	IOReturn ret = kIOReturnError;
1071*043036a2SApple OSS Distributions 	size_t inSize;
1072*043036a2SApple OSS Distributions 
1073*043036a2SApple OSS Distributions 	if (queueMemory == NULL || data == NULL || size == NULL || queueHeaderShadow == NULL) {
1074*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1075*043036a2SApple OSS Distributions 	}
1076*043036a2SApple OSS Distributions 
1077*043036a2SApple OSS Distributions 	inSize = *size;
1078*043036a2SApple OSS Distributions 	*size = 0;
1079*043036a2SApple OSS Distributions 
1080*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
1081*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1082*043036a2SApple OSS Distributions 	}
1083*043036a2SApple OSS Distributions 
1084*043036a2SApple OSS Distributions 	if (os_unlikely(_isCursorPositionInvalid(queue))) {
1085*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1086*043036a2SApple OSS Distributions 	}
1087*043036a2SApple OSS Distributions 
1088*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
1089*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
1090*043036a2SApple OSS Distributions 
1091*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
1092*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
1093*043036a2SApple OSS Distributions 
1094*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.rstStatus & IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS)) {
1095*043036a2SApple OSS Distributions 		// Another thread is resetting the queue
1096*043036a2SApple OSS Distributions 		return kIOReturnBusy;
1097*043036a2SApple OSS Distributions 	}
1098*043036a2SApple OSS Distributions 
1099*043036a2SApple OSS Distributions 	uint32_t queueGeneration = currState.fields.generation;
1100*043036a2SApple OSS Distributions 
1101*043036a2SApple OSS Distributions 	// was the queue reset ?
1102*043036a2SApple OSS Distributions 	if (os_unlikely(cursor->generation != queueGeneration || cursor->sequenceNum > currState.fields.seqNum)) {
1103*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1104*043036a2SApple OSS Distributions 	}
1105*043036a2SApple OSS Distributions 
1106*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.seqNum == UINT64_MAX)) {
1107*043036a2SApple OSS Distributions 		// Nothing has ever been written to the queue yet.
1108*043036a2SApple OSS Distributions 		return kIOReturnUnderrun;
1109*043036a2SApple OSS Distributions 	}
1110*043036a2SApple OSS Distributions 
1111*043036a2SApple OSS Distributions 	// nothing new written or an active write is in progress for the next entry.
1112*043036a2SApple OSS Distributions 	if (os_unlikely(cursor->sequenceNum == currState.fields.seqNum
1113*043036a2SApple OSS Distributions 	    || ((cursor->sequenceNum + 1) == currState.fields.seqNum
1114*043036a2SApple OSS Distributions 	    && currState.fields.wrStatus == IOCIRCULARDATAQUEUE_STATE_WRITE_INPROGRESS))) {
1115*043036a2SApple OSS Distributions 		return kIOReturnUnderrun;
1116*043036a2SApple OSS Distributions 	}
1117*043036a2SApple OSS Distributions 
1118*043036a2SApple OSS Distributions 	uint32_t nextIndex = (cursor->position + 1) % queueHeaderShadow->numEntries;
1119*043036a2SApple OSS Distributions 	__auto_type entry
1120*043036a2SApple OSS Distributions 	        = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0] + (nextIndex * queueEntryDataSize));
1121*043036a2SApple OSS Distributions 
1122*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
1123*043036a2SApple OSS Distributions 	// within the queueMemory entries buffer before we begin writing.
1124*043036a2SApple OSS Distributions 	if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
1125*043036a2SApple OSS Distributions 	    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
1126*043036a2SApple OSS Distributions 		queue_debug_error("Out of Bounds! " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT, QUEUE_ARGS(queueMemory),
1127*043036a2SApple OSS Distributions 		    CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1128*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1129*043036a2SApple OSS Distributions 	}
1130*043036a2SApple OSS Distributions 
1131*043036a2SApple OSS Distributions 	os_compiler_barrier();
1132*043036a2SApple OSS Distributions 
1133*043036a2SApple OSS Distributions 	if (os_unlikely(entry->sentinel != queueHeaderShadow->sentinel)) {
1134*043036a2SApple OSS Distributions 		queue_debug_error("entry->sentinel != queueMemory->sentinel " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
1135*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1136*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1137*043036a2SApple OSS Distributions 	}
1138*043036a2SApple OSS Distributions 
1139*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
1140*043036a2SApple OSS Distributions 	enHeaderInfo.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
1141*043036a2SApple OSS Distributions 	uint32_t entryGeneration = enHeaderInfo.fields.generation;
1142*043036a2SApple OSS Distributions 	if (os_unlikely(entryGeneration != queueGeneration)) {
1143*043036a2SApple OSS Distributions 		queue_debug_note("entryGeneration != queueGeneration " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
1144*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1145*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1146*043036a2SApple OSS Distributions 	}
1147*043036a2SApple OSS Distributions 
1148*043036a2SApple OSS Distributions 	// is the entry currently being written to or has the cursor fallen too far behind and the cursor is no longer
1149*043036a2SApple OSS Distributions 	// valid.
1150*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.wrStatus == IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS
1151*043036a2SApple OSS Distributions 	    || enHeaderInfo.fields.seqNum != cursor->sequenceNum + 1)) {
1152*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1153*043036a2SApple OSS Distributions 	}
1154*043036a2SApple OSS Distributions 
1155*043036a2SApple OSS Distributions 	cursor->position = nextIndex;
1156*043036a2SApple OSS Distributions 	cursor->generation = entryGeneration;
1157*043036a2SApple OSS Distributions 	cursor->sequenceNum = enHeaderInfo.fields.seqNum;
1158*043036a2SApple OSS Distributions 
1159*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.dataSize > queueHeaderShadow->entryDataSize)) {
1160*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1161*043036a2SApple OSS Distributions 	}
1162*043036a2SApple OSS Distributions 	*size = enHeaderInfo.fields.dataSize;
1163*043036a2SApple OSS Distributions 
1164*043036a2SApple OSS Distributions 	if (!copyMem) {
1165*043036a2SApple OSS Distributions 		*data = entry->data;
1166*043036a2SApple OSS Distributions 		ret = kIOReturnSuccess;
1167*043036a2SApple OSS Distributions 	} else {
1168*043036a2SApple OSS Distributions 		if (os_unlikely(enHeaderInfo.fields.dataSize > inSize)) {
1169*043036a2SApple OSS Distributions 			return kIOReturnOverrun;
1170*043036a2SApple OSS Distributions 		}
1171*043036a2SApple OSS Distributions 		memcpy(*data, entry->data, enHeaderInfo.fields.dataSize);
1172*043036a2SApple OSS Distributions 		// Lets re-verify after the memcpy if the buffer is/has been overwritten.
1173*043036a2SApple OSS Distributions 
1174*043036a2SApple OSS Distributions 		IOCircularDataQueueEntryHeaderInfo enHeaderInfoAfter;
1175*043036a2SApple OSS Distributions 		enHeaderInfoAfter.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
1176*043036a2SApple OSS Distributions 		// Did something change, while we were memcopying ?
1177*043036a2SApple OSS Distributions 		if (enHeaderInfo.val == enHeaderInfoAfter.val) {
1178*043036a2SApple OSS Distributions 			ret = kIOReturnSuccess;
1179*043036a2SApple OSS Distributions 		} else {
1180*043036a2SApple OSS Distributions 			// while we were memcopying, the writer wrapped around and is writing into our index. or the queue got reset
1181*043036a2SApple OSS Distributions 			*size = 0;
1182*043036a2SApple OSS Distributions 			ret = kIOReturnOverrun;
1183*043036a2SApple OSS Distributions 		}
1184*043036a2SApple OSS Distributions 	}
1185*043036a2SApple OSS Distributions 
1186*043036a2SApple OSS Distributions 	if ((kIOReturnSuccess == ret) && os_unlikely(_isQueueMemoryCorrupted(queue))) {
1187*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1188*043036a2SApple OSS Distributions 	}
1189*043036a2SApple OSS Distributions 
1190*043036a2SApple OSS Distributions 	return ret;
1191*043036a2SApple OSS Distributions }
1192*043036a2SApple OSS Distributions 
1193*043036a2SApple OSS Distributions /*!
1194*043036a2SApple OSS Distributions  * @function getNextInQueueMem
1195*043036a2SApple OSS Distributions  * Access the data at the next cursor position and updates the cursor position to the next. No copy is made of the data.
1196*043036a2SApple OSS Distributions  * <br> Caller is supposed to call isDataEntryValidInQueueMem() to check data integrity after reading the data is
1197*043036a2SApple OSS Distributions  * complete.
1198*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1199*043036a2SApple OSS Distributions  * @param data A pointer to the data memory region for the next entry data in the queue.
1200*043036a2SApple OSS Distributions  * @param size A pointer to the size of the data parameter.  On return, this contains the actual size of the data
1201*043036a2SApple OSS Distributions  * pointed to by data param.
1202*043036a2SApple OSS Distributions  *  @return
1203*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated.
1204*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid, possibly due to a reset of the queue.
1205*043036a2SApple OSS Distributions  *  - `kIOReturnUnderrun` if the cursor has reached the latest available data.
1206*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer in
1207*043036a2SApple OSS Distributions  *     the queue's buffer. Call getLatestInQueueMem to get the latest data and cursor position.
1208*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
1209*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1210*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1211*043036a2SApple OSS Distributions  *
1212*043036a2SApple OSS Distributions  */
1213*043036a2SApple OSS Distributions 
1214*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
getNextInQueueMem(IOCircularDataQueue * queue,void ** data,size_t * size)1215*043036a2SApple OSS Distributions getNextInQueueMem(IOCircularDataQueue *queue,
1216*043036a2SApple OSS Distributions     void **data,
1217*043036a2SApple OSS Distributions     size_t *size)
1218*043036a2SApple OSS Distributions {
1219*043036a2SApple OSS Distributions 	return _getNextInQueueMemInternal(queue, data, size, false);
1220*043036a2SApple OSS Distributions }
1221*043036a2SApple OSS Distributions 
1222*043036a2SApple OSS Distributions /*!
1223*043036a2SApple OSS Distributions  * @function copyNextInQueueMem
1224*043036a2SApple OSS Distributions  * Access the data at the next cursor position and copy into the provided buffer. Also update the cursor position to the
1225*043036a2SApple OSS Distributions  * next. If successful, function gaurantees that the data returned is always valid hence no need to call
1226*043036a2SApple OSS Distributions  * isDataEntryValidInQueueMem().
1227*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1228*043036a2SApple OSS Distributions  * @param data Pointer to memory into which the next data from the queue is copied. Lifetime of this memory is
1229*043036a2SApple OSS Distributions  * controlled by the caller.
1230*043036a2SApple OSS Distributions  * @param size Size of the data buffer provided for copying. On return, this contains the actual size of the data
1231*043036a2SApple OSS Distributions  * pointed to by data param.
1232*043036a2SApple OSS Distributions  *  @return
1233*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated.
1234*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid, possibly due to a reset of the queue.
1235*043036a2SApple OSS Distributions  *  - `kIOReturnUnderrun` if the cursor has reached the latest available data.
1236*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer in
1237*043036a2SApple OSS Distributions  *     the queue's buffer. Call getLatestInQueueMem to get the latest data and cursor position.
1238*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
1239*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1240*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1241*043036a2SApple OSS Distributions  *
1242*043036a2SApple OSS Distributions  */
1243*043036a2SApple OSS Distributions 
1244*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
copyNextInQueueMem(IOCircularDataQueue * queue,void * data,size_t * size)1245*043036a2SApple OSS Distributions copyNextInQueueMem(IOCircularDataQueue *queue,
1246*043036a2SApple OSS Distributions     void *data,
1247*043036a2SApple OSS Distributions     size_t *size)
1248*043036a2SApple OSS Distributions {
1249*043036a2SApple OSS Distributions 	return _getNextInQueueMemInternal(queue, &data, size, true);
1250*043036a2SApple OSS Distributions }
1251*043036a2SApple OSS Distributions 
1252*043036a2SApple OSS Distributions /*!
1253*043036a2SApple OSS Distributions  * @function getPrevInQueueMem
1254*043036a2SApple OSS Distributions  * Access the data at the previous cursor position and updates the cursor position to the previous. No copy is made of
1255*043036a2SApple OSS Distributions  * the data. <br> Caller is supposed to call isDataEntryValidInQueueMem() to check data integrity after reading the data
1256*043036a2SApple OSS Distributions  * is complete.
1257*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1258*043036a2SApple OSS Distributions  * @param data A pointer to the data memory region for the previous entry data in the queue.
1259*043036a2SApple OSS Distributions  * @param size A pointer to the size of the data parameter.  On return, this contains the actual size of the data
1260*043036a2SApple OSS Distributions  * pointed to by data param.
1261*043036a2SApple OSS Distributions  *  @return
1262*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated to the previous.
1263*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid, possibly due to a reset of the queue.
1264*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer in
1265*043036a2SApple OSS Distributions  *     the queue's buffer. Call getLatestInQueueMem to get the latest data and cursor position.
1266*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
1267*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1268*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1269*043036a2SApple OSS Distributions  *
1270*043036a2SApple OSS Distributions  */
1271*043036a2SApple OSS Distributions 
1272*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
_getPrevInQueueMemInternal(IOCircularDataQueue * queue,void ** data,size_t * size,bool copyMem)1273*043036a2SApple OSS Distributions _getPrevInQueueMemInternal(IOCircularDataQueue *queue,
1274*043036a2SApple OSS Distributions     void **data,
1275*043036a2SApple OSS Distributions     size_t *size,
1276*043036a2SApple OSS Distributions     bool copyMem)
1277*043036a2SApple OSS Distributions {
1278*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
1279*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
1280*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor *cursor = &queue->queueCursor;
1281*043036a2SApple OSS Distributions 	size_t inSize;
1282*043036a2SApple OSS Distributions 
1283*043036a2SApple OSS Distributions 	IOReturn ret = kIOReturnError;
1284*043036a2SApple OSS Distributions 	if (queueMemory == NULL || data == NULL || size == NULL || queueHeaderShadow == NULL) {
1285*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1286*043036a2SApple OSS Distributions 	}
1287*043036a2SApple OSS Distributions 
1288*043036a2SApple OSS Distributions 	inSize = *size;
1289*043036a2SApple OSS Distributions 	*size = 0;
1290*043036a2SApple OSS Distributions 
1291*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
1292*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1293*043036a2SApple OSS Distributions 	}
1294*043036a2SApple OSS Distributions 
1295*043036a2SApple OSS Distributions 	if (os_unlikely(_isCursorPositionInvalid(queue))) {
1296*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1297*043036a2SApple OSS Distributions 	}
1298*043036a2SApple OSS Distributions 
1299*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
1300*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
1301*043036a2SApple OSS Distributions 
1302*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
1303*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
1304*043036a2SApple OSS Distributions 
1305*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.rstStatus & IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS)) {
1306*043036a2SApple OSS Distributions 		// Another thread is resetting the queue
1307*043036a2SApple OSS Distributions 		return kIOReturnBusy;
1308*043036a2SApple OSS Distributions 	}
1309*043036a2SApple OSS Distributions 
1310*043036a2SApple OSS Distributions 	uint32_t queueGeneration = currState.fields.generation;
1311*043036a2SApple OSS Distributions 
1312*043036a2SApple OSS Distributions 	// was the queue reset ?
1313*043036a2SApple OSS Distributions 	if (os_unlikely(cursor->generation != queueGeneration || cursor->sequenceNum > currState.fields.seqNum)) {
1314*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1315*043036a2SApple OSS Distributions 	}
1316*043036a2SApple OSS Distributions 
1317*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.seqNum == UINT64_MAX)) {
1318*043036a2SApple OSS Distributions 		// Nothing has ever been written to the queue yet.
1319*043036a2SApple OSS Distributions 		return kIOReturnUnderrun;
1320*043036a2SApple OSS Distributions 	}
1321*043036a2SApple OSS Distributions 
1322*043036a2SApple OSS Distributions 	uint32_t prevIndex = (cursor->position == 0) ? (queueHeaderShadow->numEntries - 1) : (cursor->position - 1);
1323*043036a2SApple OSS Distributions 	__auto_type entry
1324*043036a2SApple OSS Distributions 	        = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0] + (prevIndex * queueEntryDataSize));
1325*043036a2SApple OSS Distributions 
1326*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
1327*043036a2SApple OSS Distributions 	// within the queueMemory entries buffer before we begin writing.
1328*043036a2SApple OSS Distributions 	if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
1329*043036a2SApple OSS Distributions 	    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
1330*043036a2SApple OSS Distributions 		queue_debug_error("Out of Bounds! " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT, QUEUE_ARGS(queueMemory),
1331*043036a2SApple OSS Distributions 		    CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1332*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1333*043036a2SApple OSS Distributions 	}
1334*043036a2SApple OSS Distributions 
1335*043036a2SApple OSS Distributions 	os_compiler_barrier();
1336*043036a2SApple OSS Distributions 
1337*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
1338*043036a2SApple OSS Distributions 	enHeaderInfo.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
1339*043036a2SApple OSS Distributions 	// is the entry currently being written to or this is the newest entry that was just written.
1340*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.wrStatus == IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS
1341*043036a2SApple OSS Distributions 	    || enHeaderInfo.fields.seqNum > cursor->sequenceNum)) {
1342*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1343*043036a2SApple OSS Distributions 	}
1344*043036a2SApple OSS Distributions 
1345*043036a2SApple OSS Distributions 	uint32_t entryGeneration = enHeaderInfo.fields.generation;
1346*043036a2SApple OSS Distributions 	if (os_unlikely(entryGeneration != queueGeneration)) {
1347*043036a2SApple OSS Distributions 		queue_debug_note("entryGeneration != queueGeneration " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
1348*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1349*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1350*043036a2SApple OSS Distributions 	}
1351*043036a2SApple OSS Distributions 
1352*043036a2SApple OSS Distributions 	// the sentinel has been corrupted.
1353*043036a2SApple OSS Distributions 	if (os_unlikely(entry->sentinel != queueHeaderShadow->sentinel)) {
1354*043036a2SApple OSS Distributions 		queue_debug_error("entry->sentinel != queueMemory->sentinel " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
1355*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1356*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1357*043036a2SApple OSS Distributions 	}
1358*043036a2SApple OSS Distributions 
1359*043036a2SApple OSS Distributions 	cursor->position = prevIndex;
1360*043036a2SApple OSS Distributions 	cursor->generation = entryGeneration;
1361*043036a2SApple OSS Distributions 	cursor->sequenceNum = enHeaderInfo.fields.seqNum;
1362*043036a2SApple OSS Distributions 
1363*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.dataSize > queueHeaderShadow->entryDataSize)) {
1364*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1365*043036a2SApple OSS Distributions 	}
1366*043036a2SApple OSS Distributions 	*size = enHeaderInfo.fields.dataSize;
1367*043036a2SApple OSS Distributions 	ret = kIOReturnSuccess;
1368*043036a2SApple OSS Distributions 
1369*043036a2SApple OSS Distributions 	if (!copyMem) {
1370*043036a2SApple OSS Distributions 		*data = entry->data;
1371*043036a2SApple OSS Distributions 	} else {
1372*043036a2SApple OSS Distributions 		if (os_unlikely(enHeaderInfo.fields.dataSize > inSize)) {
1373*043036a2SApple OSS Distributions 			return kIOReturnOverrun;
1374*043036a2SApple OSS Distributions 		}
1375*043036a2SApple OSS Distributions 		memcpy(*data, entry->data, enHeaderInfo.fields.dataSize);
1376*043036a2SApple OSS Distributions 		// Lets re-verify after the memcpy if the buffer is/has been overwritten.
1377*043036a2SApple OSS Distributions 
1378*043036a2SApple OSS Distributions 		IOCircularDataQueueEntryHeaderInfo enHeaderInfoAfter;
1379*043036a2SApple OSS Distributions 		enHeaderInfoAfter.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
1380*043036a2SApple OSS Distributions 		// Did something change, while we were memcopying ?
1381*043036a2SApple OSS Distributions 		if (enHeaderInfo.val != enHeaderInfoAfter.val) {
1382*043036a2SApple OSS Distributions 			// while we were memcopying, the writer wrapped around and is writing into our index. or the queue got reset
1383*043036a2SApple OSS Distributions 			*size = 0;
1384*043036a2SApple OSS Distributions 			ret = kIOReturnOverrun;
1385*043036a2SApple OSS Distributions 		}
1386*043036a2SApple OSS Distributions 	}
1387*043036a2SApple OSS Distributions 
1388*043036a2SApple OSS Distributions 	if ((kIOReturnSuccess == ret) && os_unlikely(_isQueueMemoryCorrupted(queue))) {
1389*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1390*043036a2SApple OSS Distributions 	}
1391*043036a2SApple OSS Distributions 
1392*043036a2SApple OSS Distributions 	return ret;
1393*043036a2SApple OSS Distributions }
1394*043036a2SApple OSS Distributions 
1395*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
getPrevInQueueMem(IOCircularDataQueue * queue,void ** data,size_t * size)1396*043036a2SApple OSS Distributions getPrevInQueueMem(IOCircularDataQueue *queue,
1397*043036a2SApple OSS Distributions     void **data,
1398*043036a2SApple OSS Distributions     size_t *size)
1399*043036a2SApple OSS Distributions {
1400*043036a2SApple OSS Distributions 	return _getPrevInQueueMemInternal(queue, data, size, false);
1401*043036a2SApple OSS Distributions }
1402*043036a2SApple OSS Distributions 
1403*043036a2SApple OSS Distributions /*!
1404*043036a2SApple OSS Distributions  * @function copyPrevInQueueMem
1405*043036a2SApple OSS Distributions  * Access the data at the previous cursor position and copy into the provided buffer. Also update the cursor position to
1406*043036a2SApple OSS Distributions  * the previous. If successful, function gaurantees that the data returned is always valid, hence no need to call
1407*043036a2SApple OSS Distributions  * isDataEntryValidInQueueMem().
1408*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1409*043036a2SApple OSS Distributions  * @param data Pointer to memory into which the previous data is copied. Lifetime of this memory is controlled by the
1410*043036a2SApple OSS Distributions  * caller.
1411*043036a2SApple OSS Distributions  * @param size Size of the data buffer provided for copying. On return, this contains the actual size of the data
1412*043036a2SApple OSS Distributions  * pointed to by data param.
1413*043036a2SApple OSS Distributions  *  @return
1414*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated.
1415*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid, possibly due to a reset of the queue.
1416*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer in
1417*043036a2SApple OSS Distributions  *     the queue's buffer. Call getLatestInQueueMem to get the latest data and cursor position.
1418*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
1419*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1420*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1421*043036a2SApple OSS Distributions  *
1422*043036a2SApple OSS Distributions  */
1423*043036a2SApple OSS Distributions 
1424*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
copyPrevInQueueMem(IOCircularDataQueue * queue,void * data,size_t * size)1425*043036a2SApple OSS Distributions copyPrevInQueueMem(IOCircularDataQueue *queue,
1426*043036a2SApple OSS Distributions     void *data,
1427*043036a2SApple OSS Distributions     size_t *size)
1428*043036a2SApple OSS Distributions {
1429*043036a2SApple OSS Distributions 	return _getPrevInQueueMemInternal(queue, &data, size, true);
1430*043036a2SApple OSS Distributions }
1431*043036a2SApple OSS Distributions 
1432*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
_getCurrentInQueueMemInternal(IOCircularDataQueue * queue,void ** data,size_t * size,bool copyMem)1433*043036a2SApple OSS Distributions _getCurrentInQueueMemInternal(IOCircularDataQueue *queue,
1434*043036a2SApple OSS Distributions     void **data,
1435*043036a2SApple OSS Distributions     size_t *size,
1436*043036a2SApple OSS Distributions     bool copyMem)
1437*043036a2SApple OSS Distributions {
1438*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory = queue->queueMemory;
1439*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
1440*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor const *cursor = &queue->queueCursor;
1441*043036a2SApple OSS Distributions 
1442*043036a2SApple OSS Distributions 	size_t inSize;
1443*043036a2SApple OSS Distributions 
1444*043036a2SApple OSS Distributions 	if (queueMemory == NULL || data == NULL || size == NULL || queueHeaderShadow == NULL) {
1445*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1446*043036a2SApple OSS Distributions 	}
1447*043036a2SApple OSS Distributions 
1448*043036a2SApple OSS Distributions 	inSize = *size;
1449*043036a2SApple OSS Distributions 	*size = 0;
1450*043036a2SApple OSS Distributions 
1451*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
1452*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1453*043036a2SApple OSS Distributions 	}
1454*043036a2SApple OSS Distributions 
1455*043036a2SApple OSS Distributions 	if (os_unlikely(_isCursorPositionInvalid(queue))) {
1456*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1457*043036a2SApple OSS Distributions 	}
1458*043036a2SApple OSS Distributions 
1459*043036a2SApple OSS Distributions 	const size_t queueAllocMemSize = queueHeaderShadow->allocMemSize;
1460*043036a2SApple OSS Distributions 	const size_t queueEntryDataSize = queueHeaderShadow->entryDataSize;
1461*043036a2SApple OSS Distributions 
1462*043036a2SApple OSS Distributions 	IOCircularDataQueueState currState;
1463*043036a2SApple OSS Distributions 	currState.val = atomic_load_explicit(&queueMemory->queueStateVal, memory_order_acquire);
1464*043036a2SApple OSS Distributions 
1465*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.rstStatus & IOCIRCULARDATAQUEUE_STATE_RESET_INPROGRESS)) {
1466*043036a2SApple OSS Distributions 		// Another thread is resetting the queue
1467*043036a2SApple OSS Distributions 		return kIOReturnBusy;
1468*043036a2SApple OSS Distributions 	}
1469*043036a2SApple OSS Distributions 
1470*043036a2SApple OSS Distributions 	uint32_t queueGeneration = currState.fields.generation;
1471*043036a2SApple OSS Distributions 
1472*043036a2SApple OSS Distributions 	// was the queue reset ?
1473*043036a2SApple OSS Distributions 	if (os_unlikely(cursor->generation != queueGeneration || cursor->sequenceNum > currState.fields.seqNum)) {
1474*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1475*043036a2SApple OSS Distributions 	}
1476*043036a2SApple OSS Distributions 
1477*043036a2SApple OSS Distributions 	if (os_unlikely(currState.fields.seqNum == UINT64_MAX)) {
1478*043036a2SApple OSS Distributions 		// Nothing has ever been written to the queue yet.
1479*043036a2SApple OSS Distributions 		return kIOReturnUnderrun;
1480*043036a2SApple OSS Distributions 	}
1481*043036a2SApple OSS Distributions 
1482*043036a2SApple OSS Distributions 	__auto_type entry = (IOCircularDataQueueEntryHeader *)(uintptr_t)((uint8_t *)&queueMemory->entries[0]
1483*043036a2SApple OSS Distributions 	    + (cursor->position * queueEntryDataSize));
1484*043036a2SApple OSS Distributions 
1485*043036a2SApple OSS Distributions 	// SANITY CHECK - Final check to ensure the 'entry' pointer is
1486*043036a2SApple OSS Distributions 	// within the queueMemory entries buffer before we begin writing.
1487*043036a2SApple OSS Distributions 	if (os_unlikely((uint8_t *)entry < (uint8_t *)(&queueMemory->entries[0])
1488*043036a2SApple OSS Distributions 	    || (uint8_t *)entry >= (uint8_t *)queueMemory + queueAllocMemSize)) {
1489*043036a2SApple OSS Distributions 		queue_debug_error("Out of Bounds! " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT, QUEUE_ARGS(queueMemory),
1490*043036a2SApple OSS Distributions 		    CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1491*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1492*043036a2SApple OSS Distributions 	}
1493*043036a2SApple OSS Distributions 
1494*043036a2SApple OSS Distributions 	os_compiler_barrier();
1495*043036a2SApple OSS Distributions 
1496*043036a2SApple OSS Distributions 	if (os_unlikely(entry->sentinel != queueHeaderShadow->sentinel)) {
1497*043036a2SApple OSS Distributions 		queue_debug_error("entry->sentinel != queueMemory->sentinel " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
1498*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1499*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1500*043036a2SApple OSS Distributions 	}
1501*043036a2SApple OSS Distributions 
1502*043036a2SApple OSS Distributions 	IOCircularDataQueueEntryHeaderInfo enHeaderInfo;
1503*043036a2SApple OSS Distributions 	enHeaderInfo.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
1504*043036a2SApple OSS Distributions 	uint32_t entryGeneration = enHeaderInfo.fields.generation;
1505*043036a2SApple OSS Distributions 	if (os_unlikely(entryGeneration != queueGeneration)) {
1506*043036a2SApple OSS Distributions 		queue_debug_note("entryGeneration != queueGeneration " QUEUE_FORMAT " " CURSOR_FORMAT " " ENTRY_FORMAT,
1507*043036a2SApple OSS Distributions 		    QUEUE_ARGS(queueMemory), CURSOR_ARGS(cursor), ENTRY_ARGS(entry));
1508*043036a2SApple OSS Distributions 		return kIOReturnAborted;
1509*043036a2SApple OSS Distributions 	}
1510*043036a2SApple OSS Distributions 
1511*043036a2SApple OSS Distributions 	// is the entry currently being written to or has the cursor fallen too far behind and the cursor is no longer
1512*043036a2SApple OSS Distributions 	// valid.
1513*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.wrStatus == IOCIRCULARDATAQUEUE_ENTRY_STATE_WRITE_INPROGRESS
1514*043036a2SApple OSS Distributions 	    || enHeaderInfo.fields.seqNum != cursor->sequenceNum)) {
1515*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1516*043036a2SApple OSS Distributions 	}
1517*043036a2SApple OSS Distributions 
1518*043036a2SApple OSS Distributions 	if (os_unlikely(enHeaderInfo.fields.dataSize > queueHeaderShadow->entryDataSize)) {
1519*043036a2SApple OSS Distributions 		return kIOReturnOverrun;
1520*043036a2SApple OSS Distributions 	}
1521*043036a2SApple OSS Distributions 	*size = enHeaderInfo.fields.dataSize;
1522*043036a2SApple OSS Distributions 
1523*043036a2SApple OSS Distributions 	if (!copyMem) {
1524*043036a2SApple OSS Distributions 		*data = entry->data;
1525*043036a2SApple OSS Distributions 	} else {
1526*043036a2SApple OSS Distributions 		if (os_unlikely(enHeaderInfo.fields.dataSize > inSize)) {
1527*043036a2SApple OSS Distributions 			return kIOReturnOverrun;
1528*043036a2SApple OSS Distributions 		}
1529*043036a2SApple OSS Distributions 		memcpy(*data, entry->data, enHeaderInfo.fields.dataSize);
1530*043036a2SApple OSS Distributions 		// Lets re-verify after the memcpy if the buffer is/has been overwritten.
1531*043036a2SApple OSS Distributions 
1532*043036a2SApple OSS Distributions 		IOCircularDataQueueEntryHeaderInfo enHeaderInfoAfter;
1533*043036a2SApple OSS Distributions 		enHeaderInfoAfter.val = atomic_load_explicit(&entry->headerInfoVal, memory_order_acquire);
1534*043036a2SApple OSS Distributions 		// Did something change, while we were memcopying ?
1535*043036a2SApple OSS Distributions 		if (enHeaderInfo.val != enHeaderInfoAfter.val) {
1536*043036a2SApple OSS Distributions 			// while we were memcopying, the writer wrapped around and is writing into our index. or the queue got reset
1537*043036a2SApple OSS Distributions 			*size = 0;
1538*043036a2SApple OSS Distributions 			return kIOReturnBusy;
1539*043036a2SApple OSS Distributions 		}
1540*043036a2SApple OSS Distributions 	}
1541*043036a2SApple OSS Distributions 
1542*043036a2SApple OSS Distributions 	if (os_unlikely(_isQueueMemoryCorrupted(queue))) {
1543*043036a2SApple OSS Distributions 		return kIOReturnBadMedia;
1544*043036a2SApple OSS Distributions 	}
1545*043036a2SApple OSS Distributions 
1546*043036a2SApple OSS Distributions 	return kIOReturnSuccess;
1547*043036a2SApple OSS Distributions }
1548*043036a2SApple OSS Distributions 
1549*043036a2SApple OSS Distributions /*!
1550*043036a2SApple OSS Distributions  * @function getCurrentInQueueMem
1551*043036a2SApple OSS Distributions  * Access the data at the current cursor position. The cursor position is unchanged. No copy is made of the data. <br>
1552*043036a2SApple OSS Distributions  * Caller is supposed to call isDataEntryValidInQueueMem() to check data integrity after reading the data is complete.
1553*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1554*043036a2SApple OSS Distributions  * @param data A pointer to the data memory region for the previous entry data in the queue.
1555*043036a2SApple OSS Distributions  * @param size A pointer to the size of the data parameter.  On return, this contains the actual size of the data
1556*043036a2SApple OSS Distributions  * pointed to by data param.
1557*043036a2SApple OSS Distributions  *  @return
1558*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated to the previous.
1559*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid, possibly due to a reset of the queue.
1560*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer in
1561*043036a2SApple OSS Distributions  *     the queue's buffer. Call getLatestInQueueMem to get the latest data and cursor position.
1562*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
1563*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1564*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1565*043036a2SApple OSS Distributions  *
1566*043036a2SApple OSS Distributions  */
1567*043036a2SApple OSS Distributions 
1568*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
getCurrentInQueueMem(IOCircularDataQueue * queue,void ** data,size_t * size)1569*043036a2SApple OSS Distributions getCurrentInQueueMem(IOCircularDataQueue *queue,
1570*043036a2SApple OSS Distributions     void **data,
1571*043036a2SApple OSS Distributions     size_t *size)
1572*043036a2SApple OSS Distributions {
1573*043036a2SApple OSS Distributions 	return _getCurrentInQueueMemInternal(queue, data, size, false);
1574*043036a2SApple OSS Distributions }
1575*043036a2SApple OSS Distributions 
1576*043036a2SApple OSS Distributions /*!
1577*043036a2SApple OSS Distributions  * @function copyCurrentInQueueMem
1578*043036a2SApple OSS Distributions  * Access the data at the current cursor position and copy into the provided buffer. The cursor position is unchanged.
1579*043036a2SApple OSS Distributions  * If successful, function gaurantees that the data returned is always valid, hence no need to call
1580*043036a2SApple OSS Distributions  * isDataEntryValidInQueueMem().
1581*043036a2SApple OSS Distributions  * @param queue Handle to the queue.
1582*043036a2SApple OSS Distributions  * @param data Pointer to memory into which the previous data is copied. Lifetime of this memory is controlled by the
1583*043036a2SApple OSS Distributions  * caller.
1584*043036a2SApple OSS Distributions  * @param size Size of the data buffer provided for copying. On return, this contains the actual size of the data
1585*043036a2SApple OSS Distributions  * pointed to by data param.
1586*043036a2SApple OSS Distributions  *  @return
1587*043036a2SApple OSS Distributions  *  - `kIOReturnSuccess` if the cursor position was updated.
1588*043036a2SApple OSS Distributions  *  - `kIOReturnAborted` if the cursor has become invalid.
1589*043036a2SApple OSS Distributions  *  - `kIOReturnOverrun` if the entry at the cursor position is no longer in
1590*043036a2SApple OSS Distributions  *     the queue's buffer. Call getLatestInQueueMem to get the latest data and cursor position.
1591*043036a2SApple OSS Distributions  *  - `kIOReturnBadArgument` if an invalid argument is passsed.
1592*043036a2SApple OSS Distributions  *  - `kIOReturnBadMedia` if the queue shared memory has been compromised.
1593*043036a2SApple OSS Distributions  *  - Other values indicate an error.
1594*043036a2SApple OSS Distributions  *
1595*043036a2SApple OSS Distributions  */
1596*043036a2SApple OSS Distributions 
1597*043036a2SApple OSS Distributions static IOReturn ATTR_LSE2
copyCurrentInQueueMem(IOCircularDataQueue * queue,void * data,size_t * size)1598*043036a2SApple OSS Distributions copyCurrentInQueueMem(IOCircularDataQueue *queue,
1599*043036a2SApple OSS Distributions     void *data,
1600*043036a2SApple OSS Distributions     size_t *size)
1601*043036a2SApple OSS Distributions {
1602*043036a2SApple OSS Distributions 	return _getCurrentInQueueMemInternal(queue, &data, size, true);
1603*043036a2SApple OSS Distributions }
1604*043036a2SApple OSS Distributions 
1605*043036a2SApple OSS Distributions 
1606*043036a2SApple OSS Distributions /* API */
1607*043036a2SApple OSS Distributions 
1608*043036a2SApple OSS Distributions static void ATTR_LSE2
_initCursor(IOCircularDataQueue * queue)1609*043036a2SApple OSS Distributions _initCursor(IOCircularDataQueue *queue)
1610*043036a2SApple OSS Distributions {
1611*043036a2SApple OSS Distributions 	// Invalidate the cursor
1612*043036a2SApple OSS Distributions 	IOCircularDataQueueMemoryCursor *cursor = &queue->queueCursor;
1613*043036a2SApple OSS Distributions 	cursor->generation = UINT32_MAX;
1614*043036a2SApple OSS Distributions 	cursor->position = UINT32_MAX;
1615*043036a2SApple OSS Distributions 	cursor->sequenceNum = UINT64_MAX;
1616*043036a2SApple OSS Distributions }
1617*043036a2SApple OSS Distributions 
1618*043036a2SApple OSS Distributions #if KERNEL
1619*043036a2SApple OSS Distributions 
1620*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueCreateWithEntries(IOCircularDataQueueCreateOptions options,uint32_t numEntries,uint32_t entrySize,IOCircularDataQueue ** pQueue)1621*043036a2SApple OSS Distributions IOCircularDataQueueCreateWithEntries(IOCircularDataQueueCreateOptions options, uint32_t numEntries, uint32_t entrySize, IOCircularDataQueue **pQueue)
1622*043036a2SApple OSS Distributions {
1623*043036a2SApple OSS Distributions 	IOCircularDataQueueMemory *queueMemory;
1624*043036a2SApple OSS Distributions 	IOReturn ret;
1625*043036a2SApple OSS Distributions 
1626*043036a2SApple OSS Distributions 	if (!pQueue) {
1627*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1628*043036a2SApple OSS Distributions 	}
1629*043036a2SApple OSS Distributions 	*pQueue = NULL;
1630*043036a2SApple OSS Distributions 	if (!numEntries || !entrySize) {
1631*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1632*043036a2SApple OSS Distributions 	}
1633*043036a2SApple OSS Distributions 
1634*043036a2SApple OSS Distributions 	uint64_t sentinel = 0xA5A5A5A5A5A5A5A5;
1635*043036a2SApple OSS Distributions 
1636*043036a2SApple OSS Distributions #if HEADER_16BYTE_ALIGNED
1637*043036a2SApple OSS Distributions 	size_t entryRoundedDataSize = IORound(entrySize, sizeof(__uint128_t));
1638*043036a2SApple OSS Distributions #else
1639*043036a2SApple OSS Distributions 	size_t entryRoundedDataSize = IORound(entrySize, sizeof(UInt64));
1640*043036a2SApple OSS Distributions #endif
1641*043036a2SApple OSS Distributions 	size_t entryDataSize = entryRoundedDataSize + CIRCULAR_DATA_QUEUE_ENTRY_HEADER_SIZE;
1642*043036a2SApple OSS Distributions 	size_t entriesSize = numEntries * (entryDataSize);
1643*043036a2SApple OSS Distributions 	size_t totalSize = entriesSize + CIRCULAR_DATA_QUEUE_MEMORY_HEADER_SIZE;
1644*043036a2SApple OSS Distributions 
1645*043036a2SApple OSS Distributions 	if (os_unlikely(numEntries > UINT32_MAX - 1
1646*043036a2SApple OSS Distributions 	    || entryRoundedDataSize > (UINT32_MAX - sizeof(IOCircularDataQueueEntryHeader))
1647*043036a2SApple OSS Distributions 	    || entryDataSize > UINT32_MAX || totalSize > UINT32_MAX)) {
1648*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1649*043036a2SApple OSS Distributions 	}
1650*043036a2SApple OSS Distributions 
1651*043036a2SApple OSS Distributions 	IOCircularDataQueue *queue = IONew(IOCircularDataQueue, 1);
1652*043036a2SApple OSS Distributions 	if (!queue) {
1653*043036a2SApple OSS Distributions 		return kIOReturnNoMemory;
1654*043036a2SApple OSS Distributions 	}
1655*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
1656*043036a2SApple OSS Distributions 
1657*043036a2SApple OSS Distributions 	OSData * desc;
1658*043036a2SApple OSS Distributions 	queue->iomd = IOBufferMemoryDescriptor::inTaskWithOptions(
1659*043036a2SApple OSS Distributions 		kernel_task, kIOMemoryDirectionOutIn | kIOMemoryKernelUserShared, totalSize, page_size);
1660*043036a2SApple OSS Distributions 	if (os_unlikely(queue->iomd == NULL)) {
1661*043036a2SApple OSS Distributions 		ret = kIOReturnNoMemory;
1662*043036a2SApple OSS Distributions 		goto error;
1663*043036a2SApple OSS Distributions 	}
1664*043036a2SApple OSS Distributions 	queueMemory = (IOCircularDataQueueMemory *)queue->iomd->getBytesNoCopy();
1665*043036a2SApple OSS Distributions 	queue->queueMemory = queueMemory;
1666*043036a2SApple OSS Distributions 	queueMemory->sentinel = queueHeaderShadow->sentinel = sentinel;
1667*043036a2SApple OSS Distributions 
1668*043036a2SApple OSS Distributions 	queueHeaderShadow->allocMemSize = (uint32_t)totalSize;
1669*043036a2SApple OSS Distributions 	queueHeaderShadow->entryDataSize
1670*043036a2SApple OSS Distributions 	        = (uint32_t)entryDataSize; // totalSize check above gaurantess this will not overflow UINT32_MAX.
1671*043036a2SApple OSS Distributions 	queueHeaderShadow->numEntries = numEntries;
1672*043036a2SApple OSS Distributions 	queueHeaderShadow->dataSize = entrySize; // the client requested fixed entry size.
1673*043036a2SApple OSS Distributions 	queueHeaderShadow->memorySize = (uint32_t)entriesSize;
1674*043036a2SApple OSS Distributions 
1675*043036a2SApple OSS Distributions 	desc = OSData::withBytes(queueHeaderShadow, sizeof(*queueHeaderShadow));
1676*043036a2SApple OSS Distributions 	queue->iomd->setSharingContext(kIOCircularQueueDescriptionKey, desc);
1677*043036a2SApple OSS Distributions 
1678*043036a2SApple OSS Distributions 	IOCircularDataQueueState newState;
1679*043036a2SApple OSS Distributions 	newState.val = 0;
1680*043036a2SApple OSS Distributions 	newState.fields.seqNum = UINT64_MAX;
1681*043036a2SApple OSS Distributions 	atomic_store_explicit(&queueMemory->queueStateVal, newState.val, memory_order_release);
1682*043036a2SApple OSS Distributions 
1683*043036a2SApple OSS Distributions 	ret = _reset(queue);
1684*043036a2SApple OSS Distributions 	if (ret != kIOReturnSuccess) {
1685*043036a2SApple OSS Distributions 		goto error;
1686*043036a2SApple OSS Distributions 	}
1687*043036a2SApple OSS Distributions 
1688*043036a2SApple OSS Distributions 	_initCursor(queue);
1689*043036a2SApple OSS Distributions 	*pQueue = queue;
1690*043036a2SApple OSS Distributions 	return kIOReturnSuccess;
1691*043036a2SApple OSS Distributions 
1692*043036a2SApple OSS Distributions 
1693*043036a2SApple OSS Distributions error:
1694*043036a2SApple OSS Distributions 	IOCircularDataQueueDestroy(&queue);
1695*043036a2SApple OSS Distributions 	return ret;
1696*043036a2SApple OSS Distributions }
1697*043036a2SApple OSS Distributions 
1698*043036a2SApple OSS Distributions IOMemoryDescriptor * ATTR_LSE2
IOCircularDataQueueCopyMemoryDescriptor(IOCircularDataQueue * queue)1699*043036a2SApple OSS Distributions IOCircularDataQueueCopyMemoryDescriptor(IOCircularDataQueue  *queue)
1700*043036a2SApple OSS Distributions {
1701*043036a2SApple OSS Distributions 	IOMemoryDescriptor * md;
1702*043036a2SApple OSS Distributions 	md = queue->iomd;
1703*043036a2SApple OSS Distributions 	if (md) {
1704*043036a2SApple OSS Distributions 		md->retain();
1705*043036a2SApple OSS Distributions 	}
1706*043036a2SApple OSS Distributions 	return md;
1707*043036a2SApple OSS Distributions }
1708*043036a2SApple OSS Distributions 
1709*043036a2SApple OSS Distributions #else /* KERNEL */
1710*043036a2SApple OSS Distributions 
1711*043036a2SApple OSS Distributions #if defined(__arm64__) && defined(__LP64__)
1712*043036a2SApple OSS Distributions #include <System/arm/cpu_capabilities.h>
1713*043036a2SApple OSS Distributions #endif /* defined(__arm64__) */
1714*043036a2SApple OSS Distributions 
1715*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueCreateWithConnection(IOCircularDataQueueCreateOptions options,io_connect_t connect,uint32_t memoryType,IOCircularDataQueue ** pQueue)1716*043036a2SApple OSS Distributions IOCircularDataQueueCreateWithConnection(IOCircularDataQueueCreateOptions options, io_connect_t connect, uint32_t memoryType, IOCircularDataQueue **pQueue)
1717*043036a2SApple OSS Distributions {
1718*043036a2SApple OSS Distributions 	if (!pQueue) {
1719*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1720*043036a2SApple OSS Distributions 	}
1721*043036a2SApple OSS Distributions 	*pQueue = NULL;
1722*043036a2SApple OSS Distributions 
1723*043036a2SApple OSS Distributions #if defined(__arm64__) && defined(__LP64__)
1724*043036a2SApple OSS Distributions 	if (0 == (kHasFeatLSE2 & _get_cpu_capabilities())) {
1725*043036a2SApple OSS Distributions 		return kIOReturnUnsupported;
1726*043036a2SApple OSS Distributions 	}
1727*043036a2SApple OSS Distributions #else
1728*043036a2SApple OSS Distributions 	return kIOReturnUnsupported;
1729*043036a2SApple OSS Distributions #endif /* defined(__arm64__) */
1730*043036a2SApple OSS Distributions 
1731*043036a2SApple OSS Distributions 	uint64_t sentinel = 0xA5A5A5A5A5A5A5A5;
1732*043036a2SApple OSS Distributions 
1733*043036a2SApple OSS Distributions 	IOCircularDataQueue *queue = IONew(IOCircularDataQueue, 1);
1734*043036a2SApple OSS Distributions 	if (!queue) {
1735*043036a2SApple OSS Distributions 		return kIOReturnNoMemory;
1736*043036a2SApple OSS Distributions 	}
1737*043036a2SApple OSS Distributions 	IOCircularDataQueueDescription *queueHeaderShadow = &queue->queueHeaderShadow;
1738*043036a2SApple OSS Distributions 
1739*043036a2SApple OSS Distributions 	queue->connect = connect;
1740*043036a2SApple OSS Distributions 	queue->memoryType = memoryType;
1741*043036a2SApple OSS Distributions 
1742*043036a2SApple OSS Distributions 	io_struct_inband_t inband_output;
1743*043036a2SApple OSS Distributions 	mach_msg_type_number_t inband_outputCnt;
1744*043036a2SApple OSS Distributions 	mach_vm_address_t map_address;
1745*043036a2SApple OSS Distributions 	mach_vm_size_t map_size;
1746*043036a2SApple OSS Distributions 	IOReturn ret;
1747*043036a2SApple OSS Distributions 
1748*043036a2SApple OSS Distributions 	inband_outputCnt = sizeof(inband_output);
1749*043036a2SApple OSS Distributions 
1750*043036a2SApple OSS Distributions 	ret = io_connect_map_shared_memory(connect, memoryType, mach_task_self(),
1751*043036a2SApple OSS Distributions 	    &map_address, &map_size,
1752*043036a2SApple OSS Distributions 	    /* flags */ 0,
1753*043036a2SApple OSS Distributions 	    (char *) kIOCircularQueueDescriptionKey,
1754*043036a2SApple OSS Distributions 	    inband_output,
1755*043036a2SApple OSS Distributions 	    &inband_outputCnt);
1756*043036a2SApple OSS Distributions 
1757*043036a2SApple OSS Distributions 	printf("%x, %lx, 0x%llx, 0x%llx\n", inband_outputCnt, sizeof(IOCircularDataQueueDescription), map_address, map_size);
1758*043036a2SApple OSS Distributions 
1759*043036a2SApple OSS Distributions 	assert(sizeof(IOCircularDataQueueDescription) == inband_outputCnt);
1760*043036a2SApple OSS Distributions 	memcpy(queueHeaderShadow, inband_output, sizeof(IOCircularDataQueueDescription));
1761*043036a2SApple OSS Distributions 	printf("sentinel %qx\n", queueHeaderShadow->sentinel);
1762*043036a2SApple OSS Distributions 	assert(queueHeaderShadow->allocMemSize == map_size);
1763*043036a2SApple OSS Distributions 	queue->queueMemory = (IOCircularDataQueueMemory *) map_address;
1764*043036a2SApple OSS Distributions 
1765*043036a2SApple OSS Distributions 	if (!isQueueMemoryValid(queue)) {
1766*043036a2SApple OSS Distributions 		IOCircularDataQueueDestroy(&queue);
1767*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1768*043036a2SApple OSS Distributions 	}
1769*043036a2SApple OSS Distributions 
1770*043036a2SApple OSS Distributions 	_initCursor(queue);
1771*043036a2SApple OSS Distributions 	*pQueue = queue;
1772*043036a2SApple OSS Distributions 
1773*043036a2SApple OSS Distributions 	return ret;
1774*043036a2SApple OSS Distributions }
1775*043036a2SApple OSS Distributions 
1776*043036a2SApple OSS Distributions #endif /* !KERNEL */
1777*043036a2SApple OSS Distributions 
1778*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueDestroy(IOCircularDataQueue ** pQueue)1779*043036a2SApple OSS Distributions IOCircularDataQueueDestroy(IOCircularDataQueue **pQueue)
1780*043036a2SApple OSS Distributions {
1781*043036a2SApple OSS Distributions 	IOCircularDataQueue * queue;
1782*043036a2SApple OSS Distributions 	IOReturn ret = kIOReturnSuccess;
1783*043036a2SApple OSS Distributions 
1784*043036a2SApple OSS Distributions 	if (!pQueue) {
1785*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1786*043036a2SApple OSS Distributions 	}
1787*043036a2SApple OSS Distributions 	queue = *pQueue;
1788*043036a2SApple OSS Distributions 	if (queue) {
1789*043036a2SApple OSS Distributions 		ret = destroyQueueMem(queue);
1790*043036a2SApple OSS Distributions 		IODelete(queue, IOCircularDataQueue, 1);
1791*043036a2SApple OSS Distributions 		*pQueue = NULL;
1792*043036a2SApple OSS Distributions 	}
1793*043036a2SApple OSS Distributions 	return ret;
1794*043036a2SApple OSS Distributions }
1795*043036a2SApple OSS Distributions 
1796*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueEnqueue(IOCircularDataQueue * queue,const void * data,size_t dataSize)1797*043036a2SApple OSS Distributions IOCircularDataQueueEnqueue(IOCircularDataQueue *queue, const void *data, size_t dataSize)
1798*043036a2SApple OSS Distributions {
1799*043036a2SApple OSS Distributions 	if (!queue) {
1800*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1801*043036a2SApple OSS Distributions 	}
1802*043036a2SApple OSS Distributions 
1803*043036a2SApple OSS Distributions 	return enqueueQueueMem(queue, data, dataSize);
1804*043036a2SApple OSS Distributions }
1805*043036a2SApple OSS Distributions 
1806*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueGetLatest(IOCircularDataQueue * queue,void ** data,size_t * size)1807*043036a2SApple OSS Distributions IOCircularDataQueueGetLatest(IOCircularDataQueue *queue, void **data, size_t *size)
1808*043036a2SApple OSS Distributions {
1809*043036a2SApple OSS Distributions 	if (!queue) {
1810*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1811*043036a2SApple OSS Distributions 	}
1812*043036a2SApple OSS Distributions 
1813*043036a2SApple OSS Distributions 	return getLatestInQueueMem(queue, data, size);
1814*043036a2SApple OSS Distributions }
1815*043036a2SApple OSS Distributions 
1816*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueCopyLatest(IOCircularDataQueue * queue,void * data,size_t * size)1817*043036a2SApple OSS Distributions IOCircularDataQueueCopyLatest(IOCircularDataQueue *queue, void *data, size_t *size)
1818*043036a2SApple OSS Distributions {
1819*043036a2SApple OSS Distributions 	if (!queue) {
1820*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1821*043036a2SApple OSS Distributions 	}
1822*043036a2SApple OSS Distributions 
1823*043036a2SApple OSS Distributions 	return copyLatestInQueueMem(queue, data, size);
1824*043036a2SApple OSS Distributions }
1825*043036a2SApple OSS Distributions 
1826*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueGetNext(IOCircularDataQueue * queue,void ** data,size_t * size)1827*043036a2SApple OSS Distributions IOCircularDataQueueGetNext(IOCircularDataQueue *queue, void **data, size_t *size)
1828*043036a2SApple OSS Distributions {
1829*043036a2SApple OSS Distributions 	if (!queue) {
1830*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1831*043036a2SApple OSS Distributions 	}
1832*043036a2SApple OSS Distributions 
1833*043036a2SApple OSS Distributions 	return getNextInQueueMem(queue, data, size);
1834*043036a2SApple OSS Distributions }
1835*043036a2SApple OSS Distributions 
1836*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueCopyNext(IOCircularDataQueue * queue,void * data,size_t * size)1837*043036a2SApple OSS Distributions IOCircularDataQueueCopyNext(IOCircularDataQueue *queue, void *data, size_t *size)
1838*043036a2SApple OSS Distributions {
1839*043036a2SApple OSS Distributions 	if (!queue) {
1840*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1841*043036a2SApple OSS Distributions 	}
1842*043036a2SApple OSS Distributions 
1843*043036a2SApple OSS Distributions 	return copyNextInQueueMem(queue, data, size);
1844*043036a2SApple OSS Distributions }
1845*043036a2SApple OSS Distributions 
1846*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueGetPrevious(IOCircularDataQueue * queue,void ** data,size_t * size)1847*043036a2SApple OSS Distributions IOCircularDataQueueGetPrevious(IOCircularDataQueue *queue, void **data, size_t *size)
1848*043036a2SApple OSS Distributions {
1849*043036a2SApple OSS Distributions 	if (!queue) {
1850*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1851*043036a2SApple OSS Distributions 	}
1852*043036a2SApple OSS Distributions 
1853*043036a2SApple OSS Distributions 	return getPrevInQueueMem(queue, data, size);
1854*043036a2SApple OSS Distributions }
1855*043036a2SApple OSS Distributions 
1856*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueCopyPrevious(IOCircularDataQueue * queue,void * data,size_t * size)1857*043036a2SApple OSS Distributions IOCircularDataQueueCopyPrevious(IOCircularDataQueue *queue, void *data, size_t *size)
1858*043036a2SApple OSS Distributions {
1859*043036a2SApple OSS Distributions 	if (!queue) {
1860*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1861*043036a2SApple OSS Distributions 	}
1862*043036a2SApple OSS Distributions 
1863*043036a2SApple OSS Distributions 	return copyPrevInQueueMem(queue, data, size);
1864*043036a2SApple OSS Distributions }
1865*043036a2SApple OSS Distributions 
1866*043036a2SApple OSS Distributions // IOReturn
1867*043036a2SApple OSS Distributions //IOCircularDataQueueGetLatestWithBlock(IOCircularDataQueue *queue, void (^handler)(void * data, size_t size))
1868*043036a2SApple OSS Distributions //{
1869*043036a2SApple OSS Distributions //     if (!queue) {
1870*043036a2SApple OSS Distributions //         return kIOReturnBadArgument;
1871*043036a2SApple OSS Distributions //     }
1872*043036a2SApple OSS Distributions //
1873*043036a2SApple OSS Distributions ////    return getPrevInQueueMem(queue->queueMemory, (IOCircularDataQueueDescription *)
1874*043036a2SApple OSS Distributions ///&queue->queueHeaderShadow, (IOCircularDataQueueMemoryCursor *) &queue->queueCursor, data, size);
1875*043036a2SApple OSS Distributions //}
1876*043036a2SApple OSS Distributions //
1877*043036a2SApple OSS Distributions 
1878*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueIsCurrentDataValid(IOCircularDataQueue * queue)1879*043036a2SApple OSS Distributions IOCircularDataQueueIsCurrentDataValid(IOCircularDataQueue *queue)
1880*043036a2SApple OSS Distributions {
1881*043036a2SApple OSS Distributions 	if (!queue) {
1882*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1883*043036a2SApple OSS Distributions 	}
1884*043036a2SApple OSS Distributions 
1885*043036a2SApple OSS Distributions 	return isDataEntryValidInQueueMem(queue);
1886*043036a2SApple OSS Distributions }
1887*043036a2SApple OSS Distributions 
1888*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueSetCursorLatest(IOCircularDataQueue * queue)1889*043036a2SApple OSS Distributions IOCircularDataQueueSetCursorLatest(IOCircularDataQueue *queue)
1890*043036a2SApple OSS Distributions {
1891*043036a2SApple OSS Distributions 	if (!queue) {
1892*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1893*043036a2SApple OSS Distributions 	}
1894*043036a2SApple OSS Distributions 
1895*043036a2SApple OSS Distributions 	return setCursorLatestInQueueMem(queue);
1896*043036a2SApple OSS Distributions }
1897*043036a2SApple OSS Distributions 
1898*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueGetCurrent(IOCircularDataQueue * queue,void ** data,size_t * size)1899*043036a2SApple OSS Distributions IOCircularDataQueueGetCurrent(IOCircularDataQueue *queue, void **data, size_t *size)
1900*043036a2SApple OSS Distributions {
1901*043036a2SApple OSS Distributions 	if (!queue) {
1902*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1903*043036a2SApple OSS Distributions 	}
1904*043036a2SApple OSS Distributions 
1905*043036a2SApple OSS Distributions 	return getCurrentInQueueMem(queue, data, size);
1906*043036a2SApple OSS Distributions }
1907*043036a2SApple OSS Distributions 
1908*043036a2SApple OSS Distributions IOReturn ATTR_LSE2
IOCircularDataQueueCopyCurrent(IOCircularDataQueue * queue,void * data,size_t * size)1909*043036a2SApple OSS Distributions IOCircularDataQueueCopyCurrent(IOCircularDataQueue *queue, void *data, size_t *size)
1910*043036a2SApple OSS Distributions {
1911*043036a2SApple OSS Distributions 	if (!queue) {
1912*043036a2SApple OSS Distributions 		return kIOReturnBadArgument;
1913*043036a2SApple OSS Distributions 	}
1914*043036a2SApple OSS Distributions 
1915*043036a2SApple OSS Distributions 	return copyCurrentInQueueMem(queue, data, size);
1916*043036a2SApple OSS Distributions }
1917*043036a2SApple OSS Distributions 
1918*043036a2SApple OSS Distributions __END_DECLS
1919