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