]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IONVRAM.cpp
daa6cf7cb712f8df26fc4652b01148552a673c58
[apple/xnu.git] / iokit / Kernel / IONVRAM.cpp
1 /*
2 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
4 *
5 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. The rights granted to you under the License
11 * may not be used to create, or enable the creation or redistribution of,
12 * unlawful or unlicensed copies of an Apple operating system, or to
13 * circumvent, violate, or enable the circumvention or violation of, any
14 * terms of an Apple operating system software license agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 *
19 * The Original Code and all software distributed under the License are
20 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
24 * Please see the License for the specific language governing rights and
25 * limitations under the License.
26 *
27 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 */
29
30 #define IOKIT_ENABLE_SHARED_PTR
31
32 #include <AssertMacros.h>
33 #include <IOKit/IOLib.h>
34 #include <IOKit/IONVRAM.h>
35 #include <IOKit/IOPlatformExpert.h>
36 #include <IOKit/IOUserClient.h>
37 #include <IOKit/IOKitKeys.h>
38 #include <IOKit/IOKitKeysPrivate.h>
39 #include <IOKit/IOBSD.h>
40 #include <kern/debug.h>
41 #include <pexpert/boot.h>
42 #include <pexpert/pexpert.h>
43
44 #define super IOService
45
46 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
47
48 // Internal values
49 #define NVRAM_CHRP_SIG_APPLE 0x5A
50 #define NVRAM_CHRP_APPLE_HEADER_NAME "nvram"
51
52 // From Apple CHRP Spec
53 #define NVRAM_CHRP_SIG_SYSTEM 0x70
54 #define NVRAM_CHRP_SIG_CONFIG 0x71
55 #define NVRAM_CHRP_SIG_FREESPACE 0x7F
56
57 #define NVRAM_CHRP_PARTITION_NAME_COMMON "common"
58 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM "system"
59 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY "secure"
60 #define NVRAM_CHRP_PARTITION_NAME_FREESPACE "\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77"
61
62 #define NVRAM_CHRP_LENGTH_BLOCK_SIZE 0x10 // CHRP length field is in 16 byte blocks
63
64 typedef struct chrp_nvram_header { //16 bytes
65 uint8_t sig;
66 uint8_t cksum; // checksum on sig, len, and name
67 uint16_t len; // total length of the partition in 16 byte blocks starting with the signature
68 // and ending with the last byte of data area, ie len includes its own header size
69 char name[12];
70 uint8_t data[0];
71 } chrp_nvram_header_t;
72
73 typedef struct apple_nvram_header { // 16 + 16 bytes
74 struct chrp_nvram_header chrp;
75 uint32_t adler;
76 uint32_t generation;
77 uint8_t padding[8];
78 } apple_nvram_header_t;
79
80
81 #define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
82
83 OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
84
85 #if defined(DEBUG) || defined(DEVELOPMENT)
86 #define DEBUG_INFO(fmt, args...) \
87 ({ \
88 if (gNVRAMLogging) \
89 IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \
90 })
91
92 #define DEBUG_ALWAYS(fmt, args...) \
93 ({ \
94 IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \
95 })
96 #else
97 #define DEBUG_INFO(fmt, args...)
98 #define DEBUG_ALWAYS(fmt, args...)
99 #endif
100
101 #define DEBUG_ERROR DEBUG_ALWAYS
102
103 #define NVRAMLOCK() \
104 ({ \
105 if (preemption_enabled() && !panic_active()) \
106 IOLockLock(_variableLock); \
107 })
108
109 #define NVRAMUNLOCK() \
110 ({ \
111 if (preemption_enabled() && !panic_active()) \
112 IOLockUnlock(_variableLock); \
113 })
114
115 #define NVRAMLOCKASSERT() \
116 ({ \
117 if (preemption_enabled() && !panic_active()) \
118 IOLockAssert(_variableLock, kIOLockAssertOwned); \
119 })
120
121 typedef struct {
122 const char *name;
123 UInt32 offset;
124 UInt32 size;
125 OSSharedPtr<OSDictionary> &dict;
126 UInt8 *image;
127 } NVRAMRegionInfo;
128
129 // Guid for Apple System Boot variables
130 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
131 UUID_DEFINE(gAppleSystemVariableGuid, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16);
132
133 // Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID)
134 // 7C436110-AB2A-4BBB-A880-FE41995C9F82
135 UUID_DEFINE(gAppleNVRAMGuid, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
136
137 static bool gNVRAMLogging = false;
138
139 // allowlist variables from macboot that need to be set/get from system region if present
140 static const char * const gNVRAMSystemList[] = {
141 "adbe-tunable",
142 "adbe-tunables",
143 "adfe-tunables",
144 "alamo-path",
145 "alt-boot-volume",
146 "ASMB",
147 "atc0",
148 "atc1",
149 "auto-boot",
150 "auto-boot-halt-stage",
151 "auto-boot-once",
152 "auto-boot-usb",
153 "auxkc-path",
154 "backlight-level",
155 "backlight-nits",
156 "base-system-path",
157 "boot-args",
158 "boot-breadcrumbs",
159 "boot-command",
160 "boot-device",
161 "boot-image",
162 "boot-partition",
163 "boot-path",
164 "boot-ramdisk",
165 "boot-script",
166 "boot-volume",
167 "bootdelay",
168 "bt1addr",
169 "btaddr",
170 "cam-use-ext-ldo",
171 "CLCG_override",
172 "com.apple.System.boot-nonce",
173 "com.apple.System.rtc-offset",
174 "com.apple.System.tz0-size",
175 "core-bin-offset",
176 "cpu-bin-offset",
177 "darkboot",
178 "DClr_override",
179 "dcp-auto-boot",
180 "debug-gg",
181 "debug-soc",
182 "debug-uarts",
183 "diags-path",
184 "disable-boot-wdt",
185 "display-color-space",
186 "display-timing",
187 "display-vsh-comp",
188 "dpcd-max-brightness",
189 "dtdump",
190 "dtdump-path",
191 "e75",
192 "emu",
193 "enable-auth-debug",
194 "enable-jop",
195 "enable-marconi",
196 "enable-upgrade-fallback",
197 "enforce-iuob",
198 "eth1addr",
199 "ethaddr",
200 "failboot-breadcrumbs",
201 "fixed-lcm-boost",
202 "force-ctrr-lock",
203 "force-upgrade-fail",
204 "fuos-path",
205 "hib-ui-force",
206 "hibhack-test-hmac",
207 "iboot-data",
208 "iboot-failure-reason",
209 "iboot-failure-reason-str",
210 "iboot-failure-volume",
211 "iboot1-precommitted",
212 "idle-off",
213 "is-tethered",
214 "kaslr-off",
215 "kaslr-slide",
216 "kis-rsm",
217 "knobs",
218 "loadaddr",
219 "memmapdump",
220 "mipi-bridge-cmd-verify",
221 "mipi-bridge-poll-cmd-fifo",
222 "no-ctrr",
223 "one-time-boot-command",
224 "osenvironment",
225 "ota-breadcrumbs",
226 "ota-outcome",
227 "panicmedic",
228 "panicmedic-threshold",
229 "panicmedic-timestamps",
230 "phleet-path",
231 "pinot-panel-id",
232 "pintoaddr",
233 "policy-nonce-digests",
234 "preserve-debuggability",
235 "prevent-restores", // Keep for factory <rdar://problem/70476321>
236 "prev-lang:kbd",
237 "ramrod-kickstart-aces",
238 "rbdaddr0",
239 "rbm-path",
240 "reconfig-behavior",
241 "reconfig-breakpoints",
242 "recovery-boot-mode",
243 "recovery-breadcrumbs",
244 "restored-host-timeout",
245 "root-live-fs",
246 "rtos-path",
247 "soc-bin-offset",
248 "StartupMute",
249 "StartupMuteAccessibility",
250 "storage-prev-assert",
251 "storage-prev-assert-stored",
252 "summit-panel-id",
253 "SystemAudioVolume",
254 "SystemAudioVolumeExtension",
255 "SystemAudioVolumeSaved",
256 "tz0-size-override",
257 "upgrade-fallback-boot-command",
258 "upgrade-retry",
259 "usb-enabled",
260 "wifi1addr",
261 "wifiaddr",
262 nullptr
263 };
264
265 typedef struct {
266 const char *name;
267 IONVRAMVariableType type;
268 } VariableTypeEntry;
269
270 static const
271 VariableTypeEntry gVariableTypes[] = {
272 {"auto-boot?", kOFVariableTypeBoolean},
273 {"boot-args", kOFVariableTypeString},
274 {"boot-command", kOFVariableTypeString},
275 {"boot-device", kOFVariableTypeString},
276 {"boot-file", kOFVariableTypeString},
277 {"boot-screen", kOFVariableTypeString},
278 {"boot-script", kOFVariableTypeString},
279 {"console-screen", kOFVariableTypeString},
280 {"default-client-ip", kOFVariableTypeString},
281 {"default-gateway-ip", kOFVariableTypeString},
282 {"default-mac-address?", kOFVariableTypeBoolean},
283 {"default-router-ip", kOFVariableTypeString},
284 {"default-server-ip", kOFVariableTypeString},
285 {"default-subnet-mask", kOFVariableTypeString},
286 {"diag-device", kOFVariableTypeString},
287 {"diag-file", kOFVariableTypeString},
288 {"diag-switch?", kOFVariableTypeBoolean},
289 {"fcode-debug?", kOFVariableTypeBoolean},
290 {"input-device", kOFVariableTypeString},
291 {"input-device-1", kOFVariableTypeString},
292 {"little-endian?", kOFVariableTypeBoolean},
293 {"load-base", kOFVariableTypeNumber},
294 {"mouse-device", kOFVariableTypeString},
295 {"nvramrc", kOFVariableTypeString},
296 {"oem-banner", kOFVariableTypeString},
297 {"oem-banner?", kOFVariableTypeBoolean},
298 {"oem-logo", kOFVariableTypeString},
299 {"oem-logo?", kOFVariableTypeBoolean},
300 {"output-device", kOFVariableTypeString},
301 {"output-device-1", kOFVariableTypeString},
302 {"pci-probe-list", kOFVariableTypeNumber},
303 {"pci-probe-mask", kOFVariableTypeNumber},
304 {"real-base", kOFVariableTypeNumber},
305 {"real-mode?", kOFVariableTypeBoolean},
306 {"real-size", kOFVariableTypeNumber},
307 {"screen-#columns", kOFVariableTypeNumber},
308 {"screen-#rows", kOFVariableTypeNumber},
309 {"security-mode", kOFVariableTypeString},
310 {"selftest-#megs", kOFVariableTypeNumber},
311 {"use-generic?", kOFVariableTypeBoolean},
312 {"use-nvramrc?", kOFVariableTypeBoolean},
313 {"virt-base", kOFVariableTypeNumber},
314 {"virt-size", kOFVariableTypeNumber},
315
316 #if !defined(__x86_64__)
317 {"acc-cm-override-charger-count", kOFVariableTypeNumber},
318 {"acc-cm-override-count", kOFVariableTypeNumber},
319 {"acc-mb-ld-lifetime", kOFVariableTypeNumber},
320 {"com.apple.System.boot-nonce", kOFVariableTypeString},
321 {"darkboot", kOFVariableTypeBoolean},
322 {"enter-tdm-mode", kOFVariableTypeBoolean},
323 #endif /* !defined(__x86_64__) */
324 {nullptr, kOFVariableTypeData} // Default type to return
325 };
326
327 union VariablePermission {
328 struct {
329 uint64_t UserWrite :1;
330 uint64_t RootRequired :1;
331 uint64_t KernelOnly :1;
332 uint64_t ResetNVRAMOnlyDelete :1;
333 uint64_t NeverAllowedToDelete :1;
334 uint64_t FullAccess :1;
335 uint64_t Reserved:58;
336 } Bits;
337 uint64_t Uint64;
338 };
339
340 typedef struct {
341 const char *name;
342 VariablePermission p;
343 } VariablePermissionEntry;
344
345 static const
346 VariablePermissionEntry gVariablePermissions[] = {
347 {"aapl,pci", .p.Bits.RootRequired = 1},
348 {"battery-health", .p.Bits.RootRequired = 1,
349 .p.Bits.NeverAllowedToDelete = 1},
350 {"boot-image", .p.Bits.UserWrite = 1},
351 {"com.apple.System.fp-state", .p.Bits.KernelOnly = 1},
352 {"policy-nonce-digests", .p.Bits.ResetNVRAMOnlyDelete = 1},
353 {"security-password", .p.Bits.RootRequired = 1},
354
355 #if !defined(__x86_64__)
356 {"acc-cm-override-charger-count", .p.Bits.KernelOnly = 1},
357 {"acc-cm-override-count", .p.Bits.KernelOnly = 1},
358 {"acc-mb-ld-lifetime", .p.Bits.KernelOnly = 1},
359 {"backlight-level", .p.Bits.UserWrite = 1},
360 {"com.apple.System.boot-nonce", .p.Bits.KernelOnly = 1},
361 {"com.apple.System.sep.art", .p.Bits.KernelOnly = 1},
362 {"darkboot", .p.Bits.UserWrite = 1},
363 {"nonce-seeds", .p.Bits.KernelOnly = 1},
364 #endif /* !defined(__x86_64__) */
365
366 {nullptr, {.Bits.FullAccess = 1}} // Default access
367 };
368
369 static IONVRAMVariableType
370 getVariableType(const char *propName)
371 {
372 const VariableTypeEntry *entry;
373
374 entry = gVariableTypes;
375 while (entry->name != nullptr) {
376 if (strcmp(entry->name, propName) == 0) {
377 break;
378 }
379 entry++;
380 }
381
382 return entry->type;
383 }
384
385 static IONVRAMVariableType
386 getVariableType(const OSSymbol *propSymbol)
387 {
388 return getVariableType(propSymbol->getCStringNoCopy());
389 }
390
391 static VariablePermission
392 getVariablePermission(const char *propName)
393 {
394 const VariablePermissionEntry *entry;
395
396 entry = gVariablePermissions;
397 while (entry->name != nullptr) {
398 if (strcmp(entry->name, propName) == 0) {
399 break;
400 }
401 entry++;
402 }
403
404 return entry->p;
405 }
406
407 static bool
408 variableInAllowList(const char *varName)
409 {
410 unsigned int i = 0;
411
412 while (gNVRAMSystemList[i] != nullptr) {
413 if (strcmp(varName, gNVRAMSystemList[i]) == 0) {
414 return true;
415 }
416 i++;
417 }
418
419 return false;
420 }
421
422 static bool
423 verifyWriteSizeLimit(const uuid_t *varGuid, const char *variableName, size_t propDataSize)
424 {
425 if (variableInAllowList(variableName)) {
426 if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) {
427 return propDataSize <= 1024;
428 } else {
429 return propDataSize <= 768;
430 }
431 }
432
433 return true;
434 }
435
436 static bool
437 verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const char *varName)
438 {
439 VariablePermission perm;
440 bool kernel, admin, writeEntitled, readEntitled, allowList, systemGuid, systemEntitled;
441
442 perm = getVariablePermission(varName);
443
444 kernel = current_task() == kernel_task;
445
446 if (perm.Bits.KernelOnly) {
447 DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
448 return kernel;
449 }
450
451 allowList = variableInAllowList(varName);
452 systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0;
453 admin = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
454 writeEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMWriteAccessKey);
455 readEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMReadAccessKey);
456 systemEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMSystemAllowKey) || kernel;
457
458 switch (op) {
459 case kIONVRAMOperationRead:
460 if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
461 return true;
462 }
463 break;
464
465 case kIONVRAMOperationWrite:
466 if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
467 if (systemGuid) {
468 if (allowList) {
469 if (!systemEntitled) {
470 DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
471 }
472 } else if (!systemEntitled) {
473 DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
474 break;
475 }
476 }
477 return true;
478 }
479 break;
480
481 case kIONVRAMOperationDelete:
482 case kIONVRAMOperationObliterate:
483 case kIONVRAMOperationReset:
484 if (perm.Bits.NeverAllowedToDelete) {
485 DEBUG_INFO("Never allowed to delete %s\n", varName);
486 break;
487 } else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
488 DEBUG_INFO("Not allowed to obliterate %s\n", varName);
489 break;
490 }
491
492 if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
493 if (systemGuid) {
494 if (allowList) {
495 if (!systemEntitled) {
496 DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
497 }
498 } else if (!systemEntitled) {
499 DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
500 break;
501 }
502 }
503 return true;
504 }
505 break;
506 }
507
508 DEBUG_INFO("Permission for %s denied, kernel=%d, admin=%d, writeEntitled=%d, readEntitled=%d, systemGuid=%d, systemEntitled=%d\n",
509 varName, kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled);
510 return false;
511 }
512
513 static bool
514 verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const OSSymbol *varName)
515 {
516 return verifyPermission(op, varGuid, varName->getCStringNoCopy());
517 }
518
519 /*
520 * Parse a variable name of the form "GUID:name".
521 * If the name cannot be parsed, substitute the Apple global variable GUID.
522 * Returns TRUE if a GUID was found in the name, FALSE otherwise.
523 * The guidResult and nameResult arguments may be nullptr if you just want
524 * to check the format of the string.
525 */
526 static bool
527 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
528 {
529 uuid_string_t temp = {0};
530 size_t keyLen = strlen(key);
531 bool result = false;
532 const char *name = key;
533 uuid_t guid;
534
535 if (keyLen > sizeof(temp)) {
536 // check for at least UUID + ":" + more
537 memcpy(temp, key, sizeof(temp) - 1);
538
539 if ((uuid_parse(temp, guid) == 0) &&
540 (key[sizeof(temp) - 1] == ':')) {
541 name = key + sizeof(temp);
542 result = true;
543 }
544 }
545
546 if (guidResult) {
547 result ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
548 }
549 if (nameResult) {
550 *nameResult = name;
551 }
552
553 return false;
554 }
555
556 // private IOService based class for publishing distinct dictionary properties on
557 // for easy ioreg access since the serializeProperties call is overloaded and is used
558 // as variable access
559 class IODTNVRAMVariables : public IOService
560 {
561 OSDeclareDefaultStructors(IODTNVRAMVariables)
562 private:
563 IODTNVRAM *_provider;
564 OSDictionary *_properties;
565 uuid_t _guid;
566
567 public:
568 bool init(const uuid_t *guid);
569 virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
570 virtual IOReturn setProperties(OSObject * properties) APPLE_KEXT_OVERRIDE;
571 virtual bool serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
572 };
573
574 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
575
576 bool
577 IODTNVRAMVariables::init(const uuid_t *guid)
578 {
579 require(super::init(), error);
580 require(guid, error);
581
582 uuid_copy(_guid, *guid);
583
584 return true;
585
586 error:
587 return false;
588 }
589
590 bool
591 IODTNVRAMVariables::start(IOService * provider)
592 {
593 require(IOService::start(provider), error);
594
595 require(_provider = OSDynamicCast(IODTNVRAM, provider), error);
596
597 registerService();
598
599 return true;
600
601 error:
602 stop(provider);
603
604 return false;
605 }
606
607 IOReturn
608 IODTNVRAMVariables::setProperties(OSObject * properties)
609 {
610 if (OSDynamicCast(OSDictionary, properties)) {
611 OSSafeReleaseNULL(_properties);
612 _properties = OSDynamicCast(OSDictionary, properties);
613 properties->retain();
614 }
615
616 return IOService::setProperties(properties);
617 }
618
619 bool
620 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
621 {
622 const OSSymbol *key;
623 OSSharedPtr<OSDictionary> dict;
624 OSSharedPtr<OSCollectionIterator> iter;
625 OSSharedPtr<OSDictionary> localProperties(_properties, OSRetain);
626 bool result = false;
627
628 require(localProperties != nullptr, exit);
629
630 dict = OSDictionary::withCapacity(localProperties->getCount());
631 require_action(dict, exit, DEBUG_ERROR("No dictionary\n"));
632
633 iter = OSCollectionIterator::withCollection(localProperties.get());
634 require_action(iter, exit, DEBUG_ERROR("failed to create iterator\n"));
635
636 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
637 if (verifyPermission(kIONVRAMOperationRead, &_guid, key)) {
638 dict->setObject(key, localProperties->getObject(key));
639 }
640 }
641
642 result = dict->serialize(s);
643
644 exit:
645 DEBUG_INFO("result=%d\n", result);
646 return result;
647 }
648
649 bool
650 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
651 {
652 OSSharedPtr<OSDictionary> dict;
653
654 if (!super::init(old, plane)) {
655 return false;
656 }
657
658 _variableLock = IOLockAlloc();
659 if (!_variableLock) {
660 return false;
661 }
662
663 PE_parse_boot_argn("nvram-log", &gNVRAMLogging, sizeof(gNVRAMLogging));
664
665 dict = OSDictionary::withCapacity(1);
666 if (dict == nullptr) {
667 return false;
668 }
669 setPropertyTable(dict.get());
670 dict.reset();
671
672 _nvramSize = getNVRAMSize();
673 if (_nvramSize == 0) {
674 DEBUG_ERROR("NVRAM : Error - default size not specified in DT\n");
675 return false;
676 }
677 // partition offsets are UInt16 (bytes / 0x10) + 1
678 if (_nvramSize > 0xFFFF * 0x10) {
679 DEBUG_ERROR("NVRAM : truncating _nvramSize from %ld\n", (long) _nvramSize);
680 _nvramSize = 0xFFFF * 0x10;
681 }
682 _nvramImage = IONew(UInt8, _nvramSize);
683 if (_nvramImage == nullptr) {
684 return false;
685 }
686
687 _nvramPartitionOffsets = OSDictionary::withCapacity(1);
688 if (_nvramPartitionOffsets == nullptr) {
689 return false;
690 }
691
692 _nvramPartitionLengths = OSDictionary::withCapacity(1);
693 if (_nvramPartitionLengths == nullptr) {
694 return false;
695 }
696
697 _registryPropertiesKey = OSSymbol::withCStringNoCopy("aapl,pci");
698 if (_registryPropertiesKey == nullptr) {
699 return false;
700 }
701
702 // <rdar://problem/9529235> race condition possible between
703 // IODTNVRAM and IONVRAMController (restore loses boot-args)
704 initProxyData();
705
706 // Require at least the common partition to be present and error free
707 if (_commonDict == nullptr) {
708 return false;
709 }
710
711 return true;
712 }
713
714 void
715 IODTNVRAM::initProxyData(void)
716 {
717 OSSharedPtr<IORegistryEntry> entry;
718 const char *key = "nvram-proxy-data";
719 OSData *data;
720 const void *bytes;
721
722 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
723 if (entry != nullptr) {
724 OSSharedPtr<OSObject> prop = entry->copyProperty(key);
725 if (prop != nullptr) {
726 data = OSDynamicCast(OSData, prop.get());
727 if (data != nullptr) {
728 bytes = data->getBytesNoCopy();
729 if ((bytes != nullptr) && (data->getLength() <= _nvramSize)) {
730 bcopy(bytes, _nvramImage, data->getLength());
731 initNVRAMImage();
732 _isProxied = true;
733 }
734 }
735 }
736 entry->removeProperty(key);
737 }
738 }
739
740 UInt32
741 IODTNVRAM::getNVRAMSize(void)
742 {
743 OSSharedPtr<IORegistryEntry> entry;
744 const char *key = "nvram-total-size";
745 OSData *data;
746 UInt32 size = 0;
747
748 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
749 if (entry != nullptr) {
750 OSSharedPtr<OSObject> prop = entry->copyProperty(key);
751 if (prop != nullptr) {
752 data = OSDynamicCast(OSData, prop.get());
753 if (data != nullptr) {
754 size = *((UInt32*)data->getBytesNoCopy());
755 DEBUG_ALWAYS("NVRAM size is %u bytes\n", (unsigned int) size);
756 }
757 }
758 }
759 return size;
760 }
761
762
763 void
764 IODTNVRAM::registerNVRAMController(IONVRAMController *nvram)
765 {
766 if (_nvramController != nullptr) {
767 DEBUG_ERROR("Duplicate controller set\n");
768 return;
769 }
770
771 DEBUG_INFO("setting controller\n");
772
773 _nvramController = nvram;
774
775 // <rdar://problem/9529235> race condition possible between
776 // IODTNVRAM and IONVRAMController (restore loses boot-args)
777 if (!_isProxied) {
778 DEBUG_INFO("Proxied NVRAM data\n");
779 _nvramController->read(0, _nvramImage, _nvramSize);
780 initNVRAMImage();
781 }
782
783 if (_systemPartitionSize) {
784 _systemService = new IODTNVRAMVariables;
785
786 if (!_systemService || !_systemService->init(&gAppleSystemVariableGuid)) {
787 DEBUG_ERROR("Unable to start the system service!\n");
788 goto no_system;
789 }
790
791 _systemService->setName("options-system");
792
793 if (!_systemService->attach(this)) {
794 DEBUG_ERROR("Unable to attach the system service!\n");
795 OSSafeReleaseNULL(_systemService);
796 goto no_system;
797 }
798
799 if (!_systemService->start(this)) {
800 DEBUG_ERROR("Unable to start the system service!\n");
801 _systemService->detach(this);
802 OSSafeReleaseNULL(_systemService);
803 goto no_system;
804 }
805 }
806
807 no_system:
808 if (_commonPartitionSize) {
809 _commonService = new IODTNVRAMVariables;
810
811 if (!_commonService || !_commonService->init(&gAppleNVRAMGuid)) {
812 DEBUG_ERROR("Unable to start the common service!\n");
813 goto no_common;
814 }
815
816 _commonService->setName("options-common");
817
818 if (!_commonService->attach(this)) {
819 DEBUG_ERROR("Unable to attach the common service!\n");
820 OSSafeReleaseNULL(_commonService);
821 goto no_common;
822 }
823
824 if (!_commonService->start(this)) {
825 DEBUG_ERROR("Unable to start the common service!\n");
826 _systemService->detach(this);
827 OSSafeReleaseNULL(_commonService);
828 goto no_common;
829 }
830 }
831
832 no_common:
833 NVRAMLOCK();
834 (void) syncVariables();
835 NVRAMUNLOCK();
836 }
837
838 void
839 IODTNVRAM::initNVRAMImage(void)
840 {
841 char partitionID[18];
842 UInt32 partitionOffset, partitionLength;
843 UInt32 currentLength, currentOffset = 0;
844
845 _commonPartitionOffset = 0xFFFFFFFF;
846 _systemPartitionOffset = 0xFFFFFFFF;
847
848 // Look through the partitions to find the OF and System partitions.
849 while (currentOffset < _nvramSize) {
850 bool common_partition;
851 bool system_partition;
852
853 chrp_nvram_header_t * header = (chrp_nvram_header_t *)(_nvramImage + currentOffset);
854
855 currentLength = header->len * NVRAM_CHRP_LENGTH_BLOCK_SIZE;
856
857 if (currentLength < sizeof(chrp_nvram_header_t)) {
858 break;
859 }
860
861 partitionOffset = currentOffset + sizeof(chrp_nvram_header_t);
862 partitionLength = currentLength - sizeof(chrp_nvram_header_t);
863
864 if ((partitionOffset + partitionLength) > _nvramSize) {
865 break;
866 }
867
868 common_partition = memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_COMMON, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON)) == 0;
869 system_partition = (memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM)) == 0) ||
870 (memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY)) == 0);
871
872 if (common_partition) {
873 _commonPartitionOffset = partitionOffset;
874 _commonPartitionSize = partitionLength;
875 } else if (system_partition) {
876 _systemPartitionOffset = partitionOffset;
877 _systemPartitionSize = partitionLength;
878 } else {
879 OSSharedPtr<OSNumber> partitionOffsetNumber, partitionLengthNumber;
880
881 // Construct the partition ID from the signature and name.
882 snprintf(partitionID, sizeof(partitionID), "0x%02x,", header->sig);
883 strncpy(partitionID + 5, header->name, sizeof(header->name));
884 partitionID[17] = '\0';
885
886 partitionOffsetNumber = OSNumber::withNumber(partitionOffset, 32);
887 partitionLengthNumber = OSNumber::withNumber(partitionLength, 32);
888
889 // Save the partition offset and length
890 _nvramPartitionOffsets->setObject(partitionID, partitionOffsetNumber.get());
891 _nvramPartitionLengths->setObject(partitionID, partitionLengthNumber.get());
892 }
893 currentOffset += currentLength;
894 }
895
896 if (_commonPartitionOffset != 0xFFFFFFFF) {
897 _commonImage = _nvramImage + _commonPartitionOffset;
898 }
899
900 if (_systemPartitionOffset != 0xFFFFFFFF) {
901 _systemImage = _nvramImage + _systemPartitionOffset;
902 }
903
904 DEBUG_ALWAYS("NVRAM : ofPartitionOffset - 0x%x, ofPartitionSize - 0x%x, systemPartitionOffset - 0x%x, systemPartitionSize - 0x%x\n",
905 (unsigned int) _commonPartitionOffset, (unsigned int) _commonPartitionSize, (unsigned int) _systemPartitionOffset, (unsigned int) _systemPartitionSize);
906
907 _lastDeviceSync = 0;
908 _freshInterval = TRUE; // we will allow sync() even before the first 15 minutes have passed.
909
910 initVariables();
911 }
912
913 void
914 IODTNVRAM::syncInternal(bool rateLimit)
915 {
916 DEBUG_INFO("rateLimit=%d\n", rateLimit);
917
918 // Don't try to perform controller operations if none has been registered.
919 if (_nvramController == nullptr) {
920 return;
921 }
922
923 // Rate limit requests to sync. Drivers that need this rate limiting will
924 // shadow the data and only write to flash when they get a sync call
925 if (rateLimit && !safeToSync()) {
926 return;
927 }
928
929 DEBUG_INFO("Calling sync()\n");
930 NVRAMLOCK();
931 _nvramController->sync();
932 NVRAMUNLOCK();
933 }
934
935 void
936 IODTNVRAM::sync(void)
937 {
938 syncInternal(false);
939 }
940
941 bool
942 IODTNVRAM::serializeProperties(OSSerialize *s) const
943 {
944 const OSSymbol *key;
945 OSSharedPtr<OSDictionary> dict;
946 OSSharedPtr<OSCollectionIterator> iter;
947 bool result = false;
948 unsigned int totalCapacity = 0;
949
950 NVRAMLOCK();
951 if (_commonDict) {
952 totalCapacity += _commonDict->getCapacity();
953 }
954
955 if (_systemDict) {
956 totalCapacity += _systemDict->getCapacity();
957 }
958
959 dict = OSDictionary::withCapacity(totalCapacity);
960
961 if (dict == nullptr) {
962 DEBUG_ERROR("No dictionary\n");
963 goto unlock;
964 }
965
966 // Copy system entries first if present then copy unique common entries
967 if (_systemDict != nullptr) {
968 iter = OSCollectionIterator::withCollection(_systemDict.get());
969 if (iter == nullptr) {
970 DEBUG_ERROR("failed to create iterator\n");
971 goto unlock;
972 }
973
974 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
975 if (verifyPermission(kIONVRAMOperationRead, &gAppleSystemVariableGuid, key)) {
976 dict->setObject(key, _systemDict->getObject(key));
977 }
978 }
979
980 iter.reset();
981 }
982
983 if (_commonDict != nullptr) {
984 iter = OSCollectionIterator::withCollection(_commonDict.get());
985 if (iter == nullptr) {
986 DEBUG_ERROR("failed to create common iterator\n");
987 goto unlock;
988 }
989
990 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
991 if (dict->getObject(key) != nullptr) {
992 // Skip non uniques
993 continue;
994 }
995 if (verifyPermission(kIONVRAMOperationRead, &gAppleNVRAMGuid, key)) {
996 dict->setObject(key, _commonDict->getObject(key));
997 }
998 }
999 }
1000
1001 result = dict->serialize(s);
1002
1003 unlock:
1004 NVRAMUNLOCK();
1005
1006 DEBUG_INFO("result=%d\n", result);
1007
1008 return result;
1009 }
1010
1011 IOReturn
1012 IODTNVRAM::chooseDictionary(IONVRAMOperation operation, const uuid_t *varGuid, const char *variableName, OSDictionary **dict) const
1013 {
1014 if (_systemDict != nullptr) {
1015 bool systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0;
1016
1017 if (variableInAllowList(variableName)) {
1018 DEBUG_INFO("Using system dictionary due to allow list\n");
1019 if (!systemGuid) {
1020 DEBUG_ERROR("System GUID NOT used for %s\n", variableName);
1021 }
1022 *dict = _systemDict.get();
1023 } else if (systemGuid) {
1024 DEBUG_INFO("Using system dictionary via GUID\n");
1025 *dict = _systemDict.get();
1026 } else {
1027 DEBUG_INFO("Using common dictionary\n");
1028 *dict = _commonDict.get();
1029 }
1030 } else {
1031 DEBUG_INFO("Defaulting to common dictionary\n");
1032 *dict = _commonDict.get();
1033 }
1034
1035 return kIOReturnSuccess;
1036 }
1037
1038 bool
1039 IODTNVRAM::handleSpecialVariables(const char *name, uuid_t *guid, OSObject *obj, IOReturn *error)
1040 {
1041 IOReturn err = kIOReturnSuccess;
1042 bool special = false;
1043
1044 NVRAMLOCKASSERT();
1045
1046 if (strcmp(name, "ResetNVRam") == 0) {
1047 DEBUG_INFO("%s requested\n", name);
1048
1049 if (uuid_compare(*guid, gAppleSystemVariableGuid) == 0) {
1050 if (_systemDict != nullptr) {
1051 _systemDict->flushCollection();
1052 }
1053
1054 _commonDict->flushCollection();
1055 DEBUG_INFO("system & common dictionary flushed\n");
1056
1057 err = syncVariables();
1058 }
1059
1060 special = true;
1061 } else if (strcmp(name, "ObliterateNVRam") == 0) {
1062 DEBUG_INFO("%s requested\n", name);
1063
1064 if ((_systemDict != nullptr) && (uuid_compare(*guid, gAppleSystemVariableGuid) == 0)) {
1065 const OSSymbol *key;
1066 OSSharedPtr<OSDictionary> newDict;
1067 OSSharedPtr<OSCollectionIterator> iter;
1068
1069 newDict = OSDictionary::withCapacity(_systemDict->getCapacity());
1070 iter = OSCollectionIterator::withCollection(newDict.get());
1071 if ((newDict == nullptr) || (iter == nullptr)) {
1072 err = kIOReturnNoMemory;
1073 goto exit;
1074 }
1075
1076 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1077 const OSSymbol *key = OSDynamicCast(OSSymbol, iter->getNextObject());
1078 if (key == nullptr) {
1079 err = kIOReturnNoMemory;
1080 goto exit;
1081 }
1082
1083 if (!verifyPermission(kIONVRAMOperationObliterate, &gAppleSystemVariableGuid, key)) {
1084 newDict->setObject(key, _systemDict->getObject(key));
1085 }
1086 }
1087
1088 _systemDict = newDict;
1089
1090 DEBUG_INFO("system dictionary flushed\n");
1091 } else if (_commonDict != nullptr) {
1092 const OSSymbol *key;
1093 OSSharedPtr<OSDictionary> newDict;
1094 OSSharedPtr<OSCollectionIterator> iter;
1095
1096 newDict = OSDictionary::withCapacity(_commonDict->getCapacity());
1097 iter = OSCollectionIterator::withCollection(newDict.get());
1098 if ((newDict == nullptr) || (iter == nullptr)) {
1099 err = kIOReturnNoMemory;
1100 goto exit;
1101 }
1102
1103 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1104 if (!verifyPermission(kIONVRAMOperationObliterate, &gAppleNVRAMGuid, key)) {
1105 newDict->setObject(key, _commonDict->getObject(key));
1106 }
1107 }
1108
1109 _commonDict = newDict;
1110
1111 DEBUG_INFO("common dictionary flushed\n");
1112 }
1113
1114 special = true;
1115 err = syncVariables();
1116 }
1117
1118 exit:
1119 if (error) {
1120 *error = err;
1121 }
1122
1123 return special;
1124 }
1125
1126 OSSharedPtr<OSObject>
1127 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1128 {
1129 IOReturn result;
1130 const char *variableName;
1131 uuid_t varGuid;
1132 OSDictionary *dict;
1133 OSSharedPtr<OSObject> theObject = nullptr;
1134
1135 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1136
1137 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1138
1139 result = chooseDictionary(kIONVRAMOperationRead, &varGuid, variableName, &dict);
1140 if (result != kIOReturnSuccess) {
1141 goto exit;
1142 }
1143
1144 if (!verifyPermission(kIONVRAMOperationRead, &varGuid, variableName)) {
1145 DEBUG_INFO("Not privileged\n");
1146 goto exit;
1147 }
1148
1149 NVRAMLOCK();
1150 theObject.reset(dict->getObject(variableName), OSRetain);
1151 NVRAMUNLOCK();
1152
1153 if (theObject != nullptr) {
1154 DEBUG_INFO("found data\n");
1155 }
1156
1157 exit:
1158 return theObject;
1159 }
1160
1161 OSSharedPtr<OSObject>
1162 IODTNVRAM::copyProperty(const char *aKey) const
1163 {
1164 OSSharedPtr<const OSSymbol> keySymbol;
1165 OSSharedPtr<OSObject> theObject;
1166
1167 keySymbol = OSSymbol::withCString(aKey);
1168 if (keySymbol != nullptr) {
1169 theObject = copyProperty(keySymbol.get());
1170 }
1171
1172 return theObject;
1173 }
1174
1175 OSObject *
1176 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1177 {
1178 // The shared pointer gets released at the end of the function,
1179 // and returns a view into theObject.
1180 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1181
1182 return theObject.get();
1183 }
1184
1185 OSObject *
1186 IODTNVRAM::getProperty(const char *aKey) const
1187 {
1188 // The shared pointer gets released at the end of the function,
1189 // and returns a view into theObject.
1190 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1191
1192 return theObject.get();
1193 }
1194
1195 IOReturn
1196 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1197 {
1198 IOReturn result = kIOReturnSuccess;
1199 bool remove = false;
1200 OSString *tmpString = nullptr;
1201 OSSharedPtr<OSObject> propObject, oldObject;
1202 OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1203 const char *variableName;
1204 uuid_t varGuid;
1205 OSDictionary *dict;
1206 bool deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
1207 size_t propDataSize = 0;
1208
1209 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1210
1211 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1212 deletePropertyKey = strncmp(variableName, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1213 syncNowPropertyKey = strncmp(variableName, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1214 forceSyncNowPropertyKey = strncmp(variableName, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1215
1216 if (deletePropertyKey) {
1217 tmpString = OSDynamicCast(OSString, anObject);
1218 if (tmpString != nullptr) {
1219 DEBUG_INFO("kIONVRAMDeletePropertyKey found\n");
1220 OSSharedPtr<const OSSymbol> sharedKey = OSSymbol::withString(tmpString);
1221 removeProperty(sharedKey.get());
1222 } else {
1223 DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1224 result = kIOReturnError;
1225 }
1226 goto exit;
1227 } else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1228 tmpString = OSDynamicCast(OSString, anObject);
1229 DEBUG_INFO("NVRAM sync key %s found\n", aKey->getCStringNoCopy());
1230 if (tmpString != nullptr) {
1231 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1232 syncInternal(syncNowPropertyKey);
1233 } else {
1234 DEBUG_INFO("%s value needs to be an OSString\n", variableName);
1235 result = kIOReturnError;
1236 }
1237 goto exit;
1238 }
1239
1240 result = chooseDictionary(kIONVRAMOperationWrite, &varGuid, variableName, &dict);
1241 if (result != kIOReturnSuccess) {
1242 goto exit;
1243 }
1244
1245 if (!verifyPermission(kIONVRAMOperationWrite, &varGuid, variableName)) {
1246 DEBUG_INFO("Not privileged\n");
1247 result = kIOReturnNotPrivileged;
1248 goto exit;
1249 }
1250
1251 // Make sure the object is of the correct type.
1252 switch (getVariableType(variableName)) {
1253 case kOFVariableTypeBoolean:
1254 propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1255 break;
1256
1257 case kOFVariableTypeNumber:
1258 propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1259 break;
1260
1261 case kOFVariableTypeString:
1262 propObject = OSDynamicPtrCast<OSString>(sharedObject);
1263 if (propObject != nullptr) {
1264 propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1265
1266 if (aKey->isEqualTo(kIONVRAMBootArgsKey) && (propDataSize >= BOOT_LINE_LENGTH)) {
1267 DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1268 result = kIOReturnNoSpace;
1269 goto exit;
1270 }
1271 }
1272 break;
1273
1274 case kOFVariableTypeData:
1275 propObject = OSDynamicPtrCast<OSData>(sharedObject);
1276 if (propObject == nullptr) {
1277 tmpString = OSDynamicCast(OSString, sharedObject.get());
1278 if (tmpString != nullptr) {
1279 propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1280 tmpString->getLength());
1281 }
1282 }
1283
1284 if (propObject != nullptr) {
1285 propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1286 }
1287
1288 #if defined(XNU_TARGET_OS_OSX)
1289 if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1290 remove = true;
1291 }
1292 #endif /* defined(XNU_TARGET_OS_OSX) */
1293 break;
1294 default:
1295 break;
1296 }
1297
1298 if (propObject == nullptr) {
1299 DEBUG_INFO("No property object\n");
1300 result = kIOReturnBadArgument;
1301 goto exit;
1302 }
1303
1304 if (!verifyWriteSizeLimit(&varGuid, variableName, propDataSize)) {
1305 DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, variableName);
1306 result = kIOReturnNoSpace;
1307 goto exit;
1308 }
1309
1310 NVRAMLOCK();
1311
1312 if (handleSpecialVariables(variableName, &varGuid, propObject.get(), &result)) {
1313 goto unlock;
1314 }
1315
1316 oldObject.reset(dict->getObject(variableName), OSRetain);
1317 if (remove == false) {
1318 DEBUG_INFO("Adding object\n");
1319 if (!dict->setObject(variableName, propObject.get())) {
1320 result = kIOReturnBadArgument;
1321 }
1322 } else {
1323 DEBUG_INFO("Removing object\n");
1324 // Check for existence so we can decide whether we need to sync variables
1325 if (oldObject) {
1326 result = removePropertyInternal(aKey);
1327 } else {
1328 result = kIOReturnNotFound;
1329 }
1330 }
1331
1332 if (result == kIOReturnSuccess) {
1333 result = syncVariables();
1334 if (result != kIOReturnSuccess) {
1335 DEBUG_ERROR("syncVariables failed, result=0x%08x\n", result);
1336 if (oldObject) {
1337 dict->setObject(variableName, oldObject.get());
1338 } else {
1339 dict->removeObject(variableName);
1340 }
1341 (void) syncVariables();
1342 result = kIOReturnNoMemory;
1343 }
1344 }
1345
1346 if (oldObject) {
1347 oldObject.reset();
1348 }
1349 if (tmpString) {
1350 propObject.reset();
1351 }
1352
1353 unlock:
1354 NVRAMUNLOCK();
1355
1356 exit:
1357 DEBUG_INFO("result=0x%08x\n", result);
1358
1359 return result;
1360 }
1361
1362 bool
1363 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1364 {
1365 return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1366 }
1367
1368 void
1369 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1370 {
1371 IOReturn ret;
1372
1373 NVRAMLOCK();
1374
1375 ret = removePropertyInternal(aKey);
1376
1377 NVRAMUNLOCK();
1378
1379 if (ret != kIOReturnSuccess) {
1380 DEBUG_INFO("removePropertyInternal failed, ret=0x%08x\n", ret);
1381 }
1382 }
1383
1384 IOReturn
1385 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
1386 {
1387 IOReturn result;
1388 const char *variableName;
1389 uuid_t varGuid;
1390 OSDictionary *dict;
1391
1392 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1393
1394 NVRAMLOCKASSERT();
1395
1396 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1397
1398 result = chooseDictionary(kIONVRAMOperationDelete, &varGuid, variableName, &dict);
1399 if (result != kIOReturnSuccess) {
1400 goto exit;
1401 }
1402
1403 if (!verifyPermission(kIONVRAMOperationDelete, &varGuid, variableName)) {
1404 DEBUG_INFO("Not priveleged\n");
1405 result = kIOReturnNotPrivileged;
1406 goto exit;
1407 }
1408
1409 // If the object exists, remove it from the dictionary.
1410 if (dict->getObject(variableName) != nullptr) {
1411 dict->removeObject(variableName);
1412 result = syncVariables();
1413 }
1414
1415 exit:
1416 return result;
1417 }
1418
1419 IOReturn
1420 IODTNVRAM::setProperties(OSObject *properties)
1421 {
1422 IOReturn result = kIOReturnSuccess;
1423 OSObject *object;
1424 const OSSymbol *key;
1425 OSDictionary *dict;
1426 OSSharedPtr<OSCollectionIterator> iter;
1427
1428 dict = OSDynamicCast(OSDictionary, properties);
1429 if (dict == nullptr) {
1430 DEBUG_ERROR("Not a dictionary\n");
1431 return kIOReturnBadArgument;
1432 }
1433
1434 iter = OSCollectionIterator::withCollection(dict);
1435 if (iter == nullptr) {
1436 DEBUG_ERROR("Couldn't create iterator\n");
1437 return kIOReturnBadArgument;
1438 }
1439
1440 while (result == kIOReturnSuccess) {
1441 key = OSDynamicCast(OSSymbol, iter->getNextObject());
1442 if (key == nullptr) {
1443 break;
1444 }
1445
1446 object = dict->getObject(key);
1447 if (object == nullptr) {
1448 continue;
1449 }
1450
1451 result = setPropertyInternal(key, object);
1452 }
1453
1454 DEBUG_INFO("result=0x%08x\n", result);
1455
1456 return result;
1457 }
1458
1459 IOReturn
1460 IODTNVRAM::readXPRAM(IOByteCount offset, UInt8 *buffer,
1461 IOByteCount length)
1462 {
1463 return kIOReturnUnsupported;
1464 }
1465
1466 IOReturn
1467 IODTNVRAM::writeXPRAM(IOByteCount offset, UInt8 *buffer,
1468 IOByteCount length)
1469 {
1470 return kIOReturnUnsupported;
1471 }
1472
1473 IOReturn
1474 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
1475 const OSSymbol **name,
1476 OSData **value)
1477 {
1478 IOReturn err;
1479
1480 err = readNVRAMPropertyType1(entry, name, value);
1481
1482 return err;
1483 }
1484
1485 IOReturn
1486 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
1487 const OSSymbol *name,
1488 OSData *value)
1489 {
1490 IOReturn err;
1491
1492 err = writeNVRAMPropertyType1(entry, name, value);
1493
1494 return err;
1495 }
1496
1497 OSDictionary *
1498 IODTNVRAM::getNVRAMPartitions(void)
1499 {
1500 return _nvramPartitionLengths.get();
1501 }
1502
1503 IOReturn
1504 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
1505 IOByteCount offset, UInt8 *buffer,
1506 IOByteCount length)
1507 {
1508 OSNumber *partitionOffsetNumber, *partitionLengthNumber;
1509 UInt32 partitionOffset, partitionLength, end;
1510
1511 partitionOffsetNumber =
1512 (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
1513 partitionLengthNumber =
1514 (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
1515
1516 if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
1517 return kIOReturnNotFound;
1518 }
1519
1520 partitionOffset = partitionOffsetNumber->unsigned32BitValue();
1521 partitionLength = partitionLengthNumber->unsigned32BitValue();
1522
1523 if (os_add_overflow(offset, length, &end)) {
1524 return kIOReturnBadArgument;
1525 }
1526 if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
1527 return kIOReturnBadArgument;
1528 }
1529
1530 bcopy(_nvramImage + partitionOffset + offset, buffer, length);
1531
1532 return kIOReturnSuccess;
1533 }
1534
1535 IOReturn
1536 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
1537 IOByteCount offset, UInt8 *buffer,
1538 IOByteCount length)
1539 {
1540 OSNumber *partitionOffsetNumber, *partitionLengthNumber;
1541 UInt32 partitionOffset, partitionLength, end;
1542
1543 partitionOffsetNumber =
1544 (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
1545 partitionLengthNumber =
1546 (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
1547
1548 if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
1549 return kIOReturnNotFound;
1550 }
1551
1552 partitionOffset = partitionOffsetNumber->unsigned32BitValue();
1553 partitionLength = partitionLengthNumber->unsigned32BitValue();
1554
1555 if (os_add_overflow(offset, length, &end)) {
1556 return kIOReturnBadArgument;
1557 }
1558 if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
1559 return kIOReturnBadArgument;
1560 }
1561
1562 bcopy(buffer, _nvramImage + partitionOffset + offset, length);
1563
1564 if (_nvramController != nullptr) {
1565 _nvramController->write(0, _nvramImage, _nvramSize);
1566 }
1567
1568 return kIOReturnSuccess;
1569 }
1570
1571 IOByteCount
1572 IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length)
1573 {
1574 return 0;
1575 }
1576
1577 // Private methods
1578
1579 UInt8
1580 IODTNVRAM::calculatePartitionChecksum(UInt8 *partitionHeader)
1581 {
1582 UInt8 cnt, isum, csum = 0;
1583
1584 for (cnt = 0; cnt < 0x10; cnt++) {
1585 isum = csum + partitionHeader[cnt];
1586 if (isum < csum) {
1587 isum++;
1588 }
1589 csum = isum;
1590 }
1591
1592 return csum;
1593 }
1594
1595 IOReturn
1596 IODTNVRAM::initVariables(void)
1597 {
1598 UInt32 cnt;
1599 UInt8 *propName, *propData;
1600 UInt32 propNameLength, propDataLength, regionIndex;
1601 OSSharedPtr<const OSSymbol> propSymbol;
1602 OSSharedPtr<OSObject> propObject;
1603 NVRAMRegionInfo *currentRegion;
1604
1605 NVRAMRegionInfo variableRegions[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
1606 { NVRAM_CHRP_PARTITION_NAME_SYSTEM, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
1607
1608 DEBUG_INFO("...\n");
1609
1610 for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
1611 currentRegion = &variableRegions[regionIndex];
1612
1613 if (currentRegion->size == 0) {
1614 continue;
1615 }
1616
1617 currentRegion->dict = OSDictionary::withCapacity(1);
1618
1619 DEBUG_INFO("region = %s\n", currentRegion->name);
1620 cnt = 0;
1621 while (cnt < currentRegion->size) {
1622 // Break if there is no name.
1623 if (currentRegion->image[cnt] == '\0') {
1624 break;
1625 }
1626
1627 // Find the length of the name.
1628 propName = currentRegion->image + cnt;
1629 for (propNameLength = 0; (cnt + propNameLength) < currentRegion->size;
1630 propNameLength++) {
1631 if (currentRegion->image[cnt + propNameLength] == '=') {
1632 break;
1633 }
1634 }
1635
1636 // Break if the name goes past the end of the partition.
1637 if ((cnt + propNameLength) >= currentRegion->size) {
1638 break;
1639 }
1640 cnt += propNameLength + 1;
1641
1642 propData = currentRegion->image + cnt;
1643 for (propDataLength = 0; (cnt + propDataLength) < currentRegion->size;
1644 propDataLength++) {
1645 if (currentRegion->image[cnt + propDataLength] == '\0') {
1646 break;
1647 }
1648 }
1649
1650 // Break if the data goes past the end of the partition.
1651 if ((cnt + propDataLength) >= currentRegion->size) {
1652 break;
1653 }
1654 cnt += propDataLength + 1;
1655
1656 if (convertPropToObject(propName, propNameLength,
1657 propData, propDataLength,
1658 propSymbol, propObject)) {
1659 DEBUG_INFO("adding %s, dataLength=%u\n", propSymbol.get()->getCStringNoCopy(), (unsigned int)propDataLength);
1660 currentRegion->dict.get()->setObject(propSymbol.get(), propObject.get());
1661 }
1662 }
1663 }
1664
1665 // Create the boot-args property if it is not in the dictionary.
1666 if (_commonDict->getObject(kIONVRAMBootArgsKey) == nullptr) {
1667 propObject = OSString::withCStringNoCopy("");
1668 if (propObject != nullptr) {
1669 _commonDict->setObject(kIONVRAMBootArgsKey, propObject.get());
1670 }
1671 }
1672
1673 DEBUG_INFO("%s _commonDict=%p _systemDict=%p\n", __FUNCTION__, _commonDict.get(), _systemDict.get());
1674
1675 return kIOReturnSuccess;
1676 }
1677
1678 IOReturn
1679 IODTNVRAM::syncOFVariables(void)
1680 {
1681 return kIOReturnUnsupported;
1682 }
1683
1684 IOReturn
1685 IODTNVRAM::syncVariables(void)
1686 {
1687 bool ok;
1688 UInt32 length, maxLength, regionIndex;
1689 UInt8 *buffer, *tmpBuffer;
1690 const OSSymbol *tmpSymbol;
1691 OSObject *tmpObject;
1692 OSSharedPtr<OSCollectionIterator> iter;
1693 NVRAMRegionInfo *currentRegion;
1694
1695 NVRAMRegionInfo variableRegions[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
1696 { NVRAM_CHRP_PARTITION_NAME_SYSTEM, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
1697
1698 NVRAMLOCKASSERT();
1699
1700 if (_systemPanicked) {
1701 return kIOReturnNotReady;
1702 }
1703
1704 if (_nvramController == nullptr) {
1705 DEBUG_ERROR("No _nvramController\n");
1706 return kIOReturnNotReady;
1707 }
1708
1709 DEBUG_INFO("...\n");
1710
1711 for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
1712 OSSharedPtr<OSNumber> sizeUsed;
1713 currentRegion = &variableRegions[regionIndex];
1714
1715 if (currentRegion->size == 0) {
1716 continue;
1717 }
1718
1719 DEBUG_INFO("region = %s\n", currentRegion->name);
1720 buffer = tmpBuffer = IONew(UInt8, currentRegion->size);
1721 if (buffer == nullptr) {
1722 return kIOReturnNoMemory;
1723 }
1724 bzero(buffer, currentRegion->size);
1725
1726 ok = true;
1727 maxLength = currentRegion->size;
1728
1729 iter = OSCollectionIterator::withCollection(currentRegion->dict.get());
1730 if (iter == nullptr) {
1731 ok = false;
1732 }
1733
1734 while (ok) {
1735 tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
1736 if (tmpSymbol == nullptr) {
1737 break;
1738 }
1739
1740 DEBUG_INFO("adding variable %s\n", tmpSymbol->getCStringNoCopy());
1741
1742 tmpObject = currentRegion->dict->getObject(tmpSymbol);
1743
1744 length = maxLength;
1745 ok = convertObjectToProp(tmpBuffer, &length, tmpSymbol, tmpObject);
1746 if (ok) {
1747 tmpBuffer += length;
1748 maxLength -= length;
1749 }
1750 }
1751
1752 if (ok) {
1753 bcopy(buffer, currentRegion->image, currentRegion->size);
1754 }
1755
1756 IODelete(buffer, UInt8, currentRegion->size);
1757
1758 sizeUsed = OSNumber::withNumber(maxLength, 32);
1759 _nvramController->setProperty(currentRegion->name, sizeUsed.get());
1760 sizeUsed.reset();
1761
1762 if ((strncmp(currentRegion->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM)) == 0) &&
1763 (_systemService != nullptr)) {
1764 _systemService->setProperties(_systemDict.get());
1765 } else if ((strncmp(currentRegion->name, NVRAM_CHRP_PARTITION_NAME_COMMON, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON)) == 0) &&
1766 (_commonService != nullptr)) {
1767 _commonService->setProperties(_commonDict.get());
1768 }
1769
1770 if (!ok) {
1771 return kIOReturnBadArgument;
1772 }
1773 }
1774
1775 DEBUG_INFO("ok=%d\n", ok);
1776
1777 return _nvramController->write(0, _nvramImage, _nvramSize);
1778 }
1779
1780 UInt32
1781 IODTNVRAM::getOFVariableType(const char *propName) const
1782 {
1783 return 0;
1784 }
1785
1786 UInt32
1787 IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
1788 {
1789 return 0;
1790 }
1791
1792
1793 UInt32
1794 IODTNVRAM::getOFVariablePerm(const char *propName) const
1795 {
1796 return 0;
1797 }
1798
1799 UInt32
1800 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
1801 {
1802 return 0;
1803 }
1804
1805 bool
1806 IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
1807 UInt32 *propType, UInt32 *propOffset)
1808 {
1809 /* UNSUPPORTED */
1810 return false;
1811 }
1812 bool
1813 IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
1814 UInt8 *propData, UInt32 propDataLength,
1815 const OSSymbol **propSymbol,
1816 OSObject **propObject)
1817 {
1818 OSSharedPtr<const OSSymbol> tmpSymbol;
1819 OSSharedPtr<OSNumber> tmpNumber;
1820 OSSharedPtr<OSString> tmpString;
1821 OSSharedPtr<OSObject> tmpObject = nullptr;
1822
1823 propName[propNameLength] = '\0';
1824 tmpSymbol = OSSymbol::withCString((const char *)propName);
1825 propName[propNameLength] = '=';
1826 if (tmpSymbol == nullptr) {
1827 return false;
1828 }
1829
1830 switch (getVariableType(tmpSymbol.get())) {
1831 case kOFVariableTypeBoolean:
1832 if (!strncmp("true", (const char *)propData, propDataLength)) {
1833 tmpObject.reset(kOSBooleanTrue, OSRetain);
1834 } else if (!strncmp("false", (const char *)propData, propDataLength)) {
1835 tmpObject.reset(kOSBooleanFalse, OSRetain);
1836 }
1837 break;
1838
1839 case kOFVariableTypeNumber:
1840 tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
1841 if (tmpNumber != nullptr) {
1842 tmpObject = tmpNumber;
1843 }
1844 break;
1845
1846 case kOFVariableTypeString:
1847 tmpString = OSString::withCString((const char *)propData);
1848 if (tmpString != nullptr) {
1849 tmpObject = tmpString;
1850 }
1851 break;
1852
1853 case kOFVariableTypeData:
1854 tmpObject = unescapeBytesToData(propData, propDataLength);
1855 break;
1856
1857 default:
1858 break;
1859 }
1860
1861 if (tmpObject == nullptr) {
1862 tmpSymbol.reset();
1863 return false;
1864 }
1865
1866 *propSymbol = tmpSymbol.detach();
1867 *propObject = tmpObject.detach();
1868
1869 return true;
1870 }
1871
1872 bool
1873 IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
1874 UInt8 *propData, UInt32 propDataLength,
1875 OSSharedPtr<const OSSymbol>& propSymbol,
1876 OSSharedPtr<OSObject>& propObject)
1877 {
1878 const OSSymbol* propSymbolRaw = nullptr;
1879 OSObject* propObjectRaw = nullptr;
1880 bool result = convertPropToObject(propName, propNameLength, propData, propDataLength,
1881 &propSymbolRaw, &propObjectRaw);
1882 propSymbol.reset(propSymbolRaw, OSNoRetain);
1883 propObject.reset(propObjectRaw, OSNoRetain);
1884 return result;
1885 }
1886
1887 bool
1888 IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
1889 const OSSymbol *propSymbol, OSObject *propObject)
1890 {
1891 const UInt8 *propName;
1892 UInt32 propNameLength, propDataLength, remaining;
1893 IONVRAMVariableType propType;
1894 OSBoolean *tmpBoolean = nullptr;
1895 OSNumber *tmpNumber = nullptr;
1896 OSString *tmpString = nullptr;
1897 OSSharedPtr<OSData> tmpData;
1898
1899 propName = (const UInt8 *)propSymbol->getCStringNoCopy();
1900 propNameLength = propSymbol->getLength();
1901 propType = getVariableType(propSymbol);
1902
1903 // Get the size of the data.
1904 propDataLength = 0xFFFFFFFF;
1905 switch (propType) {
1906 case kOFVariableTypeBoolean:
1907 tmpBoolean = OSDynamicCast(OSBoolean, propObject);
1908 if (tmpBoolean != nullptr) {
1909 propDataLength = 5;
1910 }
1911 break;
1912
1913 case kOFVariableTypeNumber:
1914 tmpNumber = OSDynamicCast(OSNumber, propObject);
1915 if (tmpNumber != nullptr) {
1916 propDataLength = 10;
1917 }
1918 break;
1919
1920 case kOFVariableTypeString:
1921 tmpString = OSDynamicCast(OSString, propObject);
1922 if (tmpString != nullptr) {
1923 propDataLength = tmpString->getLength();
1924 }
1925 break;
1926
1927 case kOFVariableTypeData:
1928 tmpData.reset(OSDynamicCast(OSData, propObject), OSNoRetain);
1929 if (tmpData != nullptr) {
1930 tmpData = escapeDataToData(tmpData.detach());
1931 propDataLength = tmpData->getLength();
1932 }
1933 break;
1934
1935 default:
1936 break;
1937 }
1938
1939 // Make sure the propertySize is known and will fit.
1940 if (propDataLength == 0xFFFFFFFF) {
1941 return false;
1942 }
1943 if ((propNameLength + propDataLength + 2) > *length) {
1944 return false;
1945 }
1946
1947 // Copy the property name equal sign.
1948 buffer += snprintf((char *)buffer, *length, "%s=", propName);
1949 remaining = *length - propNameLength - 1;
1950
1951 switch (propType) {
1952 case kOFVariableTypeBoolean:
1953 if (tmpBoolean->getValue()) {
1954 strlcpy((char *)buffer, "true", remaining);
1955 } else {
1956 strlcpy((char *)buffer, "false", remaining);
1957 }
1958 break;
1959
1960 case kOFVariableTypeNumber:
1961 {
1962 uint32_t tmpValue = tmpNumber->unsigned32BitValue();
1963 if (tmpValue == 0xFFFFFFFF) {
1964 strlcpy((char *)buffer, "-1", remaining);
1965 } else if (tmpValue < 1000) {
1966 snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
1967 } else {
1968 snprintf((char *)buffer, remaining, "0x%x", (uint32_t)tmpValue);
1969 }
1970 }
1971 break;
1972
1973 case kOFVariableTypeString:
1974 strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
1975 break;
1976
1977 case kOFVariableTypeData:
1978 bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
1979 tmpData.reset();
1980 break;
1981
1982 default:
1983 break;
1984 }
1985
1986 propDataLength = ((UInt32) strlen((const char *)buffer));
1987
1988 *length = propNameLength + propDataLength + 2;
1989
1990 return true;
1991 }
1992
1993
1994 UInt16
1995 IODTNVRAM::generateOWChecksum(UInt8 *buffer)
1996 {
1997 UInt32 cnt, checksum = 0;
1998 UInt16 *tmpBuffer = (UInt16 *)buffer;
1999
2000 for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
2001 checksum += tmpBuffer[cnt];
2002 }
2003
2004 return checksum % 0x0000FFFF;
2005 }
2006
2007 bool
2008 IODTNVRAM::validateOWChecksum(UInt8 *buffer)
2009 {
2010 UInt32 cnt, checksum, sum = 0;
2011 UInt16 *tmpBuffer = (UInt16 *)buffer;
2012
2013 for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
2014 sum += tmpBuffer[cnt];
2015 }
2016
2017 checksum = (sum >> 16) + (sum & 0x0000FFFF);
2018 if (checksum == 0x10000) {
2019 checksum--;
2020 }
2021 checksum = (checksum ^ 0x0000FFFF) & 0x0000FFFF;
2022
2023 return checksum == 0;
2024 }
2025
2026 void
2027 IODTNVRAM::updateOWBootArgs(const OSSymbol *key, OSObject *value)
2028 {
2029 /* UNSUPPORTED */
2030 }
2031
2032 bool
2033 IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor *hdr, UInt32 *where)
2034 {
2035 return false;
2036 }
2037
2038 IOReturn
2039 IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry *entry,
2040 const OSSymbol **name,
2041 OSData **value)
2042 {
2043 return kIOReturnUnsupported;
2044 }
2045
2046
2047 IOReturn
2048 IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry *entry,
2049 const OSSymbol *name,
2050 OSData *value)
2051 {
2052 return kIOReturnUnsupported;
2053 }
2054
2055
2056 OSSharedPtr<OSData>
2057 IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length)
2058 {
2059 OSSharedPtr<OSData> data;
2060 UInt32 totalLength = 0;
2061 UInt32 cnt, cnt2;
2062 UInt8 byte;
2063 bool ok;
2064
2065 // Calculate the actual length of the data.
2066 ok = true;
2067 totalLength = 0;
2068 for (cnt = 0; cnt < length;) {
2069 byte = bytes[cnt++];
2070 if (byte == 0xFF) {
2071 byte = bytes[cnt++];
2072 if (byte == 0x00) {
2073 ok = false;
2074 break;
2075 }
2076 cnt2 = byte & 0x7F;
2077 } else {
2078 cnt2 = 1;
2079 }
2080 totalLength += cnt2;
2081 }
2082
2083 if (ok) {
2084 // Create an empty OSData of the correct size.
2085 data = OSData::withCapacity(totalLength);
2086 if (data != nullptr) {
2087 for (cnt = 0; cnt < length;) {
2088 byte = bytes[cnt++];
2089 if (byte == 0xFF) {
2090 byte = bytes[cnt++];
2091 cnt2 = byte & 0x7F;
2092 byte = (byte & 0x80) ? 0xFF : 0x00;
2093 } else {
2094 cnt2 = 1;
2095 }
2096 data->appendByte(byte, cnt2);
2097 }
2098 }
2099 }
2100
2101 return data;
2102 }
2103
2104 OSSharedPtr<OSData>
2105 IODTNVRAM::escapeDataToData(OSData * value)
2106 {
2107 OSSharedPtr<OSData> result;
2108 const UInt8 *startPtr;
2109 const UInt8 *endPtr;
2110 const UInt8 *wherePtr;
2111 UInt8 byte;
2112 bool ok = true;
2113
2114 wherePtr = (const UInt8 *) value->getBytesNoCopy();
2115 endPtr = wherePtr + value->getLength();
2116
2117 result = OSData::withCapacity((unsigned int) (endPtr - wherePtr));
2118 if (!result) {
2119 return result;
2120 }
2121
2122 while (wherePtr < endPtr) {
2123 startPtr = wherePtr;
2124 byte = *wherePtr++;
2125 if ((byte == 0x00) || (byte == 0xFF)) {
2126 for (;
2127 ((wherePtr - startPtr) < 0x80) && (wherePtr < endPtr) && (byte == *wherePtr);
2128 wherePtr++) {
2129 }
2130 ok &= result->appendByte(0xff, 1);
2131 byte = (byte & 0x80) | ((UInt8)(wherePtr - startPtr));
2132 }
2133 ok &= result->appendByte(byte, 1);
2134 }
2135 ok &= result->appendByte(0, 1);
2136
2137 if (!ok) {
2138 result.reset();
2139 }
2140
2141 return result;
2142 }
2143
2144 static bool
2145 IsApplePropertyName(const char * propName)
2146 {
2147 char c;
2148 while ((c = *propName++)) {
2149 if ((c >= 'A') && (c <= 'Z')) {
2150 break;
2151 }
2152 }
2153
2154 return c == 0;
2155 }
2156
2157 IOReturn
2158 IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry *entry,
2159 const OSSymbol **name,
2160 OSData **value)
2161 {
2162 IOReturn err = kIOReturnNoResources;
2163 OSData *data;
2164 const UInt8 *startPtr;
2165 const UInt8 *endPtr;
2166 const UInt8 *wherePtr;
2167 const UInt8 *nvPath = nullptr;
2168 const char *nvName = nullptr;
2169 const char *resultName = nullptr;
2170 const UInt8 *resultValue = nullptr;
2171 UInt32 resultValueLen = 0;
2172 UInt8 byte;
2173
2174 NVRAMLOCK();
2175 data = OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get()));
2176 NVRAMUNLOCK();
2177
2178 if (data == nullptr) {
2179 return err;
2180 }
2181
2182 startPtr = (const UInt8 *) data->getBytesNoCopy();
2183 endPtr = startPtr + data->getLength();
2184
2185 wherePtr = startPtr;
2186 while (wherePtr < endPtr) {
2187 byte = *(wherePtr++);
2188 if (byte) {
2189 continue;
2190 }
2191
2192 if (nvPath == nullptr) {
2193 nvPath = startPtr;
2194 } else if (nvName == nullptr) {
2195 nvName = (const char *) startPtr;
2196 } else {
2197 OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
2198 if (entry == compareEntry) {
2199 bool appleProp = IsApplePropertyName(nvName);
2200 if (!appleProp || !resultName) {
2201 resultName = nvName;
2202 resultValue = startPtr;
2203 resultValueLen = (UInt32) (wherePtr - startPtr - 1); // OSData getLength() is 32b
2204 }
2205 if (!appleProp) {
2206 break;
2207 }
2208 }
2209 nvPath = nullptr;
2210 nvName = nullptr;
2211 }
2212 startPtr = wherePtr;
2213 }
2214 if (resultName) {
2215 *name = OSSymbol::withCString(resultName).detach();
2216 *value = unescapeBytesToData(resultValue, resultValueLen).detach();
2217 if ((*name != nullptr) && (*value != nullptr)) {
2218 err = kIOReturnSuccess;
2219 } else {
2220 err = kIOReturnNoMemory;
2221 }
2222 }
2223 return err;
2224 }
2225
2226 IOReturn
2227 IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
2228 const OSSymbol *propName,
2229 OSData *value)
2230 {
2231 OSSharedPtr<OSData> data, oldData;
2232 const UInt8 *startPtr;
2233 const UInt8 *propStart;
2234 const UInt8 *endPtr;
2235 const UInt8 *wherePtr;
2236 const UInt8 *nvPath = nullptr;
2237 const char *nvName = nullptr;
2238 const char *comp;
2239 const char *name;
2240 UInt8 byte;
2241 bool ok = true;
2242 bool settingAppleProp;
2243
2244 settingAppleProp = IsApplePropertyName(propName->getCStringNoCopy());
2245
2246 // copy over existing properties for other entries
2247
2248 NVRAMLOCK();
2249
2250 oldData.reset(OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get())), OSRetain);
2251 if (oldData) {
2252 startPtr = (const UInt8 *) oldData->getBytesNoCopy();
2253 endPtr = startPtr + oldData->getLength();
2254
2255 propStart = startPtr;
2256 wherePtr = startPtr;
2257 while (wherePtr < endPtr) {
2258 byte = *(wherePtr++);
2259 if (byte) {
2260 continue;
2261 }
2262 if (nvPath == nullptr) {
2263 nvPath = startPtr;
2264 } else if (nvName == nullptr) {
2265 nvName = (const char *) startPtr;
2266 } else {
2267 OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
2268
2269 if (entry == compareEntry) {
2270 if ((settingAppleProp && propName->isEqualTo(nvName))
2271 || (!settingAppleProp && !IsApplePropertyName(nvName))) {
2272 // delete old property (nvPath -> wherePtr) source OSData len is 32b
2273 data = OSData::withBytes(propStart, (UInt32)(nvPath - propStart));
2274 if (data) {
2275 ok &= data->appendBytes(wherePtr, (UInt32)(endPtr - wherePtr));
2276 }
2277 break;
2278 }
2279 }
2280 nvPath = nullptr;
2281 nvName = nullptr;
2282 }
2283
2284 startPtr = wherePtr;
2285 }
2286 }
2287
2288 // make the new property
2289
2290 if (!data) {
2291 if (oldData) {
2292 data = OSData::withData(oldData.get());
2293 } else {
2294 data = OSData::withCapacity(16);
2295 }
2296 if (!data) {
2297 ok = false;
2298 }
2299 }
2300
2301 if (ok && value && value->getLength()) {
2302 do {
2303 // get entries in path
2304 OSSharedPtr<OSArray> array = OSArray::withCapacity(5);
2305 if (!array) {
2306 ok = false;
2307 break;
2308 }
2309 do{
2310 array->setObject(entry);
2311 } while ((entry = entry->getParentEntry(gIODTPlane)));
2312
2313 // append path
2314 for (int i = array->getCount() - 3;
2315 (entry = (IORegistryEntry *) array->getObject(i));
2316 i--) {
2317 name = entry->getName(gIODTPlane);
2318 comp = entry->getLocation(gIODTPlane);
2319 if (comp) {
2320 ok &= data->appendBytes("/@", 2);
2321 } else {
2322 if (!name) {
2323 continue;
2324 }
2325 ok &= data->appendByte('/', 1);
2326 comp = name;
2327 }
2328 ok &= data->appendBytes(comp, (unsigned int) strnlen(comp, UINT16_MAX));
2329 }
2330 ok &= data->appendByte(0, 1);
2331 // append prop name
2332 ok &= data->appendBytes(propName->getCStringNoCopy(), propName->getLength() + 1);
2333
2334 // append escaped data
2335 OSSharedPtr<OSData> escapedData = escapeDataToData(value);
2336 ok &= (escapedData != nullptr);
2337 if (ok) {
2338 ok &= data->appendBytes(escapedData.get());
2339 }
2340 } while (false);
2341 }
2342
2343 if (ok) {
2344 ok = _commonDict->setObject(_registryPropertiesKey.get(), data.get());
2345 }
2346
2347 if (ok) {
2348 if (syncVariables() != kIOReturnSuccess) {
2349 if (oldData) {
2350 _commonDict->setObject(_registryPropertiesKey.get(), oldData.get());
2351 } else {
2352 _commonDict->removeObject(_registryPropertiesKey.get());
2353 }
2354 (void) syncVariables();
2355 ok = false;
2356 }
2357 }
2358
2359 oldData.reset();
2360
2361 NVRAMUNLOCK();
2362
2363 return ok ? kIOReturnSuccess : kIOReturnNoMemory;
2364 }
2365
2366 bool
2367 IODTNVRAM::safeToSync(void)
2368 {
2369 AbsoluteTime delta;
2370 UInt64 delta_ns;
2371 SInt32 delta_secs;
2372
2373 // delta interval went by
2374 clock_get_uptime(&delta);
2375
2376 // Figure it in seconds.
2377 absolutetime_to_nanoseconds(delta, &delta_ns);
2378 delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
2379
2380 if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
2381 _lastDeviceSync = delta_secs;
2382 _freshInterval = FALSE;
2383 return TRUE;
2384 }
2385
2386 return FALSE;
2387 }