Skip to content

Commit cb87ea2

Browse files
John Calixtocjb
authored andcommitted
mmc: core: Add mmc CMD+ACMD passthrough ioctl
Allows appropriately-privileged applications to send CMD (normal) and ACMD (application-specific; preceded with CMD55) commands to cards/devices on the mmc bus. This is primarily useful for enabling the security functionality built in to every SD card. It can also be used as a generic passthrough (e.g. to enable virtual machines to control mmc bus devices directly). However, this use case has not been tested rigorously. Generic passthrough testing was only conducted for a few non-security opcodes to prove the feasibility of the passthrough. Since any opcode can be sent using this passthrough, it is very possible to render the card/device unusable. Applications that use this ioctl must have CAP_SYS_RAWIO. Security commands tested on TI PCIxx12 (SDHCI), Sigma Designs SMP8652 SoC, TI OMAP3621/OMAP3630 SoC, Samsung S5PC110 SoC, Qualcomm MSM7200A SoC. Signed-off-by: John Calixto <[email protected]> Reviewed-by: Andrei Warkentin <[email protected]> Reviewed-by: Arnd Bergmann <[email protected]> Signed-off-by: Chris Ball <[email protected]>
1 parent 641c318 commit cb87ea2

File tree

7 files changed

+261
-1
lines changed

7 files changed

+261
-1
lines changed

Documentation/ioctl/ioctl-number.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ Code Seq#(hex) Include File Comments
304304
0xB0 all RATIO devices in development:
305305
306306
0xB1 00-1F PPPoX <mailto:[email protected]>
307+
0xB3 00 linux/mmc/ioctl.h
307308
0xC0 00-0F linux/usb/iowarrior.h
308309
0xCB 00-1F CBM serial IEC bus in development:
309310

drivers/mmc/card/block.c

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@
3131
#include <linux/mutex.h>
3232
#include <linux/scatterlist.h>
3333
#include <linux/string_helpers.h>
34+
#include <linux/delay.h>
35+
#include <linux/capability.h>
36+
#include <linux/compat.h>
3437

38+
#include <linux/mmc/ioctl.h>
3539
#include <linux/mmc/card.h>
3640
#include <linux/mmc/host.h>
3741
#include <linux/mmc/mmc.h>
@@ -218,11 +222,208 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
218222
return 0;
219223
}
220224

