linux-mmc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC
@ 2014-03-05 23:44 Al Cooper
  2014-03-05 23:44 ` [PATCH V3 1/7] mmc: lock: Use the kernel "KEYS" subsystem to get a card's password Al Cooper
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

Changed for V3:
The previously submitted patches are apparently being used and
uncovered an issue where hotplug scripts are trying to read
the block device of a locked card and getting read/write
errors. This has been solved by preventing the block layer from
coming up until the card is unlocked. Also added more detail
to this cover letter on the use of the KEYS subsystem.

Changed for V2:
The V2 changes were not functional and were just general cleanup.
  - Use stub functions to reduce the number of CONFIG ifdefs.
  - Add static to a few functions that were local.
  - Use pr_warn instead of pr_warning.
  - Improve a few variable names and messages.

This set of patches adds support for password protected locking
and unlocking of MMC and SD devices. It uses the LOCK/UNLOCK command
(CMD42) available in both the MMC and SD command sets.

Some of this code was based on a patch set submitted in 2006 by
Anderson Briglia "Add MMC Password Protection (lock/unlock)". This
patch set never made it into mainline. 

By default, a card with no password assigned is always in "unlocked"
state. After password assignment, in the next power cycle the card
switches to a "locked" state where only the "basic" and "lock card"
command classes are accepted by the card. Only after unlocking it with
the correct password can the card be used for normal operations like
block I/O.

Password management and caching is done through the "Kernel Key
Retention Service" mechanism and the sysfs filesystem. The KEY
name for a specific SD/MMC device is the devices "cid" because it
will be unique for every card. The use of the kernel KEYS subsystem
requires support from user space and needs to have the keyutils
package included in the rootfs. A user space script or helper app
will need to be added that will lookup the password based on cid
and use the "keyctl" utility to instantiate the key. Two new sysfs
attributes were added. The "lock" attribute is used to lock, unlock,
assign a password, clear a password and force erase a card. The
"unlock_retry" attribute is used to retry an unlock that failed
during boot because the rootfs was not yet available to setup the
keys password. The "unlock_retry" attribute is a NOP for unlocked
cards, so user space init processing can write a 1 to all instances
found in the sysfs once the rootfs with keyutils functionality is
available.

Al Cooper (7):
  mmc: lock: Use the kernel "KEYS" subsystem to get a card's password
  mmc: lock: Add low level LOCK_UNLOCK command
  mmc: lock: Add function to unlock a password locked card
  mmc: lock: Add card lock/unlock maintenance commands
  mmc: lock: Change SD init functionality to handle locked SD cards
  mmc: lock: Prevent block device from coming up for locked cards.
  mmc: lock: Change MMC init to handle locked cards.

 drivers/mmc/card/block.c   |  10 ++++
 drivers/mmc/core/Kconfig   |   8 +++
 drivers/mmc/core/core.c    | 131 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/core.h    |  15 ++++-
 drivers/mmc/core/mmc.c     | 123 ++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc_ops.c | 109 +++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc_ops.h |  13 +++++
 drivers/mmc/core/sd.c      | 138 +++++++++++++++++++++++++++++++++------------
 include/linux/mmc/card.h   |   6 ++
 9 files changed, 515 insertions(+), 38 deletions(-)

-- 
1.8.1.3


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH V3 1/7] mmc: lock: Use the kernel "KEYS" subsystem to get a card's password
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  2014-03-05 23:44 ` [PATCH V3 2/7] mmc: lock: Add low level LOCK_UNLOCK command Al Cooper
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

Use the kernel "KEYS" subsystem to get a password for a card based on
the card's CID. This code was based on a patch set submitted by
Anderson Briglia in 2006.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/core/Kconfig |   8 ++++
 drivers/mmc/core/core.c  | 105 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/core.h  |  11 ++++-
 3 files changed, 123 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 269d072..b0ba79d 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -26,3 +26,11 @@ config MMC_CLKGATE
 	  support handling this in order for it to be of any use.
 
 	  If unsure, say N.
+
+config MMC_LOCK
+	bool "MMC/SD password based card lock/unlock"
+	select KEYS
+	help
+	  This will add the ability to lock/unlock SD and MMC cards.
+
+	  If unsure, say N.
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 098374b..8fc8c11 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -29,6 +29,7 @@
 #include <linux/random.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/key-type.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -2712,6 +2713,104 @@ void mmc_init_context_info(struct mmc_host *host)
 	init_waitqueue_head(&host->context_info.wait);
 }
 
