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