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