xref: /xnu-8796.121.2/tests/nvram_tests/nvram_helper.c (revision c54f35ca767986246321eb901baf8f5ff7923f6a)
1 #include <IOKit/IOKitLib.h>
2 #include <CoreFoundation/CoreFoundation.h>
3 #include <darwintest.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include "nvram_helper.h"
8 
9 //  Ascii value of 'A' (65) - Ascii value of '9' (57)
10 #define ASCII_OFFSET 7
11 #define NVRAM_BYTE_LEN 3
12 
13 // NVRAM helper functions from https://stashweb.sd.apple.com/projects/COREOS/repos/system_cmds/browse/nvram.tproj/nvram.c
14 
15 /**
16  * @brief Print the given firmware variable.
17  */
18 static void
PrintVariable(const void * key,const void * value,void * context)19 PrintVariable(const void *key, const void *value, void *context)
20 {
21 	if (CFGetTypeID(key) != CFStringGetTypeID()) {
22 		printf("Variable name passed in isn't a string");
23 		return;
24 	}
25 	long cnt, cnt2;
26 	CFIndex nameLen;
27 	char *nameBuffer = 0;
28 	const char *nameString;
29 	char numberBuffer[10];
30 	const uint8_t *dataPtr;
31 	uint8_t dataChar;
32 	char *dataBuffer = 0;
33 	CFIndex valueLen;
34 	char *valueBuffer = 0;
35 	const char *valueString = 0;
36 	uint32_t number;
37 	long length;
38 	CFTypeID typeID;
39 	// Get the variable's name.
40 	nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), kCFStringEncodingUTF8) + 1;
41 
42 	nameBuffer = malloc(nameLen);
43 
44 	if (nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8)) {
45 		nameString = nameBuffer;
46 	} else {
47 		printf("Unable to convert property name to C string");
48 		nameString = "<UNPRINTABLE>";
49 	}
50 
51 	// Get the variable's type.
52 	typeID = CFGetTypeID(value);
53 
54 	if (typeID == CFBooleanGetTypeID()) {
55 		if (CFBooleanGetValue(value)) {
56 			valueString = "true";
57 		} else {
58 			valueString = "false";
59 		}
60 	} else if (typeID == CFNumberGetTypeID()) {
61 		CFNumberGetValue(value, kCFNumberSInt32Type, &number);
62 		if (number == 0xFFFFFFFF) {
63 			sprintf(numberBuffer, "-1");
64 		} else {
65 			sprintf(numberBuffer, "0x%x", number);
66 		}
67 		valueString = numberBuffer;
68 	} else if (typeID == CFStringGetTypeID()) {
69 		valueLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value),
70 		    kCFStringEncodingUTF8) +
71 		    1;
72 		valueBuffer = malloc(valueLen + 1);
73 		if (valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8)) {
74 			valueString = valueBuffer;
75 		} else {
76 			printf("Unable to convert value to C string");
77 			valueString = "<UNPRINTABLE>";
78 		}
79 	} else if (typeID == CFDataGetTypeID()) {
80 		length = CFDataGetLength(value);
81 		if (length == 0) {
82 			valueString = "";
83 		} else {
84 			dataBuffer = malloc(length * NVRAM_BYTE_LEN + NVRAM_BYTE_LEN);
85 			if (dataBuffer != 0) {
86 				dataPtr = CFDataGetBytePtr(value);
87 				cnt = cnt2 = 0;
88 				for (; cnt < length; cnt++) {
89 					dataChar = dataPtr[cnt];
90 					if (isprint(dataChar) && dataChar != '%') {
91 						dataBuffer[cnt2++] = dataChar;
92 					} else {
93 						sprintf(dataBuffer + cnt2, "%%%02x", dataChar);
94 						cnt2 += NVRAM_BYTE_LEN;
95 					}
96 				}
97 				dataBuffer[cnt2] = '\0';
98 				valueString = dataBuffer;
99 			}
100 		}
101 	} else {
102 		valueString = "<INVALID>";
103 	}
104 
105 	if ((nameString != 0) && (valueString != 0)) {
106 		printf("%s\t%s\n", nameString, valueString);
107 	}
108 
109 	if (dataBuffer != 0) {
110 		free(dataBuffer);
111 	}
112 	if (nameBuffer != 0) {
113 		free(nameBuffer);
114 	}
115 	if (valueBuffer != 0) {
116 		free(valueBuffer);
117 	}
118 }
119 
120 /**
121  * @brief Convert the value into a CFType given the typeID
122  */
123 static CFTypeRef
ConvertValueToCFTypeRef(CFTypeID typeID,const char * value)124 ConvertValueToCFTypeRef(CFTypeID typeID, const char *value)
125 {
126 	CFTypeRef valueRef = 0;
127 	long cnt, cnt2, length;
128 	unsigned long number, tmp;
129 
130 	if (typeID == CFBooleanGetTypeID()) {
131 		if (value == NULL) {
132 			return valueRef;
133 		}
134 		if (!strcmp("true", value)) {
135 			valueRef = kCFBooleanTrue;
136 		} else if (!strcmp("false", value)) {
137 			valueRef = kCFBooleanFalse;
138 		}
139 	} else if (typeID == CFNumberGetTypeID()) {
140 		number = strtol(value, 0, 0);
141 		valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
142 		    &number);
143 	} else if (typeID == CFStringGetTypeID()) {
144 		valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value,
145 		    kCFStringEncodingUTF8);
146 	} else if (typeID == CFDataGetTypeID()) {
147 		if (value == NULL) {
148 			length = 0;
149 		} else {
150 			length = strlen(value);
151 		}
152 
153 		char valueCopy[length + 1];
154 
155 		for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) {
156 			if (value[cnt] == '%') {
157 				if ((cnt + 2 > length) ||
158 				    !ishexnumber(value[cnt + 1]) ||
159 				    !ishexnumber(value[cnt + 2])) {
160 					return 0;
161 				}
162 				number = toupper(value[++cnt]) - '0';
163 				if (number > 9) {
164 					number -= ASCII_OFFSET;
165 				}
166 				tmp = toupper(value[++cnt]) - '0';
167 				if (tmp > 9) {
168 					tmp -= ASCII_OFFSET;
169 				}
170 				number = (number << 4) + tmp;
171 				valueCopy[cnt2] = number;
172 			} else {
173 				valueCopy[cnt2] = value[cnt];
174 			}
175 		}
176 		valueRef = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)valueCopy, cnt2);
177 	} else {
178 		return 0;
179 	}
180 
181 	return valueRef;
182 }
183 
184 /**
185  * @brief Prints the variable. returns kIOReturnNotFound if not found
186  */
187 kern_return_t
GetVariable(const char * name,io_registry_entry_t optionsRef)188 GetVariable(const char *name, io_registry_entry_t optionsRef)
189 {
190 	CFStringRef nameRef = NULL;
191 	CFTypeRef valueRef = NULL;
192 	nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
193 	    kCFStringEncodingUTF8);
194 	if (nameRef == 0) {
195 		printf("Error creating CFString for key %s", name);
196 		return KERN_FAILURE;
197 	}
198 
199 	valueRef = IORegistryEntryCreateCFProperty(optionsRef, nameRef, 0, 0);
200 	if (valueRef == 0) {
201 		return kIOReturnNotFound;
202 	}
203 
204 	PrintVariable(nameRef, valueRef, 0);
205 
206 	return KERN_SUCCESS;
207 }
208 
209 /**
210  * @brief Set the named variable with the value passed in
211  */
212 kern_return_t
SetVariable(const char * name,const char * value,io_registry_entry_t optionsRef)213 SetVariable(const char *name, const char *value, io_registry_entry_t optionsRef)
214 {
215 	CFStringRef nameRef;
216 	CFTypeRef valueRef;
217 	CFTypeID typeID;
218 	kern_return_t result = KERN_FAILURE;
219 
220 	nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
221 	    kCFStringEncodingUTF8);
222 	if (nameRef == 0) {
223 		printf("Error creating CFString for key %s", name);
224 		return result;
225 	}
226 
227 	valueRef = IORegistryEntryCreateCFProperty(optionsRef, nameRef, 0, 0);
228 	if (valueRef) {
229 		typeID = CFGetTypeID(valueRef);
230 		CFRelease(valueRef);
231 		valueRef = ConvertValueToCFTypeRef(typeID, value);
232 		if (valueRef == 0) {
233 			printf("Error creating CFTypeRef for value %s", value);
234 			return result;
235 		}
236 		result = IORegistryEntrySetCFProperty(optionsRef, nameRef, valueRef);
237 	} else {
238 		// skip testing different CFTypeIDs if there is no entry and it's a delete operation.
239 		if (value == NULL) {
240 			return result;
241 		}
242 		// In the default case, try data, string, number, then boolean.
243 		CFTypeID types[] = {CFDataGetTypeID(),
244 			            CFStringGetTypeID(), CFNumberGetTypeID(), CFBooleanGetTypeID()};
245 		for (int i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
246 			valueRef = ConvertValueToCFTypeRef(types[i], value);
247 			if (valueRef != 0) {
248 				result = IORegistryEntrySetCFProperty(optionsRef, nameRef, valueRef);
249 				if (result == KERN_SUCCESS || result == kIOReturnNoMemory || result == kIOReturnNoSpace) {
250 					break;
251 				}
252 			}
253 		}
254 	}
255 
256 	CFRelease(nameRef);
257 
258 	return result;
259 }
260 
261 /**
262  * @brief Delete named variable
263  */
264 kern_return_t
DeleteVariable(const char * name,io_registry_entry_t optionsRef)265 DeleteVariable(const char *name, io_registry_entry_t optionsRef)
266 {
267 	// Since delete always returns ok, read to make sure it is deleted.
268 	if (SetVariable(kIONVRAMDeletePropertyKey, name, optionsRef) == KERN_SUCCESS) {
269 		if (GetVariable(name, optionsRef) == kIOReturnNotFound) {
270 			return KERN_SUCCESS;
271 		}
272 	}
273 	return KERN_FAILURE;
274 }
275 
276 /**
277  * @brief Reset NVram
278  */
279 kern_return_t
ResetNVram(io_registry_entry_t optionsRef)280 ResetNVram(io_registry_entry_t optionsRef)
281 {
282 	if (SetVariable("ResetNVRam", "1", optionsRef) == KERN_SUCCESS) {
283 		return KERN_SUCCESS;
284 	}
285 	return KERN_FAILURE;
286 }
287 
288 /**
289  * @brief Get the Options object
290  */
291 io_registry_entry_t
GetOptions(void)292 GetOptions(void)
293 {
294 	io_registry_entry_t optionsRef = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
295 	T_ASSERT_NE(optionsRef, IO_OBJECT_NULL, "got options");
296 	return optionsRef;
297 }
298 
299 /**
300  * @brief Release option object passed in
301  */
302 void
ReleaseOptions(io_registry_entry_t optionsRef)303 ReleaseOptions(io_registry_entry_t optionsRef)
304 {
305 	if (optionsRef != IO_OBJECT_NULL) {
306 		IOObjectRelease(optionsRef);
307 	}
308 }
309