225+
struct mmc_blk_ioc_data {
226+
struct mmc_ioc_cmd ic;
227+
unsigned char *buf;
228+
u64 buf_bytes;
229+
};
230+
231+
static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
232+
struct mmc_ioc_cmd __user *user)
233+
{
234+
struct mmc_blk_ioc_data *idata;
235+
int err;
236+
237+
idata = kzalloc(sizeof(*idata), GFP_KERNEL);
238+
if (!idata) {
239+
err = -ENOMEM;
240+
goto copy_err;
241+
}
242+
243+
if (copy_from_user(&idata->ic, user, sizeof(idata->ic))) {
244+
err = -EFAULT;
245+
goto copy_err;
246+
}
247+
248+
idata->buf_bytes = (u64) idata->ic.blksz * idata->ic.blocks;
249+
if (idata->buf_bytes > MMC_IOC_MAX_BYTES) {
250+
err = -EOVERFLOW;
251+
goto copy_err;
252+
}
253+
254+
idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL);
255+
if (!idata->buf) {
256+
err = -ENOMEM;
257+
goto copy_err;
258+
}
259+
260+
if (copy_from_user(idata->buf, (void __user *)(unsigned long)
261+
idata->ic.data_ptr, idata->buf_bytes)) {
262+
err = -EFAULT;
263+
goto copy_err;
264+
}
265+
266+
return idata;
267+
268+
copy_err:
269+
kfree(idata->buf);
270+
kfree(idata);
271+
return ERR_PTR(err);
272+
273+
}
274+
275+
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
276+
struct mmc_ioc_cmd __user *ic_ptr)
277+
{
278+
struct mmc_blk_ioc_data *idata;
279+
struct mmc_blk_data *md;
280+
struct mmc_card *card;
281+
struct mmc_command cmd = {0};
282+
struct mmc_data data = {0};
283+
struct mmc_request mrq = {0};
284+
struct scatterlist sg;
285+
int err;
286+
287+
/*
288+
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
289+
* whole block device, not on a partition. This prevents overspray
290+
* between sibling partitions.
291+
*/
292+
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
293+
return -EPERM;
294+
295+
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
296+
if (IS_ERR(idata))
297+
return PTR_ERR(idata);
298+
299+
cmd.opcode = idata->ic.opcode;
300+
cmd.arg = idata->ic.arg;
301+
cmd.flags = idata->ic.flags;
302+
303+
data.sg = &sg;
304+
data.sg_len = 1;
305+
data.blksz = idata->ic.blksz;
306+
data.blocks = idata->ic.blocks;
307+
308+
sg_init_one(data.sg, idata->buf, idata->buf_bytes);
309+
310+
if (idata->ic.write_flag)
311+
data.flags = MMC_DATA_WRITE;
312+
else
313+
data.flags = MMC_DATA_READ;
314+
315+
mrq.cmd = &cmd;
316+
mrq.data = &data;
317+
318+
md = mmc_blk_get(bdev->bd_disk);
319+
if (!md) {
320+
err = -EINVAL;
321+
goto cmd_done;
322+
}
323+
324+
card = md->queue.card;
325+
if (IS_ERR(card)) {
326+
err = PTR_ERR(card);
327+
goto cmd_done;
328+
}
329+
330+
mmc_claim_host(card->host);
331+
332+
if (idata->ic.is_acmd) {
333+
err = mmc_app_cmd(card->host, card);
334+
if (err)
335+
goto cmd_rel_host;
336+
}
337+
338+
/* data.flags must already be set before doing this. */
339+
mmc_set_data_timeout(&data, card);
340+
/* Allow overriding the timeout_ns for empirical tuning. */
341+
if (idata->ic.data_timeout_ns)
342+
data.timeout_ns = idata->ic.data_timeout_ns;
343+
344+
if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
345+
/*
346+
* Pretend this is a data transfer and rely on the host driver
347+
* to compute timeout. When all host drivers support
348+
* cmd.cmd_timeout for R1B, this can be changed to:
349+
*
350+
* mrq.data = NULL;
351+
* cmd.cmd_timeout = idata->ic.cmd_timeout_ms;
352+
*/
353+
data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000;
354+
}
355+
356+
mmc_wait_for_req(card->host, &mrq);
357+
358+
if (cmd.error) {
359+
dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
360+
__func__, cmd.error);
361+
err = cmd.error;
362+
goto cmd_rel_host;
363+
}
364+
if (data.error) {
365+
dev_err(mmc_dev(card->host), "%s: data error %d\n",
366+
__func__, data.error);
367+
err = data.error;
368+
goto cmd_rel_host;
369+
}
370+
371+
/*
372+
* According to the SD specs, some commands require a delay after
373+
* issuing the command.
374+
*/
375+
if (idata->ic.postsleep_min_us)
376+
usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
377+
378+
if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) {
379+
err = -EFAULT;
380+
goto cmd_rel_host;
381+
}
382+
383+
if (!idata->ic.write_flag) {
384+
if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr,
385+
idata->buf, idata->buf_bytes)) {
386+
err = -EFAULT;
387+
goto cmd_rel_host;
388+
}
389+
}
390+
391+
cmd_rel_host:
392+
mmc_release_host(card->host);
393+
394+
cmd_done:
395+
mmc_blk_put(md);
396+
kfree(idata->buf);
397+
kfree(idata);
398+
return err;
399+
}
400+
401+
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
402+
unsigned int cmd, unsigned long arg)
403+
{
404+
int ret = -EINVAL;
405+
if (cmd == MMC_IOC_CMD)
406+
ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
407+
return ret;
408+
}
409+
410+
#ifdef CONFIG_COMPAT
411+
static int mmc_blk_compat_ioctl(struct block_device *bdev, fmode_t mode,
412+
unsigned int cmd, unsigned long arg)
413+
{
414+
return mmc_blk_ioctl(bdev, mode, cmd, (unsigned long) compat_ptr(arg));
415+
}
416+
#endif
417+
221418
static const struct block_device_operations mmc_bdops = {
222419
.open = mmc_blk_open,
223420
.release = mmc_blk_release,
224421
.getgeo = mmc_blk_getgeo,
225422
.owner = THIS_MODULE,
423+
.ioctl = mmc_blk_ioctl,
424+
#ifdef CONFIG_COMPAT
425+
.compat_ioctl = mmc_blk_compat_ioctl,
426+
#endif
226427
};
227428

