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