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 * 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 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 167 nvram_get_adler(uint8_t *buf, int version) 168 { 169 return ((struct apple_nvram_header *)buf)->adler; 170 } 171 172 static uint32_t 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 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 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 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 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 283 IONVRAMCHRPHandler::~IONVRAMCHRPHandler() 284 { 285 } 286 287 bool 288 IONVRAMCHRPHandler::isValidImage(const uint8_t *image, IOByteCount length) 289 { 290 return validateNVRAMVersion(image, length, nullptr) != kNVRAMVersionUnknown; 291 } 292 293 IONVRAMCHRPHandler* 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 322 IONVRAMCHRPHandler::IONVRAMCHRPHandler(OSSharedPtr<OSDictionary> &varDict) : 323 _commonPartitionSize(0x800), 324 _varDict(varDict) 325 { 326 } 327 328 IOReturn 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 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 399 IONVRAMCHRPHandler::reload(void) 400 { 401 _reload = true; 402 403 DEBUG_INFO("reload marked\n"); 404 } 405 406 IOReturn 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 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 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 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 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 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 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 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 901 IONVRAMCHRPHandler::getGeneration(void) const 902 { 903 return _generation; 904 } 905 906 uint32_t 907 IONVRAMCHRPHandler::getVersion(void) const 908 { 909 return _version; 910 } 911 912 uint32_t 913 IONVRAMCHRPHandler::getSystemUsed(void) const 914 { 915 return _systemUsed; 916 } 917 918 uint32_t 919 IONVRAMCHRPHandler::getCommonUsed(void) const 920 { 921 return _commonUsed; 922 } 923 924 bool 925 IONVRAMCHRPHandler::getSystemPartitionActive(void) const 926 { 927 return _systemPartitionSize != 0; 928 } 929 930 OSSharedPtr<OSData> 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> 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 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 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 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 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