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