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