1 /*
2 * Copyright (c) 2021-2022 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
29 #include <libkern/libkern.h>
30
31 #define VARIABLE_STORE_SIGNATURE 'NVV3'
32
33 // Variable Store Version
34 #define VARIABLE_STORE_VERSION 0x1
35
36 #define VARIABLE_DATA 0x55AA
37 #define INVALIDATED_VARIABLE_DATA 0x0000
38
39 // Variable State flags
40 #define VAR_IN_DELETED_TRANSITION 0xFE // Variable is in obsolete transistion
41 #define VAR_DELETED 0xFD // Variable is obsolete
42 #define VAR_INACTIVE 0xFB // Variable is inactive due to failing CRC
43 #define VAR_ADDED 0x7F // Variable has been completely added
44
45 // No changes needed on save
46 #define VAR_NEW_STATE_NONE 0x01
47 // Remove existing entry on save
48 #define VAR_NEW_STATE_REMOVE 0x02
49 // Add new value on save, mark previous as inactive
50 #define VAR_NEW_STATE_APPEND 0x03
51
52 #pragma pack(1)
53 struct v3_store_header {
54 uint32_t name;
55 uint32_t size;
56 uint32_t generation;
57 uint8_t state;
58 uint8_t flags;
59 uint8_t version;
60 uint8_t reserved1;
61 uint32_t system_size;
62 uint32_t common_size;
63 };
64
65 struct v3_var_header {
66 uint16_t startId;
67 uint8_t state;
68 uint8_t reserved;
69 uint32_t attributes;
70 uint32_t nameSize;
71 uint32_t dataSize;
72 uuid_t guid;
73 uint32_t crc;
74 uint8_t name_data_buf[];
75 };
76 #pragma pack()
77
78 struct nvram_v3_var_entry {
79 uint8_t new_state;
80 size_t existing_offset;
81 struct v3_var_header header;
82 };
83
84 static size_t
nvram_v3_var_container_size(const struct v3_var_header * header)85 nvram_v3_var_container_size(const struct v3_var_header *header)
86 {
87 return sizeof(struct nvram_v3_var_entry) + header->nameSize + header->dataSize;
88 }
89
90 static size_t
variable_length(const struct v3_var_header * header)91 variable_length(const struct v3_var_header *header)
92 {
93 return sizeof(struct v3_var_header) + header->nameSize + header->dataSize;
94 }
95
96 static bool
valid_store_header(const struct v3_store_header * header)97 valid_store_header(const struct v3_store_header *header)
98 {
99 return (header->name == VARIABLE_STORE_SIGNATURE) && (header->version == VARIABLE_STORE_VERSION);
100 }
101
102 static bool
valid_variable_header(const struct v3_var_header * header,size_t buf_len)103 valid_variable_header(const struct v3_var_header *header, size_t buf_len)
104 {
105 return (buf_len > sizeof(struct v3_var_header)) &&
106 (header->startId == VARIABLE_DATA) &&
107 (variable_length(header) <= buf_len);
108 }
109
110 static uint32_t
find_active_var_in_image(const struct v3_var_header * var,const uint8_t * image,uint32_t len)111 find_active_var_in_image(const struct v3_var_header *var, const uint8_t *image, uint32_t len)
112 {
113 uint32_t offset = sizeof(struct v3_store_header);
114 const struct v3_var_header *store_var;
115 uint32_t var_offset = 0;
116
117 while ((offset + sizeof(struct v3_var_header) < len)) {
118 store_var = (const struct v3_var_header *)(image + offset);
119
120 if (valid_variable_header(store_var, len - offset)) {
121 if ((store_var->state == VAR_ADDED) &&
122 (uuid_compare(var->guid, store_var->guid) == 0) &&
123 (var->nameSize == store_var->nameSize) &&
124 (memcmp(var->name_data_buf, store_var->name_data_buf, var->nameSize) == 0)) {
125 var_offset = offset;
126 break;
127 }
128 } else {
129 break;
130 }
131
132 offset += variable_length(store_var);
133 }
134
135 return var_offset;
136 }
137
138 static IOReturn
find_current_offset_in_image(const uint8_t * image,uint32_t len,uint32_t * newOffset)139 find_current_offset_in_image(const uint8_t *image, uint32_t len, uint32_t *newOffset)
140 {
141 uint32_t offset = 0;
142 uint32_t inner_offset = 0;
143
144 if (valid_store_header((const struct v3_store_header *)(image + offset))) {
145 DEBUG_INFO("valid store header @ %#x\n", offset);
146 offset += sizeof(struct v3_store_header);
147 }
148
149 while (offset < len) {
150 const struct v3_var_header *store_var = (const struct v3_var_header *)(image + offset);
151 uuid_string_t uuidString;
152
153 if (valid_variable_header(store_var, len - offset)) {
154 uuid_unparse(store_var->guid, uuidString);
155 DEBUG_INFO("Valid var @ %#08x, state=%#02x, length=%#08zx, %s:%s\n", offset, store_var->state,
156 variable_length(store_var), uuidString, store_var->name_data_buf);
157 offset += variable_length(store_var);
158 } else {
159 break;
160 }
161 }
162
163 while (offset < len) {
164 if (image[offset] == 0xFF) {
165 DEBUG_INFO("scanning for clear memory @ %#x\n", offset);
166
167 inner_offset = offset;
168
169 while ((inner_offset < len) && (image[inner_offset] == 0xFF)) {
170 inner_offset++;
171 }
172
173 if (inner_offset == len) {
174 DEBUG_INFO("found start of clear mem @ %#x\n", offset);
175 break;
176 } else {
177 DEBUG_ERROR("ERROR!!!!! found non-clear byte @ %#x\n", offset);
178 return kIOReturnInvalid;
179 }
180 }
181 offset++;
182 }
183
184 *newOffset = offset;
185
186 return kIOReturnSuccess;
187 }
188
189 class IONVRAMV3Handler : public IODTNVRAMFormatHandler, IOTypedOperatorsMixin<IONVRAMV3Handler>
190 {
191 private:
192 IONVRAMController *_nvramController;
193 IODTNVRAM *_provider;
194
195 bool _newData;
196 bool _resetData;
197
198 uint32_t _generation;
199
200 uint8_t *_nvramImage;
201 uint32_t _nvramSize;
202 uint8_t *_controllerImage;
203
204 OSSharedPtr<OSDictionary> &_commonDict;
205 OSSharedPtr<OSDictionary> &_systemDict;
206
207 uint32_t _commonSize;
208 uint32_t _systemSize;
209
210 uint32_t _commonUsed;
211 uint32_t _systemUsed;
212
213 uint32_t _currentOffset;
214
215 OSSharedPtr<OSArray> _varEntries;
216
217 IOReturn unserializeImage(const uint8_t *image, IOByteCount length);
218
219 IOReturn reclaim(void);
220
221 static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const char *propSymbol, OSObject *propObject);
222 static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength,
223 OSSharedPtr<const OSSymbol>& propSymbol, OSSharedPtr<OSObject>& propObject);
224
225 void setEntryForRemove(struct nvram_v3_var_entry *v3Entry, bool system);
226 void findExistingEntry(const uuid_t *varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex);
227 IOReturn syncRaw(void);
228
229 public:
230 virtual
231 ~IONVRAMV3Handler() APPLE_KEXT_OVERRIDE;
232 IONVRAMV3Handler(OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict);
233
234 static bool isValidImage(const uint8_t *image, IOByteCount length);
235
236 static IONVRAMV3Handler *init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length,
237 OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict);
238
239 virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) APPLE_KEXT_OVERRIDE;
240 virtual bool setController(IONVRAMController *controller) APPLE_KEXT_OVERRIDE;
241 virtual bool sync(void) APPLE_KEXT_OVERRIDE;
242 virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) APPLE_KEXT_OVERRIDE;
243 virtual uint32_t getGeneration(void) const APPLE_KEXT_OVERRIDE;
244 virtual uint32_t getVersion(void) const APPLE_KEXT_OVERRIDE;
245 virtual uint32_t getSystemUsed(void) const APPLE_KEXT_OVERRIDE;
246 virtual uint32_t getCommonUsed(void) const APPLE_KEXT_OVERRIDE;
247 };
248
~IONVRAMV3Handler()249 IONVRAMV3Handler::~IONVRAMV3Handler()
250 {
251 }
252
IONVRAMV3Handler(OSSharedPtr<OSDictionary> & commonDict,OSSharedPtr<OSDictionary> & systemDict)253 IONVRAMV3Handler::IONVRAMV3Handler(OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict) :
254 _commonDict(commonDict),
255 _systemDict(systemDict)
256 {
257 }
258
259 bool
isValidImage(const uint8_t * image,IOByteCount length)260 IONVRAMV3Handler::isValidImage(const uint8_t *image, IOByteCount length)
261 {
262 const struct v3_store_header *header = (const struct v3_store_header *)image;
263
264 if ((header == nullptr) || (length < sizeof(*header))) {
265 return false;
266 }
267
268 return valid_store_header(header);
269 }
270
271 IONVRAMV3Handler*
init(IODTNVRAM * provider,const uint8_t * image,IOByteCount length,OSSharedPtr<OSDictionary> & commonDict,OSSharedPtr<OSDictionary> & systemDict)272 IONVRAMV3Handler::init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length,
273 OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict)
274 {
275 IONVRAMV3Handler *handler = new IONVRAMV3Handler(commonDict, systemDict);
276
277 handler->_provider = provider;
278
279 if ((image != nullptr) && (length != 0)) {
280 if (handler->unserializeImage(image, length) != kIOReturnSuccess) {
281 DEBUG_ERROR("Unable to unserialize image, len=%#x\n", (unsigned int)length);
282 }
283 }
284
285 return handler;
286 }
287
288 IOReturn
flush(const uuid_t guid,IONVRAMOperation op)289 IONVRAMV3Handler::flush(const uuid_t guid, IONVRAMOperation op)
290 {
291 IOReturn ret = kIOReturnSuccess;
292
293 if ((_systemDict != nullptr) && (uuid_compare(guid, gAppleSystemVariableGuid) == 0)) {
294 // System dictionary contains keys that are only using the system GUID
295 const OSSymbol *key;
296 OSSharedPtr<OSDictionary> systemCopy;
297 OSSharedPtr<OSCollectionIterator> iter;
298 uuid_string_t uuidString;
299
300 systemCopy = OSDictionary::withDictionary(_systemDict.get());
301 iter = OSCollectionIterator::withCollection(systemCopy.get());
302 if ((systemCopy == nullptr) || (iter == nullptr)) {
303 ret = kIOReturnNoMemory;
304 goto exit;
305 }
306
307 DEBUG_INFO("Flushing system region...\n");
308
309 uuid_unparse(gAppleSystemVariableGuid, uuidString);
310 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
311 if (verifyPermission(op, gAppleSystemVariableGuid, key)) {
312 DEBUG_INFO("Clearing entry for %s:%s\n", uuidString, key->getCStringNoCopy());
313 // Using setVariable() instead of setEntryForRemove() to handle any GUID logic
314 // for system region
315 setVariable(guid, key->getCStringNoCopy(), nullptr);
316 } else {
317 DEBUG_INFO("Keeping entry for %s:%s\n", uuidString, key->getCStringNoCopy());
318 }
319 }
320
321 DEBUG_INFO("system dictionary flushed\n");
322 } else if ((_commonDict != nullptr) && (uuid_compare(guid, gAppleNVRAMGuid) == 0)) {
323 // Common dictionary contains everything that is not system this goes through our entire
324 // store and clears anything that is permitted
325 struct nvram_v3_var_entry *v3Entry = nullptr;
326 OSData *entryContainer = nullptr;
327 OSSharedPtr<OSDictionary> newCommonDict;
328 uuid_string_t uuidString;
329
330 DEBUG_INFO("Flushing common region...\n");
331
332 newCommonDict = OSDictionary::withCapacity(_commonDict->getCapacity());
333
334 if (newCommonDict == nullptr) {
335 ret = kIOReturnNoMemory;
336 goto exit;
337 }
338
339 for (unsigned int index = 0; index < _varEntries->getCount(); index++) {
340 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(index)));
341 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
342 const char *entryName = (const char *)v3Entry->header.name_data_buf;
343
344 // Skip system variables if there is a system region
345 if ((_systemSize != 0) && uuid_compare(v3Entry->header.guid, gAppleSystemVariableGuid) == 0) {
346 continue;
347 }
348
349 uuid_unparse(v3Entry->header.guid, uuidString);
350 if (verifyPermission(op, v3Entry->header.guid, entryName)) {
351 DEBUG_INFO("Clearing entry for %s:%s\n", uuidString, entryName);
352 setEntryForRemove(v3Entry, false);
353 } else {
354 DEBUG_INFO("Keeping entry for %s:%s\n", uuidString, entryName);
355 newCommonDict->setObject(entryName, _commonDict->getObject(entryName));
356 }
357 }
358
359 _commonDict = newCommonDict;
360 }
361
362 _newData = true;
363
364 if (_provider->_diags) {
365 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
366 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
367
368 val = OSNumber::withNumber(getCommonUsed(), 32);
369 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
370 }
371
372 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed);
373
374 exit:
375 return ret;
376 }
377
378 void
setEntryForRemove(struct nvram_v3_var_entry * v3Entry,bool system)379 IONVRAMV3Handler::setEntryForRemove(struct nvram_v3_var_entry *v3Entry, bool system)
380 {
381 const char * variableName;
382 uint32_t variableSize;
383
384 require_action(v3Entry != nullptr, exit, DEBUG_INFO("remove with no entry\n"));
385
386 variableName = (const char *)v3Entry->header.name_data_buf;
387 variableSize = (uint32_t)variable_length(&v3Entry->header);
388
389 if (v3Entry->new_state == VAR_NEW_STATE_REMOVE) {
390 DEBUG_INFO("entry %s already marked for remove\n", variableName);
391 } else {
392 DEBUG_INFO("marking entry %s for remove\n", variableName);
393
394 v3Entry->new_state = VAR_NEW_STATE_REMOVE;
395
396 if (system) {
397 _provider->_systemDict->removeObject(variableName);
398
399 if (_systemUsed < variableSize) {
400 panic("Invalid _systemUsed size\n");
401 }
402
403 _systemUsed -= variableSize;
404 } else {
405 _provider->_commonDict->removeObject(variableName);
406
407 if (_commonUsed < variableSize) {
408 panic("Invalid _commonUsed size\n");
409 }
410 _commonUsed -= variableSize;
411 }
412
413 if (_provider->_diags) {
414 _provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid),
415 kIONVRAMOperationDelete,
416 variableName,
417 nullptr);
418 }
419 }
420
421 exit:
422 return;
423 }
424
425 void
findExistingEntry(const uuid_t * varGuid,const char * varName,struct nvram_v3_var_entry ** existing,unsigned int * existingIndex)426 IONVRAMV3Handler::findExistingEntry(const uuid_t *varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex)
427 {
428 struct nvram_v3_var_entry *v3Entry = nullptr;
429 OSData *entryContainer = nullptr;
430 unsigned int index = 0;
431 uint32_t nameLen = (uint32_t)strlen(varName) + 1;
432
433 for (index = 0; index < _varEntries->getCount(); index++) {
434 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(index)));
435 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
436
437 if ((v3Entry->header.nameSize == nameLen) &&
438 (memcmp(v3Entry->header.name_data_buf, varName, nameLen) == 0)) {
439 if (varGuid) {
440 if (uuid_compare(*varGuid, v3Entry->header.guid) == 0) {
441 uuid_string_t uuidString;
442 uuid_unparse(*varGuid, uuidString);
443 DEBUG_INFO("found existing entry for %s:%s, e_off=%#lx, len=%#lx, new_state=%#x\n", uuidString, varName,
444 v3Entry->existing_offset, variable_length(&v3Entry->header), v3Entry->new_state);
445 break;
446 }
447 } else {
448 DEBUG_INFO("found existing entry for %s, e_off=%#lx, len=%#lx\n", varName, v3Entry->existing_offset, variable_length(&v3Entry->header));
449 break;
450 }
451 }
452
453 v3Entry = nullptr;
454 }
455
456 if (v3Entry != nullptr) {
457 if (existing) {
458 *existing = v3Entry;
459 }
460
461 if (existingIndex) {
462 *existingIndex = index;
463 }
464 }
465 }
466
467 IOReturn
unserializeImage(const uint8_t * image,IOByteCount length)468 IONVRAMV3Handler::unserializeImage(const uint8_t *image, IOByteCount length)
469 {
470 OSSharedPtr<const OSSymbol> propSymbol;
471 OSSharedPtr<OSObject> propObject;
472 OSSharedPtr<OSData> entryContainer;
473 const struct v3_store_header *storeHeader;
474 IOReturn ret = kIOReturnSuccess;
475 size_t existingSize;
476 struct nvram_v3_var_entry *v3Entry;
477 const struct v3_var_header *header;
478 size_t offset = sizeof(struct v3_store_header);
479 uint32_t crc;
480 unsigned int i;
481 bool system;
482 OSDictionary *dict;
483 uuid_string_t uuidString;
484
485 require(isValidImage(image, length), exit);
486
487 storeHeader = (const struct v3_store_header *)image;
488 require_action(storeHeader->size == (uint32_t)length, exit,
489 DEBUG_ERROR("Image size %#x != header size %#x\n", (unsigned int)length, storeHeader->size));
490
491 _generation = storeHeader->generation;
492 _systemSize = storeHeader->system_size;
493 _commonSize = storeHeader->common_size - sizeof(struct v3_store_header);
494
495 _systemUsed = 0;
496 _commonUsed = 0;
497
498 if (_nvramImage) {
499 IOFreeData(_nvramImage, _nvramSize);
500 }
501
502 _varEntries.reset();
503 _varEntries = OSArray::withCapacity(40);
504
505 _nvramImage = IONewData(uint8_t, length);
506 _nvramSize = (uint32_t)length;
507 bcopy(image, _nvramImage, _nvramSize);
508
509 if (_systemSize) {
510 _systemDict = OSDictionary::withCapacity(1);
511 }
512
513 if (_commonSize) {
514 _commonDict = OSDictionary::withCapacity(1);
515 }
516
517 while ((offset + sizeof(struct v3_var_header)) < length) {
518 struct nvram_v3_var_entry *existingEntry = nullptr;
519 unsigned int existingIndex = 0;
520
521 header = (const struct v3_var_header *)(image + offset);
522
523 for (i = 0; i < sizeof(struct v3_var_header); i++) {
524 if ((image[offset + i] != 0) && (image[offset + i] != 0xFF)) {
525 break;
526 }
527 }
528
529 if (i == sizeof(struct v3_var_header)) {
530 DEBUG_INFO("No more variables after offset %#lx\n", offset);
531 break;
532 }
533
534 if (!valid_variable_header(header, length - offset)) {
535 DEBUG_ERROR("invalid header @ %#lx\n", offset);
536 offset += sizeof(struct v3_var_header);
537 continue;
538 }
539
540 uuid_unparse(header->guid, uuidString);
541 DEBUG_INFO("Valid var @ %#08zx, state=%#02x, length=%#08zx, %s:%s\n", offset, header->state,
542 variable_length(header), uuidString, header->name_data_buf);
543
544 if (header->state != VAR_ADDED) {
545 goto skip;
546 }
547
548 crc = crc32(0, header->name_data_buf + header->nameSize, header->dataSize);
549
550 if (crc != header->crc) {
551 DEBUG_ERROR("invalid crc @ %#lx, calculated=%#x, read=%#x\n", offset, crc, header->crc);
552 goto skip;
553 }
554
555 v3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(nvram_v3_var_container_size(header));
556 __nochk_memcpy(&v3Entry->header, _nvramImage + offset, variable_length(header));
557
558 // It is assumed that the initial image being unserialized here is going to be the proxy data from EDT and not the image
559 // read from the controller, which for various reasons due to the setting of states and saves from iBoot, can be
560 // different. We will have an initial existing_offset of 0 and once the controller is set we will read
561 // out the image there and update the existing offset with what is present on the NOR image
562 v3Entry->existing_offset = 0;
563 v3Entry->new_state = VAR_NEW_STATE_NONE;
564
565 // safe guard for any strange duplicate entries in the store
566 findExistingEntry(&v3Entry->header.guid, (const char *)v3Entry->header.name_data_buf, &existingEntry, &existingIndex);
567
568 if (existingEntry != nullptr) {
569 existingSize = variable_length(&existingEntry->header);
570
571 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_container_size(header));
572 _varEntries->replaceObject(existingIndex, entryContainer.get());
573
574 DEBUG_INFO("Found existing for %s, resetting when controller available\n", v3Entry->header.name_data_buf);
575 _resetData = true;
576 } else {
577 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_container_size(header));
578 _varEntries->setObject(entryContainer.get());
579 existingSize = 0;
580 }
581
582 system = (_systemSize != 0) && (uuid_compare(v3Entry->header.guid, gAppleSystemVariableGuid) == 0);
583 if (system) {
584 dict = _systemDict.get();
585 _systemUsed = _systemUsed + (uint32_t)variable_length(header) - (uint32_t)existingSize;
586 } else {
587 dict = _commonDict.get();
588 _commonUsed = _commonUsed + (uint32_t)variable_length(header) - (uint32_t)existingSize;
589 }
590
591 if (convertPropToObject(v3Entry->header.name_data_buf, v3Entry->header.nameSize,
592 v3Entry->header.name_data_buf + v3Entry->header.nameSize, v3Entry->header.dataSize,
593 propSymbol, propObject)) {
594 DEBUG_INFO("adding %s, dataLength=%u, system=%d\n",
595 propSymbol->getCStringNoCopy(), v3Entry->header.dataSize, system);
596
597 dict->setObject(propSymbol.get(), propObject.get());
598
599 if (_provider->_diags) {
600 _provider->_diags->logVariable(_provider->getDictionaryType(dict),
601 kIONVRAMOperationInit, propSymbol.get()->getCStringNoCopy(),
602 (void *)(uintptr_t)(header->name_data_buf + header->nameSize));
603 }
604 }
605 IOFreeData(v3Entry, nvram_v3_var_container_size(header));
606 skip:
607 offset += variable_length(header);
608 }
609
610 _currentOffset = (uint32_t)offset;
611
612 DEBUG_ALWAYS("_commonSize %#x, _systemSize %#x, _currentOffset %#x\n", _commonSize, _systemSize, _currentOffset);
613 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed);
614
615 exit:
616 _newData = true;
617
618 if (_provider->_diags) {
619 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
620 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
621 DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
622
623 val = OSNumber::withNumber(getCommonUsed(), 32);
624 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
625 DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
626 }
627
628 return ret;
629 }
630
631 IOReturn
setVariable(const uuid_t varGuid,const char * variableName,OSObject * object)632 IONVRAMV3Handler::setVariable(const uuid_t varGuid, const char *variableName, OSObject *object)
633 {
634 struct nvram_v3_var_entry *v3Entry = nullptr;
635 struct nvram_v3_var_entry *newV3Entry;
636 OSSharedPtr<OSData> newContainer;
637 bool unset = (object == nullptr);
638 bool system = false;
639 IOReturn ret = kIOReturnSuccess;
640 size_t entryNameLen = strlen(variableName) + 1;
641 unsigned int existingEntryIndex;
642 uint32_t dataSize = 0;
643 size_t existingVariableSize = 0;
644 size_t newVariableSize = 0;
645 size_t newEntrySize;
646 uuid_t destGuid;
647 uuid_string_t uuidString;
648
649 if (_systemSize != 0) {
650 // System region case, if they're using the GUID directly or it's on the system allow list
651 // force it to use the System GUID
652 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) {
653 system = true;
654 uuid_copy(destGuid, gAppleSystemVariableGuid);
655 } else {
656 uuid_copy(destGuid, varGuid);
657 }
658 } else {
659 // No system region, store System GUID as Common GUID
660 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) {
661 uuid_copy(destGuid, gAppleNVRAMGuid);
662 } else {
663 uuid_copy(destGuid, varGuid);
664 }
665 }
666
667 uuid_unparse(varGuid, uuidString);
668 DEBUG_INFO("setting %s:%s, system=%d, current var count=%u\n", uuidString, variableName, system, _varEntries->getCount());
669
670 uuid_unparse(destGuid, uuidString);
671 DEBUG_INFO("using %s, _commonUsed %#x, _systemUsed %#x\n", uuidString, _commonUsed, _systemUsed);
672
673 findExistingEntry(&destGuid, variableName, &v3Entry, &existingEntryIndex);
674
675 if (unset == true) {
676 setEntryForRemove(v3Entry, system);
677 } else {
678 if ((v3Entry != nullptr) && (v3Entry->new_state != VAR_NEW_STATE_REMOVE)) {
679 // Sizing was subtracted in setEntryForRemove
680 existingVariableSize = variable_length(&v3Entry->header);
681 }
682
683 convertObjectToProp(nullptr, &dataSize, variableName, object);
684
685 newVariableSize = sizeof(struct v3_var_header) + entryNameLen + dataSize;
686 newEntrySize = sizeof(struct nvram_v3_var_entry) + entryNameLen + dataSize;
687
688 if (system) {
689 if (_systemUsed - existingVariableSize + newVariableSize > _systemSize) {
690 DEBUG_ERROR("system region full\n");
691 ret = kIOReturnNoSpace;
692 goto exit;
693 }
694 } else if (_commonUsed - existingVariableSize + newVariableSize > _commonSize) {
695 DEBUG_ERROR("common region full\n");
696 ret = kIOReturnNoSpace;
697 goto exit;
698 }
699
700 DEBUG_INFO("creating new entry for %s, existingVariableSize=%#zx, newVariableSize=%#zx\n", variableName, existingVariableSize, newVariableSize);
701 newV3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(newEntrySize);
702
703 memcpy(newV3Entry->header.name_data_buf, variableName, entryNameLen);
704 convertObjectToProp(newV3Entry->header.name_data_buf + entryNameLen, &dataSize, variableName, object);
705
706 newV3Entry->header.startId = VARIABLE_DATA;
707 newV3Entry->header.nameSize = (uint32_t)entryNameLen;
708 newV3Entry->header.dataSize = dataSize;
709 newV3Entry->header.crc = crc32(0, newV3Entry->header.name_data_buf + entryNameLen, dataSize);
710 memcpy(newV3Entry->header.guid, &destGuid, sizeof(gAppleNVRAMGuid));
711 newV3Entry->new_state = VAR_NEW_STATE_APPEND;
712
713 if (v3Entry) {
714 newV3Entry->existing_offset = v3Entry->existing_offset;
715 newV3Entry->header.state = v3Entry->header.state;
716 newV3Entry->header.attributes = v3Entry->header.attributes;
717
718 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize);
719 _varEntries->replaceObject(existingEntryIndex, newContainer.get());
720 } else {
721 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize);
722 _varEntries->setObject(newContainer.get());
723 }
724
725 if (system) {
726 _systemUsed = _systemUsed + (uint32_t)newVariableSize - (uint32_t)existingVariableSize;
727 _provider->_systemDict->setObject(variableName, object);
728 } else {
729 _commonUsed = _commonUsed + (uint32_t)newVariableSize - (uint32_t)existingVariableSize;
730 _provider->_commonDict->setObject(variableName, object);
731 }
732
733 if (_provider->_diags) {
734 _provider->_diags->logVariable(getPartitionTypeForGUID(destGuid), kIONVRAMOperationWrite, variableName, (void *)(uintptr_t)dataSize);
735 }
736
737 IOFreeData(newV3Entry, newEntrySize);
738 }
739
740 exit:
741 _newData = true;
742
743 if (_provider->_diags) {
744 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
745 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
746
747 val = OSNumber::withNumber(getCommonUsed(), 32);
748 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
749 }
750
751 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed);
752
753 return ret;
754 }
755
756 bool
setController(IONVRAMController * controller)757 IONVRAMV3Handler::setController(IONVRAMController *controller)
758 {
759 IOReturn ret = kIOReturnSuccess;
760 struct nvram_v3_var_entry *v3Entry;
761 const struct v3_store_header *storeHeader;
762 const struct v3_var_header *storeVar;
763 OSData *entryContainer;
764
765 if (_nvramController == NULL) {
766 _nvramController = controller;
767 }
768
769 DEBUG_INFO("Controller name: %s\n", _nvramController->getName());
770
771 require(_nvramSize != 0, exit);
772
773 if (_resetData) {
774 _resetData = false;
775 DEBUG_ERROR("_resetData set, issuing reclaim recovery\n");
776 ret = reclaim();
777 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret));
778 goto exit;
779 }
780
781 _controllerImage = (uint8_t *)IOMallocData(_nvramSize);
782 _nvramController->read(0, _controllerImage, _nvramSize);
783
784 if (isValidImage(_controllerImage, _nvramSize)) {
785 DEBUG_INFO("valid image found\n");
786
787 storeHeader = (const struct v3_store_header *)_controllerImage;
788
789 _generation = storeHeader->generation;
790
791 // We must sync any existing variables offset on the controller image with our internal representation
792 // If we find an existing entry and the data is still the same we record the existing offset and mark it
793 // as VAR_NEW_STATE_NONE meaning no action needed
794 // Otherwise if the data is different or it is not found on the controller image we mark it as VAR_NEW_STATE_APPEND
795 // which will have us invalidate the existing entry if there is one and append it on the next save
796 for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
797 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(i)));
798 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
799
800 DEBUG_INFO("Looking for %s\n", v3Entry->header.name_data_buf);
801 v3Entry->existing_offset = find_active_var_in_image(&v3Entry->header, _controllerImage, _nvramSize);
802
803 if (v3Entry->existing_offset == 0) {
804 DEBUG_ERROR("%s is not in the NOR image\n", v3Entry->header.name_data_buf);
805 if (v3Entry->new_state != VAR_NEW_STATE_REMOVE) {
806 DEBUG_INFO("%s marked for append\n", v3Entry->header.name_data_buf);
807 // Doesn't exist in the store, just append it on next sync
808 v3Entry->new_state = VAR_NEW_STATE_APPEND;
809 }
810 } else {
811 DEBUG_INFO("Found offset for %s @ %#zx\n", v3Entry->header.name_data_buf, v3Entry->existing_offset);
812 storeVar = (const struct v3_var_header *)&_controllerImage[v3Entry->existing_offset];
813
814 // Verify that the existing data matches the store data
815 if ((variable_length(&v3Entry->header) == variable_length(storeVar)) &&
816 (memcmp(v3Entry->header.name_data_buf, storeVar->name_data_buf, storeVar->nameSize + storeVar->dataSize) == 0)) {
817 DEBUG_INFO("Store var data for %s matches, marking new state none\n", v3Entry->header.name_data_buf);
818 v3Entry->new_state = VAR_NEW_STATE_NONE;
819 } else {
820 DEBUG_INFO("Store var data for %s differs, marking new state append\n", v3Entry->header.name_data_buf);
821 v3Entry->new_state = VAR_NEW_STATE_APPEND;
822 }
823 }
824 }
825
826 ret = find_current_offset_in_image(_controllerImage, _nvramSize, &_currentOffset);
827 if (ret != kIOReturnSuccess) {
828 DEBUG_ERROR("Unidentified bytes in image, reclaiming\n");
829 ret = reclaim();
830 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim byte recovery failed, invalid controller state!!! ret=%#x\n", ret));
831 }
832 DEBUG_INFO("New _currentOffset=%#x\n", _currentOffset);
833 } else {
834 DEBUG_ERROR("Invalid image found, issuing reclaim recovery\n");
835 ret = reclaim();
836 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret));
837 }
838
839 IOFreeData(_controllerImage, _nvramSize);
840
841 exit:
842 return ret == kIOReturnSuccess;
843 }
844
845 IOReturn
reclaim(void)846 IONVRAMV3Handler::reclaim(void)
847 {
848 IOReturn ret;
849 struct v3_store_header newStoreHeader;
850 struct v3_var_header *varHeader;
851 struct nvram_v3_var_entry *varEntry;
852 OSData *entryContainer;
853 size_t new_bank_offset = sizeof(struct v3_store_header);
854
855 DEBUG_INFO("called\n");
856
857 ret = _nvramController->nextBank();
858 verify_noerr_action(ret, DEBUG_ERROR("Bank shift not triggered\n"));
859
860 for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
861 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i));
862 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
863 varHeader = &varEntry->header;
864
865 DEBUG_INFO("entry %u %s, new_state=%#x, e_offset=%#lx, state=%#x\n",
866 i, varEntry->header.name_data_buf, varEntry->new_state, varEntry->existing_offset, varHeader->state);
867
868 if (varEntry->new_state == VAR_NEW_STATE_NONE) {
869 ret = _nvramController->write(new_bank_offset, (uint8_t *)varHeader, variable_length(varHeader));
870 require_noerr_action(ret, exit, DEBUG_ERROR("var write failed, ret=%08x\n", ret));
871
872 varEntry->existing_offset = new_bank_offset;
873 new_bank_offset += variable_length(varHeader);
874 } else {
875 // Set existing offset to 0 so that they will either be appended
876 // or any remaining removals will be dropped
877 varEntry->existing_offset = 0;
878 }
879 }
880
881 memcpy(&newStoreHeader, _nvramImage, sizeof(newStoreHeader));
882
883 _generation += 1;
884
885 newStoreHeader.generation = _generation;
886
887 ret = _nvramController->write(0, (uint8_t *)&newStoreHeader, sizeof(newStoreHeader));
888 require_noerr_action(ret, exit, DEBUG_ERROR("store header write failed, ret=%08x\n", ret));
889
890 _currentOffset = (uint32_t)new_bank_offset;
891
892 DEBUG_INFO("Reclaim complete, _generation=%u, _currentOffset=%#x\n", _generation, _currentOffset);
893
894 exit:
895 return ret;
896 }
897
898 IOReturn
syncRaw(void)899 IONVRAMV3Handler::syncRaw(void)
900 {
901 IOReturn ret = kIOReturnSuccess;
902 size_t varEndOffset;
903 size_t varStartOffset;
904 struct nvram_v3_var_entry *varEntry;
905 struct v3_var_header *varHeader;
906 OSData *entryContainer;
907 OSSharedPtr<OSArray> remainingEntries;
908
909 require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n"));
910 require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n"));
911 require_action(_nvramSize != 0, exit, DEBUG_INFO("No nvram size info\n"));
912
913 DEBUG_INFO("_varEntries->getCount()=%#x\n", _varEntries->getCount());
914
915 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity());
916
917 for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
918 size_t space_needed = 0;
919 uint8_t state;
920
921 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i));
922 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
923 varHeader = &varEntry->header;
924
925 DEBUG_INFO("%s new_state=%d, e_off=%#lx, c_off=%#x, uuid=%x%x, nameSize=%#x, dataSize=%#x\n",
926 varEntry->header.name_data_buf,
927 varEntry->new_state, varEntry->existing_offset, _currentOffset,
928 varHeader->guid[0], varHeader->guid[1],
929 varHeader->nameSize, varHeader->dataSize);
930
931 if (varEntry->new_state == VAR_NEW_STATE_APPEND) {
932 space_needed = variable_length(varHeader);
933
934 // reclaim if needed
935 if ((_currentOffset + space_needed) > _nvramSize) {
936 ret = reclaim();
937 require_noerr_action(ret, exit, DEBUG_ERROR("reclaim fail, ret=%#x\n", ret));
938
939 // Check after reclaim...
940 if ((_currentOffset + space_needed) > _nvramSize) {
941 DEBUG_ERROR("nvram full!\n");
942 goto exit;
943 }
944
945 DEBUG_INFO("%s AFTER reclaim new_state=%d, e_off=%#lx, c_off=%#x, uuid=%x%x, nameSize=%#x, dataSize=%#x\n",
946 varEntry->header.name_data_buf,
947 varEntry->new_state, varEntry->existing_offset, _currentOffset,
948 varHeader->guid[0], varHeader->guid[1],
949 varHeader->nameSize, varHeader->dataSize);
950 }
951
952 if (varEntry->existing_offset) {
953 // Mark existing entry as VAR_IN_DELETED_TRANSITION
954 state = varHeader->state & VAR_IN_DELETED_TRANSITION;
955 DEBUG_INFO("invalidating with state=%#x\n", state);
956
957 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state));
958 require_noerr_action(ret, exit, DEBUG_ERROR("new state w fail, ret=%#x\n", ret));
959 }
960
961 varStartOffset = _currentOffset;
962 varEndOffset = _currentOffset;
963
964 // Append new entry as VAR_ADDED
965 varHeader->state = VAR_ADDED;
966
967 ret = _nvramController->write(varStartOffset, (uint8_t *)varHeader, variable_length(varHeader));
968 require_noerr_action(ret, exit, DEBUG_ERROR("variable write fail, ret=%#x\n", ret); );
969
970 varEndOffset += variable_length(varHeader);
971
972 if (varEntry->existing_offset) {
973 // Mark existing entry as VAR_DELETED
974 state = varHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION;
975
976 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state));
977 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret));
978 }
979
980 varEntry->existing_offset = varStartOffset;
981 varEntry->new_state = VAR_NEW_STATE_NONE;
982
983 _currentOffset = (uint32_t)varEndOffset;
984
985 remainingEntries->setObject(entryContainer);
986 } else if (varEntry->new_state == VAR_NEW_STATE_REMOVE) {
987 if (varEntry->existing_offset) {
988 DEBUG_INFO("marking entry at offset %#lx deleted\n", varEntry->existing_offset);
989
990 // Mark existing entry as VAR_IN_DELETED_TRANSITION
991 state = varHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION;
992
993 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state));
994 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret));
995 } else {
996 DEBUG_INFO("No existing, removing\n");
997 }
998
999 // not re-added to remainingEntries
1000 } else {
1001 DEBUG_INFO("skipping\n");
1002 remainingEntries->setObject(entryContainer);
1003 }
1004 }
1005
1006 _varEntries.reset(remainingEntries.get(), OSRetain);
1007
1008 _newData = false;
1009
1010 exit:
1011 return ret;
1012 }
1013
1014 bool
sync(void)1015 IONVRAMV3Handler::sync(void)
1016 {
1017 IOReturn ret;
1018
1019 ret = syncRaw();
1020
1021 if (ret != kIOReturnSuccess) {
1022 ret = reclaim();
1023 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, ret=%#x", ret));
1024 }
1025
1026 exit:
1027 return ret == kIOReturnSuccess;
1028 }
1029
1030 uint32_t
getGeneration(void) const1031 IONVRAMV3Handler::getGeneration(void) const
1032 {
1033 return _generation;
1034 }
1035
1036 uint32_t
getVersion(void) const1037 IONVRAMV3Handler::getVersion(void) const
1038 {
1039 return kNVRAMVersion3;
1040 }
1041
1042 uint32_t
getSystemUsed(void) const1043 IONVRAMV3Handler::getSystemUsed(void) const
1044 {
1045 return _systemUsed;
1046 }
1047
1048 uint32_t
getCommonUsed(void) const1049 IONVRAMV3Handler::getCommonUsed(void) const
1050 {
1051 return _commonUsed;
1052 }
1053
1054 bool
convertObjectToProp(uint8_t * buffer,uint32_t * length,const char * propName,OSObject * propObject)1055 IONVRAMV3Handler::convertObjectToProp(uint8_t *buffer, uint32_t *length,
1056 const char *propName, OSObject *propObject)
1057 {
1058 uint32_t offset;
1059 IONVRAMVariableType propType;
1060 OSBoolean *tmpBoolean = nullptr;
1061 OSNumber *tmpNumber = nullptr;
1062 OSString *tmpString = nullptr;
1063 OSData *tmpData = nullptr;
1064
1065 propType = getVariableType(propName);
1066
1067 // Get the size of the data.
1068 offset = 0;
1069 switch (propType) {
1070 case kOFVariableTypeBoolean:
1071 tmpBoolean = OSDynamicCast(OSBoolean, propObject);
1072 if (tmpBoolean != nullptr) {
1073 const char *bool_buf;
1074 if (tmpBoolean->getValue()) {
1075 bool_buf = "true";
1076 } else {
1077 bool_buf = "false";
1078 }
1079
1080 offset = (uint32_t)strlen(bool_buf);
1081
1082 if (buffer) {
1083 if (*length < offset) {
1084 return false;
1085 } else {
1086 memcpy(buffer, bool_buf, offset);
1087 }
1088 }
1089 }
1090 break;
1091
1092 case kOFVariableTypeNumber:
1093 tmpNumber = OSDynamicCast(OSNumber, propObject);
1094 if (tmpNumber != nullptr) {
1095 char num_buf[12];
1096 char *end_buf = num_buf;
1097 uint32_t tmpValue = tmpNumber->unsigned32BitValue();
1098 if (tmpValue == 0xFFFFFFFF) {
1099 end_buf += snprintf(end_buf, sizeof(num_buf), "-1");
1100 } else if (tmpValue < 1000) {
1101 end_buf += snprintf(end_buf, sizeof(num_buf), "%d", (uint32_t)tmpValue);
1102 } else {
1103 end_buf += snprintf(end_buf, sizeof(num_buf), "%#x", (uint32_t)tmpValue);
1104 }
1105
1106 offset = (uint32_t)(end_buf - num_buf);
1107 if (buffer) {
1108 if (*length < offset) {
1109 return false;
1110 } else {
1111 memcpy(buffer, num_buf, offset);
1112 }
1113 }
1114 }
1115 break;
1116
1117 case kOFVariableTypeString:
1118 tmpString = OSDynamicCast(OSString, propObject);
1119 if (tmpString != nullptr) {
1120 offset = tmpString->getLength();
1121
1122 if (buffer) {
1123 if (*length < offset) {
1124 return false;
1125 } else {
1126 bcopy(tmpString->getCStringNoCopy(), buffer, offset);
1127 }
1128 }
1129 }
1130 break;
1131
1132 case kOFVariableTypeData:
1133 tmpData = OSDynamicCast(OSData, propObject);
1134 if (tmpData != nullptr) {
1135 offset = tmpData->getLength();
1136
1137 if (buffer) {
1138 if (*length < offset) {
1139 return false;
1140 } else {
1141 bcopy(tmpData->getBytesNoCopy(), buffer, offset);
1142 }
1143 }
1144 }
1145 break;
1146
1147 default:
1148 return false;
1149 }
1150
1151 *length = offset;
1152
1153 return offset != 0;
1154 }
1155
1156
1157 bool
convertPropToObject(const uint8_t * propName,uint32_t propNameLength,const uint8_t * propData,uint32_t propDataLength,OSSharedPtr<const OSSymbol> & propSymbol,OSSharedPtr<OSObject> & propObject)1158 IONVRAMV3Handler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength,
1159 const uint8_t *propData, uint32_t propDataLength,
1160 OSSharedPtr<const OSSymbol>& propSymbol,
1161 OSSharedPtr<OSObject>& propObject)
1162 {
1163 OSSharedPtr<const OSSymbol> tmpSymbol;
1164 OSSharedPtr<OSNumber> tmpNumber;
1165 OSSharedPtr<OSString> tmpString;
1166 OSSharedPtr<OSObject> tmpObject = nullptr;
1167
1168 tmpSymbol = OSSymbol::withCString((const char *)propName);
1169
1170 if (tmpSymbol == nullptr) {
1171 return false;
1172 }
1173
1174 switch (getVariableType(tmpSymbol.get())) {
1175 case kOFVariableTypeBoolean:
1176 if (!strncmp("true", (const char *)propData, propDataLength)) {
1177 tmpObject.reset(kOSBooleanTrue, OSRetain);
1178 } else if (!strncmp("false", (const char *)propData, propDataLength)) {
1179 tmpObject.reset(kOSBooleanFalse, OSRetain);
1180 }
1181 break;
1182
1183 case kOFVariableTypeNumber:
1184 tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
1185 if (tmpNumber != nullptr) {
1186 tmpObject = tmpNumber;
1187 }
1188 break;
1189
1190 case kOFVariableTypeString:
1191 tmpString = OSString::withCString((const char *)propData, propDataLength);
1192 if (tmpString != nullptr) {
1193 tmpObject = tmpString;
1194 }
1195 break;
1196
1197 case kOFVariableTypeData:
1198 tmpObject = OSData::withBytes(propData, propDataLength);
1199 break;
1200
1201 default:
1202 break;
1203 }
1204
1205 if (tmpObject == nullptr) {
1206 tmpSymbol.reset();
1207 return false;
1208 }
1209
1210 propSymbol = tmpSymbol;
1211 propObject = tmpObject;
1212
1213 return true;
1214 }
1215