+#ifdef CONFIG_MMC_LOCK
+
+static int mmc_key_instantiate(struct key *key,
+			       struct key_preparsed_payload *prep)
+{
+	char *payload;
+
+	if (prep->datalen <= 0 || prep->datalen > MMC_PASSWORD_MAX ||
+	    !prep->data) {
+		pr_warn("Invalid data\n");
+		return  -EINVAL;
+	}
+
+	payload = kmalloc(prep->datalen, GFP_KERNEL);
+	if (!payload)
+		return -ENOMEM;
+	memcpy(payload, prep->data, prep->datalen);
+	key->payload.data = payload;
+	key->datalen = prep->datalen;
+	return 0;
+}
+
+static int mmc_key_match(const struct key *key, const void *description)
+{
+	pr_debug("mmc_key_match: %s, %s\n",
+		 key->description, (char *)description);
+	return strcmp(key->description, description) == 0;
+}
+
+/*
+ * dispose of the data dangling from the corpse of a mmc key
+ */
+static void mmc_key_destroy(struct key *key)
+{
+	kfree(key->payload.data);
+}
+
+struct key_type key_type_mmc = {
+	.name		= "mmc",
+	.instantiate	= mmc_key_instantiate,
+	.match		= mmc_key_match,
+	.destroy	= mmc_key_destroy,
+};
+
+int mmc_get_password(struct mmc_card *card, struct mmc_password *password)
+{
+	struct key *mmc_key;
+	char key_desc[(sizeof(card->raw_cid) * 2) + 1];
+
+	/* Use the CID to uniquely identify the card */
+	snprintf(key_desc, sizeof(key_desc), "%08x%08x%08x%08x",
+		 card->raw_cid[0], card->raw_cid[1],
+		 card->raw_cid[2], card->raw_cid[3]);
+
+	mmc_key = request_key(&key_type_mmc, key_desc,
+			      "password");
+	if (IS_ERR(mmc_key)) {
+		dev_warn(&card->dev, "Error, request_key %ld\n",
+			 PTR_ERR(mmc_key));
+		return PTR_ERR(mmc_key);
+	}
+	dev_dbg(&card->dev, "Found matching key\n");
+	memcpy(&password->password, mmc_key->payload.data,
+	       mmc_key->datalen);
+	password->length = mmc_key->datalen;
+	key_put(mmc_key);
+
+	return 0;
+}
+
+static inline int mmc_register_key_type(void)
+{
+	return register_key_type(&key_type_mmc);
+}
+
+static inline void mmc_unregister_key_type(void)
+{
+	unregister_key_type(&key_type_mmc);
+}
+
+#else /* CONFIG_MMC_LOCK */
+
+int mmc_get_password(struct mmc_card *card, struct mmc_password *password)
+{
+	return -ENOKEY;
+}
+
+static inline int mmc_register_key_type(void)
+{
+	return 0;
+}
+
+static inline void mmc_unregister_key_type(void)
+{
+}
+
+#endif /* CONFIG_MMC_LOCK */
+
 static int __init mmc_init(void)
 {
 	int ret;
@@ -2732,8 +2831,13 @@ static int __init mmc_init(void)
 	if (ret)
 		goto unregister_host_class;
 
+	ret = mmc_register_key_type();
+	if (ret)
+		goto unregister_sdio_bus;
 	return 0;
 
+unregister_sdio_bus:
+	sdio_unregister_bus();
 unregister_host_class:
 	mmc_unregister_host_class();
 unregister_bus:
@@ -2746,6 +2850,7 @@ destroy_workqueue:
 
 static void __exit mmc_exit(void)
 {
+	mmc_unregister_key_type();
 	sdio_unregister_bus();
 	mmc_unregister_host_class();
 	mmc_unregister_bus();
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 443a584..11a7e01 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -81,5 +81,14 @@ void mmc_add_card_debugfs(struct mmc_card *card);
 void mmc_remove_card_debugfs(struct mmc_card *card);
 
 void mmc_init_context_info(struct mmc_host *host);
-#endif
 
+/* Lock/Unlock functionality */
+#define MMC_PASSWORD_MAX 16
+struct mmc_password {
+	char password[MMC_PASSWORD_MAX];
+	int length;
+};
+int mmc_unlock_card(struct mmc_card *card);
+int mmc_get_password(struct mmc_card *card, struct mmc_password *password);
+
+#endif
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V3 2/7] mmc: lock: Add low level LOCK_UNLOCK command
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
  2014-03-05 23:44 ` [PATCH V3 1/7] mmc: lock: Use the kernel "KEYS" subsystem to get a card's password Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  2014-03-05 23:44 ` [PATCH V3 3/7] mmc: lock: Add function to unlock a password locked card Al Cooper
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

Add support for the LOCK_UNLOCK command. This command can lock,
unlock, set password, clear password and force erase SD and
MMC cards.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/core/mmc_ops.c | 109 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc_ops.h |  13 ++++++
 include/linux/mmc/card.h   |   5 +++
 3 files changed, 127 insertions(+)

diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index e5b5eeb..1df8516 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -13,6 +13,8 @@
 #include <linux/export.h>
 #include <linux/types.h>
 #include <linux/scatterlist.h>
+#include <linux/key.h>
+#include <linux/err.h>
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
@@ -637,3 +639,110 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
 
 	return 0;
 }
