1 /*
2 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (c) 2007-2021 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 <os/system_event_log.h>
42 #include <pexpert/boot.h>
43 #include <sys/csr.h>
44
45 #define super IOService
46
47 OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
48
49 class IONVRAMCHRPHandler;
50 class IONVRAMV3Handler;
51
52 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
53
54 #define MAX_VAR_NAME_SIZE 63
55
56 #define kNVRAMBankSizeKey "nvram-bank-size"
57 #define kNVRAMBankCountKey "nvram-bank-count"
58 #define kNVRAMCurrentBankKey "nvram-current-bank"
59
60 #define kCurrentGenerationCountKey "Generation"
61 #define kCurrentNVRAMVersionKey "Version"
62
63 #define kNVRAMCommonUsedKey "CommonUsed"
64 #define kNVRAMSystemUsedKey "SystemUsed"
65
66 #define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
67
68 #define MIN_SYNC_NOW_INTERVAL 15*60 /* Minimum 15 Minutes interval mandated */
69
70 #if defined(DEBUG) || defined(DEVELOPMENT)
71 #define DEBUG_IFERROR(err, fmt, args...) \
72 ({ \
73 if ((err != kIOReturnSuccess) || gNVRAMLogging) \
74 IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
75 })
76
77 #define DEBUG_INFO(fmt, args...) \
78 ({ \
79 if (gNVRAMLogging) \
80 IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
81 })
82
83 #define DEBUG_ALWAYS(fmt, args...) \
84 ({ \
85 IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
86 })
87 #else
88 #define DEBUG_IFERROR(err, fmt, args...) (void)NULL
89 #define DEBUG_INFO(fmt, args...) (void)NULL
90 #define DEBUG_ALWAYS(fmt, args...) (void)NULL
91 #endif
92
93 #define DEBUG_ERROR DEBUG_ALWAYS
94
95 #define SAFE_TO_LOCK() (preemption_enabled() && !panic_active())
96
97 #define CONTROLLERLOCK() \
98 ({ \
99 if (SAFE_TO_LOCK()) \
100 IOLockLock(_controllerLock); \
101 })
102
103 #define CONTROLLERUNLOCK() \
104 ({ \
105 if (SAFE_TO_LOCK()) \
106 IOLockUnlock(_controllerLock); \
107 })
108
109 #define NVRAMREADLOCK() \
110 ({ \
111 if (SAFE_TO_LOCK()) \
112 IORWLockRead(_variableLock); \
113 })
114
115 #define NVRAMWRITELOCK() \
116 ({ \
117 if (SAFE_TO_LOCK()) \
118 IORWLockWrite(_variableLock); \
119 })
120
121 #define NVRAMUNLOCK() \
122 ({ \
123 if (SAFE_TO_LOCK()) \
124 IORWLockUnlock(_variableLock); \
125 })
126
127 #define NVRAMLOCKASSERTHELD() \
128 ({ \
129 if (SAFE_TO_LOCK()) \
130 IORWLockAssert(_variableLock, kIORWLockAssertHeld); \
131 })
132
133 #define NVRAMLOCKASSERTEXCLUSIVE() \
134 ({ \
135 if (SAFE_TO_LOCK()) \
136 IORWLockAssert(_variableLock, kIORWLockAssertWrite); \
137 })
138
139 // MOD = Write, Delete
140 // RST = Reset, Obliterate
141 // RD = Read
142 // DEL = Delete
143 #define ENT_MOD_RST ((1 << kIONVRAMOperationWrite) | (1 << kIONVRAMOperationDelete) | (1 << kIONVRAMOperationObliterate) | (1 << kIONVRAMOperationReset))
144 #define ENT_MOD_RD ((1 << kIONVRAMOperationRead) | (1 << kIONVRAMOperationWrite) | (1 << kIONVRAMOperationDelete))
145 #define ENT_MOD ((1 << kIONVRAMOperationWrite) | (1 << kIONVRAMOperationDelete))
146 #define ENT_RST ((1 << kIONVRAMOperationObliterate) | (1 << kIONVRAMOperationReset))
147 #define ENT_RD ((1 << kIONVRAMOperationRead))
148 #define ENT_DEL ((1 << kIONVRAMOperationDelete))
149
150 enum NVRAMVersion {
151 kNVRAMVersionUnknown,
152 kNVRAMVersion1, // Legacy, banks, 0x800 common partition size
153 kNVRAMVersion2, // V1 but with (0x2000 - sizeof(struct apple_nvram_header) - sizeof(struct chrp_nvram_header)) common region
154 kNVRAMVersion3, // New EFI based format
155 kNVRAMVersionMax
156 };
157
158 // Guid for Apple System Boot variables
159 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
160 UUID_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
164 UUID_DEFINE(gAppleNVRAMGuid, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
165
166 // Wifi NVRAM namespace
167 // 36C28AB5-6566-4C50-9EBD-CBB920F83843
168 UUID_DEFINE(gAppleWifiGuid, 0x36, 0xC2, 0x8A, 0xB5, 0x65, 0x66, 0x4C, 0x50, 0x9E, 0xBD, 0xCB, 0xB9, 0x20, 0xF8, 0x38, 0x43);
169
170 // Prefix for kernel-only variables
171 #define KERNEL_ONLY_VAR_NAME_PREFIX "krn."
172
173 static TUNABLE(bool, gNVRAMLogging, "nvram-log", false);
174 static bool gInternalBuild = false;
175
176 // IONVRAMSystemVariableListInternal:
177 // Used for internal builds only
178 // "force-lock-bits" used by fwam over ssh nvram to device so they are unable to use entitlements
179 // "stress-rack" used in SEG stress-rack restore prdocs
180 #define IONVRAMSystemVariableListInternal IONVRAMSystemVariableList, \
181 "force-lock-bits", \
182 "stress-rack"
183
184 // allowlist variables from macboot that need to be set/get from system region if present
185 static const char * const gNVRAMSystemList[] = { IONVRAMSystemVariableList, nullptr };
186 static const char * const gNVRAMSystemListInternal[] = { IONVRAMSystemVariableListInternal, nullptr };
187
188 typedef struct {
189 const char *name;
190 IONVRAMVariableType type;
191 } VariableTypeEntry;
192
193 static const
194 VariableTypeEntry gVariableTypes[] = {
195 {"auto-boot?", kOFVariableTypeBoolean},
196 {"boot-args", kOFVariableTypeString},
197 {"boot-command", kOFVariableTypeString},
198 {"boot-device", kOFVariableTypeString},
199 {"boot-file", kOFVariableTypeString},
200 {"boot-screen", kOFVariableTypeString},
201 {"boot-script", kOFVariableTypeString},
202 {"console-screen", kOFVariableTypeString},
203 {"default-client-ip", kOFVariableTypeString},
204 {"default-gateway-ip", kOFVariableTypeString},
205 {"default-mac-address?", kOFVariableTypeBoolean},
206 {"default-router-ip", kOFVariableTypeString},
207 {"default-server-ip", kOFVariableTypeString},
208 {"default-subnet-mask", kOFVariableTypeString},
209 {"diag-device", kOFVariableTypeString},
210 {"diag-file", kOFVariableTypeString},
211 {"diag-switch?", kOFVariableTypeBoolean},
212 {"fcode-debug?", kOFVariableTypeBoolean},
213 {"input-device", kOFVariableTypeString},
214 {"input-device-1", kOFVariableTypeString},
215 {"little-endian?", kOFVariableTypeBoolean},
216 {"ldm", kOFVariableTypeBoolean},
217 {"load-base", kOFVariableTypeNumber},
218 {"mouse-device", kOFVariableTypeString},
219 {"nvramrc", kOFVariableTypeString},
220 {"oem-banner", kOFVariableTypeString},
221 {"oem-banner?", kOFVariableTypeBoolean},
222 {"oem-logo", kOFVariableTypeString},
223 {"oem-logo?", kOFVariableTypeBoolean},
224 {"output-device", kOFVariableTypeString},
225 {"output-device-1", kOFVariableTypeString},
226 {"pci-probe-list", kOFVariableTypeNumber},
227 {"pci-probe-mask", kOFVariableTypeNumber},
228 {"real-base", kOFVariableTypeNumber},
229 {"real-mode?", kOFVariableTypeBoolean},
230 {"real-size", kOFVariableTypeNumber},
231 {"screen-#columns", kOFVariableTypeNumber},
232 {"screen-#rows", kOFVariableTypeNumber},
233 {"security-mode", kOFVariableTypeString},
234 {"selftest-#megs", kOFVariableTypeNumber},
235 {"use-generic?", kOFVariableTypeBoolean},
236 {"use-nvramrc?", kOFVariableTypeBoolean},
237 {"virt-base", kOFVariableTypeNumber},
238 {"virt-size", kOFVariableTypeNumber},
239 // Variables used for testing
240 {"test-bool", kOFVariableTypeBoolean},
241 {"test-num", kOFVariableTypeNumber},
242 {"test-str", kOFVariableTypeString},
243 {"test-data", kOFVariableTypeData},
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
255 union 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 SystemReadHidden :1;
263 uint64_t FullAccess :1;
264 uint64_t InternalOnly :1;
265 uint64_t Reserved:57;
266 } Bits;
267 uint64_t Uint64;
268 };
269
270 typedef struct {
271 const char *name;
272 VariablePermission p;
273 } VariablePermissionEntry;
274
275 static const
276 VariablePermissionEntry gVariablePermissions[] = {
277 {"aapl,pci", .p.Bits.RootRequired = 1},
278 {"battery-health", .p.Bits.RootRequired = 1,
279 .p.Bits.NeverAllowedToDelete = 1},
280 {"boot-image", .p.Bits.UserWrite = 1},
281 {"com.apple.System.fp-state", .p.Bits.KernelOnly = 1},
282 {"fm-account-masked", .p.Bits.RootRequired = 1,
283 .p.Bits.NeverAllowedToDelete = 1},
284 {"fm-activation-locked", .p.Bits.RootRequired = 1,
285 .p.Bits.NeverAllowedToDelete = 1},
286 {"fm-spkeys", .p.Bits.RootRequired = 1,
287 .p.Bits.NeverAllowedToDelete = 1},
288 {"fm-spstatus", .p.Bits.RootRequired = 1,
289 .p.Bits.NeverAllowedToDelete = 1},
290 {"policy-nonce-digests", .p.Bits.ResetNVRAMOnlyDelete = 1}, // Deleting this via user triggered obliterate leave J273a unable to boot
291 {"recoveryos-passcode-blob", .p.Bits.SystemReadHidden = 1},
292 {"security-password", .p.Bits.RootRequired = 1},
293 {"system-passcode-lock-blob", .p.Bits.SystemReadHidden = 1},
294
295 #if !defined(__x86_64__)
296 {"acc-cm-override-charger-count", .p.Bits.KernelOnly = 1},
297 {"acc-cm-override-count", .p.Bits.KernelOnly = 1},
298 {"acc-mb-ld-lifetime", .p.Bits.KernelOnly = 1},
299 {"backlight-level", .p.Bits.UserWrite = 1},
300 {"backlight-nits", .p.Bits.UserWrite = 1},
301 {"ldm", .p.Bits.KernelOnly = 1},
302 {"com.apple.System.boot-nonce", .p.Bits.KernelOnly = 1},
303 {"com.apple.System.sep.art", .p.Bits.KernelOnly = 1},
304 {"darkboot", .p.Bits.UserWrite = 1},
305 {"nonce-seeds", .p.Bits.KernelOnly = 1},
306 #endif /* !defined(__x86_64__) */
307 // Variables used for testing permissions
308 {"testSysReadHidden", .p.Bits.SystemReadHidden = 1},
309 {"testKernelOnly", .p.Bits.KernelOnly = 1},
310 {"testResetOnlyDel", .p.Bits.ResetNVRAMOnlyDelete = 1},
311 {"testNeverDel", .p.Bits.NeverAllowedToDelete = 1},
312 {"testUserWrite", .p.Bits.UserWrite = 1},
313 {"testRootReq", .p.Bits.RootRequired = 1},
314 {"reclaim-int", .p.Bits.InternalOnly = 1},
315 {nullptr, {.Bits.FullAccess = 1}} // Default access
316 };
317
318 typedef struct {
319 const uint8_t checkOp;
320 const uuid_t *varGuid;
321 const char *varName;
322 const char *varEntitlement;
323 } VariableEntitlementEntry;
324
325 // variable-guid pair entries that require entitlement check to do specified nvram operations
326 static const
327 VariableEntitlementEntry gVariableEntitlements[] = {
328 {ENT_MOD_RST, &gAppleNVRAMGuid, "ownership-warning", "com.apple.private.iokit.ddl-write"},
329 {ENT_MOD, &gAppleSystemVariableGuid, "BluetoothInfo", "com.apple.private.iokit.nvram-bluetooth"},
330 {ENT_MOD, &gAppleSystemVariableGuid, "BluetoothUHEDevices", "com.apple.private.iokit.nvram-bluetooth"},
331 {ENT_MOD, &gAppleNVRAMGuid, "bluetoothExternalDongleFailed", "com.apple.private.iokit.nvram-bluetooth"},
332 {ENT_MOD, &gAppleNVRAMGuid, "bluetoothInternalControllerInfo", "com.apple.private.iokit.nvram-bluetooth"},
333 {ENT_RD, &gAppleSystemVariableGuid, "current-network", "com.apple.private.security.nvram.wifi-psks"},
334 {ENT_RD, &gAppleWifiGuid, "current-network", "com.apple.private.security.nvram.wifi-psks"},
335 {ENT_RD, &gAppleSystemVariableGuid, "preferred-networks", "com.apple.private.security.nvram.wifi-psks"},
336 {ENT_RD, &gAppleWifiGuid, "preferred-networks", "com.apple.private.security.nvram.wifi-psks"},
337 {ENT_RD, &gAppleSystemVariableGuid, "preferred-count", "com.apple.private.security.nvram.wifi-psks"},
338 {ENT_RD, &gAppleWifiGuid, "preferred-count", "com.apple.private.security.nvram.wifi-psks"},
339 // Variables used for testing entitlement
340 {ENT_MOD_RST, &gAppleNVRAMGuid, "testEntModRst", "com.apple.private.iokit.testEntModRst"},
341 {ENT_MOD_RST, &gAppleSystemVariableGuid, "testEntModRstSys", "com.apple.private.iokit.testEntModRst"},
342 {ENT_RST, &gAppleNVRAMGuid, "testEntRst", "com.apple.private.iokit.testEntRst"},
343 {ENT_RST, &gAppleSystemVariableGuid, "testEntRstSys", "com.apple.private.iokit.testEntRst"},
344 {ENT_RD, &gAppleNVRAMGuid, "testEntRd", "com.apple.private.iokit.testEntRd"},
345 {ENT_RD, &gAppleSystemVariableGuid, "testEntRdSys", "com.apple.private.iokit.testEntRd"},
346 {ENT_DEL, &gAppleNVRAMGuid, "testEntDel", "com.apple.private.iokit.testEntDel"},
347 {ENT_DEL, &gAppleSystemVariableGuid, "testEntDelSys", "com.apple.private.iokit.testEntDel"},
348 {0, &UUID_NULL, nullptr, nullptr}
349 };
350
351 static NVRAMPartitionType
getPartitionTypeForGUID(const uuid_t guid)352 getPartitionTypeForGUID(const uuid_t guid)
353 {
354 if (uuid_compare(guid, gAppleSystemVariableGuid) == 0) {
355 return kIONVRAMPartitionSystem;
356 } else {
357 return kIONVRAMPartitionCommon;
358 }
359 }
360
361 static IONVRAMVariableType
getVariableType(const char * propName)362 getVariableType(const char *propName)
363 {
364 const VariableTypeEntry *entry;
365
366 entry = gVariableTypes;
367 while (entry->name != nullptr) {
368 if (strcmp(entry->name, propName) == 0) {
369 break;
370 }
371 entry++;
372 }
373
374 return entry->type;
375 }
376
377 static IONVRAMVariableType
getVariableType(const OSSymbol * propSymbol)378 getVariableType(const OSSymbol *propSymbol)
379 {
380 return getVariableType(propSymbol->getCStringNoCopy());
381 }
382
383 static VariablePermission
getVariablePermission(const char * propName)384 getVariablePermission(const char *propName)
385 {
386 const VariablePermissionEntry *entry;
387
388 entry = gVariablePermissions;
389 while (entry->name != nullptr) {
390 if (strcmp(entry->name, propName) == 0) {
391 break;
392 }
393 entry++;
394 }
395
396 return entry->p;
397 }
398
399 static bool
variableInAllowList(const char * varName)400 variableInAllowList(const char *varName)
401 {
402 unsigned int i = 0;
403 const char * const *list = gInternalBuild ? gNVRAMSystemListInternal : gNVRAMSystemList;
404
405 while (list[i] != nullptr) {
406 if (strcmp(varName, list[i]) == 0) {
407 return true;
408 }
409 i++;
410 }
411
412 return false;
413 }
414
415 static bool
verifyWriteSizeLimit(const uuid_t varGuid,const char * variableName,size_t propDataSize)416 verifyWriteSizeLimit(const uuid_t varGuid, const char *variableName, size_t propDataSize)
417 {
418 if (variableInAllowList(variableName)) {
419 return propDataSize <= BOOT_LINE_LENGTH;
420 }
421
422 return true;
423 }
424
425 #if defined(DEBUG) || defined(DEVELOPMENT)
426 static const char *
getNVRAMOpString(IONVRAMOperation op)427 getNVRAMOpString(IONVRAMOperation op)
428 {
429 switch (op) {
430 case kIONVRAMOperationRead:
431 return "Read";
432 case kIONVRAMOperationWrite:
433 return "Write";
434 case kIONVRAMOperationDelete:
435 return "Delete";
436 case kIONVRAMOperationObliterate:
437 return "Obliterate";
438 case kIONVRAMOperationReset:
439 return "Reset";
440 case kIONVRAMOperationInit:
441 return "Init";
442 default:
443 return "Unknown";
444 }
445 }
446 #endif
447
448 /*
449 * Parse a variable name of the form "GUID:name".
450 * If the name cannot be parsed, substitute the Apple global variable GUID.
451 * Returns TRUE if a GUID was found in the name, FALSE otherwise.
452 * The guidResult and nameResult arguments may be nullptr if you just want
453 * to check the format of the string.
454 */
455 static bool
parseVariableName(const char * key,uuid_t * guidResult,const char ** nameResult)456 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
457 {
458 uuid_string_t temp = {0};
459 size_t keyLen = strlen(key);
460 bool ok = false;
461 const char *name = key;
462 uuid_t guid;
463
464 if (keyLen > sizeof(temp)) {
465 // check for at least UUID + ":" + more
466 memcpy(temp, key, sizeof(temp) - 1);
467
468 if ((uuid_parse(temp, guid) == 0) &&
469 (key[sizeof(temp) - 1] == ':')) {
470 name = key + sizeof(temp);
471 ok = true;
472 }
473 }
474
475 if (guidResult) {
476 ok ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
477 }
478 if (nameResult) {
479 *nameResult = name;
480 }
481
482 return ok;
483 }
484
485 static bool
parseVariableName(const OSSymbol * key,uuid_t * guidResult,const char ** nameResult)486 parseVariableName(const OSSymbol *key, uuid_t *guidResult, const char **nameResult)
487 {
488 return parseVariableName(key->getCStringNoCopy(), guidResult, nameResult);
489 }
490
491 /**
492 * @brief Translates(if needed) varGuid and stores it in destGuid
493 *
494 * @param varGuid guid to translate
495 * @param variableName variable name attached to the guid
496 * @param destGuid translated guid is saved here
497 * @param systemActive boolean to indicate if it has system partition size > 0
498 */
499 static void
translateGUID(const uuid_t varGuid,const char * variableName,uuid_t destGuid,bool systemActive)500 translateGUID(const uuid_t varGuid, const char *variableName, uuid_t destGuid, bool systemActive)
501 {
502 if (varGuid == nullptr || variableName == nullptr || destGuid == nullptr) {
503 DEBUG_ERROR("nullptr passed as an argument\n");
504 return;
505 }
506
507 bool systemGuid = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
508
509 if (systemActive) {
510 if (variableInAllowList(variableName)) {
511 DEBUG_INFO("Using system GUID due to allow list\n");
512 uuid_copy(destGuid, gAppleSystemVariableGuid);
513 } else if (systemGuid) {
514 DEBUG_INFO("System GUID used\n");
515 uuid_copy(destGuid, gAppleSystemVariableGuid);
516 } else {
517 DEBUG_INFO("Use given guid\n");
518 uuid_copy(destGuid, varGuid);
519 }
520 } else if (systemGuid) {
521 DEBUG_INFO("Overriding to Apple guid\n");
522 uuid_copy(destGuid, gAppleNVRAMGuid);
523 } else {
524 DEBUG_INFO("Use given guid\n");
525 uuid_copy(destGuid, varGuid);
526 }
527 }
528
529 /**
530 * @brief Checks if the variable-guid(translated) pair is present in gVariableEntitlements and if so,
531 * does it have the required entitlement for the NVRAM operation passed in
532 *
533 * @param varGuid guid for the variable to be checked, this gets translated by translateGUID
534 * @param varName variable name
535 * @param op NVRAM operation
536 * @param systemActive used to pass into translateGUID to get the correct guid to check against
537 * @param veChecked if variable entitlement is checked, this is set to true
538 * @return true if variable wasn't present in gVariableEntitlements,
539 * entitlement check wasn't required for operation passed in,
540 * or if entitlement check returned true
541 * @return false if varName/varGuid/veChecked was NULL or if entitlement check returned false
542 */
543 static bool
verifyVarEntitlement(const uuid_t varGuid,const char * varName,IONVRAMOperation op,bool systemActive,bool * veChecked)544 verifyVarEntitlement(const uuid_t varGuid, const char *varName, IONVRAMOperation op, bool systemActive, bool *veChecked)
545 {
546 if (varGuid == nullptr || varName == nullptr || veChecked == nullptr) {
547 DEBUG_ERROR("nullptr passed as an argument\n");
548 return false;
549 }
550
551 uuid_t translatedGuid;
552 const VariableEntitlementEntry *entry;
553 *veChecked = false;
554
555 translateGUID(varGuid, varName, translatedGuid, systemActive);
556
557 entry = gVariableEntitlements;
558 while ((entry != nullptr) && (entry->varName != nullptr)) {
559 if ((strcmp(entry->varName, varName) == 0) && (uuid_compare(translatedGuid, *(entry->varGuid)) == 0)) {
560 // check if task entitlement check is required for this operation
561 if (entry->checkOp & (1 << op)) {
562 *veChecked = true;
563 DEBUG_INFO("Checking entitlement %s for %s for operation %s\n", entry->varEntitlement, varName, getNVRAMOpString(op));
564 return IOCurrentTaskHasEntitlement(entry->varEntitlement);
565 }
566 break;
567 }
568 entry++;
569 }
570
571 return true;
572 }
573
574 static bool
kernelOnlyVar(const uuid_t varGuid,const char * varName)575 kernelOnlyVar(const uuid_t varGuid, const char *varName)
576 {
577 if (strncmp(varName, KERNEL_ONLY_VAR_NAME_PREFIX, sizeof(KERNEL_ONLY_VAR_NAME_PREFIX) - 1) == 0) {
578 return true;
579 }
580
581 return false;
582 }
583
584 static bool
verifyPermission(IONVRAMOperation op,const uuid_t varGuid,const char * varName,const bool systemActive)585 verifyPermission(IONVRAMOperation op, const uuid_t varGuid, const char *varName, const bool systemActive)
586 {
587 VariablePermission perm;
588 bool kernel, varEntitled, writeEntitled = false, readEntitled = false, allowList, systemGuid = false, systemEntitled = false, systemInternalEntitled = false, systemAllow, systemReadHiddenAllow = false;
589 bool admin = false;
590 bool ok = false;
591
592 if (verifyVarEntitlement(varGuid, varName, op, systemActive, &varEntitled) == false) {
593 goto exit;
594 }
595
596 perm = getVariablePermission(varName);
597
598 kernel = current_task() == kernel_task;
599
600 if (perm.Bits.KernelOnly || kernelOnlyVar(varGuid, varName)) {
601 DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
602 ok = kernel;
603 goto exit;
604 }
605
606 if (perm.Bits.InternalOnly && !gInternalBuild) {
607 DEBUG_INFO("InternalOnly access for %s, gInternalBuild=%d\n", varName, gInternalBuild);
608 goto exit;
609 }
610
611 allowList = variableInAllowList(varName);
612 systemGuid = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
613 admin = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
614 writeEntitled = IOCurrentTaskHasEntitlement(kIONVRAMWriteAccessKey);
615 readEntitled = IOCurrentTaskHasEntitlement(kIONVRAMReadAccessKey);
616 systemEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemAllowKey);
617 systemInternalEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemInternalAllowKey);
618 systemReadHiddenAllow = IOCurrentTaskHasEntitlement(kIONVRAMSystemHiddenAllowKey);
619
620 systemAllow = systemEntitled || (systemInternalEntitled && gInternalBuild) || kernel;
621
622 switch (op) {
623 case kIONVRAMOperationRead:
624 if (systemGuid && perm.Bits.SystemReadHidden) {
625 ok = systemReadHiddenAllow;
626 } else if (kernel || admin || readEntitled || perm.Bits.FullAccess || varEntitled) {
627 ok = true;
628 }
629 break;
630
631 case kIONVRAMOperationWrite:
632 if (kernel || perm.Bits.UserWrite || admin || writeEntitled || varEntitled) {
633 if (systemGuid) {
634 if (allowList) {
635 if (!systemAllow) {
636 DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
637 }
638 } else if (varEntitled) {
639 DEBUG_INFO("Allowed write to system region using variable specific entitlement for %s\n", varName);
640 } else if (!systemAllow) {
641 DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
642 break;
643 }
644 }
645 ok = true;
646 }
647 break;
648
649 case kIONVRAMOperationDelete:
650 case kIONVRAMOperationObliterate:
651 case kIONVRAMOperationReset:
652 if (perm.Bits.NeverAllowedToDelete) {
653 DEBUG_INFO("Never allowed to delete %s\n", varName);
654 break;
655 } else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
656 DEBUG_INFO("Not allowed to obliterate %s\n", varName);
657 break;
658 } else if ((op == kIONVRAMOperationDelete) && perm.Bits.ResetNVRAMOnlyDelete) {
659 DEBUG_INFO("Only allowed to delete %s via NVRAM reset\n", varName);
660 break;
661 }
662
663 if (kernel || perm.Bits.UserWrite || admin || writeEntitled || varEntitled) {
664 if (systemGuid) {
665 if (allowList) {
666 if (!systemAllow) {
667 DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
668 }
669 } else if (varEntitled) {
670 DEBUG_INFO("Allowed delete to system region using variable specific entitlement for %s\n", varName);
671 } else if (!systemAllow) {
672 DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
673 break;
674 }
675 }
676 ok = true;
677 }
678 break;
679
680 case kIONVRAMOperationInit:
681 break;
682 }
683
684 exit:
685 DEBUG_INFO("Permission for %s of %s %s: I=%d kern=%d, adm=%d, wE=%d, rE=%d, sG=%d, sEd=%d, sIEd=%d, sRHA=%d, UW=%d, vE=%d\n", getNVRAMOpString(op), varName, ok ? "granted" : "denied",
686 gInternalBuild, kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled, systemInternalEntitled, systemReadHiddenAllow, perm.Bits.UserWrite, varEntitled);
687
688 return ok;
689 }
690
691 static bool
verifyPermission(IONVRAMOperation op,const OSSymbol * canonicalKey,const bool systemActive)692 verifyPermission(IONVRAMOperation op, const OSSymbol *canonicalKey, const bool systemActive)
693 {
694 const char *varName;
695 uuid_t varGuid;
696
697 parseVariableName(canonicalKey->getCStringNoCopy(), &varGuid, &varName);
698
699 return verifyPermission(op, varGuid, varName, systemActive);
700 }
701
702 static bool
skipKey(const OSSymbol * aKey)703 skipKey(const OSSymbol *aKey)
704 {
705 return aKey->isEqualTo(kIORegistryEntryAllowableSetPropertiesKey) ||
706 aKey->isEqualTo(kIORegistryEntryDefaultLockingSetPropertiesKey) ||
707 aKey->isEqualTo(kIOClassNameOverrideKey) ||
708 aKey->isEqualTo(kIOBSDNameKey) ||
709 aKey->isEqualTo(kIOBSDNamesKey) ||
710 aKey->isEqualTo(kIOBSDMajorKey) ||
711 aKey->isEqualTo(kIOBSDMinorKey) ||
712 aKey->isEqualTo(kIOBSDUnitKey) ||
713 aKey->isEqualTo(kIOUserServicePropertiesKey) ||
714 aKey->isEqualTo(kIOExclaveAssignedKey) ||
715 aKey->isEqualTo(kIOMatchCategoryKey) ||
716 aKey->isEqualTo(kIOBusyInterest);
717 }
718
719 static OSSharedPtr<const OSSymbol>
keyWithGuidAndCString(const uuid_t guid,const char * cstring)720 keyWithGuidAndCString(const uuid_t guid, const char * cstring)
721 {
722 size_t length;
723 OSSharedPtr<const OSSymbol> symbolObj;
724 char *canonicalString;
725
726 length = sizeof(uuid_string_t) - 1 + sizeof(':') + strlen(cstring) + 1;
727
728 canonicalString = (char *) IOMallocData(length);
729 if (canonicalString == nullptr) {
730 return NULL;
731 }
732
733 uuid_unparse(guid, *((uuid_string_t*)canonicalString));
734 canonicalString[sizeof(uuid_string_t) - 1] = ':';
735
736 strlcpy(&canonicalString[sizeof(uuid_string_t)], cstring, length - sizeof(uuid_string_t));
737
738 symbolObj = OSSymbol::withCString(canonicalString);
739 IOFreeData(canonicalString, length);
740
741 return symbolObj;
742 }
743
744 static void
dumpDict(const OSDictionary * dict)745 dumpDict(const OSDictionary *dict)
746 {
747 const OSSymbol *key;
748 OSSharedPtr<OSCollectionIterator> iter;
749 unsigned int count = 0;
750
751 iter = OSCollectionIterator::withCollection(dict);
752 if (iter == nullptr) {
753 DEBUG_ERROR("failed to create iterator\n");
754 goto exit;
755 }
756
757 DEBUG_INFO("Dumping dict...\n");
758 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
759 count++;
760 DEBUG_INFO("%u: %s\n", count, key->getCStringNoCopy());
761 }
762
763 exit:
764 return;
765 }
766
767 // ************************** IODTNVRAMPlatformNotifier ****************************
768 // private IOService based class for passing notifications to IODTNVRAM
769
770 class IODTNVRAMPlatformNotifier : public IOService
771 {
772 OSDeclareDefaultStructors(IODTNVRAMPlatformNotifier)
773 private:
774 IODTNVRAM *_provider;
775
776 public:
777 bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
778
779 virtual IOReturn callPlatformFunction( const OSSymbol * functionName,
780 bool waitForFunction,
781 void *param1, void *param2,
782 void *param3, void *param4 ) APPLE_KEXT_OVERRIDE;
783 };
784
OSDefineMetaClassAndStructors(IODTNVRAMPlatformNotifier,IOService)785 OSDefineMetaClassAndStructors(IODTNVRAMPlatformNotifier, IOService)
786
787 bool
788 IODTNVRAMPlatformNotifier::start(IOService * provider)
789 {
790 OSSharedPtr<OSSerializer> serializer;
791 OSSharedPtr<OSNumber> value = OSNumber::withNumber(1000, 32);
792
793 _provider = OSDynamicCast(IODTNVRAM, provider);
794 require(_provider != nullptr, error);
795
796 setProperty(gIOPlatformWakeActionKey, value.get());
797
798 require(super::start(provider), error);
799
800 registerService();
801
802 return true;
803
804 error:
805 stop(provider);
806
807 return false;
808 }
809
810 #include <IOKit/IOHibernatePrivate.h>
811 #include <IOKit/pwr_mgt/RootDomain.h>
812 static const OSSharedPtr<const OSSymbol> gIOHibernateStateKey = OSSymbol::withCString(kIOHibernateStateKey);
813
814 static uint32_t
hibernateState(void)815 hibernateState(void)
816 {
817 OSSharedPtr<OSData> data = OSDynamicPtrCast<OSData>(IOService::getPMRootDomain()->copyProperty(gIOHibernateStateKey.get()->getCStringNoCopy()));
818 uint32_t hibernateState = 0;
819 if ((data != NULL) && (data->getLength() == sizeof(hibernateState))) {
820 memcpy(&hibernateState, data->getBytesNoCopy(), sizeof(hibernateState));
821 }
822 return hibernateState;
823 }
824
825 IOReturn
callPlatformFunction(const OSSymbol * functionName,bool waitForFunction,void * param1,void * param2,void * param3,void * param4)826 IODTNVRAMPlatformNotifier::callPlatformFunction( const OSSymbol * functionName,
827 bool waitForFunction,
828 void *param1, void *param2,
829 void *param3, void *param4 )
830 {
831 if ((functionName == gIOPlatformWakeActionKey) &&
832 (hibernateState() == kIOHibernateStateWakingFromHibernate)) {
833 DEBUG_INFO("waking from hibernate\n");
834 _provider->reload();
835 return kIOReturnSuccess;
836 }
837
838 return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
839 }
840
841
842 // ************************** IODTNVRAMDiags ****************************
843 // private IOService based class for passing notifications to IODTNVRAM
844 #define kIODTNVRAMDiagsStatsKey "Stats"
845 #define kIODTNVRAMDiagsInitKey "Init"
846 #define kIODTNVRAMDiagsReadKey "Read"
847 #define kIODTNVRAMDiagsWriteKey "Write"
848 #define kIODTNVRAMDiagsDeleteKey "Delete"
849 #define kIODTNVRAMDiagsNameKey "Name"
850 #define kIODTNVRAMDiagsSizeKey "Size"
851 #define kIODTNVRAMDiagsPresentKey "Present"
852
853 // private IOService based class for publishing diagnostic info for IODTNVRAM
854 class IODTNVRAMDiags : public IOService
855 {
856 OSDeclareDefaultStructors(IODTNVRAMDiags)
857 private:
858 IODTNVRAM *_provider;
859 IORWLock *_variableLock;
860 OSSharedPtr<OSDictionary> _stats;
861
862 bool serializeStats(void *, OSSerialize * serializer);
863
864 public:
865 bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
866 void logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data);
867 };
868
OSDefineMetaClassAndStructors(IODTNVRAMDiags,IOService)869 OSDefineMetaClassAndStructors(IODTNVRAMDiags, IOService)
870
871 bool
872 IODTNVRAMDiags::start(IOService * provider)
873 {
874 OSSharedPtr<OSSerializer> serializer;
875
876 _provider = OSDynamicCast(IODTNVRAM, provider);
877 require(_provider != nullptr, error);
878
879 require(super::start(provider), error);
880
881 _variableLock = IORWLockAlloc();
882 require(_variableLock != nullptr, error);
883
884 _stats = OSDictionary::withCapacity(1);
885 require(_stats != nullptr, error);
886
887 serializer = OSSerializer::forTarget(this, OSMemberFunctionCast(OSSerializerCallback, this, &IODTNVRAMDiags::serializeStats));
888 require(serializer != nullptr, error);
889
890 setProperty(kIODTNVRAMDiagsStatsKey, serializer.get());
891
892 registerService();
893
894 return true;
895
896 error:
897 stop(provider);
898
899 return false;
900 }
901
902 void
logVariable(NVRAMPartitionType region,IONVRAMOperation op,const char * name,void * data)903 IODTNVRAMDiags::logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data)
904 {
905 // "Stats" : OSDictionary
906 // - "XX:varName" : OSDictionary, XX is the region value prefix to distinguish which dictionary the variable is in
907 // - "Init" : OSBoolean True/present if variable present at initialization
908 // - "Read" : OSNumber count
909 // - "Write" : OSNumber count
910 // - "Delete" : OSNumber count
911 // - "Size" : OSNumber size, latest size from either init or write
912 // - "Present" : OSBoolean True/False if variable is present or not
913 char *entryKey;
914 size_t entryKeySize;
915 OSSharedPtr<OSDictionary> existingEntry;
916 OSSharedPtr<OSNumber> currentCount;
917 OSSharedPtr<OSNumber> varSize;
918 const char *opCountKey = nullptr;
919
920 entryKeySize = strlen("XX:") + strlen(name) + 1;
921 entryKey = IONewData(char, entryKeySize);
922 require(entryKey, exit);
923
924 snprintf(entryKey, entryKeySize, "%02X:%s", region, name);
925
926 NVRAMWRITELOCK();
927 existingEntry.reset(OSDynamicCast(OSDictionary, _stats->getObject(entryKey)), OSRetain);
928
929 if (existingEntry == nullptr) {
930 existingEntry = OSDictionary::withCapacity(4);
931 }
932
933 switch (op) {
934 case kIONVRAMOperationRead:
935 opCountKey = kIODTNVRAMDiagsReadKey;
936 if (existingEntry->getObject(kIODTNVRAMDiagsPresentKey) == nullptr) {
937 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
938 }
939 break;
940 case kIONVRAMOperationWrite:
941 opCountKey = kIODTNVRAMDiagsWriteKey;
942 varSize = OSNumber::withNumber((size_t)data, 64);
943 existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
944 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
945 break;
946 case kIONVRAMOperationDelete:
947 case kIONVRAMOperationObliterate:
948 case kIONVRAMOperationReset:
949 opCountKey = kIODTNVRAMDiagsDeleteKey;
950 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
951 break;
952 case kIONVRAMOperationInit:
953 varSize = OSNumber::withNumber((size_t)data, 64);
954 existingEntry->setObject(kIODTNVRAMDiagsInitKey, varSize);
955 existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
956 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
957 break;
958 default:
959 goto unlock;
960 }
961
962 if (opCountKey) {
963 currentCount.reset(OSDynamicCast(OSNumber, existingEntry->getObject(opCountKey)), OSRetain);
964
965 if (currentCount == nullptr) {
966 currentCount = OSNumber::withNumber(1, 64);
967 } else {
968 currentCount->addValue(1);
969 }
970
971 existingEntry->setObject(opCountKey, currentCount);
972 }
973
974 _stats->setObject(entryKey, existingEntry);
975
976 unlock:
977 NVRAMUNLOCK();
978
979 exit:
980 IODeleteData(entryKey, char, entryKeySize);
981
982 return;
983 }
984
985 bool
serializeStats(void *,OSSerialize * serializer)986 IODTNVRAMDiags::serializeStats(void *, OSSerialize * serializer)
987 {
988 bool ok;
989
990 NVRAMREADLOCK();
991 ok = _stats->serialize(serializer);
992 NVRAMUNLOCK();
993
994 return ok;
995 }
996
997 // ************************** IODTNVRAMVariables ****************************
998
999 // private IOService based class for publishing distinct dictionary properties on
1000 // for easy ioreg access since the serializeProperties call is overloaded and is used
1001 // as variable access
1002 class IODTNVRAMVariables : public IOService
1003 {
1004 OSDeclareDefaultStructors(IODTNVRAMVariables)
1005 private:
1006 IODTNVRAM *_provider;
1007 uuid_t _guid;
1008 bool _systemActive;
1009
1010 public:
1011 bool init(const uuid_t guid, const bool systemActive);
1012 virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
1013
1014 virtual bool serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
1015 virtual OSPtr<OSObject> copyProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
1016 virtual OSObject *getProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
1017 virtual bool setProperty(const OSSymbol *aKey, OSObject *anObject) APPLE_KEXT_OVERRIDE;
1018 virtual IOReturn setProperties(OSObject *properties) APPLE_KEXT_OVERRIDE;
1019 virtual void removeProperty(const OSSymbol *aKey) APPLE_KEXT_OVERRIDE;
1020 };
1021
OSDefineMetaClassAndStructors(IODTNVRAMVariables,IOService)1022 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
1023
1024 bool
1025 IODTNVRAMVariables::init(const uuid_t guid, const bool systemActive)
1026 {
1027 require(super::init(), fail);
1028
1029 uuid_copy(_guid, guid);
1030 _systemActive = systemActive;
1031
1032 return true;
1033
1034 fail:
1035 return false;
1036 }
1037
1038 bool
start(IOService * provider)1039 IODTNVRAMVariables::start(IOService * provider)
1040 {
1041 _provider = OSDynamicCast(IODTNVRAM, provider);
1042 if (_provider == nullptr) {
1043 goto error;
1044 }
1045
1046 if (!super::start(provider)) {
1047 goto error;
1048 }
1049
1050 registerService();
1051
1052 return true;
1053
1054 error:
1055 stop(provider);
1056
1057 return false;
1058 }
1059
1060 bool
serializeProperties(OSSerialize * s) const1061 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
1062 {
1063 const OSSymbol *key;
1064 OSSharedPtr<OSDictionary> dict;
1065 OSSharedPtr<OSCollectionIterator> iter;
1066 OSSharedPtr<OSDictionary> localVariables = _provider->_varDict;
1067 bool ok = false;
1068
1069 dict = OSDictionary::withCapacity(localVariables->getCount());
1070 if (dict == nullptr) {
1071 DEBUG_ERROR("No dictionary\n");
1072 goto exit;
1073 }
1074
1075 iter = OSCollectionIterator::withCollection(localVariables.get());
1076 if (iter == nullptr) {
1077 DEBUG_ERROR("failed to create iterator\n");
1078 goto exit;
1079 }
1080
1081 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1082 if (verifyPermission(kIONVRAMOperationRead, key, _systemActive)) {
1083 uuid_t guid;
1084 const char *name;
1085
1086 parseVariableName(key, &guid, &name);
1087
1088 if (uuid_compare(_guid, guid) == 0) {
1089 OSSharedPtr<const OSSymbol> sym = OSSymbol::withCString(name);
1090 dict->setObject(sym.get(), localVariables->getObject(key));
1091 }
1092 }
1093 }
1094
1095 ok = dict->serialize(s);
1096
1097 exit:
1098 DEBUG_INFO("ok=%d\n", ok);
1099 return ok;
1100 }
1101
1102 OSPtr<OSObject>
copyProperty(const OSSymbol * aKey) const1103 IODTNVRAMVariables::copyProperty(const OSSymbol *aKey) const
1104 {
1105 if (_provider && !skipKey(aKey)) {
1106 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1107
1108 return _provider->copyPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
1109 } else {
1110 return nullptr;
1111 }
1112 }
1113
1114 OSObject *
getProperty(const OSSymbol * aKey) const1115 IODTNVRAMVariables::getProperty(const OSSymbol *aKey) const
1116 {
1117 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1118
1119 return theObject.get();
1120 }
1121
1122 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)1123 IODTNVRAMVariables::setProperty(const OSSymbol *aKey, OSObject *anObject)
1124 {
1125 if (_provider) {
1126 return _provider->setPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy(), anObject) == kIOReturnSuccess;
1127 } else {
1128 return false;
1129 }
1130 }
1131
1132 IOReturn
setProperties(OSObject * properties)1133 IODTNVRAMVariables::setProperties(OSObject *properties)
1134 {
1135 IOReturn ret = kIOReturnSuccess;
1136 OSObject *object;
1137 const OSSymbol *key;
1138 OSDictionary *dict;
1139 OSSharedPtr<OSCollectionIterator> iter;
1140
1141 dict = OSDynamicCast(OSDictionary, properties);
1142 if (dict == nullptr) {
1143 DEBUG_ERROR("Not a dictionary\n");
1144 return kIOReturnBadArgument;
1145 }
1146
1147 iter = OSCollectionIterator::withCollection(dict);
1148 if (iter == nullptr) {
1149 DEBUG_ERROR("Couldn't create iterator\n");
1150 return kIOReturnBadArgument;
1151 }
1152
1153 while (ret == kIOReturnSuccess) {
1154 key = OSDynamicCast(OSSymbol, iter->getNextObject());
1155 if (key == nullptr) {
1156 break;
1157 }
1158
1159 object = dict->getObject(key);
1160 if (object == nullptr) {
1161 continue;
1162 }
1163
1164 ret = _provider->setPropertyWithGUIDAndName(_guid, key->getCStringNoCopy(), object);
1165 }
1166
1167 DEBUG_INFO("ret=%#08x\n", ret);
1168
1169 return ret;
1170 }
1171
1172 void
removeProperty(const OSSymbol * aKey)1173 IODTNVRAMVariables::removeProperty(const OSSymbol *aKey)
1174 {
1175 _provider->removePropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
1176 }
1177
1178 // ************************** Format Handlers ***************************
1179 class IODTNVRAMFormatHandler
1180 {
1181 protected:
1182 uint32_t _bankSize;
1183 uint32_t _bankCount;
1184 uint32_t _currentBank;
1185
1186 public:
1187 virtual
1188 ~IODTNVRAMFormatHandler();
1189 virtual bool getNVRAMProperties(void);
1190 virtual IOReturn unserializeVariables(void) = 0;
1191 virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) = 0;
1192 virtual bool setController(IONVRAMController *_nvramController) = 0;
1193 virtual IOReturn sync(void) = 0;
1194 virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) = 0;
1195 virtual void reload(void) = 0;
1196 virtual uint32_t getGeneration(void) const = 0;
1197 virtual uint32_t getVersion(void) const = 0;
1198 virtual uint32_t getSystemUsed(void) const = 0;
1199 virtual uint32_t getCommonUsed(void) const = 0;
1200 virtual bool getSystemPartitionActive(void) const = 0;
1201 };
1202
~IODTNVRAMFormatHandler()1203 IODTNVRAMFormatHandler::~IODTNVRAMFormatHandler()
1204 {
1205 }
1206
1207 bool
getNVRAMProperties()1208 IODTNVRAMFormatHandler::getNVRAMProperties()
1209 {
1210 bool ok = false;
1211 OSSharedPtr<IORegistryEntry> entry;
1212 OSSharedPtr<OSObject> prop;
1213 OSData * data;
1214
1215 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1216 require_action(entry, exit, DEBUG_ERROR("Unable to find chosen node\n"));
1217
1218 prop = entry->copyProperty(kNVRAMBankSizeKey);
1219 require_action(prop, exit, DEBUG_ERROR("Unable to find %s property\n", kNVRAMBankSizeKey));
1220
1221 data = OSDynamicCast(OSData, prop.get());
1222 require(data, exit);
1223
1224 _bankSize = *((uint32_t *)data->getBytesNoCopy());
1225
1226 prop = entry->copyProperty(kNVRAMBankCountKey);
1227 require_action(prop, exit, DEBUG_ERROR("Unable to find %s property\n", kNVRAMBankCountKey));
1228
1229 data = OSDynamicCast(OSData, prop.get());
1230 require(data, exit);
1231
1232 _bankCount = *((uint32_t *)data->getBytesNoCopy());
1233
1234 prop = entry->copyProperty(kNVRAMCurrentBankKey);
1235 require_action(prop, exit, DEBUG_ERROR("Unable to find %s property\n", kNVRAMCurrentBankKey));
1236
1237 data = OSDynamicCast(OSData, prop.get());
1238 require(data, exit);
1239
1240 _currentBank = *((uint32_t *)data->getBytesNoCopy());
1241
1242 ok = true;
1243
1244 DEBUG_ALWAYS("_bankSize=%#X, _bankCount=%#X, _currentBank=%#X\n", _bankSize, _bankCount, _currentBank);
1245
1246 exit:
1247 return ok;
1248 }
1249
1250 #include "IONVRAMCHRPHandler.cpp"
1251
1252 #include "IONVRAMV3Handler.cpp"
1253
1254 // **************************** IODTNVRAM *********************************
1255
1256 bool
init(IORegistryEntry * old,const IORegistryPlane * plane)1257 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
1258 {
1259 OSSharedPtr<OSDictionary> dict;
1260
1261 DEBUG_INFO("...\n");
1262
1263 require(super::init(old, plane), fail);
1264
1265 #if CONFIG_CSR && XNU_TARGET_OS_OSX
1266 gInternalBuild = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0);
1267 #elif defined(DEBUG) || defined(DEVELOPMENT)
1268 gInternalBuild = true;
1269 #endif
1270 DEBUG_INFO("gInternalBuild = %d\n", gInternalBuild);
1271
1272 _variableLock = IORWLockAlloc();
1273 require(_variableLock != nullptr, fail);
1274
1275 _controllerLock = IOLockAlloc();
1276 require(_controllerLock != nullptr, fail);
1277
1278 // Clear the IORegistryEntry property table
1279 dict = OSDictionary::withCapacity(1);
1280 require(dict != nullptr, fail);
1281
1282 setPropertyTable(dict.get());
1283 dict.reset();
1284
1285 return true;
1286
1287 fail:
1288 return false;
1289 }
1290
1291 bool
start(IOService * provider)1292 IODTNVRAM::start(IOService *provider)
1293 {
1294 OSSharedPtr<OSNumber> version;
1295
1296 DEBUG_INFO("...\n");
1297
1298 require(super::start(provider), fail);
1299
1300 // Check if our overridden init function was called
1301 // If not, skip any additional initialization being done here.
1302 // This is not an error we just need to successfully exit this function to allow
1303 // AppleEFIRuntime to proceed and take over operation
1304 require_action(_controllerLock != nullptr, no_common, DEBUG_INFO("x86 init\n"));
1305
1306 _diags = new IODTNVRAMDiags;
1307 if (!_diags || !_diags->init()) {
1308 DEBUG_ERROR("Unable to create/init the diags service\n");
1309 OSSafeReleaseNULL(_diags);
1310 goto fail;
1311 }
1312
1313 if (!_diags->attach(this)) {
1314 DEBUG_ERROR("Unable to attach the diags service!\n");
1315 OSSafeReleaseNULL(_diags);
1316 goto fail;
1317 }
1318
1319 if (!_diags->start(this)) {
1320 DEBUG_ERROR("Unable to start the diags service!\n");
1321 _diags->detach(this);
1322 OSSafeReleaseNULL(_diags);
1323 goto fail;
1324 }
1325
1326 _notifier = new IODTNVRAMPlatformNotifier;
1327 if (!_notifier || !_notifier->init()) {
1328 DEBUG_ERROR("Unable to create/init the notifier service\n");
1329 OSSafeReleaseNULL(_notifier);
1330 goto fail;
1331 }
1332
1333 if (!_notifier->attach(this)) {
1334 DEBUG_ERROR("Unable to attach the notifier service!\n");
1335 OSSafeReleaseNULL(_notifier);
1336 goto fail;
1337 }
1338
1339 if (!_notifier->start(this)) {
1340 DEBUG_ERROR("Unable to start the notifier service!\n");
1341 _notifier->detach(this);
1342 OSSafeReleaseNULL(_notifier);
1343 goto fail;
1344 }
1345
1346 // This will load the proxied variable data which will call back into
1347 // IODTNVRAM for the variable sets which will also update the system/common services
1348 initImageFormat();
1349
1350 version = OSNumber::withNumber(_format->getVersion(), 32);
1351 _diags->setProperty(kCurrentNVRAMVersionKey, version.get());
1352
1353 if (_format->getSystemUsed()) {
1354 _systemService = new IODTNVRAMVariables;
1355
1356 if (!_systemService || !_systemService->init(gAppleSystemVariableGuid, _format->getSystemPartitionActive())) {
1357 DEBUG_ERROR("Unable to start the system service!\n");
1358 OSSafeReleaseNULL(_systemService);
1359 goto no_system;
1360 }
1361
1362 _systemService->setName("options-system");
1363
1364 if (!_systemService->attach(this)) {
1365 DEBUG_ERROR("Unable to attach the system service!\n");
1366 OSSafeReleaseNULL(_systemService);
1367 goto no_system;
1368 }
1369
1370 if (!_systemService->start(this)) {
1371 DEBUG_ERROR("Unable to start the system service!\n");
1372 _systemService->detach(this);
1373 OSSafeReleaseNULL(_systemService);
1374 goto no_system;
1375 }
1376 }
1377
1378 no_system:
1379 _commonService = new IODTNVRAMVariables;
1380
1381 if (!_commonService || !_commonService->init(gAppleNVRAMGuid, _format->getSystemPartitionActive())) {
1382 DEBUG_ERROR("Unable to start the common service!\n");
1383 OSSafeReleaseNULL(_commonService);
1384 goto no_common;
1385 }
1386
1387 _commonService->setName("options-common");
1388
1389 if (!_commonService->attach(this)) {
1390 DEBUG_ERROR("Unable to attach the common service!\n");
1391 OSSafeReleaseNULL(_commonService);
1392 goto no_common;
1393 }
1394
1395 if (!_commonService->start(this)) {
1396 DEBUG_ERROR("Unable to start the common service!\n");
1397 _commonService->detach(this);
1398 OSSafeReleaseNULL(_commonService);
1399 goto no_common;
1400 }
1401
1402 no_common:
1403 return true;
1404
1405 fail:
1406 stop(provider);
1407 return false;
1408 }
1409
1410 void
initImageFormat(void)1411 IODTNVRAM::initImageFormat(void)
1412 {
1413 OSSharedPtr<IORegistryEntry> entry;
1414 OSSharedPtr<OSObject> prop;
1415 const char *proxyDataKey = "nvram-proxy-data";
1416 const char *bankSizeKey = "nvram-bank-size";
1417 OSData *data = nullptr;
1418 uint32_t size = 0;
1419 const uint8_t *image = nullptr;
1420
1421 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1422
1423 require(entry != nullptr, skip);
1424
1425 prop = entry->copyProperty(bankSizeKey);
1426 require(prop != nullptr, skip);
1427
1428 data = OSDynamicCast(OSData, prop.get());
1429 require(data != nullptr, skip);
1430
1431 size = *((uint32_t*)data->getBytesNoCopy());
1432 require_action(size != 0, skip, panic("NVRAM size is 0 bytes, possibly due to bad config with iBoot + xnu mismatch"));
1433 DEBUG_ALWAYS("NVRAM size is %u bytes\n", size);
1434
1435 prop = entry->copyProperty(proxyDataKey);
1436 require(prop != nullptr, skip);
1437
1438 data = OSDynamicCast(OSData, prop.get());
1439 require_action(data != nullptr, skip, DEBUG_ERROR("No proxy data!\n"));
1440
1441 image = (const uint8_t *)data->getBytesNoCopy();
1442
1443 skip:
1444 if (IONVRAMV3Handler::isValidImage(image, size)) {
1445 _format = IONVRAMV3Handler::init(this, image, size, _varDict);
1446 require_action(_format, skip, panic("IONVRAMV3Handler creation failed\n"));
1447 } else {
1448 _format = IONVRAMCHRPHandler::init(this, image, size, _varDict);
1449 require_action(_format, skip, panic("IONVRAMCHRPHandler creation failed\n"));
1450 }
1451
1452 _format->unserializeVariables();
1453
1454 dumpDict(_varDict.get());
1455
1456 #if defined(RELEASE)
1457 if (entry != nullptr) {
1458 entry->removeProperty(proxyDataKey);
1459 }
1460 #endif
1461
1462 _lastDeviceSync = 0;
1463 _freshInterval = true;
1464 }
1465
1466 void
registerNVRAMController(IONVRAMController * controller)1467 IODTNVRAM::registerNVRAMController(IONVRAMController *controller)
1468 {
1469 DEBUG_INFO("setting controller\n");
1470
1471 NVRAMWRITELOCK();
1472 CONTROLLERLOCK();
1473
1474 _format->setController(controller);
1475
1476 CONTROLLERUNLOCK();
1477 NVRAMUNLOCK();
1478
1479 return;
1480 }
1481
1482 bool
safeToSync(void)1483 IODTNVRAM::safeToSync(void)
1484 {
1485 AbsoluteTime delta;
1486 UInt64 delta_ns;
1487 SInt32 delta_secs;
1488
1489 // delta interval went by
1490 clock_get_uptime(&delta);
1491
1492 // Figure it in seconds.
1493 absolutetime_to_nanoseconds(delta, &delta_ns);
1494 delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
1495
1496 if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
1497 _lastDeviceSync = delta_secs;
1498 _freshInterval = false;
1499 return true;
1500 }
1501
1502 return false;
1503 }
1504
1505 IOReturn
syncInternal(bool rateLimit)1506 IODTNVRAM::syncInternal(bool rateLimit)
1507 {
1508 IOReturn ret = kIOReturnSuccess;
1509
1510 DEBUG_INFO("rateLimit=%d\n", rateLimit);
1511
1512 if (!SAFE_TO_LOCK()) {
1513 DEBUG_INFO("cannot lock\n");
1514 goto exit;
1515 }
1516
1517 // Rate limit requests to sync. Drivers that need this rate limiting will
1518 // shadow the data and only write to flash when they get a sync call
1519 if (rateLimit) {
1520 if (safeToSync() == false) {
1521 DEBUG_INFO("safeToSync()=false\n");
1522 goto exit;
1523 }
1524 }
1525
1526 DEBUG_INFO("Calling sync()\n");
1527 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "sync", "triggered");
1528
1529 NVRAMREADLOCK();
1530 CONTROLLERLOCK();
1531
1532 ret = _format->sync();
1533
1534 CONTROLLERUNLOCK();
1535 NVRAMUNLOCK();
1536
1537 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "sync", "completed with ret=%08x", ret);
1538
1539 if (_diags) {
1540 OSSharedPtr<OSNumber> generation = OSNumber::withNumber(_format->getGeneration(), 32);
1541 _diags->setProperty(kCurrentGenerationCountKey, generation.get());
1542 }
1543
1544 exit:
1545 return ret;
1546 }
1547
1548 IOReturn
sync(void)1549 IODTNVRAM::sync(void)
1550 {
1551 return syncInternal(false);
1552 }
1553
1554 void
reload(void)1555 IODTNVRAM::reload(void)
1556 {
1557 _format->reload();
1558 }
1559
1560 OSPtr<OSDictionary>
dictionaryWithProperties(void) const1561 IODTNVRAM::dictionaryWithProperties(void) const
1562 {
1563 const OSSymbol *canonicalKey;
1564 OSSharedPtr<OSDictionary> localVarDict, returnDict;
1565 OSSharedPtr<OSCollectionIterator> iter;
1566 unsigned int totalCapacity = 0;
1567 uuid_t varGuid;
1568 const char * varName;
1569
1570 NVRAMREADLOCK();
1571 if (_varDict) {
1572 localVarDict = OSDictionary::withDictionary(_varDict.get());
1573 }
1574 NVRAMUNLOCK();
1575
1576 if (localVarDict != nullptr) {
1577 totalCapacity = localVarDict->getCapacity();
1578 }
1579
1580 returnDict = OSDictionary::withCapacity(totalCapacity);
1581
1582 if (returnDict == nullptr) {
1583 DEBUG_ERROR("No dictionary\n");
1584 goto exit;
1585 }
1586
1587 // Copy system entries first if present then copy unique other entries
1588 iter = OSCollectionIterator::withCollection(localVarDict.get());
1589 if (iter == nullptr) {
1590 DEBUG_ERROR("failed to create iterator\n");
1591 goto exit;
1592 }
1593
1594 while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1595 parseVariableName(canonicalKey, &varGuid, &varName);
1596
1597 if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) &&
1598 verifyPermission(kIONVRAMOperationRead, varGuid, varName, _format->getSystemPartitionActive())) {
1599 OSSharedPtr<const OSSymbol> returnKey = OSSymbol::withCString(varName);
1600 returnDict->setObject(returnKey.get(), localVarDict->getObject(canonicalKey));
1601 }
1602 }
1603
1604 iter.reset();
1605
1606 iter = OSCollectionIterator::withCollection(localVarDict.get());
1607 if (iter == nullptr) {
1608 DEBUG_ERROR("failed to create iterator\n");
1609 goto exit;
1610 }
1611
1612 while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1613 parseVariableName(canonicalKey, &varGuid, &varName);
1614
1615 if (uuid_compare(varGuid, gAppleNVRAMGuid) == 0) {
1616 if (returnDict->getObject(varName) != nullptr) {
1617 // Skip non uniques
1618 continue;
1619 }
1620
1621 if (verifyPermission(kIONVRAMOperationRead, varGuid, varName, _format->getSystemPartitionActive())) {
1622 OSSharedPtr<const OSSymbol> returnKey = OSSymbol::withCString(varName);
1623 returnDict->setObject(returnKey.get(), localVarDict->getObject(canonicalKey));
1624 }
1625 }
1626 }
1627 exit:
1628 return returnDict;
1629 }
1630
1631 bool
serializeProperties(OSSerialize * s) const1632 IODTNVRAM::serializeProperties(OSSerialize *s) const
1633 {
1634 bool ok = false;
1635 OSSharedPtr<OSDictionary> dict;
1636
1637 dict = dictionaryWithProperties();
1638 if (dict) {
1639 ok = dict->serialize(s);
1640 }
1641
1642 DEBUG_INFO("ok=%d\n", ok);
1643 return ok;
1644 }
1645
1646 IOReturn
flushGUID(const uuid_t guid,IONVRAMOperation op)1647 IODTNVRAM::flushGUID(const uuid_t guid, IONVRAMOperation op)
1648 {
1649 IOReturn ret = kIOReturnSuccess;
1650
1651 if (_format->getSystemPartitionActive() && (uuid_compare(guid, gAppleSystemVariableGuid) == 0)) {
1652 ret = _format->flush(guid, op);
1653
1654 DEBUG_INFO("system variables flushed, ret=%08x\n", ret);
1655 } else if (uuid_compare(guid, gAppleNVRAMGuid) == 0) {
1656 ret = _format->flush(guid, op);
1657
1658 DEBUG_INFO("common variables flushed, ret=%08x\n", ret);
1659 }
1660
1661 return ret;
1662 }
1663
1664 bool
handleSpecialVariables(const char * name,const uuid_t guid,const OSObject * obj,IOReturn * error)1665 IODTNVRAM::handleSpecialVariables(const char *name, const uuid_t guid, const OSObject *obj, IOReturn *error)
1666 {
1667 IOReturn ret = kIOReturnSuccess;
1668 bool special = false;
1669
1670 NVRAMLOCKASSERTEXCLUSIVE();
1671
1672 // ResetNVRam flushes both regions in one call
1673 // Obliterate can flush either separately
1674 if (strcmp(name, "ObliterateNVRam") == 0) {
1675 special = true;
1676 ret = flushGUID(guid, kIONVRAMOperationObliterate);
1677 } else if (strcmp(name, "ResetNVRam") == 0) {
1678 special = true;
1679 ret = flushGUID(gAppleSystemVariableGuid, kIONVRAMOperationReset);
1680
1681 if (ret != kIOReturnSuccess) {
1682 goto exit;
1683 }
1684
1685 ret = flushGUID(gAppleNVRAMGuid, kIONVRAMOperationReset);
1686 }
1687
1688 exit:
1689 if (error) {
1690 *error = ret;
1691 }
1692
1693 return special;
1694 }
1695
1696 OSSharedPtr<OSObject>
copyPropertyWithGUIDAndName(const uuid_t guid,const char * name) const1697 IODTNVRAM::copyPropertyWithGUIDAndName(const uuid_t guid, const char *name) const
1698 {
1699 OSSharedPtr<const OSSymbol> canonicalKey;
1700 OSSharedPtr<OSObject> theObject;
1701 uuid_t newGuid;
1702
1703 if (_varDict == nullptr) {
1704 DEBUG_INFO("No dictionary\n");
1705 goto exit;
1706 }
1707
1708 if (!verifyPermission(kIONVRAMOperationRead, guid, name, _format->getSystemPartitionActive())) {
1709 DEBUG_INFO("Not privileged\n");
1710 goto exit;
1711 }
1712
1713 translateGUID(guid, name, newGuid, _format->getSystemPartitionActive());
1714
1715 canonicalKey = keyWithGuidAndCString(newGuid, name);
1716
1717 NVRAMREADLOCK();
1718 theObject.reset(_varDict->getObject(canonicalKey.get()), OSRetain);
1719 NVRAMUNLOCK();
1720
1721 if (_diags) {
1722 _diags->logVariable(getPartitionTypeForGUID(newGuid), kIONVRAMOperationRead, name, NULL);
1723 }
1724
1725 if (theObject != nullptr) {
1726 DEBUG_INFO("%s has object\n", canonicalKey.get()->getCStringNoCopy());
1727 } else {
1728 DEBUG_INFO("%s no entry\n", canonicalKey.get()->getCStringNoCopy());
1729 }
1730
1731 exit:
1732 return theObject;
1733 }
1734
1735 OSSharedPtr<OSObject>
copyProperty(const OSSymbol * aKey) const1736 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1737 {
1738 const char *variableName;
1739 uuid_t varGuid;
1740
1741 if (skipKey(aKey)) {
1742 return nullptr;
1743 }
1744 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1745
1746 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1747
1748 return copyPropertyWithGUIDAndName(varGuid, variableName);
1749 }
1750
1751 OSSharedPtr<OSObject>
copyProperty(const char * aKey) const1752 IODTNVRAM::copyProperty(const char *aKey) const
1753 {
1754 OSSharedPtr<const OSSymbol> keySymbol;
1755 OSSharedPtr<OSObject> theObject;
1756
1757 keySymbol = OSSymbol::withCString(aKey);
1758 if (keySymbol != nullptr) {
1759 theObject = copyProperty(keySymbol.get());
1760 }
1761
1762 return theObject;
1763 }
1764
1765 OSObject *
getProperty(const OSSymbol * aKey) const1766 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1767 {
1768 // The shared pointer gets released at the end of the function,
1769 // and returns a view into theObject.
1770 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1771
1772 return theObject.get();
1773 }
1774
1775 OSObject *
getProperty(const char * aKey) const1776 IODTNVRAM::getProperty(const char *aKey) const
1777 {
1778 // The shared pointer gets released at the end of the function,
1779 // and returns a view into theObject.
1780 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1781
1782 return theObject.get();
1783 }
1784
1785 IOReturn
setPropertyWithGUIDAndName(const uuid_t guid,const char * name,OSObject * anObject)1786 IODTNVRAM::setPropertyWithGUIDAndName(const uuid_t guid, const char *name, OSObject *anObject)
1787 {
1788 IOReturn ret = kIOReturnSuccess;
1789 bool remove = false;
1790 OSString *tmpString = nullptr;
1791 OSSharedPtr<OSObject> propObject;
1792 OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1793 bool deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey, deletePropertyKeyWRet;
1794 bool ok;
1795 size_t propDataSize = 0;
1796 uuid_t newGuid;
1797
1798 deletePropertyKey = strncmp(name, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1799 deletePropertyKeyWRet = strncmp(name, kIONVRAMDeletePropertyKeyWRet, sizeof(kIONVRAMDeletePropertyKeyWRet)) == 0;
1800 syncNowPropertyKey = strncmp(name, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1801 forceSyncNowPropertyKey = strncmp(name, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1802
1803 if (deletePropertyKey || deletePropertyKeyWRet) {
1804 tmpString = OSDynamicCast(OSString, anObject);
1805 if (tmpString != nullptr) {
1806 const char *variableName;
1807 uuid_t valueVarGuid;
1808 bool guidProvided;
1809 IOReturn removeRet;
1810
1811 guidProvided = parseVariableName(tmpString->getCStringNoCopy(), &valueVarGuid, &variableName);
1812
1813 // nvram tool will provide a "nvram -d var" or "nvram -d guid:var" as
1814 // kIONVRAMDeletePropertyKey=var or kIONVRAMDeletePropertyKey=guid:var
1815 // that will come into this function as (gAppleNVRAMGuid, varname, nullptr)
1816 // if we provide the "-z" flag to the nvram tool this function will come in as
1817 // (gAppleSystemVariableGuid, varname, nullptr). We are reparsing the value string,
1818 // if there is a GUID provided with the value then use that GUID otherwise use the
1819 // guid that was provided via the node selection or default.
1820 if (guidProvided == false) {
1821 DEBUG_INFO("Removing with API provided GUID\n");
1822 removeRet = removePropertyWithGUIDAndName(guid, variableName);
1823 } else {
1824 DEBUG_INFO("Removing with value provided GUID\n");
1825 removeRet = removePropertyWithGUIDAndName(valueVarGuid, variableName);
1826 }
1827 if (deletePropertyKeyWRet) {
1828 ret = removeRet;
1829 }
1830 DEBUG_INFO("%s found, removeRet=%#08x\n", deletePropertyKeyWRet ? "deletePropertyKeyWRet" : "deletePropertyKey", removeRet);
1831 } else {
1832 DEBUG_INFO("%s value needs to be an OSString\n", deletePropertyKeyWRet ? "deletePropertyKeyWRet" : "deletePropertyKey");
1833 ret = kIOReturnError;
1834 }
1835 goto exit;
1836 } else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1837 tmpString = OSDynamicCast(OSString, anObject);
1838 DEBUG_INFO("NVRAM sync key %s found\n", name);
1839 if (tmpString != nullptr) {
1840 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1841 ret = syncInternal(syncNowPropertyKey);
1842 } else {
1843 DEBUG_INFO("%s value needs to be an OSString\n", name);
1844 ret = kIOReturnError;
1845 }
1846 goto exit;
1847 }
1848
1849 if (!verifyPermission(kIONVRAMOperationWrite, guid, name, _format->getSystemPartitionActive())) {
1850 DEBUG_INFO("Not privileged\n");
1851 ret = kIOReturnNotPrivileged;
1852 goto exit;
1853 }
1854
1855 // Make sure the object is of the correct type.
1856 switch (getVariableType(name)) {
1857 case kOFVariableTypeBoolean:
1858 propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1859 if (propObject) {
1860 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s as bool to %d", name, ((OSBoolean *)propObject.get())->getValue());
1861 }
1862 break;
1863
1864 case kOFVariableTypeNumber:
1865 propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1866 if (propObject) {
1867 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s as number to %#llx", name, ((OSNumber *)propObject.get())->unsigned64BitValue());
1868 }
1869 break;
1870
1871 case kOFVariableTypeString:
1872 propObject = OSDynamicPtrCast<OSString>(sharedObject);
1873 if (propObject != nullptr) {
1874 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s as string to %s", name, ((OSString *)propObject.get())->getCStringNoCopy());
1875
1876 propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1877
1878 if ((strncmp(name, kIONVRAMBootArgsKey, sizeof(kIONVRAMBootArgsKey)) == 0) && (propDataSize >= BOOT_LINE_LENGTH)) {
1879 DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1880 ret = kIOReturnNoSpace;
1881 goto exit;
1882 }
1883 }
1884 break;
1885
1886 case kOFVariableTypeData:
1887 propObject = OSDynamicPtrCast<OSData>(sharedObject);
1888 if (propObject == nullptr) {
1889 tmpString = OSDynamicCast(OSString, sharedObject.get());
1890 if (tmpString != nullptr) {
1891 propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1892 tmpString->getLength());
1893 }
1894 }
1895
1896 if (propObject != nullptr) {
1897 propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1898 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s as data with size %#x", name, ((OSData *)propObject.get())->getLength());
1899 }
1900
1901 #if defined(XNU_TARGET_OS_OSX)
1902 if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1903 remove = true;
1904 }
1905 #endif /* defined(XNU_TARGET_OS_OSX) */
1906 break;
1907 default:
1908 break;
1909 }
1910
1911 if (propObject == nullptr) {
1912 DEBUG_ERROR("No property object\n");
1913 ret = kIOReturnBadArgument;
1914 goto exit;
1915 }
1916
1917 if (!verifyWriteSizeLimit(guid, name, propDataSize)) {
1918 DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, name);
1919 ret = kIOReturnNoSpace;
1920 goto exit;
1921 }
1922
1923 NVRAMWRITELOCK();
1924 ok = handleSpecialVariables(name, guid, propObject.get(), &ret);
1925 NVRAMUNLOCK();
1926
1927 if (ok) {
1928 goto exit;
1929 }
1930
1931 if (remove == false) {
1932 DEBUG_INFO("Adding object\n");
1933
1934 translateGUID(guid, name, newGuid, _format->getSystemPartitionActive());
1935
1936 NVRAMWRITELOCK();
1937
1938 ret = _format->setVariable(newGuid, name, propObject.get());
1939
1940 NVRAMUNLOCK();
1941 } else {
1942 DEBUG_INFO("Removing object\n");
1943 ret = removePropertyWithGUIDAndName(guid, name);
1944 }
1945
1946 if (tmpString) {
1947 propObject.reset();
1948 }
1949
1950 exit:
1951 DEBUG_INFO("ret=%#08x\n", ret);
1952
1953 return ret;
1954 }
1955
1956 IOReturn
setPropertyInternal(const OSSymbol * aKey,OSObject * anObject)1957 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1958 {
1959 const char *variableName;
1960 uuid_t varGuid;
1961
1962 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1963
1964 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1965
1966 return setPropertyWithGUIDAndName(varGuid, variableName, anObject);
1967 }
1968
1969 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)1970 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1971 {
1972 return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1973 }
1974
1975 void
removeProperty(const OSSymbol * aKey)1976 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1977 {
1978 IOReturn ret;
1979
1980 ret = removePropertyInternal(aKey);
1981
1982 if (ret != kIOReturnSuccess) {
1983 DEBUG_INFO("removePropertyInternal failed, ret=%#08x\n", ret);
1984 }
1985 }
1986
1987 IOReturn
removePropertyWithGUIDAndName(const uuid_t guid,const char * name)1988 IODTNVRAM::removePropertyWithGUIDAndName(const uuid_t guid, const char *name)
1989 {
1990 IOReturn ret;
1991 uuid_t newGuid;
1992
1993 DEBUG_INFO("name=%s\n", name);
1994
1995 if (_varDict == nullptr) {
1996 DEBUG_INFO("No dictionary\n");
1997 ret = kIOReturnNotFound;
1998 goto exit;
1999 }
2000
2001 if (!verifyPermission(kIONVRAMOperationDelete, guid, name, _format->getSystemPartitionActive())) {
2002 DEBUG_INFO("Not privileged\n");
2003 ret = kIOReturnNotPrivileged;
2004 goto exit;
2005 }
2006
2007 translateGUID(guid, name, newGuid, _format->getSystemPartitionActive());
2008
2009 NVRAMWRITELOCK();
2010
2011 ret = _format->setVariable(newGuid, name, nullptr);
2012
2013 if (ret != kIOReturnSuccess) {
2014 DEBUG_INFO("%s not found\n", name);
2015 ret = kIOReturnNotFound;
2016 }
2017
2018 NVRAMUNLOCK();
2019
2020 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "delete", "%s", name);
2021
2022 exit:
2023 return ret;
2024 }
2025
2026 IOReturn
removePropertyInternal(const OSSymbol * aKey)2027 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
2028 {
2029 IOReturn ret;
2030 const char *variableName;
2031 uuid_t varGuid;
2032
2033 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
2034
2035 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
2036
2037 ret = removePropertyWithGUIDAndName(varGuid, variableName);
2038
2039 return ret;
2040 }
2041
2042 IOReturn
setProperties(OSObject * properties)2043 IODTNVRAM::setProperties(OSObject *properties)
2044 {
2045 IOReturn ret = kIOReturnSuccess;
2046 OSObject *object;
2047 const OSSymbol *key;
2048 OSDictionary *dict;
2049 OSSharedPtr<OSCollectionIterator> iter;
2050
2051 dict = OSDynamicCast(OSDictionary, properties);
2052 if (dict == nullptr) {
2053 DEBUG_ERROR("Not a dictionary\n");
2054 return kIOReturnBadArgument;
2055 }
2056
2057 iter = OSCollectionIterator::withCollection(dict);
2058 if (iter == nullptr) {
2059 DEBUG_ERROR("Couldn't create iterator\n");
2060 return kIOReturnBadArgument;
2061 }
2062
2063 while (ret == kIOReturnSuccess) {
2064 key = OSDynamicCast(OSSymbol, iter->getNextObject());
2065 if (key == nullptr) {
2066 break;
2067 }
2068
2069 object = dict->getObject(key);
2070 if (object == nullptr) {
2071 continue;
2072 }
2073
2074 ret = setPropertyInternal(key, object);
2075 }
2076
2077 DEBUG_INFO("ret=%#08x\n", ret);
2078
2079 return ret;
2080 }
2081
2082 // ********************** Deprecated ********************
2083
2084 IOReturn
readXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)2085 IODTNVRAM::readXPRAM(IOByteCount offset, uint8_t *buffer,
2086 IOByteCount length)
2087 {
2088 return kIOReturnUnsupported;
2089 }
2090
2091 IOReturn
writeXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)2092 IODTNVRAM::writeXPRAM(IOByteCount offset, uint8_t *buffer,
2093 IOByteCount length)
2094 {
2095 return kIOReturnUnsupported;
2096 }
2097
2098 IOReturn
readNVRAMProperty(IORegistryEntry * entry,const OSSymbol ** name,OSData ** value)2099 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
2100 const OSSymbol **name,
2101 OSData **value)
2102 {
2103 return kIOReturnUnsupported;
2104 }
2105
2106 IOReturn
writeNVRAMProperty(IORegistryEntry * entry,const OSSymbol * name,OSData * value)2107 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
2108 const OSSymbol *name,
2109 OSData *value)
2110 {
2111 return kIOReturnUnsupported;
2112 }
2113
2114 OSDictionary *
getNVRAMPartitions(void)2115 IODTNVRAM::getNVRAMPartitions(void)
2116 {
2117 return NULL;
2118 }
2119
2120 IOReturn
readNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)2121 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
2122 IOByteCount offset, uint8_t *buffer,
2123 IOByteCount length)
2124 {
2125 return kIOReturnUnsupported;
2126 }
2127
2128 IOReturn
writeNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)2129 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
2130 IOByteCount offset, uint8_t *buffer,
2131 IOByteCount length)
2132 {
2133 return kIOReturnUnsupported;
2134 }
2135
2136 IOByteCount
savePanicInfo(uint8_t * buffer,IOByteCount length)2137 IODTNVRAM::savePanicInfo(uint8_t *buffer, IOByteCount length)
2138 {
2139 return 0;
2140 }
2141