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