blob: faa8df3d86d1b13f02a96ffc739ca883f62b21ae [file] [log] [blame]
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001/*
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
Gwendal Grignou771984c2014-07-01 12:46:18 -070017#include <errno.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050018#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/ioctl.h>
Gwendal Grignou771984c2014-07-01 12:46:18 -070022#include <sys/param.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050023#include <sys/types.h>
24#include <dirent.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <fcntl.h>
28#include <libgen.h>
29#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050030#include <ctype.h>
Roman Peniaev023cc7c2014-08-12 23:25:45 +090031#include <errno.h>
32#include <stdint.h>
33#include <assert.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050034
35#include "mmc.h"
36#include "mmc_cmds.h"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080037#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050038
Nick Sanders9d57aa72014-03-05 21:38:54 -080039#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080040#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080041#define CID_SIZE 16
42
Julius Wernerbcc3e2e2016-04-21 16:53:02 -070043/* Sending several commands too close together seems to cause timeouts. */
44#define INTER_COMMAND_GAP_US (50 * 1000)
45
Roman Peniaev023cc7c2014-08-12 23:25:45 +090046#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080047
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050048int read_extcsd(int fd, __u8 *ext_csd)
49{
50 int ret = 0;
51 struct mmc_ioc_cmd idata;
52 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080053 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050054 idata.write_flag = 0;
55 idata.opcode = MMC_SEND_EXT_CSD;
56 idata.arg = 0;
57 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080058 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050059 idata.blocks = 1;
60 mmc_ioc_cmd_set_data(idata, ext_csd);
61
62 ret = ioctl(fd, MMC_IOC_CMD, &idata);
63 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080064 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050065
66 return ret;
67}
68
69int write_extcsd_value(int fd, __u8 index, __u8 value)
70{
71 int ret = 0;
72 struct mmc_ioc_cmd idata;
73
74 memset(&idata, 0, sizeof(idata));
75 idata.write_flag = 1;
76 idata.opcode = MMC_SWITCH;
77 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
78 (index << 16) |
79 (value << 8) |
80 EXT_CSD_CMD_SET_NORMAL;
81 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
82
83 ret = ioctl(fd, MMC_IOC_CMD, &idata);
84 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080085 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050086
87 return ret;
88}
89
Ben Gardiner27c357d2013-05-30 17:12:47 -040090int send_status(int fd, __u32 *response)
91{
92 int ret = 0;
93 struct mmc_ioc_cmd idata;
94
95 memset(&idata, 0, sizeof(idata));
96 idata.opcode = MMC_SEND_STATUS;
97 idata.arg = (1 << 16);
98 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
99
100 ret = ioctl(fd, MMC_IOC_CMD, &idata);
101 if (ret)
102 perror("ioctl");
103
104 *response = idata.response[0];
105
106 return ret;
107}
108
Chris Ballb9c7a172012-02-20 12:34:25 -0500109void print_writeprotect_status(__u8 *ext_csd)
110{
111 __u8 reg;
Al Cooper786418c2015-04-29 18:12:35 -0400112 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500113
114 /* A43: reserved [174:0] */
115 if (ext_csd_rev >= 5) {
116 printf("Boot write protection status registers"
117 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
118
119 reg = ext_csd[EXT_CSD_BOOT_WP];
120 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
121 printf(" Power ro locking: ");
122 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
123 printf("not possible\n");
124 else
125 printf("possible\n");
126
127 printf(" Permanent ro locking: ");
128 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
129 printf("not possible\n");
130 else
131 printf("possible\n");
132
133 printf(" ro lock status: ");
134 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
135 printf("locked until next power on\n");
136 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
137 printf("locked permanently\n");
138 else
139 printf("not locked\n");
140 }
141}
142
143int do_writeprotect_get(int nargs, char **argv)
144{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800145 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500146 int fd, ret;
147 char *device;
148
Chris Ball8ba44662012-04-19 13:22:54 -0400149 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
150 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500151
152 device = argv[1];
153
154 fd = open(device, O_RDWR);
155 if (fd < 0) {
156 perror("open");
157 exit(1);
158 }
159
160 ret = read_extcsd(fd, ext_csd);
161 if (ret) {
162 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
163 exit(1);
164 }
165
166 print_writeprotect_status(ext_csd);
167
168 return ret;
169}
170
171int do_writeprotect_set(int nargs, char **argv)
172{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800173 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500174 int fd, ret;
175 char *device;
176
Chris Ball8ba44662012-04-19 13:22:54 -0400177 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
178 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500179
180 device = argv[1];
181
182 fd = open(device, O_RDWR);
183 if (fd < 0) {
184 perror("open");
185 exit(1);
186 }
187
188 ret = read_extcsd(fd, ext_csd);
189 if (ret) {
190 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
191 exit(1);
192 }
193
194 value = ext_csd[EXT_CSD_BOOT_WP] |
195 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
196 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
197 if (ret) {
198 fprintf(stderr, "Could not write 0x%02x to "
199 "EXT_CSD[%d] in %s\n",
200 value, EXT_CSD_BOOT_WP, device);
201 exit(1);
202 }
203
204 return ret;
205}
206
Saugata Dasb7e25992012-05-17 09:26:34 -0400207int do_disable_512B_emulation(int nargs, char **argv)
208{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800209 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400210 int fd, ret;
211 char *device;
212
213 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
214 device = argv[1];
215
216 fd = open(device, O_RDWR);
217 if (fd < 0) {
218 perror("open");
219 exit(1);
220 }
221
222 ret = read_extcsd(fd, ext_csd);
223 if (ret) {
224 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
225 exit(1);
226 }
227
228 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
229 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
230 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
231
232 if (native_sector_size && !data_sector_size &&
233 (wr_rel_param & EN_REL_WR)) {
234 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
235
236 if (ret) {
237 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
238 1, EXT_CSD_BOOT_WP, device);
239 exit(1);
240 }
241 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
242 } else if (native_sector_size && data_sector_size) {
243 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
244 } else {
245 printf("MMC does not support disabling 512B emulation mode.\n");
246 }
247
248 return ret;
249}
250
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200251int do_write_boot_en(int nargs, char **argv)
252{
253 __u8 ext_csd[512];
254 __u8 value = 0;
255 int fd, ret;
256 char *device;
257 int boot_area, send_ack;
258
259 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
260 "<send_ack> </path/to/mmcblkX>\n", exit(1));
261
262 /*
263 * If <send_ack> is 1, the device will send acknowledgment
264 * pattern "010" to the host when boot operation begins.
265 * If <send_ack> is 0, it won't.
266 */
267 boot_area = strtol(argv[1], NULL, 10);
268 send_ack = strtol(argv[2], NULL, 10);
269 device = argv[3];
270
271 fd = open(device, O_RDWR);
272 if (fd < 0) {
273 perror("open");
274 exit(1);
275 }
276
277 ret = read_extcsd(fd, ext_csd);
278 if (ret) {
279 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
280 exit(1);
281 }
282
283 value = ext_csd[EXT_CSD_PART_CONFIG];
284
285 switch (boot_area) {
286 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
287 value |= (1 << 3);
288 value &= ~(3 << 4);
289 break;
290 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
291 value |= (1 << 4);
292 value &= ~(1 << 3);
293 value &= ~(1 << 5);
294 break;
295 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
296 value |= (boot_area << 3);
297 break;
298 default:
299 fprintf(stderr, "Cannot enable the boot area\n");
300 exit(1);
301 }
302 if (send_ack)
303 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
304 else
305 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
306
307 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
308 if (ret) {
309 fprintf(stderr, "Could not write 0x%02x to "
310 "EXT_CSD[%d] in %s\n",
311 value, EXT_CSD_PART_CONFIG, device);
312 exit(1);
313 }
314 return ret;
315}
316
Al Cooper794314c2015-05-01 08:24:37 -0400317int do_boot_bus_conditions_set(int nargs, char **argv)
318{
319 __u8 ext_csd[512];
320 __u8 value = 0;
321 int fd, ret;
322 char *device;
323
324 CHECK(nargs != 5, "Usage: mmc: bootbus set <boot_mode> "
325 "<reset_boot_bus_conditions> <boot_bus_width> <device>\n",
326 exit(1));
327
328 if (strcmp(argv[1], "single_backward") == 0)
329 value |= 0;
330 else if (strcmp(argv[1], "single_hs") == 0)
331 value |= 0x8;
332 else if (strcmp(argv[1], "dual") == 0)
333 value |= 0x10;
334 else {
335 fprintf(stderr, "illegal <boot_mode> specified\n");
336 exit(1);
337 }
338
339 if (strcmp(argv[2], "x1") == 0)
340 value |= 0;
341 else if (strcmp(argv[2], "retain") == 0)
342 value |= 0x4;
343 else {
344 fprintf(stderr,
345 "illegal <reset_boot_bus_conditions> specified\n");
346 exit(1);
347 }
348
349 if (strcmp(argv[3], "x1") == 0)
350 value |= 0;
351 else if (strcmp(argv[3], "x4") == 0)
352 value |= 0x1;
353 else if (strcmp(argv[3], "x8") == 0)
354 value |= 0x2;
355 else {
356 fprintf(stderr, "illegal <boot_bus_width> specified\n");
357 exit(1);
358 }
359
360 device = argv[4];
361 fd = open(device, O_RDWR);
362 if (fd < 0) {
363 perror("open");
364 exit(1);
365 }
366
367 ret = read_extcsd(fd, ext_csd);
368 if (ret) {
369 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
370 exit(1);
371 }
372 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
373 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
374
375 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
376 if (ret) {
377 fprintf(stderr, "Could not write 0x%02x to "
378 "EXT_CSD[%d] in %s\n",
379 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
380 exit(1);
381 }
382 close(fd);
383 return ret;
384}
385
Chris Ballf74dfe22012-10-19 16:49:55 -0400386int do_hwreset(int value, int nargs, char **argv)
387{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800388 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400389 int fd, ret;
390 char *device;
391
392 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
393 exit(1));
394
395 device = argv[1];
396
397 fd = open(device, O_RDWR);
398 if (fd < 0) {
399 perror("open");
400 exit(1);
401 }
402
403 ret = read_extcsd(fd, ext_csd);
404 if (ret) {
405 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
406 exit(1);
407 }
408
409 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
410 EXT_CSD_HW_RESET_EN) {
411 fprintf(stderr,
412 "H/W Reset is already permanently enabled on %s\n",
413 device);
414 exit(1);
415 }
416 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
417 EXT_CSD_HW_RESET_DIS) {
418 fprintf(stderr,
419 "H/W Reset is already permanently disabled on %s\n",
420 device);
421 exit(1);
422 }
423
424 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
425 if (ret) {
426 fprintf(stderr,
427 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
428 value, EXT_CSD_RST_N_FUNCTION, device);
429 exit(1);
430 }
431
432 return ret;
433}
434
435int do_hwreset_en(int nargs, char **argv)
436{
437 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
438}
439
440int do_hwreset_dis(int nargs, char **argv)
441{
442 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
443}
444
Jaehoon Chung86496512012-09-21 10:08:05 +0000445int do_write_bkops_en(int nargs, char **argv)
446{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800447 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000448 int fd, ret;
449 char *device;
450
451 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
452 exit(1));
453
454 device = argv[1];
455
456 fd = open(device, O_RDWR);
457 if (fd < 0) {
458 perror("open");
459 exit(1);
460 }
461
462 ret = read_extcsd(fd, ext_csd);
463 if (ret) {
464 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
465 exit(1);
466 }
467
468 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
469 fprintf(stderr, "%s doesn't support BKOPS\n", device);
470 exit(1);
471 }
472
473 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
474 if (ret) {
475 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
476 value, EXT_CSD_BKOPS_EN, device);
477 exit(1);
478 }
479
480 return ret;
481}
482
Ben Gardiner27c357d2013-05-30 17:12:47 -0400483int do_status_get(int nargs, char **argv)
484{
485 __u32 response;
486 int fd, ret;
487 char *device;
488
489 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
490 exit(1));
491
492 device = argv[1];
493
494 fd = open(device, O_RDWR);
495 if (fd < 0) {
496 perror("open");
497 exit(1);
498 }
499
500 ret = send_status(fd, &response);
501 if (ret) {
502 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
503 exit(1);
504 }
505
506 printf("SEND_STATUS response: 0x%08x\n", response);
507
508 return ret;
509}
510
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800511__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
512{
513 return (ext_csd_loc[3] << 24) |
514 (ext_csd_loc[2] << 16) |
515 (ext_csd_loc[1] << 8) |
516 ext_csd_loc[0];
517}
518
Ben Gardiner4e850232013-05-30 17:12:49 -0400519unsigned int get_sector_count(__u8 *ext_csd)
520{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800521 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400522}
523
524int is_blockaddresed(__u8 *ext_csd)
525{
526 unsigned int sectors = get_sector_count(ext_csd);
527
528 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
529}
530
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400531unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
532{
533 return ext_csd[221];
534}
535
536unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
537{
538 return ext_csd[224];
539}
540
Ben Gardinere6e84e92013-09-19 11:14:27 -0400541int set_partitioning_setting_completed(int dry_run, const char * const device,
542 int fd)
543{
544 int ret;
545
546 if (dry_run) {
547 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
548 fprintf(stderr, "These changes will not take effect neither "
549 "now nor after a power cycle\n");
550 return 1;
551 }
552
553 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
554 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
555 if (ret) {
556 fprintf(stderr, "Could not write 0x1 to "
557 "EXT_CSD[%d] in %s\n",
558 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
559 return 1;
560 }
561
562 __u32 response;
563 ret = send_status(fd, &response);
564 if (ret) {
565 fprintf(stderr, "Could not get response to SEND_STATUS "
566 "from %s\n", device);
567 return 1;
568 }
569
570 if (response & R1_SWITCH_ERROR) {
571 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
572 "failed on %s\n", device);
573 return 1;
574 }
575
576 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
577 "%s SUCCESS\n", device);
578 fprintf(stderr, "Device power cycle needed for settings to "
579 "take effect.\n"
580 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
581 "using 'extcsd read' after power cycle\n");
582
583 return 0;
584}
585
Balaji T K1fdb7f92015-04-29 18:12:32 -0400586int check_enhanced_area_total_limit(const char * const device, int fd)
587{
588 __u8 ext_csd[512];
589 __u32 regl;
590 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
591 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400592 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400593 unsigned int wp_sz, erase_sz;
594 int ret;
595
596 ret = read_extcsd(fd, ext_csd);
597 if (ret) {
598 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
599 exit(1);
600 }
601 wp_sz = get_hc_wp_grp_size(ext_csd);
602 erase_sz = get_hc_erase_grp_size(ext_csd);
603
604 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
605 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
606 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
607 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
608 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
609 enh_area_sz += gp4_part_sz;
610 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
611 printf(" i.e. %lu KiB\n", gp4_part_sz);
612 }
613
614 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
615 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
616 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
617 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
618 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
619 enh_area_sz += gp3_part_sz;
620 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
621 printf(" i.e. %lu KiB\n", gp3_part_sz);
622 }
623
624 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
625 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
626 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
627 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
628 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
629 enh_area_sz += gp2_part_sz;
630 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
631 printf(" i.e. %lu KiB\n", gp2_part_sz);
632 }
633
634 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
635 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
636 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
637 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
638 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
639 enh_area_sz += gp1_part_sz;
640 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
641 printf(" i.e. %lu KiB\n", gp1_part_sz);
642 }
643
644 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
645 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
646 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
647 user_area_sz = 512l * regl * erase_sz * wp_sz;
648 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
649 enh_area_sz += user_area_sz;
650 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
651 printf(" i.e. %lu KiB\n", user_area_sz);
652 }
653
654 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
655 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
656 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
657 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
658 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
659 printf(" i.e. %lu KiB\n", max_enh_area_sz);
660 if (enh_area_sz > max_enh_area_sz) {
661 fprintf(stderr,
662 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
663 enh_area_sz, max_enh_area_sz, device);
664 return 1;
665 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400666 total_sz = get_sector_count(ext_csd) / 2;
667 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
668 gp1_part_sz + user_area_sz;
669 if (total_gp_user_sz > total_sz) {
670 fprintf(stderr,
671 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
672 total_gp_user_sz, total_sz, device);
673 return 1;
674 }
675
676 return 0;
677}
678
679int do_create_gp_partition(int nargs, char **argv)
680{
681 __u8 value;
682 __u8 ext_csd[512];
683 __u8 address;
684 int fd, ret;
685 char *device;
686 int dry_run = 1;
687 int partition, enh_attr, ext_attr;
688 unsigned int length_kib, gp_size_mult;
689 unsigned long align;
690
691 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n> <length KiB> "
692 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
693
694 if (!strcmp("-y", argv[1]))
695 dry_run = 0;
696
697 length_kib = strtol(argv[2], NULL, 10);
698 partition = strtol(argv[3], NULL, 10);
699 enh_attr = strtol(argv[4], NULL, 10);
700 ext_attr = strtol(argv[5], NULL, 10);
701 device = argv[6];
702
Marcus Folkessoncb04fde2015-11-18 15:06:16 -0500703 if (partition < 1 || partition > 4) {
704 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -0400705 exit(1);
706 }
707
708 if (enh_attr && ext_attr) {
709 printf("Not allowed to set both enhanced attribute and extended attribute\n");
710 exit(1);
711 }
712
713 fd = open(device, O_RDWR);
714 if (fd < 0) {
715 perror("open");
716 exit(1);
717 }
718
719 ret = read_extcsd(fd, ext_csd);
720 if (ret) {
721 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
722 exit(1);
723 }
724
725 /* assert not PARTITION_SETTING_COMPLETED */
726 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
727 printf(" Device is already partitioned\n");
728 exit(1);
729 }
730
731 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
732 gp_size_mult = (length_kib + align/2l) / align;
733
734 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
735 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
736 if (ret) {
737 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
738 EXT_CSD_ERASE_GROUP_DEF, device);
739 exit(1);
740 }
741
742 value = (gp_size_mult >> 16) & 0xff;
743 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
744 ret = write_extcsd_value(fd, address, value);
745 if (ret) {
746 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
747 value, address, device);
748 exit(1);
749 }
750 value = (gp_size_mult >> 8) & 0xff;
751 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
752 ret = write_extcsd_value(fd, address, value);
753 if (ret) {
754 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
755 value, address, device);
756 exit(1);
757 }
758 value = gp_size_mult & 0xff;
759 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
760 ret = write_extcsd_value(fd, address, value);
761 if (ret) {
762 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
763 value, address, device);
764 exit(1);
765 }
766
767 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
768 if (enh_attr)
769 value |= (1 << partition);
770 else
771 value &= ~(1 << partition);
772
773 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
774 if (ret) {
775 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
776 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
777 exit(1);
778 }
779
780 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
781 value = ext_csd[address];
782 if (ext_attr)
783 value |= (ext_attr << (4 * ((partition - 1) % 2)));
784 else
785 value &= (0xF << (4 * ((partition % 2))));
786
787 ret = write_extcsd_value(fd, address, value);
788 if (ret) {
789 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
790 value, address, device);
791 exit(1);
792 }
793
794 ret = check_enhanced_area_total_limit(device, fd);
795 if (ret)
796 exit(1);
797
798 if (!set_partitioning_setting_completed(dry_run, device, fd))
799 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -0400800
801 return 0;
802}
803
Ben Gardinerd91d3692013-05-30 17:12:51 -0400804int do_enh_area_set(int nargs, char **argv)
805{
806 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800807 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400808 int fd, ret;
809 char *device;
810 int dry_run = 1;
811 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
812 unsigned long align;
813
814 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
815 "</path/to/mmcblkX>\n", exit(1));
816
817 if (!strcmp("-y", argv[1]))
818 dry_run = 0;
819
820 start_kib = strtol(argv[2], NULL, 10);
821 length_kib = strtol(argv[3], NULL, 10);
822 device = argv[4];
823
824 fd = open(device, O_RDWR);
825 if (fd < 0) {
826 perror("open");
827 exit(1);
828 }
829
830 ret = read_extcsd(fd, ext_csd);
831 if (ret) {
832 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
833 exit(1);
834 }
835
836 /* assert ENH_ATTRIBUTE_EN */
837 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
838 {
839 printf(" Device cannot have enhanced tech.\n");
840 exit(1);
841 }
842
843 /* assert not PARTITION_SETTING_COMPLETED */
844 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
845 {
846 printf(" Device is already partitioned\n");
847 exit(1);
848 }
849
850 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
851
852 enh_size_mult = (length_kib + align/2l) / align;
853
854 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
855 enh_start_addr /= align;
856 enh_start_addr *= align;
857
858 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
859 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
860 if (ret) {
861 fprintf(stderr, "Could not write 0x1 to "
862 "EXT_CSD[%d] in %s\n",
863 EXT_CSD_ERASE_GROUP_DEF, device);
864 exit(1);
865 }
866
867 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
868 value = (enh_start_addr >> 24) & 0xff;
869 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
870 if (ret) {
871 fprintf(stderr, "Could not write 0x%02x to "
872 "EXT_CSD[%d] in %s\n", value,
873 EXT_CSD_ENH_START_ADDR_3, device);
874 exit(1);
875 }
876 value = (enh_start_addr >> 16) & 0xff;
877 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
878 if (ret) {
879 fprintf(stderr, "Could not write 0x%02x to "
880 "EXT_CSD[%d] in %s\n", value,
881 EXT_CSD_ENH_START_ADDR_2, device);
882 exit(1);
883 }
884 value = (enh_start_addr >> 8) & 0xff;
885 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
886 if (ret) {
887 fprintf(stderr, "Could not write 0x%02x to "
888 "EXT_CSD[%d] in %s\n", value,
889 EXT_CSD_ENH_START_ADDR_1, device);
890 exit(1);
891 }
892 value = enh_start_addr & 0xff;
893 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
894 if (ret) {
895 fprintf(stderr, "Could not write 0x%02x to "
896 "EXT_CSD[%d] in %s\n", value,
897 EXT_CSD_ENH_START_ADDR_0, device);
898 exit(1);
899 }
900
901 value = (enh_size_mult >> 16) & 0xff;
902 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
903 if (ret) {
904 fprintf(stderr, "Could not write 0x%02x to "
905 "EXT_CSD[%d] in %s\n", value,
906 EXT_CSD_ENH_SIZE_MULT_2, device);
907 exit(1);
908 }
909 value = (enh_size_mult >> 8) & 0xff;
910 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
911 if (ret) {
912 fprintf(stderr, "Could not write 0x%02x to "
913 "EXT_CSD[%d] in %s\n", value,
914 EXT_CSD_ENH_SIZE_MULT_1, device);
915 exit(1);
916 }
917 value = enh_size_mult & 0xff;
918 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
919 if (ret) {
920 fprintf(stderr, "Could not write 0x%02x to "
921 "EXT_CSD[%d] in %s\n", value,
922 EXT_CSD_ENH_SIZE_MULT_0, device);
923 exit(1);
924 }
Balaji T K1fdb7f92015-04-29 18:12:32 -0400925 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
926 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400927 if (ret) {
928 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
929 "EXT_CSD[%d] in %s\n",
930 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
931 exit(1);
932 }
933
Balaji T K1fdb7f92015-04-29 18:12:32 -0400934 ret = check_enhanced_area_total_limit(device, fd);
935 if (ret)
936 exit(1);
937
Ben Gardinere6e84e92013-09-19 11:14:27 -0400938 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400939
Ben Gardinere6e84e92013-09-19 11:14:27 -0400940 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400941 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400942
943 return 0;
944}
945
Ben Gardiner196d0d22013-09-19 11:14:29 -0400946int do_write_reliability_set(int nargs, char **argv)
947{
948 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800949 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400950 int fd, ret;
951
952 int dry_run = 1;
953 int partition;
954 char *device;
955
956 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
957 "<partition> </path/to/mmcblkX>\n", exit(1));
958
959 if (!strcmp("-y", argv[1]))
960 dry_run = 0;
961
962 partition = strtol(argv[2], NULL, 10);
963 device = argv[3];
964
965 fd = open(device, O_RDWR);
966 if (fd < 0) {
967 perror("open");
968 exit(1);
969 }
970
971 ret = read_extcsd(fd, ext_csd);
972 if (ret) {
973 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
974 exit(1);
975 }
976
977 /* assert not PARTITION_SETTING_COMPLETED */
978 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
979 {
980 printf(" Device is already partitioned\n");
981 exit(1);
982 }
983
984 /* assert HS_CTRL_REL */
985 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
986 printf("Cannot set write reliability parameters, WR_REL_SET is "
987 "read-only\n");
988 exit(1);
989 }
990
991 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
992 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
993 if (ret) {
994 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
995 value, EXT_CSD_WR_REL_SET, device);
996 exit(1);
997 }
998
999 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1000 value, device);
1001
1002 if (!set_partitioning_setting_completed(dry_run, device, fd))
1003 exit(1);
1004
1005 return 0;
1006}
1007
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001008int do_read_extcsd(int nargs, char **argv)
1009{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001010 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001011 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001012 int fd, ret;
1013 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001014 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001015 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001016 "4.0", /* 0 */
1017 "4.1", /* 1 */
1018 "4.2", /* 2 */
1019 "4.3", /* 3 */
1020 "Obsolete", /* 4 */
1021 "4.41", /* 5 */
1022 "4.5", /* 6 */
1023 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001024 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001025 };
1026 int boot_access;
1027 const char* boot_access_str[] = {
1028 "No access to boot partition", /* 0 */
1029 "R/W Boot Partition 1", /* 1 */
1030 "R/W Boot Partition 2", /* 2 */
1031 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1032 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001033
Chris Ball8ba44662012-04-19 13:22:54 -04001034 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
1035 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001036
1037 device = argv[1];
1038
1039 fd = open(device, O_RDWR);
1040 if (fd < 0) {
1041 perror("open");
1042 exit(1);
1043 }
1044
1045 ret = read_extcsd(fd, ext_csd);
1046 if (ret) {
1047 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1048 exit(1);
1049 }
1050
Al Cooper786418c2015-04-29 18:12:35 -04001051 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001052
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001053 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1054 (ext_csd_rev != 4))
1055 str = ver_str[ext_csd_rev];
1056 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001057 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001058
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001059 printf("=============================================\n");
1060 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1061 printf("=============================================\n\n");
1062
1063 if (ext_csd_rev < 3)
1064 goto out_free; /* No ext_csd */
1065
1066 /* Parse the Extended CSD registers.
1067 * Reserved bit should be read as "0" in case of spec older
1068 * than A441.
1069 */
1070 reg = ext_csd[EXT_CSD_S_CMD_SET];
1071 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1072 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001073 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001074
1075 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1076 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1077 if (reg & EXT_CSD_HPI_SUPP) {
1078 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001079 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001080 else
1081 printf("implementation based on CMD13\n");
1082 }
1083
1084 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1085 ext_csd[502]);
1086
1087 if (ext_csd_rev >= 6) {
1088 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1089 ext_csd[501]);
1090 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1091 ext_csd[500]);
1092 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1093 ext_csd[499]);
1094
1095 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1096 ext_csd[498]);
1097 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1098 ext_csd[497]);
1099 printf("Context Management Capabilities"
1100 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1101 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1102 ext_csd[495]);
1103 printf("Extended partition attribute support"
1104 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001105 }
1106 if (ext_csd_rev >= 7) {
1107 int j;
1108 int eol_info;
1109 char* eol_info_str[] = {
1110 "Not Defined", /* 0 */
1111 "Normal", /* 1 */
1112 "Warning", /* 2 */
1113 "Urgent", /* 3 */
1114 };
1115
1116 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1117 ext_csd[493]);
1118 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1119 ext_csd[492]);
1120 printf("Operation codes timeout"
1121 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1122 ext_csd[491]);
1123 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1124 get_word_from_ext_csd(&ext_csd[487]));
1125 printf("Number of FW sectors correctly programmed"
1126 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1127 get_word_from_ext_csd(&ext_csd[302]));
1128 printf("Vendor proprietary health report:\n");
1129 for (j = 301; j >= 270; j--)
1130 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1131 " 0x%02x\n", j, ext_csd[j]);
1132 for (j = 269; j >= 268; j--) {
1133 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001134 char est_type = 'B' + (j - 269);
1135 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001136 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001137 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001138 if (life_used >= 0x1 && life_used <= 0xa)
1139 printf(" i.e. %d%% - %d%% device life time"
1140 " used\n",
1141 (life_used - 1) * 10, life_used * 10);
1142 else if (life_used == 0xb)
1143 printf(" i.e. Exceeded its maximum estimated"
1144 " device life time\n");
1145 }
1146 eol_info = ext_csd[267];
1147 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1148 eol_info);
1149 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1150 printf(" i.e. %s\n", eol_info_str[eol_info]);
1151 else
1152 printf(" i.e. Reserved\n");
1153
1154 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1155 ext_csd[266]);
1156 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1157 ext_csd[265]);
1158 printf("Optimal trim unit size"
1159 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1160 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1161 ext_csd[263], ext_csd[262]);
1162 printf("Firmware version:\n");
1163 for (j = 261; j >= 254; j--)
1164 printf("[FIRMWARE_VERSION[%d]]:"
1165 " 0x%02x\n", j, ext_csd[j]);
1166
1167 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1168 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1169 }
1170 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001171 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1172 ext_csd[248]);
1173 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1174 ext_csd[247]);
1175 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001176 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001177 }
1178
1179 /* A441: Reserved [501:247]
1180 A43: reserved [246:229] */
1181 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001182 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001183 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001184
1185 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1186
1187 printf("1st Initialisation Time after programmed sector"
1188 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1189
1190 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001191 printf("Power class for 52MHz, DDR at 3.6V"
1192 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1193 printf("Power class for 52MHz, DDR at 1.95V"
1194 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1195
1196 /* A441: reserved [237-236] */
1197
1198 if (ext_csd_rev >= 6) {
1199 printf("Power class for 200MHz at 3.6V"
1200 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1201 printf("Power class for 200MHz, at 1.95V"
1202 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1203 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001204 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001205 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1206 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1207 /* A441: reserved [233] */
1208 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1209 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1210 ext_csd[231]);
1211 }
1212 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1213 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1214 ext_csd[230]);
1215 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1216 ext_csd[229]);
1217 }
1218 reg = ext_csd[EXT_CSD_BOOT_INFO];
1219 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1220 if (reg & EXT_CSD_BOOT_INFO_ALT)
1221 printf(" Device supports alternative boot method\n");
1222 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1223 printf(" Device supports dual data rate during boot\n");
1224 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1225 printf(" Device supports high speed timing during boot\n");
1226
1227 /* A441/A43: reserved [227] */
1228 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1229 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001230
1231 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001232 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001233 reg);
1234 printf(" i.e. %u KiB\n", 512 * reg);
1235
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001236 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1237 ext_csd[223]);
1238 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1239 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001240
1241 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001242 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001243 reg);
1244 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1245
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001246 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1247 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1248 /* A441/A43: reserved [218] */
1249 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1250 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001251
1252 unsigned int sectors = get_sector_count(ext_csd);
1253 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1254 if (is_blockaddresed(ext_csd))
1255 printf(" Device is block-addressed\n");
1256 else
1257 printf(" Device is NOT block-addressed\n");
1258
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001259 /* A441/A43: reserved [211] */
1260 printf("Minimum Write Performance for 8bit:\n");
1261 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1262 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1263 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1264 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1265 printf("Minimum Write Performance for 4bit:\n");
1266 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1267 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1268 /* A441/A43: reserved [204] */
1269 printf("Power classes registers:\n");
1270 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1271 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1272 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1273 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1274
1275 /* A43: reserved [199:198] */
1276 if (ext_csd_rev >= 5) {
1277 printf("Partition switching timing "
1278 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1279 printf("Out-of-interrupt busy timing"
1280 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1281 }
1282
1283 /* A441/A43: reserved [197] [195] [193] [190] [188]
1284 * [186] [184] [182] [180] [176] */
1285
1286 if (ext_csd_rev >= 6)
1287 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1288 ext_csd[197]);
1289
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001290 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001291 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1292 ext_csd[196], ext_csd[195]);
1293 reg = ext_csd[195];
1294 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1295 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001296 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001297 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1298 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001299 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1300 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1301 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1302 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1303 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1304 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001305
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001306 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001307 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001308 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1309 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1310 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1311 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1312 ext_csd[185]);
1313 /* bus_width: ext_csd[183] not readable */
1314 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1315 ext_csd[181]);
1316 reg = ext_csd[EXT_CSD_BOOT_CFG];
1317 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001318 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001319 case 0x0:
1320 printf(" Not boot enable\n");
1321 break;
1322 case 0x1:
1323 printf(" Boot Partition 1 enabled\n");
1324 break;
1325 case 0x2:
1326 printf(" Boot Partition 2 enabled\n");
1327 break;
1328 case 0x7:
1329 printf(" User Area Enabled for boot\n");
1330 break;
1331 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001332 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1333 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1334 printf(" %s\n", boot_access_str[boot_access]);
1335 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001336 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001337 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001338
1339 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1340 ext_csd[178]);
1341 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1342 ext_csd[177]);
1343 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001344 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001345
Chris Ballb9c7a172012-02-20 12:34:25 -05001346 print_writeprotect_status(ext_csd);
1347
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001348 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001349 /* A441]: reserved [172] */
1350 printf("User area write protection register"
1351 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1352 /* A441]: reserved [170] */
1353 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1354 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001355
1356 reg = ext_csd[EXT_CSD_WR_REL_SET];
1357 const char * const fast = "existing data is at risk if a power "
1358 "failure occurs during a write operation";
1359 const char * const reliable = "the device protects existing "
1360 "data if a power failure occurs during a write "
1361 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001362 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001363 " [WR_REL_SET]: 0x%02x\n", reg);
1364
1365 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1366 int i;
1367 for (i = 1; i <= 4; i++) {
1368 printf(" partition %d: %s\n", i,
1369 reg & (1<<i) ? reliable : fast);
1370 }
1371
1372 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001373 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001374 " [WR_REL_PARAM]: 0x%02x\n", reg);
1375 if (reg & 0x01)
1376 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1377 if (reg & 0x04)
1378 printf(" Device supports the enhanced def. of reliable "
1379 "write\n");
1380
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001381 /* sanitize_start ext_csd[165]]: not readable
1382 * bkops_start ext_csd[164]]: only writable */
1383 printf("Enable background operations handshake"
1384 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1385 printf("H/W reset function"
1386 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1387 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001388 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001389 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1390 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001391 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001392 printf(" Device support partitioning feature\n");
1393 else
1394 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001395 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001396 printf(" Device can have enhanced tech.\n");
1397 else
1398 printf(" Device cannot have enhanced tech.\n");
1399
Oliver Metz11f2cea2013-09-23 08:40:52 +02001400 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001401 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1402 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1403
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001404 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001405 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001406 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1407 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001408 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001409
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001410 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001411 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001412 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001413 printf("Partitioning Setting"
1414 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001415 reg);
1416 if (reg)
1417 printf(" Device partition setting complete\n");
1418 else
1419 printf(" Device partition setting NOT complete\n");
1420
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001421 printf("General Purpose Partition Size\n"
1422 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1423 (ext_csd[153] << 8) | ext_csd[152]);
1424 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1425 (ext_csd[150] << 8) | ext_csd[149]);
1426 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1427 (ext_csd[147] << 8) | ext_csd[146]);
1428 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1429 (ext_csd[144] << 8) | ext_csd[143]);
1430
Oliver Metz11f2cea2013-09-23 08:40:52 +02001431 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001432 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1433 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001434 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001435 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1436 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001437 get_hc_erase_grp_size(ext_csd) *
1438 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001439
Oliver Metz11f2cea2013-09-23 08:40:52 +02001440 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001441 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1442 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1443 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001444 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001445 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001446 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001447 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001448
1449 /* A441]: reserved [135] */
1450 printf("Bad Block Management mode"
1451 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1452 /* A441: reserved [133:0] */
1453 }
1454 /* B45 */
1455 if (ext_csd_rev >= 6) {
1456 int j;
1457 /* tcase_support ext_csd[132] not readable */
1458 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1459 ext_csd[131]);
1460 printf("Program CID/CSD in DDR mode support"
1461 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1462 ext_csd[130]);
1463
1464 for (j = 127; j >= 64; j--)
1465 printf("Vendor Specific Fields"
1466 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1467 j, ext_csd[j]);
1468
Gwendal Grignoue966e672014-07-07 14:03:13 -07001469 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001470 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001471 reg);
1472 if (reg == 0x00)
1473 printf(" i.e. 512 B\n");
1474 else if (reg == 0x01)
1475 printf(" i.e. 4 KiB\n");
1476 else
1477 printf(" i.e. Reserved\n");
1478
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001479 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1480 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001481 reg = ext_csd[61];
1482 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1483 if (reg == 0x00)
1484 printf(" i.e. 512 B\n");
1485 else if (reg == 0x01)
1486 printf(" i.e. 4 KiB\n");
1487 else
1488 printf(" i.e. Reserved\n");
1489
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001490 printf("1st initialization after disabling sector"
1491 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1492 ext_csd[60]);
1493 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1494 ext_csd[59]);
1495 printf("Number of addressed group to be Released"
1496 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1497 printf("Exception events control"
1498 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1499 (ext_csd[57] << 8) | ext_csd[56]);
1500 printf("Exception events status"
1501 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1502 (ext_csd[55] << 8) | ext_csd[54]);
1503 printf("Extended Partitions Attribute"
1504 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1505 (ext_csd[53] << 8) | ext_csd[52]);
1506
1507 for (j = 51; j >= 37; j--)
1508 printf("Context configuration"
1509 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1510
1511 printf("Packed command status"
1512 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1513 printf("Packed command failure index"
1514 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1515 printf("Power Off Notification"
1516 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001517 printf("Control to turn the Cache ON/OFF"
1518 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001519 /* flush_cache ext_csd[32] not readable */
1520 /*Reserved [31:0] */
1521 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001522 if (ext_csd_rev >= 7) {
1523 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1524 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1525 ext_csd[29]);
1526
1527 reg = ext_csd[26];
1528 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1529 switch (reg) {
1530 case 0x00:
1531 printf(" Success\n");
1532 break;
1533 case 0x10:
1534 printf(" General error\n");
1535 break;
1536 case 0x11:
1537 printf(" Firmware install error\n");
1538 break;
1539 case 0x12:
1540 printf(" Error in downloading firmware\n");
1541 break;
1542 default:
1543 printf(" Reserved\n");
1544 }
1545 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1546 " %d sector size\n",
1547 get_word_from_ext_csd(&ext_csd[22]));
1548 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1549 " %d sector size\n",
1550 get_word_from_ext_csd(&ext_csd[18]));
1551 printf("Product state awareness enablement"
1552 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1553 ext_csd[17]);
1554 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1555 ext_csd[16]);
1556 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001557
1558out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001559 return ret;
1560}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001561
Nick Sanders9d57aa72014-03-05 21:38:54 -08001562int do_dump_extcsd(int nargs, char **argv)
1563{
1564 __u8 ext_csd[EXT_CSD_SIZE];
1565 int fd, ret;
1566 char *device;
1567 int i, j;
1568
1569 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1570 exit(1));
1571
1572 device = argv[1];
1573
1574 fd = open(device, O_RDWR);
1575 if (fd < 0) {
1576 perror("Failed to open mmc device");
1577 exit(1);
1578 }
1579
1580 ret = read_extcsd(fd, ext_csd);
1581 if (ret) {
1582 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1583 exit(1);
1584 }
1585
1586 /* Dump all bytes so that any undecoded or proprietary registers */
1587 /* can be acessed. */
1588 printf("EXT_CSD binary dump:\n");
1589 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1590 printf(" %3d: %3x: ", i, i);
1591 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1592 printf(" %02x", ext_csd[i+j]);
1593 }
1594 printf("\n");
1595 }
1596
1597 return ret;
1598}
1599
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001600int do_sanitize(int nargs, char **argv)
1601{
1602 int fd, ret;
1603 char *device;
1604
1605 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1606 exit(1));
1607
1608 device = argv[1];
1609
1610 fd = open(device, O_RDWR);
1611 if (fd < 0) {
1612 perror("open");
1613 exit(1);
1614 }
1615
1616 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1617 if (ret) {
1618 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1619 1, EXT_CSD_SANITIZE_START, device);
1620 exit(1);
1621 }
1622
1623 return ret;
1624
1625}
1626
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07001627enum blockprotect_mode {
1628 BLOCKPROTECT_TEMPORARY = 0,
1629 BLOCKPROTECT_POWERON,
1630 BLOCKPROTECT_PERMANENT,
1631};
1632
1633int write_blockprotect(int fd, __u32 sector, int enable)
1634{
1635 struct mmc_ioc_cmd cmd;
1636 int ret;
1637
1638 memset(&cmd, 0, sizeof(cmd));
1639 cmd.write_flag = 1;
1640 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
1641 cmd.arg = sector;
1642 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
1643
1644 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
1645 if (ret)
1646 perror("SET/CLR_WRITE_PROT command");
1647 return ret;
1648}
1649
1650int do_blockprotect_enable(int nargs, char **argv)
1651{
1652 __u8 ext_csd[EXT_CSD_SIZE];
1653 __u8 user_wp;
1654 __u32 sector;
1655 char *end;
1656 int ret, fd;
1657 int arg_index = 0;
1658 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
1659
1660 if (nargs > 0 && !strcmp(argv[1], "-r")) {
1661 arg_index++;
1662 mode = BLOCKPROTECT_POWERON;
1663 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
1664 arg_index++;
1665 mode = BLOCKPROTECT_PERMANENT;
1666 }
1667
1668 CHECK(nargs != 3 + arg_index, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n", exit(1));
1669
1670 sector = strtoul(argv[2 + arg_index], &end, 0);
1671 if (*end != '\0') {
1672 fprintf(stderr, "Not a block number: %s\n",
1673 argv[2 + arg_index]);
1674 exit(1);
1675 }
1676
1677 fd = open(argv[1 + arg_index], O_RDWR);
1678 if (fd < 0) {
1679 perror("open");
1680 exit(1);
1681 }
1682
1683 if (read_extcsd(fd, ext_csd))
1684 exit(1);
1685
1686 user_wp = ext_csd[EXT_CSD_USER_WP];
1687 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
1688 if (mode == BLOCKPROTECT_POWERON)
1689 user_wp |= EXT_CSD_US_PWR_WP_EN;
1690 else if (mode == BLOCKPROTECT_PERMANENT)
1691 user_wp |= EXT_CSD_US_PERM_WP_EN;
1692
1693 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
1694 if (ret) {
1695 perror("update EXT_CSD[USER_WP]");
1696 exit(1);
1697 }
1698
1699 usleep(INTER_COMMAND_GAP_US);
1700
1701 ret = write_blockprotect(fd, sector, 1);
1702
1703 usleep(INTER_COMMAND_GAP_US);
1704
1705 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
1706 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
1707 perror("reset EXT_CSD[USER_WP]");
1708 if (!ret)
1709 ret = -1;
1710 }
1711
1712 return ret;
1713}
1714
1715int do_blockprotect_disable(int nargs, char **argv)
1716{
1717 __u32 sector;
1718 char *end;
1719 int fd;
1720
1721 CHECK(nargs != 3, "Usage: mmc blockprotect disable <device> <write protect block>\n", exit(1));
1722
1723 sector = strtoul(argv[2], &end, 0);
1724 if (*end != '\0') {
1725 fprintf(stderr, "Not a block number: %s\n", argv[2]);
1726 exit(1);
1727 }
1728
1729
1730 fd = open(argv[1], O_RDWR);
1731 if (fd < 0) {
1732 perror("open");
1733 exit(1);
1734 }
1735
1736 return write_blockprotect(fd, sector, 0);
1737}
1738
1739int do_blockprotect_read(int nargs, char **argv)
1740{
1741 __u8 wp_bits[8];
1742 __u32 sector;
1743 char *end;
1744 int fd;
1745 struct mmc_ioc_cmd cmd;
1746
1747 CHECK(nargs != 3, "Usage: mmc blockprotect read <device> <write protect block>\n", exit(1));
1748
1749 fd = open(argv[1], O_RDWR);
1750 if (fd < 0) {
1751 perror("open");
1752 exit(1);
1753 }
1754
1755 sector = strtoul(argv[2], &end, 0);
1756 if (*end != '\0') {
1757 fprintf(stderr, "Not a block number: %s\n", argv[2]);
1758 exit(1);
1759 }
1760
1761 memset(&cmd, 0, sizeof(cmd));
1762 cmd.write_flag = 0;
1763 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
1764 cmd.arg = sector;
1765 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1766 cmd.blksz = sizeof(wp_bits);
1767 cmd.blocks = 1;
1768 mmc_ioc_cmd_set_data(cmd, wp_bits);
1769
1770 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
1771 perror("SEND_WRITE_PROT_TYPE command");
1772 exit(1);
1773 }
1774
1775 printf("Sector %u write protection: ", sector);
1776 switch (wp_bits[7] & 3) {
1777 case 0:
1778 printf("NONE\n");
1779 break;
1780 case 1:
1781 printf("TEMPORARY\n");
1782 break;
1783 case 2:
1784 printf("POWER-ON\n");
1785 break;
1786 case 3:
1787 printf("PERMANENT\n");
1788 break;
1789 }
1790
1791 return 0;
1792}
1793
1794int do_blockprotect_info(int nargs, char **argv)
1795{
1796 __u8 ext_csd[EXT_CSD_SIZE];
1797 __u8 user_wp;
1798 int fd, wp_sz, erase_sz;
1799
1800 CHECK(nargs != 2, "Usage: mmc blockprotect info <device>\n", exit(1));
1801
1802 fd = open(argv[1], O_RDWR);
1803 if (fd < 0) {
1804 perror("open");
1805 exit(1);
1806 }
1807
1808 if (read_extcsd(fd, ext_csd))
1809 exit(1);
1810
1811 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
1812 fprintf(stderr, "Block protection commands not supported: "
1813 "CLASS_6_CTRL set.\n");
1814 exit(1);
1815 }
1816
1817 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
1818 fprintf(stderr, "Block protection commands not supported: "
1819 "high-capacity sizes not enabled.\n");
1820 exit(1);
1821 }
1822
1823 wp_sz = get_hc_wp_grp_size(ext_csd);
1824 erase_sz = get_hc_erase_grp_size(ext_csd);
1825
1826 if (erase_sz == 0 || wp_sz == 0) {
1827 fprintf(stderr, "Block protection commands not supported: "
1828 "no high-capacity size for erase or WP blocks.\n");
1829 exit(1);
1830 }
1831
1832 printf("Write protect block size in sectors: %d\n",
1833 erase_sz * wp_sz * 1024);
1834
1835 user_wp = ext_csd[EXT_CSD_USER_WP];
1836 printf("Permanent write protection: %s\n",
1837 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
1838 printf("Power-on write protection: %s\n",
1839 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
1840
1841 return 0;
1842}
1843
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001844static const char* const mmc_ffu_hack_names[] = {
1845 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1846};
1847
Gwendal Grignou771984c2014-07-01 12:46:18 -07001848int do_emmc50_ffu (int nargs, char **argv)
1849{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001850 int fd, ret, i, argc=1, ffu_hack=0;
1851 char *device, *type, *path;
1852 __u64 value;
1853 union {
1854 __u8 data[FFU_DATA_SIZE];
1855 struct mmc_ffu_args ffu_args;
1856 } ffu_data;
1857 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001858 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001859
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001860 while (!strcmp("-k", argv[argc])) {
1861 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1862 if (ret < 1) {
1863 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1864 exit(1);
1865 }
1866 for (i = 0; i < MMC_HACK_LEN; i++) {
1867 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1868 ffu_args->hack[ffu_hack].type = i;
1869 if (ret == 2) {
1870 ffu_args->hack[ffu_hack].value = value;
1871 }
1872 ffu_hack++;
1873 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1874 sizeof(struct mmc_ffu_args) >
1875 FFU_DATA_SIZE) {
1876 fprintf(stderr, "Too many %d hacks",
1877 ffu_hack);
1878 exit(1);
1879 }
1880 break;
1881 }
1882 }
1883 if (i == MMC_HACK_LEN) {
1884 fprintf(stderr, "Hack type %s not found\n", type);
1885 fprintf(stderr, "Supported types are: ");
1886 for (i = 0; i < MMC_HACK_LEN; i++)
1887 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1888 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001889
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001890 exit(1);
1891 }
1892 free(type);
1893 argc++;
1894 }
1895 ffu_args->hack_nb = ffu_hack;
1896
1897 path = argv[argc++];
1898 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001899 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1900 exit(1);
1901 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001902 strcpy(ffu_args->name, path);
1903 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001904 fd = open(device, O_RDWR);
1905 if (fd < 0) {
1906 perror("open");
1907 exit(1);
1908 }
1909
Gwendal Grignou0f757342014-10-16 16:52:46 -07001910 /* prepare and send ioctl */
1911 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1912 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001913 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001914 mmc_ioc_cmd.blocks = 1;
1915 mmc_ioc_cmd.arg = 0;
1916 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1917 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001918 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001919 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001920 if (ret) {
1921 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1922 exit(1);
1923 }
1924
1925 close(fd);
1926 return 0;
1927}
1928
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001929#define DO_IO(func, fd, buf, nbyte) \
1930 ({ \
1931 ssize_t ret = 0, r; \
1932 do { \
1933 r = func(fd, buf + ret, nbyte - ret); \
1934 if (r < 0 && errno != EINTR) { \
1935 ret = -1; \
1936 break; \
1937 } \
1938 else if (r > 0) \
1939 ret += r; \
1940 } while (r != 0 && (size_t)ret != nbyte); \
1941 \
1942 ret; \
1943 })
1944
1945enum rpmb_op_type {
1946 MMC_RPMB_WRITE_KEY = 0x01,
1947 MMC_RPMB_READ_CNT = 0x02,
1948 MMC_RPMB_WRITE = 0x03,
1949 MMC_RPMB_READ = 0x04,
1950
1951 /* For internal usage only, do not use it directly */
1952 MMC_RPMB_READ_RESP = 0x05
1953};
1954
1955struct rpmb_frame {
1956 u_int8_t stuff[196];
1957 u_int8_t key_mac[32];
1958 u_int8_t data[256];
1959 u_int8_t nonce[16];
1960 u_int32_t write_counter;
1961 u_int16_t addr;
1962 u_int16_t block_count;
1963 u_int16_t result;
1964 u_int16_t req_resp;
1965};
1966
1967/* Performs RPMB operation.
1968 *
1969 * @fd: RPMB device on which we should perform ioctl command
1970 * @frame_in: input RPMB frame, should be properly inited
1971 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1972 * result and req_resp for output frame.
1973 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1974 * in the other cases -EINVAL will be returned.
1975 */
1976static int do_rpmb_op(int fd,
1977 const struct rpmb_frame *frame_in,
1978 struct rpmb_frame *frame_out,
1979 unsigned int out_cnt)
1980{
1981 int err;
1982 u_int16_t rpmb_type;
1983
1984 struct mmc_ioc_cmd ioc = {
1985 .arg = 0x0,
1986 .blksz = 512,
1987 .blocks = 1,
1988 .write_flag = 1,
1989 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1990 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1991 .data_ptr = (uintptr_t)frame_in
1992 };
1993
1994 if (!frame_in || !frame_out || !out_cnt)
1995 return -EINVAL;
1996
1997 rpmb_type = be16toh(frame_in->req_resp);
1998
1999 switch(rpmb_type) {
2000 case MMC_RPMB_WRITE:
2001 case MMC_RPMB_WRITE_KEY:
2002 if (out_cnt != 1) {
2003 err = -EINVAL;
2004 goto out;
2005 }
2006
2007 /* Write request */
2008 ioc.write_flag |= (1<<31);
2009 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2010 if (err < 0) {
2011 err = -errno;
2012 goto out;
2013 }
2014
2015 /* Result request */
2016 memset(frame_out, 0, sizeof(*frame_out));
2017 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2018 ioc.write_flag = 1;
2019 ioc.data_ptr = (uintptr_t)frame_out;
2020 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2021 if (err < 0) {
2022 err = -errno;
2023 goto out;
2024 }
2025
2026 /* Get response */
2027 ioc.write_flag = 0;
2028 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2029 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2030 if (err < 0) {
2031 err = -errno;
2032 goto out;
2033 }
2034
2035 break;
2036 case MMC_RPMB_READ_CNT:
2037 if (out_cnt != 1) {
2038 err = -EINVAL;
2039 goto out;
2040 }
2041 /* fall through */
2042
2043 case MMC_RPMB_READ:
2044 /* Request */
2045 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2046 if (err < 0) {
2047 err = -errno;
2048 goto out;
2049 }
2050
2051 /* Get response */
2052 ioc.write_flag = 0;
2053 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2054 ioc.blocks = out_cnt;
2055 ioc.data_ptr = (uintptr_t)frame_out;
2056 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2057 if (err < 0) {
2058 err = -errno;
2059 goto out;
2060 }
2061
2062 break;
2063 default:
2064 err = -EINVAL;
2065 goto out;
2066 }
2067
2068out:
2069 return err;
2070}
2071
2072int do_rpmb_write_key(int nargs, char **argv)
2073{
2074 int ret, dev_fd, key_fd;
2075 struct rpmb_frame frame_in = {
2076 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2077 }, frame_out;
2078
2079 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
2080 exit(1));
2081
2082 dev_fd = open(argv[1], O_RDWR);
2083 if (dev_fd < 0) {
2084 perror("device open");
2085 exit(1);
2086 }
2087
2088 if (0 == strcmp(argv[2], "-"))
2089 key_fd = STDIN_FILENO;
2090 else {
2091 key_fd = open(argv[2], O_RDONLY);
2092 if (key_fd < 0) {
2093 perror("can't open key file");
2094 exit(1);
2095 }
2096 }
2097
2098 /* Read the auth key */
2099 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2100 if (ret < 0) {
2101 perror("read the key");
2102 exit(1);
2103 } else if (ret != sizeof(frame_in.key_mac)) {
2104 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2105 (unsigned long)sizeof(frame_in.key_mac),
2106 ret);
2107 exit(1);
2108 }
2109
2110 /* Execute RPMB op */
2111 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2112 if (ret != 0) {
2113 perror("RPMB ioctl failed");
2114 exit(1);
2115 }
2116
2117 /* Check RPMB response */
2118 if (frame_out.result != 0) {
2119 printf("RPMB operation failed, retcode 0x%04x\n",
2120 be16toh(frame_out.result));
2121 exit(1);
2122 }
2123
2124 close(dev_fd);
2125 if (key_fd != STDIN_FILENO)
2126 close(key_fd);
2127
2128 return ret;
2129}
2130
2131int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2132{
2133 int ret;
2134 struct rpmb_frame frame_in = {
2135 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2136 }, frame_out;
2137
2138 /* Execute RPMB op */
2139 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2140 if (ret != 0) {
2141 perror("RPMB ioctl failed");
2142 exit(1);
2143 }
2144
2145 /* Check RPMB response */
2146 if (frame_out.result != 0)
2147 return be16toh(frame_out.result);
2148
2149 *cnt = be32toh(frame_out.write_counter);
2150
2151 return 0;
2152}
2153
2154int do_rpmb_read_counter(int nargs, char **argv)
2155{
2156 int ret, dev_fd;
2157 unsigned int cnt;
2158
2159 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
2160 exit(1));
2161
2162 dev_fd = open(argv[1], O_RDWR);
2163 if (dev_fd < 0) {
2164 perror("device open");
2165 exit(1);
2166 }
2167
2168 ret = rpmb_read_counter(dev_fd, &cnt);
2169
2170 /* Check RPMB response */
2171 if (ret != 0) {
2172 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2173 exit(1);
2174 }
2175
2176 close(dev_fd);
2177
2178 printf("Counter value: 0x%08x\n", cnt);
2179
2180 return ret;
2181}
2182
2183int do_rpmb_read_block(int nargs, char **argv)
2184{
2185 int i, ret, dev_fd, data_fd, key_fd = -1;
2186 uint16_t addr, blocks_cnt;
2187 unsigned char key[32];
2188 struct rpmb_frame frame_in = {
2189 .req_resp = htobe16(MMC_RPMB_READ),
2190 }, *frame_out_p;
2191
2192 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
2193 exit(1));
2194
2195 dev_fd = open(argv[1], O_RDWR);
2196 if (dev_fd < 0) {
2197 perror("device open");
2198 exit(1);
2199 }
2200
2201 /* Get block address */
2202 errno = 0;
2203 addr = strtol(argv[2], NULL, 0);
2204 if (errno) {
2205 perror("incorrect address");
2206 exit(1);
2207 }
2208 frame_in.addr = htobe16(addr);
2209
2210 /* Get blocks count */
2211 errno = 0;
2212 blocks_cnt = strtol(argv[3], NULL, 0);
2213 if (errno) {
2214 perror("incorrect blocks count");
2215 exit(1);
2216 }
2217
2218 if (!blocks_cnt) {
2219 printf("please, specify valid blocks count number\n");
2220 exit(1);
2221 }
2222
2223 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2224 if (!frame_out_p) {
2225 printf("can't allocate memory for RPMB outer frames\n");
2226 exit(1);
2227 }
2228
2229 /* Write 256b data */
2230 if (0 == strcmp(argv[4], "-"))
2231 data_fd = STDOUT_FILENO;
2232 else {
2233 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2234 S_IRUSR | S_IWUSR);
2235 if (data_fd < 0) {
2236 perror("can't open output file");
2237 exit(1);
2238 }
2239 }
2240
2241 /* Key is specified */
2242 if (nargs == 6) {
2243 if (0 == strcmp(argv[5], "-"))
2244 key_fd = STDIN_FILENO;
2245 else {
2246 key_fd = open(argv[5], O_RDONLY);
2247 if (key_fd < 0) {
2248 perror("can't open input key file");
2249 exit(1);
2250 }
2251 }
2252
2253 ret = DO_IO(read, key_fd, key, sizeof(key));
2254 if (ret < 0) {
2255 perror("read the key data");
2256 exit(1);
2257 } else if (ret != sizeof(key)) {
2258 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2259 (unsigned long)sizeof(key),
2260 ret);
2261 exit(1);
2262 }
2263 }
2264
2265 /* Execute RPMB op */
2266 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2267 if (ret != 0) {
2268 perror("RPMB ioctl failed");
2269 exit(1);
2270 }
2271
2272 /* Check RPMB response */
2273 if (frame_out_p[blocks_cnt - 1].result != 0) {
2274 printf("RPMB operation failed, retcode 0x%04x\n",
2275 be16toh(frame_out_p[blocks_cnt - 1].result));
2276 exit(1);
2277 }
2278
2279 /* Do we have to verify data against key? */
2280 if (nargs == 6) {
2281 unsigned char mac[32];
2282 hmac_sha256_ctx ctx;
2283 struct rpmb_frame *frame_out = NULL;
2284
2285 hmac_sha256_init(&ctx, key, sizeof(key));
2286 for (i = 0; i < blocks_cnt; i++) {
2287 frame_out = &frame_out_p[i];
2288 hmac_sha256_update(&ctx, frame_out->data,
2289 sizeof(*frame_out) -
2290 offsetof(struct rpmb_frame, data));
2291 }
2292
2293 hmac_sha256_final(&ctx, mac, sizeof(mac));
2294
2295 /* Impossible */
2296 assert(frame_out);
2297
2298 /* Compare calculated MAC and MAC from last frame */
2299 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2300 printf("RPMB MAC missmatch\n");
2301 exit(1);
2302 }
2303 }
2304
2305 /* Write data */
2306 for (i = 0; i < blocks_cnt; i++) {
2307 struct rpmb_frame *frame_out = &frame_out_p[i];
2308 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2309 if (ret < 0) {
2310 perror("write the data");
2311 exit(1);
2312 } else if (ret != sizeof(frame_out->data)) {
2313 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2314 (unsigned long)sizeof(frame_out->data),
2315 ret);
2316 exit(1);
2317 }
2318 }
2319
2320 free(frame_out_p);
2321 close(dev_fd);
2322 if (data_fd != STDOUT_FILENO)
2323 close(data_fd);
2324 if (key_fd != -1 && key_fd != STDIN_FILENO)
2325 close(key_fd);
2326
2327 return ret;
2328}
2329
2330int do_rpmb_write_block(int nargs, char **argv)
2331{
2332 int ret, dev_fd, key_fd, data_fd;
2333 unsigned char key[32];
2334 uint16_t addr;
2335 unsigned int cnt;
2336 struct rpmb_frame frame_in = {
2337 .req_resp = htobe16(MMC_RPMB_WRITE),
2338 .block_count = htobe16(1)
2339 }, frame_out;
2340
2341 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
2342 exit(1));
2343
2344 dev_fd = open(argv[1], O_RDWR);
2345 if (dev_fd < 0) {
2346 perror("device open");
2347 exit(1);
2348 }
2349
2350 ret = rpmb_read_counter(dev_fd, &cnt);
2351 /* Check RPMB response */
2352 if (ret != 0) {
2353 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2354 exit(1);
2355 }
2356 frame_in.write_counter = htobe32(cnt);
2357
2358 /* Get block address */
2359 errno = 0;
2360 addr = strtol(argv[2], NULL, 0);
2361 if (errno) {
2362 perror("incorrect address");
2363 exit(1);
2364 }
2365 frame_in.addr = htobe16(addr);
2366
2367 /* Read 256b data */
2368 if (0 == strcmp(argv[3], "-"))
2369 data_fd = STDIN_FILENO;
2370 else {
2371 data_fd = open(argv[3], O_RDONLY);
2372 if (data_fd < 0) {
2373 perror("can't open input file");
2374 exit(1);
2375 }
2376 }
2377
2378 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2379 if (ret < 0) {
2380 perror("read the data");
2381 exit(1);
2382 } else if (ret != sizeof(frame_in.data)) {
2383 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2384 (unsigned long)sizeof(frame_in.data),
2385 ret);
2386 exit(1);
2387 }
2388
2389 /* Read the auth key */
2390 if (0 == strcmp(argv[4], "-"))
2391 key_fd = STDIN_FILENO;
2392 else {
2393 key_fd = open(argv[4], O_RDONLY);
2394 if (key_fd < 0) {
2395 perror("can't open key file");
2396 exit(1);
2397 }
2398 }
2399
2400 ret = DO_IO(read, key_fd, key, sizeof(key));
2401 if (ret < 0) {
2402 perror("read the key");
2403 exit(1);
2404 } else if (ret != sizeof(key)) {
2405 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2406 (unsigned long)sizeof(key),
2407 ret);
2408 exit(1);
2409 }
2410
2411 /* Calculate HMAC SHA256 */
2412 hmac_sha256(
2413 key, sizeof(key),
2414 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2415 frame_in.key_mac, sizeof(frame_in.key_mac));
2416
2417 /* Execute RPMB op */
2418 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2419 if (ret != 0) {
2420 perror("RPMB ioctl failed");
2421 exit(1);
2422 }
2423
2424 /* Check RPMB response */
2425 if (frame_out.result != 0) {
2426 printf("RPMB operation failed, retcode 0x%04x\n",
2427 be16toh(frame_out.result));
2428 exit(1);
2429 }
2430
2431 close(dev_fd);
2432 if (data_fd != STDIN_FILENO)
2433 close(data_fd);
2434 if (key_fd != STDIN_FILENO)
2435 close(key_fd);
2436
2437 return ret;
2438}
Al Cooper786418c2015-04-29 18:12:35 -04002439
2440int do_cache_ctrl(int value, int nargs, char **argv)
2441{
2442 __u8 ext_csd[512];
2443 int fd, ret;
2444 char *device;
2445
2446 CHECK(nargs != 2, "Usage: mmc cache enable </path/to/mmcblkX>\n",
2447 exit(1));
2448
2449 device = argv[1];
2450
2451 fd = open(device, O_RDWR);
2452 if (fd < 0) {
2453 perror("open");
2454 exit(1);
2455 }
2456
2457 ret = read_extcsd(fd, ext_csd);
2458 if (ret) {
2459 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2460 exit(1);
2461 }
2462
2463 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2464 fprintf(stderr,
2465 "The CACHE option is only availabe on devices >= "
2466 "MMC 4.5 %s\n", device);
2467 exit(1);
2468 }
2469
2470 /* If the cache size is zero, this device does not have a cache */
2471 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2472 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2473 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2474 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2475 fprintf(stderr,
2476 "The CACHE option is not available on %s\n",
2477 device);
2478 exit(1);
2479 }
2480 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2481 if (ret) {
2482 fprintf(stderr,
2483 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2484 value, EXT_CSD_CACHE_CTRL, device);
2485 exit(1);
2486 }
2487
2488 return ret;
2489}
2490
2491int do_cache_en(int nargs, char **argv)
2492{
2493 return do_cache_ctrl(1, nargs, argv);
2494}
2495
2496int do_cache_dis(int nargs, char **argv)
2497{
2498 return do_cache_ctrl(0, nargs, argv);
2499}