Skip to contentSkip to conversation

Add MMC password protection (lock/unlock) support V2

PATCH In review Anderson Lizardo 3 participants 8 messages Dec 28, 2005 – Dec 29, 2005

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover Add MMC password protection (lock/unlock) support V2

Hi,

These series of patches add support for MultiMediaCard (MMC) password protection, as described in the MMC Specification v4.1. This feature is supported by all compliant MMC cards, and used by some devices such as Symbian OS cell phones to optionally protect MMC cards with a password.

By default, a MMC 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 the card can be normally used for operations like block I/O.

Password management and caching is done through the "Kernel Key Retention Service" mechanism and the sysfs filesystem. The Key Retention Service is used for (1) unlocking the card, (2) assigning a password to an unlocked card and
(3) change a card's password. To remove the password and check locked/unlocked status, a new sysfs attribute was added to the MMC driver.

A sample text-mode reference UI written in shell script (using the keyctl command from the keyutils package), can be found at:

http://www.indt.org.br/10le/mmc_pwd/mmc_reference_ui-20051215.tar.gz

New in this version:

- Removed unnecessary #include directive
- Fixed comment formatting issues.
- Added code to force the block driver to re-probe() the card after unlocking it.

TODO:

- Password caching: when inserting a locked card, the driver should try to unlock it with the currently stored password (if any), and if it fails, revoke the key containing it and fallback to the normal "no password present" situation.

- Currently, some host drivers assume the block length will always be a power of 2. This is not true for the MMC_LOCK_UNLOCK command, which is a block command that accepts arbitratry block lengths. We have made the necessary changes to the omap.c driver (present on the linux-omap tree), but the same needs to be done for other hosts' drivers.

Known Issue:

- Some cards have an incorrect behaviour (hardware bug?) regarding password
acceptance: if an affected card has password <pwd>, it accepts <pwd><xxx> as the correct password too, where <xxx> is any sequence of characters, of any length. In other words, on these cards only the first <password length> bytes need to match the correct password.

Comments, suggestions are welcome.
--
Anderson Briglia,
Anderson Lizardo,
Carlos Eduardo Aguiar
Embedded Linux Lab - 10LE
Nokia Institute of Technology - INdT
Manaus - Brazil

#1 lore raw

PATCH 1/5 Add MMC password protection (lock/unlock) support V2

When a card is locked, only commands from the "basic" and "lock card" classes are accepted. To be able to use the other commands, the card must be unlocked first.

This patch prevents the block driver from trying to run privileged class commands on locked MMC cards, which will fail anyway.

drivers/mmc/mmc.c +7 -2
Index: linux-2.6.15-rc4-omap1/drivers/mmc/mmc.c
===================================================================
@@ -986,10 +986,15 @@ static void mmc_check_cards(struct mmc_h
 		cmd.flags = MMC_RSP_R1;
 
 		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
-		if (err == MMC_ERR_NONE)
+		if (err != MMC_ERR_NONE) {
+			mmc_card_set_dead(card);
 			continue;
+		}
 
-		mmc_card_set_dead(card);
+		if (cmd.resp[0] & R1_CARD_IS_LOCKED)
+			mmc_card_set_locked(card);
+		else
+			card->state &= ~MMC_STATE_LOCKED;
 	}
 }
 
drivers/mmc/mmc_sysfs.c +10 -0
Index: linux-2.6.15-rc4-omap1/drivers/mmc/mmc_sysfs.c
===================================================================
@@ -16,6 +16,7 @@
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
 
 #include "mmc.h"
 
