1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <inttypes.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <hardware/nvram.h>
24 
25 #define countof(array) (sizeof(array) / sizeof((array)[0]))
26 
27 // Exit status codes. These are all negative as the positive ones are used for
28 // the NV_RESULT_ codes.
29 enum StatusCode {
30   kStatusInvalidArg = -1,
31   kStatusHALError = -2,
32   kStatusAllocationFailure = -3,
33 };
34 
35 static struct {
36   int status;
37   const char* description;
38 } kStatusStringTable[] = {
39     {kStatusInvalidArg, "Bad parameter"},
40     {kStatusHALError, "NVRAM HAL initialization error"},
41     {kStatusAllocationFailure, "Memory allocation error"},
42     {NV_RESULT_SUCCESS, "Success"},
43     {NV_RESULT_INTERNAL_ERROR, "Internal error"},
44     {NV_RESULT_ACCESS_DENIED, "Access denied"},
45     {NV_RESULT_INVALID_PARAMETER, "Invalid NVRAM parameter"},
46     {NV_RESULT_SPACE_DOES_NOT_EXIST, "Space does not exist"},
47     {NV_RESULT_SPACE_ALREADY_EXISTS, "Space already exists"},
48     {NV_RESULT_OPERATION_DISABLED, "Operation disabled"},
49 };
50 
51 // Returns a string describing |status|.
StatusToString(int status)52 static const char* StatusToString(int status) {
53   for (size_t i = 0; i < countof(kStatusStringTable); ++i) {
54     if (kStatusStringTable[i].status == status) {
55       return kStatusStringTable[i].description;
56     }
57   }
58 
59   return "unknown error";
60 }
61 
62 // A table mapping control values to names.
63 static struct {
64   nvram_control_t control;
65   const char* name;
66 } kControlNameTable[] = {
67     {NV_CONTROL_PERSISTENT_WRITE_LOCK, "PERSISTENT_WRITE_LOCK"},
68     {NV_CONTROL_BOOT_WRITE_LOCK, "BOOT_WRITE_LOCK"},
69     {NV_CONTROL_BOOT_READ_LOCK, "BOOT_READ_LOCK"},
70     {NV_CONTROL_WRITE_AUTHORIZATION, "WRITE_AUTHORIZATION"},
71     {NV_CONTROL_READ_AUTHORIZATION, "READ_AUTHORIZATION"},
72     {NV_CONTROL_WRITE_EXTEND, "WRITE_EXTEND"},
73 };
74 
75 // Returns the string representation of |control|, or NULL if |control| isn't a
76 // valid control value.
ControlToString(nvram_control_t control)77 static const char* ControlToString(nvram_control_t control) {
78   for (size_t i = 0; i < countof(kControlNameTable); ++i) {
79     if (kControlNameTable[i].control == control) {
80       return kControlNameTable[i].name;
81     }
82   }
83 
84   return NULL;
85 }
86 
87 // Sets |control| to the NV_CONTROL_ value corresponding to the string control
88 // representation found in |name|. Returns 0 if successful, 1 if name doesn't
89 // match any control string.
StringToControl(const char * name,nvram_control_t * control)90 static int StringToControl(const char* name, nvram_control_t* control) {
91   for (size_t i = 0; i < countof(kControlNameTable); ++i) {
92     if (strcmp(kControlNameTable[i].name, name) == 0) {
93       *control = kControlNameTable[i].control;
94       return 0;
95     }
96   }
97 
98   return 1;
99 }
100 
HandleGetTotalSize(nvram_device_t * device,char * args[])101 static int HandleGetTotalSize(nvram_device_t* device, char* args[]) {
102   (void)args;
103   uint64_t total_size = 0;
104   nvram_result_t result = device->get_total_size_in_bytes(device, &total_size);
105   if (result != NV_RESULT_SUCCESS) {
106     return result;
107   }
108 
109   printf("%" PRIu64 "\n", total_size);
110   return 0;
111 }
112 
HandleGetAvailableSize(nvram_device_t * device,char * args[])113 static int HandleGetAvailableSize(nvram_device_t* device, char* args[]) {
114   (void)args;
115   uint64_t available_size = 0;
116   nvram_result_t result =
117       device->get_available_size_in_bytes(device, &available_size);
118   if (result != NV_RESULT_SUCCESS) {
119     return result;
120   }
121 
122   printf("%" PRIu64 "\n", available_size);
123   return 0;
124 }
125 
HandleGetMaxSpaceSize(nvram_device_t * device,char * args[])126 static int HandleGetMaxSpaceSize(nvram_device_t* device, char* args[]) {
127   (void)args;
128   uint64_t max_space_size = 0;
129   nvram_result_t result =
130       device->get_max_space_size_in_bytes(device, &max_space_size);
131   if (result != NV_RESULT_SUCCESS) {
132     return result;
133   }
134 
135   printf("%" PRIu64 "\n", max_space_size);
136   return 0;
137 }
138 
HandleGetMaxSpaces(nvram_device_t * device,char * args[])139 static int HandleGetMaxSpaces(nvram_device_t* device, char* args[]) {
140   (void)args;
141   uint32_t max_spaces = 0;
142   nvram_result_t result = device->get_max_spaces(device, &max_spaces);
143   if (result != NV_RESULT_SUCCESS) {
144     return result;
145   }
146 
147   printf("%" PRIu32 "\n", max_spaces);
148   return 0;
149 }
150 
HandleGetSpaceList(nvram_device_t * device,char * args[])151 static int HandleGetSpaceList(nvram_device_t* device, char* args[]) {
152   (void)args;
153   uint32_t list_size = 0;
154   nvram_result_t result = device->get_space_list(device, 0, NULL, &list_size);
155   if (result != NV_RESULT_SUCCESS) {
156     return result;
157   }
158 
159   uint32_t* space_index_list = calloc(list_size, sizeof(uint32_t));
160   if (!space_index_list) {
161     return kStatusAllocationFailure;
162   }
163 
164   result =
165       device->get_space_list(device, list_size, space_index_list, &list_size);
166   if (result != NV_RESULT_SUCCESS) {
167     free(space_index_list);
168     return result;
169   }
170 
171   for (uint32_t i = 0; i < list_size; ++i) {
172     if (i != 0) {
173       fputs(",", stdout);
174     }
175     printf("%" PRIu32, space_index_list[i]);
176   }
177   fputs("\n", stdout);
178 
179   free(space_index_list);
180   return 0;
181 }
182 
HandleGetSpaceSize(nvram_device_t * device,char * args[])183 static int HandleGetSpaceSize(nvram_device_t* device, char* args[]) {
184   uint32_t index = strtoul(args[0], NULL, 0);
185   uint64_t space_size = 0;
186   nvram_result_t result = device->get_space_size(device, index, &space_size);
187   if (result != NV_RESULT_SUCCESS) {
188     return result;
189   }
190 
191   printf("%" PRIu64 "\n", space_size);
192   return 0;
193 }
194 
HandleGetSpaceControls(nvram_device_t * device,char * args[])195 static int HandleGetSpaceControls(nvram_device_t* device, char* args[]) {
196   uint32_t index = strtoul(args[0], NULL, 0);
197   uint32_t list_size = 0;
198   nvram_result_t result =
199       device->get_space_controls(device, index, 0, NULL, &list_size);
200   if (result != NV_RESULT_SUCCESS) {
201     return result;
202   }
203 
204   uint32_t* controls_list = calloc(list_size, sizeof(nvram_control_t));
205   if (!controls_list) {
206     return kStatusAllocationFailure;
207   }
208 
209   result = device->get_space_controls(device, index, list_size, controls_list,
210                                       &list_size);
211   if (result != NV_RESULT_SUCCESS) {
212     free(controls_list);
213     return result;
214   }
215 
216   for (uint32_t i = 0; i < list_size; ++i) {
217     if (i != 0) {
218       fputs(",", stdout);
219     }
220     const char* name = ControlToString(controls_list[i]);
221     if (name) {
222       fputs(name, stdout);
223     } else {
224       printf("<unknown_control_%" PRIu32 ">", controls_list[i]);
225     }
226   }
227   fputs("", stdout);
228 
229   free(controls_list);
230   return 0;
231 }
232 
HandleIsSpaceReadLocked(nvram_device_t * device,char * args[])233 static int HandleIsSpaceReadLocked(nvram_device_t* device, char* args[]) {
234   uint32_t index = strtoul(args[0], NULL, 0);
235   int read_locked = 0;
236   int write_locked = 0;
237   nvram_result_t result =
238       device->is_space_locked(device, index, &read_locked, &write_locked);
239   if (result != NV_RESULT_SUCCESS) {
240     return result;
241   }
242 
243   printf("%d\n", read_locked);
244   return 0;
245 }
246 
HandleIsSpaceWriteLocked(nvram_device_t * device,char * args[])247 static int HandleIsSpaceWriteLocked(nvram_device_t* device, char* args[]) {
248   uint32_t index = strtoul(args[0], NULL, 0);
249   int read_locked = 0;
250   int write_locked = 0;
251   nvram_result_t result =
252       device->is_space_locked(device, index, &read_locked, &write_locked);
253   if (result != NV_RESULT_SUCCESS) {
254     return result;
255   }
256 
257   printf("%d\n", write_locked);
258   return 0;
259 }
260 
HandleCreateSpace(nvram_device_t * device,char * args[])261 static int HandleCreateSpace(nvram_device_t* device, char* args[]) {
262   uint32_t index = strtoul(args[0], NULL, 0);
263   uint64_t size = strtoull(args[1], NULL, 0);
264   uint32_t list_size = 0;
265   nvram_control_t* controls_list = NULL;
266   char* tail = args[2];
267   while (tail) {
268     ++list_size;
269     nvram_control_t* new_controls_list =
270         realloc(controls_list, sizeof(nvram_control_t) * list_size);
271     if (new_controls_list) {
272       controls_list = new_controls_list;
273     } else {
274       free(controls_list);
275       return kStatusAllocationFailure;
276     }
277 
278     if (StringToControl(strsep(&tail, ","), &(controls_list[list_size - 1]))) {
279       free(controls_list);
280       return kStatusInvalidArg;
281     }
282   }
283 
284   return device->create_space(device, index, size, controls_list, list_size,
285                               (uint8_t*)args[3], strlen(args[3]));
286 }
287 
HandleDeleteSpace(nvram_device_t * device,char * args[])288 static int HandleDeleteSpace(nvram_device_t* device, char* args[]) {
289   uint32_t index = strtoul(args[0], NULL, 0);
290   return device->delete_space(device, index, (uint8_t*)args[3],
291                               strlen(args[3]));
292 }
293 
HandleDisableCreate(nvram_device_t * device,char * args[])294 static int HandleDisableCreate(nvram_device_t* device, char* args[]) {
295   (void)args;
296   return device->disable_create(device);
297 }
298 
HandleWriteSpace(nvram_device_t * device,char * args[])299 static int HandleWriteSpace(nvram_device_t* device, char* args[]) {
300   uint32_t index = strtoul(args[0], NULL, 0);
301   return device->write_space(device, index, (uint8_t*)args[1], strlen(args[1]),
302                              (uint8_t*)args[2], strlen(args[2]));
303 }
304 
HandleReadSpace(nvram_device_t * device,char * args[])305 static int HandleReadSpace(nvram_device_t* device, char* args[]) {
306   uint32_t index = strtoul(args[0], NULL, 0);
307   uint64_t size = 0;
308   nvram_result_t result = device->get_space_size(device, index, &size);
309   if (result != NV_RESULT_SUCCESS) {
310     return result;
311   }
312 
313   uint8_t* buffer = calloc(sizeof(uint8_t), size);
314   if (!buffer) {
315     return kStatusAllocationFailure;
316   }
317 
318   result = device->read_space(device, index, size, (uint8_t*)args[1],
319                               strlen(args[1]), buffer, &size);
320   if (result != NV_RESULT_SUCCESS) {
321     free(buffer);
322     return result;
323   }
324 
325   fwrite(buffer, sizeof(uint8_t), size, stdout);
326   fputs("\n", stdout);
327   free(buffer);
328   return 0;
329 }
330 
HandleEnableWriteLock(nvram_device_t * device,char * args[])331 static int HandleEnableWriteLock(nvram_device_t* device, char* args[]) {
332   uint32_t index = strtoul(args[0], NULL, 0);
333   return device->enable_write_lock(device, index, (uint8_t*)args[1],
334                                    strlen(args[1]));
335 }
336 
HandleEnableReadLock(nvram_device_t * device,char * args[])337 static int HandleEnableReadLock(nvram_device_t* device, char* args[]) {
338   uint32_t index = strtoul(args[0], NULL, 0);
339   return device->enable_read_lock(device, index, (uint8_t*)args[1],
340                                   strlen(args[1]));
341 }
342 
343 struct CommandHandler {
344   const char* name;
345   const char* params_desc;
346   int nparams;
347   int (*run)(nvram_device_t*, char* args[]);
348 };
349 
350 struct CommandHandler kCommandHandlers[] = {
351     {"get_total_size", "", 0, &HandleGetTotalSize},
352     {"get_available_size", "", 0, &HandleGetAvailableSize},
353     {"get_max_space_size", "", 0, &HandleGetMaxSpaceSize},
354     {"get_max_spaces", "", 0, &HandleGetMaxSpaces},
355     {"get_space_list", "", 0, &HandleGetSpaceList},
356     {"get_space_size", "<index>", 1, &HandleGetSpaceSize},
357     {"get_space_controls", "<index>", 1, &HandleGetSpaceControls},
358     {"is_space_read_locked", "<index>", 1, &HandleIsSpaceReadLocked},
359     {"is_space_write_locked", "<index>", 1, &HandleIsSpaceWriteLocked},
360     {"create_space", "<index> <size> <controls> <auth>", 4, &HandleCreateSpace},
361     {"delete_space", "<index> <auth>", 2, &HandleDeleteSpace},
362     {"disable_create", "", 0, &HandleDisableCreate},
363     {"write_space", "<index> <data> <auth>", 3, &HandleWriteSpace},
364     {"read_space", "<index> <auth>", 2, &HandleReadSpace},
365     {"enable_write_lock", "<index> <auth>", 2, &HandleEnableWriteLock},
366     {"enable_read_lock", "<index> <auth>", 2, &HandleEnableReadLock},
367 };
368 
main(int argc,char * argv[])369 int main(int argc, char* argv[]) {
370   if (argc < 2) {
371     fprintf(stderr, "Usage: %s <command> <command-args>\n", argv[0]);
372     fprintf(stderr, "Valid commands are:\n");
373     for (size_t i = 0; i < countof(kCommandHandlers); ++i) {
374       fprintf(stderr, "  %s %s\n", kCommandHandlers[i].name,
375               kCommandHandlers[i].params_desc);
376     }
377     return kStatusInvalidArg;
378   }
379 
380   const struct CommandHandler* cmd = NULL;
381   for (size_t i = 0; i < countof(kCommandHandlers); ++i) {
382     if (strcmp(kCommandHandlers[i].name, argv[1]) == 0) {
383       cmd = &kCommandHandlers[i];
384     }
385   }
386 
387   if (!cmd) {
388     fprintf(stderr, "Bad command: %s\n", argv[1]);
389     return kStatusInvalidArg;
390   }
391 
392   if (argc - 2 != cmd->nparams) {
393     fprintf(stderr, "Command %s takes %d parameters, %d given.\n", argv[1],
394             cmd->nparams, argc - 2);
395     return kStatusInvalidArg;
396   }
397 
398   const hw_module_t* module = NULL;
399   nvram_device_t* nvram_device = NULL;
400   if (hw_get_module(NVRAM_HARDWARE_MODULE_ID, &module) != 0 ||
401       module->methods->open(module, NVRAM_HARDWARE_DEVICE_ID,
402                             (hw_device_t**)&nvram_device) != 0) {
403     fprintf(stderr, "Failed to open NVRAM HAL.\n");
404     return kStatusHALError;
405   }
406 
407   if (nvram_device->common.version != NVRAM_DEVICE_API_VERSION_1_1) {
408     fprintf(stderr, "Unsupported NVRAM HAL version.\n");
409     nvram_device->common.close(&nvram_device->common);
410     return kStatusHALError;
411   }
412 
413   int ret = cmd->run(nvram_device, argv + 2);
414   if (ret != 0) {
415     fprintf(stderr, "Command execution failure: %s (%d).\n",
416             StatusToString(ret), ret);
417   }
418 
419   nvram_device->common.close(&nvram_device->common);
420   return ret;
421 }
422