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