]>
Commit | Line | Data |
---|---|---|
1815bff5 | 1 | /* |
cf37c299 | 2 | * Copyright (c) 2000-2016 Apple Inc. All rights reserved. |
1815bff5 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
2fc1e207 A |
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 | |
1815bff5 A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2fc1e207 A |
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 | * | |
1815bff5 A |
20 | * @APPLE_LICENSE_HEADER_END@ |
21 | */ | |
22 | /* | |
83f6dbe8 | 23 | cc -o nvram nvram.c -framework CoreFoundation -framework IOKit -Wall |
1815bff5 A |
24 | */ |
25 | ||
26 | #include <stdio.h> | |
27 | #include <IOKit/IOKitLib.h> | |
83f6dbe8 | 28 | #include <IOKit/IOKitKeys.h> |
9726c137 | 29 | #include <IOKit/IOKitKeysPrivate.h> |
1815bff5 | 30 | #include <CoreFoundation/CoreFoundation.h> |
ef8ad44b A |
31 | #include <err.h> |
32 | #include <mach/mach_error.h> | |
cf37c299 | 33 | #include <sys/stat.h> |
1815bff5 A |
34 | |
35 | // Prototypes | |
00bf83c0 A |
36 | static void UsageMessage(const char *message); |
37 | static void ParseFile(const char *fileName); | |
38 | static void ParseXMLFile(const char *fileName); | |
1815bff5 | 39 | static void SetOrGetOFVariable(char *str); |
00bf83c0 A |
40 | static kern_return_t GetOFVariable(const char *name, CFStringRef *nameRef, |
41 | CFTypeRef *valueRef); | |
42 | static kern_return_t SetOFVariable(const char *name, const char *value); | |
43 | static void DeleteOFVariable(const char *name); | |
1815bff5 A |
44 | static void PrintOFVariables(void); |
45 | static void PrintOFVariable(const void *key,const void *value,void *context); | |
34d340d7 A |
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); | |
00bf83c0 | 49 | static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, const char *value); |
1815bff5 | 50 | |
00bf83c0 | 51 | static void NVRamSyncNow(void); |
8459d725 | 52 | |
1815bff5 | 53 | // Global Variables |
1815bff5 | 54 | static io_registry_entry_t gOptionsRef; |
00bf83c0 A |
55 | static io_registry_entry_t gSystemOptionsRef; |
56 | static io_registry_entry_t gSelectedOptionsRef; | |
34d340d7 | 57 | static bool gUseXML; |
9726c137 | 58 | static bool gUseForceSync; |
1815bff5 | 59 | |
c00fdd17 A |
60 | #if TARGET_OS_BRIDGE /* Stuff for nvram bridge -> intel */ |
61 | #include <dlfcn.h> | |
62 | #include <libMacEFIManager/MacEFIHostInterfaceAPI.h> | |
63 | ||
64 | static kern_return_t LinkMacNVRAMSymbols(void); | |
65 | static kern_return_t GetMacOFVariable(char *name, char **value); | |
66 | static kern_return_t SetMacOFVariable(char *name, char *value); | |
919ea02d | 67 | static kern_return_t DeleteMacOFVariable(char *name); |
c00fdd17 A |
68 | |
69 | static bool gBridgeToIntel; | |
70 | static void *gDL_handle; | |
71 | static void *gNvramInterface; | |
72 | ||
73 | static void (*hostInterfaceInitialize_fptr)(void); | |
74 | static void *(*createNvramHostInterface_fptr)(const char *handle); | |
75 | static kern_return_t (*destroyNvramHostInterface_fptr)(void *interface); | |
76 | static kern_return_t (*getNVRAMVariable_fptr)(void *interface, char *name, char **buffer, uint32_t *size); | |
919ea02d A |
77 | static kern_return_t (*setNVRAMVariable_fptr)(void *interface, char *name, char *buffer); |
78 | static kern_return_t (*deleteNVRAMVariable_fptr)(void *interface, char *name); | |
c00fdd17 A |
79 | static void (*hostInterfaceDeinitialize_fptr)(void); /* may not need? */ |
80 | ||
81 | #endif /* TARGET_OS_BRIDGE */ | |
1815bff5 A |
82 | |
83 | int main(int argc, char **argv) | |
84 | { | |
85 | long cnt; | |
86 | char *str, errorMessage[256]; | |
87 | kern_return_t result; | |
88 | mach_port_t masterPort; | |
00bf83c0 | 89 | int argcount = 0; |
cf37c299 | 90 | |
1815bff5 A |
91 | result = IOMasterPort(bootstrap_port, &masterPort); |
92 | if (result != KERN_SUCCESS) { | |
ef8ad44b A |
93 | errx(1, "Error getting the IOMaster port: %s", |
94 | mach_error_string(result)); | |
1815bff5 | 95 | } |
cf37c299 | 96 | |
1815bff5 A |
97 | gOptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options"); |
98 | if (gOptionsRef == 0) { | |
ef8ad44b | 99 | errx(1, "nvram is not supported on this system"); |
1815bff5 | 100 | } |
cf37c299 | 101 | |
00bf83c0 A |
102 | gSystemOptionsRef = IORegistryEntryFromPath(masterPort, "IOService:/options/options-system"); |
103 | ||
104 | gSelectedOptionsRef = gOptionsRef; | |
105 | ||
1815bff5 A |
106 | for (cnt = 1; cnt < argc; cnt++) { |
107 | str = argv[cnt]; | |
108 | if (str[0] == '-' && str[1] != 0) { | |
109 | // Parse the options. | |
110 | for (str += 1 ; *str; str++) { | |
00bf83c0 A |
111 | switch (*str) { |
112 | case 'p' : | |
c00fdd17 | 113 | #if TARGET_OS_BRIDGE |
00bf83c0 A |
114 | if (gBridgeToIntel) { |
115 | fprintf(stderr, "-p not supported for Mac NVRAM store.\n"); | |
116 | return 1; | |
117 | } | |
c00fdd17 | 118 | #endif |
00bf83c0 A |
119 | PrintOFVariables(); |
120 | break; | |
34d340d7 | 121 | |
00bf83c0 A |
122 | case 'x' : |
123 | gUseXML = true; | |
124 | break; | |
34d340d7 | 125 | |
00bf83c0 | 126 | case 'f': |
c00fdd17 | 127 | #if TARGET_OS_BRIDGE |
00bf83c0 A |
128 | if (gBridgeToIntel) { |
129 | fprintf(stderr, "-f not supported for Mac NVRAM store.\n"); | |
130 | return 1; | |
131 | } | |
c00fdd17 | 132 | #endif |
00bf83c0 A |
133 | cnt++; |
134 | if (cnt < argc && *argv[cnt] != '-') { | |
135 | ParseFile(argv[cnt]); | |
136 | } else { | |
137 | UsageMessage("missing filename"); | |
138 | } | |
139 | break; | |
140 | ||
141 | case 'd': | |
142 | cnt++; | |
143 | if (cnt < argc && *argv[cnt] != '-') { | |
c00fdd17 | 144 | #if TARGET_OS_BRIDGE |
00bf83c0 A |
145 | if (gBridgeToIntel) { |
146 | if ((result = DeleteMacOFVariable(argv[cnt])) != KERN_SUCCESS) { | |
919ea02d A |
147 | errx(1, "Error deleting variable - '%s': %s (0x%08x)", argv[cnt], |
148 | mach_error_string(result), result); | |
00bf83c0 | 149 | } |
919ea02d | 150 | } |
00bf83c0 | 151 | else |
c00fdd17 | 152 | #endif |
00bf83c0 A |
153 | { |
154 | DeleteOFVariable(argv[cnt]); | |
155 | } | |
156 | } else { | |
157 | UsageMessage("missing name"); | |
158 | } | |
159 | break; | |
cf37c299 | 160 | |
00bf83c0 | 161 | case 'c': |
c00fdd17 | 162 | #if TARGET_OS_BRIDGE |
00bf83c0 A |
163 | if (gBridgeToIntel) { |
164 | fprintf(stderr, "-c not supported for Mac NVRAM store.\n"); | |
165 | return 1; | |
166 | } | |
c00fdd17 | 167 | #endif |
00bf83c0 A |
168 | ClearOFVariables(); |
169 | break; | |
170 | case 's': | |
171 | // -s option is unadvertised -- advises the kernel more forcibly to | |
172 | // commit the variable to nonvolatile storage | |
173 | gUseForceSync = true; | |
174 | break; | |
c00fdd17 | 175 | #if TARGET_OS_BRIDGE |
00bf83c0 A |
176 | case 'm': |
177 | // used to set nvram variables on the Intel side | |
178 | // from the ARM side (Bridge -> Mac) | |
179 | fprintf(stdout, "Using Mac NVRAM store.\n"); | |
180 | ||
181 | LinkMacNVRAMSymbols(); | |
182 | gBridgeToIntel = true; | |
183 | break; | |
c00fdd17 A |
184 | #endif |
185 | ||
00bf83c0 A |
186 | case 'z': |
187 | // -z option is unadvertised -- attempts to use the options-system node | |
188 | // to write to the system NVRAM region if available | |
189 | if (gSystemOptionsRef) { | |
190 | fprintf(stderr, "Selecting options-system node.\n"); | |
191 | gSelectedOptionsRef = gSystemOptionsRef; | |
192 | } else { | |
193 | fprintf(stderr, "No options-system node, using options.\n"); | |
194 | } | |
195 | break; | |
196 | ||
197 | default: | |
198 | strcpy(errorMessage, "no such option as --"); | |
199 | errorMessage[strlen(errorMessage)-1] = *str; | |
200 | UsageMessage(errorMessage); | |
201 | } | |
202 | } | |
203 | } else { | |
204 | // Other arguments will be firmware variable requests. | |
205 | argcount++; | |
206 | SetOrGetOFVariable(str); | |
1815bff5 | 207 | } |
1815bff5 | 208 | } |
cf37c299 A |
209 | |
210 | // radar:25206371 | |
211 | if (argcount == 0 && gUseForceSync == true) { | |
00bf83c0 | 212 | NVRamSyncNow(); |
cf37c299 A |
213 | } |
214 | ||
1815bff5 | 215 | IOObjectRelease(gOptionsRef); |
cf37c299 | 216 | |
00bf83c0 A |
217 | if (gSystemOptionsRef) { |
218 | IOObjectRelease(gSystemOptionsRef); | |
219 | } | |
220 | ||
1815bff5 A |
221 | return 0; |
222 | } | |
223 | ||
1815bff5 A |
224 | // UsageMessage(message) |
225 | // | |
226 | // Print the usage information and exit. | |
227 | // | |
00bf83c0 | 228 | static void UsageMessage(const char *message) |
1815bff5 | 229 | { |
ef8ad44b | 230 | warnx("(usage: %s)", message); |
cf37c299 | 231 | |
00bf83c0 | 232 | printf("nvram [-x] [-p] [-f filename] [-d name] [-c] name[=value] ...\n"); |
34d340d7 A |
233 | printf("\t-x use XML format for printing or reading variables\n"); |
234 | printf("\t (must appear before -p or -f)\n"); | |
4c00c0ae A |
235 | printf("\t-p print all firmware variables\n"); |
236 | printf("\t-f set firmware variables from a text file\n"); | |
83f6dbe8 | 237 | printf("\t-d delete the named variable\n"); |
34d340d7 | 238 | printf("\t-c delete all variables\n"); |
d52496fd A |
239 | #if TARGET_OS_BRIDGE |
240 | printf("\t-m set nvram variables on macOS from bridgeOS\n"); | |
241 | #endif | |
1815bff5 A |
242 | printf("\tname=value set named variable\n"); |
243 | printf("\tname print variable\n"); | |
244 | printf("Note that arguments and options are executed in order.\n"); | |
cf37c299 | 245 | |
1815bff5 A |
246 | exit(1); |
247 | } | |
248 | ||
249 | ||
250 | // States for ParseFile. | |
251 | enum { | |
252 | kFirstColumn = 0, | |
253 | kScanComment, | |
254 | kFindName, | |
255 | kCollectName, | |
256 | kFindValue, | |
257 | kCollectValue, | |
258 | kContinueValue, | |
259 | kSetenv, | |
cf37c299 | 260 | |
1815bff5 A |
261 | kMaxStringSize = 0x800, |
262 | kMaxNameSize = 0x100 | |
263 | }; | |
264 | ||
265 | ||
266 | // ParseFile(fileName) | |
267 | // | |
268 | // Open and parse the specified file. | |
269 | // | |
00bf83c0 | 270 | static void ParseFile(const char *fileName) |
1815bff5 | 271 | { |
cf37c299 A |
272 | long state, ni = 0, vi = 0; |
273 | int tc; | |
1815bff5 A |
274 | char name[kMaxNameSize]; |
275 | char value[kMaxStringSize]; | |
276 | FILE *patches; | |
ef8ad44b | 277 | kern_return_t kret; |
34d340d7 A |
278 | |
279 | if (gUseXML) { | |
280 | ParseXMLFile(fileName); | |
281 | return; | |
282 | } | |
cf37c299 | 283 | |
1815bff5 A |
284 | patches = fopen(fileName, "r"); |
285 | if (patches == 0) { | |
ef8ad44b | 286 | err(1, "Couldn't open patch file - '%s'", fileName); |
1815bff5 | 287 | } |
cf37c299 | 288 | |
1815bff5 A |
289 | state = kFirstColumn; |
290 | while ((tc = getc(patches)) != EOF) { | |
cf37c299 | 291 | if(ni==(kMaxNameSize-1)) |
ef8ad44b | 292 | errx(1, "Name exceeded max length of %d", kMaxNameSize); |
34d340d7 | 293 | if(vi==(kMaxStringSize-1)) |
ef8ad44b | 294 | errx(1, "Value exceeded max length of %d", kMaxStringSize); |
1815bff5 A |
295 | switch (state) { |
296 | case kFirstColumn : | |
297 | ni = 0; | |
298 | vi = 0; | |
299 | if (tc == '#') { | |
00bf83c0 | 300 | state = kScanComment; |
1815bff5 | 301 | } else if (tc == '\n') { |
00bf83c0 | 302 | // state stays kFirstColumn. |
1815bff5 | 303 | } else if (isspace(tc)) { |
00bf83c0 | 304 | state = kFindName; |
1815bff5 | 305 | } else { |
00bf83c0 A |
306 | state = kCollectName; |
307 | name[ni++] = tc; | |
1815bff5 A |
308 | } |
309 | break; | |
cf37c299 | 310 | |
1815bff5 A |
311 | case kScanComment : |
312 | if (tc == '\n') { | |
00bf83c0 | 313 | state = kFirstColumn; |
1815bff5 | 314 | } else { |
00bf83c0 | 315 | // state stays kScanComment. |
1815bff5 A |
316 | } |
317 | break; | |
cf37c299 | 318 | |
1815bff5 A |
319 | case kFindName : |
320 | if (tc == '\n') { | |
00bf83c0 | 321 | state = kFirstColumn; |
1815bff5 | 322 | } else if (isspace(tc)) { |
00bf83c0 | 323 | // state stays kFindName. |
1815bff5 | 324 | } else { |
00bf83c0 A |
325 | state = kCollectName; |
326 | name[ni++] = tc; | |
1815bff5 A |
327 | } |
328 | break; | |
cf37c299 | 329 | |
1815bff5 A |
330 | case kCollectName : |
331 | if (tc == '\n') { | |
00bf83c0 A |
332 | name[ni] = 0; |
333 | warnx("Name must be followed by white space - '%s'", name); | |
334 | state = kFirstColumn; | |
1815bff5 | 335 | } else if (isspace(tc)) { |
00bf83c0 | 336 | state = kFindValue; |
1815bff5 | 337 | } else { |
00bf83c0 A |
338 | name[ni++] = tc; |
339 | // state staus kCollectName. | |
1815bff5 A |
340 | } |
341 | break; | |
cf37c299 | 342 | |
1815bff5 A |
343 | case kFindValue : |
344 | case kContinueValue : | |
345 | if (tc == '\n') { | |
00bf83c0 | 346 | state = kSetenv; |
1815bff5 | 347 | } else if (isspace(tc)) { |
00bf83c0 | 348 | // state stays kFindValue or kContinueValue. |
1815bff5 | 349 | } else { |
00bf83c0 A |
350 | state = kCollectValue; |
351 | value[vi++] = tc; | |
1815bff5 A |
352 | } |
353 | break; | |
cf37c299 | 354 | |
1815bff5 A |
355 | case kCollectValue : |
356 | if (tc == '\n') { | |
00bf83c0 A |
357 | if (value[vi-1] == '\\') { |
358 | value[vi-1] = '\r'; | |
359 | state = kContinueValue; | |
360 | } else { | |
361 | state = kSetenv; | |
362 | } | |
1815bff5 | 363 | } else { |
00bf83c0 A |
364 | // state stays kCollectValue. |
365 | value[vi++] = tc; | |
1815bff5 A |
366 | } |
367 | break; | |
368 | } | |
cf37c299 | 369 | |
1815bff5 A |
370 | if (state == kSetenv) { |
371 | name[ni] = 0; | |
372 | value[vi] = 0; | |
ef8ad44b A |
373 | if ((kret = SetOFVariable(name, value)) != KERN_SUCCESS) { |
374 | errx(1, "Error setting variable - '%s': %s", name, | |
375 | mach_error_string(kret)); | |
1815bff5 A |
376 | } |
377 | state = kFirstColumn; | |
378 | } | |
379 | } | |
cf37c299 | 380 | |
1815bff5 | 381 | if (state != kFirstColumn) { |
ef8ad44b | 382 | errx(1, "Last line ended abruptly"); |
1815bff5 A |
383 | } |
384 | } | |
385 | ||
34d340d7 A |
386 | // ParseXMLFile(fileName) |
387 | // | |
388 | // Open and parse the specified file in XML format, | |
389 | // and set variables appropriately. | |
390 | // | |
00bf83c0 | 391 | static void ParseXMLFile(const char *fileName) |
34d340d7 | 392 | { |
00bf83c0 A |
393 | CFPropertyListRef plist; |
394 | int fd; | |
395 | struct stat sb; | |
396 | char *buffer; | |
397 | CFReadStreamRef stream; | |
398 | CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; | |
399 | ||
400 | fd = open(fileName, O_RDONLY | O_NOFOLLOW, S_IFREG); | |
401 | if (fd == -1) { | |
402 | errx(1, "Could not open %s: %s", fileName, strerror(errno)); | |
403 | } | |
34d340d7 | 404 | |
00bf83c0 A |
405 | if (fstat(fd, &sb) == -1) { |
406 | errx(1, "Could not fstat %s: %s", fileName, strerror(errno)); | |
407 | } | |
34d340d7 | 408 | |
00bf83c0 A |
409 | if (sb.st_size > UINT32_MAX) { |
410 | errx(1, "too big for our purposes"); | |
411 | } | |
34d340d7 | 412 | |
00bf83c0 A |
413 | buffer = malloc((size_t)sb.st_size); |
414 | if (buffer == NULL) { | |
415 | errx(1, "Could not allocate buffer"); | |
416 | } | |
34d340d7 | 417 | |
00bf83c0 A |
418 | if (read(fd, buffer, (size_t)sb.st_size) != sb.st_size) { |
419 | errx(1, "Could not read %s: %s", fileName, strerror(errno)); | |
420 | } | |
34d340d7 | 421 | |
00bf83c0 | 422 | close(fd); |
34d340d7 | 423 | |
00bf83c0 A |
424 | stream = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, |
425 | (const UInt8 *)buffer, | |
426 | (CFIndex)sb.st_size, | |
427 | kCFAllocatorNull); | |
428 | if (stream == NULL) { | |
429 | errx(1, "Could not create stream from serialized data"); | |
430 | } | |
cf37c299 | 431 | |
00bf83c0 A |
432 | if (!CFReadStreamOpen(stream)) { |
433 | errx(1, "Could not open the stream"); | |
434 | } | |
cf37c299 | 435 | |
00bf83c0 A |
436 | plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, |
437 | stream, | |
438 | (CFIndex)sb.st_size, | |
439 | kCFPropertyListImmutable, | |
440 | &format, | |
441 | NULL); | |
34d340d7 | 442 | |
00bf83c0 A |
443 | if (plist == NULL) { |
444 | errx(1, "Error parsing XML file"); | |
445 | } | |
34d340d7 | 446 | |
00bf83c0 | 447 | CFReadStreamClose(stream); |
cf37c299 | 448 | |
00bf83c0 | 449 | CFRelease(stream); |
cf37c299 | 450 | |
00bf83c0 | 451 | free(buffer); |
34d340d7 | 452 | |
00bf83c0 | 453 | CFDictionaryApplyFunction(plist, &SetOFVariableFromFile, 0); |
34d340d7 | 454 | |
00bf83c0 | 455 | CFRelease(plist); |
34d340d7 A |
456 | } |
457 | ||
1815bff5 A |
458 | // SetOrGetOFVariable(str) |
459 | // | |
c0bbac3a A |
460 | // Parse the input string, then set, append or get |
461 | // the specified firmware variable. | |
1815bff5 A |
462 | // |
463 | static void SetOrGetOFVariable(char *str) | |
464 | { | |
00bf83c0 A |
465 | long set = 0; |
466 | long append = 0; | |
467 | const char *name; | |
468 | char *value; | |
c0bbac3a A |
469 | CFStringRef nameRef = NULL; |
470 | CFTypeRef valueRef = NULL; | |
471 | CFMutableStringRef appended = NULL; | |
00bf83c0 | 472 | kern_return_t result; |
cf37c299 | 473 | |
1815bff5 A |
474 | // OF variable name is first. |
475 | name = str; | |
cf37c299 | 476 | |
c0bbac3a | 477 | // Find the equal sign for set or += for append |
1815bff5 | 478 | while (*str) { |
c0bbac3a A |
479 | if (*str == '+' && *(str+1) == '=') { |
480 | append = 1; | |
481 | *str++ = '\0'; | |
482 | *str++ = '\0'; | |
483 | break; | |
484 | } | |
485 | ||
1815bff5 A |
486 | if (*str == '=') { |
487 | set = 1; | |
488 | *str++ = '\0'; | |
489 | break; | |
490 | } | |
491 | str++; | |
492 | } | |
cf37c299 | 493 | |
c0bbac3a A |
494 | // Read the current value if appending or if no =/+= |
495 | if (append == 1 || (set == 0 && append == 0)) { | |
c00fdd17 A |
496 | #if TARGET_OS_BRIDGE |
497 | if (gBridgeToIntel) { | |
498 | result = GetMacOFVariable(name, &value); | |
499 | if (result != KERN_SUCCESS) { | |
500 | errx(1, "Error getting variable - '%s': %s", name, | |
501 | mach_error_string(result)); | |
502 | } | |
503 | nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8); | |
504 | valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, kCFStringEncodingUTF8); | |
919ea02d | 505 | free(value); |
c00fdd17 A |
506 | } |
507 | else | |
508 | #endif | |
509 | { | |
510 | result = GetOFVariable(name, &nameRef, &valueRef); | |
511 | if (result != KERN_SUCCESS) { | |
512 | errx(1, "Error getting variable - '%s': %s", name, | |
513 | mach_error_string(result)); | |
514 | } | |
1815bff5 | 515 | } |
c0bbac3a | 516 | } |
cf37c299 | 517 | |
c0bbac3a A |
518 | if (set == 1) { |
519 | // On sets, the OF variable's value follows the equal sign. | |
520 | value = str; | |
521 | } | |
522 | ||
523 | if (append == 1) { | |
524 | // On append, the value to append follows the += substring | |
525 | appended = CFStringCreateMutableCopy(NULL, 0, valueRef); | |
526 | CFStringAppendCString(appended, str, kCFStringEncodingUTF8); | |
527 | value = (char*)CFStringGetCStringPtr(appended, kCFStringEncodingUTF8); | |
528 | } | |
529 | ||
530 | if (set == 1 || append == 1) { | |
531 | #if TARGET_OS_BRIDGE | |
532 | if (gBridgeToIntel) { | |
533 | result = SetMacOFVariable(name, value); | |
534 | } | |
535 | else | |
536 | #endif | |
537 | { | |
538 | result = SetOFVariable(name, value); | |
00bf83c0 A |
539 | /* Try syncing the new data to device, best effort! */ |
540 | NVRamSyncNow(); | |
c0bbac3a A |
541 | } |
542 | if (result != KERN_SUCCESS) { | |
543 | errx(1, "Error setting variable - '%s': %s", name, | |
544 | mach_error_string(result)); | |
545 | } | |
546 | } else { | |
1815bff5 | 547 | PrintOFVariable(nameRef, valueRef, 0); |
1815bff5 | 548 | } |
00bf83c0 A |
549 | if (nameRef) CFRelease(nameRef); |
550 | if (valueRef) CFRelease(valueRef); | |
551 | if (appended) CFRelease(appended); | |
1815bff5 A |
552 | } |
553 | ||
c00fdd17 A |
554 | #if TARGET_OS_BRIDGE |
555 | static kern_return_t LinkMacNVRAMSymbols() | |
556 | { | |
557 | gDL_handle = dlopen("libMacEFIHostInterface.dylib", RTLD_LAZY); | |
558 | if (gDL_handle == NULL) { | |
559 | errx(errno, "Failed to dlopen libMacEFIHostInterface.dylib"); | |
560 | return KERN_FAILURE; /* NOTREACHED */ | |
561 | } | |
562 | ||
563 | hostInterfaceInitialize_fptr = dlsym(gDL_handle, "hostInterfaceInitialize"); | |
564 | if (hostInterfaceInitialize_fptr == NULL) { | |
565 | errx(errno, "failed to link hostInterfaceInitialize"); | |
566 | } | |
567 | createNvramHostInterface_fptr = dlsym(gDL_handle, "createNvramHostInterface"); | |
568 | if (createNvramHostInterface_fptr == NULL) { | |
569 | errx(errno, "failed to link createNvramHostInterface"); | |
570 | } | |
571 | destroyNvramHostInterface_fptr = dlsym(gDL_handle, "destroyNvramHostInterface"); | |
572 | if (destroyNvramHostInterface_fptr == NULL) { | |
573 | errx(errno, "failed to link destroyNvramHostInterface"); | |
574 | } | |
575 | getNVRAMVariable_fptr = dlsym(gDL_handle, "getNVRAMVariable"); | |
576 | if (getNVRAMVariable_fptr == NULL) { | |
577 | errx(errno, "failed to link getNVRAMVariable"); | |
578 | } | |
579 | setNVRAMVariable_fptr = dlsym(gDL_handle, "setNVRAMVariable"); | |
580 | if (setNVRAMVariable_fptr == NULL) { | |
581 | errx(errno, "failed to link setNVRAMVariable"); | |
582 | } | |
919ea02d A |
583 | deleteNVRAMVariable_fptr = dlsym(gDL_handle, "deleteNVRAMVariable"); |
584 | if (deleteNVRAMVariable_fptr == NULL) { | |
585 | errx(errno, "failed to link deleteNVRAMVariable"); | |
586 | } | |
c00fdd17 A |
587 | hostInterfaceDeinitialize_fptr = dlsym(gDL_handle, "hostInterfaceDeinitialize"); |
588 | if (hostInterfaceDeinitialize_fptr == NULL) { | |
589 | errx(errno, "failed to link hostInterfaceDeinitialize"); | |
590 | } | |
591 | ||
592 | /* also do the initialization */ | |
593 | hostInterfaceInitialize_fptr(); | |
594 | gNvramInterface = createNvramHostInterface_fptr(NULL); | |
595 | ||
596 | return KERN_SUCCESS; | |
597 | } | |
598 | #endif | |
1815bff5 A |
599 | |
600 | // GetOFVariable(name, nameRef, valueRef) | |
601 | // | |
4c00c0ae | 602 | // Get the named firmware variable. |
1815bff5 A |
603 | // Return it and it's symbol in valueRef and nameRef. |
604 | // | |
00bf83c0 A |
605 | static kern_return_t GetOFVariable(const char *name, CFStringRef *nameRef, |
606 | CFTypeRef *valueRef) | |
1815bff5 A |
607 | { |
608 | *nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, | |
00bf83c0 | 609 | kCFStringEncodingUTF8); |
1815bff5 | 610 | if (*nameRef == 0) { |
ef8ad44b | 611 | errx(1, "Error creating CFString for key %s", name); |
1815bff5 | 612 | } |
cf37c299 | 613 | |
00bf83c0 | 614 | *valueRef = IORegistryEntryCreateCFProperty(gSelectedOptionsRef, *nameRef, 0, 0); |
ef8ad44b | 615 | if (*valueRef == 0) return kIOReturnNotFound; |
cf37c299 | 616 | |
1815bff5 A |
617 | return KERN_SUCCESS; |
618 | } | |
619 | ||
c00fdd17 A |
620 | #if TARGET_OS_BRIDGE |
621 | // GetMacOFVariable(name, value) | |
622 | // | |
623 | // Get the named firmware variable from the Intel side. | |
624 | // Return the value in value | |
625 | // | |
626 | static kern_return_t GetMacOFVariable(char *name, char **value) | |
627 | { | |
628 | uint32_t value_size; | |
c00fdd17 | 629 | |
919ea02d | 630 | return getNVRAMVariable_fptr(gNvramInterface, name, value, &value_size); |
c00fdd17 A |
631 | } |
632 | #endif | |
1815bff5 A |
633 | |
634 | // SetOFVariable(name, value) | |
635 | // | |
4c00c0ae | 636 | // Set or create an firmware variable with name and value. |
1815bff5 | 637 | // |
00bf83c0 | 638 | static kern_return_t SetOFVariable(const char *name, const char *value) |
1815bff5 A |
639 | { |
640 | CFStringRef nameRef; | |
641 | CFTypeRef valueRef; | |
642 | CFTypeID typeID; | |
ef8ad44b | 643 | kern_return_t result = KERN_SUCCESS; |
cf37c299 | 644 | |
00bf83c0 A |
645 | nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, |
646 | kCFStringEncodingUTF8); | |
647 | if (nameRef == 0) { | |
648 | errx(1, "Error creating CFString for key %s", name); | |
649 | } | |
cf37c299 | 650 | |
00bf83c0 A |
651 | valueRef = IORegistryEntryCreateCFProperty(gSelectedOptionsRef, nameRef, 0, 0); |
652 | if (valueRef) { | |
653 | typeID = CFGetTypeID(valueRef); | |
654 | CFRelease(valueRef); | |
919ea02d | 655 | |
00bf83c0 A |
656 | valueRef = ConvertValueToCFTypeRef(typeID, value); |
657 | if (valueRef == 0) { | |
658 | errx(1, "Error creating CFTypeRef for value %s", value); | |
659 | } result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, nameRef, valueRef); | |
660 | } else { | |
661 | while (1) { | |
662 | // In the default case, try data, string, number, then boolean. | |
919ea02d | 663 | |
00bf83c0 A |
664 | valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value); |
665 | if (valueRef != 0) { | |
666 | result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, nameRef, valueRef); | |
667 | if (result == KERN_SUCCESS) break; | |
668 | } | |
919ea02d | 669 | |
00bf83c0 A |
670 | valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value); |
671 | if (valueRef != 0) { | |
672 | result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, nameRef, valueRef); | |
673 | if (result == KERN_SUCCESS) break; | |
674 | } | |
919ea02d | 675 | |
00bf83c0 A |
676 | valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value); |
677 | if (valueRef != 0) { | |
678 | result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, nameRef, valueRef); | |
679 | if (result == KERN_SUCCESS) break; | |
680 | } | |
681 | ||
682 | valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value); | |
683 | if (valueRef != 0) { | |
684 | result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, nameRef, valueRef); | |
685 | if (result == KERN_SUCCESS) break; | |
686 | } | |
687 | ||
688 | break; | |
689 | } | |
690 | } | |
cf37c299 | 691 | |
1815bff5 | 692 | CFRelease(nameRef); |
cf37c299 | 693 | |
1815bff5 A |
694 | return result; |
695 | } | |
696 | ||
c00fdd17 A |
697 | #if TARGET_OS_BRIDGE |
698 | static kern_return_t SetMacOFVariable(char *name, char *value) | |
699 | { | |
919ea02d | 700 | return setNVRAMVariable_fptr(gNvramInterface, name, value); |
c00fdd17 A |
701 | } |
702 | #endif | |
1815bff5 | 703 | |
83f6dbe8 A |
704 | // DeleteOFVariable(name) |
705 | // | |
4c00c0ae | 706 | // Delete the named firmware variable. |
cf37c299 | 707 | // |
00bf83c0 | 708 | static void DeleteOFVariable(const char *name) |
83f6dbe8 A |
709 | { |
710 | SetOFVariable(kIONVRAMDeletePropertyKey, name); | |
711 | } | |
712 | ||
c00fdd17 | 713 | #if TARGET_OS_BRIDGE |
919ea02d | 714 | static kern_return_t DeleteMacOFVariable(char *name) |
c00fdd17 | 715 | { |
00bf83c0 | 716 | return deleteNVRAMVariable_fptr(gNvramInterface, name); |
c00fdd17 A |
717 | } |
718 | #endif | |
719 | ||
00bf83c0 | 720 | static void NVRamSyncNow(void) |
8459d725 | 721 | { |
9726c137 | 722 | if (!gUseForceSync) { |
00bf83c0 | 723 | SetOFVariable(kIONVRAMSyncNowPropertyKey, kIONVRAMSyncNowPropertyKey); |
9726c137 | 724 | } else { |
00bf83c0 | 725 | SetOFVariable(kIONVRAMForceSyncNowPropertyKey, kIONVRAMForceSyncNowPropertyKey); |
9726c137 | 726 | } |
8459d725 | 727 | } |
83f6dbe8 | 728 | |
1815bff5 A |
729 | // PrintOFVariables() |
730 | // | |
4c00c0ae | 731 | // Print all of the firmware variables. |
1815bff5 | 732 | // |
cf37c299 | 733 | static void PrintOFVariables(void) |
1815bff5 A |
734 | { |
735 | kern_return_t result; | |
736 | CFMutableDictionaryRef dict; | |
cf37c299 | 737 | |
00bf83c0 | 738 | result = IORegistryEntryCreateCFProperties(gSelectedOptionsRef, &dict, 0, 0); |
1815bff5 | 739 | if (result != KERN_SUCCESS) { |
ef8ad44b | 740 | errx(1, "Error getting the firmware variables: %s", mach_error_string(result)); |
1815bff5 | 741 | } |
34d340d7 A |
742 | |
743 | if (gUseXML) { | |
744 | CFDataRef data; | |
745 | ||
cf37c299 | 746 | data = CFPropertyListCreateData( kCFAllocatorDefault, dict, kCFPropertyListXMLFormat_v1_0, 0, NULL ); |
34d340d7 | 747 | if (data == NULL) { |
ef8ad44b | 748 | errx(1, "Error converting variables to xml"); |
34d340d7 A |
749 | } |
750 | ||
751 | fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout); | |
752 | ||
753 | CFRelease(data); | |
754 | ||
755 | } else { | |
756 | ||
757 | CFDictionaryApplyFunction(dict, &PrintOFVariable, 0); | |
758 | ||
759 | } | |
cf37c299 | 760 | |
1815bff5 A |
761 | CFRelease(dict); |
762 | } | |
763 | ||
1815bff5 A |
764 | // PrintOFVariable(key, value, context) |
765 | // | |
4c00c0ae | 766 | // Print the given firmware variable. |
1815bff5 A |
767 | // |
768 | static void PrintOFVariable(const void *key, const void *value, void *context) | |
769 | { | |
770 | long cnt, cnt2; | |
2fc1e207 A |
771 | CFIndex nameLen; |
772 | char *nameBuffer = 0; | |
1815bff5 A |
773 | const char *nameString; |
774 | char numberBuffer[10]; | |
b51d5b5f A |
775 | const uint8_t *dataPtr; |
776 | uint8_t dataChar; | |
1815bff5 | 777 | char *dataBuffer = 0; |
2fc1e207 A |
778 | CFIndex valueLen; |
779 | char *valueBuffer = 0; | |
b51d5b5f | 780 | const char *valueString = 0; |
cf37c299 | 781 | uint32_t number; |
00bf83c0 | 782 | long length; |
1815bff5 | 783 | CFTypeID typeID; |
cf37c299 | 784 | |
887d5eed A |
785 | if (gUseXML) { |
786 | CFDataRef data; | |
787 | CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, | |
788 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
789 | if (dict == NULL) { | |
790 | errx(1, "Error creating dictionary for variable value"); | |
791 | } | |
792 | ||
793 | data = CFPropertyListCreateData( kCFAllocatorDefault, dict, kCFPropertyListXMLFormat_v1_0, 0, NULL ); | |
794 | if (data == NULL) { | |
795 | errx(1, "Error creating xml plist for variable"); | |
796 | } | |
797 | ||
798 | fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout); | |
799 | ||
800 | CFRelease(dict); | |
801 | CFRelease(data); | |
802 | return; | |
803 | } | |
804 | ||
1815bff5 | 805 | // Get the OF variable's name. |
d52496fd A |
806 | nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), |
807 | kCFStringEncodingUTF8) + 1; | |
2fc1e207 A |
808 | nameBuffer = malloc(nameLen); |
809 | if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) ) | |
810 | nameString = nameBuffer; | |
811 | else { | |
ef8ad44b | 812 | warnx("Unable to convert property name to C string"); |
2fc1e207 A |
813 | nameString = "<UNPRINTABLE>"; |
814 | } | |
cf37c299 | 815 | |
1815bff5 A |
816 | // Get the OF variable's type. |
817 | typeID = CFGetTypeID(value); | |
cf37c299 | 818 | |
2fc1e207 | 819 | if (typeID == CFBooleanGetTypeID()) { |
1815bff5 A |
820 | if (CFBooleanGetValue(value)) valueString = "true"; |
821 | else valueString = "false"; | |
822 | } else if (typeID == CFNumberGetTypeID()) { | |
823 | CFNumberGetValue(value, kCFNumberSInt32Type, &number); | |
824 | if (number == 0xFFFFFFFF) sprintf(numberBuffer, "-1"); | |
825 | else if (number < 1000) sprintf(numberBuffer, "%d", number); | |
826 | else sprintf(numberBuffer, "0x%x", number); | |
827 | valueString = numberBuffer; | |
828 | } else if (typeID == CFStringGetTypeID()) { | |
d52496fd A |
829 | valueLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), |
830 | kCFStringEncodingUTF8) + 1; | |
2fc1e207 A |
831 | valueBuffer = malloc(valueLen + 1); |
832 | if ( valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8) ) | |
833 | valueString = valueBuffer; | |
834 | else { | |
ef8ad44b | 835 | warnx("Unable to convert value to C string"); |
2fc1e207 A |
836 | valueString = "<UNPRINTABLE>"; |
837 | } | |
1815bff5 A |
838 | } else if (typeID == CFDataGetTypeID()) { |
839 | length = CFDataGetLength(value); | |
840 | if (length == 0) valueString = ""; | |
841 | else { | |
842 | dataBuffer = malloc(length * 3 + 1); | |
843 | if (dataBuffer != 0) { | |
00bf83c0 A |
844 | dataPtr = CFDataGetBytePtr(value); |
845 | for (cnt = cnt2 = 0; cnt < length; cnt++) { | |
846 | dataChar = dataPtr[cnt]; | |
847 | if (isprint(dataChar) && dataChar != '%') { | |
848 | dataBuffer[cnt2++] = dataChar; | |
849 | } else { | |
850 | sprintf(dataBuffer + cnt2, "%%%02x", dataChar); | |
851 | cnt2 += 3; | |
852 | } | |
853 | } | |
854 | dataBuffer[cnt2] = '\0'; | |
855 | valueString = dataBuffer; | |
1815bff5 A |
856 | } |
857 | } | |
2fc1e207 A |
858 | } else { |
859 | valueString="<INVALID>"; | |
860 | } | |
cf37c299 | 861 | |
1815bff5 A |
862 | if ((nameString != 0) && (valueString != 0)) |
863 | printf("%s\t%s\n", nameString, valueString); | |
cf37c299 | 864 | |
1815bff5 | 865 | if (dataBuffer != 0) free(dataBuffer); |
2fc1e207 A |
866 | if (nameBuffer != 0) free(nameBuffer); |
867 | if (valueBuffer != 0) free(valueBuffer); | |
1815bff5 A |
868 | } |
869 | ||
34d340d7 A |
870 | // ClearOFVariables() |
871 | // | |
872 | // Deletes all OF variables | |
873 | // | |
874 | static void ClearOFVariables(void) | |
875 | { | |
876 | kern_return_t result; | |
877 | CFMutableDictionaryRef dict; | |
878 | ||
00bf83c0 | 879 | result = IORegistryEntryCreateCFProperties(gSelectedOptionsRef, &dict, 0, 0); |
34d340d7 | 880 | if (result != KERN_SUCCESS) { |
ef8ad44b | 881 | errx(1, "Error getting the firmware variables: %s", mach_error_string(result)); |
34d340d7 A |
882 | } |
883 | CFDictionaryApplyFunction(dict, &ClearOFVariable, 0); | |
884 | ||
885 | CFRelease(dict); | |
886 | } | |
887 | ||
888 | static void ClearOFVariable(const void *key, const void *value, void *context) | |
889 | { | |
890 | kern_return_t result; | |
00bf83c0 | 891 | result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, |
34d340d7 A |
892 | CFSTR(kIONVRAMDeletePropertyKey), key); |
893 | if (result != KERN_SUCCESS) { | |
c0bbac3a A |
894 | assert(CFGetTypeID(key) == CFStringGetTypeID()); |
895 | const char *keyStr = CFStringGetCStringPtr(key, kCFStringEncodingUTF8); | |
896 | char *keyBuffer = NULL; | |
897 | size_t keyBufferLen = 0; | |
898 | if (!keyStr) { | |
899 | keyBufferLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), kCFStringEncodingUTF8) + 1; | |
900 | keyBuffer = (char *)malloc(keyBufferLen); | |
901 | if (keyBuffer != NULL && CFStringGetCString(key, keyBuffer, keyBufferLen, kCFStringEncodingUTF8)) { | |
902 | keyStr = keyBuffer; | |
903 | } else { | |
904 | warnx("Unable to convert property name to C string"); | |
905 | keyStr = "<UNPRINTABLE>"; | |
906 | } | |
907 | } | |
908 | ||
909 | warnx("Error clearing firmware variable %s: %s", keyStr, mach_error_string(result)); | |
910 | if (keyBuffer) { | |
911 | free(keyBuffer); | |
912 | } | |
34d340d7 A |
913 | } |
914 | } | |
1815bff5 A |
915 | |
916 | // ConvertValueToCFTypeRef(typeID, value) | |
917 | // | |
918 | // Convert the value into a CFType given the typeID. | |
919 | // | |
00bf83c0 | 920 | static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, const char *value) |
1815bff5 | 921 | { |
919ea02d A |
922 | CFTypeRef valueRef = 0; |
923 | long cnt, cnt2, length; | |
924 | unsigned long number, tmp; | |
925 | ||
926 | if (typeID == CFBooleanGetTypeID()) { | |
00bf83c0 A |
927 | if (!strcmp("true", value)) valueRef = kCFBooleanTrue; |
928 | else if (!strcmp("false", value)) valueRef = kCFBooleanFalse; | |
919ea02d | 929 | } else if (typeID == CFNumberGetTypeID()) { |
00bf83c0 A |
930 | number = strtol(value, 0, 0); |
931 | valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, | |
932 | &number); | |
919ea02d | 933 | } else if (typeID == CFStringGetTypeID()) { |
00bf83c0 A |
934 | valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, |
935 | kCFStringEncodingUTF8); | |
919ea02d | 936 | } else if (typeID == CFDataGetTypeID()) { |
00bf83c0 A |
937 | length = strlen(value); |
938 | char valueCopy[length + 1]; | |
939 | ||
940 | for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) { | |
941 | if (value[cnt] == '%') { | |
942 | if ((cnt + 2 > length) || | |
943 | !ishexnumber(value[cnt + 1]) || | |
944 | !ishexnumber(value[cnt + 2])) return 0; | |
945 | number = toupper(value[++cnt]) - '0'; | |
946 | if (number > 9) number -= 7; | |
947 | tmp = toupper(value[++cnt]) - '0'; | |
948 | if (tmp > 9) tmp -= 7; | |
949 | number = (number << 4) + tmp; | |
950 | valueCopy[cnt2] = number; | |
951 | } else valueCopy[cnt2] = value[cnt]; | |
952 | } | |
953 | valueRef = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)valueCopy, cnt2); | |
919ea02d | 954 | } else return 0; |
cf37c299 | 955 | |
919ea02d | 956 | return valueRef; |
1815bff5 | 957 | } |
09fd88e4 | 958 | |
34d340d7 A |
959 | static void SetOFVariableFromFile(const void *key, const void *value, void *context) |
960 | { | |
961 | kern_return_t result; | |
962 | ||
00bf83c0 | 963 | result = IORegistryEntrySetCFProperty(gSelectedOptionsRef, key, value); |
34d340d7 | 964 | if ( result != KERN_SUCCESS ) { |
00bf83c0 A |
965 | long nameLen; |
966 | char *nameBuffer; | |
967 | char *nameString; | |
968 | ||
969 | // Get the variable's name. | |
970 | nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), | |
971 | kCFStringEncodingUTF8) + 1; | |
972 | nameBuffer = malloc(nameLen); | |
973 | if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) ) | |
974 | nameString = nameBuffer; | |
975 | else { | |
976 | warnx("Unable to convert property name to C string"); | |
977 | nameString = "<UNPRINTABLE>"; | |
978 | } | |
979 | errx(1, "Error setting variable - '%s': %s", nameString, | |
980 | mach_error_string(result)); | |
34d340d7 A |
981 | } |
982 | } |