mmc_cmds.c revision 22f2641fe6155fe9fb8b38a8ebe2093ec3e2ec11
1/* 2 * This program is free software; you can redistribute it and/or 3 * modify it under the terms of the GNU General Public 4 * License v2 as published by the Free Software Foundation. 5 * 6 * This program is distributed in the hope that it will be useful, 7 * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 * General Public License for more details. 10 * 11 * You should have received a copy of the GNU General Public 12 * License along with this program; if not, write to the 13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 14 * Boston, MA 021110-1307, USA. 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20#include <sys/ioctl.h> 21#include <sys/types.h> 22#include <dirent.h> 23#include <sys/stat.h> 24#include <unistd.h> 25#include <fcntl.h> 26#include <libgen.h> 27#include <limits.h> 28#include <ctype.h> 29 30#include "mmc.h" 31#include "mmc_cmds.h" 32 33int read_extcsd(int fd, __u8 *ext_csd) 34{ 35 int ret = 0; 36 struct mmc_ioc_cmd idata; 37 memset(&idata, 0, sizeof(idata)); 38 memset(ext_csd, 0, sizeof(__u8) * 512); 39 idata.write_flag = 0; 40 idata.opcode = MMC_SEND_EXT_CSD; 41 idata.arg = 0; 42 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; 43 idata.blksz = 512; 44 idata.blocks = 1; 45 mmc_ioc_cmd_set_data(idata, ext_csd); 46 47 ret = ioctl(fd, MMC_IOC_CMD, &idata); 48 if (ret) 49 perror("ioctl"); 50 51 return ret; 52} 53 54int write_extcsd_value(int fd, __u8 index, __u8 value) 55{ 56 int ret = 0; 57 struct mmc_ioc_cmd idata; 58 59 memset(&idata, 0, sizeof(idata)); 60 idata.write_flag = 1; 61 idata.opcode = MMC_SWITCH; 62 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | 63 (index << 16) | 64 (value << 8) | 65 EXT_CSD_CMD_SET_NORMAL; 66 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; 67 68 ret = ioctl(fd, MMC_IOC_CMD, &idata); 69 if (ret) 70 perror("ioctl"); 71 72 return ret; 73} 74 75int send_status(int fd, __u32 *response) 76{ 77 int ret = 0; 78 struct mmc_ioc_cmd idata; 79 80 memset(&idata, 0, sizeof(idata)); 81 idata.opcode = MMC_SEND_STATUS; 82 idata.arg = (1 << 16); 83 idata.flags = MMC_RSP_R1 | MMC_CMD_AC; 84 85 ret = ioctl(fd, MMC_IOC_CMD, &idata); 86 if (ret) 87 perror("ioctl"); 88 89 *response = idata.response[0]; 90 91 return ret; 92} 93 94void print_writeprotect_status(__u8 *ext_csd) 95{ 96 __u8 reg; 97 __u8 ext_csd_rev = ext_csd[192]; 98 99 /* A43: reserved [174:0] */ 100 if (ext_csd_rev >= 5) { 101 printf("Boot write protection status registers" 102 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]); 103 104 reg = ext_csd[EXT_CSD_BOOT_WP]; 105 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg); 106 printf(" Power ro locking: "); 107 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS) 108 printf("not possible\n"); 109 else 110 printf("possible\n"); 111 112 printf(" Permanent ro locking: "); 113 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS) 114 printf("not possible\n"); 115 else 116 printf("possible\n"); 117 118 printf(" ro lock status: "); 119 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN) 120 printf("locked until next power on\n"); 121 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN) 122 printf("locked permanently\n"); 123 else 124 printf("not locked\n"); 125 } 126} 127 128int do_writeprotect_get(int nargs, char **argv) 129{ 130 __u8 ext_csd[512]; 131 int fd, ret; 132 char *device; 133 134 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n", 135 exit(1)); 136 137 device = argv[1]; 138 139 fd = open(device, O_RDWR); 140 if (fd < 0) { 141 perror("open"); 142 exit(1); 143 } 144 145 ret = read_extcsd(fd, ext_csd); 146 if (ret) { 147 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 148 exit(1); 149 } 150 151 print_writeprotect_status(ext_csd); 152 153 return ret; 154} 155 156int do_writeprotect_set(int nargs, char **argv) 157{ 158 __u8 ext_csd[512], value; 159 int fd, ret; 160 char *device; 161 162 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n", 163 exit(1)); 164 165 device = argv[1]; 166 167 fd = open(device, O_RDWR); 168 if (fd < 0) { 169 perror("open"); 170 exit(1); 171 } 172 173 ret = read_extcsd(fd, ext_csd); 174 if (ret) { 175 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 176 exit(1); 177 } 178 179 value = ext_csd[EXT_CSD_BOOT_WP] | 180 EXT_CSD_BOOT_WP_B_PWR_WP_EN; 181 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value); 182 if (ret) { 183 fprintf(stderr, "Could not write 0x%02x to " 184 "EXT_CSD[%d] in %s\n", 185 value, EXT_CSD_BOOT_WP, device); 186 exit(1); 187 } 188 189 return ret; 190} 191 192int do_disable_512B_emulation(int nargs, char **argv) 193{ 194 __u8 ext_csd[512], native_sector_size, data_sector_size, wr_rel_param; 195 int fd, ret; 196 char *device; 197 198 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1)); 199 device = argv[1]; 200 201 fd = open(device, O_RDWR); 202 if (fd < 0) { 203 perror("open"); 204 exit(1); 205 } 206 207 ret = read_extcsd(fd, ext_csd); 208 if (ret) { 209 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 210 exit(1); 211 } 212 213 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; 214 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE]; 215 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE]; 216 217 if (native_sector_size && !data_sector_size && 218 (wr_rel_param & EN_REL_WR)) { 219 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1); 220 221 if (ret) { 222 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 223 1, EXT_CSD_BOOT_WP, device); 224 exit(1); 225 } 226 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n"); 227 } else if (native_sector_size && data_sector_size) { 228 printf("MMC 512B emulation mode is already disabled; doing nothing.\n"); 229 } else { 230 printf("MMC does not support disabling 512B emulation mode.\n"); 231 } 232 233 return ret; 234} 235 236int do_write_boot_en(int nargs, char **argv) 237{ 238 __u8 ext_csd[512]; 239 __u8 value = 0; 240 int fd, ret; 241 char *device; 242 int boot_area, send_ack; 243 244 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> " 245 "<send_ack> </path/to/mmcblkX>\n", exit(1)); 246 247 /* 248 * If <send_ack> is 1, the device will send acknowledgment 249 * pattern "010" to the host when boot operation begins. 250 * If <send_ack> is 0, it won't. 251 */ 252 boot_area = strtol(argv[1], NULL, 10); 253 send_ack = strtol(argv[2], NULL, 10); 254 device = argv[3]; 255 256 fd = open(device, O_RDWR); 257 if (fd < 0) { 258 perror("open"); 259 exit(1); 260 } 261 262 ret = read_extcsd(fd, ext_csd); 263 if (ret) { 264 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 265 exit(1); 266 } 267 268 value = ext_csd[EXT_CSD_PART_CONFIG]; 269 270 switch (boot_area) { 271 case EXT_CSD_PART_CONFIG_ACC_BOOT0: 272 value |= (1 << 3); 273 value &= ~(3 << 4); 274 break; 275 case EXT_CSD_PART_CONFIG_ACC_BOOT1: 276 value |= (1 << 4); 277 value &= ~(1 << 3); 278 value &= ~(1 << 5); 279 break; 280 case EXT_CSD_PART_CONFIG_ACC_USER_AREA: 281 value |= (boot_area << 3); 282 break; 283 default: 284 fprintf(stderr, "Cannot enable the boot area\n"); 285 exit(1); 286 } 287 if (send_ack) 288 value |= EXT_CSD_PART_CONFIG_ACC_ACK; 289 else 290 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK; 291 292 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value); 293 if (ret) { 294 fprintf(stderr, "Could not write 0x%02x to " 295 "EXT_CSD[%d] in %s\n", 296 value, EXT_CSD_PART_CONFIG, device); 297 exit(1); 298 } 299 return ret; 300} 301 302int do_hwreset(int value, int nargs, char **argv) 303{ 304 __u8 ext_csd[512]; 305 int fd, ret; 306 char *device; 307 308 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n", 309 exit(1)); 310 311 device = argv[1]; 312 313 fd = open(device, O_RDWR); 314 if (fd < 0) { 315 perror("open"); 316 exit(1); 317 } 318 319 ret = read_extcsd(fd, ext_csd); 320 if (ret) { 321 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 322 exit(1); 323 } 324 325 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) == 326 EXT_CSD_HW_RESET_EN) { 327 fprintf(stderr, 328 "H/W Reset is already permanently enabled on %s\n", 329 device); 330 exit(1); 331 } 332 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) == 333 EXT_CSD_HW_RESET_DIS) { 334 fprintf(stderr, 335 "H/W Reset is already permanently disabled on %s\n", 336 device); 337 exit(1); 338 } 339 340 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value); 341 if (ret) { 342 fprintf(stderr, 343 "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 344 value, EXT_CSD_RST_N_FUNCTION, device); 345 exit(1); 346 } 347 348 return ret; 349} 350 351int do_hwreset_en(int nargs, char **argv) 352{ 353 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv); 354} 355 356int do_hwreset_dis(int nargs, char **argv) 357{ 358 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv); 359} 360 361int do_write_bkops_en(int nargs, char **argv) 362{ 363 __u8 ext_csd[512], value = 0; 364 int fd, ret; 365 char *device; 366 367 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n", 368 exit(1)); 369 370 device = argv[1]; 371 372 fd = open(device, O_RDWR); 373 if (fd < 0) { 374 perror("open"); 375 exit(1); 376 } 377 378 ret = read_extcsd(fd, ext_csd); 379 if (ret) { 380 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 381 exit(1); 382 } 383 384 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) { 385 fprintf(stderr, "%s doesn't support BKOPS\n", device); 386 exit(1); 387 } 388 389 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE); 390 if (ret) { 391 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 392 value, EXT_CSD_BKOPS_EN, device); 393 exit(1); 394 } 395 396 return ret; 397} 398 399int do_status_get(int nargs, char **argv) 400{ 401 __u32 response; 402 int fd, ret; 403 char *device; 404 405 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n", 406 exit(1)); 407 408 device = argv[1]; 409 410 fd = open(device, O_RDWR); 411 if (fd < 0) { 412 perror("open"); 413 exit(1); 414 } 415 416 ret = send_status(fd, &response); 417 if (ret) { 418 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device); 419 exit(1); 420 } 421 422 printf("SEND_STATUS response: 0x%08x\n", response); 423 424 return ret; 425} 426 427unsigned int get_sector_count(__u8 *ext_csd) 428{ 429 return (ext_csd[EXT_CSD_SEC_COUNT_3] << 24) | 430 (ext_csd[EXT_CSD_SEC_COUNT_2] << 16) | 431 (ext_csd[EXT_CSD_SEC_COUNT_1] << 8) | 432 ext_csd[EXT_CSD_SEC_COUNT_0]; 433} 434 435int is_blockaddresed(__u8 *ext_csd) 436{ 437 unsigned int sectors = get_sector_count(ext_csd); 438 439 return (sectors > (2u * 1024 * 1024 * 1024) / 512); 440} 441 442unsigned int get_hc_wp_grp_size(__u8 *ext_csd) 443{ 444 return ext_csd[221]; 445} 446 447unsigned int get_hc_erase_grp_size(__u8 *ext_csd) 448{ 449 return ext_csd[224]; 450} 451 452int set_partitioning_setting_completed(int dry_run, const char * const device, 453 int fd) 454{ 455 int ret; 456 457 if (dry_run) { 458 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n"); 459 fprintf(stderr, "These changes will not take effect neither " 460 "now nor after a power cycle\n"); 461 return 1; 462 } 463 464 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n"); 465 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1); 466 if (ret) { 467 fprintf(stderr, "Could not write 0x1 to " 468 "EXT_CSD[%d] in %s\n", 469 EXT_CSD_PARTITION_SETTING_COMPLETED, device); 470 return 1; 471 } 472 473 __u32 response; 474 ret = send_status(fd, &response); 475 if (ret) { 476 fprintf(stderr, "Could not get response to SEND_STATUS " 477 "from %s\n", device); 478 return 1; 479 } 480 481 if (response & R1_SWITCH_ERROR) { 482 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED " 483 "failed on %s\n", device); 484 return 1; 485 } 486 487 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on " 488 "%s SUCCESS\n", device); 489 fprintf(stderr, "Device power cycle needed for settings to " 490 "take effect.\n" 491 "Confirm that PARTITION_SETTING_COMPLETED bit is set " 492 "using 'extcsd read' after power cycle\n"); 493 494 return 0; 495} 496 497int do_enh_area_set(int nargs, char **argv) 498{ 499 __u8 value; 500 __u8 ext_csd[512]; 501 int fd, ret; 502 char *device; 503 int dry_run = 1; 504 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult; 505 unsigned long align; 506 507 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> " 508 "</path/to/mmcblkX>\n", exit(1)); 509 510 if (!strcmp("-y", argv[1])) 511 dry_run = 0; 512 513 start_kib = strtol(argv[2], NULL, 10); 514 length_kib = strtol(argv[3], NULL, 10); 515 device = argv[4]; 516 517 fd = open(device, O_RDWR); 518 if (fd < 0) { 519 perror("open"); 520 exit(1); 521 } 522 523 ret = read_extcsd(fd, ext_csd); 524 if (ret) { 525 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 526 exit(1); 527 } 528 529 /* assert ENH_ATTRIBUTE_EN */ 530 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN)) 531 { 532 printf(" Device cannot have enhanced tech.\n"); 533 exit(1); 534 } 535 536 /* assert not PARTITION_SETTING_COMPLETED */ 537 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) 538 { 539 printf(" Device is already partitioned\n"); 540 exit(1); 541 } 542 543 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd); 544 545 enh_size_mult = (length_kib + align/2l) / align; 546 547 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1); 548 enh_start_addr /= align; 549 enh_start_addr *= align; 550 551 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */ 552 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1); 553 if (ret) { 554 fprintf(stderr, "Could not write 0x1 to " 555 "EXT_CSD[%d] in %s\n", 556 EXT_CSD_ERASE_GROUP_DEF, device); 557 exit(1); 558 } 559 560 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */ 561 value = (enh_start_addr >> 24) & 0xff; 562 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value); 563 if (ret) { 564 fprintf(stderr, "Could not write 0x%02x to " 565 "EXT_CSD[%d] in %s\n", value, 566 EXT_CSD_ENH_START_ADDR_3, device); 567 exit(1); 568 } 569 value = (enh_start_addr >> 16) & 0xff; 570 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value); 571 if (ret) { 572 fprintf(stderr, "Could not write 0x%02x to " 573 "EXT_CSD[%d] in %s\n", value, 574 EXT_CSD_ENH_START_ADDR_2, device); 575 exit(1); 576 } 577 value = (enh_start_addr >> 8) & 0xff; 578 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value); 579 if (ret) { 580 fprintf(stderr, "Could not write 0x%02x to " 581 "EXT_CSD[%d] in %s\n", value, 582 EXT_CSD_ENH_START_ADDR_1, device); 583 exit(1); 584 } 585 value = enh_start_addr & 0xff; 586 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value); 587 if (ret) { 588 fprintf(stderr, "Could not write 0x%02x to " 589 "EXT_CSD[%d] in %s\n", value, 590 EXT_CSD_ENH_START_ADDR_0, device); 591 exit(1); 592 } 593 594 value = (enh_size_mult >> 16) & 0xff; 595 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value); 596 if (ret) { 597 fprintf(stderr, "Could not write 0x%02x to " 598 "EXT_CSD[%d] in %s\n", value, 599 EXT_CSD_ENH_SIZE_MULT_2, device); 600 exit(1); 601 } 602 value = (enh_size_mult >> 8) & 0xff; 603 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value); 604 if (ret) { 605 fprintf(stderr, "Could not write 0x%02x to " 606 "EXT_CSD[%d] in %s\n", value, 607 EXT_CSD_ENH_SIZE_MULT_1, device); 608 exit(1); 609 } 610 value = enh_size_mult & 0xff; 611 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value); 612 if (ret) { 613 fprintf(stderr, "Could not write 0x%02x to " 614 "EXT_CSD[%d] in %s\n", value, 615 EXT_CSD_ENH_SIZE_MULT_0, device); 616 exit(1); 617 } 618 619 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, EXT_CSD_ENH_USR); 620 if (ret) { 621 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to " 622 "EXT_CSD[%d] in %s\n", 623 EXT_CSD_PARTITIONS_ATTRIBUTE, device); 624 exit(1); 625 } 626 627 printf("Done setting ENH_USR area on %s\n", device); 628 629 if (!set_partitioning_setting_completed(dry_run, device, fd)) 630 exit(1); 631 632 return 0; 633} 634 635int do_write_reliability_set(int nargs, char **argv) 636{ 637 __u8 value; 638 __u8 ext_csd[512]; 639 int fd, ret; 640 641 int dry_run = 1; 642 int partition; 643 char *device; 644 645 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> " 646 "<partition> </path/to/mmcblkX>\n", exit(1)); 647 648 if (!strcmp("-y", argv[1])) 649 dry_run = 0; 650 651 partition = strtol(argv[2], NULL, 10); 652 device = argv[3]; 653 654 fd = open(device, O_RDWR); 655 if (fd < 0) { 656 perror("open"); 657 exit(1); 658 } 659 660 ret = read_extcsd(fd, ext_csd); 661 if (ret) { 662 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 663 exit(1); 664 } 665 666 /* assert not PARTITION_SETTING_COMPLETED */ 667 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) 668 { 669 printf(" Device is already partitioned\n"); 670 exit(1); 671 } 672 673 /* assert HS_CTRL_REL */ 674 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) { 675 printf("Cannot set write reliability parameters, WR_REL_SET is " 676 "read-only\n"); 677 exit(1); 678 } 679 680 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition); 681 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value); 682 if (ret) { 683 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 684 value, EXT_CSD_WR_REL_SET, device); 685 exit(1); 686 } 687 688 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n", 689 value, device); 690 691 if (!set_partitioning_setting_completed(dry_run, device, fd)) 692 exit(1); 693 694 return 0; 695} 696 697int do_read_extcsd(int nargs, char **argv) 698{ 699 __u8 ext_csd[512], ext_csd_rev, reg; 700 int fd, ret; 701 char *device; 702 const char *str; 703 704 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n", 705 exit(1)); 706 707 device = argv[1]; 708 709 fd = open(device, O_RDWR); 710 if (fd < 0) { 711 perror("open"); 712 exit(1); 713 } 714 715 ret = read_extcsd(fd, ext_csd); 716 if (ret) { 717 fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 718 exit(1); 719 } 720 721 ext_csd_rev = ext_csd[192]; 722 723 switch (ext_csd_rev) { 724 case 6: 725 str = "4.5"; 726 break; 727 case 5: 728 str = "4.41"; 729 break; 730 case 3: 731 str = "4.3"; 732 break; 733 case 2: 734 str = "4.2"; 735 break; 736 case 1: 737 str = "4.1"; 738 break; 739 case 0: 740 str = "4.0"; 741 break; 742 default: 743 goto out_free; 744 } 745 printf("=============================================\n"); 746 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str); 747 printf("=============================================\n\n"); 748 749 if (ext_csd_rev < 3) 750 goto out_free; /* No ext_csd */ 751 752 /* Parse the Extended CSD registers. 753 * Reserved bit should be read as "0" in case of spec older 754 * than A441. 755 */ 756 reg = ext_csd[EXT_CSD_S_CMD_SET]; 757 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg); 758 if (!reg) 759 printf(" - Standard MMC command sets\n"); 760 761 reg = ext_csd[EXT_CSD_HPI_FEATURE]; 762 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg); 763 if (reg & EXT_CSD_HPI_SUPP) { 764 if (reg & EXT_CSD_HPI_IMPL) 765 printf("implementation based on CMD12\n"); 766 else 767 printf("implementation based on CMD13\n"); 768 } 769 770 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n", 771 ext_csd[502]); 772 773 if (ext_csd_rev >= 6) { 774 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n", 775 ext_csd[501]); 776 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n", 777 ext_csd[500]); 778 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n", 779 ext_csd[499]); 780 781 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n", 782 ext_csd[498]); 783 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n", 784 ext_csd[497]); 785 printf("Context Management Capabilities" 786 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]); 787 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n", 788 ext_csd[495]); 789 printf("Extended partition attribute support" 790 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]); 791 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n", 792 ext_csd[248]); 793 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n", 794 ext_csd[247]); 795 printf("Cache Size [CACHE_SIZE] is %d KiB\n", 796 ext_csd[249] << 0 | (ext_csd[250] << 8) | 797 (ext_csd[251] << 16) | (ext_csd[252] << 24)); 798 } 799 800 /* A441: Reserved [501:247] 801 A43: reserved [246:229] */ 802 if (ext_csd_rev >= 5) { 803 printf("Background operations status" 804 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]); 805 806 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */ 807 808 printf("1st Initialisation Time after programmed sector" 809 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]); 810 811 /* A441: reserved [240] */ 812 printf("Power class for 52MHz, DDR at 3.6V" 813 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]); 814 printf("Power class for 52MHz, DDR at 1.95V" 815 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]); 816 817 /* A441: reserved [237-236] */ 818 819 if (ext_csd_rev >= 6) { 820 printf("Power class for 200MHz at 3.6V" 821 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]); 822 printf("Power class for 200MHz, at 1.95V" 823 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]); 824 } 825 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n"); 826 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]); 827 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]); 828 /* A441: reserved [233] */ 829 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]); 830 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n", 831 ext_csd[231]); 832 } 833 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */ 834 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n", 835 ext_csd[230]); 836 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n", 837 ext_csd[229]); 838 } 839 reg = ext_csd[EXT_CSD_BOOT_INFO]; 840 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg); 841 if (reg & EXT_CSD_BOOT_INFO_ALT) 842 printf(" Device supports alternative boot method\n"); 843 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR) 844 printf(" Device supports dual data rate during boot\n"); 845 if (reg & EXT_CSD_BOOT_INFO_HS_MODE) 846 printf(" Device supports high speed timing during boot\n"); 847 848 /* A441/A43: reserved [227] */ 849 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]); 850 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]); 851 852 reg = get_hc_erase_grp_size(ext_csd); 853 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n", 854 reg); 855 printf(" i.e. %u KiB\n", 512 * reg); 856 857 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n", 858 ext_csd[223]); 859 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n", 860 ext_csd[222]); 861 862 reg = get_hc_wp_grp_size(ext_csd); 863 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n", 864 reg); 865 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg); 866 867 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]); 868 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]); 869 /* A441/A43: reserved [218] */ 870 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]); 871 /* A441/A43: reserved [216] */ 872 873 unsigned int sectors = get_sector_count(ext_csd); 874 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors); 875 if (is_blockaddresed(ext_csd)) 876 printf(" Device is block-addressed\n"); 877 else 878 printf(" Device is NOT block-addressed\n"); 879 880 /* A441/A43: reserved [211] */ 881 printf("Minimum Write Performance for 8bit:\n"); 882 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]); 883 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]); 884 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]); 885 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]); 886 printf("Minimum Write Performance for 4bit:\n"); 887 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]); 888 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]); 889 /* A441/A43: reserved [204] */ 890 printf("Power classes registers:\n"); 891 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]); 892 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]); 893 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]); 894 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]); 895 896 /* A43: reserved [199:198] */ 897 if (ext_csd_rev >= 5) { 898 printf("Partition switching timing " 899 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]); 900 printf("Out-of-interrupt busy timing" 901 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]); 902 } 903 904 /* A441/A43: reserved [197] [195] [193] [190] [188] 905 * [186] [184] [182] [180] [176] */ 906 907 if (ext_csd_rev >= 6) 908 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n", 909 ext_csd[197]); 910 911 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */ 912 reg = ext_csd[196]; 913 printf("Card Type [CARD_TYPE: 0x%02x]\n", reg); 914 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n"); 915 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n"); 916 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n"); 917 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n"); 918 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n"); 919 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n"); 920 921 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]); 922 /* ext_csd_rev = ext_csd[192] (already done!!!) */ 923 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]); 924 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]); 925 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]); 926 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n", 927 ext_csd[185]); 928 /* bus_width: ext_csd[183] not readable */ 929 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n", 930 ext_csd[181]); 931 reg = ext_csd[EXT_CSD_BOOT_CFG]; 932 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg); 933 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) { 934 case 0x0: 935 printf(" Not boot enable\n"); 936 break; 937 case 0x1: 938 printf(" Boot Partition 1 enabled\n"); 939 break; 940 case 0x2: 941 printf(" Boot Partition 2 enabled\n"); 942 break; 943 case 0x7: 944 printf(" User Area Enabled for boot\n"); 945 break; 946 } 947 switch (reg & EXT_CSD_BOOT_CFG_ACC) { 948 case 0x0: 949 printf(" No access to boot partition\n"); 950 break; 951 case 0x1: 952 printf(" R/W Boot Partition 1\n"); 953 break; 954 case 0x2: 955 printf(" R/W Boot Partition 2\n"); 956 break; 957 case 0x3: 958 printf(" R/W Replay Protected Memory Block (RPMB)\n"); 959 break; 960 default: 961 printf(" Access to General Purpose partition %d\n", 962 (reg & EXT_CSD_BOOT_CFG_ACC) - 3); 963 break; 964 } 965 966 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n", 967 ext_csd[178]); 968 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n", 969 ext_csd[177]); 970 printf("High-density erase group definition" 971 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]); 972 973 print_writeprotect_status(ext_csd); 974 975 if (ext_csd_rev >= 5) { 976 /* A441]: reserved [172] */ 977 printf("User area write protection register" 978 " [USER_WP]: 0x%02x\n", ext_csd[171]); 979 /* A441]: reserved [170] */ 980 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]); 981 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]); 982 983 reg = ext_csd[EXT_CSD_WR_REL_SET]; 984 const char * const fast = "existing data is at risk if a power " 985 "failure occurs during a write operation"; 986 const char * const reliable = "the device protects existing " 987 "data if a power failure occurs during a write " 988 "operation"; 989 printf("Write reliability setting register" 990 " [WR_REL_SET]: 0x%02x\n", reg); 991 992 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast); 993 int i; 994 for (i = 1; i <= 4; i++) { 995 printf(" partition %d: %s\n", i, 996 reg & (1<<i) ? reliable : fast); 997 } 998 999 reg = ext_csd[EXT_CSD_WR_REL_PARAM]; 1000 printf("Write reliability parameter register" 1001 " [WR_REL_PARAM]: 0x%02x\n", reg); 1002 if (reg & 0x01) 1003 printf(" Device supports writing EXT_CSD_WR_REL_SET\n"); 1004 if (reg & 0x04) 1005 printf(" Device supports the enhanced def. of reliable " 1006 "write\n"); 1007 1008 /* sanitize_start ext_csd[165]]: not readable 1009 * bkops_start ext_csd[164]]: only writable */ 1010 printf("Enable background operations handshake" 1011 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]); 1012 printf("H/W reset function" 1013 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]); 1014 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]); 1015 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; 1016 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n", 1017 reg); 1018 if (reg & EXT_CSD_PARTITIONING_EN) 1019 printf(" Device support partitioning feature\n"); 1020 else 1021 printf(" Device NOT support partitioning feature\n"); 1022 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN) 1023 printf(" Device can have enhanced tech.\n"); 1024 else 1025 printf(" Device cannot have enhanced tech.\n"); 1026 1027 reg = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) | 1028 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) | 1029 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0]; 1030 1031 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", 1032 reg); 1033 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd); 1034 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd); 1035 printf(" i.e. %lu KiB\n", 512l * reg * wp_sz * erase_sz); 1036 1037 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n", 1038 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]); 1039 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]; 1040 printf("Partitioning Setting" 1041 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n", 1042 reg); 1043 if (reg) 1044 printf(" Device partition setting complete\n"); 1045 else 1046 printf(" Device partition setting NOT complete\n"); 1047 1048 printf("General Purpose Partition Size\n" 1049 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) | 1050 (ext_csd[153] << 8) | ext_csd[152]); 1051 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) | 1052 (ext_csd[150] << 8) | ext_csd[149]); 1053 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) | 1054 (ext_csd[147] << 8) | ext_csd[146]); 1055 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) | 1056 (ext_csd[144] << 8) | ext_csd[143]); 1057 1058 reg = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) | 1059 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) | 1060 ext_csd[EXT_CSD_ENH_SIZE_MULT_0]; 1061 printf("Enhanced User Data Area Size" 1062 " [ENH_SIZE_MULT]: 0x%06x\n", reg); 1063 printf(" i.e. %lu KiB\n", 512l * reg * 1064 get_hc_erase_grp_size(ext_csd) * 1065 get_hc_wp_grp_size(ext_csd)); 1066 1067 reg = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) | 1068 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) | 1069 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) | 1070 ext_csd[EXT_CSD_ENH_START_ADDR_0]; 1071 printf("Enhanced User Data Start Address" 1072 " [ENH_START_ADDR]: 0x%06x\n", reg); 1073 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ? 1074 1l : 512l) * reg); 1075 1076 /* A441]: reserved [135] */ 1077 printf("Bad Block Management mode" 1078 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]); 1079 /* A441: reserved [133:0] */ 1080 } 1081 /* B45 */ 1082 if (ext_csd_rev >= 6) { 1083 int j; 1084 /* tcase_support ext_csd[132] not readable */ 1085 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n", 1086 ext_csd[131]); 1087 printf("Program CID/CSD in DDR mode support" 1088 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n", 1089 ext_csd[130]); 1090 1091 for (j = 127; j >= 64; j--) 1092 printf("Vendor Specific Fields" 1093 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n", 1094 j, ext_csd[j]); 1095 1096 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n", 1097 ext_csd[63]); 1098 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n", 1099 ext_csd[62]); 1100 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", ext_csd[61]); 1101 printf("1st initialization after disabling sector" 1102 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n", 1103 ext_csd[60]); 1104 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n", 1105 ext_csd[59]); 1106 printf("Number of addressed group to be Released" 1107 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]); 1108 printf("Exception events control" 1109 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n", 1110 (ext_csd[57] << 8) | ext_csd[56]); 1111 printf("Exception events status" 1112 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n", 1113 (ext_csd[55] << 8) | ext_csd[54]); 1114 printf("Extended Partitions Attribute" 1115 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n", 1116 (ext_csd[53] << 8) | ext_csd[52]); 1117 1118 for (j = 51; j >= 37; j--) 1119 printf("Context configuration" 1120 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]); 1121 1122 printf("Packed command status" 1123 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]); 1124 printf("Packed command failure index" 1125 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]); 1126 printf("Power Off Notification" 1127 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]); 1128 printf("Control to turn the Cache ON/OFF" 1129 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]); 1130 /* flush_cache ext_csd[32] not readable */ 1131 /*Reserved [31:0] */ 1132 } 1133 1134out_free: 1135 return ret; 1136} 1137 1138int do_sanitize(int nargs, char **argv) 1139{ 1140 int fd, ret; 1141 char *device; 1142 1143 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n", 1144 exit(1)); 1145 1146 device = argv[1]; 1147 1148 fd = open(device, O_RDWR); 1149 if (fd < 0) { 1150 perror("open"); 1151 exit(1); 1152 } 1153 1154 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1); 1155 if (ret) { 1156 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 1157 1, EXT_CSD_SANITIZE_START, device); 1158 exit(1); 1159 } 1160 1161 return ret; 1162 1163} 1164 1165