+
+#ifdef CONFIG_MMC_LOCK
+/**
+ *	mmc_lock_unlock - send LOCK_UNLOCK command to a specific card.
+ *	@card: card to which the LOCK_UNLOCK command should be sent
+ *	@key: key containing the MMC password
+ *	@mode: LOCK_UNLOCK mode
+ *
+ */
+int mmc_lock_unlock(struct mmc_card *card, struct mmc_password *password,
+		    int mode)
+{
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_data data;
+	struct scatterlist sg;
+	unsigned long erase_timeout;
+	int err;
+	u8 *data_buf;
+	int buffer_size;
+
+	buffer_size = 512;
+	data_buf = kzalloc(buffer_size, GFP_KERNEL);
+	if (!data_buf)
+		return -ENOMEM;
+	data_buf[0] |= mode;
+	if (!(mode & MMC_LOCK_MODE_ERASE)) {
+		data_buf[1] = password->length;
+		memcpy(data_buf + 2, password->password, password->length);
+	}
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	cmd.opcode = MMC_SET_BLOCKLEN;
+	cmd.arg = buffer_size;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+	if (err)
+		goto out;
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	cmd.opcode = MMC_LOCK_UNLOCK;
+	cmd.arg = 0;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	memset(&data, 0, sizeof(struct mmc_data));
+	mmc_set_data_timeout(&data, card);
+	data.blksz = buffer_size;
+	data.blocks = 1;
+	data.flags = MMC_DATA_WRITE;
+	data.sg = &sg;
+	data.sg_len = 1;
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+
+	sg_init_one(&sg, data_buf, buffer_size);
+	mmc_wait_for_req(card->host, &mrq);
+	if (cmd.error) {
+		err = cmd.error;
+		goto out;
+	}
+	if (data.error) {
+		err = data.error;
+		goto out;
+	}
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+	cmd.opcode = MMC_SEND_STATUS;
+	cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+	/* set timeout for forced erase operation to 3 min. (see MMC spec) */
+	erase_timeout = jiffies + 180 * HZ;
+	do {
+		/*
+		 * we cannot use "retries" here because the
+		 * R1_LOCK_UNLOCK_FAILED bit is cleared by subsequent reads to
+		 * the status register, hiding the error condition
+		 */
+		err = mmc_wait_for_cmd(card->host, &cmd, 0);
+		if (err)
+			break;
+		/* the other modes don't need timeout checking */
+		if (!(mode & MMC_LOCK_MODE_ERASE))
+			continue;
+		if (time_after(jiffies, erase_timeout) &&
+		    !(cmd.resp[0] & R1_READY_FOR_DATA)) {
+			dev_err(&card->dev, "forced erase timed out\n");
+			err = -ETIMEDOUT;
+			break;
+		}
+	} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+	if (cmd.resp[0] & R1_LOCK_UNLOCK_FAILED) {
+		dev_dbg(&card->dev, "LOCK_UNLOCK operation failed\n");
+		err = -EIO;
+	}
+	if (cmd.resp[0] & R1_CARD_IS_LOCKED)
+		mmc_card_set_locked(card);
+	else
+		mmc_card_clear_locked(card);
+
+out:
+	kfree(data_buf);
+
+	return err;
+}
+#endif /* CONFIG_MMC_LOCK */
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 80ae9f4..5fe50e0 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -12,6 +12,15 @@
 #ifndef _MMC_MMC_OPS_H
 #define _MMC_MMC_OPS_H
 
