diff options
Diffstat (limited to 'misc-utils/flash_unlock.c')
-rw-r--r-- | misc-utils/flash_unlock.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/misc-utils/flash_unlock.c b/misc-utils/flash_unlock.c new file mode 100644 index 0000000..f51870a --- /dev/null +++ b/misc-utils/flash_unlock.c @@ -0,0 +1,220 @@ +/* + * flash_{lock,unlock} + * + * utilities for locking/unlocking sectors of flash devices + */ + +enum flash_lock_request { + REQUEST_LOCK, + REQUEST_UNLOCK, + REQUEST_ISLOCKED, +}; + +#ifndef PROGRAM_NAME +#define PROGRAM_NAME "flash_unlock" +#define DEFAULT_REQUEST REQUEST_UNLOCK +#else +#define DEFAULT_REQUEST REQUEST_LOCK +#endif + +#include <getopt.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <string.h> + +#include "common.h" +#include <mtd/mtd-user.h> + +static const char *flash_msg[] = { + [ REQUEST_LOCK ] = "lock", + [ REQUEST_UNLOCK ] = "unlock", + [ REQUEST_ISLOCKED ] = "check lock status", +}; + +static void usage(int status) +{ + fprintf(status ? stderr : stdout, + "Utility to lock, unlock, or check the lock status of the flash.\n" + "Default action: %s\n" + "\n" + "Usage: %s [options] [--] <mtd device> [offset [block count]]\n" + "\n" + "Options:\n" + " -h --help Display this help and exit\n" + " --version Display version information and exit\n" + " -i --islocked Check if flash region is locked\n" + " -l --lock Lock a region of flash\n" + " -u --unlock Unlock a region of flash\n" + "\n" + "If offset is not specified, it defaults to 0.\n" + "If block count is not specified, it defaults to all blocks.\n" + "A block count of -1 means all blocks.\n", + flash_msg[DEFAULT_REQUEST], + PROGRAM_NAME); + exit(status); +} + +static const char short_opts[] = "hilu"; +static const struct option long_opts[] = { + { "help", no_argument, 0, 'h' }, + { "islocked", no_argument, 0, 'i' }, + { "lock", no_argument, 0, 'l' }, + { "unlock", no_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 }, +}; + +/* Program arguments */ +static const char *dev, *offs_s, *count_s; +static enum flash_lock_request req = DEFAULT_REQUEST; + +static void process_args(int argc, char *argv[]) +{ + int arg_idx; + int req_set = 0; + + for (;;) { + int c; + + c = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (c == EOF) + break; + + switch (c) { + case 'h': + usage(0); + break; + case 'i': + req = REQUEST_ISLOCKED; + req_set++; + break; + case 'l': + req = REQUEST_LOCK; + req_set++; + break; + case 'u': + req = REQUEST_UNLOCK; + req_set++; + break; + case 'v': + common_print_version(); + exit(0); + default: + usage(1); + break; + } + } + + if (req_set > 1) { + errmsg("cannot specify more than one lock/unlock/islocked option"); + usage(1); + } + + arg_idx = optind; + + /* Sanity checks */ + if (argc - arg_idx < 1) { + errmsg("too few arguments"); + usage(1); + } else if (argc - arg_idx > 3) { + errmsg("too many arguments"); + usage(1); + } + + /* First non-option argument */ + dev = argv[arg_idx++]; + + /* Second non-option argument */ + if (arg_idx < argc) + offs_s = argv[arg_idx++]; + else + offs_s = NULL; + + /* Third non-option argument */ + if (arg_idx < argc) + count_s = argv[arg_idx++]; + else + count_s = NULL; + +} + +int main(int argc, char *argv[]) +{ + int fd, request; + struct mtd_info_user mtdInfo; + struct erase_info_user mtdLockInfo; + long count; + int ret = 0; + + process_args(argc, argv); + + /* Get the device info to compare to command line sizes */ + fd = open(dev, O_RDWR); + if (fd < 0) + sys_errmsg_die("could not open: %s", dev); + + if (ioctl(fd, MEMGETINFO, &mtdInfo)) + sys_errmsg_die("could not get mtd info: %s", dev); + + /* Make sure user options are valid */ + if (offs_s) { + mtdLockInfo.start = simple_strtol(offs_s, &ret); + if (ret) + errmsg_die("bad offset"); + } else { + mtdLockInfo.start = 0; + } + if (mtdLockInfo.start >= mtdInfo.size) + errmsg_die("%#x is beyond device size %#x", + mtdLockInfo.start, mtdInfo.size); + + if (count_s) { + count = simple_strtol(count_s, &ret); + if (ret) + errmsg_die("bad count"); + if (count == -1) + mtdLockInfo.length = mtdInfo.size; + else + mtdLockInfo.length = mtdInfo.erasesize * count; + } else { + mtdLockInfo.length = mtdInfo.size; + } + if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size) + errmsg_die("range is more than device supports: %#x + %#x > %#x", + mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size); + + /* Finally do the operation */ + switch (req) { + case REQUEST_LOCK: + request = MEMLOCK; + break; + case REQUEST_UNLOCK: + request = MEMUNLOCK; + break; + case REQUEST_ISLOCKED: + request = MEMISLOCKED; + break; + default: + errmsg_die("unknown request type: %d", req); + break; + } + ret = ioctl(fd, request, &mtdLockInfo); + if (ret < 0) + sys_errmsg_die("could not %s device: %s\n", + flash_msg[req], dev); + + if (req == REQUEST_ISLOCKED) { + printf("Device: %s\n", dev); + printf("Start: %#0x\n", mtdLockInfo.start); + printf("Len: %#0x\n", mtdLockInfo.length); + printf("Lock status: %s\n", ret ? "locked" : "unlocked"); + printf("Return code: %d\n", ret); + } + + return 0; +} |