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