+/*
+ * MMC_LOCK_UNLOCK modes
+ */
+#define MMC_LOCK_MODE_ERASE	(1<<3)
+#define MMC_LOCK_MODE_LOCK	(1<<2)
+#define MMC_LOCK_MODE_UNLOCK	(0<<2)
+#define MMC_LOCK_MODE_CLR_PWD	(1<<1)
+#define MMC_LOCK_MODE_SET_PWD	(1<<0)
+
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
 int mmc_go_idle(struct mmc_host *host);
@@ -26,6 +35,10 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 int mmc_bus_test(struct mmc_card *card, u8 bus_width);
 int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
+#ifdef CONFIG_MMC_LOCK
+int mmc_lock_unlock(struct mmc_card *card, struct mmc_password *password,
+		    int mode);
+#endif
 
 #endif
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index b730272..6846369 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -259,6 +259,7 @@ struct mmc_card {
 #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200 mode */
 #define MMC_STATE_DOING_BKOPS	(1<<10)		/* card is doing BKOPS */
 #define MMC_STATE_SUSPENDED	(1<<11)		/* card is suspended */
+#define MMC_STATE_LOCKED	(1<<12)		/* card is currently locked */
 	unsigned int		quirks; 	/* card quirks */
 #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize */
@@ -427,6 +428,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_removed(c)	((c) && ((c)->state & MMC_CARD_REMOVED))
 #define mmc_card_doing_bkops(c)	((c)->state & MMC_STATE_DOING_BKOPS)
 #define mmc_card_suspended(c)	((c)->state & MMC_STATE_SUSPENDED)
+#define mmc_card_locked(c)	((c)->state & MMC_STATE_LOCKED)
+#define mmc_card_lockable(c)	((c)->csd.cmdclass & CCC_LOCK_CARD)
 
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -441,6 +444,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_clr_doing_bkops(c)	((c)->state &= ~MMC_STATE_DOING_BKOPS)
 #define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
 #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
+#define mmc_card_set_locked(c)	((c)->state |= MMC_STATE_LOCKED)
+#define mmc_card_clear_locked(c) ((c)->state &= ~MMC_STATE_LOCKED)
 
 /*
  * Quirk add/remove for MMC products.
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V3 3/7] mmc: lock: Add function to unlock a password locked card
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
  2014-03-05 23:44 ` [PATCH V3 1/7] mmc: lock: Use the kernel "KEYS" subsystem to get a card's password Al Cooper
  2014-03-05 23:44 ` [PATCH V3 2/7] mmc: lock: Add low level LOCK_UNLOCK command Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  2014-03-05 23:44 ` [PATCH V3 4/7] mmc: lock: Add card lock/unlock maintenance commands Al Cooper
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

This function will try to get a password for the card and use the
password to unlock it. It will leave the card state flag set
appropriately.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/core/core.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 8fc8c11..978e4e5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2793,6 +2793,27 @@ static inline void mmc_unregister_key_type(void)
 	unregister_key_type(&key_type_mmc);
 }
 
+int mmc_unlock_card(struct mmc_card *card)
+{
+	int stat;
+	struct mmc_password password;
+
+	mmc_card_set_locked(card);
+	stat = mmc_get_password(card, &password);
+	if (stat) {
+		pr_warn("%s: Cannot find matching key\n",
+			mmc_hostname(card->host));
+		return stat;
+	}
+	stat = mmc_lock_unlock(card, &password, MMC_LOCK_MODE_UNLOCK);
+	if (stat)
+		pr_warn("%s: Password failed to unlock card\n",
+			mmc_hostname(card->host));
+	else
+		mmc_card_clear_locked(card);
+	return stat;
+}
+
 #else /* CONFIG_MMC_LOCK */
 
 int mmc_get_password(struct mmc_card *card, struct mmc_password *password)
@@ -2809,6 +2830,11 @@ static inline void mmc_unregister_key_type(void)
 {
 }
 
+int mmc_unlock_card(struct mmc_card *card)
+{
+	return -ENOKEY;
+}
+
 #endif /* CONFIG_MMC_LOCK */
 
 static int __init mmc_init(void)
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V3 4/7] mmc: lock: Add card lock/unlock maintenance commands
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
                   ` (2 preceding siblings ...)
  2014-03-05 23:44 ` [PATCH V3 3/7] mmc: lock: Add function to unlock a password locked card Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  2014-03-05 23:44 ` [PATCH V3 5/7] mmc: lock: Change SD init functionality to handle locked SD cards Al Cooper
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

Create a sysfs interface that allows a user to manage an inserted
cards lock state. The sysfs attribute "lock" will be added to the
device's sysfs directory. The following commands are supported:
"setpw"   - Set the cards password
"clearpw" - Clear the cards password
"lock"    - Lock the card
"unlock"  - Unlock the card
"erase"   - Force erase the card, clear the password and unlock it
Commands that require a password will request the password through
the kernels KEYS subsystem.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/core/mmc.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 98e9eb0..3703cd1 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -655,6 +655,84 @@ out:
 	return err;
 }
 
+#ifdef CONFIG_MMC_LOCK
+
+ssize_t mmc_lock_show(struct device *dev, struct device_attribute *att,
+			  char *buf)
+{
+	struct mmc_card *card = mmc_dev_to_card(dev);
+
+	if (!mmc_card_lockable(card))
+		return sprintf(buf, "unsupported\n");
+	else
+		return sprintf(buf, "%slocked\n", mmc_card_locked(card) ?
+			       "" : "un");
+}
+
+
+static struct lock_cmd {
+	const char *name;
+	int io_cmd;
+	int locked_required;
+	int need_pw;
+} lock_cmds[] = {
+	{ "erase" , MMC_LOCK_MODE_ERASE,  true,  false },
+	{ "clrpw", MMC_LOCK_MODE_CLR_PWD, false, true },
+	{ "setpw", MMC_LOCK_MODE_SET_PWD, false, true },
+	{ "lock", MMC_LOCK_MODE_LOCK, false,  true },
+	{ "unlock", MMC_LOCK_MODE_UNLOCK, true,  true },
+};
+
+/*
+ * implement MMC password functions: force erase, set password,
+ * clear password, lock and unlock.
+ */
+ssize_t mmc_lock_store(struct device *dev, struct device_attribute *att,
+			   const char *data, size_t len)
+{
+	struct mmc_card *card = mmc_dev_to_card(dev);
+	int res = -EINVAL;
+	int x;
+	struct mmc_password password;
+
+	mmc_claim_host(card->host);
+	if (!mmc_card_lockable(card))
+		goto out;
+	for (x = 0; x < ARRAY_SIZE(lock_cmds); x++) {
+		if (sysfs_streq(data, lock_cmds[x].name))
+			break;
+	}
+	if (x >= ARRAY_SIZE(lock_cmds))
+		goto out;
+
+	if ((lock_cmds[x].locked_required && !mmc_card_locked(card)) ||
+	    (!lock_cmds[x].locked_required && mmc_card_locked(card))) {
+		dev_warn(dev, "%s requires %slocked card\n",
+			 lock_cmds[x].name,
+			 lock_cmds[x].locked_required ? "" : "un");
+		goto out;
+	}
+	if (lock_cmds[x].need_pw) {
+		res = mmc_get_password(card, &password);
+		if (res)
+			goto out;
+	}
+	res = mmc_lock_unlock(card, &password, lock_cmds[x].io_cmd);
+out:
+	mmc_release_host(card->host);
+	if (res == 0)
+		return len;
+	else
+		return res;
+}
+
+static DEVICE_ATTR(lock, S_IWUSR | S_IRUGO,
+		   mmc_lock_show, mmc_lock_store);
+
+
+#endif /* CONFIG_MMC_LOCK */
+
+
 MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
 	card->raw_cid[2], card->raw_cid[3]);
 MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
@@ -692,6 +770,9 @@ static struct attribute *mmc_std_attrs[] = {
 	&dev_attr_enhanced_area_size.attr,
 	&dev_attr_raw_rpmb_size_mult.attr,
 	&dev_attr_rel_sectors.attr,
+#ifdef CONFIG_MMC_LOCK
+	&dev_attr_lock.attr,
+#endif /* CONFIG_MMC_LOCK */
 	NULL,
 };
 
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V3 5/7] mmc: lock: Change SD init functionality to handle locked SD cards
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
                   ` (3 preceding siblings ...)
  2014-03-05 23:44 ` [PATCH V3 4/7] mmc: lock: Add card lock/unlock maintenance commands Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  2014-03-05 23:44 ` [PATCH V3 6/7] mmc: lock: Prevent block device from coming up for locked cards Al Cooper
  2014-03-05 23:44 ` [PATCH V3 7/7] mmc: lock: Change MMC init to handle " Al Cooper
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

- Change mmc_sd_init_card() to check for a locked card and, if found,
try to get a password using the kernel KEYS subsystem, unlock the card
and continue. The unlock can fail due to a bad password, no password
or during boot when the rootfs that holds the password is not yet
available. To handle this, mmc_sd_init_card() will send just the early
init commands before trying to unlock and, on unlock failure, skip the
later init commands (which would fail on a locked card). mmc_sd_init_card()
will also handle the retry case, trigger via sysfs, where it will skip
the already issued early init commands, try again to unlock the card and
if successful issue the previously skipped later init commands.
These changes allow a card that failed unlock to still come up to the
point that the block device and sysfs for the device is created. This
allows the sysfs to be used to issue LOCK maintenance commands or to
trigger a retry, presumably after the password has be made available
by user space.

- Add sysfs attribute "unlock_retry" that will try again to unlock
and fully init the card.

- Add sysfs attribute "lock" to enable sysfs LOCK maintenance
commands

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/core/core.h  |   4 ++
 drivers/mmc/core/sd.c    | 138 ++++++++++++++++++++++++++++++++++-------------
 include/linux/mmc/card.h |   1 +
 3 files changed, 106 insertions(+), 37 deletions(-)

diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 11a7e01..4a55cae 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -90,5 +90,9 @@ struct mmc_password {
 };
 int mmc_unlock_card(struct mmc_card *card);
 int mmc_get_password(struct mmc_card *card, struct mmc_password *password);
+ssize_t mmc_lock_show(struct device *dev, struct device_attribute *att,
+		      char *buf);
+ssize_t mmc_lock_store(struct device *dev, struct device_attribute *att,
+		       const char *data, size_t len);
 
 #endif
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 692fdb1..33398c9 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -27,6 +27,9 @@
 #include "sd.h"
 #include "sd_ops.h"
 
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+			    struct mmc_card *oldcard);
+
 static const unsigned int tran_exp[] = {
 	10000,		100000,		1000000,	10000000,
 	0,		0,		0,		0
@@ -676,6 +679,39 @@ out:
 	return err;
 }
 
+#ifdef CONFIG_MMC_LOCK
+static ssize_t mmc_sd_unlock_retry_store(struct device *dev,
+					 struct device_attribute *att,
+					 const char *data, size_t len)
+{
+	int err;
+	struct mmc_card *card = mmc_dev_to_card(dev);
+	struct mmc_host *host = card->host;
+	BUG_ON(!card);
+	BUG_ON(!host);
+
+	mmc_claim_host(host);
+	if (!mmc_card_locked(card)) {
+		mmc_release_host(host);
+		return len;
+	}
+	err = mmc_sd_init_card(host, card->ocr, card);
+	mmc_release_host(host);
+	if (err)
+		return err;
+	device_release_driver(dev);
+	err = device_attach(dev);
+	if (err < 0)
+		return err;
+	return len;
+}
+
+static DEVICE_ATTR(lock, S_IWUSR | S_IRUGO,
+		   mmc_lock_show, mmc_lock_store);
+static DEVICE_ATTR(unlock_retry, S_IWUSR,
+		   NULL, mmc_sd_unlock_retry_store);
+#endif /* CONFIG_MMC_LOCK */
+
 MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
 	card->raw_cid[2], card->raw_cid[3]);
 MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
@@ -705,6 +741,10 @@ static struct attribute *sd_std_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_oemid.attr,
 	&dev_attr_serial.attr,
+#ifdef CONFIG_MMC_LOCK
+	&dev_attr_lock.attr,
+	&dev_attr_unlock_retry.attr,
+#endif /* CONFIG_MMC_LOCK */
 	NULL,
 };
 
@@ -924,56 +964,79 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 	int err;
 	u32 cid[4];
 	u32 rocr = 0;
+	u32 status;
 
 	BUG_ON(!host);
 	WARN_ON(!host->claimed);
 
-	err = mmc_sd_get_cid(host, ocr, cid, &rocr);
-	if (err)
-		return err;
-
-	if (oldcard) {
-		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
-			return -ENOENT;
-
+	/* Retry init of locked card */
+	if (oldcard && mmc_card_locked(oldcard)) {
 		card = oldcard;
+		oldcard = NULL;
+		rocr = card->raw_ocr;
 	} else {
-		/*
-		 * Allocate card structure.
-		 */
-		card = mmc_alloc_card(host, &sd_type);
-		if (IS_ERR(card))
-			return PTR_ERR(card);
+		err = mmc_sd_get_cid(host, ocr, cid, &rocr);
+		if (err)
+			return err;
+
+		if (oldcard) {
+			if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+				return -ENOENT;
+
+			card = oldcard;
+		} else {
+			/*
+			 * Allocate card structure.
+			 */
+			card = mmc_alloc_card(host, &sd_type);
+			if (IS_ERR(card))
+				return PTR_ERR(card);
 
 		card->ocr = ocr;
-		card->type = MMC_TYPE_SD;
-		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
-	}
+			card->type = MMC_TYPE_SD;
+			memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+		}
 
-	/*
-	 * For native busses:  get card RCA and quit open drain mode.
-	 */
-	if (!mmc_host_is_spi(host)) {
-		err = mmc_send_relative_addr(host, &card->rca);
-		if (err)
-			goto free_card;
-	}
+		/*
+		 * For native busses:  get card RCA and quit open drain mode.
+		 */
+		if (!mmc_host_is_spi(host)) {
+			err = mmc_send_relative_addr(host, &card->rca);
+			if (err)
+				goto free_card;
+		}
 
-	if (!oldcard) {
-		err = mmc_sd_get_csd(host, card);
-		if (err)
-			goto free_card;
+		if (!oldcard) {
+			err = mmc_sd_get_csd(host, card);
+			if (err)
+				goto free_card;
 
-		mmc_decode_cid(card);
+			mmc_decode_cid(card);
+		}
+
+		/*
+		 * Select card, as all following commands rely on that.
+		 */
+		if (!mmc_host_is_spi(host)) {
+			err = mmc_select_card(card);
+			if (err)
+				goto free_card;
+		}
 	}
 
-	/*
-	 * Select card, as all following commands rely on that.
-	 */
-	if (!mmc_host_is_spi(host)) {
-		err = mmc_select_card(card);
-		if (err)
-			goto free_card;
+	/* If card is locked, skip the rest of the init */
+	err = mmc_send_status(card, &status);
+	if (err)
+		goto free_card;
+	if (status & R1_CARD_IS_LOCKED) {
+		pr_info("%s: card is locked.\n", mmc_hostname(card->host));
+		err = mmc_unlock_card(card);
+		if (err != 0) {
+			pr_warn("%s: Card unlock failed.\n",
+				mmc_hostname(card->host));
+			card->raw_ocr = rocr;
+			goto locked_return;
+		}
 	}
 
 	err = mmc_sd_setup_card(host, card, oldcard != NULL);
@@ -1016,6 +1079,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
 		}
 	}
 
+locked_return:
 	host->card = card;
 	return 0;
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 6846369..bd4b6b9 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -285,6 +285,7 @@ struct mmc_card {
 	u32			raw_cid[4];	/* raw card CID */
 	u32			raw_csd[4];	/* raw card CSD */
 	u32			raw_scr[2];	/* raw card SCR */
+	u32			raw_ocr;	/* raw card OCR */
 	struct mmc_cid		cid;		/* card identification */
 	struct mmc_csd		csd;		/* card specific */
 	struct mmc_ext_csd	ext_csd;	/* mmc v4 extended card specific */
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V3 6/7] mmc: lock: Prevent block device from coming up for locked cards.
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
                   ` (4 preceding siblings ...)
  2014-03-05 23:44 ` [PATCH V3 5/7] mmc: lock: Change SD init functionality to handle locked SD cards Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  2014-03-05 23:44 ` [PATCH V3 7/7] mmc: lock: Change MMC init to handle " Al Cooper
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

