diff options
| author | John Calixto <john.calixto@modsystems.com> | 2011-04-26 18:56:29 -0400 |
|---|---|---|
| committer | Chris Ball <cjb@laptop.org> | 2011-05-24 21:02:54 -0400 |
| commit | cb87ea28ed9e75a41eb456bfcb547b4e6f10e750 (patch) | |
| tree | e3fe4a653bd96815c650dd9f5db11edc6b39b0db | |
| parent | 641c3187b9d53cfd4c23b0ce2ab18a13d5e775e5 (diff) | |
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 <john.calixto@modsystems.com>
Reviewed-by: Andrei Warkentin <andreiw@motorola.com>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Chris Ball <cjb@laptop.org>
| -rw-r--r-- | Documentation/ioctl/ioctl-number.txt | 1 | ||||
| -rw-r--r-- | drivers/mmc/card/block.c | 201 | ||||
| -rw-r--r-- | drivers/mmc/core/sd_ops.c | 3 | ||||
| -rw-r--r-- | include/linux/Kbuild | 1 | ||||
| -rw-r--r-- | include/linux/mmc/Kbuild | 1 | ||||
| -rw-r--r-- | include/linux/mmc/core.h | 1 | ||||
| -rw-r--r-- | include/linux/mmc/ioctl.h | 54 |
7 files changed, 261 insertions, 1 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index a0a5d82b6b0..2a34d822e6d 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
| @@ -304,6 +304,7 @@ Code Seq#(hex) Include File Comments | |||
| 304 | 0xB0 all RATIO devices in development: | 304 | 0xB0 all RATIO devices in development: |
| 305 | <mailto:vgo@ratio.de> | 305 | <mailto:vgo@ratio.de> |
| 306 | 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> | 306 | 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> |
| 307 | 0xB3 00 linux/mmc/ioctl.h | ||
| 307 | 0xC0 00-0F linux/usb/iowarrior.h | 308 | 0xC0 00-0F linux/usb/iowarrior.h |
| 308 | 0xCB 00-1F CBM serial IEC bus in development: | 309 | 0xCB 00-1F CBM serial IEC bus in development: |
| 309 | <mailto:michael.klein@puffin.lb.shuttle.de> | 310 | <mailto:michael.klein@puffin.lb.shuttle.de> |
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e7efd6faeaf..407836d5571 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c | |||
| @@ -31,7 +31,11 @@ | |||
| 31 | #include <linux/mutex.h> | 31 | #include <linux/mutex.h> |
| 32 | #include <linux/scatterlist.h> | 32 | #include <linux/scatterlist.h> |
| 33 | #include <linux/string_helpers.h> | 33 | #include <linux/string_helpers.h> |
| 34 | #include <linux/delay.h> | ||
| 35 | #include <linux/capability.h> | ||
| 36 | #include <linux/compat.h> | ||
| 34 | 37 | ||
| 38 | #include <linux/mmc/ioctl.h> | ||
| 35 | #include <linux/mmc/card.h> | 39 | #include <linux/mmc/card.h> |
| 36 | #include <linux/mmc/host.h> | 40 | #include <linux/mmc/host.h> |
| 37 | #include <linux/mmc/mmc.h> | 41 | #include <linux/mmc/mmc.h> |
| @@ -218,11 +222,208 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) | |||
| 218 | return 0; | 222 | return 0; |
| 219 | } | 223 | } |
| 220 | 224 | ||
| 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 | |||
