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