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 // Originally read from the proxy data and needs to be syncd
52 // with the backing store when available
53 #define VAR_NEW_STATE_INIT 0x04
54
55 #pragma pack(1)
56 struct v3_store_header {
57 uint32_t name;
58 uint32_t size;
59 uint32_t generation;
60 uint8_t state;
61 uint8_t flags;
62 uint8_t version;
63 uint8_t reserved1;
64 uint32_t system_size;
65 uint32_t common_size;
66 };
67
68 struct v3_var_header {
69 uint16_t startId;
70 uint8_t state;
71 uint8_t reserved;
72 uint32_t attributes;
73 uint32_t nameSize;
74 uint32_t dataSize;
75 uuid_t guid;
76 uint32_t crc;
77 uint8_t name_data_buf[];
78 };
79 #pragma pack()
80
81 struct nvram_v3_var_entry {
82 uint8_t new_state;
83 size_t existing_offset;
84 struct v3_var_header header;
85 };
86
87 static size_t
nvram_v3_var_entry_size(const struct v3_var_header * header)88 nvram_v3_var_entry_size(const struct v3_var_header *header)
89 {
90 return sizeof(struct nvram_v3_var_entry) + header->nameSize + header->dataSize;
91 }
92
93 static size_t
variable_length(const struct v3_var_header * header)94 variable_length(const struct v3_var_header *header)
95 {
96 return sizeof(struct v3_var_header) + header->nameSize + header->dataSize;
97 }
98
99 static bool
valid_store_header(const struct v3_store_header * header)100 valid_store_header(const struct v3_store_header *header)
101 {
102 return (header->name == VARIABLE_STORE_SIGNATURE) && (header->version == VARIABLE_STORE_VERSION);
103 }
104
105 static bool
valid_variable_header(const struct v3_var_header * header,size_t buf_len)106 valid_variable_header(const struct v3_var_header *header, size_t buf_len)
107 {
108 return (buf_len > sizeof(struct v3_var_header)) &&
109 (header->startId == VARIABLE_DATA) &&
110 (variable_length(header) <= buf_len);
111 }
112
113 static uint32_t
find_active_var_in_image(const struct v3_var_header * var,const uint8_t * image,uint32_t len)114 find_active_var_in_image(const struct v3_var_header *var, const uint8_t *image, uint32_t len)
115 {
116 uint32_t offset = sizeof(struct v3_store_header);
117 const struct v3_var_header *store_var;
118 uint32_t var_offset = 0;
119
120 while ((offset + sizeof(struct v3_var_header) < len)) {
121 store_var = (const struct v3_var_header *)(image + offset);
122
123 if (valid_variable_header(store_var, len - offset)) {
124 if ((store_var->state == VAR_ADDED) &&
125 (var->nameSize == store_var->nameSize) &&
126 (memcmp(var->name_data_buf, store_var->name_data_buf, var->nameSize) == 0)) {
127 var_offset = offset;
128 break;
129 }
130 } else {
131 break;
132 }
133
134 offset += variable_length(store_var);
135 }
136
137 return var_offset;
138 }
139
140 static uint32_t
find_current_offset_in_image(const uint8_t * image,uint32_t len)141 find_current_offset_in_image(const uint8_t *image, uint32_t len)
142 {
143 uint32_t offset = 0;
144 uint32_t inner_offset = 0;
145
146 if (valid_store_header((const struct v3_store_header *)(image + offset))) {
147 DEBUG_INFO("valid store header @ %#x\n", offset);
148 offset += sizeof(struct v3_store_header);
149 }
150
151 while (offset < len) {
152 if (valid_variable_header((const struct v3_var_header *)(image + offset), len - offset)) {
153 DEBUG_INFO("valid variable header @ %#x\n", offset);
154 offset += variable_length((const struct v3_var_header *)(image + offset));
155 } else {
156 break;
157 }
158 }
159
160 while (offset < len) {
161 if (image[offset] == 0xFF) {
162 DEBUG_INFO("scanning for clear memory @ %#x\n", offset);
163
164 inner_offset = offset;
165
166 while ((inner_offset < len) && (image[inner_offset] == 0xFF)) {
167 inner_offset++;
168 }
169
170 if (inner_offset == len) {
171 DEBUG_INFO("found start of clear mem @ %#x\n", offset);
172 return offset;
173 } else {
174 DEBUG_ERROR("ERROR!!!!! found non-clear byte @ %#x\n", offset);
175 offset = inner_offset;
176 }
177 }
178 offset++;
179 }
180
181 return 0;
182 }
183
184 class IONVRAMV3Handler : public IODTNVRAMFormatHandler
185 {
186 private:
187 IONVRAMController *_nvramController;
188 IODTNVRAM *_provider;
189
190 bool _newData;
191
192 uint32_t _generation;
193
194 uint8_t *_nvramImage;
195 uint32_t _nvramSize;
196
197 OSSharedPtr<OSDictionary> &_commonDict;
198 OSSharedPtr<OSDictionary> &_systemDict;
199
200 uint32_t _commonSize;
201 uint32_t _systemSize;
202
203 uint32_t _commonUsed;
204 uint32_t _systemUsed;
205
206 uint32_t _currentOffset;
207
208 OSSharedPtr<OSArray> _varEntries;
209
210 IOReturn unserializeImage(const uint8_t *image, IOByteCount length);
211
212 IOReturn reclaim(void);
213
214 static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const char *propSymbol, OSObject *propObject);
215 static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength,
216 OSSharedPtr<const OSSymbol>& propSymbol, OSSharedPtr<OSObject>& propObject);
217
218 IOReturn syncInternal(void);
219
220 public:
221 virtual
222 ~IONVRAMV3Handler() APPLE_KEXT_OVERRIDE;
223 IONVRAMV3Handler(OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict);
224
225 static bool isValidImage(const uint8_t *image, IOByteCount length);
226
227 static IONVRAMV3Handler *init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length,
228 OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict);
229
230 virtual IOReturn setVariable(const uuid_t *varGuid, const char *variableName, OSObject *object) APPLE_KEXT_OVERRIDE;
231 virtual bool setController(IONVRAMController *controller) APPLE_KEXT_OVERRIDE;
232 virtual bool sync(void) APPLE_KEXT_OVERRIDE;
233 virtual uint32_t getGeneration(void) const APPLE_KEXT_OVERRIDE;
234 virtual uint32_t getVersion(void) const APPLE_KEXT_OVERRIDE;
235 virtual uint32_t getSystemUsed(void) const APPLE_KEXT_OVERRIDE;
236 virtual uint32_t getCommonUsed(void) const APPLE_KEXT_OVERRIDE;
237 };
238
~IONVRAMV3Handler()239 IONVRAMV3Handler::~IONVRAMV3Handler()
240 {
241 }
242
IONVRAMV3Handler(OSSharedPtr<OSDictionary> & commonDict,OSSharedPtr<OSDictionary> & systemDict)243 IONVRAMV3Handler::IONVRAMV3Handler(OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict) :
244 _commonDict(commonDict),
245 _systemDict(systemDict)
246 {
247 }
248
249 bool
isValidImage(const uint8_t * image,IOByteCount length)250 IONVRAMV3Handler::isValidImage(const uint8_t *image, IOByteCount length)
251 {
252 const struct v3_store_header *header = (const struct v3_store_header *)image;
253
254 if ((header == nullptr) || (length < sizeof(*header))) {
255 return false;
256 }
257
258 return valid_store_header(header);
259 }
260
261 IONVRAMV3Handler*
init(IODTNVRAM * provider,const uint8_t * image,IOByteCount length,OSSharedPtr<OSDictionary> & commonDict,OSSharedPtr<OSDictionary> & systemDict)262 IONVRAMV3Handler::init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length,
263 OSSharedPtr<OSDictionary> &commonDict, OSSharedPtr<OSDictionary> &systemDict)
264 {
265 IONVRAMV3Handler *handler = new IONVRAMV3Handler(commonDict, systemDict);
266
267 handler->_provider = provider;
268
269 if ((image != nullptr) && (length != 0)) {
270 if (handler->unserializeImage(image, length) != kIOReturnSuccess) {
271 DEBUG_ERROR("Unable to unserialize image, len=%#x\n", (unsigned int)length);
272 }
273 }
274
275 return handler;
276 }
277
278 IOReturn
unserializeImage(const uint8_t * image,IOByteCount length)279 IONVRAMV3Handler::unserializeImage(const uint8_t *image, IOByteCount length)
280 {
281 OSSharedPtr<const OSSymbol> propSymbol;
282 OSSharedPtr<OSObject> propObject;
283 OSSharedPtr<OSData> entryContainer;
284 const struct v3_store_header *storeHeader;
285 IOReturn ret = kIOReturnSuccess;
286 struct nvram_v3_var_entry *v3Entry;
287 const struct v3_var_header *header;
288 size_t offset = sizeof(struct v3_store_header);
289 uint32_t crc;
290 unsigned int i;
291 bool system;
292 OSDictionary *dict;
293
294 require(isValidImage(image, length), exit);
295
296 storeHeader = (const struct v3_store_header *)image;
297 require_action(storeHeader->size == (uint32_t)length, exit,
298 DEBUG_ERROR("Image size %#x != header size %#x\n", (unsigned int)length, storeHeader->size));
299
300 _generation = storeHeader->generation;
301 _systemSize = storeHeader->system_size;
302 _commonSize = storeHeader->common_size - sizeof(struct v3_store_header);
303
304 _systemUsed = 0;
305 _commonUsed = 0;
306
307 if (_nvramImage) {
308 IOFreeData(_nvramImage, _nvramSize);
309 }
310
311 _varEntries.reset();
312 _varEntries = OSArray::withCapacity(40);
313
314 _nvramImage = IONewData(uint8_t, length);
315 _nvramSize = (uint32_t)length;
316 bcopy(image, _nvramImage, _nvramSize);
317
318 if (_systemSize) {
319 _systemDict = OSDictionary::withCapacity(1);
320 }
321
322 if (_commonSize) {
323 _commonDict = OSDictionary::withCapacity(1);
324 }
325
326 while ((offset + sizeof(struct v3_var_header)) < length) {
327 header = (const struct v3_var_header *)(image + offset);
328
329 for (i = 0; i < sizeof(struct v3_var_header); i++) {
330 if ((image[offset + i] != 0) && (image[offset + i] != 0xFF)) {
331 break;
332 }
333 }
334
335 if (i == sizeof(struct v3_var_header)) {
336 DEBUG_INFO("No more variables after offset %#lx\n", offset);
337 break;
338 }
339
340 if (!valid_variable_header(header, length - offset)) {
341 DEBUG_ERROR("invalid header @ %#lx\n", offset);
342 offset += sizeof(struct v3_var_header);
343 continue;
344 }
345
346 if (header->state != VAR_ADDED) {
347 DEBUG_INFO("inactive var @ %#lx\n", offset);
348 goto skip;
349 }
350
351 crc = crc32(0, header->name_data_buf + header->nameSize, header->dataSize);
352
353 if (crc != header->crc) {
354 DEBUG_ERROR("invalid crc @ %#lx, calculated=%#x, read=%#x\n", offset, crc, header->crc);
355 goto skip;
356 }
357
358 DEBUG_INFO("entry: %s, size=%#zx, existing_offset=%#zx\n", header->name_data_buf, nvram_v3_var_entry_size(header), offset);
359 v3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(nvram_v3_var_entry_size(header));
360 __nochk_memcpy(&v3Entry->header, _nvramImage + offset, variable_length(header));
361
362 // It is assumed that the initial image being unserialized here is going to be the proxy data from EDT and not the image
363 // read from the controller, which for various reasons due to the setting of states and saves from iBoot, can be
364 // different. We will have an initial existing_offset of 0 with VAR_NEW_STATE_INIT here and once the controller is set we will read
365 // out the image there and merge our current data with the actual store
366 v3Entry->existing_offset = 0;
367 v3Entry->new_state = VAR_NEW_STATE_INIT;
368
369 entryContainer = OSData::withBytes(v3Entry, (uint32_t)nvram_v3_var_entry_size(header));
370 _varEntries->setObject(entryContainer.get());
371
372 system = (_systemSize != 0) && (uuid_compare(v3Entry->header.guid, gAppleSystemVariableGuid) == 0);
373 if (system) {
374 dict = _systemDict.get();
375 _systemUsed += variable_length(header);
376 } else {
377 dict = _commonDict.get();
378 _commonUsed += variable_length(header);
379 }
380
381 if (convertPropToObject(v3Entry->header.name_data_buf, v3Entry->header.nameSize,
382 v3Entry->header.name_data_buf + v3Entry->header.nameSize, v3Entry->header.dataSize,
383 propSymbol, propObject)) {
384 DEBUG_INFO("adding %s, dataLength=%u, system=%d\n",
385 propSymbol->getCStringNoCopy(), v3Entry->header.dataSize, system);
386
387 dict->setObject(propSymbol.get(), propObject.get());
388
389 if (_provider->_diags) {
390 _provider->_diags->logVariable(_provider->getDictionaryType(dict),
391 kIONVRAMOperationInit, propSymbol.get()->getCStringNoCopy(),
392 (void *)(uintptr_t)(header->name_data_buf + header->nameSize));
393 }
394 }
395 IOFreeData(v3Entry, nvram_v3_var_entry_size(header));
396 skip:
397 offset += variable_length(header);
398 }
399
400 _currentOffset = (uint32_t)offset;
401
402 DEBUG_ALWAYS("_commonSize %#x, _systemSize %#x, _currentOffset %#x\n", _commonSize, _systemSize, _currentOffset);
403 exit:
404 _newData = true;
405
406 if (_provider->_diags) {
407 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
408 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
409 DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
410
411 val = OSNumber::withNumber(getCommonUsed(), 32);
412 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
413 DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
414 }
415
416 return ret;
417 }
418
419 IOReturn
setVariable(const uuid_t * varGuid,const char * variableName,OSObject * object)420 IONVRAMV3Handler::setVariable(const uuid_t *varGuid, const char *variableName, OSObject *object)
421 {
422 struct nvram_v3_var_entry *v3Entry = nullptr;
423 struct nvram_v3_var_entry *newV3Entry;
424 OSData *entryContainer = nullptr;
425 OSSharedPtr<OSData> newContainer;
426 bool unset = (object == nullptr);
427 bool system = false;
428 IOReturn ret = kIOReturnSuccess;
429 size_t entryNameLen = strlen(variableName) + 1;
430 unsigned int existingEntryIndex;
431 uint32_t dataSize = 0;
432 size_t existingEntrySize = 0;
433 size_t newEntrySize;
434
435 if (_systemSize != 0) {
436 if ((uuid_compare(v3Entry->header.guid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) {
437 system = true;
438 }
439 }
440
441 DEBUG_INFO("setting %s, system=%d\n", variableName, system);
442
443 for (existingEntryIndex = 0; existingEntryIndex < _varEntries->getCount(); existingEntryIndex++) {
444 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(existingEntryIndex)));
445 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
446
447 if ((v3Entry->header.nameSize == entryNameLen) &&
448 (memcmp(v3Entry->header.name_data_buf, variableName, entryNameLen) == 0) &&
449 (uuid_compare(*varGuid, v3Entry->header.guid) == 0)) {
450 DEBUG_INFO("found existing entry for %s, unset=%d @ %#lx\n", variableName, unset, v3Entry->existing_offset);
451 existingEntrySize = nvram_v3_var_entry_size(&v3Entry->header);
452 break;
453 }
454
455 v3Entry = nullptr;
456 }
457
458 if (unset == true) {
459 if (v3Entry == NULL) {
460 DEBUG_INFO("unset %s but no entry\n", variableName);
461 } else if (v3Entry->new_state == VAR_NEW_STATE_REMOVE) {
462 DEBUG_INFO("entry %s already marked for remove\n", variableName);
463 } else {
464 DEBUG_INFO("marking entry %s for remove\n", variableName);
465
466 v3Entry->new_state = VAR_NEW_STATE_REMOVE;
467
468 if (system) {
469 _provider->_systemDict->removeObject(variableName);
470
471 if (_systemUsed < variable_length(&v3Entry->header)) {
472 panic("Invalid _systemUsed size\n");
473 }
474
475 _systemUsed -= variable_length(&v3Entry->header);
476 } else {
477 _provider->_commonDict->removeObject(variableName);
478
479 if (_commonUsed < variable_length(&v3Entry->header)) {
480 panic("Invalid _commonUsed size\n");
481 }
482 _commonUsed -= variable_length(&v3Entry->header);
483 }
484
485 if (_provider->_diags) {
486 _provider->_diags->logVariable(getPartitionTypeForGUID(varGuid), kIONVRAMOperationDelete, variableName, nullptr);
487 }
488 }
489 } else {
490 convertObjectToProp(nullptr, &dataSize, variableName, object);
491
492 newEntrySize = sizeof(struct nvram_v3_var_entry) + entryNameLen + dataSize;
493
494 if (system && (_systemUsed - existingEntrySize + newEntrySize > _systemSize)) {
495 DEBUG_ERROR("system region full\n");
496 ret = kIOReturnNoSpace;
497 goto exit;
498 } else if (!system && (_commonUsed - existingEntrySize + newEntrySize > _commonSize)) {
499 DEBUG_ERROR("common region full\n");
500 ret = kIOReturnNoSpace;
501 goto exit;
502 }
503
504 DEBUG_INFO("creating new entry for %s, dataSize=%#x\n", variableName, dataSize);
505 newV3Entry = (struct nvram_v3_var_entry *)IOMallocZeroData(newEntrySize);
506
507 memcpy(newV3Entry->header.name_data_buf, variableName, entryNameLen);
508 convertObjectToProp(newV3Entry->header.name_data_buf + entryNameLen, &dataSize, variableName, object);
509
510 newV3Entry->header.startId = VARIABLE_DATA;
511 newV3Entry->header.nameSize = (uint32_t)entryNameLen;
512 newV3Entry->header.dataSize = dataSize;
513 newV3Entry->header.crc = crc32(0, newV3Entry->header.name_data_buf + entryNameLen, dataSize);
514
515 if (system) {
516 memcpy(newV3Entry->header.guid, varGuid, sizeof(*varGuid));
517 } else {
518 memcpy(newV3Entry->header.guid, gAppleNVRAMGuid, sizeof(gAppleNVRAMGuid));
519 }
520
521 newV3Entry->new_state = VAR_NEW_STATE_APPEND;
522
523 newEntrySize = nvram_v3_var_entry_size(&newV3Entry->header);
524
525 if (v3Entry) {
526 newV3Entry->existing_offset = v3Entry->existing_offset;
527 newV3Entry->header.state = v3Entry->header.state;
528 newV3Entry->header.attributes = v3Entry->header.attributes;
529
530 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize);
531 _varEntries->replaceObject(existingEntryIndex, newContainer.get());
532 } else {
533 newContainer = OSData::withBytes(newV3Entry, (uint32_t)newEntrySize);
534 _varEntries->setObject(newContainer.get());
535 }
536
537 if (system) {
538 _systemUsed = _systemUsed + (uint32_t)newEntrySize - (uint32_t)existingEntrySize;
539 _provider->_systemDict->setObject(variableName, object);
540 } else {
541 _commonUsed = _commonUsed + (uint32_t)newEntrySize - (uint32_t)existingEntrySize;
542 _provider->_commonDict->setObject(variableName, object);
543 }
544
545 if (_provider->_diags) {
546 _provider->_diags->logVariable(getPartitionTypeForGUID(varGuid), kIONVRAMOperationWrite, variableName, (void *)(uintptr_t)dataSize);
547 }
548
549 IOFreeData(newV3Entry, newEntrySize);
550 }
551
552 exit:
553 _newData = true;
554
555 if (_provider->_diags) {
556 OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
557 _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
558 DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
559
560 val = OSNumber::withNumber(getCommonUsed(), 32);
561 _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
562 DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
563 }
564
565 return ret;
566 }
567
568 bool
setController(IONVRAMController * controller)569 IONVRAMV3Handler::setController(IONVRAMController *controller)
570 {
571 IOReturn ret = kIOReturnSuccess;
572 uint8_t *controllerImage;
573 struct nvram_v3_var_entry *v3Entry;
574 const struct v3_store_header *storeHeader;
575 const struct v3_var_header *storeVar;
576 OSData *entryContainer;
577
578 if (_nvramController == NULL) {
579 _nvramController = controller;
580 }
581
582 require(_nvramSize != 0, exit);
583
584 controllerImage = (uint8_t *)IOMallocData(_nvramSize);
585 _nvramController->read(0, controllerImage, _nvramSize);
586
587 if (isValidImage(controllerImage, _nvramSize)) {
588 DEBUG_INFO("valid image found\n");
589
590 storeHeader = (const struct v3_store_header *)controllerImage;
591
592 _generation = storeHeader->generation;
593
594 // We must sync any existing variables offset on the controller image with our internal representation
595 // All variables added from the EDT proxy data initial unserialize are still in a VAR_NEW_STATE_INIT
596 // If we find an existing entry and the data is still the same we record the existing offset and mark it
597 // as VAR_NEW_STATE_NONE meaning no action needed
598 // Otherwise if the data is different or it is not found on the controller image we mark it as VAR_NEW_STATE_APPEND
599 // which will have us invalidate the existing entry if there is one and append it on the next save
600 for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
601 entryContainer = (OSDynamicCast(OSData, _varEntries->getObject(i)));
602 v3Entry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
603
604 if (v3Entry->new_state == VAR_NEW_STATE_INIT) {
605 v3Entry->existing_offset = find_active_var_in_image(&v3Entry->header, controllerImage, _nvramSize);
606
607 if (v3Entry->existing_offset == 0) {
608 DEBUG_ERROR("%s is not in the NOR image\n", v3Entry->header.name_data_buf);
609 if (v3Entry->header.dataSize == 0) {
610 DEBUG_INFO("%s marked for remove\n", v3Entry->header.name_data_buf);
611 // Doesn't exist in the store and with a 0 dataSize is pending remove
612 v3Entry->new_state = VAR_NEW_STATE_REMOVE;
613 } else {
614 DEBUG_INFO("%s marked for append\n", v3Entry->header.name_data_buf);
615 // Doesn't exist in the store, just append it on next sync
616 v3Entry->new_state = VAR_NEW_STATE_APPEND;
617 }
618 } else {
619 DEBUG_INFO("Found offset for %s @ %#zx\n", v3Entry->header.name_data_buf, v3Entry->existing_offset);
620 storeVar = (const struct v3_var_header *)&controllerImage[v3Entry->existing_offset];
621
622 if ((variable_length(&v3Entry->header) == variable_length(storeVar)) &&
623 (memcmp(v3Entry->header.name_data_buf, storeVar->name_data_buf, storeVar->nameSize + storeVar->dataSize) == 0)) {
624 DEBUG_INFO("Store var for %s matches, marking new state none\n", v3Entry->header.name_data_buf);
625 v3Entry->new_state = VAR_NEW_STATE_NONE;
626 } else {
627 DEBUG_INFO("Store var for %s differs, marking new state append\n", v3Entry->header.name_data_buf);
628 v3Entry->new_state = VAR_NEW_STATE_APPEND;
629 }
630 }
631 }
632 }
633
634 _currentOffset = find_current_offset_in_image(controllerImage, _nvramSize);
635 DEBUG_INFO("New _currentOffset=%#x\n", _currentOffset);
636 } else {
637 DEBUG_ERROR("Invalid image found, issuing reclaim recovery\n");
638 ret = reclaim();
639 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, invalid controller state!!! ret=%#x\n", ret));
640 }
641
642 IOFreeData(controllerImage, _nvramSize);
643
644 exit:
645 return ret == kIOReturnSuccess;
646 }
647
648 IOReturn
reclaim(void)649 IONVRAMV3Handler::reclaim(void)
650 {
651 IOReturn ret;
652 struct v3_store_header newStoreHeader;
653 struct v3_var_header *varHeader;
654 struct nvram_v3_var_entry *varEntry;
655 OSData *entryContainer;
656 size_t new_bank_offset = sizeof(struct v3_store_header);
657
658 DEBUG_INFO("called\n");
659
660 ret = _nvramController->nextBank();
661 verify_noerr_action(ret, DEBUG_ERROR("Bank shift not triggered\n"));
662
663 for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
664 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i));
665 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
666 varHeader = &varEntry->header;
667
668 DEBUG_INFO("entry %u %s, new_state=%#x, e_offset=%#lx, state=%#x\n",
669 i, varEntry->header.name_data_buf, varEntry->new_state, varEntry->existing_offset, varHeader->state);
670
671 if (varEntry->new_state == VAR_NEW_STATE_NONE) {
672 ret = _nvramController->write(new_bank_offset, (uint8_t *)varHeader, variable_length(varHeader));
673 require_noerr_action(ret, exit, DEBUG_ERROR("var write failed, ret=%08x\n", ret));
674
675 varEntry->existing_offset = new_bank_offset;
676 new_bank_offset += variable_length(varHeader);
677 } else {
678 // Set existing offset to 0 so that they will either be appended
679 // or any remaining removals will be dropped
680 varEntry->existing_offset = 0;
681 }
682 }
683
684 memcpy(&newStoreHeader, _nvramImage, sizeof(newStoreHeader));
685
686 _generation += 1;
687
688 newStoreHeader.generation = _generation;
689
690 ret = _nvramController->write(0, (uint8_t *)&newStoreHeader, sizeof(newStoreHeader));
691 require_noerr_action(ret, exit, DEBUG_ERROR("store header write failed, ret=%08x\n", ret));
692
693 _currentOffset = (uint32_t)new_bank_offset;
694
695 DEBUG_INFO("Reclaim complete, _generation=%u, _currentOffset=%#x\n", _generation, _currentOffset);
696
697 exit:
698 return ret;
699 }
700
701 IOReturn
syncInternal(void)702 IONVRAMV3Handler::syncInternal(void)
703 {
704 IOReturn ret = kIOReturnSuccess;
705 size_t varEndOffset;
706 size_t varStartOffset;
707 struct nvram_v3_var_entry *varEntry;
708 struct v3_var_header *varHeader;
709 OSData *entryContainer;
710 OSSharedPtr<OSArray> remainingEntries;
711
712 require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n"));
713 require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n"));
714 require_action(_nvramSize != 0, exit, DEBUG_INFO("No nvram size info\n"));
715
716 DEBUG_INFO("_varEntries->getCount()=%#x\n", _varEntries->getCount());
717
718 remainingEntries = OSArray::withCapacity(_varEntries->getCapacity());
719
720 for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
721 size_t space_needed = 0;
722 uint8_t state;
723
724 entryContainer = OSDynamicCast(OSData, _varEntries->getObject(i));
725 varEntry = (struct nvram_v3_var_entry *)entryContainer->getBytesNoCopy();
726 varHeader = &varEntry->header;
727
728 DEBUG_INFO("%s new_state=%d, e_off=%#lx, c_off=%#x, uuid=%x%x, nameSize=%#x, dataSize=%#x\n",
729 varEntry->header.name_data_buf,
730 varEntry->new_state, varEntry->existing_offset, _currentOffset,
731 varHeader->guid[0], varHeader->guid[1],
732 varHeader->nameSize, varHeader->dataSize);
733
734 if (varEntry->new_state == VAR_NEW_STATE_APPEND) {
735 space_needed = variable_length(varHeader);
736
737 // reclaim if needed
738 if ((_currentOffset + space_needed) > _nvramSize) {
739 ret = reclaim();
740 require_noerr_action(ret, exit, DEBUG_ERROR("reclaim fail, ret=%#x\n", ret));
741
742 // Check after reclaim...
743 if ((_currentOffset + space_needed) > _nvramSize) {
744 DEBUG_ERROR("nvram full!\n");
745 goto exit;
746 }
747
748 DEBUG_INFO("%s AFTER reclaim new_state=%d, e_off=%#lx, c_off=%#x, uuid=%x%x, nameSize=%#x, dataSize=%#x\n",
749 varEntry->header.name_data_buf,
750 varEntry->new_state, varEntry->existing_offset, _currentOffset,
751 varHeader->guid[0], varHeader->guid[1],
752 varHeader->nameSize, varHeader->dataSize);
753 }
754
755 if (varEntry->existing_offset) {
756 // Mark existing entry as VAR_IN_DELETED_TRANSITION
757 state = varHeader->state & VAR_IN_DELETED_TRANSITION;
758 DEBUG_INFO("invalidating with state=%#x\n", state);
759
760 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state));
761 require_noerr_action(ret, exit, DEBUG_ERROR("new state w fail, ret=%#x\n", ret));
762 }
763
764 varStartOffset = _currentOffset;
765 varEndOffset = _currentOffset;
766
767 // Append new entry as VAR_ADDED
768 varHeader->state = VAR_ADDED;
769
770 ret = _nvramController->write(varStartOffset, (uint8_t *)varHeader, variable_length(varHeader));
771 require_noerr_action(ret, exit, DEBUG_ERROR("variable write fail, ret=%#x\n", ret); );
772
773 varEndOffset += variable_length(varHeader);
774
775 if (varEntry->existing_offset) {
776 // Mark existing entry as VAR_DELETED
777 state = varHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION;
778
779 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state));
780 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret));
781 }
782
783 varEntry->existing_offset = varStartOffset;
784 varEntry->new_state = VAR_NEW_STATE_NONE;
785
786 _currentOffset = (uint32_t)varEndOffset;
787
788 remainingEntries->setObject(entryContainer);
789 } else if (varEntry->new_state == VAR_NEW_STATE_REMOVE) {
790 if (varEntry->existing_offset) {
791 DEBUG_INFO("marking entry at offset %#lx deleted\n", varEntry->existing_offset);
792
793 // Mark existing entry as VAR_IN_DELETED_TRANSITION
794 state = varHeader->state & VAR_DELETED & VAR_IN_DELETED_TRANSITION;
795
796 ret = _nvramController->write(varEntry->existing_offset + offsetof(struct v3_var_header, state), &state, sizeof(state));
797 require_noerr_action(ret, exit, DEBUG_ERROR("existing state w fail, ret=%#x\n", ret));
798 } else {
799 DEBUG_INFO("No existing, removing\n");
800 }
801
802 // not re-added to remainingEntries
803 } else {
804 DEBUG_INFO("skipping\n");
805 remainingEntries->setObject(entryContainer);
806 }
807 }
808
809 _varEntries.reset(remainingEntries.get(), OSRetain);
810
811 _newData = false;
812
813 exit:
814 return ret;
815 }
816
817 bool
sync(void)818 IONVRAMV3Handler::sync(void)
819 {
820 IOReturn ret;
821
822 ret = syncInternal();
823
824 if (ret != kIOReturnSuccess) {
825 ret = reclaim();
826 require_noerr_action(ret, exit, DEBUG_ERROR("Reclaim recovery failed, ret=%#x", ret));
827 }
828
829 exit:
830 return ret == kIOReturnSuccess;
831 }
832
833 uint32_t
getGeneration(void) const834 IONVRAMV3Handler::getGeneration(void) const
835 {
836 return _generation;
837 }
838
839 uint32_t
getVersion(void) const840 IONVRAMV3Handler::getVersion(void) const
841 {
842 return kNVRAMVersion3;
843 }
844
845 uint32_t
getSystemUsed(void) const846 IONVRAMV3Handler::getSystemUsed(void) const
847 {
848 return _systemUsed;
849 }
850
851 uint32_t
getCommonUsed(void) const852 IONVRAMV3Handler::getCommonUsed(void) const
853 {
854 return _commonUsed;
855 }
856
857 bool
convertObjectToProp(uint8_t * buffer,uint32_t * length,const char * propName,OSObject * propObject)858 IONVRAMV3Handler::convertObjectToProp(uint8_t *buffer, uint32_t *length,
859 const char *propName, OSObject *propObject)
860 {
861 uint32_t offset;
862 IONVRAMVariableType propType;
863 OSBoolean *tmpBoolean = nullptr;
864 OSNumber *tmpNumber = nullptr;
865 OSString *tmpString = nullptr;
866 OSData *tmpData = nullptr;
867
868 propType = getVariableType(propName);
869
870 // Get the size of the data.
871 offset = 0;
872 switch (propType) {
873 case kOFVariableTypeBoolean:
874 tmpBoolean = OSDynamicCast(OSBoolean, propObject);
875 if (tmpBoolean != nullptr) {
876 const char *bool_buf;
877 if (tmpBoolean->getValue()) {
878 bool_buf = "true";
879 } else {
880 bool_buf = "false";
881 }
882
883 offset = (uint32_t)strlen(bool_buf);
884
885 if (buffer) {
886 if (*length < offset) {
887 return false;
888 } else {
889 memcpy(buffer, bool_buf, offset);
890 }
891 }
892 }
893 break;
894
895 case kOFVariableTypeNumber:
896 tmpNumber = OSDynamicCast(OSNumber, propObject);
897 if (tmpNumber != nullptr) {
898 char num_buf[12];
899 char *end_buf = num_buf;
900 uint32_t tmpValue = tmpNumber->unsigned32BitValue();
901 if (tmpValue == 0xFFFFFFFF) {
902 end_buf += snprintf(end_buf, sizeof(num_buf), "-1");
903 } else if (tmpValue < 1000) {
904 end_buf += snprintf(end_buf, sizeof(num_buf), "%d", (uint32_t)tmpValue);
905 } else {
906 end_buf += snprintf(end_buf, sizeof(num_buf), "%#x", (uint32_t)tmpValue);
907 }
908
909 offset = (uint32_t)(end_buf - num_buf);
910 if (buffer) {
911 if (*length < offset) {
912 return false;
913 } else {
914 memcpy(buffer, num_buf, offset);
915 }
916 }
917 }
918 break;
919
920 case kOFVariableTypeString:
921 tmpString = OSDynamicCast(OSString, propObject);
922 if (tmpString != nullptr) {
923 offset = tmpString->getLength();
924
925 if (buffer) {
926 if (*length < offset) {
927 return false;
928 } else {
929 bcopy(tmpString->getCStringNoCopy(), buffer, offset);
930 }
931 }
932 }
933 break;
934
935 case kOFVariableTypeData:
936 tmpData = OSDynamicCast(OSData, propObject);
937 if (tmpData != nullptr) {
938 offset = tmpData->getLength();
939
940 if (buffer) {
941 if (*length < offset) {
942 return false;
943 } else {
944 bcopy(tmpData->getBytesNoCopy(), buffer, offset);
945 }
946 }
947 }
948 break;
949
950 default:
951 return false;
952 }
953
954 *length = offset;
955
956 return offset != 0;
957 }
958
959
960 bool
convertPropToObject(const uint8_t * propName,uint32_t propNameLength,const uint8_t * propData,uint32_t propDataLength,OSSharedPtr<const OSSymbol> & propSymbol,OSSharedPtr<OSObject> & propObject)961 IONVRAMV3Handler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength,
962 const uint8_t *propData, uint32_t propDataLength,
963 OSSharedPtr<const OSSymbol>& propSymbol,
964 OSSharedPtr<OSObject>& propObject)
965 {
966 OSSharedPtr<const OSSymbol> tmpSymbol;
967 OSSharedPtr<OSNumber> tmpNumber;
968 OSSharedPtr<OSString> tmpString;
969 OSSharedPtr<OSObject> tmpObject = nullptr;
970
971 tmpSymbol = OSSymbol::withCString((const char *)propName);
972
973 if (tmpSymbol == nullptr) {
974 return false;
975 }
976
977 switch (getVariableType(tmpSymbol.get())) {
978 case kOFVariableTypeBoolean:
979 if (!strncmp("true", (const char *)propData, propDataLength)) {
980 tmpObject.reset(kOSBooleanTrue, OSRetain);
981 } else if (!strncmp("false", (const char *)propData, propDataLength)) {
982 tmpObject.reset(kOSBooleanFalse, OSRetain);
983 }
984 break;
985
986 case kOFVariableTypeNumber:
987 tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
988 if (tmpNumber != nullptr) {
989 tmpObject = tmpNumber;
990 }
991 break;
992
993 case kOFVariableTypeString:
994 tmpString = OSString::withCString((const char *)propData, propDataLength);
995 if (tmpString != nullptr) {
996 tmpObject = tmpString;
997 }
998 break;
999
1000 case kOFVariableTypeData:
1001 tmpObject = OSData::withBytes(propData, propDataLength);
1002 break;
1003
1004 default:
1005 break;
1006 }
1007
1008 if (tmpObject == nullptr) {
1009 tmpSymbol.reset();
1010 return false;
1011 }
1012
1013 propSymbol = tmpSymbol;
1014 propObject = tmpObject;
1015
1016 return true;
1017 }
1018