A locked card will return an error for all reads/writes to the
block device, so prevent the block layer from coming up until
the card is unlocked.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/card/block.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7b5424f..a4b909d 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2377,6 +2377,16 @@ static int mmc_blk_probe(struct mmc_card *card)
 	struct mmc_blk_data *md, *part_md;
 	char cap_str[10];
 
+
+	/* if the card is locked, don't bring up the block layer because
+	 * all reads and writes will fail.
+	 */
+	if (mmc_card_locked(card)) {
+		pr_debug("%s: %s - Probe aborted because card is locked\n",
+			 mmc_hostname(card->host), __func__);
+		return -ENODEV;
+	}
+
 	/*
 	 * Check that the card supports the command class(es) we need.
 	 */
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V3 7/7] mmc: lock: Change MMC init to handle locked cards.
  2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
                   ` (5 preceding siblings ...)
  2014-03-05 23:44 ` [PATCH V3 6/7] mmc: lock: Prevent block device from coming up for locked cards Al Cooper
@ 2014-03-05 23:44 ` Al Cooper
  6 siblings, 0 replies; 8+ messages in thread
From: Al Cooper @ 2014-03-05 23:44 UTC (permalink / raw)
  To: cjb, linux-mmc; +Cc: Al Cooper

- Change mmc_init_card() to check for a locked card and, if found,
try to get a password using the kernel KEYS subsystem, unlock the card
and continue. Unlike SD cards, MMC cards support all initialization
commands when locked so the init sequence can be completed on a
locked card and the card can be used without further init after being
unlocked. If the unlock fails, the card state will be marked as "locked"
and the block layer will be prevented from coming up.

- Add sysfs attribute "unlock_retry" that will try again to unlock
the card. If the unlock succeeds, the cards "locked" state will be cleared
and the block layer will be allowed to come up.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mmc/core/mmc.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3703cd1..240ff48 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -726,10 +726,37 @@ out:
 		return res;
 }
 
-static DEVICE_ATTR(lock, S_IWUSR | S_IRUGO,
-		   mmc_lock_show, mmc_lock_store);
+static ssize_t mmc_unlock_retry_store(struct device *dev,
+				      struct device_attribute *att,
+				      const char *data, size_t len)
+{
+	struct mmc_card *card = mmc_dev_to_card(dev);
+	struct mmc_host *host = card->host;
+	int err;
 
+	BUG_ON(!card);
+	BUG_ON(!host);
+
+	mmc_claim_host(host);
+	if (!mmc_card_locked(card)) {
+		mmc_release_host(host);
+		return len;
+	}
+	err = mmc_unlock_card(card);
+	mmc_release_host(host);
+	if (err < 0)
+		return err;
+	device_release_driver(dev);
+	err = device_attach(dev);
+	if (err < 0)
+		return err;
+	return len;
+}
 
+static DEVICE_ATTR(lock, S_IWUSR | S_IRUGO,
+		   mmc_lock_show, mmc_lock_store);
+static DEVICE_ATTR(unlock_retry, S_IWUSR,
+		   NULL, mmc_unlock_retry_store);
 #endif /* CONFIG_MMC_LOCK */
 
 
@@ -772,6 +799,7 @@ static struct attribute *mmc_std_attrs[] = {
 	&dev_attr_rel_sectors.attr,
 #ifdef CONFIG_MMC_LOCK
 	&dev_attr_lock.attr,
+	&dev_attr_unlock_retry.attr,
 #endif /* CONFIG_MMC_LOCK */
 	NULL,
 };
@@ -958,6 +986,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 	unsigned int max_dtr;
 	u32 rocr;
 	u8 *ext_csd = NULL;
+	u32 status;
 
 	BUG_ON(!host);
 	WARN_ON(!host->claimed);
@@ -1413,6 +1442,19 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 		}
 	}
 
+	/* If card is locked, try to unlock it */
+	err = mmc_send_status(card, &status);
+	if (err)
+		goto free_card;
+	if (status & R1_CARD_IS_LOCKED) {
+		pr_info("%s: card is locked.\n", mmc_hostname(card->host));
+		err = mmc_unlock_card(card);
+		if (err != 0) {
+			pr_warn("%s: Card unlock failed.\n",
+				mmc_hostname(card->host));
+		}
+	}
+
 	if (!oldcard)
 		host->card = card;
 
-- 
1.8.1.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2014-03-05 23:53 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-05 23:44 [PATCH V3 0/7] Add password protected lock/unlock support for SD/MMC Al Cooper
2014-03-05 23:44 ` [PATCH V3 1/7] mmc: lock: Use the kernel "KEYS" subsystem to get a card's password Al Cooper
2014-03-05 23:44 ` [PATCH V3 2/7] mmc: lock: Add low level LOCK_UNLOCK command Al Cooper
2014-03-05 23:44 ` [PATCH V3 3/7] mmc: lock: Add function to unlock a password locked card Al Cooper
2014-03-05 23:44 ` [PATCH V3 4/7] mmc: lock: Add card lock/unlock maintenance commands Al Cooper
2014-03-05 23:44 ` [PATCH V3 5/7] mmc: lock: Change SD init functionality to handle locked SD cards Al Cooper
2014-03-05 23:44 ` [PATCH V3 6/7] mmc: lock: Prevent block device from coming up for locked cards Al Cooper
2014-03-05 23:44 ` [PATCH V3 7/7] mmc: lock: Change MMC init to handle " Al Cooper

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).