blob: 3b0b57cd53b45204a840d254d217b34e42321a01 [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
Roman Peniaev023cc7c2014-08-12 23:25:45 +090043#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080044
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050045int read_extcsd(int fd, __u8 *ext_csd)
46{
47 int ret = 0;
48 struct mmc_ioc_cmd idata;
49 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080050 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050051 idata.write_flag = 0;
52 idata.opcode = MMC_SEND_EXT_CSD;
53 idata.arg = 0;
54 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080055 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050056 idata.blocks = 1;
57 mmc_ioc_cmd_set_data(idata, ext_csd);
58
59 ret = ioctl(fd, MMC_IOC_CMD, &idata);
60 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080061 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050062
63 return ret;
64}
65
66int write_extcsd_value(int fd, __u8 index, __u8 value)
67{
68 int ret = 0;
69 struct mmc_ioc_cmd idata;
70
71 memset(&idata, 0, sizeof(idata));
72 idata.write_flag = 1;
73 idata.opcode = MMC_SWITCH;
74 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
75 (index << 16) |
76 (value << 8) |
77 EXT_CSD_CMD_SET_NORMAL;
78 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
79
80 ret = ioctl(fd, MMC_IOC_CMD, &idata);
81 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080082 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050083
84 return ret;
85}
86
Ben Gardiner27c357d2013-05-30 17:12:47 -040087int send_status(int fd, __u32 *response)
88{
89 int ret = 0;
90 struct mmc_ioc_cmd idata;
91
92 memset(&idata, 0, sizeof(idata));
93 idata.opcode = MMC_SEND_STATUS;
94 idata.arg = (1 << 16);
95 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
96
97 ret = ioctl(fd, MMC_IOC_CMD, &idata);
98 if (ret)
99 perror("ioctl");
100
101 *response = idata.response[0];
102
103 return ret;
104}
105
Chris Ballb9c7a172012-02-20 12:34:25 -0500106void print_writeprotect_status(__u8 *ext_csd)
107{
108 __u8 reg;
109 __u8 ext_csd_rev = ext_csd[192];
110
111 /* A43: reserved [174:0] */
112 if (ext_csd_rev >= 5) {
113 printf("Boot write protection status registers"
114 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
115
116 reg = ext_csd[EXT_CSD_BOOT_WP];
117 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
118 printf(" Power ro locking: ");
119 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
120 printf("not possible\n");
121 else
122 printf("possible\n");
123
124 printf(" Permanent ro locking: ");
125 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
126 printf("not possible\n");
127 else
128 printf("possible\n");
129
130 printf(" ro lock status: ");
131 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
132 printf("locked until next power on\n");
133 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
134 printf("locked permanently\n");
135 else
136 printf("not locked\n");
137 }
138}
139
140int do_writeprotect_get(int nargs, char **argv)
141{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800142 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500143 int fd, ret;
144 char *device;
145
Chris Ball8ba44662012-04-19 13:22:54 -0400146 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
147 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500148
149 device = argv[1];
150
151 fd = open(device, O_RDWR);
152 if (fd < 0) {
153 perror("open");
154 exit(1);
155 }
156
157 ret = read_extcsd(fd, ext_csd);
158 if (ret) {
159 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
160 exit(1);
161 }
162
163 print_writeprotect_status(ext_csd);
164
165 return ret;
166}
167
168int do_writeprotect_set(int nargs, char **argv)
169{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800170 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500171 int fd, ret;
172 char *device;
173
Chris Ball8ba44662012-04-19 13:22:54 -0400174 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
175 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500176
177 device = argv[1];
178
179 fd = open(device, O_RDWR);
180 if (fd < 0) {
181 perror("open");
182 exit(1);
183 }
184
185 ret = read_extcsd(fd, ext_csd);
186 if (ret) {
187 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
188 exit(1);
189 }
190
191 value = ext_csd[EXT_CSD_BOOT_WP] |
192 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
193 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
194 if (ret) {
195 fprintf(stderr, "Could not write 0x%02x to "
196 "EXT_CSD[%d] in %s\n",
197 value, EXT_CSD_BOOT_WP, device);
198 exit(1);
199 }
200
201 return ret;
202}
203
Saugata Dasb7e25992012-05-17 09:26:34 -0400204int do_disable_512B_emulation(int nargs, char **argv)
205{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800206 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400207 int fd, ret;
208 char *device;
209
210 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
211 device = argv[1];
212
213 fd = open(device, O_RDWR);
214 if (fd < 0) {
215 perror("open");
216 exit(1);
217 }
218
219 ret = read_extcsd(fd, ext_csd);
220 if (ret) {
221 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
222 exit(1);
223 }
224
225 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
226 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
227 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
228
229 if (native_sector_size && !data_sector_size &&
230 (wr_rel_param & EN_REL_WR)) {
231 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
232
233 if (ret) {
234 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
235 1, EXT_CSD_BOOT_WP, device);
236 exit(1);
237 }
238 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
239 } else if (native_sector_size && data_sector_size) {
240 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
241 } else {
242 printf("MMC does not support disabling 512B emulation mode.\n");
243 }
244
245 return ret;
246}
247
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200248int do_write_boot_en(int nargs, char **argv)
249{
250 __u8 ext_csd[512];
251 __u8 value = 0;
252 int fd, ret;
253 char *device;
254 int boot_area, send_ack;
255
256 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
257 "<send_ack> </path/to/mmcblkX>\n", exit(1));
258
259 /*
260 * If <send_ack> is 1, the device will send acknowledgment
261 * pattern "010" to the host when boot operation begins.
262 * If <send_ack> is 0, it won't.
263 */
264 boot_area = strtol(argv[1], NULL, 10);
265 send_ack = strtol(argv[2], NULL, 10);
266 device = argv[3];
267
268 fd = open(device, O_RDWR);
269 if (fd < 0) {
270 perror("open");
271 exit(1);
272 }
273
274 ret = read_extcsd(fd, ext_csd);
275 if (ret) {
276 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
277 exit(1);
278 }
279
280 value = ext_csd[EXT_CSD_PART_CONFIG];
281
282 switch (boot_area) {
283 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
284 value |= (1 << 3);
285 value &= ~(3 << 4);
286 break;
287 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
288 value |= (1 << 4);
289 value &= ~(1 << 3);
290 value &= ~(1 << 5);
291 break;
292 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
293 value |= (boot_area << 3);
294 break;
295 default:
296 fprintf(stderr, "Cannot enable the boot area\n");
297 exit(1);
298 }
299 if (send_ack)
300 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
301 else
302 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
303
304 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
305 if (ret) {
306 fprintf(stderr, "Could not write 0x%02x to "
307 "EXT_CSD[%d] in %s\n",
308 value, EXT_CSD_PART_CONFIG, device);
309 exit(1);
310 }
311 return ret;
312}
313
Chris Ballf74dfe22012-10-19 16:49:55 -0400314int do_hwreset(int value, int nargs, char **argv)
315{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800316 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400317 int fd, ret;
318 char *device;
319
320 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
321 exit(1));
322
323 device = argv[1];
324
325 fd = open(device, O_RDWR);
326 if (fd < 0) {
327 perror("open");
328 exit(1);
329 }
330
331 ret = read_extcsd(fd, ext_csd);
332 if (ret) {
333 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
334 exit(1);
335 }
336
337 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
338 EXT_CSD_HW_RESET_EN) {
339 fprintf(stderr,
340 "H/W Reset is already permanently enabled on %s\n",
341 device);
342 exit(1);
343 }
344 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
345 EXT_CSD_HW_RESET_DIS) {
346 fprintf(stderr,
347 "H/W Reset is already permanently disabled on %s\n",
348 device);
349 exit(1);
350 }
351
352 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
353 if (ret) {
354 fprintf(stderr,
355 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
356 value, EXT_CSD_RST_N_FUNCTION, device);
357 exit(1);
358 }
359
360 return ret;
361}
362
363int do_hwreset_en(int nargs, char **argv)
364{
365 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
366}
367
368int do_hwreset_dis(int nargs, char **argv)
369{
370 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
371}
372
Jaehoon Chung86496512012-09-21 10:08:05 +0000373int do_write_bkops_en(int nargs, char **argv)
374{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800375 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000376 int fd, ret;
377 char *device;
378
379 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
380 exit(1));
381
382 device = argv[1];
383
384 fd = open(device, O_RDWR);
385 if (fd < 0) {
386 perror("open");
387 exit(1);
388 }
389
390 ret = read_extcsd(fd, ext_csd);
391 if (ret) {
392 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
393 exit(1);
394 }
395
396 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
397 fprintf(stderr, "%s doesn't support BKOPS\n", device);
398 exit(1);
399 }
400
401 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
402 if (ret) {
403 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
404 value, EXT_CSD_BKOPS_EN, device);
405 exit(1);
406 }
407
408 return ret;
409}
410
Ben Gardiner27c357d2013-05-30 17:12:47 -0400411int do_status_get(int nargs, char **argv)
412{
413 __u32 response;
414 int fd, ret;
415 char *device;
416
417 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
418 exit(1));
419
420 device = argv[1];
421
422 fd = open(device, O_RDWR);
423 if (fd < 0) {
424 perror("open");
425 exit(1);
426 }
427
428 ret = send_status(fd, &response);
429 if (ret) {
430 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
431 exit(1);
432 }
433
434 printf("SEND_STATUS response: 0x%08x\n", response);
435
436 return ret;
437}
438
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800439__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
440{
441 return (ext_csd_loc[3] << 24) |
442 (ext_csd_loc[2] << 16) |
443 (ext_csd_loc[1] << 8) |
444 ext_csd_loc[0];
445}
446
Ben Gardiner4e850232013-05-30 17:12:49 -0400447unsigned int get_sector_count(__u8 *ext_csd)
448{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800449 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400450}
451
452int is_blockaddresed(__u8 *ext_csd)
453{
454 unsigned int sectors = get_sector_count(ext_csd);
455
456 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
457}
458
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400459unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
460{
461 return ext_csd[221];
462}
463
464unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
465{
466 return ext_csd[224];
467}
468
Ben Gardinere6e84e92013-09-19 11:14:27 -0400469int set_partitioning_setting_completed(int dry_run, const char * const device,
470 int fd)
471{
472 int ret;
473
474 if (dry_run) {
475 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
476 fprintf(stderr, "These changes will not take effect neither "
477 "now nor after a power cycle\n");
478 return 1;
479 }
480
481 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
482 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
483 if (ret) {
484 fprintf(stderr, "Could not write 0x1 to "
485 "EXT_CSD[%d] in %s\n",
486 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
487 return 1;
488 }
489
490 __u32 response;
491 ret = send_status(fd, &response);
492 if (ret) {
493 fprintf(stderr, "Could not get response to SEND_STATUS "
494 "from %s\n", device);
495 return 1;
496 }
497
498 if (response & R1_SWITCH_ERROR) {
499 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
500 "failed on %s\n", device);
501 return 1;
502 }
503
504 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
505 "%s SUCCESS\n", device);
506 fprintf(stderr, "Device power cycle needed for settings to "
507 "take effect.\n"
508 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
509 "using 'extcsd read' after power cycle\n");
510
511 return 0;
512}
513
Balaji T K1fdb7f92015-04-29 18:12:32 -0400514int check_enhanced_area_total_limit(const char * const device, int fd)
515{
516 __u8 ext_csd[512];
517 __u32 regl;
518 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
519 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
520 unsigned int wp_sz, erase_sz;
521 int ret;
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 wp_sz = get_hc_wp_grp_size(ext_csd);
529 erase_sz = get_hc_erase_grp_size(ext_csd);
530
531 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
532 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
533 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
534 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
535 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
536 enh_area_sz += gp4_part_sz;
537 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
538 printf(" i.e. %lu KiB\n", gp4_part_sz);
539 }
540
541 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
542 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
543 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
544 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
545 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
546 enh_area_sz += gp3_part_sz;
547 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
548 printf(" i.e. %lu KiB\n", gp3_part_sz);
549 }
550
551 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
552 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
553 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
554 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
555 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
556 enh_area_sz += gp2_part_sz;
557 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
558 printf(" i.e. %lu KiB\n", gp2_part_sz);
559 }
560
561 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
562 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
563 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
564 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
565 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
566 enh_area_sz += gp1_part_sz;
567 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
568 printf(" i.e. %lu KiB\n", gp1_part_sz);
569 }
570
571 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
572 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
573 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
574 user_area_sz = 512l * regl * erase_sz * wp_sz;
575 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
576 enh_area_sz += user_area_sz;
577 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
578 printf(" i.e. %lu KiB\n", user_area_sz);
579 }
580
581 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
582 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
583 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
584 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
585 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
586 printf(" i.e. %lu KiB\n", max_enh_area_sz);
587 if (enh_area_sz > max_enh_area_sz) {
588 fprintf(stderr,
589 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
590 enh_area_sz, max_enh_area_sz, device);
591 return 1;
592 }
593
594 return 0;
595}
596
Ben Gardinerd91d3692013-05-30 17:12:51 -0400597int do_enh_area_set(int nargs, char **argv)
598{
599 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800600 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400601 int fd, ret;
602 char *device;
603 int dry_run = 1;
604 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
605 unsigned long align;
606
607 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
608 "</path/to/mmcblkX>\n", exit(1));
609
610 if (!strcmp("-y", argv[1]))
611 dry_run = 0;
612
613 start_kib = strtol(argv[2], NULL, 10);
614 length_kib = strtol(argv[3], NULL, 10);
615 device = argv[4];
616
617 fd = open(device, O_RDWR);
618 if (fd < 0) {
619 perror("open");
620 exit(1);
621 }
622
623 ret = read_extcsd(fd, ext_csd);
624 if (ret) {
625 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
626 exit(1);
627 }
628
629 /* assert ENH_ATTRIBUTE_EN */
630 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
631 {
632 printf(" Device cannot have enhanced tech.\n");
633 exit(1);
634 }
635
636 /* assert not PARTITION_SETTING_COMPLETED */
637 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
638 {
639 printf(" Device is already partitioned\n");
640 exit(1);
641 }
642
643 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
644
645 enh_size_mult = (length_kib + align/2l) / align;
646
647 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
648 enh_start_addr /= align;
649 enh_start_addr *= align;
650
651 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
652 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
653 if (ret) {
654 fprintf(stderr, "Could not write 0x1 to "
655 "EXT_CSD[%d] in %s\n",
656 EXT_CSD_ERASE_GROUP_DEF, device);
657 exit(1);
658 }
659
660 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
661 value = (enh_start_addr >> 24) & 0xff;
662 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
663 if (ret) {
664 fprintf(stderr, "Could not write 0x%02x to "
665 "EXT_CSD[%d] in %s\n", value,
666 EXT_CSD_ENH_START_ADDR_3, device);
667 exit(1);
668 }
669 value = (enh_start_addr >> 16) & 0xff;
670 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
671 if (ret) {
672 fprintf(stderr, "Could not write 0x%02x to "
673 "EXT_CSD[%d] in %s\n", value,
674 EXT_CSD_ENH_START_ADDR_2, device);
675 exit(1);
676 }
677 value = (enh_start_addr >> 8) & 0xff;
678 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
679 if (ret) {
680 fprintf(stderr, "Could not write 0x%02x to "
681 "EXT_CSD[%d] in %s\n", value,
682 EXT_CSD_ENH_START_ADDR_1, device);
683 exit(1);
684 }
685 value = enh_start_addr & 0xff;
686 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
687 if (ret) {
688 fprintf(stderr, "Could not write 0x%02x to "
689 "EXT_CSD[%d] in %s\n", value,
690 EXT_CSD_ENH_START_ADDR_0, device);
691 exit(1);
692 }
693
694 value = (enh_size_mult >> 16) & 0xff;
695 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
696 if (ret) {
697 fprintf(stderr, "Could not write 0x%02x to "
698 "EXT_CSD[%d] in %s\n", value,
699 EXT_CSD_ENH_SIZE_MULT_2, device);
700 exit(1);
701 }
702 value = (enh_size_mult >> 8) & 0xff;
703 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
704 if (ret) {
705 fprintf(stderr, "Could not write 0x%02x to "
706 "EXT_CSD[%d] in %s\n", value,
707 EXT_CSD_ENH_SIZE_MULT_1, device);
708 exit(1);
709 }
710 value = enh_size_mult & 0xff;
711 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
712 if (ret) {
713 fprintf(stderr, "Could not write 0x%02x to "
714 "EXT_CSD[%d] in %s\n", value,
715 EXT_CSD_ENH_SIZE_MULT_0, device);
716 exit(1);
717 }
Balaji T K1fdb7f92015-04-29 18:12:32 -0400718 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
719 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400720 if (ret) {
721 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
722 "EXT_CSD[%d] in %s\n",
723 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
724 exit(1);
725 }
726
Balaji T K1fdb7f92015-04-29 18:12:32 -0400727 ret = check_enhanced_area_total_limit(device, fd);
728 if (ret)
729 exit(1);
730
Ben Gardinere6e84e92013-09-19 11:14:27 -0400731 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400732
Ben Gardinere6e84e92013-09-19 11:14:27 -0400733 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400734 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400735
736 return 0;
737}
738
Ben Gardiner196d0d22013-09-19 11:14:29 -0400739int do_write_reliability_set(int nargs, char **argv)
740{
741 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800742 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400743 int fd, ret;
744
745 int dry_run = 1;
746 int partition;
747 char *device;
748
749 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
750 "<partition> </path/to/mmcblkX>\n", exit(1));
751
752 if (!strcmp("-y", argv[1]))
753 dry_run = 0;
754
755 partition = strtol(argv[2], NULL, 10);
756 device = argv[3];
757
758 fd = open(device, O_RDWR);
759 if (fd < 0) {
760 perror("open");
761 exit(1);
762 }
763
764 ret = read_extcsd(fd, ext_csd);
765 if (ret) {
766 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
767 exit(1);
768 }
769
770 /* assert not PARTITION_SETTING_COMPLETED */
771 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
772 {
773 printf(" Device is already partitioned\n");
774 exit(1);
775 }
776
777 /* assert HS_CTRL_REL */
778 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
779 printf("Cannot set write reliability parameters, WR_REL_SET is "
780 "read-only\n");
781 exit(1);
782 }
783
784 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
785 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
786 if (ret) {
787 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
788 value, EXT_CSD_WR_REL_SET, device);
789 exit(1);
790 }
791
792 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
793 value, device);
794
795 if (!set_partitioning_setting_completed(dry_run, device, fd))
796 exit(1);
797
798 return 0;
799}
800
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500801int do_read_extcsd(int nargs, char **argv)
802{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800803 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +0200804 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500805 int fd, ret;
806 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100807 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800808 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800809 "4.0", /* 0 */
810 "4.1", /* 1 */
811 "4.2", /* 2 */
812 "4.3", /* 3 */
813 "Obsolete", /* 4 */
814 "4.41", /* 5 */
815 "4.5", /* 6 */
816 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -0700817 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800818 };
819 int boot_access;
820 const char* boot_access_str[] = {
821 "No access to boot partition", /* 0 */
822 "R/W Boot Partition 1", /* 1 */
823 "R/W Boot Partition 2", /* 2 */
824 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
825 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500826
Chris Ball8ba44662012-04-19 13:22:54 -0400827 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
828 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500829
830 device = argv[1];
831
832 fd = open(device, O_RDWR);
833 if (fd < 0) {
834 perror("open");
835 exit(1);
836 }
837
838 ret = read_extcsd(fd, ext_csd);
839 if (ret) {
840 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
841 exit(1);
842 }
843
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100844 ext_csd_rev = ext_csd[192];
845
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800846 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
847 (ext_csd_rev != 4))
848 str = ver_str[ext_csd_rev];
849 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100850 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800851
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100852 printf("=============================================\n");
853 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
854 printf("=============================================\n\n");
855
856 if (ext_csd_rev < 3)
857 goto out_free; /* No ext_csd */
858
859 /* Parse the Extended CSD registers.
860 * Reserved bit should be read as "0" in case of spec older
861 * than A441.
862 */
863 reg = ext_csd[EXT_CSD_S_CMD_SET];
864 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
865 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -0500866 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100867
868 reg = ext_csd[EXT_CSD_HPI_FEATURE];
869 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
870 if (reg & EXT_CSD_HPI_SUPP) {
871 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -0500872 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100873 else
874 printf("implementation based on CMD13\n");
875 }
876
877 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
878 ext_csd[502]);
879
880 if (ext_csd_rev >= 6) {
881 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
882 ext_csd[501]);
883 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
884 ext_csd[500]);
885 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
886 ext_csd[499]);
887
888 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
889 ext_csd[498]);
890 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
891 ext_csd[497]);
892 printf("Context Management Capabilities"
893 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
894 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
895 ext_csd[495]);
896 printf("Extended partition attribute support"
897 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800898 }
899 if (ext_csd_rev >= 7) {
900 int j;
901 int eol_info;
902 char* eol_info_str[] = {
903 "Not Defined", /* 0 */
904 "Normal", /* 1 */
905 "Warning", /* 2 */
906 "Urgent", /* 3 */
907 };
908
909 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
910 ext_csd[493]);
911 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
912 ext_csd[492]);
913 printf("Operation codes timeout"
914 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
915 ext_csd[491]);
916 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
917 get_word_from_ext_csd(&ext_csd[487]));
918 printf("Number of FW sectors correctly programmed"
919 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
920 get_word_from_ext_csd(&ext_csd[302]));
921 printf("Vendor proprietary health report:\n");
922 for (j = 301; j >= 270; j--)
923 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
924 " 0x%02x\n", j, ext_csd[j]);
925 for (j = 269; j >= 268; j--) {
926 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800927 char est_type = 'B' + (j - 269);
928 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800929 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800930 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800931 if (life_used >= 0x1 && life_used <= 0xa)
932 printf(" i.e. %d%% - %d%% device life time"
933 " used\n",
934 (life_used - 1) * 10, life_used * 10);
935 else if (life_used == 0xb)
936 printf(" i.e. Exceeded its maximum estimated"
937 " device life time\n");
938 }
939 eol_info = ext_csd[267];
940 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
941 eol_info);
942 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
943 printf(" i.e. %s\n", eol_info_str[eol_info]);
944 else
945 printf(" i.e. Reserved\n");
946
947 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
948 ext_csd[266]);
949 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
950 ext_csd[265]);
951 printf("Optimal trim unit size"
952 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
953 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
954 ext_csd[263], ext_csd[262]);
955 printf("Firmware version:\n");
956 for (j = 261; j >= 254; j--)
957 printf("[FIRMWARE_VERSION[%d]]:"
958 " 0x%02x\n", j, ext_csd[j]);
959
960 printf("Power class for 200MHz, DDR at VCC= 3.6V"
961 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
962 }
963 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100964 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
965 ext_csd[248]);
966 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
967 ext_csd[247]);
968 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800969 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100970 }
971
972 /* A441: Reserved [501:247]
973 A43: reserved [246:229] */
974 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100975 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -0500976 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100977
978 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
979
980 printf("1st Initialisation Time after programmed sector"
981 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
982
983 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100984 printf("Power class for 52MHz, DDR at 3.6V"
985 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
986 printf("Power class for 52MHz, DDR at 1.95V"
987 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
988
989 /* A441: reserved [237-236] */
990
991 if (ext_csd_rev >= 6) {
992 printf("Power class for 200MHz at 3.6V"
993 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
994 printf("Power class for 200MHz, at 1.95V"
995 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
996 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500997 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100998 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
999 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1000 /* A441: reserved [233] */
1001 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1002 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1003 ext_csd[231]);
1004 }
1005 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1006 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1007 ext_csd[230]);
1008 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1009 ext_csd[229]);
1010 }
1011 reg = ext_csd[EXT_CSD_BOOT_INFO];
1012 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1013 if (reg & EXT_CSD_BOOT_INFO_ALT)
1014 printf(" Device supports alternative boot method\n");
1015 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1016 printf(" Device supports dual data rate during boot\n");
1017 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1018 printf(" Device supports high speed timing during boot\n");
1019
1020 /* A441/A43: reserved [227] */
1021 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1022 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001023
1024 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001025 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001026 reg);
1027 printf(" i.e. %u KiB\n", 512 * reg);
1028
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001029 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1030 ext_csd[223]);
1031 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1032 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001033
1034 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001035 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001036 reg);
1037 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1038
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001039 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1040 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1041 /* A441/A43: reserved [218] */
1042 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1043 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001044
1045 unsigned int sectors = get_sector_count(ext_csd);
1046 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1047 if (is_blockaddresed(ext_csd))
1048 printf(" Device is block-addressed\n");
1049 else
1050 printf(" Device is NOT block-addressed\n");
1051
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001052 /* A441/A43: reserved [211] */
1053 printf("Minimum Write Performance for 8bit:\n");
1054 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1055 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1056 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1057 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1058 printf("Minimum Write Performance for 4bit:\n");
1059 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1060 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1061 /* A441/A43: reserved [204] */
1062 printf("Power classes registers:\n");
1063 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1064 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1065 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1066 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1067
1068 /* A43: reserved [199:198] */
1069 if (ext_csd_rev >= 5) {
1070 printf("Partition switching timing "
1071 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1072 printf("Out-of-interrupt busy timing"
1073 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1074 }
1075
1076 /* A441/A43: reserved [197] [195] [193] [190] [188]
1077 * [186] [184] [182] [180] [176] */
1078
1079 if (ext_csd_rev >= 6)
1080 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1081 ext_csd[197]);
1082
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001083 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001084 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1085 ext_csd[196], ext_csd[195]);
1086 reg = ext_csd[195];
1087 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1088 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001089 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001090 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1091 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001092 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1093 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1094 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1095 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1096 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1097 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001098
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001099 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1100 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1101 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1102 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1103 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1104 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1105 ext_csd[185]);
1106 /* bus_width: ext_csd[183] not readable */
1107 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1108 ext_csd[181]);
1109 reg = ext_csd[EXT_CSD_BOOT_CFG];
1110 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001111 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001112 case 0x0:
1113 printf(" Not boot enable\n");
1114 break;
1115 case 0x1:
1116 printf(" Boot Partition 1 enabled\n");
1117 break;
1118 case 0x2:
1119 printf(" Boot Partition 2 enabled\n");
1120 break;
1121 case 0x7:
1122 printf(" User Area Enabled for boot\n");
1123 break;
1124 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001125 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1126 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1127 printf(" %s\n", boot_access_str[boot_access]);
1128 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001129 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001130 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001131
1132 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1133 ext_csd[178]);
1134 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1135 ext_csd[177]);
1136 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001137 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001138
Chris Ballb9c7a172012-02-20 12:34:25 -05001139 print_writeprotect_status(ext_csd);
1140
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001141 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001142 /* A441]: reserved [172] */
1143 printf("User area write protection register"
1144 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1145 /* A441]: reserved [170] */
1146 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1147 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001148
1149 reg = ext_csd[EXT_CSD_WR_REL_SET];
1150 const char * const fast = "existing data is at risk if a power "
1151 "failure occurs during a write operation";
1152 const char * const reliable = "the device protects existing "
1153 "data if a power failure occurs during a write "
1154 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001155 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001156 " [WR_REL_SET]: 0x%02x\n", reg);
1157
1158 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1159 int i;
1160 for (i = 1; i <= 4; i++) {
1161 printf(" partition %d: %s\n", i,
1162 reg & (1<<i) ? reliable : fast);
1163 }
1164
1165 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001166 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001167 " [WR_REL_PARAM]: 0x%02x\n", reg);
1168 if (reg & 0x01)
1169 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1170 if (reg & 0x04)
1171 printf(" Device supports the enhanced def. of reliable "
1172 "write\n");
1173
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001174 /* sanitize_start ext_csd[165]]: not readable
1175 * bkops_start ext_csd[164]]: only writable */
1176 printf("Enable background operations handshake"
1177 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1178 printf("H/W reset function"
1179 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1180 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001181 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001182 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1183 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001184 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001185 printf(" Device support partitioning feature\n");
1186 else
1187 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001188 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001189 printf(" Device can have enhanced tech.\n");
1190 else
1191 printf(" Device cannot have enhanced tech.\n");
1192
Oliver Metz11f2cea2013-09-23 08:40:52 +02001193 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001194 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1195 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1196
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001197 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001198 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001199 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1200 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001201 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001202
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001203 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001204 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001205 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001206 printf("Partitioning Setting"
1207 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001208 reg);
1209 if (reg)
1210 printf(" Device partition setting complete\n");
1211 else
1212 printf(" Device partition setting NOT complete\n");
1213
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001214 printf("General Purpose Partition Size\n"
1215 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1216 (ext_csd[153] << 8) | ext_csd[152]);
1217 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1218 (ext_csd[150] << 8) | ext_csd[149]);
1219 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1220 (ext_csd[147] << 8) | ext_csd[146]);
1221 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1222 (ext_csd[144] << 8) | ext_csd[143]);
1223
Oliver Metz11f2cea2013-09-23 08:40:52 +02001224 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001225 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1226 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001227 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001228 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1229 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001230 get_hc_erase_grp_size(ext_csd) *
1231 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001232
Oliver Metz11f2cea2013-09-23 08:40:52 +02001233 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001234 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1235 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1236 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001237 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001238 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001239 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001240 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001241
1242 /* A441]: reserved [135] */
1243 printf("Bad Block Management mode"
1244 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1245 /* A441: reserved [133:0] */
1246 }
1247 /* B45 */
1248 if (ext_csd_rev >= 6) {
1249 int j;
1250 /* tcase_support ext_csd[132] not readable */
1251 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1252 ext_csd[131]);
1253 printf("Program CID/CSD in DDR mode support"
1254 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1255 ext_csd[130]);
1256
1257 for (j = 127; j >= 64; j--)
1258 printf("Vendor Specific Fields"
1259 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1260 j, ext_csd[j]);
1261
Gwendal Grignoue966e672014-07-07 14:03:13 -07001262 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001263 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001264 reg);
1265 if (reg == 0x00)
1266 printf(" i.e. 512 B\n");
1267 else if (reg == 0x01)
1268 printf(" i.e. 4 KiB\n");
1269 else
1270 printf(" i.e. Reserved\n");
1271
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001272 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1273 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001274 reg = ext_csd[61];
1275 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1276 if (reg == 0x00)
1277 printf(" i.e. 512 B\n");
1278 else if (reg == 0x01)
1279 printf(" i.e. 4 KiB\n");
1280 else
1281 printf(" i.e. Reserved\n");
1282
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001283 printf("1st initialization after disabling sector"
1284 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1285 ext_csd[60]);
1286 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1287 ext_csd[59]);
1288 printf("Number of addressed group to be Released"
1289 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1290 printf("Exception events control"
1291 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1292 (ext_csd[57] << 8) | ext_csd[56]);
1293 printf("Exception events status"
1294 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1295 (ext_csd[55] << 8) | ext_csd[54]);
1296 printf("Extended Partitions Attribute"
1297 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1298 (ext_csd[53] << 8) | ext_csd[52]);
1299
1300 for (j = 51; j >= 37; j--)
1301 printf("Context configuration"
1302 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1303
1304 printf("Packed command status"
1305 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1306 printf("Packed command failure index"
1307 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1308 printf("Power Off Notification"
1309 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001310 printf("Control to turn the Cache ON/OFF"
1311 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001312 /* flush_cache ext_csd[32] not readable */
1313 /*Reserved [31:0] */
1314 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001315 if (ext_csd_rev >= 7) {
1316 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1317 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1318 ext_csd[29]);
1319
1320 reg = ext_csd[26];
1321 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1322 switch (reg) {
1323 case 0x00:
1324 printf(" Success\n");
1325 break;
1326 case 0x10:
1327 printf(" General error\n");
1328 break;
1329 case 0x11:
1330 printf(" Firmware install error\n");
1331 break;
1332 case 0x12:
1333 printf(" Error in downloading firmware\n");
1334 break;
1335 default:
1336 printf(" Reserved\n");
1337 }
1338 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1339 " %d sector size\n",
1340 get_word_from_ext_csd(&ext_csd[22]));
1341 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1342 " %d sector size\n",
1343 get_word_from_ext_csd(&ext_csd[18]));
1344 printf("Product state awareness enablement"
1345 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1346 ext_csd[17]);
1347 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1348 ext_csd[16]);
1349 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001350
1351out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001352 return ret;
1353}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001354
Nick Sanders9d57aa72014-03-05 21:38:54 -08001355int do_dump_extcsd(int nargs, char **argv)
1356{
1357 __u8 ext_csd[EXT_CSD_SIZE];
1358 int fd, ret;
1359 char *device;
1360 int i, j;
1361
1362 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1363 exit(1));
1364
1365 device = argv[1];
1366
1367 fd = open(device, O_RDWR);
1368 if (fd < 0) {
1369 perror("Failed to open mmc device");
1370 exit(1);
1371 }
1372
1373 ret = read_extcsd(fd, ext_csd);
1374 if (ret) {
1375 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1376 exit(1);
1377 }
1378
1379 /* Dump all bytes so that any undecoded or proprietary registers */
1380 /* can be acessed. */
1381 printf("EXT_CSD binary dump:\n");
1382 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1383 printf(" %3d: %3x: ", i, i);
1384 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1385 printf(" %02x", ext_csd[i+j]);
1386 }
1387 printf("\n");
1388 }
1389
1390 return ret;
1391}
1392
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001393int do_sanitize(int nargs, char **argv)
1394{
1395 int fd, ret;
1396 char *device;
1397
1398 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1399 exit(1));
1400
1401 device = argv[1];
1402
1403 fd = open(device, O_RDWR);
1404 if (fd < 0) {
1405 perror("open");
1406 exit(1);
1407 }
1408
1409 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1410 if (ret) {
1411 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1412 1, EXT_CSD_SANITIZE_START, device);
1413 exit(1);
1414 }
1415
1416 return ret;
1417
1418}
1419
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001420static const char* const mmc_ffu_hack_names[] = {
1421 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1422};
1423
Gwendal Grignou771984c2014-07-01 12:46:18 -07001424int do_emmc50_ffu (int nargs, char **argv)
1425{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001426 int fd, ret, i, argc=1, ffu_hack=0;
1427 char *device, *type, *path;
1428 __u64 value;
1429 union {
1430 __u8 data[FFU_DATA_SIZE];
1431 struct mmc_ffu_args ffu_args;
1432 } ffu_data;
1433 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001434 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001435
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001436 while (!strcmp("-k", argv[argc])) {
1437 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1438 if (ret < 1) {
1439 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1440 exit(1);
1441 }
1442 for (i = 0; i < MMC_HACK_LEN; i++) {
1443 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1444 ffu_args->hack[ffu_hack].type = i;
1445 if (ret == 2) {
1446 ffu_args->hack[ffu_hack].value = value;
1447 }
1448 ffu_hack++;
1449 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1450 sizeof(struct mmc_ffu_args) >
1451 FFU_DATA_SIZE) {
1452 fprintf(stderr, "Too many %d hacks",
1453 ffu_hack);
1454 exit(1);
1455 }
1456 break;
1457 }
1458 }
1459 if (i == MMC_HACK_LEN) {
1460 fprintf(stderr, "Hack type %s not found\n", type);
1461 fprintf(stderr, "Supported types are: ");
1462 for (i = 0; i < MMC_HACK_LEN; i++)
1463 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1464 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001465
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001466 exit(1);
1467 }
1468 free(type);
1469 argc++;
1470 }
1471 ffu_args->hack_nb = ffu_hack;
1472
1473 path = argv[argc++];
1474 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001475 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1476 exit(1);
1477 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001478 strcpy(ffu_args->name, path);
1479 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001480 fd = open(device, O_RDWR);
1481 if (fd < 0) {
1482 perror("open");
1483 exit(1);
1484 }
1485
Gwendal Grignou0f757342014-10-16 16:52:46 -07001486 /* prepare and send ioctl */
1487 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1488 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001489 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001490 mmc_ioc_cmd.blocks = 1;
1491 mmc_ioc_cmd.arg = 0;
1492 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1493 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001494 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001495 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001496 if (ret) {
1497 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1498 exit(1);
1499 }
1500
1501 close(fd);
1502 return 0;
1503}
1504
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001505#define DO_IO(func, fd, buf, nbyte) \
1506 ({ \
1507 ssize_t ret = 0, r; \
1508 do { \
1509 r = func(fd, buf + ret, nbyte - ret); \
1510 if (r < 0 && errno != EINTR) { \
1511 ret = -1; \
1512 break; \
1513 } \
1514 else if (r > 0) \
1515 ret += r; \
1516 } while (r != 0 && (size_t)ret != nbyte); \
1517 \
1518 ret; \
1519 })
1520
1521enum rpmb_op_type {
1522 MMC_RPMB_WRITE_KEY = 0x01,
1523 MMC_RPMB_READ_CNT = 0x02,
1524 MMC_RPMB_WRITE = 0x03,
1525 MMC_RPMB_READ = 0x04,
1526
1527 /* For internal usage only, do not use it directly */
1528 MMC_RPMB_READ_RESP = 0x05
1529};
1530
1531struct rpmb_frame {
1532 u_int8_t stuff[196];
1533 u_int8_t key_mac[32];
1534 u_int8_t data[256];
1535 u_int8_t nonce[16];
1536 u_int32_t write_counter;
1537 u_int16_t addr;
1538 u_int16_t block_count;
1539 u_int16_t result;
1540 u_int16_t req_resp;
1541};
1542
1543/* Performs RPMB operation.
1544 *
1545 * @fd: RPMB device on which we should perform ioctl command
1546 * @frame_in: input RPMB frame, should be properly inited
1547 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1548 * result and req_resp for output frame.
1549 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1550 * in the other cases -EINVAL will be returned.
1551 */
1552static int do_rpmb_op(int fd,
1553 const struct rpmb_frame *frame_in,
1554 struct rpmb_frame *frame_out,
1555 unsigned int out_cnt)
1556{
1557 int err;
1558 u_int16_t rpmb_type;
1559
1560 struct mmc_ioc_cmd ioc = {
1561 .arg = 0x0,
1562 .blksz = 512,
1563 .blocks = 1,
1564 .write_flag = 1,
1565 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1566 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1567 .data_ptr = (uintptr_t)frame_in
1568 };
1569
1570 if (!frame_in || !frame_out || !out_cnt)
1571 return -EINVAL;
1572
1573 rpmb_type = be16toh(frame_in->req_resp);
1574
1575 switch(rpmb_type) {
1576 case MMC_RPMB_WRITE:
1577 case MMC_RPMB_WRITE_KEY:
1578 if (out_cnt != 1) {
1579 err = -EINVAL;
1580 goto out;
1581 }
1582
1583 /* Write request */
1584 ioc.write_flag |= (1<<31);
1585 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1586 if (err < 0) {
1587 err = -errno;
1588 goto out;
1589 }
1590
1591 /* Result request */
1592 memset(frame_out, 0, sizeof(*frame_out));
1593 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
1594 ioc.write_flag = 1;
1595 ioc.data_ptr = (uintptr_t)frame_out;
1596 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1597 if (err < 0) {
1598 err = -errno;
1599 goto out;
1600 }
1601
1602 /* Get response */
1603 ioc.write_flag = 0;
1604 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1605 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1606 if (err < 0) {
1607 err = -errno;
1608 goto out;
1609 }
1610
1611 break;
1612 case MMC_RPMB_READ_CNT:
1613 if (out_cnt != 1) {
1614 err = -EINVAL;
1615 goto out;
1616 }
1617 /* fall through */
1618
1619 case MMC_RPMB_READ:
1620 /* Request */
1621 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1622 if (err < 0) {
1623 err = -errno;
1624 goto out;
1625 }
1626
1627 /* Get response */
1628 ioc.write_flag = 0;
1629 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1630 ioc.blocks = out_cnt;
1631 ioc.data_ptr = (uintptr_t)frame_out;
1632 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1633 if (err < 0) {
1634 err = -errno;
1635 goto out;
1636 }
1637
1638 break;
1639 default:
1640 err = -EINVAL;
1641 goto out;
1642 }
1643
1644out:
1645 return err;
1646}
1647
1648int do_rpmb_write_key(int nargs, char **argv)
1649{
1650 int ret, dev_fd, key_fd;
1651 struct rpmb_frame frame_in = {
1652 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
1653 }, frame_out;
1654
1655 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
1656 exit(1));
1657
1658 dev_fd = open(argv[1], O_RDWR);
1659 if (dev_fd < 0) {
1660 perror("device open");
1661 exit(1);
1662 }
1663
1664 if (0 == strcmp(argv[2], "-"))
1665 key_fd = STDIN_FILENO;
1666 else {
1667 key_fd = open(argv[2], O_RDONLY);
1668 if (key_fd < 0) {
1669 perror("can't open key file");
1670 exit(1);
1671 }
1672 }
1673
1674 /* Read the auth key */
1675 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
1676 if (ret < 0) {
1677 perror("read the key");
1678 exit(1);
1679 } else if (ret != sizeof(frame_in.key_mac)) {
1680 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1681 (unsigned long)sizeof(frame_in.key_mac),
1682 ret);
1683 exit(1);
1684 }
1685
1686 /* Execute RPMB op */
1687 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1688 if (ret != 0) {
1689 perror("RPMB ioctl failed");
1690 exit(1);
1691 }
1692
1693 /* Check RPMB response */
1694 if (frame_out.result != 0) {
1695 printf("RPMB operation failed, retcode 0x%04x\n",
1696 be16toh(frame_out.result));
1697 exit(1);
1698 }
1699
1700 close(dev_fd);
1701 if (key_fd != STDIN_FILENO)
1702 close(key_fd);
1703
1704 return ret;
1705}
1706
1707int rpmb_read_counter(int dev_fd, unsigned int *cnt)
1708{
1709 int ret;
1710 struct rpmb_frame frame_in = {
1711 .req_resp = htobe16(MMC_RPMB_READ_CNT)
1712 }, frame_out;
1713
1714 /* Execute RPMB op */
1715 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1716 if (ret != 0) {
1717 perror("RPMB ioctl failed");
1718 exit(1);
1719 }
1720
1721 /* Check RPMB response */
1722 if (frame_out.result != 0)
1723 return be16toh(frame_out.result);
1724
1725 *cnt = be32toh(frame_out.write_counter);
1726
1727 return 0;
1728}
1729
1730int do_rpmb_read_counter(int nargs, char **argv)
1731{
1732 int ret, dev_fd;
1733 unsigned int cnt;
1734
1735 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
1736 exit(1));
1737
1738 dev_fd = open(argv[1], O_RDWR);
1739 if (dev_fd < 0) {
1740 perror("device open");
1741 exit(1);
1742 }
1743
1744 ret = rpmb_read_counter(dev_fd, &cnt);
1745
1746 /* Check RPMB response */
1747 if (ret != 0) {
1748 printf("RPMB operation failed, retcode 0x%04x\n", ret);
1749 exit(1);
1750 }
1751
1752 close(dev_fd);
1753
1754 printf("Counter value: 0x%08x\n", cnt);
1755
1756 return ret;
1757}
1758
1759int do_rpmb_read_block(int nargs, char **argv)
1760{
1761 int i, ret, dev_fd, data_fd, key_fd = -1;
1762 uint16_t addr, blocks_cnt;
1763 unsigned char key[32];
1764 struct rpmb_frame frame_in = {
1765 .req_resp = htobe16(MMC_RPMB_READ),
1766 }, *frame_out_p;
1767
1768 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
1769 exit(1));
1770
1771 dev_fd = open(argv[1], O_RDWR);
1772 if (dev_fd < 0) {
1773 perror("device open");
1774 exit(1);
1775 }
1776
1777 /* Get block address */
1778 errno = 0;
1779 addr = strtol(argv[2], NULL, 0);
1780 if (errno) {
1781 perror("incorrect address");
1782 exit(1);
1783 }
1784 frame_in.addr = htobe16(addr);
1785
1786 /* Get blocks count */
1787 errno = 0;
1788 blocks_cnt = strtol(argv[3], NULL, 0);
1789 if (errno) {
1790 perror("incorrect blocks count");
1791 exit(1);
1792 }
1793
1794 if (!blocks_cnt) {
1795 printf("please, specify valid blocks count number\n");
1796 exit(1);
1797 }
1798
1799 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
1800 if (!frame_out_p) {
1801 printf("can't allocate memory for RPMB outer frames\n");
1802 exit(1);
1803 }
1804
1805 /* Write 256b data */
1806 if (0 == strcmp(argv[4], "-"))
1807 data_fd = STDOUT_FILENO;
1808 else {
1809 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
1810 S_IRUSR | S_IWUSR);
1811 if (data_fd < 0) {
1812 perror("can't open output file");
1813 exit(1);
1814 }
1815 }
1816
1817 /* Key is specified */
1818 if (nargs == 6) {
1819 if (0 == strcmp(argv[5], "-"))
1820 key_fd = STDIN_FILENO;
1821 else {
1822 key_fd = open(argv[5], O_RDONLY);
1823 if (key_fd < 0) {
1824 perror("can't open input key file");
1825 exit(1);
1826 }
1827 }
1828
1829 ret = DO_IO(read, key_fd, key, sizeof(key));
1830 if (ret < 0) {
1831 perror("read the key data");
1832 exit(1);
1833 } else if (ret != sizeof(key)) {
1834 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1835 (unsigned long)sizeof(key),
1836 ret);
1837 exit(1);
1838 }
1839 }
1840
1841 /* Execute RPMB op */
1842 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
1843 if (ret != 0) {
1844 perror("RPMB ioctl failed");
1845 exit(1);
1846 }
1847
1848 /* Check RPMB response */
1849 if (frame_out_p[blocks_cnt - 1].result != 0) {
1850 printf("RPMB operation failed, retcode 0x%04x\n",
1851 be16toh(frame_out_p[blocks_cnt - 1].result));
1852 exit(1);
1853 }
1854
1855 /* Do we have to verify data against key? */
1856 if (nargs == 6) {
1857 unsigned char mac[32];
1858 hmac_sha256_ctx ctx;
1859 struct rpmb_frame *frame_out = NULL;
1860
1861 hmac_sha256_init(&ctx, key, sizeof(key));
1862 for (i = 0; i < blocks_cnt; i++) {
1863 frame_out = &frame_out_p[i];
1864 hmac_sha256_update(&ctx, frame_out->data,
1865 sizeof(*frame_out) -
1866 offsetof(struct rpmb_frame, data));
1867 }
1868
1869 hmac_sha256_final(&ctx, mac, sizeof(mac));
1870
1871 /* Impossible */
1872 assert(frame_out);
1873
1874 /* Compare calculated MAC and MAC from last frame */
1875 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
1876 printf("RPMB MAC missmatch\n");
1877 exit(1);
1878 }
1879 }
1880
1881 /* Write data */
1882 for (i = 0; i < blocks_cnt; i++) {
1883 struct rpmb_frame *frame_out = &frame_out_p[i];
1884 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
1885 if (ret < 0) {
1886 perror("write the data");
1887 exit(1);
1888 } else if (ret != sizeof(frame_out->data)) {
1889 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
1890 (unsigned long)sizeof(frame_out->data),
1891 ret);
1892 exit(1);
1893 }
1894 }
1895
1896 free(frame_out_p);
1897 close(dev_fd);
1898 if (data_fd != STDOUT_FILENO)
1899 close(data_fd);
1900 if (key_fd != -1 && key_fd != STDIN_FILENO)
1901 close(key_fd);
1902
1903 return ret;
1904}
1905
1906int do_rpmb_write_block(int nargs, char **argv)
1907{
1908 int ret, dev_fd, key_fd, data_fd;
1909 unsigned char key[32];
1910 uint16_t addr;
1911 unsigned int cnt;
1912 struct rpmb_frame frame_in = {
1913 .req_resp = htobe16(MMC_RPMB_WRITE),
1914 .block_count = htobe16(1)
1915 }, frame_out;
1916
1917 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
1918 exit(1));
1919
1920 dev_fd = open(argv[1], O_RDWR);
1921 if (dev_fd < 0) {
1922 perror("device open");
1923 exit(1);
1924 }
1925
1926 ret = rpmb_read_counter(dev_fd, &cnt);
1927 /* Check RPMB response */
1928 if (ret != 0) {
1929 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
1930 exit(1);
1931 }
1932 frame_in.write_counter = htobe32(cnt);
1933
1934 /* Get block address */
1935 errno = 0;
1936 addr = strtol(argv[2], NULL, 0);
1937 if (errno) {
1938 perror("incorrect address");
1939 exit(1);
1940 }
1941 frame_in.addr = htobe16(addr);
1942
1943 /* Read 256b data */
1944 if (0 == strcmp(argv[3], "-"))
1945 data_fd = STDIN_FILENO;
1946 else {
1947 data_fd = open(argv[3], O_RDONLY);
1948 if (data_fd < 0) {
1949 perror("can't open input file");
1950 exit(1);
1951 }
1952 }
1953
1954 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
1955 if (ret < 0) {
1956 perror("read the data");
1957 exit(1);
1958 } else if (ret != sizeof(frame_in.data)) {
1959 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1960 (unsigned long)sizeof(frame_in.data),
1961 ret);
1962 exit(1);
1963 }
1964
1965 /* Read the auth key */
1966 if (0 == strcmp(argv[4], "-"))
1967 key_fd = STDIN_FILENO;
1968 else {
1969 key_fd = open(argv[4], O_RDONLY);
1970 if (key_fd < 0) {
1971 perror("can't open key file");
1972 exit(1);
1973 }
1974 }
1975
1976 ret = DO_IO(read, key_fd, key, sizeof(key));
1977 if (ret < 0) {
1978 perror("read the key");
1979 exit(1);
1980 } else if (ret != sizeof(key)) {
1981 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1982 (unsigned long)sizeof(key),
1983 ret);
1984 exit(1);
1985 }
1986
1987 /* Calculate HMAC SHA256 */
1988 hmac_sha256(
1989 key, sizeof(key),
1990 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
1991 frame_in.key_mac, sizeof(frame_in.key_mac));
1992
1993 /* Execute RPMB op */
1994 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1995 if (ret != 0) {
1996 perror("RPMB ioctl failed");
1997 exit(1);
1998 }
1999
2000 /* Check RPMB response */
2001 if (frame_out.result != 0) {
2002 printf("RPMB operation failed, retcode 0x%04x\n",
2003 be16toh(frame_out.result));
2004 exit(1);
2005 }
2006
2007 close(dev_fd);
2008 if (data_fd != STDIN_FILENO)
2009 close(data_fd);
2010 if (key_fd != STDIN_FILENO)
2011 close(key_fd);
2012
2013 return ret;
2014}