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