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