@@ -72,10 +73,19 @@ static void mmc_release_card(struct devi
  * This currently matches any MMC driver to any MMC card - drivers
  * themselves make the decision whether to drive this card in their
  * probe method.  However, we force "bad" cards to fail.
+ *
+ * We also fail for all locked cards; drivers expect to be able to do
+ * block I/O still on probe(), which is not possible while the card is
+ * locked. Device probing must be triggered sometime later to make the
+ * card available to the block driver.
  */
 static int mmc_bus_match(struct device *dev, struct device_driver *drv)
 {
 	struct mmc_card *card = dev_to_mmc_card(dev);
+	if (mmc_card_lockable(card) && mmc_card_locked(card)) {
+		dev_dbg(&card->dev, "card is locked; binding is deferred\n");
+		return 0;
+	}
 	return !mmc_card_bad(card);
 }
 
include/linux/mmc/card.h +5 -0
Index: linux-2.6.15-rc4-omap1/include/linux/mmc/card.h
===================================================================
@@ -56,6 +56,7 @@ struct mmc_card {
 #define MMC_STATE_BAD		(1<<2)		/* unrecognised device */
 #define MMC_STATE_SDCARD	(1<<3)		/* is an SD card */
 #define MMC_STATE_READONLY	(1<<4)		/* card is read-only */
+#define MMC_STATE_LOCKED	(1<<5)		/* card is currently locked */
 	u32			raw_cid[4];	/* raw card CID */
 	u32			raw_csd[4];	/* raw card CSD */
 	u32			raw_scr[2];	/* raw card SCR */
@@ -69,12 +70,16 @@ struct mmc_card {
 #define mmc_card_bad(c)		((c)->state & MMC_STATE_BAD)
 #define mmc_card_sd(c)		((c)->state & MMC_STATE_SDCARD)
 #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
+#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_dead(c)	((c)->state |= MMC_STATE_DEAD)
 #define mmc_card_set_bad(c)	((c)->state |= MMC_STATE_BAD)
 #define mmc_card_set_sd(c)	((c)->state |= MMC_STATE_SDCARD)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
+#define mmc_card_set_locked(c)	((c)->state |= MMC_STATE_LOCKED)
 
 #define mmc_card_name(c)	((c)->cid.prod_name)
 #define mmc_card_id(c)		((c)->dev.bus_id)

--
Anderson Lizardo
Embedded Linux Lab - 10LE
Nokia Institute of Technology - INdT
Manaus - Brazil

#2 lore raw

PATCH 2/5 Add MMC password protection (lock/unlock) support V2

In reply to Anderson Lizardo

Implement card lock/unlock card operation, using the MMC_LOCK_UNLOCK command.

drivers/mmc/mmc.c +156 -0
Index: linux-2.6.15-rc4-omap1/drivers/mmc/mmc.c
===================================================================
@@ -4,6 +4,8 @@
  *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.
  *  SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
  *  SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved.
+ *  MMC password protection (C) 2005 Instituto Nokia de Tecnologia (INdT),
+ *     All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -20,6 +22,7 @@
 #include <linux/err.h>
 #include <asm/scatterlist.h>
 #include <linux/scatterlist.h>
+#include <linux/key.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -1088,6 +1091,159 @@ static void mmc_setup(struct mmc_host *h
 		mmc_read_scrs(host);
 }
 
+static int pop(unsigned x)
+{
+	x = x - ((x >> 1) & 0x55555555);
+	x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+	x = (x + (x >> 4)) & 0x0F0F0F0F;
+	x = x + (x >> 8);
+	x = x + (x >> 16);
+
+	return x & 0x0000003F;
+}
+
+static int ilog2(unsigned x)
+{
+	x = x | (x >> 1);
+	x = x | (x >> 2);
+	x = x | (x >> 4);
+	x = x | (x >> 8);
+	x = x | (x >> 16);
+
+	return pop(x) - 1;
+}
+
+/* Calculate the minimal blksz_bits to hold x bytes. The two math functions
+ * above are used to do the calculation.
+ *
+ * XXX There must be a simpler way to do this... */
+static inline int blksz_bits(unsigned x)
+{
+	return 1 + ilog2(x - 1);
+}
+
+/**
+ *	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 key *key, int mode)
+{
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_data data;
+	struct scatterlist sg;
+	struct mmc_key_payload *mpayload;
+	unsigned long erase_timeout;
+	int err, data_size;
+	u8 *data_buf;
+
+	mpayload = NULL;
+	data_size = 1;
+	if (mode != MMC_LOCK_MODE_ERASE) {
+		mpayload = rcu_dereference(key->payload.data);
+		data_size = 2 + mpayload->datalen;
+	}
+
+	data_buf = kmalloc(data_size, GFP_KERNEL);
+	if (!data_buf)
+		return -ENOMEM;
+	memset(data_buf, 0, data_size);
+
+	data_buf[0] = mode;
+	if (mode != MMC_LOCK_MODE_ERASE) {
+		data_buf[1] = mpayload->datalen;
+		memcpy(data_buf + 2, mpayload->data, mpayload->datalen);
+	}
+
+	err = mmc_card_claim_host(card);
+	if (err != MMC_ERR_NONE) {
+		mmc_card_set_dead(card);
+		goto out;
+	}
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SET_BLOCKLEN;
+	cmd.arg = data_size;
+	cmd.flags = MMC_RSP_R1;
+	err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES);
+	if (err != MMC_ERR_NONE) {
+		mmc_card_set_dead(card);
+		goto error;
+	}
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_LOCK_UNLOCK;
+	cmd.arg = 0;
+	cmd.flags = MMC_RSP_R1B;
+
+	memset(&data, 0, sizeof(struct mmc_data));
+
+	data.timeout_ns = card->csd.tacc_ns * 10;
+	data.timeout_clks = card->csd.tacc_clks * 10;
+	data.blksz_bits = blksz_bits(data_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, data_size);
+	err = mmc_wait_for_req(card->host, &mrq);
+	if (err != MMC_ERR_NONE) {
+		mmc_card_set_dead(card);
+		goto error;
+	}
+
+        memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SEND_STATUS;
+	cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_R1;
+
+	/* 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 != MMC_ERR_NONE)
+			break;
+		/* the other modes don't need timeout checking */
+		if (mode != MMC_LOCK_MODE_ERASE)
+			continue;
+		if (time_after(jiffies, erase_timeout)) {
+			dev_dbg(&card->dev, "forced erase timed out\n");
+			err = MMC_ERR_TIMEOUT;
+			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 = MMC_ERR_FAILED;
+	}
+
+error:
+	mmc_check_cards(card->host);
+	mmc_deselect_cards(card->host);
+	mmc_card_release_host(card);
+out:
+	kfree(data_buf);
+
+	return err;
+}
+
+EXPORT_SYMBOL(mmc_lock_unlock);
+
 
 /**
  *	mmc_detect_change - process change of state on a MMC socket
include/linux/mmc/card.h +3 -0
Index: linux-2.6.15-rc4-omap1/include/linux/mmc/card.h
===================================================================
@@ -109,4 +109,7 @@ static inline int mmc_card_claim_host(st
 
 #define mmc_card_release_host(c)	mmc_release_host((c)->host)
 
+struct key;
+extern int mmc_lock_unlock(struct mmc_card *card, struct key *key, int mode);
+
 #endif
include/linux/mmc/protocol.h +8 -0
Index: linux-2.6.15-rc4-omap1/include/linux/mmc/protocol.h
===================================================================
@@ -243,5 +243,13 @@ struct _mmc_csd {
 #define SD_BUS_WIDTH_1      0
 #define SD_BUS_WIDTH_4      2
 
+/*
+ * MMC_LOCK_UNLOCK modes
+ */
+#define MMC_LOCK_MODE_ERASE	(1<<3)
+#define MMC_LOCK_MODE_UNLOCK	(0<<2)
+#define MMC_LOCK_MODE_CLR_PWD	(1<<1)
+#define MMC_LOCK_MODE_SET_PWD	(1<<0)
+
 #endif  /* MMC_MMC_PROTOCOL_H */
 

--
Anderson Lizardo
Embedded Linux Lab - 10LE
Nokia Institute of Technology - INdT
Manaus - Brazil

#3 lore raw

PATCH 3/5 Add MMC password protection (lock/unlock) support V2

In reply to Anderson Lizardo

Implement key retention operations. mmc_key_instantiate() is used for unlocking and password assignment (from no-password state). mmc_key_update() is used for password change.

drivers/mmc/Kconfig +13 -0
Index: linux-2.6.15-rc4-omap1/drivers/mmc/Kconfig
===================================================================
@@ -19,6 +19,19 @@ config MMC_DEBUG
 	  This is an option for use by developers; most people should
 	  say N here.  This enables MMC core and driver debugging.
 
+config MMC_PASSWORDS
+	boolean "MMC card lock/unlock passwords (EXPERIMENTAL)"
+	depends on MMC && EXPERIMENTAL
+	select KEYS
+	help
+	  Say Y here to enable the use of passwords to lock and unlock
+	  MMC cards.  This uses the access key retention support, using
+	  request_key to look up the key associated with each card.
+
+	  For example, if you have an MMC card that was locked using
+	  Symbian OS on your cell phone, you won't be able to read it
+	  on Linux without this support.
+
 config MMC_BLOCK
 	tristate "MMC block device driver"
 	depends on MMC
drivers/mmc/mmc.h +8 -0
Index: linux-2.6.15-rc4-omap1/drivers/mmc/mmc.h
===================================================================
@@ -18,4 +18,12 @@ struct mmc_host *mmc_alloc_host_sysfs(in
 int mmc_add_host_sysfs(struct mmc_host *host);
 void mmc_remove_host_sysfs(struct mmc_host *host);
 void mmc_free_host_sysfs(struct mmc_host *host);
+
+/* core-internal data */
+extern struct key_type mmc_key_type;
+struct mmc_key_payload {
+	struct rcu_head	rcu;		/* RCU destructor */
+	unsigned short	datalen;	/* length of this data */
+	char		data[0];	/* actual data */
+};
 #endif
drivers/mmc/mmc_sysfs.c +159 -0

189 lines not on this page · raw message

--
Anderson Lizardo
Embedded Linux Lab - 10LE
Nokia Institute of Technology - INdT
Manaus - Brazil

#4 lore raw

On Thursday 29 December 2005 03:40, Anderson Lizardo wrote:

Something that sort of caught my eye while looking at this (I generally don't post here so I'm bitting the bullet and hoping I don't screw up), it seems that this is an experimental driver, but doesn't contain any sort of uniquely seperated verbose debug information. Let me try and narrow that down:

drivers/mmc/mmc_sysfs.c static struct class mmc_host_class = { · line 293

Quoted 10 lines
drivers/mmc/mmc_sysfs.c +10 -0
+int mmc_key_instantiate(struct key *key, const void *data, size_t datalen)
+{
+	struct mmc_key_payload *mpayload;
+	struct device *dev;
+	struct mmc_card *card;
+	int ret;
+
+	ret = -EINVAL;
+	if (datalen <= 0 || datalen > MMC_KEYLEN_MAXBYTES || !data)
+		goto error;

Right here something about the data being passed to the function is invalid.

drivers/mmc/mmc_sysfs.c static struct class mmc_host_class = { · line 302

Quoted 8 lines
drivers/mmc/mmc_sysfs.c +8 -0
+	ret = key_payload_reserve(key, datalen);
+	if (ret < 0)
+		goto error;
+
+	ret = -ENOMEM;
+	mpayload = kmalloc(sizeof(*mpayload) + datalen, GFP_KERNEL);
+	if (!mpayload)
+		goto error;

Unable to allocate mpayload structure, or something of the like.

drivers/mmc/mmc_sysfs.c static struct class mmc_host_class = { · line 312

Quoted 9 lines
drivers/mmc/mmc_sysfs.c +9 -0
+	/* attach the data */
+	mpayload->datalen = datalen;
+	memcpy(mpayload->data, data, datalen);
+	rcu_assign_pointer(key->payload.data, mpayload);
+
+	ret = -EINVAL;
+	dev = bus_find_device(&mmc_bus_type, NULL, NULL, mmc_match_lockable);
+	if (!dev)
+		goto error;

Unable to locate device.

drivers/mmc/mmc_sysfs.c static struct class mmc_host_class = { · line 321

Quoted 9 lines
drivers/mmc/mmc_sysfs.c +9 -0
+	card = dev_to_mmc_card(dev);
+	if (mmc_card_locked(card)) {
+		ret = mmc_lock_unlock(card, key, MMC_LOCK_MODE_UNLOCK);
+		mmc_remove_card(card);
+		mmc_register_card(card);
+	} else
+		ret = mmc_lock_unlock(card, key, MMC_LOCK_MODE_SET_PWD);
+	if (ret)
+		ret = -EKEYREJECTED;

Key was rejected, though I suppose EKEYREJECTED pretty much states that.

[snip snip]

Quoted 5 lines
drivers/mmc/mmc_sysfs.c +5 -0
+
+/*
+ * update a mmc key
+ * - the key's semaphore is write-locked
+ */

drivers/mmc/mmc_sysfs.c static struct class mmc_host_class = { · line 352

Quoted 10 lines
drivers/mmc/mmc_sysfs.c +10 -0
+int mmc_key_update(struct key *key, const void *data, size_t datalen)
+{
+	struct mmc_key_payload *mpayload, *zap;
+	struct device *dev;
+	struct mmc_card *card;
+	int ret;
+
+	ret = -EINVAL;
+	if (datalen <= 0 || datalen > MMC_KEYLEN_MAXBYTES || !data)
+		goto error;

See above about invalid data

drivers/mmc/mmc_sysfs.c static struct class mmc_host_class = { · line 358

Quoted 5 lines
drivers/mmc/mmc_sysfs.c +5 -0
+	/* construct a replacement payload */
+	ret = -ENOMEM;
+	mpayload = kmalloc(sizeof(*mpayload) + datalen, GFP_KERNEL);
+	if (!mpayload)
+		goto error;

This code almost seemed similiar to mmc_key_instantiate.. I almost wonder if the code could be consolidated into a single function with some sort of update conditional code. With that the debug information wouldn't be duplicated. so snip

drivers/mmc/mmc_sysfs.c static int __init mmc_init(void) · line 490

Quoted 4 lines
drivers/mmc/mmc_sysfs.c +4 -0
+#ifdef	CONFIG_MMC_PASSWORDS
+		else {
+			ret = register_key_type(&mmc_key_type);
+			if (ret) {

Something about the registration failing.

Quoted 9 lines
drivers/mmc/mmc_sysfs.c +5 -0
+				class_unregister(&mmc_host_class);
+				bus_unregister(&mmc_bus_type);
+			}
+		}
+#endif
 	}
 	return ret;
 }

drivers/mmc/mmc_sysfs.c static void __exit mmc_exit(void) · line 509

Quoted 9 lines
drivers/mmc/mmc_sysfs.c +3 -0
@@ -345,6 +501,9 @@ static void __exit mmc_exit(void)
 {
 	class_unregister(&mmc_host_class);
 	bus_unregister(&mmc_bus_type);
+#ifdef	CONFIG_MMC_PASSWORDS
+	unregister_key_type(&mmc_key_type);
+#endif
 }
 
 module_init(mmc_init);

That was mainly it. The verbose debug information is more of a "this would be nice" sort of thing. Just from a user's perspective of debuggin experimental drivers, this sort of thing is always nice. The code duplication in mmc_key_instantiate/update still catches my eye though, there may be a functional code flow to this that I'm not aware of, so again I bite the bullet. Best hope that I don't make a fool of myself :).

Chris White

#5 lore raw

PATCH 4/5 Add MMC password protection (lock/unlock) support V2

In reply to Anderson Lizardo

Implement MMC password reset and forced erase support. It uses the sysfs mechanism to send commands to the MMC subsystem. Usage:

Forced erase:

echo erase > /sys/bus/mmc/devices/mmc0\:0001/lockable

Remove password:

echo remove > /sys/bus/mmc/devices/mmc0\:0001/lockable

drivers/mmc/mmc_sysfs.c +64 -1
Index: linux-2.6.15-rc4-omap1/drivers/mmc/mmc_sysfs.c
===================================================================
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/idr.h>
 #include <linux/key.h>
+#include <linux/err.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -64,6 +65,58 @@ static struct device_attribute mmc_dev_a
 
 static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr);
 
+#ifdef	CONFIG_MMC_PASSWORDS
+
+static ssize_t
+mmc_lockable_show(struct device *dev, struct device_attribute *att, char *buf)
+{
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	if (!mmc_card_lockable(card))
+		return sprintf(buf, "unlockable\n");
+	else
+		return sprintf(buf, "lockable, %slocked\n", mmc_card_locked(card) ?
+			"" : "un");
+}
+
+/*
+ * implement MMC password reset ("remove password") and forced erase ("forgot
+ * password").
+ */
+static ssize_t
+mmc_lockable_store(struct device *dev, struct device_attribute *att,
+	const char *data, size_t len)
+{
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	if (!mmc_card_lockable(card))
+		return -EINVAL;
+
+	if (mmc_card_locked(card) && !strncmp(data, "erase", 5)) {
+		/* forced erase only works while card is locked */
+		mmc_lock_unlock(card, NULL, MMC_LOCK_MODE_ERASE);
+		return len;
+	} else if (!mmc_card_locked(card) && !strncmp(data, "remove", 6)) {
+		/* remove password only works while card is unlocked */
+		struct key *mmc_key = request_key(&mmc_key_type, "mmc:key", NULL);
+
+		if (!IS_ERR(mmc_key)) {
+			int err = mmc_lock_unlock(card, mmc_key, MMC_LOCK_MODE_CLR_PWD);
+			if (!err)
+				return len;
+		} else
+			dev_dbg(&card->dev, "request_key returned error %ld\n", PTR_ERR(mmc_key));
+	}
+
+	return -EINVAL;
+}
+
+static struct device_attribute mmc_dev_attr_lockable =
+	__ATTR(lockable, S_IWUSR | S_IRUGO,
+		 mmc_lockable_show, mmc_lockable_store);
+
+#endif
+
 
 static void mmc_release_card(struct device *dev)
 {
@@ -235,6 +288,13 @@ int mmc_register_card(struct mmc_card *c
 			if (ret)
 				device_del(&card->dev);
 		}
+#ifdef CONFIG_MMC_PASSWORDS
+		if (mmc_card_lockable(card)) {
+			ret = device_create_file(&card->dev, &mmc_dev_attr_lockable);
+			if (ret)
+				device_del(&card->dev);
+		}
+#endif
 	}
 	return ret;
 }
@@ -248,7 +308,10 @@ void mmc_remove_card(struct mmc_card *ca
 	if (mmc_card_present(card)) {
 		if (mmc_card_sd(card))
 			device_remove_file(&card->dev, &mmc_dev_attr_scr);
-
+#ifdef CONFIG_MMC_PASSWORDS
+		if (mmc_card_lockable(card))
+			device_remove_file(&card->dev, &mmc_dev_attr_lockable);
+#endif
 		device_del(&card->dev);
 	}
 

--
Anderson Lizardo
Embedded Linux Lab - 10LE
Nokia Institute of Technology - INdT
Manaus - Brazil

#6 lore raw
Quoted 11 lines · Anderson Lizardo

Implement MMC password reset and forced erase support. It uses the sysfs mechanism to send commands to the MMC subsystem. Usage:

Forced erase:

echo erase > /sys/bus/mmc/devices/mmc0\:0001/lockable

Remove password:

echo remove > /sys/bus/mmc/devices/mmc0\:0001/lockable

The MMC password support seems to be a subset of the operations possible on modern ATA drives. IMHO, any interface you introduce should be usable for both types of devices.

The only interface I could find for ATA is through HDIO_DRIVE_CMD. This is currently used by hdparm, but it does not look like it fits the MMC problem well.

Has anyone already done an ioctl or sysfs implementation of ATA password user interfaces, or is perhaps the one proposed here sufficient for ATA drives as well?

	Arnd <><
#7 lore raw

PATCH 5/5 Add MMC password protection (lock/unlock) support V2

In reply to Anderson Lizardo

The MMC_LOCK_UNLOCK command requires the block length to be exactly the password length + 2 bytes, but hardware-specific drivers force a "power of 2" block size.

This patch sends the exact block size (password + 2 bytes) to the host. OMAP specific.

drivers/mmc/omap.c +6 -2
Index: linux-2.6.15-rc4-omap1/drivers/mmc/omap.c
===================================================================
@@ -889,8 +889,12 @@ mmc_omap_prepare_data(struct mmc_omap_ho
 		return;
 	}
 
-
-	block_size = 1 << data->blksz_bits;
+	/*  password protection: we need to send the exact block size to the
+	 *  card (password + 2), not a power of two */
+	if (req->cmd->opcode == MMC_LOCK_UNLOCK)
+		block_size = data->sg[0].length;
+	else
+		block_size = 1 << data->blksz_bits;
 
 	OMAP_MMC_WRITE(host->base, NBLK, data->blocks - 1);
 	OMAP_MMC_WRITE(host->base, BLEN, block_size - 1);

--
Anderson Lizardo
Embedded Linux Lab - 10LE
Nokia Institute of Technology - INdT
Manaus - Brazil