228429
struct mmc_blk_request {

drivers/mmc/core/sd_ops.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "core.h"
2222
#include "sd_ops.h"
2323

24-
static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
24+
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
2525
{
2626
int err;
2727
struct mmc_command cmd = {0};
@@ -49,6 +49,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
4949

5050
return 0;
5151
}
52+
EXPORT_SYMBOL_GPL(mmc_app_cmd);
5253

5354
/**
5455
* mmc_wait_for_app_cmd - start an application command and wait for

include/linux/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ header-y += caif/
44
header-y += dvb/
55
header-y += hdlc/
66
header-y += isdn/
7+
header-y += mmc/
78
header-y += nfsd/
89
header-y += raid/
910
header-y += spi/

include/linux/mmc/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
header-y += ioctl.h

include/linux/mmc/core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ struct mmc_card;
133133

134134
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
135135
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
136+
extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
136137
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
137138
struct mmc_command *, int);
138139
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);

include/linux/mmc/ioctl.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#ifndef LINUX_MMC_IOCTL_H
2+
#define LINUX_MMC_IOCTL_H
3+
struct mmc_ioc_cmd {
4+
/* Implies direction of data. true = write, false = read */
5+
int write_flag;
6+
7+
/* Application-specific command. true = precede with CMD55 */
8+
int is_acmd;
9+
10+
__u32 opcode;
11+
__u32 arg;
12+
__u32 response[4]; /* CMD response */
13+
unsigned int flags;
14+
unsigned int blksz;
15+
unsigned int blocks;
16+
17+
/*
18+
* Sleep at least postsleep_min_us useconds, and at most
19+
* postsleep_max_us useconds *after* issuing command. Needed for
20+
* some read commands for which cards have no other way of indicating
21+
* they're ready for the next command (i.e. there is no equivalent of
22+
* a "busy" indicator for read operations).
23+
*/
24+
unsigned int postsleep_min_us;
25+
unsigned int postsleep_max_us;
26+
27+
/*
28+
* Override driver-computed timeouts. Note the difference in units!
29+
*/
30+
unsigned int data_timeout_ns;
31+
unsigned int cmd_timeout_ms;
32+
33+
/*
34+
* For 64-bit machines, the next member, ``__u64 data_ptr``, wants to
35+
* be 8-byte aligned. Make sure this struct is the same size when
36+
* built for 32-bit.
37+
*/
38+
__u32 __pad;
39+
40+
/* DAT buffer */
41+
__u64 data_ptr;
42+
};
43+
#define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long) ptr
44+
45+
#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
46+
47+
/*
48+
* Since this ioctl is only meant to enhance (and not replace) normal access
49+
* to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES
50+
* is enforced per ioctl call. For larger data transfers, use the normal
51+
* block device operations.
52+
*/
53+
#define MMC_IOC_MAX_BYTES (512L * 256)
54+
#endif /* LINUX_MMC_IOCTL_H */

0 commit comments

Comments
 (0)