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