aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Cooper <alcooperx@gmail.com>2016-06-07 16:35:46 -0400
committerChris Ball <chris@printf.net>2016-06-07 16:55:09 -0400
commit0ca049f25191c32323ba25a3cfd542b9fdefb473 (patch)
tree977099407a4aaa5090cd12ec9d9139b6288cee15
parentbb779acfc385d135b32a6998c1d1fceef0491400 (diff)
downloadmmc-utils-0ca049f25191c32323ba25a3cfd542b9fdefb473.tar.gz
mmc-utils: Add ability to configure write protect on an eMMC device
Add commands to get and set write protect modes for the specified areas of the user partition. The ability to set permanent write protect is #ifdef'd with "DANGEROUS_COMMANDS_ENABLED" because it has the ability to make the eMMC device and possibly the system permanently unusable. Signed-off-by: Al Cooper <alcooperx@gmail.com> Signed-off-by: Chris Ball <chris@printf.net>
-rw-r--r--mmc.c26
-rw-r--r--mmc.h6
-rw-r--r--mmc_cmds.c298
-rw-r--r--mmc_cmds.h6
4 files changed, 319 insertions, 17 deletions
diff --git a/mmc.c b/mmc.c
index d740535..83d8c17 100644
--- a/mmc.c
+++ b/mmc.c
@@ -58,14 +58,28 @@ static struct Command commands[] = {
"Print extcsd data from <device>.",
NULL
},
- { do_writeprotect_get, -1,
- "writeprotect get", "<device>\n"
- "Determine the eMMC writeprotect status of <device>.",
+ { do_writeprotect_boot_get, -1,
+ "writeprotect boot get", "<device>\n"
+ "Print the boot partitions write protect status for <device>.",
NULL
},
- { do_writeprotect_set, -1,
- "writeprotect set", "<device>\n"
- "Set the eMMC writeprotect status of <device>.\nThis sets the eMMC to be write-protected until next boot.",
+ { do_writeprotect_boot_set, -1,
+ "writeprotect boot set", "<device>\n"
+ "Set the boot partitions write protect status for <device>.\nThis sets the eMMC boot partitions to be write-protected until\nthe next boot.",
+ NULL
+ },
+ { do_writeprotect_user_set, -4,
+ "writeprotect user set", "<type>" "<start block>" "<blocks>" "<device>\n"
+#ifdef DANGEROUS_COMMANDS_ENABLED
+ "Set the write protect configuration for the specified region\nof the user area for <device>.\n<type> must be \"none|temp|pwron|perm\".\n \"none\" - Clear temporary write protection.\n \"temp\" - Set temporary write protection.\n \"pwron\" - Set write protection until the next poweron.\n \"perm\" - Set permanent write protection.\n<start block> specifies the first block of the protected area.\n<blocks> specifies the size of the protected area in blocks.\nNOTE! The area must start and end on Write Protect Group\nboundries, Use the \"writeprotect user get\" command to get the\nWrite Protect Group size.\nNOTE! \"perm\" is a one-time programmable (unreversible) change.",
+#else
+ "Set the write protect configuration for the specified region\nof the user area for <device>.\n<type> must be \"none|temp|pwron\".\n \"none\" - Clear temporary write protection.\n \"temp\" - Set temporary write protection.\n \"pwron\" - Set write protection until the next poweron.\n<start block> specifies the first block of the protected area.\n<blocks> specifies the size of the protected area in blocks.\nNOTE! The area must start and end on Write Protect Group\nboundries, Use the \"writeprotect user get\" command to get the\nWrite Protect Group size.",
+#endif /* DANGEROUS_COMMANDS_ENABLED */
+ NULL
+ },
+ { do_writeprotect_user_get, -1,
+ "writeprotect user get", "<device>\n"
+ "Print the user areas write protect configuration for <device>.",
NULL
},
{ do_disable_512B_emulation, -1,
diff --git a/mmc.h b/mmc.h
index 61da21f..8c77fd5 100644
--- a/mmc.h
+++ b/mmc.h
@@ -39,6 +39,9 @@
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */
#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */
+#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */
+#define MMC_CLEAR_WRITE_PROT 29 /* ac [31:0] data addr R1b */
+#define MMC_SEND_WRITE_PROT_TYPE 31 /* ac [31:0] data addr R1 */
/*
* EXT_CSD fields
@@ -62,6 +65,8 @@
#define EXT_CSD_CACHE_SIZE_1 250
#define EXT_CSD_CACHE_SIZE_0 249
#define EXT_CSD_BOOT_INFO 228 /* R/W */
+#define EXT_CSD_HC_ERASE_GRP_SIZE 224
+#define EXT_CSD_HC_WP_GRP_SIZE 221
#define EXT_CSD_SEC_COUNT_3 215
#define EXT_CSD_SEC_COUNT_2 214
#define EXT_CSD_SEC_COUNT_1 213
@@ -73,6 +78,7 @@
#define EXT_CSD_BOOT_BUS_CONDITIONS 177
#define EXT_CSD_ERASE_GROUP_DEF 175
#define EXT_CSD_BOOT_WP 173
+#define EXT_CSD_USER_WP 171
#define EXT_CSD_FW_CONFIG 169 /* R/W */
#define EXT_CSD_WR_REL_SET 167
#define EXT_CSD_WR_REL_PARAM 166
diff --git a/mmc_cmds.c b/mmc_cmds.c
index 0e84d44..3627610 100644
--- a/mmc_cmds.c
+++ b/mmc_cmds.c
@@ -32,11 +32,29 @@
#include <errno.h>
#include <stdint.h>
#include <assert.h>
+#include <linux/fs.h>
#include "mmc.h"
#include "mmc_cmds.h"
#include "3rdparty/hmac_sha/hmac_sha2.h"
+#define WP_BLKS_PER_QUERY 32
+
+#define USER_WP_PERM_PSWD_DIS 0x80
+#define USER_WP_CD_PERM_WP_DIS 0x40
+#define USER_WP_US_PERM_WP_DIS 0x10
+#define USER_WP_US_PWR_WP_DIS 0x08
+#define USER_WP_US_PERM_WP_EN 0x04
+#define USER_WP_US_PWR_WP_EN 0x01
+#define USER_WP_CLEAR (USER_WP_US_PERM_WP_DIS | USER_WP_US_PWR_WP_DIS \
+ | USER_WP_US_PERM_WP_EN | USER_WP_US_PWR_WP_EN)
+
+#define WPTYPE_NONE 0
+#define WPTYPE_TEMP 1
+#define WPTYPE_PWRON 2
+#define WPTYPE_PERM 3
+
+
int read_extcsd(int fd, __u8 *ext_csd)
{
int ret = 0;
@@ -98,7 +116,69 @@ int send_status(int fd, __u32 *response)
return ret;
}
-void print_writeprotect_status(__u8 *ext_csd)
+static __u32 get_size_in_blks(int fd)
+{
+ int res;
+ int size;
+
+ res = ioctl(fd, BLKGETSIZE, &size);
+ if (res) {
+ fprintf(stderr, "Error getting device size, errno: %d\n",
+ errno);
+ perror("");
+ return -1;
+ }
+ return size;
+}
+
+static int set_write_protect(int fd, __u32 blk_addr, int on_off)
+{
+ int ret = 0;
+ struct mmc_ioc_cmd idata;
+
+ memset(&idata, 0, sizeof(idata));
+ idata.write_flag = 1;
+ if (on_off)
+ idata.opcode = MMC_SET_WRITE_PROT;
+ else
+ idata.opcode = MMC_CLEAR_WRITE_PROT;
+ idata.arg = blk_addr;
+ idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+
+ ret = ioctl(fd, MMC_IOC_CMD, &idata);
+ if (ret)
+ perror("ioctl");
+
+ return ret;
+}
+
+static int send_write_protect_type(int fd, __u32 blk_addr, __u64 *group_bits)
+{
+ int ret = 0;
+ struct mmc_ioc_cmd idata;
+ __u8 buf[8];
+ __u64 bits = 0;
+ int x;
+
+ memset(&idata, 0, sizeof(idata));
+ idata.write_flag = 0;
+ idata.opcode = MMC_SEND_WRITE_PROT_TYPE;
+ idata.blksz = 8,
+ idata.blocks = 1,
+ idata.arg = blk_addr;
+ idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ mmc_ioc_cmd_set_data(idata, buf);
+
+ ret = ioctl(fd, MMC_IOC_CMD, &idata);
+ if (ret)
+ perror("ioctl");
+ for (x = 0; x < sizeof(buf); x++)
+ bits |= (__u64)(buf[7 - x]) << (x * 8);
+ *group_bits = bits;
+ return ret;
+}
+
+static void print_writeprotect_boot_status(__u8 *ext_csd)
{
__u8 reg;
__u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
@@ -132,14 +212,28 @@ void print_writeprotect_status(__u8 *ext_csd)
}
}
-int do_writeprotect_get(int nargs, char **argv)
+static int get_wp_group_size_in_blks(__u8 *ext_csd, __u32 *size)
+{
+ __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
+
+ if ((ext_csd_rev < 5) || (ext_csd[EXT_CSD_ERASE_GROUP_DEF] == 0))
+ return 1;
+
+ *size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
+ ext_csd[EXT_CSD_HC_WP_GRP_SIZE] * 1024;
+ return 0;
+}
+
+
+int do_writeprotect_boot_get(int nargs, char **argv)
{
__u8 ext_csd[512];
int fd, ret;
char *device;
- CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
- exit(1));
+ CHECK(nargs != 2,
+ "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n",
+ exit(1));
device = argv[1];
@@ -155,19 +249,20 @@ int do_writeprotect_get(int nargs, char **argv)
exit(1);
}
- print_writeprotect_status(ext_csd);
+ print_writeprotect_boot_status(ext_csd);
return ret;
}
-int do_writeprotect_set(int nargs, char **argv)
+int do_writeprotect_boot_set(int nargs, char **argv)
{
__u8 ext_csd[512], value;
int fd, ret;
char *device;
- CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
- exit(1));
+ CHECK(nargs != 2,
+ "Usage: mmc writeprotect boot set </path/to/mmcblkX>\n",
+ exit(1));
device = argv[1];
@@ -196,6 +291,191 @@ int do_writeprotect_set(int nargs, char **argv)
return ret;
}
+static char *prot_desc[] = {
+ "No",
+ "Temporary",
+ "Power-on",
+ "Permanent"
+};
+
+static void print_wp_status(__u32 wp_sizeblks, __u32 start_group,
+ __u32 end_group, int rptype)
+{
+ printf("Write Protect Groups %d-%d (Blocks %d-%d), ",
+ start_group, end_group,
+ start_group * wp_sizeblks, ((end_group + 1) * wp_sizeblks) - 1);
+ printf("%s Write Protection\n", prot_desc[rptype]);
+}
+
+
+int do_writeprotect_user_get(int nargs, char **argv)
+{
+ __u8 ext_csd[512];
+ int fd, ret;
+ char *device;
+ int x;
+ int y = 0;
+ __u32 wp_sizeblks;
+ __u32 dev_sizeblks;
+ __u32 cnt;
+ __u64 bits;
+ __u32 wpblk;
+ __u32 last_wpblk = 0;
+ __u32 prot;
+ __u32 last_prot = -1;
+ int remain;
+
+ CHECK(nargs != 2,
+ "Usage: mmc writeprotect user get </path/to/mmcblkX>\n",
+ exit(1));
+
+ device = argv[1];
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+ ret = read_extcsd(fd, ext_csd);
+ if (ret) {
+ fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
+ exit(1);
+ }
+
+ ret = get_wp_group_size_in_blks(ext_csd, &wp_sizeblks);
+ if (ret)
+ exit(1);
+ printf("Write Protect Group size in blocks/bytes: %d/%d\n",
+ wp_sizeblks, wp_sizeblks * 512);
+ dev_sizeblks = get_size_in_blks(fd);
+ cnt = dev_sizeblks / wp_sizeblks;
+ for (x = 0; x < cnt; x += WP_BLKS_PER_QUERY) {
+ ret = send_write_protect_type(fd, x * wp_sizeblks, &bits);
+ if (ret)
+ break;
+ remain = cnt - x;
+ if (remain > WP_BLKS_PER_QUERY)
+ remain = WP_BLKS_PER_QUERY;
+ for (y = 0; y < remain; y++) {
+ prot = (bits >> (y * 2)) & 0x3;
+ if (prot != last_prot) {
+ /* not first time */
+ if (last_prot != -1) {
+ wpblk = x + y;
+ print_wp_status(wp_sizeblks,
+ last_wpblk,
+ wpblk - 1,
+ last_prot);
+ last_wpblk = wpblk;
+ }
+ last_prot = prot;
+ }
+ }
+ }
+ if (last_wpblk != (x + y - 1))
+ print_wp_status(wp_sizeblks, last_wpblk, cnt - 1, last_prot);
+
+ return ret;
+}
+
+int do_writeprotect_user_set(int nargs, char **argv)
+{
+ __u8 ext_csd[512];
+ int fd, ret;
+ char *device;
+ int blk_start;
+ int blk_cnt;
+ __u32 wp_blks;
+ __u8 user_wp;
+ int x;
+ int wptype;
+
+ if (nargs != 5)
+ goto usage;
+ device = argv[4];
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+ if (!strcmp(argv[1], "none")) {
+ wptype = WPTYPE_NONE;
+ } else if (!strcmp(argv[1], "temp")) {
+ wptype = WPTYPE_TEMP;
+ } else if (!strcmp(argv[1], "pwron")) {
+ wptype = WPTYPE_PWRON;
+#ifdef DANGEROUS_COMMANDS_ENABLED
+ } else if (!strcmp(argv[1], "perm")) {
+ wptype = WPTYPE_PERM;
+#endif /* DANGEROUS_COMMANDS_ENABLED */
+ } else {
+ fprintf(stderr, "Error, invalid \"type\"\n");
+ goto usage;
+ }
+ ret = read_extcsd(fd, ext_csd);
+ if (ret) {
+ fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
+ exit(1);
+ }
+ ret = get_wp_group_size_in_blks(ext_csd, &wp_blks);
+ if (ret) {
+ fprintf(stderr, "Operation not supported for this device\n");
+ exit(1);
+ }
+ blk_start = strtol(argv[2], NULL, 0);
+ blk_cnt = strtol(argv[3], NULL, 0);
+ if ((blk_start % wp_blks) || (blk_cnt % wp_blks)) {
+ fprintf(stderr, "<start block> and <blocks> must be a ");
+ fprintf(stderr, "multiple of the Write Protect Group (%d)\n",
+ wp_blks);
+ exit(1);
+ }
+ if (wptype != WPTYPE_NONE) {
+ user_wp = ext_csd[EXT_CSD_USER_WP];
+ user_wp &= ~USER_WP_CLEAR;
+ switch (wptype) {
+ case WPTYPE_TEMP:
+ break;
+ case WPTYPE_PWRON:
+ user_wp |= USER_WP_US_PWR_WP_EN;
+ break;
+ case WPTYPE_PERM:
+ user_wp |= USER_WP_US_PERM_WP_EN;
+ break;
+ }
+ if (user_wp != ext_csd[EXT_CSD_USER_WP]) {
+ ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
+ if (ret) {
+ fprintf(stderr, "Error setting EXT_CSD\n");
+ exit(1);
+ }
+ }
+ }
+ for (x = 0; x < blk_cnt; x += wp_blks) {
+ ret = set_write_protect(fd, blk_start + x,
+ wptype != WPTYPE_NONE);
+ if (ret) {
+ fprintf(stderr,
+ "Could not set write protect for %s\n", device);
+ exit(1);
+ }
+ }
+ if (wptype != WPTYPE_NONE) {
+ ret = write_extcsd_value(fd, EXT_CSD_USER_WP,
+ ext_csd[EXT_CSD_USER_WP]);
+ if (ret) {
+ fprintf(stderr, "Error restoring EXT_CSD\n");
+ exit(1);
+ }
+ }
+ return ret;
+
+usage:
+ fprintf(stderr,
+ "Usage: mmc writeprotect user set <type><start block><blocks><device>\n");
+ exit(1);
+}
+
int do_disable_512B_emulation(int nargs, char **argv)
{
__u8 ext_csd[512], native_sector_size, data_sector_size, wr_rel_param;
@@ -1278,7 +1558,7 @@ int do_read_extcsd(int nargs, char **argv)
printf("High-density erase group definition"
" [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
- print_writeprotect_status(ext_csd);
+ print_writeprotect_boot_status(ext_csd);
if (ext_csd_rev >= 5) {
/* A441]: reserved [172] */
diff --git a/mmc_cmds.h b/mmc_cmds.h
index 67f5471..9d3246c 100644
--- a/mmc_cmds.h
+++ b/mmc_cmds.h
@@ -20,8 +20,10 @@
/* mmc_cmds.c */
int do_read_extcsd(int nargs, char **argv);
int do_write_extcsd(int nargs, char **argv);
-int do_writeprotect_get(int nargs, char **argv);
-int do_writeprotect_set(int nargs, char **argv);
+int do_writeprotect_boot_get(int nargs, char **argv);
+int do_writeprotect_boot_set(int nargs, char **argv);
+int do_writeprotect_user_get(int nargs, char **argv);
+int do_writeprotect_user_set(int nargs, char **argv);
int do_disable_512B_emulation(int nargs, char **argv);
int do_write_boot_en(int nargs, char **argv);
int do_boot_bus_conditions_set(int nargs, char **argv);