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