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