1/* 2 * Copyright (c) 2000-2012 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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. 11 * 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 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22/* 23cc -o nvram nvram.c -framework CoreFoundation -framework IOKit -Wall 24*/ 25 26#include <stdio.h> 27#include <IOKit/IOKitLib.h> 28#include <IOKit/IOKitKeys.h> 29#include <CoreFoundation/CoreFoundation.h> 30#include <err.h> 31#include <mach/mach_error.h> 32 33// Prototypes 34static void UsageMessage(char *message); 35static void ParseFile(char *fileName); 36static void ParseXMLFile(char *fileName); 37static void SetOrGetOFVariable(char *str); 38static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef, 39 CFTypeRef *valueRef); 40static kern_return_t SetOFVariable(char *name, char *value); 41static void DeleteOFVariable(char *name); 42static void PrintOFVariables(void); 43static void PrintOFVariable(const void *key,const void *value,void *context); 44static void SetOFVariableFromFile(const void *key, const void *value, void *context); 45static void ClearOFVariables(void); 46static void ClearOFVariable(const void *key,const void *value,void *context); 47static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value); 48 49static void NVRamSyncNow(char *name); 50 51// Global Variables 52static char *gToolName; 53static io_registry_entry_t gOptionsRef; 54static bool gUseXML; 55 56 57int main(int argc, char **argv) 58{ 59 long cnt; 60 char *str, errorMessage[256]; 61 kern_return_t result; 62 mach_port_t masterPort; 63 64 // Get the name of the command. 65 gToolName = strrchr(argv[0], '/'); 66 if (gToolName != 0) gToolName++; 67 else gToolName = argv[0]; 68 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)); 73 } 74 75 gOptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options"); 76 if (gOptionsRef == 0) { 77 errx(1, "nvram is not supported on this system"); 78 } 79 80 for (cnt = 1; cnt < argc; cnt++) { 81 str = argv[cnt]; 82 if (str[0] == '-' && str[1] != 0) { 83 // Parse the options. 84 for (str += 1 ; *str; str++) { 85 switch (*str) { 86 case 'p' : 87 PrintOFVariables(); 88 break; 89 90 case 'x' : 91 gUseXML = true; 92 break; 93 94 case 'f': 95 cnt++; 96 if (cnt < argc && *argv[cnt] != '-') { 97 ParseFile(argv[cnt]); 98 } else { 99 UsageMessage("missing filename"); 100 } 101 break; 102 103 case 'd': 104 cnt++; 105 if (cnt < argc && *argv[cnt] != '-') { 106 DeleteOFVariable(argv[cnt]); 107 } else { 108 UsageMessage("missing name"); 109 } 110 break; 111 112 case 'c': 113 ClearOFVariables(); 114 break; 115 116 default: 117 strcpy(errorMessage, "no such option as --"); 118 errorMessage[strlen(errorMessage)-1] = *str; 119 UsageMessage(errorMessage); 120 } 121 } 122 } else { 123 // Other arguments will be firmware variable requests. 124 SetOrGetOFVariable(str); 125 } 126 } 127 128 IOObjectRelease(gOptionsRef); 129 130 return 0; 131} 132 133// UsageMessage(message) 134// 135// Print the usage information and exit. 136// 137static void UsageMessage(char *message) 138{ 139 warnx("(usage: %s)", message); 140 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"); 151 152 exit(1); 153} 154 155 156// States for ParseFile. 157enum { 158 kFirstColumn = 0, 159 kScanComment, 160 kFindName, 161 kCollectName, 162 kFindValue, 163 kCollectValue, 164 kContinueValue, 165 kSetenv, 166 167 kMaxStringSize = 0x800, 168 kMaxNameSize = 0x100 169}; 170 171 172// ParseFile(fileName) 173// 174// Open and parse the specified file. 175// 176static void ParseFile(char *fileName) 177{ 178 long state, tc, ni = 0, vi = 0; 179 char name[kMaxNameSize]; 180 char value[kMaxStringSize]; 181 FILE *patches; 182 kern_return_t kret; 183 184 if (gUseXML) { 185 ParseXMLFile(fileName); 186 return; 187 } 188 189 patches = fopen(fileName, "r"); 190 if (patches == 0) { 191 err(1, "Couldn't open patch file - '%s'", fileName); 192 } 193 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); 200 switch (state) { 201 case kFirstColumn : 202 ni = 0; 203 vi = 0; 204 if (tc == '#') { 205 state = kScanComment; 206 } else if (tc == '\n') { 207 // state stays kFirstColumn. 208 } else if (isspace(tc)) { 209 state = kFindName; 210 } else { 211 state = kCollectName; 212 name[ni++] = tc; 213 } 214 break; 215 216 case kScanComment : 217 if (tc == '\n') { 218 state = kFirstColumn; 219 } else { 220 // state stays kScanComment. 221 } 222 break; 223 224 case kFindName : 225 if (tc == '\n') { 226 state = kFirstColumn; 227 } else if (isspace(tc)) { 228 // state stays kFindName. 229 } else { 230 state = kCollectName; 231 name[ni++] = tc; 232 } 233 break; 234 235 case kCollectName : 236 if (tc == '\n') { 237 name[ni] = 0; 238 warnx("Name must be followed by white space - '%s'", name); 239 state = kFirstColumn; 240 } else if (isspace(tc)) { 241 state = kFindValue; 242 } else { 243 name[ni++] = tc; 244 // state staus kCollectName. 245 } 246 break; 247 248 case kFindValue : 249 case kContinueValue : 250 if (tc == '\n') { 251 state = kSetenv; 252 } else if (isspace(tc)) { 253 // state stays kFindValue or kContinueValue. 254 } else { 255 state = kCollectValue; 256 value[vi++] = tc; 257 } 258 break; 259 260 case kCollectValue : 261 if (tc == '\n') { 262 if (value[vi-1] == '\\') { 263 value[vi-1] = '\r'; 264 state = kContinueValue; 265 } else { 266 state = kSetenv; 267 } 268 } else { 269 // state stays kCollectValue. 270 value[vi++] = tc; 271 } 272 break; 273 } 274 275 if (state == kSetenv) { 276 name[ni] = 0; 277 value[vi] = 0; 278 if ((kret = SetOFVariable(name, value)) != KERN_SUCCESS) { 279 errx(1, "Error setting variable - '%s': %s", name, 280 mach_error_string(kret)); 281 } 282 state = kFirstColumn; 283 } 284 } 285 286 if (state != kFirstColumn) { 287 errx(1, "Last line ended abruptly"); 288 } 289} 290 291 292// ParseXMLFile(fileName) 293// 294// Open and parse the specified file in XML format, 295// and set variables appropriately. 296// 297static void ParseXMLFile(char *fileName) 298{ 299 CFPropertyListRef plist; 300 CFURLRef fileURL = NULL; 301 CFStringRef filePath = NULL; 302 CFStringRef errorString = NULL; 303 CFDataRef data = NULL; 304 SInt32 errorCode = 0; 305 306 filePath = CFStringCreateWithCString(kCFAllocatorDefault, fileName, kCFStringEncodingUTF8); 307 if (filePath == NULL) { 308 errx(1, "Could not create file path string"); 309 } 310 311 // Create a URL that specifies the file we will create to 312 // hold the XML data. 313 fileURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, 314 filePath, 315 kCFURLPOSIXPathStyle, 316 false /* not a directory */ ); 317 if (fileURL == NULL) { 318 errx(1, "Could not create file path URL"); 319 } 320 321 CFRelease(filePath); 322 323 if (! CFURLCreateDataAndPropertiesFromResource( 324 kCFAllocatorDefault, 325 fileURL, 326 &data, 327 NULL, 328 NULL, 329 &errorCode) || data == NULL ) { 330 errx(1, "Error reading XML file (%d)", (int)errorCode); 331 } 332 333 CFRelease(fileURL); 334 335 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, 336 data, 337 kCFPropertyListImmutable, 338 &errorString); 339 340 CFRelease(data); 341 342 if (plist == NULL) { 343 errx(1, "Error parsing XML file"); 344 } 345 346 if (errorString != NULL) { 347 errx(1, "Error parsing XML file: %s", CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8)); 348 } 349 350 CFDictionaryApplyFunction(plist, &SetOFVariableFromFile, 0); 351 352 CFRelease(plist); 353} 354 355// SetOrGetOFVariable(str) 356// 357// Parse the input string, then set or get the specified 358// firmware variable. 359// 360static void SetOrGetOFVariable(char *str) 361{ 362 long set = 0; 363 char *name; 364 char *value; 365 CFStringRef nameRef; 366 CFTypeRef valueRef; 367 kern_return_t result; 368 369 // OF variable name is first. 370 name = str; 371 372 // Find the equal sign for set 373 while (*str) { 374 if (*str == '=') { 375 set = 1; 376 *str++ = '\0'; 377 break; 378 } 379 str++; 380 } 381 382 if (set == 1) { 383 // On sets, the OF variable's value follows the equal sign. 384 value = str; 385 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)); 391 } 392 } else { 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)); 397 } 398 399 PrintOFVariable(nameRef, valueRef, 0); 400 CFRelease(nameRef); 401 CFRelease(valueRef); 402 } 403} 404 405 406// GetOFVariable(name, nameRef, valueRef) 407// 408// Get the named firmware variable. 409// Return it and it's symbol in valueRef and nameRef. 410// 411static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef, 412 CFTypeRef *valueRef) 413{ 414 *nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, 415 kCFStringEncodingUTF8); 416 if (*nameRef == 0) { 417 errx(1, "Error creating CFString for key %s", name); 418 } 419 420 *valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, *nameRef, 0, 0); 421 if (*valueRef == 0) return kIOReturnNotFound; 422 423 return KERN_SUCCESS; 424} 425 426 427// SetOFVariable(name, value) 428// 429// Set or create an firmware variable with name and value. 430// 431static kern_return_t SetOFVariable(char *name, char *value) 432{ 433 CFStringRef nameRef; 434 CFTypeRef valueRef; 435 CFTypeID typeID; 436 kern_return_t result = KERN_SUCCESS; 437 438 nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, 439 kCFStringEncodingUTF8); 440 if (nameRef == 0) { 441 errx(1, "Error creating CFString for key %s", name); 442 } 443 444 valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0); 445 if (valueRef) { 446 typeID = CFGetTypeID(valueRef); 447 CFRelease(valueRef); 448 449 valueRef = ConvertValueToCFTypeRef(typeID, value); 450 if (valueRef == 0) { 451 errx(1, "Error creating CFTypeRef for value %s", value); 452 } result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); 453 } else { 454 while (1) { 455 // In the default case, try data, string, number, then boolean. 456 457 valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value); 458 if (valueRef != 0) { 459 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); 460 if (result == KERN_SUCCESS) break; 461 } 462 463 valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value); 464 if (valueRef != 0) { 465 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); 466 if (result == KERN_SUCCESS) break; 467 } 468 469 valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value); 470 if (valueRef != 0) { 471 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); 472 if (result == KERN_SUCCESS) break; 473 } 474 475 valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value); 476 if (valueRef != 0) { 477 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); 478 if (result == KERN_SUCCESS) break; 479 } 480 481 break; 482 } 483 } 484 485 CFRelease(nameRef); 486 487 return result; 488} 489 490 491// DeleteOFVariable(name) 492// 493// Delete the named firmware variable. 494// 495// 496static void DeleteOFVariable(char *name) 497{ 498 SetOFVariable(kIONVRAMDeletePropertyKey, name); 499} 500 501static void NVRamSyncNow(char *name) 502{ 503 SetOFVariable(kIONVRAMSyncNowPropertyKey, name); 504} 505 506// PrintOFVariables() 507// 508// Print all of the firmware variables. 509// 510static void PrintOFVariables() 511{ 512 kern_return_t result; 513 CFMutableDictionaryRef dict; 514 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)); 518 } 519 520 if (gUseXML) { 521 CFDataRef data; 522 523 data = CFPropertyListCreateXMLData( kCFAllocatorDefault, dict ); 524 if (data == NULL) { 525 errx(1, "Error converting variables to xml"); 526 } 527 528 fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout); 529 530 CFRelease(data); 531 532 } else { 533 534 CFDictionaryApplyFunction(dict, &PrintOFVariable, 0); 535 536 } 537 538 CFRelease(dict); 539} 540 541// PrintOFVariable(key, value, context) 542// 543// Print the given firmware variable. 544// 545static void PrintOFVariable(const void *key, const void *value, void *context) 546{ 547 long cnt, cnt2; 548 CFIndex nameLen; 549 char *nameBuffer = 0; 550 const char *nameString; 551 char numberBuffer[10]; 552 const uint8_t *dataPtr; 553 uint8_t dataChar; 554 char *dataBuffer = 0; 555 CFIndex valueLen; 556 char *valueBuffer = 0; 557 const char *valueString = 0; 558 uint32_t number, length; 559 CFTypeID typeID; 560 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; 566 else { 567 warnx("Unable to convert property name to C string"); 568 nameString = "<UNPRINTABLE>"; 569 } 570 571 // Get the OF variable's type. 572 typeID = CFGetTypeID(value); 573 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; 588 else { 589 warnx("Unable to convert value to C string"); 590 valueString = "<UNPRINTABLE>"; 591 } 592 } else if (typeID == CFDataGetTypeID()) { 593 length = CFDataGetLength(value); 594 if (length == 0) valueString = ""; 595 else { 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) && dataChar != '%') { 602 dataBuffer[cnt2++] = dataChar; 603 } else { 604 sprintf(dataBuffer + cnt2, "%%%02x", dataChar); 605 cnt2 += 3; 606 } 607 } 608 dataBuffer[cnt2] = '\0'; 609 valueString = dataBuffer; 610 } 611 } 612 } else { 613 valueString="<INVALID>"; 614 } 615 616 if ((nameString != 0) && (valueString != 0)) 617 printf("%s\t%s\n", nameString, valueString); 618 619 if (dataBuffer != 0) free(dataBuffer); 620 if (nameBuffer != 0) free(nameBuffer); 621 if (valueBuffer != 0) free(valueBuffer); 622} 623 624// ClearOFVariables() 625// 626// Deletes all OF variables 627// 628static void ClearOFVariables(void) 629{ 630 kern_return_t result; 631 CFMutableDictionaryRef dict; 632 633 result = IORegistryEntryCreateCFProperties(gOptionsRef, &dict, 0, 0); 634 if (result != KERN_SUCCESS) { 635 errx(1, "Error getting the firmware variables: %s", mach_error_string(result)); 636 } 637 CFDictionaryApplyFunction(dict, &ClearOFVariable, 0); 638 639 CFRelease(dict); 640} 641 642static void ClearOFVariable(const void *key, const void *value, void *context) 643{ 644 kern_return_t result; 645 result = IORegistryEntrySetCFProperty(gOptionsRef, 646 CFSTR(kIONVRAMDeletePropertyKey), key); 647 if (result != KERN_SUCCESS) { 648 errx(1, "Error clearing firmware variables: %s", mach_error_string(result)); 649 } 650} 651 652// ConvertValueToCFTypeRef(typeID, value) 653// 654// Convert the value into a CFType given the typeID. 655// 656static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value) 657{ 658 CFTypeRef valueRef = 0; 659 long cnt, cnt2, length; 660 unsigned long number, tmp; 661 662 if (typeID == CFBooleanGetTypeID()) { 663 if (!strcmp("true", value)) valueRef = kCFBooleanTrue; 664 else if (!strcmp("false", value)) valueRef = kCFBooleanFalse; 665 } else if (typeID == CFNumberGetTypeID()) { 666 number = strtol(value, 0, 0); 667 valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, 668 &number); 669 } else if (typeID == CFStringGetTypeID()) { 670 valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, 671 kCFStringEncodingUTF8); 672 } else if (typeID == CFDataGetTypeID()) { 673 length = strlen(value); 674 for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) { 675 if (value[cnt] == '%') { 676 if (!ishexnumber(value[cnt + 1]) || 677 !ishexnumber(value[cnt + 2])) return 0; 678 number = toupper(value[++cnt]) - '0'; 679 if (number > 9) number -= 7; 680 tmp = toupper(value[++cnt]) - '0'; 681 if (tmp > 9) tmp -= 7; 682 number = (number << 4) + tmp; 683 value[cnt2] = number; 684 } else value[cnt2] = value[cnt]; 685 } 686 valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value, 687 cnt2, kCFAllocatorDefault); 688 } else return 0; 689 690 return valueRef; 691} 692 693static void SetOFVariableFromFile(const void *key, const void *value, void *context) 694{ 695 kern_return_t result; 696 697 result = IORegistryEntrySetCFProperty(gOptionsRef, key, value); 698 if ( result != KERN_SUCCESS ) { 699 int nameLen; 700 char *nameBuffer; 701 char *nameString; 702 703 // Get the variable's name. 704 nameLen = CFStringGetLength(key) + 1; 705 nameBuffer = malloc(nameLen); 706 if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) ) 707 nameString = nameBuffer; 708 else { 709 warnx("Unable to convert property name to C string"); 710 nameString = "<UNPRINTABLE>"; 711 } 712 errx(1, "Error setting variable - '%s': %s", nameString, 713 mach_error_string(result)); 714 } 715} 716