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