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