diff options
-rw-r--r-- | Documentation/mmc/mmc-dev-parts.txt | 13 | ||||
-rw-r--r-- | drivers/mmc/card/block.c | 121 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 14 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 10 | ||||
-rw-r--r-- | include/linux/mmc/mmc.h | 6 |
5 files changed, 153 insertions, 11 deletions
diff --git a/Documentation/mmc/mmc-dev-parts.txt b/Documentation/mmc/mmc-dev-parts.txt index 2db28b8e662f..f08d078d43cf 100644 --- a/Documentation/mmc/mmc-dev-parts.txt +++ b/Documentation/mmc/mmc-dev-parts.txt | |||
@@ -25,3 +25,16 @@ echo 0 > /sys/block/mmcblkXbootY/force_ro | |||
25 | To re-enable read-only access: | 25 | To re-enable read-only access: |
26 | 26 | ||
27 | echo 1 > /sys/block/mmcblkXbootY/force_ro | 27 | echo 1 > /sys/block/mmcblkXbootY/force_ro |
28 | |||
29 | The boot partitions can also be locked read only until the next power on, | ||
30 | with: | ||
31 | |||
32 | echo 1 > /sys/block/mmcblkXbootY/ro_lock_until_next_power_on | ||
33 | |||
34 | This is a feature of the card and not of the kernel. If the card does | ||
35 | not support boot partition locking, the file will not exist. If the | ||
36 | feature has been disabled on the card, the file will be read-only. | ||
37 | |||
38 | The boot partitions can also be locked permanently, but this feature is | ||
39 | not accessible through sysfs in order to avoid accidental or malicious | ||
40 | bricking. | ||
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index ad0fb8d74dda..0c959c96005e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c | |||
@@ -107,6 +107,8 @@ struct mmc_blk_data { | |||
107 | */ | 107 | */ |
108 | unsigned int part_curr; | 108 | unsigned int part_curr; |
109 | struct device_attribute force_ro; | 109 | struct device_attribute force_ro; |
110 | struct device_attribute power_ro_lock; | ||
111 | int area_type; | ||
110 | }; | 112 | }; |
111 | 113 | ||
112 | static DEFINE_MUTEX(open_lock); | 114 | static DEFINE_MUTEX(open_lock); |
@@ -165,6 +167,70 @@ static void mmc_blk_put(struct mmc_blk_data *md) | |||
165 | mutex_unlock(&open_lock); | 167 | mutex_unlock(&open_lock); |
166 | } | 168 | } |
167 | 169 | ||
170 | static ssize_t power_ro_lock_show(struct device *dev, | ||
171 | struct device_attribute *attr, char *buf) | ||
172 | { | ||
173 | int ret; | ||
174 | struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); | ||
175 | struct mmc_card *card = md->queue.card; | ||
176 | int locked = 0; | ||
177 | |||
178 | if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PERM_WP_EN) | ||
179 | locked = 2; | ||
180 | else if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_EN) | ||
181 | locked = 1; | ||
182 | |||
183 | ret = snprintf(buf, PAGE_SIZE, "%d\n", locked); | ||
184 | |||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | static ssize_t power_ro_lock_store(struct device *dev, | ||
189 | struct device_attribute *attr, const char *buf, size_t count) | ||
190 | { | ||
191 | int ret; | ||
192 | struct mmc_blk_data *md, *part_md; | ||
193 | struct mmc_card *card; | ||
194 | unsigned long set; | ||
195 | |||
196 | if (kstrtoul(buf, 0, &set)) | ||
197 | return -EINVAL; | ||
198 | |||
199 | if (set != 1) | ||
200 | return count; | ||
201 | |||
202 | md = mmc_blk_get(dev_to_disk(dev)); | ||
203 | card = md->queue.card; | ||
204 | |||
205 | mmc_claim_host(card->host); | ||
206 | |||
207 | ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, | ||
208 | card->ext_csd.boot_ro_lock | | ||
209 | EXT_CSD_BOOT_WP_B_PWR_WP_EN, | ||
210 | card->ext_csd.part_time); | ||
211 | if (ret) | ||
212 | pr_err("%s: Locking boot partition ro until next power on failed: %d\n", md->disk->disk_name, ret); | ||
213 | else | ||
214 | card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN; | ||
215 | |||
216 | mmc_release_host(card->host); | ||
217 | |||
218 | if (!ret) { | ||
219 | pr_info("%s: Locking boot partition ro until next power on\n", | ||
220 | md->disk->disk_name); | ||
221 | set_disk_ro(md->disk, 1); | ||
222 | |||
223 | list_for_each_entry(part_md, &md->part, part) | ||
224 | if (part_md->area_type == MMC_BLK_DATA_AREA_BOOT) { | ||
225 | pr_info("%s: Locking boot partition ro until next power on\n", part_md->disk->disk_name); | ||
226 | set_disk_ro(part_md->disk, 1); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | mmc_blk_put(md); | ||
231 | return count; | ||
232 | } | ||
233 | |||
168 | static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr, | 234 | static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr, |
169 | char *buf) | 235 | char *buf) |
170 | { | 236 | { |
@@ -1347,7 +1413,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, | |||
1347 | struct device *parent, | 1413 | struct device *parent, |
1348 | sector_t size, | 1414 | sector_t size, |
1349 | bool default_ro, | 1415 | bool default_ro, |
1350 | const char *subname) | 1416 | const char *subname, |
1417 | int area_type) | ||
1351 | { | 1418 | { |
1352 | struct mmc_blk_data *md; | 1419 | struct mmc_blk_data *md; |
1353 | int devidx, ret; | 1420 | int devidx, ret; |
@@ -1372,11 +1439,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, | |||
1372 | if (!subname) { | 1439 | if (!subname) { |
1373 | md->name_idx = find_first_zero_bit(name_use, max_devices); | 1440 | md->name_idx = find_first_zero_bit(name_use, max_devices); |
1374 | __set_bit(md->name_idx, name_use); | 1441 | __set_bit(md->name_idx, name_use); |
1375 | } | 1442 | } else |
1376 | else | ||
1377 | md->name_idx = ((struct mmc_blk_data *) | 1443 | md->name_idx = ((struct mmc_blk_data *) |
1378 | dev_to_disk(parent)->private_data)->name_idx; | 1444 | dev_to_disk(parent)->private_data)->name_idx; |
1379 | 1445 | ||
1446 | md->area_type = area_type; | ||
1447 | |||
1380 | /* | 1448 | /* |
1381 | * Set the read-only status based on the supported commands | 1449 | * Set the read-only status based on the supported commands |
1382 | * and the write protect switch. | 1450 | * and the write protect switch. |
@@ -1470,7 +1538,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) | |||
1470 | size = card->csd.capacity << (card->csd.read_blkbits - 9); | 1538 | size = card->csd.capacity << (card->csd.read_blkbits - 9); |
1471 | } | 1539 | } |
1472 | 1540 | ||
1473 | md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL); | 1541 | md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL, |
1542 | MMC_BLK_DATA_AREA_MAIN); | ||
1474 | return md; | 1543 | return md; |
1475 | } | 1544 | } |
1476 | 1545 | ||
@@ -1479,13 +1548,14 @@ static int mmc_blk_alloc_part(struct mmc_card *card, | |||
1479 | unsigned int part_type, | 1548 | unsigned int part_type, |
1480 | sector_t size, | 1549 | sector_t size, |
1481 | bool default_ro, | 1550 | bool default_ro, |
1482 | const char *subname) | 1551 | const char *subname, |
1552 | int area_type) | ||
1483 | { | 1553 | { |
1484 | char cap_str[10]; | 1554 | char cap_str[10]; |
1485 | struct mmc_blk_data *part_md; | 1555 | struct mmc_blk_data *part_md; |
1486 | 1556 | ||
1487 | part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro, | 1557 | part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro, |
1488 | subname); | 1558 | subname, area_type); |
1489 | if (IS_ERR(part_md)) | 1559 | if (IS_ERR(part_md)) |
1490 | return PTR_ERR(part_md); | 1560 | return PTR_ERR(part_md); |
1491 | part_md->part_type = part_type; | 1561 | part_md->part_type = part_type; |
@@ -1518,7 +1588,8 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) | |||
1518 | card->part[idx].part_cfg, | 1588 | card->part[idx].part_cfg, |
1519 | card->part[idx].size >> 9, | 1589 | card->part[idx].size >> 9, |
1520 | card->part[idx].force_ro, | 1590 | card->part[idx].force_ro, |
1521 | card->part[idx].name); | 1591 | card->part[idx].name, |
1592 | card->part[idx].area_type); | ||
1522 | if (ret) | 1593 | if (ret) |
1523 | return ret; | 1594 | return ret; |
1524 | } | 1595 | } |
@@ -1547,9 +1618,16 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) | |||
1547 | 1618 | ||
1548 | static void mmc_blk_remove_req(struct mmc_blk_data *md) | 1619 | static void mmc_blk_remove_req(struct mmc_blk_data *md) |
1549 | { | 1620 | { |
1621 | struct mmc_card *card; | ||
1622 | |||
1550 | if (md) { | 1623 | if (md) { |
1624 | card = md->queue.card; | ||
1551 | if (md->disk->flags & GENHD_FL_UP) { | 1625 | if (md->disk->flags & GENHD_FL_UP) { |
1552 | device_remove_file(disk_to_dev(md->disk), &md->force_ro); | 1626 | device_remove_file(disk_to_dev(md->disk), &md->force_ro); |
1627 | if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) && | ||
1628 | card->ext_csd.boot_ro_lockable) | ||
1629 | device_remove_file(disk_to_dev(md->disk), | ||
1630 | &md->power_ro_lock); | ||
1553 | 1631 | ||
1554 | /* Stop new requests from getting into the queue */ | 1632 | /* Stop new requests from getting into the queue */ |
1555 | del_gendisk(md->disk); | 1633 | del_gendisk(md->disk); |
@@ -1578,6 +1656,7 @@ static void mmc_blk_remove_parts(struct mmc_card *card, | |||
1578 | static int mmc_add_disk(struct mmc_blk_data *md) | 1656 | static int mmc_add_disk(struct mmc_blk_data *md) |
1579 | { | 1657 | { |
1580 | int ret; | 1658 | int ret; |
1659 | struct mmc_card *card = md->queue.card; | ||
1581 | 1660 | ||
1582 | add_disk(md->disk); | 1661 | add_disk(md->disk); |
1583 | md->force_ro.show = force_ro_show; | 1662 | md->force_ro.show = force_ro_show; |
@@ -1587,7 +1666,33 @@ static int mmc_add_disk(struct mmc_blk_data *md) | |||
1587 | md->force_ro.attr.mode = S_IRUGO | S_IWUSR; | 1666 | md->force_ro.attr.mode = S_IRUGO | S_IWUSR; |
1588 | ret = device_create_file(disk_to_dev(md->disk), &md->force_ro); | 1667 | ret = device_create_file(disk_to_dev(md->disk), &md->force_ro); |
1589 | if (ret) | 1668 | if (ret) |
1590 | del_gendisk(md->disk); | 1669 | goto force_ro_fail; |
1670 | |||
1671 | if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) && | ||
1672 | card->ext_csd.boot_ro_lockable) { | ||
1673 | mode_t mode; | ||
1674 | |||
1675 | if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_DIS) | ||
1676 | mode = S_IRUGO; | ||
1677 | else | ||
1678 | mode = S_IRUGO | S_IWUSR; | ||
1679 | |||
1680 | md->power_ro_lock.show = power_ro_lock_show; | ||
1681 | md->power_ro_lock.store = power_ro_lock_store; | ||
1682 | md->power_ro_lock.attr.mode = mode; | ||
1683 | md->power_ro_lock.attr.name = | ||
1684 | "ro_lock_until_next_power_on"; | ||
1685 | ret = device_create_file(disk_to_dev(md->disk), | ||
1686 | &md->power_ro_lock); | ||
1687 | if (ret) | ||
1688 | goto power_ro_lock_fail; | ||
1689 | } | ||
1690 | return ret; | ||
1691 | |||
1692 | power_ro_lock_fail: | ||
1693 | device_remove_file(disk_to_dev(md->disk), &md->force_ro); | ||
1694 | force_ro_fail: | ||
1695 | del_gendisk(md->disk); | ||
1591 | 1696 | ||
1592 | return ret; | 1697 | return ret; |
1593 | } | 1698 | } |
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fc1059bb6a08..006e932a3ae3 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -348,7 +348,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
348 | part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; | 348 | part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; |
349 | mmc_part_add(card, part_size, | 349 | mmc_part_add(card, part_size, |
350 | EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx, | 350 | EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx, |
351 | "boot%d", idx, true); | 351 | "boot%d", idx, true, |
352 | MMC_BLK_DATA_AREA_BOOT); | ||
352 | } | 353 | } |
353 | } | 354 | } |
354 | } | 355 | } |
@@ -435,7 +436,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
435 | hc_wp_grp_sz); | 436 | hc_wp_grp_sz); |
436 | mmc_part_add(card, part_size << 19, | 437 | mmc_part_add(card, part_size << 19, |
437 | EXT_CSD_PART_CONFIG_ACC_GP0 + idx, | 438 | EXT_CSD_PART_CONFIG_ACC_GP0 + idx, |
438 | "gp%d", idx, false); | 439 | "gp%d", idx, false, |
440 | MMC_BLK_DATA_AREA_GP); | ||
439 | } | 441 | } |
440 | } | 442 | } |
441 | card->ext_csd.sec_trim_mult = | 443 | card->ext_csd.sec_trim_mult = |
@@ -446,6 +448,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
446 | ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; | 448 | ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; |
447 | card->ext_csd.trim_timeout = 300 * | 449 | card->ext_csd.trim_timeout = 300 * |
448 | ext_csd[EXT_CSD_TRIM_MULT]; | 450 | ext_csd[EXT_CSD_TRIM_MULT]; |
451 | |||
452 | /* | ||
453 | * Note that the call to mmc_part_add above defaults to read | ||
454 | * only. If this default assumption is changed, the call must | ||
455 | * take into account the value of boot_locked below. | ||
456 | */ | ||
457 | card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP]; | ||
458 | card->ext_csd.boot_ro_lockable = true; | ||
449 | } | 459 | } |
450 | 460 | ||
451 | if (card->ext_csd.rev >= 5) { | 461 | if (card->ext_csd.rev >= 5) { |
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 6402d9224d6a..9478a6bf1bb1 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h | |||
@@ -71,6 +71,8 @@ struct mmc_ext_csd { | |||
71 | bool hpi_en; /* HPI enablebit */ | 71 | bool hpi_en; /* HPI enablebit */ |
72 | bool hpi; /* HPI support bit */ | 72 | bool hpi; /* HPI support bit */ |
73 | unsigned int hpi_cmd; /* cmd used as HPI */ | 73 | unsigned int hpi_cmd; /* cmd used as HPI */ |
74 | unsigned int boot_ro_lock; /* ro lock support */ | ||
75 | bool boot_ro_lockable; | ||
74 | u8 raw_partition_support; /* 160 */ | 76 | u8 raw_partition_support; /* 160 */ |
75 | u8 raw_erased_mem_count; /* 181 */ | 77 | u8 raw_erased_mem_count; /* 181 */ |
76 | u8 raw_ext_csd_structure; /* 194 */ | 78 | u8 raw_ext_csd_structure; /* 194 */ |
@@ -187,6 +189,10 @@ struct mmc_part { | |||
187 | unsigned int part_cfg; /* partition type */ | 189 | unsigned int part_cfg; /* partition type */ |
188 | char name[MAX_MMC_PART_NAME_LEN]; | 190 | char name[MAX_MMC_PART_NAME_LEN]; |
189 | bool force_ro; /* to make boot parts RO by default */ | 191 | bool force_ro; /* to make boot parts RO by default */ |
192 | unsigned int area_type; | ||
193 | #define MMC_BLK_DATA_AREA_MAIN (1<<0) | ||
194 | #define MMC_BLK_DATA_AREA_BOOT (1<<1) | ||
195 | #define MMC_BLK_DATA_AREA_GP (1<<2) | ||
190 | }; | 196 | }; |
191 | 197 | ||
192 | /* | 198 | /* |
@@ -265,12 +271,14 @@ struct mmc_card { | |||
265 | * This function fill contents in mmc_part. | 271 | * This function fill contents in mmc_part. |
266 | */ | 272 | */ |
267 | static inline void mmc_part_add(struct mmc_card *card, unsigned int size, | 273 | static inline void mmc_part_add(struct mmc_card *card, unsigned int size, |
268 | unsigned int part_cfg, char *name, int idx, bool ro) | 274 | unsigned int part_cfg, char *name, int idx, bool ro, |
275 | int area_type) | ||
269 | { | 276 | { |
270 | card->part[card->nr_parts].size = size; | 277 | card->part[card->nr_parts].size = size; |
271 | card->part[card->nr_parts].part_cfg = part_cfg; | 278 | card->part[card->nr_parts].part_cfg = part_cfg; |
272 | sprintf(card->part[card->nr_parts].name, name, idx); | 279 | sprintf(card->part[card->nr_parts].name, name, idx); |
273 | card->part[card->nr_parts].force_ro = ro; | 280 | card->part[card->nr_parts].force_ro = ro; |
281 | card->part[card->nr_parts].area_type = area_type; | ||
274 | card->nr_parts++; | 282 | card->nr_parts++; |
275 | } | 283 | } |
276 | 284 | ||
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 0e7135697d11..665548e639e8 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h | |||
@@ -280,6 +280,7 @@ struct _mmc_csd { | |||
280 | #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ | 280 | #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ |
281 | #define EXT_CSD_SANITIZE_START 165 /* W */ | 281 | #define EXT_CSD_SANITIZE_START 165 /* W */ |
282 | #define EXT_CSD_WR_REL_PARAM 166 /* RO */ | 282 | #define EXT_CSD_WR_REL_PARAM 166 /* RO */ |
283 | #define EXT_CSD_BOOT_WP 173 /* R/W */ | ||
283 | #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ | 284 | #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ |
284 | #define EXT_CSD_PART_CONFIG 179 /* R/W */ | 285 | #define EXT_CSD_PART_CONFIG 179 /* R/W */ |
285 | #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ | 286 | #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ |
@@ -321,6 +322,11 @@ struct _mmc_csd { | |||
321 | 322 | ||
322 | #define EXT_CSD_WR_REL_PARAM_EN (1<<2) | 323 | #define EXT_CSD_WR_REL_PARAM_EN (1<<2) |
323 | 324 | ||
325 | #define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40) | ||
326 | #define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10) | ||
327 | #define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04) | ||
328 | #define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01) | ||
329 | |||
324 | #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) | 330 | #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) |
325 | #define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) | 331 | #define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) |
326 | #define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) | 332 | #define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) |