2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 cc -o nvram nvram.c -framework CoreFoundation -framework IOKit -Wall
27 #include <IOKit/IOKitLib.h>
28 #include <IOKit/IOKitKeys.h>
29 #include <IOKit/IOKitKeysPrivate.h>
30 #include <CoreFoundation/CoreFoundation.h>
32 #include <mach/mach_error.h>
36 static void UsageMessage(char *message
);
37 static void ParseFile(char *fileName
);
38 static void ParseXMLFile(char *fileName
);
39 static void SetOrGetOFVariable(char *str
);
40 static kern_return_t
GetOFVariable(char *name
, CFStringRef
*nameRef
,
42 static kern_return_t
SetOFVariable(char *name
, char *value
);
43 static void DeleteOFVariable(char *name
);
44 static void PrintOFVariables(void);
45 static void PrintOFVariable(const void *key
,const void *value
,void *context
);
46 static void SetOFVariableFromFile(const void *key
, const void *value
, void *context
);
47 static void ClearOFVariables(void);
48 static void ClearOFVariable(const void *key
,const void *value
,void *context
);
49 static CFTypeRef
ConvertValueToCFTypeRef(CFTypeID typeID
, char *value
);
51 static void NVRamSyncNow(char *name
);
54 static char *gToolName
;
55 static io_registry_entry_t gOptionsRef
;
57 static bool gUseForceSync
;
60 int main(int argc
, char **argv
)
63 char *str
, errorMessage
[256];
65 mach_port_t masterPort
;
68 // Get the name of the command.
69 gToolName
= strrchr(argv
[0], '/');
70 if (gToolName
!= 0) gToolName
++;
71 else gToolName
= argv
[0];
73 result
= IOMasterPort(bootstrap_port
, &masterPort
);
74 if (result
!= KERN_SUCCESS
) {
75 errx(1, "Error getting the IOMaster port: %s",
76 mach_error_string(result
));
79 gOptionsRef
= IORegistryEntryFromPath(masterPort
, "IODeviceTree:/options");
80 if (gOptionsRef
== 0) {
81 errx(1, "nvram is not supported on this system");
84 for (cnt
= 1; cnt
< argc
; cnt
++) {
86 if (str
[0] == '-' && str
[1] != 0) {
88 for (str
+= 1 ; *str
; str
++) {
100 if (cnt
< argc
&& *argv
[cnt
] != '-') {
101 ParseFile(argv
[cnt
]);
103 UsageMessage("missing filename");
109 if (cnt
< argc
&& *argv
[cnt
] != '-') {
110 DeleteOFVariable(argv
[cnt
]);
112 UsageMessage("missing name");
120 // -s option is unadvertised -- advises the kernel more forcibly to
121 // commit the variable to nonvolatile storage
122 gUseForceSync
= true;
125 strcpy(errorMessage
, "no such option as --");
126 errorMessage
[strlen(errorMessage
)-1] = *str
;
127 UsageMessage(errorMessage
);
131 // Other arguments will be firmware variable requests.
133 SetOrGetOFVariable(str
);
138 if (argcount
== 0 && gUseForceSync
== true) {
142 IOObjectRelease(gOptionsRef
);
147 // UsageMessage(message)
149 // Print the usage information and exit.
151 static void UsageMessage(char *message
)
153 warnx("(usage: %s)", message
);
155 printf("%s [-x] [-p] [-f filename] [-d name] [-c] name[=value] ...\n", gToolName
);
156 printf("\t-x use XML format for printing or reading variables\n");
157 printf("\t (must appear before -p or -f)\n");
158 printf("\t-p print all firmware variables\n");
159 printf("\t-f set firmware variables from a text file\n");
160 printf("\t-d delete the named variable\n");
161 printf("\t-c delete all variables\n");
162 printf("\tname=value set named variable\n");
163 printf("\tname print variable\n");
164 printf("Note that arguments and options are executed in order.\n");
170 // States for ParseFile.
181 kMaxStringSize
= 0x800,
186 // ParseFile(fileName)
188 // Open and parse the specified file.
190 static void ParseFile(char *fileName
)
192 long state
, ni
= 0, vi
= 0;
194 char name
[kMaxNameSize
];
195 char value
[kMaxStringSize
];
200 ParseXMLFile(fileName
);
204 patches
= fopen(fileName
, "r");
206 err(1, "Couldn't open patch file - '%s'", fileName
);
209 state
= kFirstColumn
;
210 while ((tc
= getc(patches
)) != EOF
) {
211 if(ni
==(kMaxNameSize
-1))
212 errx(1, "Name exceeded max length of %d", kMaxNameSize
);
213 if(vi
==(kMaxStringSize
-1))
214 errx(1, "Value exceeded max length of %d", kMaxStringSize
);
220 state
= kScanComment
;
221 } else if (tc
== '\n') {
222 // state stays kFirstColumn.
223 } else if (isspace(tc
)) {
226 state
= kCollectName
;
233 state
= kFirstColumn
;
235 // state stays kScanComment.
241 state
= kFirstColumn
;
242 } else if (isspace(tc
)) {
243 // state stays kFindName.
245 state
= kCollectName
;
253 warnx("Name must be followed by white space - '%s'", name
);
254 state
= kFirstColumn
;
255 } else if (isspace(tc
)) {
259 // state staus kCollectName.
264 case kContinueValue
:
267 } else if (isspace(tc
)) {
268 // state stays kFindValue or kContinueValue.
270 state
= kCollectValue
;
277 if (value
[vi
-1] == '\\') {
279 state
= kContinueValue
;
284 // state stays kCollectValue.
290 if (state
== kSetenv
) {
293 if ((kret
= SetOFVariable(name
, value
)) != KERN_SUCCESS
) {
294 errx(1, "Error setting variable - '%s': %s", name
,
295 mach_error_string(kret
));
297 state
= kFirstColumn
;
301 if (state
!= kFirstColumn
) {
302 errx(1, "Last line ended abruptly");
306 // ParseXMLFile(fileName)
308 // Open and parse the specified file in XML format,
309 // and set variables appropriately.
311 static void ParseXMLFile(char *fileName
)
313 CFPropertyListRef plist
;
317 CFReadStreamRef stream
;
318 CFPropertyListFormat format
= kCFPropertyListBinaryFormat_v1_0
;
320 fd
= open(fileName
, O_RDONLY
| O_NOFOLLOW
, S_IFREG
);
322 errx(1, "Could not open %s: %s", fileName
, strerror(errno
));
325 if (fstat(fd
, &sb
) == -1) {
326 errx(1, "Could not fstat %s: %s", fileName
, strerror(errno
));
329 if (sb
.st_size
> UINT32_MAX
) {
330 errx(1, "too big for our purposes");
333 buffer
= malloc((size_t)sb
.st_size
);
334 if (buffer
== NULL
) {
335 errx(1, "Could not allocate buffer");
338 if (read(fd
, buffer
, (size_t)sb
.st_size
) != sb
.st_size
) {
339 errx(1, "Could not read %s: %s", fileName
, strerror(errno
));
344 stream
= CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault
,
345 (const UInt8
*)buffer
,
348 if (stream
== NULL
) {
349 errx(1, "Could not create stream from serialized data");
352 if (!CFReadStreamOpen(stream
)) {
353 errx(1, "Could not open the stream");
356 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
,
359 kCFPropertyListImmutable
,
364 errx(1, "Error parsing XML file");
367 CFReadStreamClose(stream
);
373 CFDictionaryApplyFunction(plist
, &SetOFVariableFromFile
, 0);
378 // SetOrGetOFVariable(str)
380 // Parse the input string, then set or get the specified
381 // firmware variable.
383 static void SetOrGetOFVariable(char *str
)
390 kern_return_t result
;
392 // OF variable name is first.
395 // Find the equal sign for set
406 // On sets, the OF variable's value follows the equal sign.
409 result
= SetOFVariable(name
, value
);
410 NVRamSyncNow(name
); /* Try syncing the new data to device, best effort! */
411 if (result
!= KERN_SUCCESS
) {
412 errx(1, "Error setting variable - '%s': %s", name
,
413 mach_error_string(result
));
416 result
= GetOFVariable(name
, &nameRef
, &valueRef
);
417 if (result
!= KERN_SUCCESS
) {
418 errx(1, "Error getting variable - '%s': %s", name
,
419 mach_error_string(result
));
422 PrintOFVariable(nameRef
, valueRef
, 0);
429 // GetOFVariable(name, nameRef, valueRef)
431 // Get the named firmware variable.
432 // Return it and it's symbol in valueRef and nameRef.
434 static kern_return_t
GetOFVariable(char *name
, CFStringRef
*nameRef
,
437 *nameRef
= CFStringCreateWithCString(kCFAllocatorDefault
, name
,
438 kCFStringEncodingUTF8
);
440 errx(1, "Error creating CFString for key %s", name
);
443 *valueRef
= IORegistryEntryCreateCFProperty(gOptionsRef
, *nameRef
, 0, 0);
444 if (*valueRef
== 0) return kIOReturnNotFound
;
450 // SetOFVariable(name, value)
452 // Set or create an firmware variable with name and value.
454 static kern_return_t
SetOFVariable(char *name
, char *value
)
459 kern_return_t result
= KERN_SUCCESS
;
461 nameRef
= CFStringCreateWithCString(kCFAllocatorDefault
, name
,
462 kCFStringEncodingUTF8
);
464 errx(1, "Error creating CFString for key %s", name
);
467 valueRef
= IORegistryEntryCreateCFProperty(gOptionsRef
, nameRef
, 0, 0);
469 typeID
= CFGetTypeID(valueRef
);
472 valueRef
= ConvertValueToCFTypeRef(typeID
, value
);
474 errx(1, "Error creating CFTypeRef for value %s", value
);
475 } result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
478 // In the default case, try data, string, number, then boolean.
480 valueRef
= ConvertValueToCFTypeRef(CFDataGetTypeID(), value
);
482 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
483 if (result
== KERN_SUCCESS
) break;
486 valueRef
= ConvertValueToCFTypeRef(CFStringGetTypeID(), value
);
488 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
489 if (result
== KERN_SUCCESS
) break;
492 valueRef
= ConvertValueToCFTypeRef(CFNumberGetTypeID(), value
);
494 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
495 if (result
== KERN_SUCCESS
) break;
498 valueRef
= ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value
);
500 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
501 if (result
== KERN_SUCCESS
) break;
514 // DeleteOFVariable(name)
516 // Delete the named firmware variable.
519 static void DeleteOFVariable(char *name
)
521 SetOFVariable(kIONVRAMDeletePropertyKey
, name
);
524 static void NVRamSyncNow(char *name
)
526 if (!gUseForceSync
) {
527 SetOFVariable(kIONVRAMSyncNowPropertyKey
, name
);
529 SetOFVariable(kIONVRAMForceSyncNowPropertyKey
, name
);
533 // PrintOFVariables()
535 // Print all of the firmware variables.
537 static void PrintOFVariables(void)
539 kern_return_t result
;
540 CFMutableDictionaryRef dict
;
542 result
= IORegistryEntryCreateCFProperties(gOptionsRef
, &dict
, 0, 0);
543 if (result
!= KERN_SUCCESS
) {
544 errx(1, "Error getting the firmware variables: %s", mach_error_string(result
));
550 data
= CFPropertyListCreateData( kCFAllocatorDefault
, dict
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
552 errx(1, "Error converting variables to xml");
555 fwrite(CFDataGetBytePtr(data
), sizeof(UInt8
), CFDataGetLength(data
), stdout
);
561 CFDictionaryApplyFunction(dict
, &PrintOFVariable
, 0);
568 // PrintOFVariable(key, value, context)
570 // Print the given firmware variable.
572 static void PrintOFVariable(const void *key
, const void *value
, void *context
)
576 char *nameBuffer
= 0;
577 const char *nameString
;
578 char numberBuffer
[10];
579 const uint8_t *dataPtr
;
581 char *dataBuffer
= 0;
583 char *valueBuffer
= 0;
584 const char *valueString
= 0;
589 // Get the OF variable's name.
590 nameLen
= CFStringGetLength(key
) + 1;
591 nameBuffer
= malloc(nameLen
);
592 if( nameBuffer
&& CFStringGetCString(key
, nameBuffer
, nameLen
, kCFStringEncodingUTF8
) )
593 nameString
= nameBuffer
;
595 warnx("Unable to convert property name to C string");
596 nameString
= "<UNPRINTABLE>";
599 // Get the OF variable's type.
600 typeID
= CFGetTypeID(value
);
602 if (typeID
== CFBooleanGetTypeID()) {
603 if (CFBooleanGetValue(value
)) valueString
= "true";
604 else valueString
= "false";
605 } else if (typeID
== CFNumberGetTypeID()) {
606 CFNumberGetValue(value
, kCFNumberSInt32Type
, &number
);
607 if (number
== 0xFFFFFFFF) sprintf(numberBuffer
, "-1");
608 else if (number
< 1000) sprintf(numberBuffer
, "%d", number
);
609 else sprintf(numberBuffer
, "0x%x", number
);
610 valueString
= numberBuffer
;
611 } else if (typeID
== CFStringGetTypeID()) {
612 valueLen
= CFStringGetLength(value
) + 1;
613 valueBuffer
= malloc(valueLen
+ 1);
614 if ( valueBuffer
&& CFStringGetCString(value
, valueBuffer
, valueLen
, kCFStringEncodingUTF8
) )
615 valueString
= valueBuffer
;
617 warnx("Unable to convert value to C string");
618 valueString
= "<UNPRINTABLE>";
620 } else if (typeID
== CFDataGetTypeID()) {
621 length
= CFDataGetLength(value
);
622 if (length
== 0) valueString
= "";
624 dataBuffer
= malloc(length
* 3 + 1);
625 if (dataBuffer
!= 0) {
626 dataPtr
= CFDataGetBytePtr(value
);
627 for (cnt
= cnt2
= 0; cnt
< length
; cnt
++) {
628 dataChar
= dataPtr
[cnt
];
629 if (isprint(dataChar
) && dataChar
!= '%') {
630 dataBuffer
[cnt2
++] = dataChar
;
632 sprintf(dataBuffer
+ cnt2
, "%%%02x", dataChar
);
636 dataBuffer
[cnt2
] = '\0';
637 valueString
= dataBuffer
;
641 valueString
="<INVALID>";
644 if ((nameString
!= 0) && (valueString
!= 0))
645 printf("%s\t%s\n", nameString
, valueString
);
647 if (dataBuffer
!= 0) free(dataBuffer
);
648 if (nameBuffer
!= 0) free(nameBuffer
);
649 if (valueBuffer
!= 0) free(valueBuffer
);
652 // ClearOFVariables()
654 // Deletes all OF variables
656 static void ClearOFVariables(void)
658 kern_return_t result
;
659 CFMutableDictionaryRef dict
;
661 result
= IORegistryEntryCreateCFProperties(gOptionsRef
, &dict
, 0, 0);
662 if (result
!= KERN_SUCCESS
) {
663 errx(1, "Error getting the firmware variables: %s", mach_error_string(result
));
665 CFDictionaryApplyFunction(dict
, &ClearOFVariable
, 0);
670 static void ClearOFVariable(const void *key
, const void *value
, void *context
)
672 kern_return_t result
;
673 result
= IORegistryEntrySetCFProperty(gOptionsRef
,
674 CFSTR(kIONVRAMDeletePropertyKey
), key
);
675 if (result
!= KERN_SUCCESS
) {
676 errx(1, "Error clearing firmware variables: %s", mach_error_string(result
));
680 // ConvertValueToCFTypeRef(typeID, value)
682 // Convert the value into a CFType given the typeID.
684 static CFTypeRef
ConvertValueToCFTypeRef(CFTypeID typeID
, char *value
)
686 CFTypeRef valueRef
= 0;
687 long cnt
, cnt2
, length
;
688 unsigned long number
, tmp
;
690 if (typeID
== CFBooleanGetTypeID()) {
691 if (!strcmp("true", value
)) valueRef
= kCFBooleanTrue
;
692 else if (!strcmp("false", value
)) valueRef
= kCFBooleanFalse
;
693 } else if (typeID
== CFNumberGetTypeID()) {
694 number
= strtol(value
, 0, 0);
695 valueRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
,
697 } else if (typeID
== CFStringGetTypeID()) {
698 valueRef
= CFStringCreateWithCString(kCFAllocatorDefault
, value
,
699 kCFStringEncodingUTF8
);
700 } else if (typeID
== CFDataGetTypeID()) {
701 length
= strlen(value
);
702 for (cnt
= cnt2
= 0; cnt
< length
; cnt
++, cnt2
++) {
703 if (value
[cnt
] == '%') {
704 if (!ishexnumber(value
[cnt
+ 1]) ||
705 !ishexnumber(value
[cnt
+ 2])) return 0;
706 number
= toupper(value
[++cnt
]) - '0';
707 if (number
> 9) number
-= 7;
708 tmp
= toupper(value
[++cnt
]) - '0';
709 if (tmp
> 9) tmp
-= 7;
710 number
= (number
<< 4) + tmp
;
711 value
[cnt2
] = number
;
712 } else value
[cnt2
] = value
[cnt
];
714 valueRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)value
,
715 cnt2
, kCFAllocatorDefault
);
721 static void SetOFVariableFromFile(const void *key
, const void *value
, void *context
)
723 kern_return_t result
;
725 result
= IORegistryEntrySetCFProperty(gOptionsRef
, key
, value
);
726 if ( result
!= KERN_SUCCESS
) {
731 // Get the variable's name.
732 nameLen
= CFStringGetLength(key
) + 1;
733 nameBuffer
= malloc(nameLen
);
734 if( nameBuffer
&& CFStringGetCString(key
, nameBuffer
, nameLen
, kCFStringEncodingUTF8
) )
735 nameString
= nameBuffer
;
737 warnx("Unable to convert property name to C string");
738 nameString
= "<UNPRINTABLE>";
740 errx(1, "Error setting variable - '%s': %s", nameString
,
741 mach_error_string(result
));