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 220 static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const char *propSymbol, OSObject *propObject); 221 static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength, 222 OSSharedPtr<const OSSymbol>& propSymbol, OSSharedPtr<OSObject>& propObject); 223 224 IOReturn reloadInternal(void); 225 IOReturn setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object); 226 227 void setEntryForRemove(struct nvram_v3_var_entry *v3Entry, bool system); 228 void findExistingEntry(const uuid_t varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex); 229 IOReturn syncRaw(void); 230 IOReturn syncBlock(void); 231 232 public: 233 virtual 234 ~IONVRAMV3Handler() APPLE_KEXT_OVERRIDE; 235 IONVRAMV3Handler(OSSharedPtr<OSDictionary> &varDict); 236 237 static bool isValidImage(const uint8_t *image, IOByteCount length); 238 239 static IONVRAMV3Handler *init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length, 240 OSSharedPtr<OSDictionary> &varDict); 241 242 virtual bool getNVRAMProperties(void) APPLE_KEXT_OVERRIDE; 243 virtual IOReturn unserializeVariables(void) APPLE_KEXT_OVERRIDE; 244 virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) APPLE_KEXT_OVERRIDE; 245 virtual bool setController(IONVRAMController *controller) APPLE_KEXT_OVERRIDE; 246 virtual bool sync(void) APPLE_KEXT_OVERRIDE; 247 virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) APPLE_KEXT_OVERRIDE; 248 virtual void reload(void) APPLE_KEXT_OVERRIDE; 249 virtual uint32_t getGeneration(void) const APPLE_KEXT_OVERRIDE; 250 virtual uint32_t getVersion(void) const APPLE_KEXT_OVERRIDE; 251 virtual uint32_t getSystemUsed(void) const APPLE_KEXT_OVERRIDE; 252 virtual uint32_t getCommonUsed(void) const APPLE_KEXT_OVERRIDE; 253 virtual bool getSystemPartitionActive(void) const APPLE_KEXT_OVERRIDE; 254 }; 255 256 IONVRAMV3Handler::~IONVRAMV3Handler() 257 { 258 } 259 260 IONVRAMV3Handler::IONVRAMV3Handler(OSSharedPtr<OSDictionary> &varDict) : 261 _varDict(varDict) 262 { 263 } 264 265 bool 266 IONVRAMV3Handler::isValidImage(const uint8_t *image, IOByteCount length) 267 { 268 const struct v3_store_header *header = (const struct v3_store_header *)image; 269 270 if ((header == nullptr) || (length < sizeof(*header))) { 271 return false; 272 } 273 274 return valid_store_header(header); 275 } 276 277 IONVRAMV3Handler* 278 IONVRAMV3Handler::init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length, 279 OSSharedPtr<OSDictionary> &varDict) 280 { 281 OSSharedPtr<IORegistryEntry> entry; 282 OSSharedPtr<OSObject> prop; 283 bool propertiesOk; 284 285 IONVRAMV3Handler *handler = new IONVRAMV3Handler(varDict); 286 287 handler->_provider = provider; 288 289 propertiesOk = handler->getNVRAMProperties(); 290 require_action(propertiesOk, exit, DEBUG_ERROR("Unable to get NVRAM properties\n")); 291 292 require_action(length == handler->_bankSize, exit, DEBUG_ERROR("length %#llx != _bankSize %#x\n", length, handler->_bankSize)); 293 294 if ((image != nullptr) && (length != 0)) { 295 if (handler->unserializeImage(image, length) != kIOReturnSuccess) { 296 DEBUG_ERROR("Unable to unserialize image, len=%#x\n", (unsigned int)length); 297 } 298 } 299 300 return handler; 301 302 exit: 303 delete handler; 304 305 return nullptr; 306 } 307 308 bool 309 IONVRAMV3Handler::getNVRAMProperties() 310 { 311 bool ok = false; 312 const char *rawControllerKey = "nvram-raw"; 313 OSSharedPtr<IORegistryEntry> entry; 314 OSSharedPtr<OSObject> prop; 315 OSData * data; 316 317 require_action(IODTNVRAMFormatHandler::getNVRAMProperties(), exit, DEBUG_ERROR("parent getNVRAMProperties failed\n")); 318 319 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane); 320 require_action(entry, exit, DEBUG_ERROR("Unable to find chosen node\n")); 321 322 prop = entry->copyProperty(rawControllerKey); 323 require_action(prop != nullptr, exit, DEBUG_ERROR("No %s entry\n", rawControllerKey)); 324 325 data = OSDynamicCast(OSData, prop.get()); 326 require(data != nullptr, exit); 327 328 _rawController = *((uint32_t*)data->getBytesNoCopy()); 329 DEBUG_INFO("_rawController = %d\n", _rawController); 330 331 ok = true; 332 333 exit: 334 return ok; 335 } 336 337 IOReturn 338 IONVRAMV3Handler::flush(const uuid_t guid, IONVRAMOperation op) 339 { 340 IOReturn ret = kIOReturnSuccess; 341 bool flushSystem; 342 bool flushCommon; 343 344 flushSystem = getSystemPartitionActive() && (uuid_compare(guid, gAppleSystemVariableGuid) == 0); 345 flushCommon = uuid_compare(guid, gAppleNVRAMGuid) == 0; 346 347 DEBUG_INFO("flushSystem=%d, flushCommon=%d\n", flushSystem, flushCommon); 348 349 if (flushSystem || flushCommon) { 350 const OSSymbol *canonicalKey; 351 OSSharedPtr<OSDictionary> dictCopy; 352 OSSharedPtr<OSCollectionIterator> iter; 353 uuid_string_t uuidString; 354 355 dictCopy = OSDictionary::withDictionary(_varDict.get()); 356 iter = OSCollectionIterator::withCollection(dictCopy.get()); 357 require_action(dictCopy && iter, exit, ret = kIOReturnNoMemory); 358 359 while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) { 360 const char *varName; 361 uuid_t varGuid; 362 bool clear; 363 364 parseVariableName(canonicalKey->getCStringNoCopy(), &varGuid, &varName); 365 366 uuid_unparse(varGuid, uuidString); 367 368 clear = ((flushSystem && (uuid_compare(varGuid, gAppleSystemVariableGuid) == 0)) || 369 (flushCommon && (uuid_compare(varGuid, gAppleSystemVariableGuid) != 0))) && 370 verifyPermission(op, varGuid, varName, getSystemPartitionActive()); 371 372 if (clear) { 373 DEBUG_INFO("Clearing entry for %s:%s\n", uuidString, varName); 374 setVariableInternal(varGuid, varName, nullptr); 375 } else { 376 DEBUG_INFO("Keeping entry for %s:%s\n", uuidString, varName); 377 } 378 } 379 380 _newData = true; 381 } 382 383 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed); 384 385 exit: 386 return ret; 387 } 388 389 IOReturn 390 IONVRAMV3Handler::reloadInternal(void) 391 { 392 IOReturn ret; 393 uint32_t controllerBank; 394 uint8_t *controllerImage; 395 struct nvram_v3_var_entry *v3Entry; 396 const struct v3_store_header *storeHeader; 397 const struct v3_var_header *storeVar; 398 OSData *entryContainer; 399 400 controllerBank = findCurrentBank(); 401 402 if (_currentBank != controllerBank) { 403 DEBUG_ERROR("_currentBank %#x != controllerBank %#x", _currentBank, controllerBank); 404 } 405 406 _currentBank = controllerBank; 407 408 controllerImage = (uint8_t *)IOMallocData(_bankSize); 409 410 _nvramController->select(_currentBank); 411 _nvramController->read(0, controllerImage, _bankSize); 412 413 require_action(isValidImage(controllerImage, _bankSize), exit, 414 (ret = kIOReturnInvalid, DEBUG_ERROR("Invalid image at bank %d\n", _currentBank))); 415 416 DEBUG_INFO("valid image found\n"); 417 418 storeHeader = (const struct v3_store_header *)controllerImage; 419 420 _generation = storeHeader->generation; 421 422 // We must sync any existing variables offset on the controller image with our internal representation 423 // If we find an existing entry and the data is still the same we record the existing offset and mark it 424 // as VAR_NEW_STATE_NONE meaning no action needed 425 // Otherwise if the data is different or it is not found on the controller image we mark it as VAR_NEW_STATE_APPEND 426 // which will have us invalidate the existing entry if there is one and append it on the next save 427 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 428 uint32_t offset = sizeof(struct v3_store_header); 429 uint32_t latestOffset; 430 uint32_t prevOffset = 0; 431 432 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(i))); 433 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 434 435 DEBUG_INFO("Looking for %s\n", v3Entry->header.name_data_buf); 436 while ((latestOffset = find_active_var_in_image(&v3Entry->header, controllerImage, offset, _bankSize))) { 437 DEBUG_INFO("Found offset for %s @ %#08x\n", v3Entry->header.name_data_buf, latestOffset); 438 if (prevOffset) { 439 DEBUG_INFO("Marking prev offset for %s at %#08x invalid\n", v3Entry->header.name_data_buf, offset); 440 // Invalidate any previous duplicate entries in the store 441 struct v3_var_header *prevVarHeader = (struct v3_var_header *)(controllerImage + prevOffset); 442 uint8_t state = prevVarHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION; 443 444 ret = _nvramController->write(prevOffset + offsetof(struct v3_var_header, state), &state, sizeof(state)); 445 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret)); 446 } 447 448 prevOffset = latestOffset; 449 offset += latestOffset; 450 } 451 452 v3Entry->existing_offset = latestOffset ? latestOffset : prevOffset; 453 DEBUG_INFO("Existing offset for %s at %#08zx\n", v3Entry->header.name_data_buf, v3Entry->existing_offset); 454 455 if (v3Entry->existing_offset == 0) { 456 DEBUG_ERROR("%s is not in the NOR image\n", v3Entry->header.name_data_buf); 457 if (v3Entry->new_state != VAR_NEW_STATE_REMOVE) { 458 DEBUG_INFO("%s marked for append\n", v3Entry->header.name_data_buf); 459 // Doesn't exist in the store, just append it on next sync 460 v3Entry->new_state = VAR_NEW_STATE_APPEND; 461 } 462 } else { 463 DEBUG_INFO("Found offset for %s @ %#zx\n", v3Entry->header.name_data_buf, v3Entry->existing_offset); 464 storeVar = (const struct v3_var_header *)&controllerImage[v3Entry->existing_offset]; 465 466 if (v3Entry->new_state != VAR_NEW_STATE_REMOVE) { 467 // Verify that the existing data matches the store data 468 if ((variable_length(&v3Entry->header) == variable_length(storeVar)) && 469 (memcmp(v3Entry->header.name_data_buf, storeVar->name_data_buf, storeVar->nameSize + storeVar->dataSize) == 0)) { 470 DEBUG_INFO("Store var data for %s matches, marking new state none\n", v3Entry->header.name_data_buf); 471 v3Entry->new_state = VAR_NEW_STATE_NONE; 472 } else { 473 DEBUG_INFO("Store var data for %s differs, marking new state append\n", v3Entry->header.name_data_buf); 474 v3Entry->new_state = VAR_NEW_STATE_APPEND; 475 } 476 } else { 477 // Store has entry but it has been removed from our collection, keep it marked for delete but with updated 478 // existing_offset for coherence 479 DEBUG_INFO("Removing entry at %#08zx with next sync\n", v3Entry->existing_offset); 480 } 481 } 482 } 483 484 ret = find_current_offset_in_image(controllerImage, _bankSize, &_currentOffset); 485 if (ret != kIOReturnSuccess) { 486 DEBUG_ERROR("Unidentified bytes in image, reclaiming\n"); 487 ret = reclaim(); 488 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim byte recovery failed, invalid controller state!!! ret=%#x\n", ret)); 489 } 490 DEBUG_INFO("New _currentOffset=%#x\n", _currentOffset); 491 492 exit: 493 IOFreeData(controllerImage, _bankSize); 494 return ret; 495 } 496 497 void 498 IONVRAMV3Handler::reload(void) 499 { 500 _reload = true; 501 502 DEBUG_INFO("reload marked\n"); 503 } 504 505 void 506 IONVRAMV3Handler::setEntryForRemove(struct nvram_v3_var_entry *v3Entry, bool system) 507 { 508 OSSharedPtr<const OSSymbol> canonicalKey; 509 const char *variableName; 510 uint32_t variableSize; 511 512 require_action(v3Entry != nullptr, exit, DEBUG_INFO("remove with no entry\n")); 513 514 variableName = (const char *)v3Entry->header.name_data_buf; 515 variableSize = (uint32_t)variable_length(&v3Entry->header); 516 canonicalKey = keyWithGuidAndCString(v3Entry->header.guid, variableName); 517 518 if (v3Entry->new_state == VAR_NEW_STATE_REMOVE) { 519 DEBUG_INFO("entry %s already marked for remove\n", variableName); 520 } else { 521 DEBUG_INFO("marking entry %s for remove\n", variableName); 522 523 v3Entry->new_state = VAR_NEW_STATE_REMOVE; 524 525 _provider->_varDict->removeObject(canonicalKey.get()); 526 527 if (system) { 528 if (_systemUsed < variableSize) { 529 panic("Invalid _systemUsed size\n"); 530 } 531 _systemUsed -= variableSize; 532 } else { 533 if (_commonUsed < variableSize) { 534 panic("Invalid _commonUsed size\n"); 535 } 536 _commonUsed -= variableSize; 537 } 538 539 if (_provider->_diags) { 540 _provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid), 541 kIONVRAMOperationDelete, 542 variableName, 543 nullptr); 544 } 545 } 546 547 exit: 548 return; 549 } 550 551 void 552 IONVRAMV3Handler::findExistingEntry(const uuid_t varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex) 553 { 554 struct nvram_v3_var_entry *v3Entry = nullptr; 555 OSData *entryContainer = nullptr; 556 unsigned int index = 0; 557 uint32_t nameLen = (uint32_t)strlen(varName) + 1; 558 559 for (index = 0; index < _varEntries->getCount(); index++) { 560 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(index))); 561 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 562 563 if ((v3Entry->header.nameSize == nameLen) && 564 (memcmp(v3Entry->header.name_data_buf, varName, nameLen) == 0)) { 565 if (varGuid) { 566 if (uuid_compare(varGuid, v3Entry->header.guid) == 0) { 567 uuid_string_t uuidString; 568 uuid_unparse(varGuid, uuidString); 569 DEBUG_INFO("found existing entry for %s:%s, e_off=%#lx, len=%#lx, new_state=%#x\n", uuidString, varName, 570 v3Entry->existing_offset, variable_length(&v3Entry->header), v3Entry->new_state); 571 break; 572 } 573 } else { 574 DEBUG_INFO("found existing entry for %s, e_off=%#lx, len=%#lx\n", varName, v3Entry->existing_offset, variable_length(&v3Entry->header)); 575 break; 576 } 577 } 578 579 v3Entry = nullptr; 580 } 581 582 if (v3Entry != nullptr) { 583 if (existing) { 584 *existing = v3Entry; 585 } 586 587 if (existingIndex) { 588 *existingIndex = index; 589 } 590 } 591 } 592 593 IOReturn 594 IONVRAMV3Handler::unserializeImage(const uint8_t *image, IOByteCount length) 595 { 596 IOReturn ret = kIOReturnInvalid; 597 const struct v3_store_header *storeHeader; 598 599 require(isValidImage(image, length), exit); 600 601 storeHeader = (const struct v3_store_header *)image; 602 require_action(storeHeader->size == (uint32_t)length, exit, 603 DEBUG_ERROR("Image size %#x != header size %#x\n", (unsigned int)length, storeHeader->size)); 604 605 _generation = storeHeader->generation; 606 _systemSize = storeHeader->system_size; 607 _commonSize = storeHeader->common_size - sizeof(struct v3_store_header); 608 609 _systemUsed = 0; 610 _commonUsed = 0; 611 612 if (_nvramImage) { 613 IOFreeData(_nvramImage, _bankSize); 614 } 615 616 _varEntries.reset(); 617 _varEntries = OSArray::withCapacity(40); 618 619 _nvramImage = IONewData(uint8_t, length); 620 _bankSize = (uint32_t)length; 621 bcopy(image, _nvramImage, _bankSize); 622 623 ret = kIOReturnSuccess; 624 625 exit: 626 return ret; 627 } 628 629 IOReturn 630 IONVRAMV3Handler::unserializeVariables(void) 631 { 632 IOReturn ret = kIOReturnSuccess; 633 OSSharedPtr<const OSSymbol> propSymbol; 634 OSSharedPtr<OSObject> propObject; 635 OSSharedPtr<OSData> entryContainer; 636 struct nvram_v3_var_entry *v3Entry; 637 const struct v3_var_header *header; 638 size_t offset = sizeof(struct v3_store_header); 639 uint32_t crc; 640 unsigned int i; 641 bool system; 642 uuid_string_t uuidString; 643 size_t existingSize; 644 645 if (_systemSize || _commonSize) { 646 _varDict = OSDictionary::withCapacity(1); 647 } 648 649 while ((offset + sizeof(struct v3_var_header)) < _bankSize) { 650 struct nvram_v3_var_entry *existingEntry = nullptr; 651 unsigned int existingIndex = 0; 652 653 header = (const struct v3_var_header *)(_nvramImage + offset); 654 655 for (i = 0; i < sizeof(struct v3_var_header); i++) { 656 if ((_nvramImage[offset + i] != 0) && (_nvramImage[offset + i] != 0xFF)) { 657 break; 658 } 659 } 660 661 if (i == sizeof(struct v3_var_header)) { 662 DEBUG_INFO("No more variables after offset %#lx\n", offset); 663 break; 664 } 665 666 if (!valid_variable_header(header, _bankSize - offset)) { 667 DEBUG_ERROR("invalid header @ %#lx\n", offset); 668 offset += sizeof(struct v3_var_header); 669 continue; 670 } 671 672 uuid_unparse(header->guid, uuidString); 673 DEBUG_INFO("Valid var @ %#08zx, state=%#02x, length=%#08zx, %s:%s\n", offset, header->state, 674 variable_length(header), uuidString, header->name_data_buf); 675 676 if (header->state != VAR_ADDED) { 677 goto skip; 678 } 679 680 crc = crc32(0, header->name_data_buf + header->nameSize, header->dataSize); 681 682 if (crc != header->crc) { 683 DEBUG_ERROR("invalid crc @ %#lx, calculated=%#x, read=%#x\n", offset, crc, header->crc); 684 goto skip; 685 } 686 687 v3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(nvram_v3_var_container_size(header)); 688 __nochk_memcpy(&v3Entry->header, _nvramImage + offset, variable_length(header)); 689 690 // It is assumed that the initial image being unserialized here is going to be the proxy data from EDT and not the image 691 // read from the controller, which for various reasons due to the setting of states and saves from iBoot, can be 692 // different. We will have an initial existing_offset of 0 and once the controller is set we will read 693 // out the image there and update the existing offset with what is present on the NOR image 694 v3Entry->existing_offset = 0; 695 v3Entry->new_state = VAR_NEW_STATE_NONE; 696 697 // safe guard for any strange duplicate entries in the store 698 findExistingEntry(v3Entry->header.guid, (const char *)v3Entry->header.name_data_buf, &existingEntry, &existingIndex); 699 700 if (existingEntry != nullptr) { 701 existingSize = variable_length(&existingEntry->header); 702 703 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_container_size(header)); 704 _varEntries->replaceObject(existingIndex, entryContainer.get()); 705 706 DEBUG_INFO("Found existing for %s, resetting when controller available\n", v3Entry->header.name_data_buf); 707 _resetData = true; 708 } else { 709 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_container_size(header)); 710 _varEntries->setObject(entryContainer.get()); 711 existingSize = 0; 712 } 713 714 system = (_systemSize != 0) && (uuid_compare(v3Entry->header.guid, gAppleSystemVariableGuid) == 0); 715 if (system) { 716 _systemUsed = _systemUsed + (uint32_t)variable_length(header) - (uint32_t)existingSize; 717 } else { 718 _commonUsed = _commonUsed + (uint32_t)variable_length(header) - (uint32_t)existingSize; 719 } 720 721 if (convertPropToObject(v3Entry->header.name_data_buf, v3Entry->header.nameSize, 722 v3Entry->header.name_data_buf + v3Entry->header.nameSize, v3Entry->header.dataSize, 723 propSymbol, propObject)) { 724 OSSharedPtr<const OSSymbol> canonicalKey = keyWithGuidAndCString(v3Entry->header.guid, (const char *)v3Entry->header.name_data_buf); 725 726 DEBUG_INFO("adding %s, dataLength=%u, system=%d\n", 727 canonicalKey->getCStringNoCopy(), v3Entry->header.dataSize, system); 728 729 _varDict->setObject(canonicalKey.get(), propObject.get()); 730 731 if (_provider->_diags) { 732 _provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid), 733 kIONVRAMOperationInit, propSymbol.get()->getCStringNoCopy(), 734 (void *)(uintptr_t)(header->name_data_buf + header->nameSize)); 735 } 736 } 737 IOFreeData(v3Entry, nvram_v3_var_container_size(header)); 738 skip: 739 offset += variable_length(header); 740 } 741 742 _currentOffset = (uint32_t)offset; 743 744 DEBUG_ALWAYS("_commonSize %#x, _systemSize %#x, _currentOffset %#x\n", _commonSize, _systemSize, _currentOffset); 745 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed); 746 747 _newData = true; 748 749 if (_provider->_diags) { 750 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32); 751 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get()); 752 DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed()); 753 754 val = OSNumber::withNumber(getCommonUsed(), 32); 755 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get()); 756 DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed()); 757 } 758 759 return ret; 760 } 761 762 IOReturn 763 IONVRAMV3Handler::setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object) 764 { 765 struct nvram_v3_var_entry *v3Entry = nullptr; 766 struct nvram_v3_var_entry *newV3Entry; 767 OSSharedPtr<OSData> newContainer; 768 OSSharedPtr<const OSSymbol> canonicalKey; 769 bool unset = (object == nullptr); 770 bool system = false; 771 IOReturn ret = kIOReturnSuccess; 772 size_t entryNameLen = strlen(variableName) + 1; 773 unsigned int existingEntryIndex; 774 uint32_t dataSize = 0; 775 size_t existingVariableSize = 0; 776 size_t newVariableSize = 0; 777 size_t newEntrySize; 778 uuid_string_t uuidString; 779 780 system = (uuid_compare(varGuid, gAppleSystemVariableGuid) == 0); 781 canonicalKey = keyWithGuidAndCString(varGuid, variableName); 782 783 uuid_unparse(varGuid, uuidString); 784 DEBUG_INFO("setting %s:%s, system=%d, current var count=%u\n", uuidString, variableName, system, _varEntries->getCount()); 785 786 findExistingEntry(varGuid, variableName, &v3Entry, &existingEntryIndex); 787 788 if (unset == true) { 789 setEntryForRemove(v3Entry, system); 790 } else { 791 if ((v3Entry != nullptr) && (v3Entry->new_state != VAR_NEW_STATE_REMOVE)) { 792 // Sizing was subtracted in setEntryForRemove 793 existingVariableSize = variable_length(&v3Entry->header); 794 } 795 796 convertObjectToProp(nullptr, &dataSize, variableName, object); 797 798 newVariableSize = sizeof(struct v3_var_header) + entryNameLen + dataSize; 799 newEntrySize = sizeof(struct nvram_v3_var_entry) + entryNameLen + dataSize; 800 801 if (system) { 802 if (_systemUsed - existingVariableSize + newVariableSize > _systemSize) { 803 DEBUG_ERROR("system region full\n"); 804 ret = kIOReturnNoSpace; 805 goto exit; 806 } 807 } else if (_commonUsed - existingVariableSize + newVariableSize > _commonSize) { 808 DEBUG_ERROR("common region full\n"); 809 ret = kIOReturnNoSpace; 810 goto exit; 811 } 812 813 DEBUG_INFO("creating new entry for %s, existingVariableSize=%#zx, newVariableSize=%#zx\n", variableName, existingVariableSize, newVariableSize); 814 newV3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(newEntrySize); 815 816 memcpy(newV3Entry->header.name_data_buf, variableName, entryNameLen); 817 convertObjectToProp(newV3Entry->header.name_data_buf + entryNameLen, &dataSize, variableName, object); 818 819 newV3Entry->header.startId = VARIABLE_DATA; 820 newV3Entry->header.nameSize = (uint32_t)entryNameLen; 821 newV3Entry->header.dataSize = dataSize; 822 newV3Entry->header.crc = crc32(0, newV3Entry->header.name_data_buf + entryNameLen, dataSize); 823 memcpy(newV3Entry->header.guid, varGuid, sizeof(gAppleNVRAMGuid)); 824 newV3Entry->new_state = VAR_NEW_STATE_APPEND; 825 826 if (v3Entry) { 827 newV3Entry->existing_offset = v3Entry->existing_offset; 828 newV3Entry->header.state = v3Entry->header.state; 829 newV3Entry->header.attributes = v3Entry->header.attributes; 830 831 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize); 832 _varEntries->replaceObject(existingEntryIndex, newContainer.get()); 833 } else { 834 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize); 835 _varEntries->setObject(newContainer.get()); 836 } 837 838 if (system) { 839 _systemUsed = _systemUsed + (uint32_t)newVariableSize - (uint32_t)existingVariableSize; 840 } else { 841 _commonUsed = _commonUsed + (uint32_t)newVariableSize - (uint32_t)existingVariableSize; 842 } 843 844 _varDict->setObject(canonicalKey.get(), object); 845 846 if (_provider->_diags) { 847 _provider->_diags->logVariable(getPartitionTypeForGUID(varGuid), 848 kIONVRAMOperationWrite, variableName, 849 (void *)(uintptr_t)dataSize); 850 } 851 852 IOFreeData(newV3Entry, newEntrySize); 853 } 854 855 exit: 856 _newData = true; 857 858 if (_provider->_diags) { 859 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32); 860 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get()); 861 862 val = OSNumber::withNumber(getCommonUsed(), 32); 863 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get()); 864 } 865 866 DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed); 867 868 return ret; 869 } 870 871 IOReturn 872 IONVRAMV3Handler::setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) 873 { 874 uuid_t destGuid; 875 876 if (getSystemPartitionActive()) { 877 // System region case, if they're using the GUID directly or it's on the system allow list 878 // force it to use the System GUID 879 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) { 880 uuid_copy(destGuid, gAppleSystemVariableGuid); 881 } else { 882 uuid_copy(destGuid, varGuid); 883 } 884 } else { 885 // No system region, store System GUID as Common GUID 886 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) { 887 uuid_copy(destGuid, gAppleNVRAMGuid); 888 } else { 889 uuid_copy(destGuid, varGuid); 890 } 891 } 892 893 return setVariableInternal(destGuid, variableName, object); 894 } 895 896 uint32_t 897 IONVRAMV3Handler::findCurrentBank(void) 898 { 899 struct v3_store_header storeHeader; 900 uint32_t maxGen = 0; 901 uint32_t currentBank = 0; 902 903 for (unsigned int i = 0; i < _bankCount; i++) { 904 _nvramController->select(i); 905 _nvramController->read(0, (uint8_t *)&storeHeader, sizeof(storeHeader)); 906 907 if (valid_store_header(&storeHeader) && (storeHeader.generation >= maxGen)) { 908 currentBank = i; 909 maxGen = storeHeader.generation; 910 } 911 } 912 913 DEBUG_ALWAYS("currentBank=%#x, gen=%#x", currentBank, maxGen); 914 915 return currentBank; 916 } 917 918 bool 919 IONVRAMV3Handler::setController(IONVRAMController *controller) 920 { 921 IOReturn ret = kIOReturnSuccess; 922 923 if (_nvramController == NULL) { 924 _nvramController = controller; 925 } 926 927 DEBUG_INFO("Controller name: %s\n", _nvramController->getName()); 928 929 require(_bankSize != 0, exit); 930 931 if (_resetData) { 932 _resetData = false; 933 DEBUG_ERROR("_resetData set, issuing reclaim recovery\n"); 934 ret = reclaim(); 935 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret)); 936 goto exit; 937 } 938 939 ret = reloadInternal(); 940 if (ret != kIOReturnSuccess) { 941 DEBUG_ERROR("Invalid image found, issuing reclaim recovery\n"); 942 ret = reclaim(); 943 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret)); 944 } 945 946 exit: 947 return ret == kIOReturnSuccess; 948 } 949 950 IOReturn 951 IONVRAMV3Handler::reclaim(void) 952 { 953 IOReturn ret; 954 struct v3_store_header newStoreHeader; 955 struct v3_var_header *varHeader; 956 struct nvram_v3_var_entry *varEntry; 957 OSData *entryContainer; 958 size_t new_bank_offset = sizeof(struct v3_store_header); 959 uint32_t next_bank = (_currentBank + 1) % _bankCount; 960 961 DEBUG_INFO("called\n"); 962 963 ret = _nvramController->select(next_bank); 964 verify_noerr_action(ret, DEBUG_INFO("select of bank %#08x failed\n", next_bank)); 965 966 ret = _nvramController->eraseBank(); 967 verify_noerr_action(ret, DEBUG_INFO("eraseBank failed, ret=%#08x\n", ret)); 968 969 _currentBank = next_bank; 970 971 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 972 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i)); 973 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 974 varHeader = &varEntry->header; 975 976 DEBUG_INFO("entry %u %s, new_state=%#x, e_offset=%#lx, state=%#x\n", 977 i, varEntry->header.name_data_buf, varEntry->new_state, varEntry->existing_offset, varHeader->state); 978 979 if (varEntry->new_state == VAR_NEW_STATE_NONE) { 980 ret = _nvramController->write(new_bank_offset, (uint8_t *)varHeader, variable_length(varHeader)); 981 require_noerr_action(ret, exit, DEBUG_ERROR("var write failed, ret=%08x\n", ret)); 982 983 varEntry->existing_offset = new_bank_offset; 984 new_bank_offset += variable_length(varHeader); 985 } else { 986 // Set existing offset to 0 so that they will either be appended 987 // or any remaining removals will be dropped 988 varEntry->existing_offset = 0; 989 } 990 } 991 992 memcpy(&newStoreHeader, _nvramImage, sizeof(newStoreHeader)); 993 994 _generation += 1; 995 996 newStoreHeader.generation = _generation; 997 998 ret = _nvramController->write(0, (uint8_t *)&newStoreHeader, sizeof(newStoreHeader)); 999 require_noerr_action(ret, exit, DEBUG_ERROR("store header write failed, ret=%08x\n", ret)); 1000 1001 _currentOffset = (uint32_t)new_bank_offset; 1002 1003 DEBUG_INFO("Reclaim complete, _generation=%u, _currentOffset=%#x\n", _generation, _currentOffset); 1004 1005 exit: 1006 return ret; 1007 } 1008 1009 IOReturn 1010 IONVRAMV3Handler::syncRaw(void) 1011 { 1012 IOReturn ret = kIOReturnSuccess; 1013 size_t varEndOffset; 1014 size_t varStartOffset; 1015 struct nvram_v3_var_entry *varEntry; 1016 struct v3_var_header *varHeader; 1017 OSData *entryContainer; 1018 OSSharedPtr<OSArray> remainingEntries; 1019 1020 require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n")); 1021 require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n")); 1022 require_action(_bankSize != 0, exit, DEBUG_INFO("No nvram size info\n")); 1023 1024 DEBUG_INFO("_varEntries->getCount()=%#x\n", _varEntries->getCount()); 1025 1026 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity()); 1027 1028 for (unsigned int i = 0; i < _varEntries->getCount(); i++) { 1029 size_t space_needed = 0; 1030 uint8_t state; 1031 1032 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i)); 1033 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy(); 1034 varHeader = &varEntry->header; 1035 1036 DEBUG_INFO("%s new_state=%d, e_off=%#lx, c_off=%#x, uuid=%x%x, nameSize=%#x, dataSize=%#x\n", 1037 varEntry->header.name_data_buf, 1038 varEntry->new_state, varEntry->existing_offset, _currentOffset, 1039 varHeader->guid[0], varHeader->guid[1], 1040 varHeader->nameSize, varHeader->dataSize); 1041 1042 if (varEntry->new_state == VAR_NEW_STATE_APPEND) { 1043 space_needed = variable_length(varHeader); 1044 1045 // reclaim if needed 1046 if ((_currentOffset + space_needed) > _bankSize) { 1047 ret = reclaim(); 1048 require_noerr_action(ret, exit, DEBUG_ERROR("reclaim fail, ret=%#x\n", ret)); 1049 1050 // Check after reclaim... 1051 if ((_currentOffset + space_needed) > _bankSize) { 1052 DEBUG_ERROR("nvram full!\n"); 1053 goto exit; 1054 } 1055 1056 DEBUG_INFO("%s AFTER reclaim new_state=%d, e_off=%#lx, c_off=%#x, uuid=%x%x, nameSize=%#x, dataSize=%#x\n", 1057 varEntry->header.name_data_buf, 1058 varEntry->new_state, varEntry->existing_offset, _currentOffset, 1059 varHeader->guid[0], varHeader->guid[1], 1060 varHeader->nameSize, varHeader->dataSize); 1061 } 1062 1063 if (varEntry->existing_offset) { 1064 // Mark existing entry as VAR_IN_DELETED_TRANSITION 1065 state = varHeader->state & VAR_IN_DELETED_TRANSITION; 1066 DEBUG_INFO("invalidating with state=%#x\n", state); 1067 1068 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state)); 1069 require_noerr_action(ret, exit, DEBUG_ERROR("new state w fail, ret=%#x\n", ret)); 1070 } 1071 1072 varStartOffset = _currentOffset; 1073 varEndOffset = _currentOffset; 1074 1075 // Append new entry as VAR_ADDED 1076 varHeader->state = VAR_ADDED; 1077 1078 ret = _nvramController->write(varStartOffset, (uint8_t *)varHeader, variable_length(varHeader)); 1079 require_noerr_action(ret, exit, DEBUG_ERROR("variable write fail, ret=%#x\n", ret); ); 1080 1081 varEndOffset += variable_length(varHeader); 1082 1083 if (varEntry->existing_offset) { 1084 // Mark existing entry as VAR_DELETED 1085 state = varHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION; 1086 1087 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state)); 1088 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret)); 1089 } 1090 1091 varEntry->existing_offset = varStartOffset; 1092 varEntry->new_state = VAR_NEW_STATE_NONE; 1093 1094 _currentOffset = (uint32_t)varEndOffset; 1095 1096 remainingEntries->setObject(entryContainer); 1097 } else if (varEntry->new_state == VAR_NEW_STATE_REMOVE) { 1098 if (varEntry->existing_offset) { 1099 DEBUG_INFO("marking entry at offset %#lx deleted\n", varEntry->existing_offset); 1100 1101 // Mark existing entry as VAR_IN_DELETED_TRANSITION 1102 state = varHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION; 1103 1104 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state)); 1105 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret)); 1106 } else { 1107 DEBUG_INFO("No existing, removing\n"); 1108 } 1109 1110 // not re-added to remainingEntries 1111 } else { 1112 DEBUG_INFO("skipping\n"); 1113 remainingEntries->setObject(entryContainer); 1114 } 1115 } 1116 1117 _varEntries.reset(remainingEntries.get(), OSRetain); 1118 1119 _newData = false; 1120 1121 exit: 1122 return ret; 1123 } 1124 1125 IOReturn 1126 IONVRAMV3Handler::syncBlock(void) 1127 { 1128 IOReturn ret = kIOReturnSuccess; 1129 struct v3_store_header newStoreHeader; 1130 struct v3_var_header *varHeader; 1131 struct nvram_v3_var_entry *varEntry; 1132 OSData *entryContainer; 1133 size_t new_bank_offset = sizeof(struct v3_store_header); 1134 uint8_t *block; 1135 OSSharedPtr<OSArray> remainingEntries; 1136 uint32_t next_bank = (_currentBank + 1) % _bankCount; 1137 1138 DEBUG_INFO("called\n"); 1139 1140 require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n")); 1141 require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n")); 1142 require_action(_bankSize != 0, exit, DEBUG_INFO("No nvram size info\n")); 1143 1144 block = (uint8_t *)IOMallocData(_bankSize); 1145 1146 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity()); 1147 1148 ret = _nvramController->select(next_bank); 1149 verify_noerr_action(ret, DEBUG_INFO("select of bank %#x failed\n", next_bank)); 1150 1151 ret = _nvramController->eraseBank(); 1152 verify_noerr_action(ret, DEBUG_INFO("eraseBank failed, ret=%#08x\n", ret)); 1153 1154 _currentBank = next_bank; 1155 1156 memcpy(&newStoreHeader, _nvramImage, sizeof(newStoreHeader)); 1157 1158 _generation += 1; 1159 1160 newStoreHeader.generation = _generation; 1161 1162 memcpy(block, (uint8_t *)&newStoreHeader, sizeof(newStoreHeader)); 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 varHeader->state = VAR_ADDED; 1170 1171 DEBUG_INFO("entry %u %s, new_state=%#x, e_offset=%#lx, state=%#x\n", 1172 i, varEntry->header.name_data_buf, varEntry->new_state, varEntry->existing_offset, varHeader->state); 1173 1174 if (varEntry->new_state != VAR_NEW_STATE_REMOVE) { 1175 memcpy(block + new_bank_offset, (uint8_t *)varHeader, variable_length(varHeader)); 1176 1177 varEntry->existing_offset = new_bank_offset; 1178 new_bank_offset += variable_length(varHeader); 1179 varEntry->new_state = VAR_NEW_STATE_NONE; 1180 1181 remainingEntries->setObject(entryContainer); 1182 } else { 1183 DEBUG_INFO("Dropping %s\n", varEntry->header.name_data_buf); 1184 } 1185 } 1186 1187 ret = _nvramController->write(0, block, _bankSize); 1188 verify_noerr_action(ret, DEBUG_ERROR("w fail, ret=%#x\n", ret)); 1189 1190 _nvramController->sync(); 1191 1192 _varEntries.reset(remainingEntries.get(), OSRetain); 1193 1194 _newData = false; 1195 1196 DEBUG_INFO("Save complete, _generation=%u\n", _generation); 1197 1198 IOFreeData(block, _bankSize); 1199 1200 exit: 1201 return ret; 1202 } 1203 1204 bool 1205 IONVRAMV3Handler::sync(void) 1206 { 1207 IOReturn ret; 1208 1209 if (_reload) { 1210 ret = reloadInternal(); 1211 require_noerr_action(ret, exit, DEBUG_ERROR("Reload failed, ret=%#x", ret)); 1212 1213 _reload = false; 1214 } 1215 1216 if (_rawController == true) { 1217 ret = syncRaw(); 1218 1219 if (ret != kIOReturnSuccess) { 1220 ret = reclaim(); 1221 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, ret=%#x", ret)); 1222 1223 // Attempt to save again (will rewrite the variables still in APPEND) on the new bank 1224 ret = syncRaw(); 1225 require_noerr_action(ret, exit, DEBUG_ERROR("syncRaw retry failed, ret=%#x", ret)); 1226 } 1227 } else { 1228 ret = syncBlock(); 1229 } 1230 1231 exit: 1232 return ret == kIOReturnSuccess; 1233 } 1234 1235 uint32_t 1236 IONVRAMV3Handler::getGeneration(void) const 1237 { 1238 return _generation; 1239 } 1240 1241 uint32_t 1242 IONVRAMV3Handler::getVersion(void) const 1243 { 1244 return kNVRAMVersion3; 1245 } 1246 1247 uint32_t 1248 IONVRAMV3Handler::getSystemUsed(void) const 1249 { 1250 return _systemUsed; 1251 } 1252 1253 uint32_t 1254 IONVRAMV3Handler::getCommonUsed(void) const 1255 { 1256 return _commonUsed; 1257 } 1258 1259 bool 1260 IONVRAMV3Handler::getSystemPartitionActive(void) const 1261 { 1262 return _systemSize != 0; 1263 } 1264 1265 bool 1266 IONVRAMV3Handler::convertObjectToProp(uint8_t *buffer, uint32_t *length, 1267 const char *propName, OSObject *propObject) 1268 { 1269 uint32_t offset; 1270 IONVRAMVariableType propType; 1271 OSBoolean *tmpBoolean = nullptr; 1272 OSNumber *tmpNumber = nullptr; 1273 OSString *tmpString = nullptr; 1274 OSData *tmpData = nullptr; 1275 1276 propType = getVariableType(propName); 1277 1278 // Get the size of the data. 1279 offset = 0; 1280 switch (propType) { 1281 case kOFVariableTypeBoolean: 1282 tmpBoolean = OSDynamicCast(OSBoolean, propObject); 1283 if (tmpBoolean != nullptr) { 1284 const char *bool_buf; 1285 if (tmpBoolean->getValue()) { 1286 bool_buf = "true"; 1287 } else { 1288 bool_buf = "false"; 1289 } 1290 1291 offset = (uint32_t)strlen(bool_buf); 1292 1293 if (buffer) { 1294 if (*length < offset) { 1295 return false; 1296 } else { 1297 memcpy(buffer, bool_buf, offset); 1298 } 1299 } 1300 } 1301 break; 1302 1303 case kOFVariableTypeNumber: 1304 tmpNumber = OSDynamicCast(OSNumber, propObject); 1305 if (tmpNumber != nullptr) { 1306 char num_buf[12]; 1307 char *end_buf = num_buf; 1308 uint32_t tmpValue = tmpNumber->unsigned32BitValue(); 1309 if (tmpValue == 0xFFFFFFFF) { 1310 end_buf += snprintf(end_buf, sizeof(num_buf), "-1"); 1311 } else if (tmpValue < 1000) { 1312 end_buf += snprintf(end_buf, sizeof(num_buf), "%d", (uint32_t)tmpValue); 1313 } else { 1314 end_buf += snprintf(end_buf, sizeof(num_buf), "%#x", (uint32_t)tmpValue); 1315 } 1316 1317 offset = (uint32_t)(end_buf - num_buf); 1318 if (buffer) { 1319 if (*length < offset) { 1320 return false; 1321 } else { 1322 memcpy(buffer, num_buf, offset); 1323 } 1324 } 1325 } 1326 break; 1327 1328 case kOFVariableTypeString: 1329 tmpString = OSDynamicCast(OSString, propObject); 1330 if (tmpString != nullptr) { 1331 offset = tmpString->getLength(); 1332 1333 if (buffer) { 1334 if (*length < offset) { 1335 return false; 1336 } else { 1337 bcopy(tmpString->getCStringNoCopy(), buffer, offset); 1338 } 1339 } 1340 } 1341 break; 1342 1343 case kOFVariableTypeData: 1344 tmpData = OSDynamicCast(OSData, propObject); 1345 if (tmpData != nullptr) { 1346 offset = tmpData->getLength(); 1347 1348 if (buffer) { 1349 if (*length < offset) { 1350 return false; 1351 } else { 1352 bcopy(tmpData->getBytesNoCopy(), buffer, offset); 1353 } 1354 } 1355 } 1356 break; 1357 1358 default: 1359 return false; 1360 } 1361 1362 *length = offset; 1363 1364 return offset != 0; 1365 } 1366 1367 1368 bool 1369 IONVRAMV3Handler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength, 1370 const uint8_t *propData, uint32_t propDataLength, 1371 OSSharedPtr<const OSSymbol>& propSymbol, 1372 OSSharedPtr<OSObject>& propObject) 1373 { 1374 OSSharedPtr<const OSSymbol> tmpSymbol; 1375 OSSharedPtr<OSNumber> tmpNumber; 1376 OSSharedPtr<OSString> tmpString; 1377 OSSharedPtr<OSObject> tmpObject = nullptr; 1378 1379 tmpSymbol = OSSymbol::withCString((const char *)propName); 1380 1381 if (tmpSymbol == nullptr) { 1382 return false; 1383 } 1384 1385 switch (getVariableType(tmpSymbol.get())) { 1386 case kOFVariableTypeBoolean: 1387 if (!strncmp("true", (const char *)propData, propDataLength)) { 1388 tmpObject.reset(kOSBooleanTrue, OSRetain); 1389 } else if (!strncmp("false", (const char *)propData, propDataLength)) { 1390 tmpObject.reset(kOSBooleanFalse, OSRetain); 1391 } 1392 break; 1393 1394 case kOFVariableTypeNumber: 1395 tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32); 1396 if (tmpNumber != nullptr) { 1397 tmpObject = tmpNumber; 1398 } 1399 break; 1400 1401 case kOFVariableTypeString: 1402 tmpString = OSString::withCString((const char *)propData, propDataLength); 1403 if (tmpString != nullptr) { 1404 tmpObject = tmpString; 1405 } 1406 break; 1407 1408 case kOFVariableTypeData: 1409 tmpObject = OSData::withBytes(propData, propDataLength); 1410 break; 1411 1412 default: 1413 break; 1414 } 1415 1416 if (tmpObject == nullptr) { 1417 tmpSymbol.reset(); 1418 return false; 1419 } 1420 1421 propSymbol = tmpSymbol; 1422 propObject = tmpObject; 1423 1424 return true; 1425 } 1426