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/libkern.h> 30 31 #define VARIABLE_STORE_SIGNATURE 'NVV3' 32 33 // Variable Store Version 34 #define VARIABLE_STORE_VERSION 0x1 35 36 #define VARIABLE_DATA 0x55AA 37 #define INVALIDATED_VARIABLE_DATA 0x0000 38 39 // Variable State flags 40 #define VAR_IN_DELETED_TRANSITION 0xFE // Variable is in obsolete transistion 41 #define VAR_DELETED 0xFD // Variable is obsolete 42 #define VAR_INACTIVE 0xFB // Variable is inactive due to failing CRC 43 #define VAR_ADDED 0x7F // Variable has been completely added 44 45 // No changes needed on save 46 #define VAR_NEW_STATE_NONE 0x01 47 // Remove existing entry on save 48 #define VAR_NEW_STATE_REMOVE 0x02 49 // Add new value on save, mark previous as inactive 50 #define VAR_NEW_STATE_APPEND 0x03 51 52 #pragma pack(1) 53 struct v3_store_header { 54 uint32_t name; 55 uint32_t size; 56 uint32_t generation; 57 uint8_t state; 58 uint8_t flags; 59 uint8_t version; 60 uint8_t reserved1; 61 uint32_t system_size; 62 uint32_t common_size; 63 }; 64 65 struct v3_var_header { 66 uint16_t startId; 67 uint8_t state; 68 uint8_t reserved; 69 uint32_t attributes; 70 uint32_t nameSize; 71 uint32_t dataSize; 72 uuid_t guid; 73 uint32_t crc; 74 uint8_t name_data_buf[]; 75 }; 76 #pragma pack() 77 78 struct nvram_v3_var_entry { 79 uint8_t new_state; 80 size_t existing_offset; 81 struct v3_var_header header; 82 }; 83 84 static size_t 85 nvram_v3_var_container_size(const struct v3_var_header *header) 86 { 87 return sizeof(struct nvram_v3_var_entry) + header->nameSize + header->dataSize; 88 } 89 90 static size_t 91 variable_length(const struct v3_var_header *header) 92 { 93 return sizeof(struct v3_var_header) + header->nameSize + header->dataSize; 94 } 95 96 static bool 97 valid_store_header(const struct v3_store_header *header) 98 { 99 return (header->name == VARIABLE_STORE_SIGNATURE) && (header->version == VARIABLE_STORE_VERSION); 100 } 101 102 static bool 103 valid_variable_header(const struct v3_var_header *header, size_t buf_len) 104 { 105 return (buf_len > sizeof(struct v3_var_header)) && 106 (header->startId == VARIABLE_DATA) && 107 (variable_length(header) <= buf_len); 108 } 109 110 static uint32_t 111 find_active_var_in_image(const struct v3_var_header *var, const uint8_t *image, uint32_t offset, uint32_t len) 112 { 113 const struct v3_var_header *store_var; 114 uint32_t var_offset = 0; 115 116 while ((offset + sizeof(struct v3_var_header) < len)) { 117 store_var = (const struct v3_var_header *)(image + offset); 118 119 if (valid_variable_header(store_var, len - offset)) { 120 if ((store_var->state == VAR_ADDED) && 121 (uuid_compare(var->guid, store_var->guid) == 0) && 122 (var->nameSize == store_var->nameSize) && 123 (memcmp(var->name_data_buf, store_var->name_data_buf, var->nameSize) == 0)) { 124 var_offset = offset; 125 break; 126 } 127 } else { 128 break; 129 } 130 131 offset += variable_length(store_var); 132 } 133 134 return var_offset; 135 } 136 137 static IOReturn 138 find_current_offset_in_image(const uint8_t *image, uint32_t len, uint32_t *newOffset) 139 { 140 uint32_t offset = 0; 141 uint32_t inner_offset = 0; 142 143 if (valid_store_header((const struct v3_store_header *)(image + offset))) { 144 DEBUG_INFO("valid store header @ %#x\n", offset); 145 offset += sizeof(struct v3_store_header); 146 } 147 148 while (offset < len) { 149 const struct v3_var_header *store_var = (const struct v3_var_header *)(image + offset); 150 uuid_string_t uuidString; 151 152 if (valid_variable_header(store_var, len - offset)) { 153 uuid_unparse(store_var->guid, uuidString); 154 DEBUG_INFO("Valid var @ %#08x, state=%#02x, length=%#08zx, %s:%s\n", offset, store_var->state, 155 variable_length(store_var), uuidString, store_var->name_data_buf); 156 offset += variable_length(store_var); 157 } else { 158 break; 159 } 160 } 161 162 while (offset < len) { 163 if (image[offset] == 0xFF) { 164 DEBUG_INFO("scanning for clear memory @ %#x\n", offset); 165 166 inner_offset = offset; 167 168 while ((inner_offset < len) && (image[inner_offset] == 0xFF)) { 169 inner_offset++; 170 } 171 172 if (inner_offset == len) { 173 DEBUG_INFO("found start of clear mem @ %#x\n", offset); 174 break; 175 } else { 176 DEBUG_ERROR("ERROR!!!!! found non-clear byte @ %#x\n", offset); 177 return kIOReturnInvalid; 178 } 179 } 180 offset++; 181 } 182 183 *newOffset = offset; 184 185 return kIOReturnSuccess; 186 } 187 188 class IONVRAMV3Handler : public IODTNVRAMFormatHandler, IOTypedOperatorsMixin<IONVRAMV3Handler> 189 { 190 private: 191 IONVRAMController *_nvramController; 192 IODTNVRAM *_provider; 193 194 bool _newData; 195 bool _resetData; 196 bool _reload; 197 198 bool _rawController; 199 200 uint32_t _generation; 201 202 uint8_t *_nvramImage; 203 204 OSSharedPtr<OSDictionary> &_varDict; 205 206 uint32_t _commonSize; 207 uint32_t _systemSize; 208 209 uint32_t _commonUsed; 210 uint32_t _systemUsed; 211 212 uint32_t _currentOffset; 213 214 OSSharedPtr<OSArray> _varEntries; 215 216 IOReturn unserializeImage(const uint8_t *image, IOByteCount length); 217 IOReturn reclaim(void); 218 uint32_t findCurrentBank(void); 219 size_t getAppendSize(void); 220 221 static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const char *propSymbol, OSObject *propObject); 222 static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength, 223 OSSharedPtr<const OSSymbol>& propSymbol, OSSharedPtr<OSObject>& propObject); 224 225 IOReturn reloadInternal(void); 226 IOReturn setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object); 227 228 void setEntryForRemove(struct nvram_v3_var_entry *v3Entry, bool system); 229 void findExistingEntry(const uuid_t varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex); 230 IOReturn syncRaw(void); 231 IOReturn syncBlock(void); 232 IOReturn handleEphDM(void); 233 234 public: 235 virtual 236 ~IONVRAMV3Handler() APPLE_KEXT_OVERRIDE; 237 IONVRAMV3Handler(OSSharedPtr<OSDictionary> &varDict); 238 239 static bool isValidImage(const uint8_t *image, IOByteCount length); 240 241 static IONVRAMV3Handler *init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length, 242 OSSharedPtr<OSDictionary> &varDict); 243 244 virtual bool getNVRAMProperties(void) APPLE_KEXT_OVERRIDE; 245 virtual IOReturn unserializeVariables(void) APPLE_KEXT_OVERRIDE; 246 virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) APPLE_KEXT_OVERRIDE; 247 virtual bool setController(IONVRAMController *controller) APPLE_KEXT_OVERRIDE; 248 virtual IOReturn sync(void) APPLE_KEXT_OVERRIDE; 249 virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) APPLE_KEXT_OVERRIDE; 250 virtual void reload(void) APPLE_KEXT_OVERRIDE; 251 virtual uint32_t getGeneration(void) const APPLE_KEXT_OVERRIDE; 252 virtual uint32_t getVersion(void) const APPLE_KEXT_OVERRIDE; 253 virtual uint32_t getSystemUsed(void) const APPLE_KEXT_OVERRIDE; 254 virtual uint32_t getCommonUsed(void) const APPLE_KEXT_OVERRIDE; 255 virtual bool getSystemPartitionActive(void) const APPLE_KEXT_OVERRIDE; 256 }; 257 258 IONVRAMV3Handler::~IONVRAMV3Handler() 259 { 260 } 261 262 IONVRAMV3Handler::IONVRAMV3Handler(OSSharedPtr<OSDictionary> &varDict) : 263 _varDict(varDict) 264 { 265 } 266 267 bool 268 IONVRAMV3Handler::isValidImage(const uint8_t *image, IOByteCount length) 269 { 270 const struct v3_store_header *header = (const struct v3_store_header *)image; 271 272 if ((header == nullptr) || (length < sizeof(*header))) { 273 return false; 274 } 275 276 return valid_store_header(header); 277 } 278 279 IONVRAMV3Handler* 280 IONVRAMV3Handler::init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length, 281 OSSharedPtr<OSDictionary> &varDict) 282 { 283 OSSharedPtr<IORegistryEntry> entry; 284 OSSharedPtr<OSObject> prop; 285 bool propertiesOk; 286 287 IONVRAMV3Handler *handler = new IONVRAMV3Handler(varDict); 288 289 handler->_provider = provider; 290 291 propertiesOk = handler->getNVRAMProperties(); 292 require_action(propertiesOk, exit, DEBUG_ERROR("Unable to get NVRAM properties\n")); 293 294 require_action(length == handler->_bankSize, exit, DEBUG_ERROR("length %#llx != _bankSize %#x\n", length, handler->_bankSize)); 295 296 if ((image != nullptr) && (length != 0)) { 297 if (handler->unserializeImage(image, length) != kIOReturnSuccess) { 298 DEBUG_ERROR("Unable to unserialize image, len=%#x\n", (unsigned int)length); 299 } 300 } 301 302 return handler; 303 304 exit: 305 delete handler; 306 307 return nullptr; 308 } 309 310 bool 311 IONVRAMV3Handler::getNVRAMProperties() 312 { 313 bool ok = false; 314 const char *rawControllerKey = "nvram-raw"; 315 OSSharedPtr<IORegistryEntry> entry; 316 OSSharedPtr<OSObject> prop; 317 OSData * data; 318 319 require_action(IODTNVRAMFormatHandler::getNVRAMProperties(), exit, DEBUG_ERROR("parent getNVRAMProperties failed\n")); 320 321 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane); 322 require_action(entry, exit, DEBUG_ERROR("Unable to find chosen node\n")); 323 324 prop = entry->copyProperty(rawControllerKey); 325 require_action(prop != nullptr, exit, DEBUG_ERROR("No %s entry\n", rawControllerKey)); 326 327 data = OSDynamicCast(OSData, prop.get()); 328 require(data != nullptr, exit); 329 330 _rawController = *((uint32_t*)data->getBytesNoCopy()); 331 DEBUG_INFO("_rawController = %d\n", _rawController); 332 333 ok = true; 334 335 exit: 336 return ok; 337 } 338 339 IOReturn 340 IONVRAMV3Handler::flush(const uuid_t guid, IONVRAMOperation op) 341 { 342 IOReturn ret = kIOReturnSuccess; 343 bool flushSystem; 344 bool flushCommon; 345 346 flushSystem = getSystemPartitionActive() && (uuid_compare(guid, gAppleSystemVariableGuid) == 0); 347 flushCommon = uuid_compare(guid, gAppleNVRAMGuid) == 0; 348 349 DEBUG_INFO("flushSystem=%d, flushCommon=%d\n", flushSystem, flushCommon); 350 351 if (flushSystem || flushCommon) { 352 const OSSymbol *canonicalKey; 353 OSSharedPtr<OSDictionary> dictCopy; 354 OSSharedPtr<OSCollectionIterator> iter; 355 uuid_string_t uuidString; 356 357 dictCopy = OSDictionary::withDictionary(_varDict.get()); 358 iter = OSCollectionIterator::withCollection(dictCopy.get()); 359 require_action(dictCopy && iter, exit, ret = kIOReturnNoMemory); 360 361 while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) { 362 const char *varName; 363 uuid_t varGuid; 364 bool clear; 365 366 parseVariableName(canonicalKey->getCStringNoCopy(), &varGuid, &varName); 367 368 uuid_unparse(varGuid, uuidString); 369 370 clear = ((flushSystem && (uuid_compare(varGuid, gAppleSystemVariableGuid) == 0)) || 371 (flushCommon && (uuid_compare(varGuid, gAppleSystemVariableGuid) != 0))) && 372 verifyPermission(op, varGuid, varName, getSystemPartitionActive()); 373 374 if (clear) { 375 DEBUG_INFO("Clearing entry for %s:%s\n", uuidString, varName); 376 setVariableInternal(varGuid, varName, nullptr); 377 } else { 378 DEBUG_INFO("Keeping entry for %s:%s\n", uuidString, varName); 379 } 380 } 381 382 _newData = true; 383 } 384 385 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed); 386 387 exit: 388 return ret; 389 } 390 391 IOReturn 392 IONVRAMV3Handler::reloadInternal(void) 393 { 394 IOReturn ret; 395 uint32_t controllerBank; 396 uint8_t *controllerImage; 397 struct nvram_v3_var_entry *v3Entry; 398 const struct v3_store_header *storeHeader; 399 const struct v3_var_header *storeVar; 400 OSData *entryContainer; 401 402 controllerBank = findCurrentBank(); 403 404 if (_currentBank != controllerBank) { 405 DEBUG_ERROR("_currentBank %#x != controllerBank %#x", _currentBank, controllerBank); 406 } 407 408 _currentBank = controllerBank; 409 410 controllerImage = (uint8_t *)IOMallocData(_bankSize); 411 412 _nvramController->select(_currentBank); 413 _nvramController->read(0, controllerImage, _bankSize); 414 415 require_action(isValidImage(controllerImage, _bankSize), exit, 416 (ret = kIOReturnInvalid, DEBUG_ERROR("Invalid image at bank %d\n", _currentBank))); 417 418 DEBUG_INFO("valid image found\n"); 419 420 storeHeader = (const struct v3_store_header *)controllerImage; 421 422 _generation = storeHeader->generation; 423 424 // We must sync any existing variables offset on the controller image with our internal representation 425 // If we find an existing entry and the data is still the same we record the existing offset and mark it 426 // as VAR_NEW_STATE_NONE meaning no action needed 427 // Otherwise if the data is different or it is not found on the controller image we mark it as VAR_NEW_STATE_APPEND 428 // which will have us invalidate the existing entry if there is one and append it on the next save 429 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 430 uint32_t offset = sizeof(struct v3_store_header); 431 uint32_t latestOffset; 432 uint32_t prevOffset = 0; 433 434 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(i))); 435 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 436 437 DEBUG_INFO("Looking for %s\n", v3Entry->header.name_data_buf); 438 while ((latestOffset = find_active_var_in_image(&v3Entry->header, controllerImage, offset, _bankSize))) { 439 DEBUG_INFO("Found offset for %s @ %#08x\n", v3Entry->header.name_data_buf, latestOffset); 440 if (prevOffset) { 441 DEBUG_INFO("Marking prev offset for %s at %#08x invalid\n", v3Entry->header.name_data_buf, offset); 442 // Invalidate any previous duplicate entries in the store 443 struct v3_var_header *prevVarHeader = (struct v3_var_header *)(controllerImage + prevOffset); 444 uint8_t state = prevVarHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION; 445 446 ret = _nvramController->write(prevOffset + offsetof(struct v3_var_header, state), &state, sizeof(state)); 447 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret)); 448 } 449 450 prevOffset = latestOffset; 451 offset += latestOffset; 452 } 453 454 v3Entry->existing_offset = latestOffset ? latestOffset : prevOffset; 455 DEBUG_INFO("Existing offset for %s at %#08zx\n", v3Entry->header.name_data_buf, v3Entry->existing_offset); 456 457 if (v3Entry->existing_offset == 0) { 458 DEBUG_ERROR("%s is not in the NOR image\n", v3Entry->header.name_data_buf); 459 if (v3Entry->new_state != VAR_NEW_STATE_REMOVE) { 460 DEBUG_INFO("%s marked for append\n", v3Entry->header.name_data_buf); 461 // Doesn't exist in the store, just append it on next sync 462 v3Entry->new_state = VAR_NEW_STATE_APPEND; 463 } 464 } else { 465 DEBUG_INFO("Found offset for %s @ %#zx\n", v3Entry->header.name_data_buf, v3Entry->existing_offset); 466 storeVar = (const struct v3_var_header *)&controllerImage[v3Entry->existing_offset]; 467 468 if (v3Entry->new_state != VAR_NEW_STATE_REMOVE) { 469 // Verify that the existing data matches the store data 470 if ((variable_length(&v3Entry->header) == variable_length(storeVar)) && 471 (memcmp(v3Entry->header.name_data_buf, storeVar->name_data_buf, storeVar->nameSize + storeVar->dataSize) == 0)) { 472 DEBUG_INFO("Store var data for %s matches, marking new state none\n", v3Entry->header.name_data_buf); 473 v3Entry->new_state = VAR_NEW_STATE_NONE; 474 } else { 475 DEBUG_INFO("Store var data for %s differs, marking new state append\n", v3Entry->header.name_data_buf); 476 v3Entry->new_state = VAR_NEW_STATE_APPEND; 477 } 478 } else { 479 // Store has entry but it has been removed from our collection, keep it marked for delete but with updated 480 // existing_offset for coherence 481 DEBUG_INFO("Removing entry at %#08zx with next sync\n", v3Entry->existing_offset); 482 } 483 } 484 } 485 486 ret = find_current_offset_in_image(controllerImage, _bankSize, &_currentOffset); 487 if (ret != kIOReturnSuccess) { 488 DEBUG_ERROR("Unidentified bytes in image, reclaiming\n"); 489 ret = reclaim(); 490 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim byte recovery failed, invalid controller state!!! ret=%#x\n", ret)); 491 } 492 DEBUG_INFO("New _currentOffset=%#x\n", _currentOffset); 493 494 exit: 495 IOFreeData(controllerImage, _bankSize); 496 return ret; 497 } 498 499 void 500 IONVRAMV3Handler::reload(void) 501 { 502 _reload = true; 503 504 DEBUG_INFO("reload marked\n"); 505 } 506 507 void 508 IONVRAMV3Handler::setEntryForRemove(struct nvram_v3_var_entry *v3Entry, bool system) 509 { 510 OSSharedPtr<const OSSymbol> canonicalKey; 511 const char *variableName; 512 uint32_t variableSize; 513 514 require_action(v3Entry != nullptr, exit, DEBUG_INFO("remove with no entry\n")); 515 516 variableName = (const char *)v3Entry->header.name_data_buf; 517 variableSize = (uint32_t)variable_length(&v3Entry->header); 518 canonicalKey = keyWithGuidAndCString(v3Entry->header.guid, variableName); 519 520 if (v3Entry->new_state == VAR_NEW_STATE_REMOVE) { 521 DEBUG_INFO("entry %s already marked for remove\n", variableName); 522 } else { 523 DEBUG_INFO("marking entry %s for remove\n", variableName); 524 525 v3Entry->new_state = VAR_NEW_STATE_REMOVE; 526 527 _provider->_varDict->removeObject(canonicalKey.get()); 528 529 if (system) { 530 if (_systemUsed < variableSize) { 531 panic("Invalid _systemUsed size\n"); 532 } 533 _systemUsed -= variableSize; 534 } else { 535 if (_commonUsed < variableSize) { 536 panic("Invalid _commonUsed size\n"); 537 } 538 _commonUsed -= variableSize; 539 } 540 541 if (_provider->_diags) { 542 _provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid), 543 kIONVRAMOperationDelete, 544 variableName, 545 nullptr); 546 } 547 } 548 549 exit: 550 return; 551 } 552 553 void 554 IONVRAMV3Handler::findExistingEntry(const uuid_t varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex) 555 { 556 struct nvram_v3_var_entry *v3Entry = nullptr; 557 OSData *entryContainer = nullptr; 558 unsigned int index = 0; 559 uint32_t nameLen = (uint32_t)strlen(varName) + 1; 560 561 for (index = 0; index < _varEntries->getCount(); index++) { 562 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(index))); 563 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 564 565 if ((v3Entry->header.nameSize == nameLen) && 566 (memcmp(v3Entry->header.name_data_buf, varName, nameLen) == 0)) { 567 if (varGuid) { 568 if (uuid_compare(varGuid, v3Entry->header.guid) == 0) { 569 uuid_string_t uuidString; 570 uuid_unparse(varGuid, uuidString); 571 DEBUG_INFO("found existing entry for %s:%s, e_off=%#lx, len=%#lx, new_state=%#x\n", uuidString, varName, 572 v3Entry->existing_offset, variable_length(&v3Entry->header), v3Entry->new_state); 573 break; 574 } 575 } else { 576 DEBUG_INFO("found existing entry for %s, e_off=%#lx, len=%#lx\n", varName, v3Entry->existing_offset, variable_length(&v3Entry->header)); 577 break; 578 } 579 } 580 581 v3Entry = nullptr; 582 } 583 584 if (v3Entry != nullptr) { 585 if (existing) { 586 *existing = v3Entry; 587 } 588 589 if (existingIndex) { 590 *existingIndex = index; 591 } 592 } 593 } 594 595 IOReturn 596 IONVRAMV3Handler::unserializeImage(const uint8_t *image, IOByteCount length) 597 { 598 IOReturn ret = kIOReturnInvalid; 599 const struct v3_store_header *storeHeader; 600 601 require(isValidImage(image, length), exit); 602 603 storeHeader = (const struct v3_store_header *)image; 604 require_action(storeHeader->size == (uint32_t)length, exit, 605 DEBUG_ERROR("Image size %#x != header size %#x\n", (unsigned int)length, storeHeader->size)); 606 607 _generation = storeHeader->generation; 608 _systemSize = storeHeader->system_size; 609 _commonSize = storeHeader->common_size - sizeof(struct v3_store_header); 610 611 _systemUsed = 0; 612 _commonUsed = 0; 613 614 if (_nvramImage) { 615 IOFreeData(_nvramImage, _bankSize); 616 } 617 618 _varEntries.reset(); 619 _varEntries = OSArray::withCapacity(40); 620 621 _nvramImage = IONewData(uint8_t, length); 622 _bankSize = (uint32_t)length; 623 bcopy(image, _nvramImage, _bankSize); 624 625 ret = kIOReturnSuccess; 626 627 exit: 628 return ret; 629 } 630 631 typedef struct { 632 const char *name; 633 OSSharedPtr<OSObject> value; 634 } ephDMAllowListEntry; 635 636 static 637 ephDMAllowListEntry ephDMEntries[] = { 638 // Mobile Obliteration clears the following variables after it runs 639 { .name = "oblit-begins" }, 640 { .name = "orig-oblit" }, 641 { .name = "oblit-failure" }, 642 { .name = "oblit-inprogress" }, 643 { .name = "obliteration" }, 644 // darwin-init is used for configuring internal builds 645 { .name = "darwin-init" } 646 }; 647 648 IOReturn 649 IONVRAMV3Handler::handleEphDM(void) 650 { 651 OSSharedPtr<IORegistryEntry> entry; 652 OSData* data; 653 OSSharedPtr<OSObject> prop; 654 uint32_t ephDM = 0; 655 IOReturn ret = kIOReturnSuccess; 656 OSSharedPtr<const OSSymbol> canonicalKey; 657 uint32_t skip = 0; 658 659 // For ephemeral data mode, NVRAM needs to be cleared on every boot 660 // For system region supported targets, iBoot clears the system region 661 // For other targets, iBoot clears all the persistent variables 662 // So xnu only needs to clear the common region 663 entry = IORegistryEntry::fromPath("/product", gIODTPlane); 664 if (entry) { 665 prop = entry->copyProperty("ephemeral-data-mode"); 666 if (prop) { 667 data = OSDynamicCast(OSData, prop.get()); 668 if (data) { 669 ephDM = *((uint32_t *)data->getBytesNoCopy()); 670 } 671 } 672 } 673 674 require_action(ephDM != 0, exit, DEBUG_ALWAYS("ephemeral-data-mode not supported\n")); 675 require_action(_systemSize != 0, exit, DEBUG_ALWAYS("No system region, no need to clear\n")); 676 677 if (PE_parse_boot_argn("epdm-skip-nvram", &skip, sizeof(skip))) { 678 require_action(!(gInternalBuild && (skip == 1)), exit, DEBUG_ALWAYS("Internal build + epdm-skip-nvram set to true, skip nvram clearing\n")); 679 } 680 681 // Go through the allowlist and stash the values 682 for (uint32_t entry = 0; entry < ARRAY_SIZE(ephDMEntries); entry++) { 683 canonicalKey = keyWithGuidAndCString(gAppleNVRAMGuid, ephDMEntries[entry].name); 684 ephDMEntries[entry].value.reset(OSDynamicCast(OSData, _varDict->getObject(canonicalKey.get())), OSRetain); 685 } 686 687 DEBUG_ALWAYS("Obliterating common region\n"); 688 ret = flush(gAppleNVRAMGuid, kIONVRAMOperationObliterate); 689 require_noerr_action(ret, exit, DEBUG_ERROR("Flushing common region failed, ret=%#08x\n", ret)); 690 691 // Now write the allowlist variables back 692 for (uint32_t entry = 0; entry < ARRAY_SIZE(ephDMEntries); entry++) { 693 if (ephDMEntries[entry].value.get() == nullptr) { 694 continue; 695 } 696 ret = setVariableInternal(gAppleNVRAMGuid, ephDMEntries[entry].name, ephDMEntries[entry].value.get()); 697 require_noerr_action(ret, exit, DEBUG_ERROR("Setting allowlist variable %s failed, ret=%#08x\n", ephDMEntries[entry].name, ret)); 698 } 699 700 exit: 701 return ret; 702 } 703 704 IOReturn 705 IONVRAMV3Handler::unserializeVariables(void) 706 { 707 IOReturn ret = kIOReturnSuccess; 708 OSSharedPtr<const OSSymbol> propSymbol; 709 OSSharedPtr<OSObject> propObject; 710 OSSharedPtr<OSData> entryContainer; 711 struct nvram_v3_var_entry *v3Entry; 712 const struct v3_var_header *header; 713 size_t offset = sizeof(struct v3_store_header); 714 uint32_t crc; 715 unsigned int i; 716 bool system; 717 uuid_string_t uuidString; 718 size_t existingSize; 719 720 if (_systemSize || _commonSize) { 721 _varDict = OSDictionary::withCapacity(1); 722 } 723 724 while ((offset + sizeof(struct v3_var_header)) < _bankSize) { 725 struct nvram_v3_var_entry *existingEntry = nullptr; 726 unsigned int existingIndex = 0; 727 728 header = (const struct v3_var_header *)(_nvramImage + offset); 729 730 for (i = 0; i < sizeof(struct v3_var_header); i++) { 731 if ((_nvramImage[offset + i] != 0) && (_nvramImage[offset + i] != 0xFF)) { 732 break; 733 } 734 } 735 736 if (i == sizeof(struct v3_var_header)) { 737 DEBUG_INFO("No more variables after offset %#lx\n", offset); 738 break; 739 } 740 741 if (!valid_variable_header(header, _bankSize - offset)) { 742 DEBUG_ERROR("invalid header @ %#lx\n", offset); 743 offset += sizeof(struct v3_var_header); 744 continue; 745 } 746 747 uuid_unparse(header->guid, uuidString); 748 DEBUG_INFO("Valid var @ %#08zx, state=%#02x, length=%#08zx, %s:%s\n", offset, header->state, 749 variable_length(header), uuidString, header->name_data_buf); 750 751 if (header->state != VAR_ADDED) { 752 goto skip; 753 } 754 755 crc = crc32(0, header->name_data_buf + header->nameSize, header->dataSize); 756 757 if (crc != header->crc) { 758 DEBUG_ERROR("invalid crc @ %#lx, calculated=%#x, read=%#x\n", offset, crc, header->crc); 759 goto skip; 760 } 761 762 v3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(nvram_v3_var_container_size(header)); 763 __nochk_memcpy(&v3Entry->header, _nvramImage + offset, variable_length(header)); 764 765 // It is assumed that the initial image being unserialized here is going to be the proxy data from EDT and not the image 766 // read from the controller, which for various reasons due to the setting of states and saves from iBoot, can be 767 // different. We will have an initial existing_offset of 0 and once the controller is set we will read 768 // out the image there and update the existing offset with what is present on the NOR image 769 v3Entry->existing_offset = 0; 770 v3Entry->new_state = VAR_NEW_STATE_NONE; 771 772 // safe guard for any strange duplicate entries in the store 773 findExistingEntry(v3Entry->header.guid, (const char *)v3Entry->header.name_data_buf, &existingEntry, &existingIndex); 774 775 if (existingEntry != nullptr) { 776 existingSize = variable_length(&existingEntry->header); 777 778 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_container_size(header)); 779 _varEntries->replaceObject(existingIndex, entryContainer.get()); 780 781 DEBUG_INFO("Found existing for %s, resetting when controller available\n", v3Entry->header.name_data_buf); 782 _resetData = true; 783 } else { 784 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_container_size(header)); 785 _varEntries->setObject(entryContainer.get()); 786 existingSize = 0; 787 } 788 789 system = (_systemSize != 0) && (uuid_compare(v3Entry->header.guid, gAppleSystemVariableGuid) == 0); 790 if (system) { 791 _systemUsed = _systemUsed + (uint32_t)variable_length(header) - (uint32_t)existingSize; 792 } else { 793 _commonUsed = _commonUsed + (uint32_t)variable_length(header) - (uint32_t)existingSize; 794 } 795 796 if (convertPropToObject(v3Entry->header.name_data_buf, v3Entry->header.nameSize, 797 v3Entry->header.name_data_buf + v3Entry->header.nameSize, v3Entry->header.dataSize, 798 propSymbol, propObject)) { 799 OSSharedPtr<const OSSymbol> canonicalKey = keyWithGuidAndCString(v3Entry->header.guid, (const char *)v3Entry->header.name_data_buf); 800 801 DEBUG_INFO("adding %s, dataLength=%u, system=%d\n", 802 canonicalKey->getCStringNoCopy(), v3Entry->header.dataSize, system); 803 804 _varDict->setObject(canonicalKey.get(), propObject.get()); 805 806 if (_provider->_diags) { 807 _provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid), 808 kIONVRAMOperationInit, propSymbol.get()->getCStringNoCopy(), 809 (void *)(uintptr_t)(header->name_data_buf + header->nameSize)); 810 } 811 } 812 IOFreeData(v3Entry, nvram_v3_var_container_size(header)); 813 skip: 814 offset += variable_length(header); 815 } 816 817 _currentOffset = (uint32_t)offset; 818 819 DEBUG_ALWAYS("_commonSize %#x, _systemSize %#x, _currentOffset %#x\n", _commonSize, _systemSize, _currentOffset); 820 821 ret = handleEphDM(); 822 verify_noerr_action(ret, panic("handleEphDM failed with ret=%08x", ret)); 823 824 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed); 825 826 _newData = true; 827 828 if (_provider->_diags) { 829 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32); 830 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get()); 831 DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed()); 832 833 val = OSNumber::withNumber(getCommonUsed(), 32); 834 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get()); 835 DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed()); 836 } 837 838 return ret; 839 } 840 841 IOReturn 842 IONVRAMV3Handler::setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object) 843 { 844 struct nvram_v3_var_entry *v3Entry = nullptr; 845 struct nvram_v3_var_entry *newV3Entry; 846 OSSharedPtr<OSData> newContainer; 847 OSSharedPtr<const OSSymbol> canonicalKey; 848 bool unset = (object == nullptr); 849 bool system = false; 850 IOReturn ret = kIOReturnSuccess; 851 size_t entryNameLen = strlen(variableName) + 1; 852 unsigned int existingEntryIndex; 853 uint32_t dataSize = 0; 854 size_t existingVariableSize = 0; 855 size_t newVariableSize = 0; 856 size_t newEntrySize; 857 uuid_string_t uuidString; 858 859 system = (uuid_compare(varGuid, gAppleSystemVariableGuid) == 0); 860 canonicalKey = keyWithGuidAndCString(varGuid, variableName); 861 862 uuid_unparse(varGuid, uuidString); 863 DEBUG_INFO("setting %s:%s, system=%d, current var count=%u\n", uuidString, variableName, system, _varEntries->getCount()); 864 865 findExistingEntry(varGuid, variableName, &v3Entry, &existingEntryIndex); 866 867 if (unset == true) { 868 setEntryForRemove(v3Entry, system); 869 } else { 870 if ((v3Entry != nullptr) && (v3Entry->new_state != VAR_NEW_STATE_REMOVE)) { 871 // Sizing was subtracted in setEntryForRemove 872 existingVariableSize = variable_length(&v3Entry->header); 873 } 874 875 convertObjectToProp(nullptr, &dataSize, variableName, object); 876 877 newVariableSize = sizeof(struct v3_var_header) + entryNameLen + dataSize; 878 newEntrySize = sizeof(struct nvram_v3_var_entry) + entryNameLen + dataSize; 879 880 if (system) { 881 if (_systemUsed - existingVariableSize + newVariableSize > _systemSize) { 882 DEBUG_ERROR("system region full\n"); 883 ret = kIOReturnNoSpace; 884 goto exit; 885 } 886 } else if (_commonUsed - existingVariableSize + newVariableSize > _commonSize) { 887 DEBUG_ERROR("common region full\n"); 888 ret = kIOReturnNoSpace; 889 goto exit; 890 } 891 892 DEBUG_INFO("creating new entry for %s, existingVariableSize=%#zx, newVariableSize=%#zx\n", variableName, existingVariableSize, newVariableSize); 893 newV3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(newEntrySize); 894 895 memcpy(newV3Entry->header.name_data_buf, variableName, entryNameLen); 896 convertObjectToProp(newV3Entry->header.name_data_buf + entryNameLen, &dataSize, variableName, object); 897 898 newV3Entry->header.startId = VARIABLE_DATA; 899 newV3Entry->header.nameSize = (uint32_t)entryNameLen; 900 newV3Entry->header.dataSize = dataSize; 901 newV3Entry->header.crc = crc32(0, newV3Entry->header.name_data_buf + entryNameLen, dataSize); 902 memcpy(newV3Entry->header.guid, varGuid, sizeof(gAppleNVRAMGuid)); 903 newV3Entry->new_state = VAR_NEW_STATE_APPEND; 904 905 if (v3Entry) { 906 newV3Entry->existing_offset = v3Entry->existing_offset; 907 newV3Entry->header.state = v3Entry->header.state; 908 newV3Entry->header.attributes = v3Entry->header.attributes; 909 910 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize); 911 _varEntries->replaceObject(existingEntryIndex, newContainer.get()); 912 } else { 913 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize); 914 _varEntries->setObject(newContainer.get()); 915 } 916 917 if (system) { 918 _systemUsed = _systemUsed + (uint32_t)newVariableSize - (uint32_t)existingVariableSize; 919 } else { 920 _commonUsed = _commonUsed + (uint32_t)newVariableSize - (uint32_t)existingVariableSize; 921 } 922 923 _varDict->setObject(canonicalKey.get(), object); 924 925 if (_provider->_diags) { 926 _provider->_diags->logVariable(getPartitionTypeForGUID(varGuid), 927 kIONVRAMOperationWrite, variableName, 928 (void *)(uintptr_t)dataSize); 929 } 930 931 IOFreeData(newV3Entry, newEntrySize); 932 } 933 934 exit: 935 _newData = true; 936 937 if (_provider->_diags) { 938 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32); 939 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get()); 940 941 val = OSNumber::withNumber(getCommonUsed(), 32); 942 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get()); 943 } 944 945 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed); 946 947 return ret; 948 } 949 950 IOReturn 951 IONVRAMV3Handler::setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) 952 { 953 uuid_t destGuid; 954 955 if (strcmp(variableName, "reclaim-int") == 0) { 956 return reclaim(); 957 } 958 959 if (getSystemPartitionActive()) { 960 // System region case, if they're using the GUID directly or it's on the system allow list 961 // force it to use the System GUID 962 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) { 963 uuid_copy(destGuid, gAppleSystemVariableGuid); 964 } else { 965 uuid_copy(destGuid, varGuid); 966 } 967 } else { 968 // No system region, store System GUID as Common GUID 969 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) { 970 uuid_copy(destGuid, gAppleNVRAMGuid); 971 } else { 972 uuid_copy(destGuid, varGuid); 973 } 974 } 975 976 return setVariableInternal(destGuid, variableName, object); 977 } 978 979 uint32_t 980 IONVRAMV3Handler::findCurrentBank(void) 981 { 982 struct v3_store_header storeHeader; 983 uint32_t maxGen = 0; 984 uint32_t currentBank = 0; 985 986 for (unsigned int i = 0; i < _bankCount; i++) { 987 _nvramController->select(i); 988 _nvramController->read(0, (uint8_t *)&storeHeader, sizeof(storeHeader)); 989 990 if (valid_store_header(&storeHeader) && (storeHeader.generation >= maxGen)) { 991 currentBank = i; 992 maxGen = storeHeader.generation; 993 } 994 } 995 996 DEBUG_ALWAYS("currentBank=%#x, gen=%#x", currentBank, maxGen); 997 998 return currentBank; 999 } 1000 1001 bool 1002 IONVRAMV3Handler::setController(IONVRAMController *controller) 1003 { 1004 IOReturn ret = kIOReturnSuccess; 1005 1006 if (_nvramController == NULL) { 1007 _nvramController = controller; 1008 } 1009 1010 DEBUG_INFO("Controller name: %s\n", _nvramController->getName()); 1011 1012 require(_bankSize != 0, exit); 1013 1014 if (_resetData) { 1015 _resetData = false; 1016 DEBUG_ERROR("_resetData set, issuing reclaim recovery\n"); 1017 ret = reclaim(); 1018 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret)); 1019 goto exit; 1020 } 1021 1022 ret = reloadInternal(); 1023 if (ret != kIOReturnSuccess) { 1024 DEBUG_ERROR("Invalid image found, issuing reclaim recovery\n"); 1025 ret = reclaim(); 1026 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret)); 1027 } 1028 1029 exit: 1030 return ret == kIOReturnSuccess; 1031 } 1032 1033 IOReturn 1034 IONVRAMV3Handler::reclaim(void) 1035 { 1036 IOReturn ret; 1037 struct v3_store_header newStoreHeader; 1038 struct v3_var_header *varHeader; 1039 struct nvram_v3_var_entry *varEntry; 1040 OSData *entryContainer; 1041 size_t new_bank_offset = sizeof(struct v3_store_header); 1042 uint32_t next_bank = (_currentBank + 1) % _bankCount; 1043 uint8_t *bankData; 1044 OSSharedPtr<OSArray> remainingEntries; 1045 1046 DEBUG_INFO("called\n"); 1047 1048 bankData = (uint8_t *)IOMallocData(_bankSize); 1049 require_action(bankData != nullptr, exit, ret = kIOReturnNoMemory); 1050 1051 ret = _nvramController->select(next_bank); 1052 verify_noerr_action(ret, DEBUG_INFO("select of bank %#08x failed\n", next_bank)); 1053 1054 ret = _nvramController->eraseBank(); 1055 verify_noerr_action(ret, DEBUG_INFO("eraseBank failed, ret=%#08x\n", ret)); 1056 1057 _currentBank = next_bank; 1058 1059 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity()); 1060 1061 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 1062 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i)); 1063 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 1064 varHeader = &varEntry->header; 1065 1066 DEBUG_INFO("entry %u %s, new_state=%#x, e_offset=%#lx, state=%#x\n", 1067 i, varEntry->header.name_data_buf, varEntry->new_state, varEntry->existing_offset, varHeader->state); 1068 1069 if ((varEntry->new_state == VAR_NEW_STATE_NONE) || 1070 (varEntry->new_state == VAR_NEW_STATE_APPEND)) { 1071 varHeader->state = VAR_ADDED; 1072 1073 memcpy(bankData + new_bank_offset, (uint8_t *)varHeader, variable_length(varHeader)); 1074 1075 varEntry->new_state = VAR_NEW_STATE_NONE; 1076 varEntry->existing_offset = new_bank_offset; 1077 new_bank_offset += variable_length(varHeader); 1078 1079 remainingEntries->setObject(entryContainer); 1080 } else { 1081 // entryContainer not added to remainingEntries, entry dropped 1082 } 1083 } 1084 1085 memcpy(&newStoreHeader, _nvramImage, sizeof(newStoreHeader)); 1086 1087 _generation += 1; 1088 1089 newStoreHeader.generation = _generation; 1090 1091 memcpy(bankData, (uint8_t *)&newStoreHeader, sizeof(newStoreHeader)); 1092 1093 ret = _nvramController->write(0, bankData, new_bank_offset); 1094 require_noerr_action(ret, exit, DEBUG_ERROR("reclaim bank write failed, ret=%08x\n", ret)); 1095 1096 _currentOffset = (uint32_t)new_bank_offset; 1097 1098 DEBUG_INFO("Reclaim complete, _currentBank=%u _generation=%u, _currentOffset=%#x\n", _currentBank, _generation, _currentOffset); 1099 1100 _newData = false; 1101 1102 _varEntries.reset(remainingEntries.get(), OSRetain); 1103 1104 exit: 1105 IOFreeData(bankData, _bankSize); 1106 1107 return ret; 1108 } 1109 1110 size_t 1111 IONVRAMV3Handler::getAppendSize(void) 1112 { 1113 struct nvram_v3_var_entry *varEntry; 1114 struct v3_var_header *varHeader; 1115 OSData *entryContainer; 1116 size_t appendSize = 0; 1117 1118 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 1119 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i)); 1120 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 1121 varHeader = &varEntry->header; 1122 1123 if (varEntry->new_state == VAR_NEW_STATE_APPEND) { 1124 appendSize += variable_length(varHeader); 1125 } 1126 } 1127 1128 return appendSize; 1129 } 1130 1131 IOReturn 1132 IONVRAMV3Handler::syncRaw(void) 1133 { 1134 IOReturn ret = kIOReturnSuccess; 1135 struct nvram_v3_var_entry *varEntry; 1136 struct v3_var_header *varHeader; 1137 OSData *entryContainer; 1138 OSSharedPtr<OSArray> remainingEntries; 1139 uint8_t *appendBuffer = nullptr; 1140 size_t appendBufferOffset = 0; 1141 size_t *invalidateOffsets = nullptr; 1142 size_t invalidateOffsetsCount = 0; 1143 size_t invalidateOffsetIndex = 0; 1144 size_t invalidatedSize = 0; 1145 1146 require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n")); 1147 require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n")); 1148 require_action(_bankSize != 0, exit, DEBUG_INFO("No nvram size info\n")); 1149 1150 DEBUG_INFO("_varEntries->getCount()=%#x\n", _varEntries->getCount()); 1151 1152 if (getAppendSize() + _currentOffset < _bankSize) { 1153 // No reclaim, build append and invalidate list 1154 1155 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity()); 1156 1157 appendBuffer = (uint8_t *)IOMallocData(_bankSize); 1158 require_action(appendBuffer, exit, ret = kIOReturnNoMemory); 1159 1160 invalidateOffsetsCount = _varEntries->getCount(); 1161 invalidateOffsets = (size_t *)IOMallocData(invalidateOffsetsCount * sizeof(size_t)); 1162 require_action(invalidateOffsets, exit, ret = kIOReturnNoMemory); 1163 1164 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 1165 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i)); 1166 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 1167 varHeader = &varEntry->header; 1168 1169 DEBUG_INFO("entry %s, new_state=%#02x state=%#02x, existing_offset=%#zx\n", 1170 varEntry->header.name_data_buf, varEntry->new_state, varEntry->header.state, varEntry->existing_offset); 1171 1172 if (varEntry->new_state == VAR_NEW_STATE_APPEND) { 1173 size_t varSize = variable_length(varHeader); 1174 size_t prevOffset = varEntry->existing_offset; 1175 1176 varHeader->state = VAR_ADDED; 1177 varEntry->existing_offset = _currentOffset + appendBufferOffset; 1178 varEntry->new_state = VAR_NEW_STATE_NONE; 1179 1180 DEBUG_INFO("Appending %s in append buffer offset %#zx, actual offset %#zx, prevOffset %#zx, varsize=%#zx\n", 1181 varEntry->header.name_data_buf, appendBufferOffset, varEntry->existing_offset, prevOffset, varSize); 1182 1183 // Write to append buffer 1184 memcpy(appendBuffer + appendBufferOffset, (uint8_t *)varHeader, varSize); 1185 appendBufferOffset += varSize; 1186 1187 if (prevOffset) { 1188 invalidateOffsets[invalidateOffsetIndex++] = prevOffset; 1189 invalidatedSize += variable_length((struct v3_var_header *)prevOffset); 1190 } 1191 1192 remainingEntries->setObject(entryContainer); 1193 } else if (varEntry->new_state == VAR_NEW_STATE_REMOVE) { 1194 if (varEntry->existing_offset) { 1195 DEBUG_INFO("marking entry at offset %#lx deleted\n", varEntry->existing_offset); 1196 1197 invalidateOffsets[invalidateOffsetIndex++] = varEntry->existing_offset; 1198 invalidatedSize += variable_length((struct v3_var_header *)varEntry->existing_offset); 1199 } else { 1200 DEBUG_INFO("No existing_offset , removing\n"); 1201 } 1202 1203 // not re-added to remainingEntries 1204 } else { 1205 DEBUG_INFO("skipping\n"); 1206 remainingEntries->setObject(entryContainer); 1207 } 1208 } 1209 1210 if (appendBufferOffset > 0) { 1211 // Write appendBuffer 1212 DEBUG_INFO("Appending append buffer size=%#zx at offset=%#x\n", appendBufferOffset, _currentOffset); 1213 ret = _nvramController->write(_currentOffset, appendBuffer, appendBufferOffset); 1214 require_noerr_action(ret, exit, DEBUG_ERROR("could not re-append, ret=%#x\n", ret)); 1215 1216 _currentOffset += appendBufferOffset; 1217 } else { 1218 DEBUG_INFO("No entries to append\n"); 1219 } 1220 1221 if (invalidateOffsetIndex > 0) { 1222 // Invalidate Entries 1223 for (unsigned int i = 0; i < invalidateOffsetIndex; i++) { 1224 uint8_t state = VAR_ADDED & VAR_DELETED & VAR_IN_DELETED_TRANSITION; 1225 1226 ret = _nvramController->write(invalidateOffsets[i] + offsetof(struct v3_var_header, state), &state, sizeof(state)); 1227 require_noerr_action(ret, exit, DEBUG_ERROR("unable to invalidate at offset %#zx, ret=%#x\n", invalidateOffsets[i], ret)); 1228 DEBUG_INFO("Invalidated entry at offset=%#zx\n", invalidateOffsets[i]); 1229 } 1230 } else { 1231 DEBUG_INFO("No entries to invalidate\n"); 1232 } 1233 1234 _newData = false; 1235 1236 _varEntries.reset(remainingEntries.get(), OSRetain); 1237 } else { 1238 // Will need to reclaim, rebuild store and write everything at once 1239 ret = reclaim(); 1240 } 1241 1242 exit: 1243 IOFreeData(appendBuffer, _bankSize); 1244 IOFreeData(invalidateOffsets, invalidateOffsetsCount * sizeof(size_t)); 1245 1246 return ret; 1247 } 1248 1249 IOReturn 1250 IONVRAMV3Handler::syncBlock(void) 1251 { 1252 IOReturn ret = kIOReturnSuccess; 1253 struct v3_store_header newStoreHeader; 1254 struct v3_var_header *varHeader; 1255 struct nvram_v3_var_entry *varEntry; 1256 OSData *entryContainer; 1257 size_t new_bank_offset = sizeof(struct v3_store_header); 1258 uint8_t *block; 1259 OSSharedPtr<OSArray> remainingEntries; 1260 uint32_t next_bank = (_currentBank + 1) % _bankCount; 1261 1262 DEBUG_INFO("called\n"); 1263 1264 require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n")); 1265 require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n")); 1266 require_action(_bankSize != 0, exit, DEBUG_INFO("No nvram size info\n")); 1267 1268 block = (uint8_t *)IOMallocData(_bankSize); 1269 1270 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity()); 1271 1272 ret = _nvramController->select(next_bank); 1273 verify_noerr_action(ret, DEBUG_INFO("select of bank %#x failed\n", next_bank)); 1274 1275 ret = _nvramController->eraseBank(); 1276 verify_noerr_action(ret, DEBUG_INFO("eraseBank failed, ret=%#08x\n", ret)); 1277 1278 _currentBank = next_bank; 1279 1280 memcpy(&newStoreHeader, _nvramImage, sizeof(newStoreHeader)); 1281 1282 _generation += 1; 1283 1284 newStoreHeader.generation = _generation; 1285 1286 memcpy(block, (uint8_t *)&newStoreHeader, sizeof(newStoreHeader)); 1287 1288 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 1289 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i)); 1290 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 1291 varHeader = &varEntry->header; 1292 1293 DEBUG_INFO("entry %u %s, new_state=%#x, e_offset=%#lx, state=%#x\n", 1294 i, varEntry->header.name_data_buf, varEntry->new_state, varEntry->existing_offset, varHeader->state); 1295 1296 if (varEntry->new_state != VAR_NEW_STATE_REMOVE) { 1297 varHeader->state = VAR_ADDED; 1298 1299 memcpy(block + new_bank_offset, (uint8_t *)varHeader, variable_length(varHeader)); 1300 1301 varEntry->existing_offset = new_bank_offset; 1302 new_bank_offset += variable_length(varHeader); 1303 varEntry->new_state = VAR_NEW_STATE_NONE; 1304 1305 remainingEntries->setObject(entryContainer); 1306 } else { 1307 DEBUG_INFO("Dropping %s\n", varEntry->header.name_data_buf); 1308 } 1309 } 1310 1311 ret = _nvramController->write(0, block, _bankSize); 1312 verify_noerr_action(ret, DEBUG_ERROR("w fail, ret=%#x\n", ret)); 1313 1314 _nvramController->sync(); 1315 1316 _varEntries.reset(remainingEntries.get(), OSRetain); 1317 1318 _newData = false; 1319 1320 DEBUG_INFO("Save complete, _generation=%u\n", _generation); 1321 1322 IOFreeData(block, _bankSize); 1323 1324 exit: 1325 return ret; 1326 } 1327 1328 IOReturn 1329 IONVRAMV3Handler::sync(void) 1330 { 1331 IOReturn ret; 1332 1333 if (_reload) { 1334 ret = reloadInternal(); 1335 require_noerr_action(ret, exit, DEBUG_ERROR("Reload failed, ret=%#x", ret)); 1336 1337 _reload = false; 1338 } 1339 1340 if (_rawController == true) { 1341 ret = syncRaw(); 1342 1343 if (ret != kIOReturnSuccess) { 1344 ret = reclaim(); 1345 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, ret=%#x", ret)); 1346 } 1347 } else { 1348 ret = syncBlock(); 1349 } 1350 1351 exit: 1352 return ret; 1353 } 1354 1355 uint32_t 1356 IONVRAMV3Handler::getGeneration(void) const 1357 { 1358 return _generation; 1359 } 1360 1361 uint32_t 1362 IONVRAMV3Handler::getVersion(void) const 1363 { 1364 return kNVRAMVersion3; 1365 } 1366 1367 uint32_t 1368 IONVRAMV3Handler::getSystemUsed(void) const 1369 { 1370 return _systemUsed; 1371 } 1372 1373 uint32_t 1374 IONVRAMV3Handler::getCommonUsed(void) const 1375 { 1376 return _commonUsed; 1377 } 1378 1379 bool 1380 IONVRAMV3Handler::getSystemPartitionActive(void) const 1381 { 1382 return _systemSize != 0; 1383 } 1384 1385 bool 1386 IONVRAMV3Handler::convertObjectToProp(uint8_t *buffer, uint32_t *length, 1387 const char *propName, OSObject *propObject) 1388 { 1389 uint32_t offset; 1390 IONVRAMVariableType propType; 1391 OSBoolean *tmpBoolean = nullptr; 1392 OSNumber *tmpNumber = nullptr; 1393 OSString *tmpString = nullptr; 1394 OSData *tmpData = nullptr; 1395 1396 propType = getVariableType(propName); 1397 1398 // Get the size of the data. 1399 offset = 0; 1400 switch (propType) { 1401 case kOFVariableTypeBoolean: 1402 tmpBoolean = OSDynamicCast(OSBoolean, propObject); 1403 if (tmpBoolean != nullptr) { 1404 const char *bool_buf; 1405 if (tmpBoolean->getValue()) { 1406 bool_buf = "true"; 1407 } else { 1408 bool_buf = "false"; 1409 } 1410 1411 offset = (uint32_t)strlen(bool_buf); 1412 1413 if (buffer) { 1414 if (*length < offset) { 1415 return false; 1416 } else { 1417 memcpy(buffer, bool_buf, offset); 1418 } 1419 } 1420 } 1421 break; 1422 1423 case kOFVariableTypeNumber: 1424 tmpNumber = OSDynamicCast(OSNumber, propObject); 1425 if (tmpNumber != nullptr) { 1426 char num_buf[12]; 1427 char *end_buf = num_buf; 1428 uint32_t tmpValue = tmpNumber->unsigned32BitValue(); 1429 if (tmpValue == 0xFFFFFFFF) { 1430 end_buf += snprintf(end_buf, sizeof(num_buf), "-1"); 1431 } else if (tmpValue < 1000) { 1432 end_buf += snprintf(end_buf, sizeof(num_buf), "%d", (uint32_t)tmpValue); 1433 } else { 1434 end_buf += snprintf(end_buf, sizeof(num_buf), "%#x", (uint32_t)tmpValue); 1435 } 1436 1437 offset = (uint32_t)(end_buf - num_buf); 1438 if (buffer) { 1439 if (*length < offset) { 1440 return false; 1441 } else { 1442 memcpy(buffer, num_buf, offset); 1443 } 1444 } 1445 } 1446 break; 1447 1448 case kOFVariableTypeString: 1449 tmpString = OSDynamicCast(OSString, propObject); 1450 if (tmpString != nullptr) { 1451 offset = tmpString->getLength(); 1452 1453 if (buffer) { 1454 if (*length < offset) { 1455 return false; 1456 } else { 1457 bcopy(tmpString->getCStringNoCopy(), buffer, offset); 1458 } 1459 } 1460 } 1461 break; 1462 1463 case kOFVariableTypeData: 1464 tmpData = OSDynamicCast(OSData, propObject); 1465 if (tmpData != nullptr) { 1466 offset = tmpData->getLength(); 1467 1468 if (buffer) { 1469 if (*length < offset) { 1470 return false; 1471 } else { 1472 bcopy(tmpData->getBytesNoCopy(), buffer, offset); 1473 } 1474 } 1475 } 1476 break; 1477 1478 default: 1479 return false; 1480 } 1481 1482 *length = offset; 1483 1484 return offset != 0; 1485 } 1486 1487 1488 bool 1489 IONVRAMV3Handler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength, 1490 const uint8_t *propData, uint32_t propDataLength, 1491 OSSharedPtr<const OSSymbol>& propSymbol, 1492 OSSharedPtr<OSObject>& propObject) 1493 { 1494 OSSharedPtr<const OSSymbol> tmpSymbol; 1495 OSSharedPtr<OSNumber> tmpNumber; 1496 OSSharedPtr<OSString> tmpString; 1497 OSSharedPtr<OSObject> tmpObject = nullptr; 1498 1499 tmpSymbol = OSSymbol::withCString((const char *)propName); 1500 1501 if (tmpSymbol == nullptr) { 1502 return false; 1503 } 1504 1505 switch (getVariableType(tmpSymbol.get())) { 1506 case kOFVariableTypeBoolean: 1507 if (!strncmp("true", (const char *)propData, propDataLength)) { 1508 tmpObject.reset(kOSBooleanTrue, OSRetain); 1509 } else if (!strncmp("false", (const char *)propData, propDataLength)) { 1510 tmpObject.reset(kOSBooleanFalse, OSRetain); 1511 } 1512 break; 1513 1514 case kOFVariableTypeNumber: 1515 tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32); 1516 if (tmpNumber != nullptr) { 1517 tmpObject = tmpNumber; 1518 } 1519 break; 1520 1521 case kOFVariableTypeString: 1522 tmpString = OSString::withCString((const char *)propData, propDataLength); 1523 if (tmpString != nullptr) { 1524 tmpObject = tmpString; 1525 } 1526 break; 1527 1528 case kOFVariableTypeData: 1529 tmpObject = OSData::withBytes(propData, propDataLength); 1530 break; 1531 1532 default: 1533 break; 1534 } 1535 1536 if (tmpObject == nullptr) { 1537 tmpSymbol.reset(); 1538 return false; 1539 } 1540 1541 propSymbol = tmpSymbol; 1542 propObject = tmpObject; 1543 1544 return true; 1545 } 1546