xref: /xnu-8792.61.2/iokit/Kernel/IONVRAMCHRPHandler.cpp (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
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/c++/OSBoundedPtr.h>
30 
31 #define NVRAM_CHRP_APPLE_HEADER_NAME_V1  "nvram"
32 #define NVRAM_CHRP_APPLE_HEADER_NAME_V2  "2nvram"
33 
34 #define NVRAM_CHRP_PARTITION_NAME_COMMON_V1   "common"
35 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM_V1   "system"
36 #define NVRAM_CHRP_PARTITION_NAME_COMMON_V2   "2common"
37 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM_V2   "2system"
38 
39 #define NVRAM_CHRP_LENGTH_BLOCK_SIZE 0x10 // CHRP length field is in 16 byte blocks
40 
41 typedef struct chrp_nvram_header { //16 bytes
42 	uint8_t  sig;
43 	uint8_t  cksum; // checksum on sig, len, and name
44 	uint16_t len;   // total length of the partition in 16 byte blocks starting with the signature
45 	// and ending with the last byte of data area, ie len includes its own header size
46 	char     name[12];
47 	uint8_t  data[0];
48 } chrp_nvram_header_t;
49 
50 typedef struct apple_nvram_header {  // 16 + 16 bytes
51 	struct   chrp_nvram_header chrp;
52 	uint32_t adler;
53 	uint32_t generation;
54 	uint8_t  padding[8];
55 } apple_nvram_header_t;
56 
57 typedef struct {
58 	NVRAMPartitionType        type;
59 	uint32_t                    offset;
60 	uint32_t                    size;
61 	OSSharedPtr<OSDictionary> &dict;
62 } NVRAMRegionInfo;
63 
64 class IONVRAMCHRPHandler : public IODTNVRAMFormatHandler, IOTypedOperatorsMixin<IONVRAMCHRPHandler>
65 {
66 private:
67 	bool              _newData;
68 	bool              _reload;
69 	IONVRAMController *_nvramController;
70 	IODTNVRAM         *_provider;
71 	NVRAMVersion      _version;
72 	uint32_t          _generation;
73 
74 	uint8_t           *_nvramImage;
75 
76 	uint32_t          _commonPartitionOffset;
77 	uint32_t          _commonPartitionSize;
78 
79 	uint32_t          _systemPartitionOffset;
80 	uint32_t          _systemPartitionSize;
81 
82 	OSSharedPtr<OSDictionary>    &_commonDict;
83 	OSSharedPtr<OSDictionary>    &_systemDict;
84 
85 	uint32_t          _commonUsed;
86 	uint32_t          _systemUsed;
87 
88 	uint32_t findCurrentBank(uint32_t *gen);
89 	IOReturn unserializeImage(const uint8_t *image, IOByteCount length);
90 	IOReturn unserializeVariables();
91 
92 	IOReturn serializeVariables(void);
93 
94 	IOReturn reloadInternal(void);
95 	IOReturn setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object, bool systemVar);
96 
97 	static OSSharedPtr<OSData> unescapeBytesToData(const uint8_t *bytes, uint32_t length);
98 	static OSSharedPtr<OSData> escapeDataToData(OSData * value);
99 
100 	static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength,
101 	    LIBKERN_RETURNS_RETAINED const OSSymbol **propSymbol, LIBKERN_RETURNS_RETAINED OSObject **propObject);
102 	static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength,
103 	    OSSharedPtr<const OSSymbol>& propSymbol, OSSharedPtr<OSObject>& propObject);
104 	static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const OSSymbol *propSymbol, OSObject *propObject);
105 	static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const char *propSymbol, OSObject *propObject);
106 
107 public:
108 	virtual
109 	~IONVRAMCHRPHandler() APPLE_KEXT_OVERRIDE;
110 	IONVRAMCHRPHandler(OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict);
111 
112 	static bool isValidImage(const uint8_t *image, IOByteCount length);
113 
114 	static  IONVRAMCHRPHandler *init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length,
115 	    OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict);
116 
117 	virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) APPLE_KEXT_OVERRIDE;
118 	virtual bool     setController(IONVRAMController *controller) APPLE_KEXT_OVERRIDE;
119 	virtual bool     sync(void) APPLE_KEXT_OVERRIDE;
120 	virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) APPLE_KEXT_OVERRIDE;
121 	virtual void     reload(void) APPLE_KEXT_OVERRIDE;
122 	virtual uint32_t getGeneration(void) const APPLE_KEXT_OVERRIDE;
123 	virtual uint32_t getVersion(void) const APPLE_KEXT_OVERRIDE;
124 	virtual uint32_t getSystemUsed(void) const APPLE_KEXT_OVERRIDE;
125 	virtual uint32_t getCommonUsed(void) const APPLE_KEXT_OVERRIDE;
126 };
127 
128 static const char *
get_bank_version_string(int version)129 get_bank_version_string(int version)
130 {
131 	switch (version) {
132 	case kNVRAMVersion1:
133 		return NVRAM_CHRP_APPLE_HEADER_NAME_V1;
134 	case kNVRAMVersion2:
135 		return NVRAM_CHRP_APPLE_HEADER_NAME_V2;
136 	default:
137 		return "Unknown";
138 	}
139 }
140 
141 static uint32_t
adler32(const uint8_t * buffer,size_t length)142 adler32(const uint8_t *buffer, size_t length)
143 {
144 	uint32_t offset;
145 	uint32_t adler, lowHalf, highHalf;
146 
147 	lowHalf = 1;
148 	highHalf = 0;
149 
150 	for (offset = 0; offset < length; offset++) {
151 		if ((offset % 5000) == 0) {
152 			lowHalf  %= 65521L;
153 			highHalf %= 65521L;
154 		}
155 
156 		lowHalf += buffer[offset];
157 		highHalf += lowHalf;
158 	}
159 
160 	lowHalf  %= 65521L;
161 	highHalf %= 65521L;
162 
163 	adler = (highHalf << 16) | lowHalf;
164 
165 	return adler;
166 }
167 
168 static uint32_t
nvram_get_adler(uint8_t * buf,int version)169 nvram_get_adler(uint8_t *buf, int version)
170 {
171 	return ((struct apple_nvram_header *)buf)->adler;
172 }
173 
174 static uint32_t
adler32_with_version(const uint8_t * buf,size_t len,int version)175 adler32_with_version(const uint8_t *buf, size_t len, int version)
176 {
177 	size_t offset;
178 
179 	switch (version) {
180 	case kNVRAMVersion1:
181 	case kNVRAMVersion2:
182 		offset = offsetof(struct apple_nvram_header, generation);
183 		break;
184 	default:
185 		return 0;
186 	}
187 
188 	return adler32(buf + offset, len - offset);
189 }
190 
191 static uint8_t
chrp_checksum(const struct chrp_nvram_header * hdr)192 chrp_checksum(const struct chrp_nvram_header *hdr)
193 {
194 	uint16_t      sum;
195 	const uint8_t *p;
196 	const uint8_t *begin = (const uint8_t *)hdr + offsetof(struct chrp_nvram_header, len);
197 	const uint8_t *end = (const uint8_t *)hdr + offsetof(struct chrp_nvram_header, data);
198 
199 	// checksum the header (minus the checksum itself)
200 	sum = hdr->sig;
201 	for (p = begin; p < end; p++) {
202 		sum += *p;
203 	}
204 	while (sum > 0xff) {
205 		sum = (sum & 0xff) + (sum >> 8);
206 	}
207 
208 	return sum & 0xff;
209 }
210 
211 static IOReturn
nvram_validate_header_v1v2(const uint8_t * buf,uint32_t * generation,int version)212 nvram_validate_header_v1v2(const uint8_t * buf, uint32_t *generation, int version)
213 {
214 	IOReturn   result = kIOReturnError;
215 	uint8_t    checksum;
216 	const char *header_string = get_bank_version_string(version);
217 	struct     chrp_nvram_header *chrp_header = (struct chrp_nvram_header *)buf;
218 	uint32_t   local_gen = 0;
219 
220 	require(buf != nullptr, exit);
221 
222 	// <rdar://problem/73454488> Recovery Mode [Internal Build] 18D52-->18E141 [J307/308 Only]
223 	// we can only compare the first "nvram" parts of the name as some devices have additional junk from
224 	// a previous build likely copying past bounds of the "nvram" name in the const section
225 	if (memcmp(header_string, chrp_header->name, strlen(header_string)) == 0) {
226 		checksum = chrp_checksum(chrp_header);
227 		if (checksum == chrp_header->cksum) {
228 			result = kIOReturnSuccess;
229 			local_gen = ((struct apple_nvram_header*)buf)->generation;
230 
231 			DEBUG_INFO("Found %s gen=%u\n", header_string, local_gen);
232 
233 			if (generation) {
234 				*generation = local_gen;
235 			}
236 		} else {
237 			DEBUG_INFO("invalid checksum in header, found %#02x, expected %#02x\n", chrp_header->cksum, checksum);
238 		}
239 	} else {
240 		DEBUG_INFO("invalid bank for \"%s\", name = %#02x %#02x %#02x %#02x\n", header_string,
241 		    chrp_header->name[0],
242 		    chrp_header->name[1],
243 		    chrp_header->name[2],
244 		    chrp_header->name[3]);
245 	}
246 
247 exit:
248 	return result;
249 }
250 
251 static void
nvram_set_apple_header(uint8_t * buf,size_t len,uint32_t generation,int version)252 nvram_set_apple_header(uint8_t *buf, size_t len, uint32_t generation, int version)
253 {
254 	if (version == kNVRAMVersion1 ||
255 	    version == kNVRAMVersion2) {
256 		struct apple_nvram_header *apple_hdr = (struct apple_nvram_header *)buf;
257 		generation += 1;
258 		apple_hdr->generation = generation;
259 		apple_hdr->adler = adler32_with_version(buf, len, version);
260 	}
261 }
262 
263 static NVRAMVersion
validateNVRAMVersion(const uint8_t * buf,size_t len,uint32_t * generation)264 validateNVRAMVersion(const uint8_t *buf, size_t len, uint32_t *generation)
265 {
266 	NVRAMVersion version = kNVRAMVersionUnknown;
267 
268 	if (nvram_validate_header_v1v2(buf, generation, kNVRAMVersion1) == kIOReturnSuccess) {
269 		version = kNVRAMVersion1;
270 		goto exit;
271 	}
272 
273 	if (nvram_validate_header_v1v2(buf, generation, kNVRAMVersion2) == kIOReturnSuccess) {
274 		version = kNVRAMVersion2;
275 		goto exit;
276 	}
277 
278 	DEBUG_INFO("Unable to determine version\n");
279 
280 exit:
281 	DEBUG_INFO("version=%u\n", version);
282 	return version;
283 }
284 
~IONVRAMCHRPHandler()285 IONVRAMCHRPHandler::~IONVRAMCHRPHandler()
286 {
287 }
288 
289 bool
isValidImage(const uint8_t * image,IOByteCount length)290 IONVRAMCHRPHandler::isValidImage(const uint8_t *image, IOByteCount length)
291 {
292 	return validateNVRAMVersion(image, length, nullptr) != kNVRAMVersionUnknown;
293 }
294 
295 IONVRAMCHRPHandler*
init(IODTNVRAM * provider,const uint8_t * image,IOByteCount length,OSSharedPtr<OSDictionary> & commonDict,OSSharedPtr<OSDictionary> & systemDict)296 IONVRAMCHRPHandler::init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length,
297     OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict)
298 {
299 	bool propertiesOk;
300 
301 	IONVRAMCHRPHandler *handler = new IONVRAMCHRPHandler(commonDict, systemDict);
302 
303 	handler->_provider = provider;
304 
305 	propertiesOk = handler->getNVRAMProperties();
306 	require_action(propertiesOk, exit, DEBUG_ERROR("Unable to get NVRAM properties\n"));
307 
308 	require_action(length == handler->_bankSize, exit, DEBUG_ERROR("length 0x%llx != _bankSize 0x%x\n", length, handler->_bankSize));
309 
310 	if ((image != nullptr) && (length != 0)) {
311 		if (handler->unserializeImage(image, length) != kIOReturnSuccess) {
312 			DEBUG_ALWAYS("Unable to unserialize image, len=%#x\n", (unsigned int)length);
313 		}
314 	}
315 
316 	return handler;
317 
318 exit:
319 	delete handler;
320 
321 	return nullptr;
322 }
323 
IONVRAMCHRPHandler(OSSharedPtr<OSDictionary> & commonDict,OSSharedPtr<OSDictionary> & systemDict)324 IONVRAMCHRPHandler::IONVRAMCHRPHandler(OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict) :
325 	_commonDict(commonDict),
326 	_systemDict(systemDict)
327 {
328 }
329 
330 IOReturn
flush(const uuid_t guid,IONVRAMOperation op)331 IONVRAMCHRPHandler::flush(const uuid_t guid, IONVRAMOperation op)
332 {
333 	IOReturn ret = kIOReturnSuccess;
334 
335 	if ((_systemDict != nullptr) && (uuid_compare(guid, gAppleSystemVariableGuid) == 0)) {
336 		const OSSymbol *key;
337 		OSSharedPtr<OSDictionary> systemCopy;
338 		OSSharedPtr<OSCollectionIterator> iter;
339 
340 		systemCopy = OSDictionary::withDictionary(_systemDict.get());
341 		iter = OSCollectionIterator::withCollection(systemCopy.get());
342 		if ((systemCopy == nullptr) || (iter == nullptr)) {
343 			ret = kIOReturnNoMemory;
344 			goto exit;
345 		}
346 
347 		while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
348 			if (verifyPermission(op, gAppleSystemVariableGuid, key)) {
349 				// Force use of system partition
350 				setVariableInternal(guid, key->getCStringNoCopy(), nullptr, true);
351 			}
352 		}
353 
354 		DEBUG_INFO("system dictionary flushed\n");
355 	} else if ((_commonDict != nullptr) && (uuid_compare(guid, gAppleNVRAMGuid) == 0)) {
356 		const OSSymbol *key;
357 		OSSharedPtr<OSDictionary> commonCopy;
358 		OSSharedPtr<OSCollectionIterator> iter;
359 
360 		commonCopy = OSDictionary::withDictionary(_commonDict.get());
361 		iter = OSCollectionIterator::withCollection(commonCopy.get());
362 		if ((commonCopy == nullptr) || (iter == nullptr)) {
363 			ret = kIOReturnNoMemory;
364 			goto exit;
365 		}
366 
367 		while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
368 			if (verifyPermission(op, gAppleNVRAMGuid, key)) {
369 				// Don't use the system partition if it exists
370 				setVariableInternal(guid, key->getCStringNoCopy(), nullptr, false);
371 			}
372 		}
373 
374 		DEBUG_INFO("common dictionary flushed\n");
375 	}
376 
377 exit:
378 	return ret;
379 }
380 
381 IOReturn
reloadInternal(void)382 IONVRAMCHRPHandler::reloadInternal(void)
383 {
384 	uint32_t controllerBank;
385 	uint32_t controllerGen;
386 
387 	controllerBank = findCurrentBank(&controllerGen);
388 
389 	if (_currentBank != controllerBank) {
390 		DEBUG_ERROR("_currentBank 0x%x != controllerBank 0x%x", _currentBank, controllerBank);
391 	}
392 
393 	if (_generation != controllerGen) {
394 		DEBUG_ERROR("_generation 0x%x != controllerGen 0x%x", _generation, controllerGen);
395 	}
396 
397 	_currentBank = controllerBank;
398 	_generation = controllerGen;
399 
400 	return kIOReturnSuccess;
401 }
402 
403 void
reload(void)404 IONVRAMCHRPHandler::reload(void)
405 {
406 	_reload = true;
407 
408 	DEBUG_INFO("reload marked\n");
409 }
410 
411 IOReturn
unserializeImage(const uint8_t * image,IOByteCount length)412 IONVRAMCHRPHandler::unserializeImage(const uint8_t *image, IOByteCount length)
413 {
414 	uint32_t partitionOffset, partitionLength;
415 	uint32_t currentLength, currentOffset = 0;
416 	uint32_t hdr_adler, calculated_adler;
417 
418 	_commonPartitionOffset = 0xFFFFFFFF;
419 	_systemPartitionOffset = 0xFFFFFFFF;
420 
421 	_version = validateNVRAMVersion(image, _bankSize, &_generation);
422 	require(_version != kNVRAMVersionUnknown, exit);
423 
424 	if (_nvramImage) {
425 		IOFreeData(_nvramImage, _bankSize);
426 	}
427 
428 	_nvramImage = IONewData(uint8_t, length);
429 	_bankSize = (uint32_t)length;
430 	bcopy(image, _nvramImage, _bankSize);
431 
432 	hdr_adler = nvram_get_adler(_nvramImage, _version);
433 	calculated_adler = adler32_with_version(_nvramImage, _bankSize, _version);
434 
435 	if (hdr_adler != calculated_adler) {
436 		panic("header adler %#08X != calculated_adler %#08X\n", hdr_adler, calculated_adler);
437 	}
438 
439 	// Look through the partitions to find the common and system partitions.
440 	while (currentOffset < _bankSize) {
441 		bool common_partition;
442 		bool system_partition;
443 		const chrp_nvram_header_t * header = (chrp_nvram_header_t *)(_nvramImage + currentOffset);
444 		const uint8_t common_v1_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_COMMON_V1};
445 		const uint8_t common_v2_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_COMMON_V2};
446 		const uint8_t system_v1_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_SYSTEM_V1};
447 		const uint8_t system_v2_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_SYSTEM_V2};
448 
449 		currentLength = header->len * NVRAM_CHRP_LENGTH_BLOCK_SIZE;
450 
451 		if (currentLength < sizeof(chrp_nvram_header_t)) {
452 			break;
453 		}
454 
455 		partitionOffset = currentOffset + sizeof(chrp_nvram_header_t);
456 		partitionLength = currentLength - sizeof(chrp_nvram_header_t);
457 
458 		if ((partitionOffset + partitionLength) > _bankSize) {
459 			break;
460 		}
461 
462 		common_partition = (memcmp(header->name, common_v1_name, sizeof(header->name)) == 0) ||
463 		    (memcmp(header->name, common_v2_name, sizeof(header->name)) == 0);
464 		system_partition = (memcmp(header->name, system_v1_name, sizeof(header->name)) == 0) ||
465 		    (memcmp(header->name, system_v2_name, sizeof(header->name)) == 0);
466 
467 		if (common_partition) {
468 			_commonPartitionOffset = partitionOffset;
469 			_commonPartitionSize = partitionLength;
470 		} else if (system_partition) {
471 			_systemPartitionOffset = partitionOffset;
472 			_systemPartitionSize = partitionLength;
473 		}
474 		currentOffset += currentLength;
475 	}
476 
477 	if (_commonPartitionSize) {
478 		_commonDict = OSDictionary::withCapacity(1);
479 	}
480 
481 	if (_systemPartitionSize) {
482 		_systemDict = OSDictionary::withCapacity(1);
483 	}
484 
485 exit:
486 	DEBUG_ALWAYS("NVRAM : commonPartitionOffset - %#x, commonPartitionSize - %#x, systemPartitionOffset - %#x, systemPartitionSize - %#x\n",
487 	    _commonPartitionOffset, _commonPartitionSize, _systemPartitionOffset, _systemPartitionSize);
488 
489 	return unserializeVariables();
490 }
491 
492 IOReturn
unserializeVariables(void)493 IONVRAMCHRPHandler::unserializeVariables(void)
494 {
495 	uint32_t                    cnt, cntStart;
496 	const uint8_t               *propName, *propData;
497 	uint32_t                    propNameLength, propDataLength, regionIndex;
498 	OSSharedPtr<const OSSymbol> propSymbol;
499 	OSSharedPtr<OSObject>       propObject;
500 	NVRAMRegionInfo             *currentRegion;
501 	NVRAMRegionInfo             variableRegions[] = { { kIONVRAMPartitionCommon, _commonPartitionOffset, _commonPartitionSize, _commonDict},
502 							  { kIONVRAMPartitionSystem, _systemPartitionOffset, _systemPartitionSize, _systemDict} };
503 
504 	DEBUG_INFO("...\n");
505 
506 	for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
507 		currentRegion = &variableRegions[regionIndex];
508 		const uint8_t * imageData = _nvramImage + currentRegion->offset;
509 
510 		if (currentRegion->size == 0) {
511 			continue;
512 		}
513 
514 		DEBUG_INFO("region = %d\n", currentRegion->type);
515 		cnt = 0;
516 		while (cnt < currentRegion->size) {
517 			cntStart = cnt;
518 			// Break if there is no name.
519 			if (imageData[cnt] == '\0') {
520 				break;
521 			}
522 
523 			// Find the length of the name.
524 			propName = imageData + cnt;
525 			for (propNameLength = 0; (cnt + propNameLength) < currentRegion->size;
526 			    propNameLength++) {
527 				if (imageData[cnt + propNameLength] == '=') {
528 					break;
529 				}
530 			}
531 
532 			// Break if the name goes past the end of the partition.
533 			if ((cnt + propNameLength) >= currentRegion->size) {
534 				break;
535 			}
536 			cnt += propNameLength + 1;
537 
538 			propData = imageData + cnt;
539 			for (propDataLength = 0; (cnt + propDataLength) < currentRegion->size;
540 			    propDataLength++) {
541 				if (imageData[cnt + propDataLength] == '\0') {
542 					break;
543 				}
544 			}
545 
546 			// Break if the data goes past the end of the partition.
547 			if ((cnt + propDataLength) >= currentRegion->size) {
548 				break;
549 			}
550 			cnt += propDataLength + 1;
551 
552 			if (convertPropToObject(propName, propNameLength,
553 			    propData, propDataLength,
554 			    propSymbol, propObject)) {
555 				NVRAMPartitionType partitionType;
556 				const char         *varName = propSymbol.get()->getCStringNoCopy();
557 				uint32_t           variableLength = cnt - cntStart;
558 
559 				DEBUG_INFO("adding %s, variableLength=%#x,dataLength=%#x\n", varName, variableLength, propDataLength);
560 
561 				currentRegion->dict.get()->setObject(propSymbol.get(), propObject.get());
562 				partitionType = _provider->getDictionaryType(currentRegion->dict.get());
563 
564 				if (_provider->_diags) {
565 					_provider->_diags->logVariable(partitionType, kIONVRAMOperationInit, varName,
566 					    (void *)(uintptr_t)(variableLength));
567 				}
568 
569 				if (partitionType == kIONVRAMPartitionSystem) {
570 					_systemUsed += variableLength;
571 				} else if (partitionType == kIONVRAMPartitionCommon) {
572 					_commonUsed += variableLength;
573 				}
574 			}
575 		}
576 	}
577 
578 	if (_provider->_diags) {
579 		OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
580 		_provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
581 		DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
582 
583 		val = OSNumber::withNumber(getCommonUsed(), 32);
584 		_provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
585 		DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
586 	}
587 
588 	// Create the boot-args property if it is not in the dictionary.
589 	if (_systemDict != nullptr) {
590 		if (_systemDict->getObject(kIONVRAMBootArgsKey) == nullptr) {
591 			propObject = OSString::withCStringNoCopy("");
592 			if (propObject != nullptr) {
593 				_systemDict->setObject(kIONVRAMBootArgsKey, propObject.get());
594 			}
595 		}
596 	} else if (_commonDict != nullptr) {
597 		if (_commonDict->getObject(kIONVRAMBootArgsKey) == nullptr) {
598 			propObject = OSString::withCStringNoCopy("");
599 			if (propObject != nullptr) {
600 				_commonDict->setObject(kIONVRAMBootArgsKey, propObject.get());
601 			}
602 		}
603 	}
604 
605 	_newData = true;
606 
607 	DEBUG_INFO("%s _commonDict=%p _systemDict=%p\n", __FUNCTION__, _commonDict ? _commonDict.get() : nullptr, _systemDict ? _systemDict.get() : nullptr);
608 
609 	return kIOReturnSuccess;
610 }
611 
612 IOReturn
serializeVariables(void)613 IONVRAMCHRPHandler::serializeVariables(void)
614 {
615 	IOReturn                          ret;
616 	bool                              ok = false;
617 	uint32_t                          length, maxLength, regionIndex;
618 	uint8_t                           *buffer, *tmpBuffer;
619 	const OSSymbol                    *tmpSymbol;
620 	OSObject                          *tmpObject;
621 	OSSharedPtr<OSCollectionIterator> iter;
622 	OSSharedPtr<OSNumber>             generation;
623 	uint8_t                           *nvramImage;
624 	NVRAMRegionInfo                   *currentRegion;
625 	NVRAMRegionInfo                   variableRegions[] = { { kIONVRAMPartitionCommon, _commonPartitionOffset, _commonPartitionSize, _commonDict},
626 								{ kIONVRAMPartitionSystem, _systemPartitionOffset, _systemPartitionSize, _systemDict} };
627 
628 	require_action(_nvramController != nullptr, exit, (ret = kIOReturnNotReady, DEBUG_ERROR("No _nvramController\n")));
629 	require_action(_newData == true, exit, (ret = kIOReturnSuccess, DEBUG_INFO("No _newData to sync\n")));
630 	require_action(_bankSize != 0, exit, (ret = kIOReturnSuccess, DEBUG_INFO("No nvram size info\n")));
631 	require_action(_nvramImage != nullptr, exit, (ret = kIOReturnSuccess, DEBUG_INFO("No nvram image info\n")));
632 
633 	nvramImage = IONewZeroData(uint8_t, _bankSize);
634 	require_action(nvramImage != nullptr, exit, (ret = kIOReturnNoMemory, DEBUG_ERROR("Can't create NVRAM image copy\n")));
635 
636 	DEBUG_INFO("...\n");
637 
638 	bcopy(_nvramImage, nvramImage, _bankSize);
639 
640 	for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
641 		currentRegion = &variableRegions[regionIndex];
642 
643 		if (currentRegion->size == 0) {
644 			continue;
645 		}
646 
647 		DEBUG_INFO("region = %d\n", currentRegion->type);
648 		buffer = tmpBuffer = nvramImage + currentRegion->offset;
649 
650 		bzero(buffer, currentRegion->size);
651 
652 		ok = true;
653 		maxLength = currentRegion->size;
654 
655 		iter = OSCollectionIterator::withCollection(currentRegion->dict.get());
656 		if (iter == nullptr) {
657 			ok = false;
658 		}
659 
660 		while (ok) {
661 			tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
662 			if (tmpSymbol == nullptr) {
663 				break;
664 			}
665 
666 			DEBUG_INFO("adding variable %s\n", tmpSymbol->getCStringNoCopy());
667 
668 			tmpObject = currentRegion->dict->getObject(tmpSymbol);
669 
670 			length = maxLength;
671 			ok = convertObjectToProp(tmpBuffer, &length, tmpSymbol, tmpObject);
672 			if (ok) {
673 				tmpBuffer += length;
674 				maxLength -= length;
675 			}
676 		}
677 
678 		if (!ok) {
679 			ret = kIOReturnNoSpace;
680 			IODeleteData(nvramImage, uint8_t, _bankSize);
681 			break;
682 		}
683 
684 		if (currentRegion->type == kIONVRAMPartitionSystem) {
685 			_systemUsed = (uint32_t)(tmpBuffer - buffer);
686 		} else if (currentRegion->type == kIONVRAMPartitionCommon) {
687 			_commonUsed = (uint32_t)(tmpBuffer - buffer);
688 		}
689 	}
690 
691 	DEBUG_INFO("ok=%d\n", ok);
692 	require(ok, exit);
693 
694 	nvram_set_apple_header(nvramImage, _bankSize, ++_generation, _version);
695 
696 	_currentBank = (_currentBank + 1) % _bankCount;
697 
698 	ret = _nvramController->select(_currentBank);
699 	DEBUG_IFERROR(ret, "_currentBank=%#x, select=%#x\n", _currentBank, ret);
700 
701 	ret = _nvramController->eraseBank();
702 	DEBUG_IFERROR(ret, "eraseBank=%#x\n", ret);
703 
704 	ret = _nvramController->write(0, nvramImage, _bankSize);
705 	DEBUG_IFERROR(ret, "write=%#x\n", ret);
706 
707 	_nvramController->sync();
708 
709 	if (_nvramImage) {
710 		IODeleteData(_nvramImage, uint8_t, _bankSize);
711 	}
712 
713 	_nvramImage = nvramImage;
714 
715 	_newData = false;
716 
717 exit:
718 	return ret;
719 }
720 
721 IOReturn
setVariableInternal(const uuid_t varGuid,const char * variableName,OSObject * object,bool systemVar)722 IONVRAMCHRPHandler::setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object, bool systemVar)
723 {
724 	uint32_t   newSize = 0;
725 	uint32_t   existingSize = 0;
726 	bool       remove = (object == nullptr);
727 	OSObject   *existing;
728 
729 	DEBUG_INFO("setting %s, systemVar=%d\n", variableName, systemVar);
730 
731 	if (systemVar) {
732 		if ((existing = _systemDict->getObject(variableName))) {
733 			convertObjectToProp(nullptr, &existingSize, variableName, existing);
734 		}
735 	} else {
736 		if ((existing = _commonDict->getObject(variableName))) {
737 			convertObjectToProp(nullptr, &existingSize, variableName, existing);
738 		}
739 	}
740 
741 	if (remove == false) {
742 		convertObjectToProp(nullptr, &newSize, variableName, object);
743 
744 		if (systemVar) {
745 			if ((newSize + _systemUsed - existingSize) > _systemPartitionSize) {
746 				DEBUG_ERROR("No space left in system partition, need=%#x, _systemUsed=%#x, _systemPartitionSize=%#x\n",
747 				    newSize, _systemUsed, _systemPartitionSize);
748 				return kIOReturnNoSpace;
749 			} else {
750 				_systemUsed = _systemUsed + newSize - existingSize;
751 				_systemDict->setObject(variableName, object);
752 			}
753 		} else {
754 			if ((newSize + _commonUsed - existingSize) > _commonPartitionSize) {
755 				DEBUG_ERROR("No space left in common partition, need=%#x, _commonUsed=%#x, _commonPartitionSize=%#x\n",
756 				    newSize, _commonUsed, _commonPartitionSize);
757 				return kIOReturnNoSpace;
758 			} else {
759 				_commonUsed = _commonUsed + newSize - existingSize;
760 				_commonDict->setObject(variableName, object);
761 			}
762 		}
763 
764 		if (_provider->_diags) {
765 			_provider->_diags->logVariable(getPartitionTypeForGUID(varGuid), kIONVRAMOperationWrite, variableName, (void *)(uintptr_t)newSize);
766 		}
767 	} else {
768 		if (systemVar) {
769 			_systemDict->removeObject(variableName);
770 			_systemUsed -= existingSize;
771 		} else {
772 			_commonDict->removeObject(variableName);
773 			_commonUsed -= existingSize;
774 		}
775 
776 		if (_provider->_diags) {
777 			_provider->_diags->logVariable(getPartitionTypeForGUID(varGuid), kIONVRAMOperationDelete, variableName, nullptr);
778 		}
779 	}
780 
781 	if (_provider->_diags) {
782 		OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
783 		_provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
784 		DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
785 
786 		val = OSNumber::withNumber(getCommonUsed(), 32);
787 		_provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
788 		DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
789 	}
790 
791 	_newData = true;
792 
793 	return kIOReturnSuccess;
794 }
795 
796 IOReturn
setVariable(const uuid_t varGuid,const char * variableName,OSObject * object)797 IONVRAMCHRPHandler::setVariable(const uuid_t varGuid, const char *variableName, OSObject *object)
798 {
799 	bool systemVar = false;
800 
801 	if (_systemPartitionSize) {
802 		if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) {
803 			systemVar = true;
804 		}
805 	}
806 
807 	return setVariableInternal(varGuid, variableName, object, systemVar);
808 }
809 
810 uint32_t
findCurrentBank(uint32_t * gen)811 IONVRAMCHRPHandler::findCurrentBank(uint32_t *gen)
812 {
813 	struct apple_nvram_header storeHeader;
814 	uint32_t                  maxGen = 0;
815 	uint32_t                  currentBank = 0;
816 
817 	for (unsigned int i = 0; i < _bankCount; i++) {
818 		NVRAMVersion bankVer;
819 		uint32_t bankGen = 0;
820 
821 		_nvramController->select(i);
822 		_nvramController->read(0, (uint8_t *)&storeHeader, sizeof(storeHeader));
823 		bankVer = validateNVRAMVersion((uint8_t *)&storeHeader, sizeof(storeHeader), &bankGen);
824 
825 		if ((bankVer != kNVRAMVersionUnknown) && (bankGen >= maxGen)) {
826 			currentBank = i;
827 			maxGen = bankGen;
828 		}
829 	}
830 
831 	DEBUG_ALWAYS("currentBank=%#x, gen=%#x", currentBank, maxGen);
832 
833 	*gen = maxGen;
834 	return currentBank;
835 }
836 
837 bool
setController(IONVRAMController * controller)838 IONVRAMCHRPHandler::setController(IONVRAMController *controller)
839 {
840 	IOReturn ret;
841 
842 	if (_nvramController == NULL) {
843 		_nvramController = controller;
844 	}
845 
846 	DEBUG_INFO("Controller name: %s\n", _nvramController->getName());
847 
848 	ret = reloadInternal();
849 	if (ret != kIOReturnSuccess) {
850 		DEBUG_ERROR("reloadInternal failed, ret=0x%08x\n", ret);
851 	}
852 
853 	return true;
854 }
855 
856 bool
sync(void)857 IONVRAMCHRPHandler::sync(void)
858 {
859 	IOReturn ret;
860 
861 	if (_reload) {
862 		ret = reloadInternal();
863 		require_noerr_action(ret, exit, DEBUG_ERROR("Reload failed, ret=%#x", ret));
864 
865 		_reload = false;
866 	}
867 
868 	ret = serializeVariables();
869 	require_noerr_action(ret, exit, DEBUG_ERROR("serializeVariables failed, ret=%#x", ret));
870 
871 exit:
872 	return ret;
873 }
874 
875 uint32_t
getGeneration(void) const876 IONVRAMCHRPHandler::getGeneration(void) const
877 {
878 	return _generation;
879 }
880 
881 uint32_t
getVersion(void) const882 IONVRAMCHRPHandler::getVersion(void) const
883 {
884 	return _version;
885 }
886 
887 uint32_t
getSystemUsed(void) const888 IONVRAMCHRPHandler::getSystemUsed(void) const
889 {
890 	return _systemUsed;
891 }
892 
893 uint32_t
getCommonUsed(void) const894 IONVRAMCHRPHandler::getCommonUsed(void) const
895 {
896 	return _commonUsed;
897 }
898 
899 OSSharedPtr<OSData>
unescapeBytesToData(const uint8_t * bytes,uint32_t length)900 IONVRAMCHRPHandler::unescapeBytesToData(const uint8_t *bytes, uint32_t length)
901 {
902 	OSSharedPtr<OSData> data;
903 	uint32_t            totalLength = 0;
904 	uint32_t            offset, offset2;
905 	uint8_t             byte;
906 	bool                ok;
907 
908 	// Calculate the actual length of the data.
909 	ok = true;
910 	totalLength = 0;
911 	for (offset = 0; offset < length;) {
912 		byte = bytes[offset++];
913 		if (byte == 0xFF) {
914 			byte = bytes[offset++];
915 			if (byte == 0x00) {
916 				ok = false;
917 				break;
918 			}
919 			offset2 = byte & 0x7F;
920 		} else {
921 			offset2 = 1;
922 		}
923 		totalLength += offset2;
924 	}
925 
926 	if (ok) {
927 		// Create an empty OSData of the correct size.
928 		data = OSData::withCapacity(totalLength);
929 		if (data != nullptr) {
930 			for (offset = 0; offset < length;) {
931 				byte = bytes[offset++];
932 				if (byte == 0xFF) {
933 					byte = bytes[offset++];
934 					offset2 = byte & 0x7F;
935 					byte = (byte & 0x80) ? 0xFF : 0x00;
936 				} else {
937 					offset2 = 1;
938 				}
939 				data->appendByte(byte, offset2);
940 			}
941 		}
942 	}
943 
944 	return data;
945 }
946 
947 OSSharedPtr<OSData>
escapeDataToData(OSData * value)948 IONVRAMCHRPHandler::escapeDataToData(OSData * value)
949 {
950 	OSSharedPtr<OSData>         result;
951 	OSBoundedPtr<const uint8_t> startPtr;
952 	const uint8_t               *endPtr;
953 	const uint8_t               *valueBytesPtr;
954 	OSBoundedPtr<const uint8_t> wherePtr;
955 	uint8_t                     byte;
956 	bool                        ok = true;
957 
958 	valueBytesPtr = (const uint8_t *) value->getBytesNoCopy();
959 	endPtr = valueBytesPtr + value->getLength();
960 	wherePtr = OSBoundedPtr<const uint8_t>(valueBytesPtr, valueBytesPtr, endPtr);
961 
962 	result = OSData::withCapacity((unsigned int)(endPtr - wherePtr));
963 	if (!result) {
964 		return result;
965 	}
966 
967 	while (wherePtr < endPtr) {
968 		startPtr = wherePtr;
969 		byte = *wherePtr++;
970 		if ((byte == 0x00) || (byte == 0xFF)) {
971 			for (;
972 			    ((wherePtr - startPtr) < 0x7F) && (wherePtr < endPtr) && (byte == *wherePtr);
973 			    wherePtr++) {
974 			}
975 			ok &= result->appendByte(0xff, 1);
976 			byte = (byte & 0x80) | ((uint8_t)(wherePtr - startPtr));
977 		}
978 		ok &= result->appendByte(byte, 1);
979 	}
980 	ok &= result->appendByte(0, 1);
981 
982 	if (!ok) {
983 		result.reset();
984 	}
985 
986 	return result;
987 }
988 
989 bool
convertPropToObject(const uint8_t * propName,uint32_t propNameLength,const uint8_t * propData,uint32_t propDataLength,const OSSymbol ** propSymbol,OSObject ** propObject)990 IONVRAMCHRPHandler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength,
991     const uint8_t *propData, uint32_t propDataLength,
992     const OSSymbol **propSymbol,
993     OSObject **propObject)
994 {
995 	OSSharedPtr<const OSString> delimitedName;
996 	OSSharedPtr<const OSSymbol> tmpSymbol;
997 	OSSharedPtr<OSNumber>       tmpNumber;
998 	OSSharedPtr<OSString>       tmpString;
999 	OSSharedPtr<OSObject>       tmpObject = nullptr;
1000 
1001 	delimitedName = OSString::withCString((const char *)propName, propNameLength);
1002 	tmpSymbol = OSSymbol::withString(delimitedName.get());
1003 
1004 	if (tmpSymbol == nullptr) {
1005 		return false;
1006 	}
1007 
1008 	switch (getVariableType(tmpSymbol.get())) {
1009 	case kOFVariableTypeBoolean:
1010 		if (!strncmp("true", (const char *)propData, propDataLength)) {
1011 			tmpObject.reset(kOSBooleanTrue, OSRetain);
1012 		} else if (!strncmp("false", (const char *)propData, propDataLength)) {
1013 			tmpObject.reset(kOSBooleanFalse, OSRetain);
1014 		}
1015 		break;
1016 
1017 	case kOFVariableTypeNumber:
1018 		tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
1019 		if (tmpNumber != nullptr) {
1020 			tmpObject = tmpNumber;
1021 		}
1022 		break;
1023 
1024 	case kOFVariableTypeString:
1025 		tmpString = OSString::withCString((const char *)propData, propDataLength);
1026 		if (tmpString != nullptr) {
1027 			tmpObject = tmpString;
1028 		}
1029 		break;
1030 
1031 	case kOFVariableTypeData:
1032 		tmpObject = unescapeBytesToData(propData, propDataLength);
1033 		break;
1034 
1035 	default:
1036 		break;
1037 	}
1038 
1039 	if (tmpObject == nullptr) {
1040 		tmpSymbol.reset();
1041 		return false;
1042 	}
1043 
1044 	*propSymbol = tmpSymbol.detach();
1045 	*propObject = tmpObject.detach();
1046 
1047 	return true;
1048 }
1049 
1050 bool
convertPropToObject(const uint8_t * propName,uint32_t propNameLength,const uint8_t * propData,uint32_t propDataLength,OSSharedPtr<const OSSymbol> & propSymbol,OSSharedPtr<OSObject> & propObject)1051 IONVRAMCHRPHandler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength,
1052     const uint8_t *propData, uint32_t propDataLength,
1053     OSSharedPtr<const OSSymbol>& propSymbol,
1054     OSSharedPtr<OSObject>& propObject)
1055 {
1056 	const OSSymbol* propSymbolRaw = nullptr;
1057 	OSObject* propObjectRaw       = nullptr;
1058 
1059 	bool ok = convertPropToObject(propName, propNameLength, propData, propDataLength,
1060 	    &propSymbolRaw, &propObjectRaw);
1061 
1062 	propSymbol.reset(propSymbolRaw, OSNoRetain);
1063 	propObject.reset(propObjectRaw, OSNoRetain);
1064 
1065 	return ok;
1066 }
1067 
1068 bool
convertObjectToProp(uint8_t * buffer,uint32_t * length,const OSSymbol * propSymbol,OSObject * propObject)1069 IONVRAMCHRPHandler::convertObjectToProp(uint8_t *buffer, uint32_t *length,
1070     const OSSymbol *propSymbol, OSObject *propObject)
1071 {
1072 	return convertObjectToProp(buffer, length, propSymbol->getCStringNoCopy(), propObject);
1073 }
1074 
1075 bool
convertObjectToProp(uint8_t * buffer,uint32_t * length,const char * propName,OSObject * propObject)1076 IONVRAMCHRPHandler::convertObjectToProp(uint8_t *buffer, uint32_t *length,
1077     const char *propName, OSObject *propObject)
1078 {
1079 	uint32_t             propNameLength, propDataLength, remaining, offset;
1080 	IONVRAMVariableType  propType;
1081 	OSBoolean            *tmpBoolean = nullptr;
1082 	OSNumber             *tmpNumber = nullptr;
1083 	OSString             *tmpString = nullptr;
1084 	OSSharedPtr<OSData>  tmpData;
1085 
1086 	propNameLength = (uint32_t)strlen(propName);
1087 	propType = getVariableType(propName);
1088 	offset = 0;
1089 	remaining = 0;
1090 
1091 	// Get the size of the data.
1092 	propDataLength = 0xFFFFFFFF;
1093 	switch (propType) {
1094 	case kOFVariableTypeBoolean:
1095 		tmpBoolean = OSDynamicCast(OSBoolean, propObject);
1096 		if (tmpBoolean != nullptr) {
1097 			propDataLength = 5;
1098 		}
1099 		break;
1100 
1101 	case kOFVariableTypeNumber:
1102 		tmpNumber = OSDynamicCast(OSNumber, propObject);
1103 		if (tmpNumber != nullptr) {
1104 			propDataLength = 10;
1105 		}
1106 		break;
1107 
1108 	case kOFVariableTypeString:
1109 		tmpString = OSDynamicCast(OSString, propObject);
1110 		if (tmpString != nullptr) {
1111 			propDataLength = tmpString->getLength();
1112 		}
1113 		break;
1114 
1115 	case kOFVariableTypeData:
1116 		tmpData.reset(OSDynamicCast(OSData, propObject), OSNoRetain);
1117 		if (tmpData != nullptr) {
1118 			tmpData = escapeDataToData(tmpData.detach());
1119 			// escapeDataToData() adds the NULL byte to the data
1120 			// subtract 1 here to keep offset consistent with the other cases
1121 			propDataLength = tmpData->getLength() - 1;
1122 		}
1123 		break;
1124 
1125 	default:
1126 		break;
1127 	}
1128 
1129 	// Make sure the propertySize is known and will fit.
1130 	if (propDataLength == 0xFFFFFFFF) {
1131 		return false;
1132 	}
1133 
1134 	if (buffer) {
1135 		// name + '=' + data + '\0'
1136 		if ((propNameLength + propDataLength + 2) > *length) {
1137 			return false;
1138 		}
1139 
1140 		remaining = *length;
1141 	}
1142 
1143 	*length = 0;
1144 
1145 	// Copy the property name equal sign.
1146 	offset += snprintf((char *)buffer, remaining, "%s=", propName);
1147 	if (buffer) {
1148 		if (remaining > offset) {
1149 			buffer += offset;
1150 			remaining = remaining - offset;
1151 		} else {
1152 			return false;
1153 		}
1154 	}
1155 
1156 	switch (propType) {
1157 	case kOFVariableTypeBoolean:
1158 		if (tmpBoolean->getValue()) {
1159 			offset += strlcpy((char *)buffer, "true", remaining);
1160 		} else {
1161 			offset += strlcpy((char *)buffer, "false", remaining);
1162 		}
1163 		break;
1164 
1165 	case kOFVariableTypeNumber:
1166 	{
1167 		uint32_t tmpValue = tmpNumber->unsigned32BitValue();
1168 		if (tmpValue == 0xFFFFFFFF) {
1169 			offset += strlcpy((char *)buffer, "-1", remaining);
1170 		} else if (tmpValue < 1000) {
1171 			offset += snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
1172 		} else {
1173 			offset += snprintf((char *)buffer, remaining, "%#x", (uint32_t)tmpValue);
1174 		}
1175 	}
1176 	break;
1177 
1178 	case kOFVariableTypeString:
1179 		offset += strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
1180 		break;
1181 
1182 	case kOFVariableTypeData:
1183 		if (buffer) {
1184 			bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
1185 		}
1186 		tmpData.reset();
1187 		offset += propDataLength;
1188 		break;
1189 
1190 	default:
1191 		break;
1192 	}
1193 
1194 	*length = offset + 1;
1195 
1196 	return true;
1197 }
1198