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