| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <inttypes.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <hardware/nvram.h> |
| |
| #define countof(array) (sizeof(array) / sizeof((array)[0])) |
| |
| // Exit status codes. These are all negative as the positive ones are used for |
| // the NV_RESULT_ codes. |
| enum StatusCode { |
| kStatusInvalidArg = -1, |
| kStatusHALError = -2, |
| kStatusAllocationFailure = -3, |
| }; |
| |
| static struct { |
| int status; |
| const char* description; |
| } kStatusStringTable[] = { |
| {kStatusInvalidArg, "Bad parameter"}, |
| {kStatusHALError, "NVRAM HAL initialization error"}, |
| {kStatusAllocationFailure, "Memory allocation error"}, |
| {NV_RESULT_SUCCESS, "Success"}, |
| {NV_RESULT_INTERNAL_ERROR, "Internal error"}, |
| {NV_RESULT_ACCESS_DENIED, "Access denied"}, |
| {NV_RESULT_INVALID_PARAMETER, "Invalid NVRAM parameter"}, |
| {NV_RESULT_SPACE_DOES_NOT_EXIST, "Space does not exist"}, |
| {NV_RESULT_SPACE_ALREADY_EXISTS, "Space already exists"}, |
| {NV_RESULT_OPERATION_DISABLED, "Operation disabled"}, |
| }; |
| |
| // Returns a string describing |status|. |
| static const char* StatusToString(int status) { |
| for (size_t i = 0; i < countof(kStatusStringTable); ++i) { |
| if (kStatusStringTable[i].status == status) { |
| return kStatusStringTable[i].description; |
| } |
| } |
| |
| return "unknown error"; |
| } |
| |
| // A table mapping control values to names. |
| static struct { |
| nvram_control_t control; |
| const char* name; |
| } kControlNameTable[] = { |
| {NV_CONTROL_PERSISTENT_WRITE_LOCK, "PERSISTENT_WRITE_LOCK"}, |
| {NV_CONTROL_BOOT_WRITE_LOCK, "BOOT_WRITE_LOCK"}, |
| {NV_CONTROL_BOOT_READ_LOCK, "BOOT_READ_LOCK"}, |
| {NV_CONTROL_WRITE_AUTHORIZATION, "WRITE_AUTHORIZATION"}, |
| {NV_CONTROL_READ_AUTHORIZATION, "READ_AUTHORIZATION"}, |
| {NV_CONTROL_WRITE_EXTEND, "WRITE_EXTEND"}, |
| }; |
| |
| // Returns the string representation of |control|, or NULL if |control| isn't a |
| // valid control value. |
| static const char* ControlToString(nvram_control_t control) { |
| for (size_t i = 0; i < countof(kControlNameTable); ++i) { |
| if (kControlNameTable[i].control == control) { |
| return kControlNameTable[i].name; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| // Sets |control| to the NV_CONTROL_ value corresponding to the string control |
| // representation found in |name|. Returns 0 if successful, 1 if name doesn't |
| // match any control string. |
| static int StringToControl(const char* name, nvram_control_t* control) { |
| for (size_t i = 0; i < countof(kControlNameTable); ++i) { |
| if (strcmp(kControlNameTable[i].name, name) == 0) { |
| *control = kControlNameTable[i].control; |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| static int HandleGetTotalSize(nvram_device_t* device, char* args[]) { |
| (void)args; |
| uint64_t total_size = 0; |
| nvram_result_t result = device->get_total_size_in_bytes(device, &total_size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%" PRIu64 "\n", total_size); |
| return 0; |
| } |
| |
| static int HandleGetAvailableSize(nvram_device_t* device, char* args[]) { |
| (void)args; |
| uint64_t available_size = 0; |
| nvram_result_t result = |
| device->get_available_size_in_bytes(device, &available_size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%" PRIu64 "\n", available_size); |
| return 0; |
| } |
| |
| static int HandleGetMaxSpaceSize(nvram_device_t* device, char* args[]) { |
| (void)args; |
| uint64_t max_space_size = 0; |
| nvram_result_t result = |
| device->get_max_space_size_in_bytes(device, &max_space_size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%" PRIu64 "\n", max_space_size); |
| return 0; |
| } |
| |
| static int HandleGetMaxSpaces(nvram_device_t* device, char* args[]) { |
| (void)args; |
| uint32_t max_spaces = 0; |
| nvram_result_t result = device->get_max_spaces(device, &max_spaces); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%" PRIu32 "\n", max_spaces); |
| return 0; |
| } |
| |
| static int HandleGetSpaceList(nvram_device_t* device, char* args[]) { |
| (void)args; |
| uint32_t list_size = 0; |
| nvram_result_t result = device->get_space_list(device, 0, NULL, &list_size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| uint32_t* space_index_list = calloc(list_size, sizeof(uint32_t)); |
| if (!space_index_list) { |
| return kStatusAllocationFailure; |
| } |
| |
| result = |
| device->get_space_list(device, list_size, space_index_list, &list_size); |
| if (result != NV_RESULT_SUCCESS) { |
| free(space_index_list); |
| return result; |
| } |
| |
| for (uint32_t i = 0; i < list_size; ++i) { |
| if (i != 0) { |
| fputs(",", stdout); |
| } |
| printf("%" PRIu32, space_index_list[i]); |
| } |
| fputs("\n", stdout); |
| |
| free(space_index_list); |
| return 0; |
| } |
| |
| static int HandleGetSpaceSize(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| uint64_t space_size = 0; |
| nvram_result_t result = device->get_space_size(device, index, &space_size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%" PRIu64 "\n", space_size); |
| return 0; |
| } |
| |
| static int HandleGetSpaceControls(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| uint32_t list_size = 0; |
| nvram_result_t result = |
| device->get_space_controls(device, index, 0, NULL, &list_size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| uint32_t* controls_list = calloc(list_size, sizeof(nvram_control_t)); |
| if (!controls_list) { |
| return kStatusAllocationFailure; |
| } |
| |
| result = device->get_space_controls(device, index, list_size, controls_list, |
| &list_size); |
| if (result != NV_RESULT_SUCCESS) { |
| free(controls_list); |
| return result; |
| } |
| |
| for (uint32_t i = 0; i < list_size; ++i) { |
| if (i != 0) { |
| fputs(",", stdout); |
| } |
| const char* name = ControlToString(controls_list[i]); |
| if (name) { |
| fputs(name, stdout); |
| } else { |
| printf("<unknown_control_%" PRIu32 ">", controls_list[i]); |
| } |
| } |
| fputs("", stdout); |
| |
| free(controls_list); |
| return 0; |
| } |
| |
| static int HandleIsSpaceReadLocked(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| int read_locked = 0; |
| int write_locked = 0; |
| nvram_result_t result = |
| device->is_space_locked(device, index, &read_locked, &write_locked); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%d\n", read_locked); |
| return 0; |
| } |
| |
| static int HandleIsSpaceWriteLocked(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| int read_locked = 0; |
| int write_locked = 0; |
| nvram_result_t result = |
| device->is_space_locked(device, index, &read_locked, &write_locked); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| printf("%d\n", write_locked); |
| return 0; |
| } |
| |
| static int HandleCreateSpace(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| uint64_t size = strtoull(args[1], NULL, 0); |
| uint32_t list_size = 0; |
| nvram_control_t* controls_list = NULL; |
| char* tail = args[2]; |
| while (tail) { |
| ++list_size; |
| nvram_control_t* new_controls_list = |
| realloc(controls_list, sizeof(nvram_control_t) * list_size); |
| if (new_controls_list) { |
| controls_list = new_controls_list; |
| } else { |
| free(controls_list); |
| return kStatusAllocationFailure; |
| } |
| |
| if (StringToControl(strsep(&tail, ","), &(controls_list[list_size - 1]))) { |
| free(controls_list); |
| return kStatusInvalidArg; |
| } |
| } |
| |
| return device->create_space(device, index, size, controls_list, list_size, |
| (uint8_t*)args[3], strlen(args[3])); |
| } |
| |
| static int HandleDeleteSpace(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| return device->delete_space(device, index, (uint8_t*)args[3], |
| strlen(args[3])); |
| } |
| |
| static int HandleDisableCreate(nvram_device_t* device, char* args[]) { |
| (void)args; |
| return device->disable_create(device); |
| } |
| |
| static int HandleWriteSpace(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| return device->write_space(device, index, (uint8_t*)args[1], strlen(args[1]), |
| (uint8_t*)args[2], strlen(args[2])); |
| } |
| |
| static int HandleReadSpace(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| uint64_t size = 0; |
| nvram_result_t result = device->get_space_size(device, index, &size); |
| if (result != NV_RESULT_SUCCESS) { |
| return result; |
| } |
| |
| uint8_t* buffer = calloc(sizeof(uint8_t), size); |
| if (!buffer) { |
| return kStatusAllocationFailure; |
| } |
| |
| result = device->read_space(device, index, size, (uint8_t*)args[1], |
| strlen(args[1]), buffer, &size); |
| if (result != NV_RESULT_SUCCESS) { |
| free(buffer); |
| return result; |
| } |
| |
| fwrite(buffer, sizeof(uint8_t), size, stdout); |
| fputs("\n", stdout); |
| free(buffer); |
| return 0; |
| } |
| |
| static int HandleEnableWriteLock(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| return device->enable_write_lock(device, index, (uint8_t*)args[1], |
| strlen(args[1])); |
| } |
| |
| static int HandleEnableReadLock(nvram_device_t* device, char* args[]) { |
| uint32_t index = strtoul(args[0], NULL, 0); |
| return device->enable_read_lock(device, index, (uint8_t*)args[1], |
| strlen(args[1])); |
| } |
| |
| struct CommandHandler { |
| const char* name; |
| const char* params_desc; |
| int nparams; |
| int (*run)(nvram_device_t*, char* args[]); |
| }; |
| |
| struct CommandHandler kCommandHandlers[] = { |
| {"get_total_size", "", 0, &HandleGetTotalSize}, |
| {"get_available_size", "", 0, &HandleGetAvailableSize}, |
| {"get_max_space_size", "", 0, &HandleGetMaxSpaceSize}, |
| {"get_max_spaces", "", 0, &HandleGetMaxSpaces}, |
| {"get_space_list", "", 0, &HandleGetSpaceList}, |
| {"get_space_size", "<index>", 1, &HandleGetSpaceSize}, |
| {"get_space_controls", "<index>", 1, &HandleGetSpaceControls}, |
| {"is_space_read_locked", "<index>", 1, &HandleIsSpaceReadLocked}, |
| {"is_space_write_locked", "<index>", 1, &HandleIsSpaceWriteLocked}, |
| {"create_space", "<index> <size> <controls> <auth>", 4, &HandleCreateSpace}, |
| {"delete_space", "<index> <auth>", 2, &HandleDeleteSpace}, |
| {"disable_create", "", 0, &HandleDisableCreate}, |
| {"write_space", "<index> <data> <auth>", 3, &HandleWriteSpace}, |
| {"read_space", "<index> <auth>", 2, &HandleReadSpace}, |
| {"enable_write_lock", "<index> <auth>", 2, &HandleEnableWriteLock}, |
| {"enable_read_lock", "<index> <auth>", 2, &HandleEnableReadLock}, |
| }; |
| |
| int main(int argc, char* argv[]) { |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <command> <command-args>\n", argv[0]); |
| fprintf(stderr, "Valid commands are:\n"); |
| for (size_t i = 0; i < countof(kCommandHandlers); ++i) { |
| fprintf(stderr, " %s %s\n", kCommandHandlers[i].name, |
| kCommandHandlers[i].params_desc); |
| } |
| return kStatusInvalidArg; |
| } |
| |
| const struct CommandHandler* cmd = NULL; |
| for (size_t i = 0; i < countof(kCommandHandlers); ++i) { |
| if (strcmp(kCommandHandlers[i].name, argv[1]) == 0) { |
| cmd = &kCommandHandlers[i]; |
| } |
| } |
| |
| if (!cmd) { |
| fprintf(stderr, "Bad command: %s\n", argv[1]); |
| return kStatusInvalidArg; |
| } |
| |
| if (argc - 2 != cmd->nparams) { |
| fprintf(stderr, "Command %s takes %d parameters, %d given.\n", argv[1], |
| cmd->nparams, argc - 2); |
| return kStatusInvalidArg; |
| } |
| |
| const hw_module_t* module = NULL; |
| nvram_device_t* nvram_device = NULL; |
| if (hw_get_module(NVRAM_HARDWARE_MODULE_ID, &module) != 0 || |
| module->methods->open(module, NVRAM_HARDWARE_DEVICE_ID, |
| (hw_device_t**)&nvram_device) != 0) { |
| fprintf(stderr, "Failed to open NVRAM HAL.\n"); |
| return kStatusHALError; |
| } |
| |
| if (nvram_device->common.version != NVRAM_DEVICE_API_VERSION_1_1) { |
| fprintf(stderr, "Unsupported NVRAM HAL version.\n"); |
| nvram_device->common.close(&nvram_device->common); |
| return kStatusHALError; |
| } |
| |
| int ret = cmd->run(nvram_device, argv + 2); |
| if (ret != 0) { |
| fprintf(stderr, "Command execution failure: %s (%d).\n", |
| StatusToString(ret), ret); |
| } |
| |
| nvram_device->common.close(&nvram_device->common); |
| return ret; |
| } |