2 * Copyright (c) 2000-2012 Apple Computer, 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 <CoreFoundation/CoreFoundation.h>
31 #include <mach/mach_error.h>
34 static void UsageMessage(char *message
);
35 static void ParseFile(char *fileName
);
36 static void ParseXMLFile(char *fileName
);
37 static void SetOrGetOFVariable(char *str
);
38 static kern_return_t
GetOFVariable(char *name
, CFStringRef
*nameRef
,
40 static kern_return_t
SetOFVariable(char *name
, char *value
);
41 static void DeleteOFVariable(char *name
);
42 static void PrintOFVariables(void);
43 static void PrintOFVariable(const void *key
,const void *value
,void *context
);
44 static void SetOFVariableFromFile(const void *key
, const void *value
, void *context
);
45 static void ClearOFVariables(void);
46 static void ClearOFVariable(const void *key
,const void *value
,void *context
);
47 static CFTypeRef
ConvertValueToCFTypeRef(CFTypeID typeID
, char *value
);
49 static void NVRamSyncNow(char *name
);
52 static char *gToolName
;
53 static io_registry_entry_t gOptionsRef
;
57 int main(int argc
, char **argv
)
60 char *str
, errorMessage
[256];
62 mach_port_t masterPort
;
64 // Get the name of the command.
65 gToolName
= strrchr(argv
[0], '/');
66 if (gToolName
!= 0) gToolName
++;
67 else gToolName
= argv
[0];
69 result
= IOMasterPort(bootstrap_port
, &masterPort
);
70 if (result
!= KERN_SUCCESS
) {
71 errx(1, "Error getting the IOMaster port: %s",
72 mach_error_string(result
));
75 gOptionsRef
= IORegistryEntryFromPath(masterPort
, "IODeviceTree:/options");
76 if (gOptionsRef
== 0) {
77 errx(1, "nvram is not supported on this system");
80 for (cnt
= 1; cnt
< argc
; cnt
++) {
82 if (str
[0] == '-' && str
[1] != 0) {
84 for (str
+= 1 ; *str
; str
++) {
96 if (cnt
< argc
&& *argv
[cnt
] != '-') {
99 UsageMessage("missing filename");
105 if (cnt
< argc
&& *argv
[cnt
] != '-') {
106 DeleteOFVariable(argv
[cnt
]);
108 UsageMessage("missing name");
117 strcpy(errorMessage
, "no such option as --");
118 errorMessage
[strlen(errorMessage
)-1] = *str
;
119 UsageMessage(errorMessage
);
123 // Other arguments will be firmware variable requests.
124 SetOrGetOFVariable(str
);
128 IOObjectRelease(gOptionsRef
);
133 // UsageMessage(message)
135 // Print the usage information and exit.
137 static void UsageMessage(char *message
)
139 warnx("(usage: %s)", message
);
141 printf("%s [-x] [-p] [-f filename] [-d name] [-c] name[=value] ...\n", gToolName
);
142 printf("\t-x use XML format for printing or reading variables\n");
143 printf("\t (must appear before -p or -f)\n");
144 printf("\t-p print all firmware variables\n");
145 printf("\t-f set firmware variables from a text file\n");
146 printf("\t-d delete the named variable\n");
147 printf("\t-c delete all variables\n");
148 printf("\tname=value set named variable\n");
149 printf("\tname print variable\n");
150 printf("Note that arguments and options are executed in order.\n");
156 // States for ParseFile.
167 kMaxStringSize
= 0x800,
172 // ParseFile(fileName)
174 // Open and parse the specified file.
176 static void ParseFile(char *fileName
)
178 long state
, tc
, ni
= 0, vi
= 0;
179 char name
[kMaxNameSize
];
180 char value
[kMaxStringSize
];
185 ParseXMLFile(fileName
);
189 patches
= fopen(fileName
, "r");
191 err(1, "Couldn't open patch file - '%s'", fileName
);
194 state
= kFirstColumn
;
195 while ((tc
= getc(patches
)) != EOF
) {
196 if(ni
==(kMaxNameSize
-1))
197 errx(1, "Name exceeded max length of %d", kMaxNameSize
);
198 if(vi
==(kMaxStringSize
-1))
199 errx(1, "Value exceeded max length of %d", kMaxStringSize
);
205 state
= kScanComment
;
206 } else if (tc
== '\n') {
207 // state stays kFirstColumn.
208 } else if (isspace(tc
)) {
211 state
= kCollectName
;
218 state
= kFirstColumn
;
220 // state stays kScanComment.
226 state
= kFirstColumn
;
227 } else if (isspace(tc
)) {
228 // state stays kFindName.
230 state
= kCollectName
;
238 warnx("Name must be followed by white space - '%s'", name
);
239 state
= kFirstColumn
;
240 } else if (isspace(tc
)) {
244 // state staus kCollectName.
249 case kContinueValue
:
252 } else if (isspace(tc
)) {
253 // state stays kFindValue or kContinueValue.
255 state
= kCollectValue
;
262 if (value
[vi
-1] == '\\') {
264 state
= kContinueValue
;
269 // state stays kCollectValue.
275 if (state
== kSetenv
) {
278 if ((kret
= SetOFVariable(name
, value
)) != KERN_SUCCESS
) {
279 errx(1, "Error setting variable - '%s': %s", name
,
280 mach_error_string(kret
));
282 state
= kFirstColumn
;
286 if (state
!= kFirstColumn
) {
287 errx(1, "Last line ended abruptly");
292 // ParseXMLFile(fileName)
294 // Open and parse the specified file in XML format,
295 // and set variables appropriately.
297 static void ParseXMLFile(char *fileName
)
299 CFPropertyListRef plist
;
300 CFURLRef fileURL
= NULL
;
301 CFStringRef filePath
= NULL
;
302 CFStringRef errorString
= NULL
;
303 CFDataRef data
= NULL
;
304 SInt32 errorCode
= 0;
306 filePath
= CFStringCreateWithCString(kCFAllocatorDefault
, fileName
, kCFStringEncodingUTF8
);
307 if (filePath
== NULL
) {
308 errx(1, "Could not create file path string");
311 // Create a URL that specifies the file we will create to
312 // hold the XML data.
313 fileURL
= CFURLCreateWithFileSystemPath( kCFAllocatorDefault
,
315 kCFURLPOSIXPathStyle
,
316 false /* not a directory */ );
317 if (fileURL
== NULL
) {
318 errx(1, "Could not create file path URL");
323 if (! CFURLCreateDataAndPropertiesFromResource(
329 &errorCode
) || data
== NULL
) {
330 errx(1, "Error reading XML file (%d)", (int)errorCode
);
335 plist
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
,
337 kCFPropertyListImmutable
,
343 errx(1, "Error parsing XML file");
346 if (errorString
!= NULL
) {
347 errx(1, "Error parsing XML file: %s", CFStringGetCStringPtr(errorString
, kCFStringEncodingUTF8
));
350 CFDictionaryApplyFunction(plist
, &SetOFVariableFromFile
, 0);
355 // SetOrGetOFVariable(str)
357 // Parse the input string, then set or get the specified
358 // firmware variable.
360 static void SetOrGetOFVariable(char *str
)
367 kern_return_t result
;
369 // OF variable name is first.
372 // Find the equal sign for set
383 // On sets, the OF variable's value follows the equal sign.
386 result
= SetOFVariable(name
, value
);
387 NVRamSyncNow(name
); /* Try syncing the new data to device, best effort! */
388 if (result
!= KERN_SUCCESS
) {
389 errx(1, "Error setting variable - '%s': %s", name
,
390 mach_error_string(result
));
393 result
= GetOFVariable(name
, &nameRef
, &valueRef
);
394 if (result
!= KERN_SUCCESS
) {
395 errx(1, "Error getting variable - '%s': %s", name
,
396 mach_error_string(result
));
399 PrintOFVariable(nameRef
, valueRef
, 0);
406 // GetOFVariable(name, nameRef, valueRef)
408 // Get the named firmware variable.
409 // Return it and it's symbol in valueRef and nameRef.
411 static kern_return_t
GetOFVariable(char *name
, CFStringRef
*nameRef
,
414 *nameRef
= CFStringCreateWithCString(kCFAllocatorDefault
, name
,
415 kCFStringEncodingUTF8
);
417 errx(1, "Error creating CFString for key %s", name
);
420 *valueRef
= IORegistryEntryCreateCFProperty(gOptionsRef
, *nameRef
, 0, 0);
421 if (*valueRef
== 0) return kIOReturnNotFound
;
427 // SetOFVariable(name, value)
429 // Set or create an firmware variable with name and value.
431 static kern_return_t
SetOFVariable(char *name
, char *value
)
436 kern_return_t result
= KERN_SUCCESS
;
438 nameRef
= CFStringCreateWithCString(kCFAllocatorDefault
, name
,
439 kCFStringEncodingUTF8
);
441 errx(1, "Error creating CFString for key %s", name
);
444 valueRef
= IORegistryEntryCreateCFProperty(gOptionsRef
, nameRef
, 0, 0);
446 typeID
= CFGetTypeID(valueRef
);
449 valueRef
= ConvertValueToCFTypeRef(typeID
, value
);
451 errx(1, "Error creating CFTypeRef for value %s", value
);
452 } result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
455 // In the default case, try data, string, number, then boolean.
457 valueRef
= ConvertValueToCFTypeRef(CFDataGetTypeID(), value
);
459 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
460 if (result
== KERN_SUCCESS
) break;
463 valueRef
= ConvertValueToCFTypeRef(CFStringGetTypeID(), value
);
465 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
466 if (result
== KERN_SUCCESS
) break;
469 valueRef
= ConvertValueToCFTypeRef(CFNumberGetTypeID(), value
);
471 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
472 if (result
== KERN_SUCCESS
) break;
475 valueRef
= ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value
);
477 result
= IORegistryEntrySetCFProperty(gOptionsRef
, nameRef
, valueRef
);
478 if (result
== KERN_SUCCESS
) break;
491 // DeleteOFVariable(name)
493 // Delete the named firmware variable.
496 static void DeleteOFVariable(char *name
)
498 SetOFVariable(kIONVRAMDeletePropertyKey
, name
);
501 static void NVRamSyncNow(char *name
)
503 SetOFVariable(kIONVRAMSyncNowPropertyKey
, name
);
506 // PrintOFVariables()
508 // Print all of the firmware variables.
510 static void PrintOFVariables()
512 kern_return_t result
;
513 CFMutableDictionaryRef dict
;
515 result
= IORegistryEntryCreateCFProperties(gOptionsRef
, &dict
, 0, 0);
516 if (result
!= KERN_SUCCESS
) {
517 errx(1, "Error getting the firmware variables: %s", mach_error_string(result
));
523 data
= CFPropertyListCreateXMLData( kCFAllocatorDefault
, dict
);
525 errx(1, "Error converting variables to xml");
528 fwrite(CFDataGetBytePtr(data
), sizeof(UInt8
), CFDataGetLength(data
), stdout
);
534 CFDictionaryApplyFunction(dict
, &PrintOFVariable
, 0);
541 // PrintOFVariable(key, value, context)
543 // Print the given firmware variable.
545 static void PrintOFVariable(const void *key
, const void *value
, void *context
)
549 char *nameBuffer
= 0;
550 const char *nameString
;
551 char numberBuffer
[10];
552 const uint8_t *dataPtr
;
554 char *dataBuffer
= 0;
556 char *valueBuffer
= 0;
557 const char *valueString
= 0;
558 uint32_t number
, length
;
561 // Get the OF variable's name.
562 nameLen
= CFStringGetLength(key
) + 1;
563 nameBuffer
= malloc(nameLen
);
564 if( nameBuffer
&& CFStringGetCString(key
, nameBuffer
, nameLen
, kCFStringEncodingUTF8
) )
565 nameString
= nameBuffer
;
567 warnx("Unable to convert property name to C string");
568 nameString
= "<UNPRINTABLE>";
571 // Get the OF variable's type.
572 typeID
= CFGetTypeID(value
);
574 if (typeID
== CFBooleanGetTypeID()) {
575 if (CFBooleanGetValue(value
)) valueString
= "true";
576 else valueString
= "false";
577 } else if (typeID
== CFNumberGetTypeID()) {
578 CFNumberGetValue(value
, kCFNumberSInt32Type
, &number
);
579 if (number
== 0xFFFFFFFF) sprintf(numberBuffer
, "-1");
580 else if (number
< 1000) sprintf(numberBuffer
, "%d", number
);
581 else sprintf(numberBuffer
, "0x%x", number
);
582 valueString
= numberBuffer
;
583 } else if (typeID
== CFStringGetTypeID()) {
584 valueLen
= CFStringGetLength(value
) + 1;
585 valueBuffer
= malloc(valueLen
+ 1);
586 if ( valueBuffer
&& CFStringGetCString(value
, valueBuffer
, valueLen
, kCFStringEncodingUTF8
) )
587 valueString
= valueBuffer
;
589 warnx("Unable to convert value to C string");
590 valueString
= "<UNPRINTABLE>";
592 } else if (typeID
== CFDataGetTypeID()) {
593 length
= CFDataGetLength(value
);
594 if (length
== 0) valueString
= "";
596 dataBuffer
= malloc(length
* 3 + 1);
597 if (dataBuffer
!= 0) {
598 dataPtr
= CFDataGetBytePtr(value
);
599 for (cnt
= cnt2
= 0; cnt
< length
; cnt
++) {
600 dataChar
= dataPtr
[cnt
];
601 if (isprint(dataChar
)) dataBuffer
[cnt2
++] = dataChar
;
603 sprintf(dataBuffer
+ cnt2
, "%%%02x", dataChar
);
607 dataBuffer
[cnt2
] = '\0';
608 valueString
= dataBuffer
;
612 valueString
="<INVALID>";
615 if ((nameString
!= 0) && (valueString
!= 0))
616 printf("%s\t%s\n", nameString
, valueString
);
618 if (dataBuffer
!= 0) free(dataBuffer
);
619 if (nameBuffer
!= 0) free(nameBuffer
);
620 if (valueBuffer
!= 0) free(valueBuffer
);
623 // ClearOFVariables()
625 // Deletes all OF variables
627 static void ClearOFVariables(void)
629 kern_return_t result
;
630 CFMutableDictionaryRef dict
;
632 result
= IORegistryEntryCreateCFProperties(gOptionsRef
, &dict
, 0, 0);
633 if (result
!= KERN_SUCCESS
) {
634 errx(1, "Error getting the firmware variables: %s", mach_error_string(result
));
636 CFDictionaryApplyFunction(dict
, &ClearOFVariable
, 0);
641 static void ClearOFVariable(const void *key
, const void *value
, void *context
)
643 kern_return_t result
;
644 result
= IORegistryEntrySetCFProperty(gOptionsRef
,
645 CFSTR(kIONVRAMDeletePropertyKey
), key
);
646 if (result
!= KERN_SUCCESS
) {
647 errx(1, "Error clearing firmware variables: %s", mach_error_string(result
));
651 // ConvertValueToCFTypeRef(typeID, value)
653 // Convert the value into a CFType given the typeID.
655 static CFTypeRef
ConvertValueToCFTypeRef(CFTypeID typeID
, char *value
)
657 CFTypeRef valueRef
= 0;
658 long cnt
, cnt2
, length
;
659 unsigned long number
, tmp
;
661 if (typeID
== CFBooleanGetTypeID()) {
662 if (!strcmp("true", value
)) valueRef
= kCFBooleanTrue
;
663 else if (!strcmp("false", value
)) valueRef
= kCFBooleanFalse
;
664 } else if (typeID
== CFNumberGetTypeID()) {
665 number
= strtol(value
, 0, 0);
666 valueRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
,
668 } else if (typeID
== CFStringGetTypeID()) {
669 valueRef
= CFStringCreateWithCString(kCFAllocatorDefault
, value
,
670 kCFStringEncodingUTF8
);
671 } else if (typeID
== CFDataGetTypeID()) {
672 length
= strlen(value
);
673 for (cnt
= cnt2
= 0; cnt
< length
; cnt
++, cnt2
++) {
674 if (value
[cnt
] == '%') {
675 if (!ishexnumber(value
[cnt
+ 1]) ||
676 !ishexnumber(value
[cnt
+ 2])) return 0;
677 number
= toupper(value
[++cnt
]) - '0';
678 if (number
> 9) number
-= 7;
679 tmp
= toupper(value
[++cnt
]) - '0';
680 if (tmp
> 9) tmp
-= 7;
681 number
= (number
<< 4) + tmp
;
682 value
[cnt2
] = number
;
683 } else value
[cnt2
] = value
[cnt
];
685 valueRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)value
,
686 cnt2
, kCFAllocatorDefault
);
692 static void SetOFVariableFromFile(const void *key
, const void *value
, void *context
)
694 kern_return_t result
;
696 result
= IORegistryEntrySetCFProperty(gOptionsRef
, key
, value
);
697 if ( result
!= KERN_SUCCESS
) {
702 // Get the variable's name.
703 nameLen
= CFStringGetLength(key
) + 1;
704 nameBuffer
= malloc(nameLen
);
705 if( nameBuffer
&& CFStringGetCString(key
, nameBuffer
, nameLen
, kCFStringEncodingUTF8
) )
706 nameString
= nameBuffer
;
708 warnx("Unable to convert property name to C string");
709 nameString
= "<UNPRINTABLE>";
711 errx(1, "Error setting variable - '%s': %s", nameString
,
712 mach_error_string(result
));