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