1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* OSValueObject.h created by spoirier on Mon 28-Jun-2021 */
29
30 #ifndef _OS_OSVALUEOBJECT_H
31 #define _OS_OSVALUEOBJECT_H
32
33 #if KERNEL_PRIVATE
34 #if __cplusplus >= 201703L /* C++17 is required for this class */
35
36 #include <IOKit/IOLib.h>
37 #include <libkern/c++/OSObject.h>
38 #include <libkern/c++/OSPtr.h>
39 #include <os/base.h>
40 #include <string.h>
41
42 /*!
43 * @header
44 *
45 * @abstract
46 * This header declares the OSValueObject container class.
47 */
48
49
50 namespace os_detail {
51 kalloc_type_view_t GetOSValueObjectKTV();
52 }
53
54 /*!
55 * @class OSValueObject
56 *
57 * @abstract
58 * OSValueObject wraps a single C++ type allocation in a C++ object
59 * for use in Libkern collections.
60 *
61 * @discussion
62 * OSValueObject a single type heap allocated as a Libkern C++ object.
63 * OSValueObject objects are mutable:
64 * You can overwrite the value data it contains.
65 *
66 * <b>Use Restrictions</b>
67 *
68 * With very few exceptions in the I/O Kit, all Libkern-based C++
69 * classes, functions, and macros are <b>unsafe</b>
70 * to use in a primary interrupt context.
71 * Consult the I/O Kit documentation related to primary interrupts
72 * for more information.
73 *
74 * OSValueObject provides no concurrency protection;
75 * it's up to the usage context to provide any protection necessary.
76 * Some portions of the I/O Kit, such as
77 * @link //apple_ref/doc/class/IORegistryEntry IORegistryEntry@/link,
78 * handle synchronization via defined member functions for setting
79 * properties.
80 */
81 template <typename T>
82 class OSValueObject final : public OSObject
83 {
84 public:
85
86 /*!
87 * @function create
88 *
89 * @abstract
90 * Creates and initializes an empty instance of OSValueObject,
91 * where the contained object's memory has been zeroed and then
92 * default-constructed.
93 *
94 * @result
95 * An instance of OSValueObject with a reference count of 1;
96 * <code>nullptr</code> on failure.
97 */
98 static OSPtr<OSValueObject> create();
99
100 /*!
101 * @function withBytes
102 *
103 * @abstract
104 * Creates and initializes an instance of OSValueObject
105 * with a copy of the provided value.
106 *
107 * @param value A reference to the value to copy.
108 *
109 * @result
110 * An instance of OSValueObject containing a copy of the provided value,
111 * with a reference count of 1;
112 * <code>nullptr</code> on failure.
113 */
114 static OSPtr<OSValueObject> withValue(const T& value);
115
116 /*!
117 * @function withValueObject
118 *
119 * @abstract
120 * Creates and initializes an instance of OSValueObject
121 * with contents copied from another OSValueObject object.
122 *
123 * @param inValueObject An OSValueObject object that provides the initial data.
124 *
125 * @result
126 * An instance of OSValueObject containing a copy of the data in
127 * <code>inValueObject</code>, with a reference count of 1;
128 * <code>nullptr</code> on failure.
129 */
130 static OSPtr<OSValueObject> withValueObject(const OSValueObject * inValueObject);
131
132 /*!
133 * @function free
134 *
135 * @abstract
136 * Deallocates or releases any resources
137 * used by the OSValueObject instance.
138 *
139 * @discussion
140 * This function should not be called directly;
141 * use
142 * <code>@link
143 * //apple_ref/cpp/instm/OSObject/release/virtualvoid/()
144 * release@/link</code>
145 * instead.
146 */
147 void free() APPLE_KEXT_OVERRIDE;
148
149 /*!
150 * @function setValue
151 *
152 * @abstract
153 * Replaces the contents of the OSValueObject object's internal data buffer
154 * with a copy of the provided value.
155 *
156 * @param value A reference to the value to copy.
157 */
158 void setValue(const T& value);
159
160 /*!
161 * @function getBytesNoCopy
162 *
163 * @abstract
164 * Returns a pointer to the OSValueObject object's internal data buffer.
165 *
166 * @result
167 * A pointer to the OSValueObject object's internal data buffer.
168 *
169 * @discussion
170 * You cannot modify the existing contents of an OSValueObject object
171 * via this function.
172 */
173 const T * getBytesNoCopy() const;
174
175 /*!
176 * @function getMutableBytesNoCopy
177 *
178 * @abstract
179 * Returns a pointer to the OSValueObject object's internal data buffer.
180 *
181 * @result
182 * A pointer to the OSValueObject object's internal data buffer.
183 *
184 * @discussion
185 * You can modify the existing contents of an OSValueObject object
186 * via this function.
187 */
188 T * getMutableBytesNoCopy();
189
190 /*!
191 * @function getRef
192 *
193 * @abstract
194 * Returns a const reference to the OSValueObject object's internal value data.
195 *
196 * @result
197 * A const reference to the OSValueObject object's internal value data.
198 *
199 * @discussion
200 * You cannot modify the existing contents of an OSValueObject object
201 * via this function.
202 */
203 const T & getRef() const;
204
205 /*!
206 * @function getMutableRef
207 *
208 * @abstract
209 * Returns a pointer to the OSValueObject object's internal value data.
210 *
211 * @result
212 * A pointer to the OSValueObject object's internal value data.
213 *
214 * @discussion
215 * You can modify the existing contents of an OSValueObject object
216 * via this function.
217 */
218 T & getMutableRef();
219
220 /*!
221 * @function getLength
222 *
223 * @abstract
224 * Returns the number of bytes in or referenced by the OSValueObject object.
225 *
226 * @result
227 * The number of bytes in or referenced by the OSValueObject object.
228 */
229 constexpr size_t
getLength()230 getLength() const
231 {
232 return sizeof(T);
233 }
234
235 /*!
236 * @function isEqualTo
237 *
238 * @abstract
239 * Tests the equality of two OSValueObject objects.
240 *
241 * @param inValueObject The OSValueObject object being compared against the receiver.
242 *
243 * @result
244 * <code>true</code> if the two OSValueObject objects are equivalent,
245 * <code>false</code> otherwise.
246 *
247 * @discussion
248 * Two OSValueObject objects are considered equal
249 * if they have same length and if their
250 * byte buffers hold the same contents.
251 */
252 virtual bool isEqualTo(const OSValueObject * inValueObject) const;
253
254
255 /*!
256 * @function isEqualTo
257 *
258 * @abstract
259 * Tests the equality of an OSValueObject object's contents
260 * to a C array of bytes.
261 *
262 * @param value A reference to the value to compare.
263 *
264 * @result
265 * <code>true</code> if the values' data are equal,
266 * <code>false</code> otherwise.
267 */
268 virtual bool isEqualTo(const T& value) const;
269
270
271 /*!
272 * @function isEqualTo
273 *
274 * @abstract
275 * Tests the equality of an OSValueObject object to an arbitrary object.
276 *
277 * @param inObject The object to be compared against the receiver.
278 *
279 * @result
280 * <code>true</code> if the two objects are equivalent,
281 * <code>false</code> otherwise.
282 *
283 * @discussion
284 * An OSValueObject is considered equal to another object
285 * if that object is derived from OSValueObject<T>
286 * and contains the equivalent bytes of the same length.
287 */
288 virtual bool isEqualTo(const OSMetaClassBase * inObject) const APPLE_KEXT_OVERRIDE;
289
290 /*!
291 * @function serialize
292 *
293 * @abstract
294 * This class is not serializable.
295 *
296 * @param serializer The OSSerialize object.
297 *
298 * @result
299 * <code>false</code> always
300 */
301 virtual bool serialize(OSSerialize * serializer) const APPLE_KEXT_OVERRIDE;
302
303 protected:
304
305 /*!
306 * @function init
307 *
308 * @abstract
309 * Initializes an instance of OSValueObject with a zeroed
310 * and then default-constructed data buffer.
311 *
312 * @result
313 * <code>true</code> on success, <code>false</code> on failure.
314 *
315 * @discussion
316 * Not for general use. Use the static instance creation method
317 * <code>@link create create@/link</code> instead.
318 */
319 virtual bool init() APPLE_KEXT_OVERRIDE;
320
321 private:
322 T * OS_PTRAUTH_SIGNED_PTR("OSValueObject.data") data = nullptr;
323
324
325 // inline expansion of OSDeclareDefaultStructors and OSDefineMetaClassAndStructors macros
326 // (existing macros do not support template classes)
327 public:
OSValueObject()328 OSValueObject() : OSObject(&gMetaClass)
329 {
330 gMetaClass.instanceConstructed();
331 }
332
333 static inline class MetaClass : public OSMetaClass
334 {
335 public:
MetaClass()336 MetaClass() : OSMetaClass("OSValueObject", &OSObject::gMetaClass, sizeof(OSValueObject))
337 {
338 }
339 virtual OSObject *
alloc()340 alloc() const APPLE_KEXT_OVERRIDE
341 {
342 return new OSValueObject;
343 }
344 } gMetaClass;
345 static inline const OSMetaClass * const metaClass = &OSValueObject::gMetaClass;
346
347 friend class OSValueObject::MetaClass;
348
349 virtual const OSMetaClass *
getMetaClass()350 getMetaClass() const APPLE_KEXT_OVERRIDE
351 {
352 return &gMetaClass;
353 }
354
355 static void *
new(size_t size)356 operator new(size_t size)
357 {
358 // requires alternate implementation if this SPI is ever made public API
359 return OSObject_typed_operator_new(os_detail::GetOSValueObjectKTV(), size);
360 }
361
362 protected:
OSValueObject(const OSMetaClass * meta)363 explicit OSValueObject(const OSMetaClass * meta) : OSObject(meta)
364 {
365 }
366
367 static void
delete(void * mem,size_t size)368 operator delete(void * mem, size_t size)
369 {
370 // requires alternate implementation if this SPI is ever made public API
371 return OSObject_typed_operator_delete(os_detail::GetOSValueObjectKTV(), mem, size);
372 }
373 };
374
375 /*!
376 * @function OSValueObjectWithValue
377 *
378 * @abstract
379 * Creates and initializes an instance of OSValueObject
380 * with a copy of the provided value.
381 * This is a free function wrapper of OSValueObject<T>::withValue to allow for
382 * type inference of the class' template argument from the function parameter
383 * (so that you do not need to explicitly provide the type template argument).
384 *
385 * @param value A reference to the value to copy.
386 *
387 * @result
388 * An instance of OSValueObject containing a copy of the provided value,
389 * with a reference count of 1;
390 * <code>nullptr</code> on failure.
391 */
392 template <typename T>
393 OSPtr<OSValueObject<T> > OSValueObjectWithValue(const T& value);
394
395
396 #pragma mark -
397
398 void* operator new(size_t, void*) noexcept; // forward declaration needed for placement-new
399
400 template <typename T>
401 OSPtr<OSValueObject<T> >
create()402 OSValueObject<T>::create()
403 {
404 #ifdef IOKIT_ENABLE_SHARED_PTR
405 auto me = OSMakeShared<OSValueObject<T> >();
406 #else
407 auto * me = OSTypeAlloc(OSValueObject<T>);
408 #endif
409 if (me && !me->init()) {
410 #ifndef IOKIT_ENABLE_SHARED_PTR
411 me->release();
412 #endif
413 return nullptr;
414 }
415 return me;
416 }
417
418 template <typename T>
419 OSPtr<OSValueObject<T> >
withValue(const T & value)420 OSValueObject<T>::withValue(const T& value)
421 {
422 #ifdef IOKIT_ENABLE_SHARED_PTR
423 OSSharedPtr<OSValueObject<T> > me = create();
424 #else
425 OSValueObject<T> * me = create();
426 #endif
427 if (me) {
428 me->setValue(value);
429 }
430 return me;
431 }
432
433 template <typename T>
434 OSPtr<OSValueObject<T> >
withValueObject(const OSValueObject * inValueObject)435 OSValueObject<T>::withValueObject(const OSValueObject * inValueObject)
436 {
437 if (!inValueObject || !inValueObject->getBytesNoCopy()) {
438 return {};
439 }
440 return withValue(inValueObject->getRef());
441 }
442
443 template <typename T>
444 bool
init()445 OSValueObject<T>::init()
446 {
447 if (!OSObject::init()) {
448 return false;
449 }
450
451 if (data) {
452 data->~T();
453 bzero(data, getLength());
454 } else {
455 data = IOMallocType(T);
456 if (!data) {
457 return false;
458 }
459 }
460
461 ::new (static_cast<void*>(data)) T();
462
463 return true;
464 }
465
466 template <typename T>
467 void
free()468 OSValueObject<T>::free()
469 {
470 IOFreeType(data, T);
471 data = nullptr;
472
473 OSObject::free();
474 }
475
476 template <typename T>
477 void
setValue(const T & value)478 OSValueObject<T>::setValue(const T& value)
479 {
480 //static_assert(__is_trivially_copyable(T) || __is_copy_assignable(T));
481 if (&value != data) {
482 if constexpr (__is_trivially_copyable(T)) {
483 memcpy(data, &value, getLength());
484 } else {
485 *data = value;
486 }
487 }
488 }
489
490 template <typename T>
491 const T *
getBytesNoCopy()492 OSValueObject<T>::getBytesNoCopy() const
493 {
494 return data;
495 }
496
497 template <typename T>
498 T *
getMutableBytesNoCopy()499 OSValueObject<T>::getMutableBytesNoCopy()
500 {
501 return data;
502 }
503
504 template <typename T>
505 const T &
getRef()506 OSValueObject<T>::getRef() const
507 {
508 assert(data);
509 return *data;
510 }
511
512 template <typename T>
513 T &
getMutableRef()514 OSValueObject<T>::getMutableRef()
515 {
516 assert(data);
517 return *data;
518 }
519
520 template <typename T>
521 bool
isEqualTo(const OSValueObject * inValueObject)522 OSValueObject<T>::isEqualTo(const OSValueObject * inValueObject) const
523 {
524 return inValueObject && isEqualTo(inValueObject->getRef());
525 }
526
527 template <typename T>
528 bool
isEqualTo(const T & value)529 OSValueObject<T>::isEqualTo(const T& value) const
530 {
531 if constexpr (__is_scalar(T) || __is_aggregate(T)) {
532 return memcmp(data, &value, getLength()) == 0;
533 } else {
534 return *data == value;
535 }
536 }
537
538 template <typename T>
539 bool
isEqualTo(const OSMetaClassBase * inObject)540 OSValueObject<T>::isEqualTo(const OSMetaClassBase * inObject) const
541 {
542 if (const auto * const otherValueObject = OSDynamicCast(OSValueObject, inObject)) {
543 return isEqualTo(otherValueObject);
544 }
545 return false;
546 }
547
548 template <typename T>
549 bool
serialize(__unused OSSerialize * serializer)550 OSValueObject<T>::serialize(__unused OSSerialize * serializer) const
551 {
552 return false;
553 }
554
555 template <typename T>
556 OSPtr<OSValueObject<T> >
OSValueObjectWithValue(const T & value)557 OSValueObjectWithValue(const T& value)
558 {
559 return OSValueObject<T>::withValue(value);
560 }
561
562
563 #endif /* __cplusplus */
564 #endif /* KERNEL_PRIVATE */
565
566 #endif /* !_OS_OSVALUEOBJECT_H */
567