blob: 79a07cf615a0cef8dc929e6f91df789def88fb1d [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;
Al Cooper786418c2015-04-29 18:12:35 -0400109 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500110
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
Al Cooper794314c2015-05-01 08:24:37 -0400314int do_boot_bus_conditions_set(int nargs, char **argv)
315{
316 __u8 ext_csd[512];
317 __u8 value = 0;
318 int fd, ret;
319 char *device;
320
321 CHECK(nargs != 5, "Usage: mmc: bootbus set <boot_mode> "
322 "<reset_boot_bus_conditions> <boot_bus_width> <device>\n",
323 exit(1));
324
325 if (strcmp(argv[1], "single_backward") == 0)
326 value |= 0;
327 else if (strcmp(argv[1], "single_hs") == 0)
328 value |= 0x8;
329 else if (strcmp(argv[1], "dual") == 0)
330 value |= 0x10;
331 else {
332 fprintf(stderr, "illegal <boot_mode> specified\n");
333 exit(1);
334 }
335
336 if (strcmp(argv[2], "x1") == 0)
337 value |= 0;
338 else if (strcmp(argv[2], "retain") == 0)
339 value |= 0x4;
340 else {
341 fprintf(stderr,
342 "illegal <reset_boot_bus_conditions> specified\n");
343 exit(1);
344 }
345
346 if (strcmp(argv[3], "x1") == 0)
347 value |= 0;
348 else if (strcmp(argv[3], "x4") == 0)
349 value |= 0x1;
350 else if (strcmp(argv[3], "x8") == 0)
351 value |= 0x2;
352 else {
353 fprintf(stderr, "illegal <boot_bus_width> specified\n");
354 exit(1);
355 }
356
357 device = argv[4];
358 fd = open(device, O_RDWR);
359 if (fd < 0) {
360 perror("open");
361 exit(1);
362 }
363
364 ret = read_extcsd(fd, ext_csd);
365 if (ret) {
366 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
367 exit(1);
368 }
369 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
370 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
371
372 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
373 if (ret) {
374 fprintf(stderr, "Could not write 0x%02x to "
375 "EXT_CSD[%d] in %s\n",
376 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
377 exit(1);
378 }
379 close(fd);
380 return ret;
381}
382
Chris Ballf74dfe22012-10-19 16:49:55 -0400383int do_hwreset(int value, int nargs, char **argv)
384{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800385 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400386 int fd, ret;
387 char *device;
388
389 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
390 exit(1));
391
392 device = argv[1];
393
394 fd = open(device, O_RDWR);
395 if (fd < 0) {
396 perror("open");
397 exit(1);
398 }
399
400 ret = read_extcsd(fd, ext_csd);
401 if (ret) {
402 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
403 exit(1);
404 }
405
406 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
407 EXT_CSD_HW_RESET_EN) {
408 fprintf(stderr,
409 "H/W Reset is already permanently enabled on %s\n",
410 device);
411 exit(1);
412 }
413 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
414 EXT_CSD_HW_RESET_DIS) {
415 fprintf(stderr,
416 "H/W Reset is already permanently disabled on %s\n",
417 device);
418 exit(1);
419 }
420
421 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
422 if (ret) {
423 fprintf(stderr,
424 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
425 value, EXT_CSD_RST_N_FUNCTION, device);
426 exit(1);
427 }
428
429 return ret;
430}
431
432int do_hwreset_en(int nargs, char **argv)
433{
434 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
435}
436
437int do_hwreset_dis(int nargs, char **argv)
438{
439 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
440}
441
Jaehoon Chung86496512012-09-21 10:08:05 +0000442int do_write_bkops_en(int nargs, char **argv)
443{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800444 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000445 int fd, ret;
446 char *device;
447
448 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
449 exit(1));
450
451 device = argv[1];
452
453 fd = open(device, O_RDWR);
454 if (fd < 0) {
455 perror("open");
456 exit(1);
457 }
458
459 ret = read_extcsd(fd, ext_csd);
460 if (ret) {
461 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
462 exit(1);
463 }
464
465 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
466 fprintf(stderr, "%s doesn't support BKOPS\n", device);
467 exit(1);
468 }
469
470 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
471 if (ret) {
472 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
473 value, EXT_CSD_BKOPS_EN, device);
474 exit(1);
475 }
476
477 return ret;
478}
479
Ben Gardiner27c357d2013-05-30 17:12:47 -0400480int do_status_get(int nargs, char **argv)
481{
482 __u32 response;
483 int fd, ret;
484 char *device;
485
486 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
487 exit(1));
488
489 device = argv[1];
490
491 fd = open(device, O_RDWR);
492 if (fd < 0) {
493 perror("open");
494 exit(1);
495 }
496
497 ret = send_status(fd, &response);
498 if (ret) {
499 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
500 exit(1);
501 }
502
503 printf("SEND_STATUS response: 0x%08x\n", response);
504
505 return ret;
506}
507
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800508__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
509{
510 return (ext_csd_loc[3] << 24) |
511 (ext_csd_loc[2] << 16) |
512 (ext_csd_loc[1] << 8) |
513 ext_csd_loc[0];
514}
515
Ben Gardiner4e850232013-05-30 17:12:49 -0400516unsigned int get_sector_count(__u8 *ext_csd)
517{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800518 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400519}
520
521int is_blockaddresed(__u8 *ext_csd)
522{
523 unsigned int sectors = get_sector_count(ext_csd);
524
525 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
526}
527
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400528unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
529{
530 return ext_csd[221];
531}
532
533unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
534{
535 return ext_csd[224];
536}
537
Ben Gardinere6e84e92013-09-19 11:14:27 -0400538int set_partitioning_setting_completed(int dry_run, const char * const device,
539 int fd)
540{
541 int ret;
542
543 if (dry_run) {
544 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
545 fprintf(stderr, "These changes will not take effect neither "
546 "now nor after a power cycle\n");
547 return 1;
548 }
549
550 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
551 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
552 if (ret) {
553 fprintf(stderr, "Could not write 0x1 to "
554 "EXT_CSD[%d] in %s\n",
555 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
556 return 1;
557 }
558
559 __u32 response;
560 ret = send_status(fd, &response);
561 if (ret) {
562 fprintf(stderr, "Could not get response to SEND_STATUS "
563 "from %s\n", device);
564 return 1;
565 }
566
567 if (response & R1_SWITCH_ERROR) {
568 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
569 "failed on %s\n", device);
570 return 1;
571 }
572
573 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
574 "%s SUCCESS\n", device);
575 fprintf(stderr, "Device power cycle needed for settings to "
576 "take effect.\n"
577 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
578 "using 'extcsd read' after power cycle\n");
579
580 return 0;
581}
582
Balaji T K1fdb7f92015-04-29 18:12:32 -0400583int check_enhanced_area_total_limit(const char * const device, int fd)
584{
585 __u8 ext_csd[512];
586 __u32 regl;
587 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
588 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400589 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400590 unsigned int wp_sz, erase_sz;
591 int ret;
592
593 ret = read_extcsd(fd, ext_csd);
594 if (ret) {
595 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
596 exit(1);
597 }
598 wp_sz = get_hc_wp_grp_size(ext_csd);
599 erase_sz = get_hc_erase_grp_size(ext_csd);
600
601 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
602 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
603 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
604 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
605 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
606 enh_area_sz += gp4_part_sz;
607 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
608 printf(" i.e. %lu KiB\n", gp4_part_sz);
609 }
610
611 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
612 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
613 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
614 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
615 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
616 enh_area_sz += gp3_part_sz;
617 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
618 printf(" i.e. %lu KiB\n", gp3_part_sz);
619 }
620
621 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
622 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
623 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
624 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
625 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
626 enh_area_sz += gp2_part_sz;
627 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
628 printf(" i.e. %lu KiB\n", gp2_part_sz);
629 }
630
631 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
632 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
633 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
634 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
635 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
636 enh_area_sz += gp1_part_sz;
637 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
638 printf(" i.e. %lu KiB\n", gp1_part_sz);
639 }
640
641 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
642 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
643 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
644 user_area_sz = 512l * regl * erase_sz * wp_sz;
645 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
646 enh_area_sz += user_area_sz;
647 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
648 printf(" i.e. %lu KiB\n", user_area_sz);
649 }
650
651 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
652 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
653 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
654 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
655 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
656 printf(" i.e. %lu KiB\n", max_enh_area_sz);
657 if (enh_area_sz > max_enh_area_sz) {
658 fprintf(stderr,
659 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
660 enh_area_sz, max_enh_area_sz, device);
661 return 1;
662 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400663 total_sz = get_sector_count(ext_csd) / 2;
664 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
665 gp1_part_sz + user_area_sz;
666 if (total_gp_user_sz > total_sz) {
667 fprintf(stderr,
668 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
669 total_gp_user_sz, total_sz, device);
670 return 1;
671 }
672
673 return 0;
674}
675
676int do_create_gp_partition(int nargs, char **argv)
677{
678 __u8 value;
679 __u8 ext_csd[512];
680 __u8 address;
681 int fd, ret;
682 char *device;
683 int dry_run = 1;
684 int partition, enh_attr, ext_attr;
685 unsigned int length_kib, gp_size_mult;
686 unsigned long align;
687
688 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n> <length KiB> "
689 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
690
691 if (!strcmp("-y", argv[1]))
692 dry_run = 0;
693
694 length_kib = strtol(argv[2], NULL, 10);
695 partition = strtol(argv[3], NULL, 10);
696 enh_attr = strtol(argv[4], NULL, 10);
697 ext_attr = strtol(argv[5], NULL, 10);
698 device = argv[6];
699
700 if (partition < 0 || partition > 4) {
701 printf("Invalid gp parition number valid range [1-4]\n");
702 exit(1);
703 }
704
705 if (enh_attr && ext_attr) {
706 printf("Not allowed to set both enhanced attribute and extended attribute\n");
707 exit(1);
708 }
709
710 fd = open(device, O_RDWR);
711 if (fd < 0) {
712 perror("open");
713 exit(1);
714 }
715
716 ret = read_extcsd(fd, ext_csd);
717 if (ret) {
718 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
719 exit(1);
720 }
721
722 /* assert not PARTITION_SETTING_COMPLETED */
723 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
724 printf(" Device is already partitioned\n");
725 exit(1);
726 }
727
728 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
729 gp_size_mult = (length_kib + align/2l) / align;
730
731 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
732 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
733 if (ret) {
734 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
735 EXT_CSD_ERASE_GROUP_DEF, device);
736 exit(1);
737 }
738
739 value = (gp_size_mult >> 16) & 0xff;
740 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
741 ret = write_extcsd_value(fd, address, value);
742 if (ret) {
743 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
744 value, address, device);
745 exit(1);
746 }
747 value = (gp_size_mult >> 8) & 0xff;
748 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
749 ret = write_extcsd_value(fd, address, value);
750 if (ret) {
751 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
752 value, address, device);
753 exit(1);
754 }
755 value = gp_size_mult & 0xff;
756 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
757 ret = write_extcsd_value(fd, address, value);
758 if (ret) {
759 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
760 value, address, device);
761 exit(1);
762 }
763
764 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
765 if (enh_attr)
766 value |= (1 << partition);
767 else
768 value &= ~(1 << partition);
769
770 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
771 if (ret) {
772 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
773 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
774 exit(1);
775 }
776
777 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
778 value = ext_csd[address];
779 if (ext_attr)
780 value |= (ext_attr << (4 * ((partition - 1) % 2)));
781 else
782 value &= (0xF << (4 * ((partition % 2))));
783
784 ret = write_extcsd_value(fd, address, value);
785 if (ret) {
786 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
787 value, address, device);
788 exit(1);
789 }
790
791 ret = check_enhanced_area_total_limit(device, fd);
792 if (ret)
793 exit(1);
794
795 if (!set_partitioning_setting_completed(dry_run, device, fd))
796 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -0400797
798 return 0;
799}
800
Ben Gardinerd91d3692013-05-30 17:12:51 -0400801int do_enh_area_set(int nargs, char **argv)
802{
803 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800804 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400805 int fd, ret;
806 char *device;
807 int dry_run = 1;
808 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
809 unsigned long align;
810
811 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
812 "</path/to/mmcblkX>\n", exit(1));
813
814 if (!strcmp("-y", argv[1]))
815 dry_run = 0;
816
817 start_kib = strtol(argv[2], NULL, 10);
818 length_kib = strtol(argv[3], NULL, 10);
819 device = argv[4];
820
821 fd = open(device, O_RDWR);
822 if (fd < 0) {
823 perror("open");
824 exit(1);
825 }
826
827 ret = read_extcsd(fd, ext_csd);
828 if (ret) {
829 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
830 exit(1);
831 }
832
833 /* assert ENH_ATTRIBUTE_EN */
834 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
835 {
836 printf(" Device cannot have enhanced tech.\n");
837 exit(1);
838 }
839
840 /* assert not PARTITION_SETTING_COMPLETED */
841 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
842 {
843 printf(" Device is already partitioned\n");
844 exit(1);
845 }
846
847 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
848
849 enh_size_mult = (length_kib + align/2l) / align;
850
851 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
852 enh_start_addr /= align;
853 enh_start_addr *= align;
854
855 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
856 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
857 if (ret) {
858 fprintf(stderr, "Could not write 0x1 to "
859 "EXT_CSD[%d] in %s\n",
860 EXT_CSD_ERASE_GROUP_DEF, device);
861 exit(1);
862 }
863
864 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
865 value = (enh_start_addr >> 24) & 0xff;
866 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
867 if (ret) {
868 fprintf(stderr, "Could not write 0x%02x to "
869 "EXT_CSD[%d] in %s\n", value,
870 EXT_CSD_ENH_START_ADDR_3, device);
871 exit(1);
872 }
873 value = (enh_start_addr >> 16) & 0xff;
874 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
875 if (ret) {
876 fprintf(stderr, "Could not write 0x%02x to "
877 "EXT_CSD[%d] in %s\n", value,
878 EXT_CSD_ENH_START_ADDR_2, device);
879 exit(1);
880 }
881 value = (enh_start_addr >> 8) & 0xff;
882 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
883 if (ret) {
884 fprintf(stderr, "Could not write 0x%02x to "
885 "EXT_CSD[%d] in %s\n", value,
886 EXT_CSD_ENH_START_ADDR_1, device);
887 exit(1);
888 }
889 value = enh_start_addr & 0xff;
890 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
891 if (ret) {
892 fprintf(stderr, "Could not write 0x%02x to "
893 "EXT_CSD[%d] in %s\n", value,
894 EXT_CSD_ENH_START_ADDR_0, device);
895 exit(1);
896 }
897
898 value = (enh_size_mult >> 16) & 0xff;
899 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
900 if (ret) {
901 fprintf(stderr, "Could not write 0x%02x to "
902 "EXT_CSD[%d] in %s\n", value,
903 EXT_CSD_ENH_SIZE_MULT_2, device);
904 exit(1);
905 }
906 value = (enh_size_mult >> 8) & 0xff;
907 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
908 if (ret) {
909 fprintf(stderr, "Could not write 0x%02x to "
910 "EXT_CSD[%d] in %s\n", value,
911 EXT_CSD_ENH_SIZE_MULT_1, device);
912 exit(1);
913 }
914 value = enh_size_mult & 0xff;
915 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
916 if (ret) {
917 fprintf(stderr, "Could not write 0x%02x to "
918 "EXT_CSD[%d] in %s\n", value,
919 EXT_CSD_ENH_SIZE_MULT_0, device);
920 exit(1);
921 }
Balaji T K1fdb7f92015-04-29 18:12:32 -0400922 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
923 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400924 if (ret) {
925 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
926 "EXT_CSD[%d] in %s\n",
927 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
928 exit(1);
929 }
930
Balaji T K1fdb7f92015-04-29 18:12:32 -0400931 ret = check_enhanced_area_total_limit(device, fd);
932 if (ret)
933 exit(1);
934
Ben Gardinere6e84e92013-09-19 11:14:27 -0400935 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400936
Ben Gardinere6e84e92013-09-19 11:14:27 -0400937 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400938 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400939
940 return 0;
941}
942
Ben Gardiner196d0d22013-09-19 11:14:29 -0400943int do_write_reliability_set(int nargs, char **argv)
944{
945 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800946 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400947 int fd, ret;
948
949 int dry_run = 1;
950 int partition;
951 char *device;
952
953 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
954 "<partition> </path/to/mmcblkX>\n", exit(1));
955
956 if (!strcmp("-y", argv[1]))
957 dry_run = 0;
958
959 partition = strtol(argv[2], NULL, 10);
960 device = argv[3];
961
962 fd = open(device, O_RDWR);
963 if (fd < 0) {
964 perror("open");
965 exit(1);
966 }
967
968 ret = read_extcsd(fd, ext_csd);
969 if (ret) {
970 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
971 exit(1);
972 }
973
974 /* assert not PARTITION_SETTING_COMPLETED */
975 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
976 {
977 printf(" Device is already partitioned\n");
978 exit(1);
979 }
980
981 /* assert HS_CTRL_REL */
982 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
983 printf("Cannot set write reliability parameters, WR_REL_SET is "
984 "read-only\n");
985 exit(1);
986 }
987
988 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
989 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
990 if (ret) {
991 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
992 value, EXT_CSD_WR_REL_SET, device);
993 exit(1);
994 }
995
996 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
997 value, device);
998
999 if (!set_partitioning_setting_completed(dry_run, device, fd))
1000 exit(1);
1001
1002 return 0;
1003}
1004
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001005int do_read_extcsd(int nargs, char **argv)
1006{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001007 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001008 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001009 int fd, ret;
1010 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001011 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001012 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001013 "4.0", /* 0 */
1014 "4.1", /* 1 */
1015 "4.2", /* 2 */
1016 "4.3", /* 3 */
1017 "Obsolete", /* 4 */
1018 "4.41", /* 5 */
1019 "4.5", /* 6 */
1020 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001021 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001022 };
1023 int boot_access;
1024 const char* boot_access_str[] = {
1025 "No access to boot partition", /* 0 */
1026 "R/W Boot Partition 1", /* 1 */
1027 "R/W Boot Partition 2", /* 2 */
1028 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1029 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001030
Chris Ball8ba44662012-04-19 13:22:54 -04001031 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
1032 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001033
1034 device = argv[1];
1035
1036 fd = open(device, O_RDWR);
1037 if (fd < 0) {
1038 perror("open");
1039 exit(1);
1040 }
1041
1042 ret = read_extcsd(fd, ext_csd);
1043 if (ret) {
1044 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1045 exit(1);
1046 }
1047
Al Cooper786418c2015-04-29 18:12:35 -04001048 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001049
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001050 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1051 (ext_csd_rev != 4))
1052 str = ver_str[ext_csd_rev];
1053 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001054 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001055
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001056 printf("=============================================\n");
1057 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1058 printf("=============================================\n\n");
1059
1060 if (ext_csd_rev < 3)
1061 goto out_free; /* No ext_csd */
1062
1063 /* Parse the Extended CSD registers.
1064 * Reserved bit should be read as "0" in case of spec older
1065 * than A441.
1066 */
1067 reg = ext_csd[EXT_CSD_S_CMD_SET];
1068 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1069 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001070 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001071
1072 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1073 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1074 if (reg & EXT_CSD_HPI_SUPP) {
1075 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001076 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001077 else
1078 printf("implementation based on CMD13\n");
1079 }
1080
1081 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1082 ext_csd[502]);
1083
1084 if (ext_csd_rev >= 6) {
1085 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1086 ext_csd[501]);
1087 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1088 ext_csd[500]);
1089 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1090 ext_csd[499]);
1091
1092 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1093 ext_csd[498]);
1094 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1095 ext_csd[497]);
1096 printf("Context Management Capabilities"
1097 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1098 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1099 ext_csd[495]);
1100 printf("Extended partition attribute support"
1101 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001102 }
1103 if (ext_csd_rev >= 7) {
1104 int j;
1105 int eol_info;
1106 char* eol_info_str[] = {
1107 "Not Defined", /* 0 */
1108 "Normal", /* 1 */
1109 "Warning", /* 2 */
1110 "Urgent", /* 3 */
1111 };
1112
1113 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1114 ext_csd[493]);
1115 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1116 ext_csd[492]);
1117 printf("Operation codes timeout"
1118 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1119 ext_csd[491]);
1120 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1121 get_word_from_ext_csd(&ext_csd[487]));
1122 printf("Number of FW sectors correctly programmed"
1123 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1124 get_word_from_ext_csd(&ext_csd[302]));
1125 printf("Vendor proprietary health report:\n");
1126 for (j = 301; j >= 270; j--)
1127 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1128 " 0x%02x\n", j, ext_csd[j]);
1129 for (j = 269; j >= 268; j--) {
1130 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001131 char est_type = 'B' + (j - 269);
1132 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001133 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001134 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001135 if (life_used >= 0x1 && life_used <= 0xa)
1136 printf(" i.e. %d%% - %d%% device life time"
1137 " used\n",
1138 (life_used - 1) * 10, life_used * 10);
1139 else if (life_used == 0xb)
1140 printf(" i.e. Exceeded its maximum estimated"
1141 " device life time\n");
1142 }
1143 eol_info = ext_csd[267];
1144 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1145 eol_info);
1146 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1147 printf(" i.e. %s\n", eol_info_str[eol_info]);
1148 else
1149 printf(" i.e. Reserved\n");
1150
1151 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1152 ext_csd[266]);
1153 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1154 ext_csd[265]);
1155 printf("Optimal trim unit size"
1156 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1157 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1158 ext_csd[263], ext_csd[262]);
1159 printf("Firmware version:\n");
1160 for (j = 261; j >= 254; j--)
1161 printf("[FIRMWARE_VERSION[%d]]:"
1162 " 0x%02x\n", j, ext_csd[j]);
1163
1164 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1165 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1166 }
1167 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001168 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1169 ext_csd[248]);
1170 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1171 ext_csd[247]);
1172 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001173 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001174 }
1175
1176 /* A441: Reserved [501:247]
1177 A43: reserved [246:229] */
1178 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001179 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001180 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001181
1182 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1183
1184 printf("1st Initialisation Time after programmed sector"
1185 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1186
1187 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001188 printf("Power class for 52MHz, DDR at 3.6V"
1189 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1190 printf("Power class for 52MHz, DDR at 1.95V"
1191 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1192
1193 /* A441: reserved [237-236] */
1194
1195 if (ext_csd_rev >= 6) {
1196 printf("Power class for 200MHz at 3.6V"
1197 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1198 printf("Power class for 200MHz, at 1.95V"
1199 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1200 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001201 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001202 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1203 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1204 /* A441: reserved [233] */
1205 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1206 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1207 ext_csd[231]);
1208 }
1209 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1210 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1211 ext_csd[230]);
1212 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1213 ext_csd[229]);
1214 }
1215 reg = ext_csd[EXT_CSD_BOOT_INFO];
1216 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1217 if (reg & EXT_CSD_BOOT_INFO_ALT)
1218 printf(" Device supports alternative boot method\n");
1219 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1220 printf(" Device supports dual data rate during boot\n");
1221 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1222 printf(" Device supports high speed timing during boot\n");
1223
1224 /* A441/A43: reserved [227] */
1225 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1226 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001227
1228 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001229 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001230 reg);
1231 printf(" i.e. %u KiB\n", 512 * reg);
1232
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001233 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1234 ext_csd[223]);
1235 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1236 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001237
1238 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001239 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001240 reg);
1241 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1242
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001243 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1244 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1245 /* A441/A43: reserved [218] */
1246 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1247 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001248
1249 unsigned int sectors = get_sector_count(ext_csd);
1250 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1251 if (is_blockaddresed(ext_csd))
1252 printf(" Device is block-addressed\n");
1253 else
1254 printf(" Device is NOT block-addressed\n");
1255
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001256 /* A441/A43: reserved [211] */
1257 printf("Minimum Write Performance for 8bit:\n");
1258 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1259 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1260 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1261 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1262 printf("Minimum Write Performance for 4bit:\n");
1263 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1264 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1265 /* A441/A43: reserved [204] */
1266 printf("Power classes registers:\n");
1267 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1268 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1269 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1270 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1271
1272 /* A43: reserved [199:198] */
1273 if (ext_csd_rev >= 5) {
1274 printf("Partition switching timing "
1275 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1276 printf("Out-of-interrupt busy timing"
1277 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1278 }
1279
1280 /* A441/A43: reserved [197] [195] [193] [190] [188]
1281 * [186] [184] [182] [180] [176] */
1282
1283 if (ext_csd_rev >= 6)
1284 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1285 ext_csd[197]);
1286
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001287 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001288 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1289 ext_csd[196], ext_csd[195]);
1290 reg = ext_csd[195];
1291 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1292 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001293 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001294 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1295 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001296 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1297 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1298 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1299 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1300 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1301 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001302
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001303 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001304 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001305 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1306 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1307 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1308 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1309 ext_csd[185]);
1310 /* bus_width: ext_csd[183] not readable */
1311 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1312 ext_csd[181]);
1313 reg = ext_csd[EXT_CSD_BOOT_CFG];
1314 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001315 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001316 case 0x0:
1317 printf(" Not boot enable\n");
1318 break;
1319 case 0x1:
1320 printf(" Boot Partition 1 enabled\n");
1321 break;
1322 case 0x2:
1323 printf(" Boot Partition 2 enabled\n");
1324 break;
1325 case 0x7:
1326 printf(" User Area Enabled for boot\n");
1327 break;
1328 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001329 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1330 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1331 printf(" %s\n", boot_access_str[boot_access]);
1332 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001333 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001334 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001335
1336 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1337 ext_csd[178]);
1338 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1339 ext_csd[177]);
1340 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001341 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001342
Chris Ballb9c7a172012-02-20 12:34:25 -05001343 print_writeprotect_status(ext_csd);
1344
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001345 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001346 /* A441]: reserved [172] */
1347 printf("User area write protection register"
1348 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1349 /* A441]: reserved [170] */
1350 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1351 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001352
1353 reg = ext_csd[EXT_CSD_WR_REL_SET];
1354 const char * const fast = "existing data is at risk if a power "
1355 "failure occurs during a write operation";
1356 const char * const reliable = "the device protects existing "
1357 "data if a power failure occurs during a write "
1358 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001359 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001360 " [WR_REL_SET]: 0x%02x\n", reg);
1361
1362 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1363 int i;
1364 for (i = 1; i <= 4; i++) {
1365 printf(" partition %d: %s\n", i,
1366 reg & (1<<i) ? reliable : fast);
1367 }
1368
1369 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001370 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001371 " [WR_REL_PARAM]: 0x%02x\n", reg);
1372 if (reg & 0x01)
1373 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1374 if (reg & 0x04)
1375 printf(" Device supports the enhanced def. of reliable "
1376 "write\n");
1377
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001378 /* sanitize_start ext_csd[165]]: not readable
1379 * bkops_start ext_csd[164]]: only writable */
1380 printf("Enable background operations handshake"
1381 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1382 printf("H/W reset function"
1383 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1384 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001385 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001386 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1387 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001388 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001389 printf(" Device support partitioning feature\n");
1390 else
1391 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001392 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001393 printf(" Device can have enhanced tech.\n");
1394 else
1395 printf(" Device cannot have enhanced tech.\n");
1396
Oliver Metz11f2cea2013-09-23 08:40:52 +02001397 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001398 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1399 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1400
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001401 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001402 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001403 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1404 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001405 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001406
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001407 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001408 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001409 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001410 printf("Partitioning Setting"
1411 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001412 reg);
1413 if (reg)
1414 printf(" Device partition setting complete\n");
1415 else
1416 printf(" Device partition setting NOT complete\n");
1417
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001418 printf("General Purpose Partition Size\n"
1419 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1420 (ext_csd[153] << 8) | ext_csd[152]);
1421 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1422 (ext_csd[150] << 8) | ext_csd[149]);
1423 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1424 (ext_csd[147] << 8) | ext_csd[146]);
1425 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1426 (ext_csd[144] << 8) | ext_csd[143]);
1427
Oliver Metz11f2cea2013-09-23 08:40:52 +02001428 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001429 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1430 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001431 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001432 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1433 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001434 get_hc_erase_grp_size(ext_csd) *
1435 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001436
Oliver Metz11f2cea2013-09-23 08:40:52 +02001437 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001438 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1439 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1440 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001441 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001442 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001443 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001444 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001445
1446 /* A441]: reserved [135] */
1447 printf("Bad Block Management mode"
1448 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1449 /* A441: reserved [133:0] */
1450 }
1451 /* B45 */
1452 if (ext_csd_rev >= 6) {
1453 int j;
1454 /* tcase_support ext_csd[132] not readable */
1455 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1456 ext_csd[131]);
1457 printf("Program CID/CSD in DDR mode support"
1458 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1459 ext_csd[130]);
1460
1461 for (j = 127; j >= 64; j--)
1462 printf("Vendor Specific Fields"
1463 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1464 j, ext_csd[j]);
1465
Gwendal Grignoue966e672014-07-07 14:03:13 -07001466 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001467 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001468 reg);
1469 if (reg == 0x00)
1470 printf(" i.e. 512 B\n");
1471 else if (reg == 0x01)
1472 printf(" i.e. 4 KiB\n");
1473 else
1474 printf(" i.e. Reserved\n");
1475
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001476 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1477 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001478 reg = ext_csd[61];
1479 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1480 if (reg == 0x00)
1481 printf(" i.e. 512 B\n");
1482 else if (reg == 0x01)
1483 printf(" i.e. 4 KiB\n");
1484 else
1485 printf(" i.e. Reserved\n");
1486
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001487 printf("1st initialization after disabling sector"
1488 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1489 ext_csd[60]);
1490 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1491 ext_csd[59]);
1492 printf("Number of addressed group to be Released"
1493 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1494 printf("Exception events control"
1495 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1496 (ext_csd[57] << 8) | ext_csd[56]);
1497 printf("Exception events status"
1498 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1499 (ext_csd[55] << 8) | ext_csd[54]);
1500 printf("Extended Partitions Attribute"
1501 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1502 (ext_csd[53] << 8) | ext_csd[52]);
1503
1504 for (j = 51; j >= 37; j--)
1505 printf("Context configuration"
1506 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1507
1508 printf("Packed command status"
1509 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1510 printf("Packed command failure index"
1511 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1512 printf("Power Off Notification"
1513 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001514 printf("Control to turn the Cache ON/OFF"
1515 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001516 /* flush_cache ext_csd[32] not readable */
1517 /*Reserved [31:0] */
1518 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001519 if (ext_csd_rev >= 7) {
1520 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1521 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1522 ext_csd[29]);
1523
1524 reg = ext_csd[26];
1525 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1526 switch (reg) {
1527 case 0x00:
1528 printf(" Success\n");
1529 break;
1530 case 0x10:
1531 printf(" General error\n");
1532 break;
1533 case 0x11:
1534 printf(" Firmware install error\n");
1535 break;
1536 case 0x12:
1537 printf(" Error in downloading firmware\n");
1538 break;
1539 default:
1540 printf(" Reserved\n");
1541 }
1542 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1543 " %d sector size\n",
1544 get_word_from_ext_csd(&ext_csd[22]));
1545 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1546 " %d sector size\n",
1547 get_word_from_ext_csd(&ext_csd[18]));
1548 printf("Product state awareness enablement"
1549 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1550 ext_csd[17]);
1551 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1552 ext_csd[16]);
1553 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001554
1555out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001556 return ret;
1557}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001558
Nick Sanders9d57aa72014-03-05 21:38:54 -08001559int do_dump_extcsd(int nargs, char **argv)
1560{
1561 __u8 ext_csd[EXT_CSD_SIZE];
1562 int fd, ret;
1563 char *device;
1564 int i, j;
1565
1566 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1567 exit(1));
1568
1569 device = argv[1];
1570
1571 fd = open(device, O_RDWR);
1572 if (fd < 0) {
1573 perror("Failed to open mmc device");
1574 exit(1);
1575 }
1576
1577 ret = read_extcsd(fd, ext_csd);
1578 if (ret) {
1579 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1580 exit(1);
1581 }
1582
1583 /* Dump all bytes so that any undecoded or proprietary registers */
1584 /* can be acessed. */
1585 printf("EXT_CSD binary dump:\n");
1586 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1587 printf(" %3d: %3x: ", i, i);
1588 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1589 printf(" %02x", ext_csd[i+j]);
1590 }
1591 printf("\n");
1592 }
1593
1594 return ret;
1595}
1596
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001597int do_sanitize(int nargs, char **argv)
1598{
1599 int fd, ret;
1600 char *device;
1601
1602 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1603 exit(1));
1604
1605 device = argv[1];
1606
1607 fd = open(device, O_RDWR);
1608 if (fd < 0) {
1609 perror("open");
1610 exit(1);
1611 }
1612
1613 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1614 if (ret) {
1615 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1616 1, EXT_CSD_SANITIZE_START, device);
1617 exit(1);
1618 }
1619
1620 return ret;
1621
1622}
1623
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001624static const char* const mmc_ffu_hack_names[] = {
1625 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1626};
1627
Gwendal Grignou771984c2014-07-01 12:46:18 -07001628int do_emmc50_ffu (int nargs, char **argv)
1629{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001630 int fd, ret, i, argc=1, ffu_hack=0;
1631 char *device, *type, *path;
1632 __u64 value;
1633 union {
1634 __u8 data[FFU_DATA_SIZE];
1635 struct mmc_ffu_args ffu_args;
1636 } ffu_data;
1637 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001638 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001639
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001640 while (!strcmp("-k", argv[argc])) {
1641 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1642 if (ret < 1) {
1643 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1644 exit(1);
1645 }
1646 for (i = 0; i < MMC_HACK_LEN; i++) {
1647 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1648 ffu_args->hack[ffu_hack].type = i;
1649 if (ret == 2) {
1650 ffu_args->hack[ffu_hack].value = value;
1651 }
1652 ffu_hack++;
1653 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1654 sizeof(struct mmc_ffu_args) >
1655 FFU_DATA_SIZE) {
1656 fprintf(stderr, "Too many %d hacks",
1657 ffu_hack);
1658 exit(1);
1659 }
1660 break;
1661 }
1662 }
1663 if (i == MMC_HACK_LEN) {
1664 fprintf(stderr, "Hack type %s not found\n", type);
1665 fprintf(stderr, "Supported types are: ");
1666 for (i = 0; i < MMC_HACK_LEN; i++)
1667 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1668 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001669
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001670 exit(1);
1671 }
1672 free(type);
1673 argc++;
1674 }
1675 ffu_args->hack_nb = ffu_hack;
1676
1677 path = argv[argc++];
1678 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001679 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1680 exit(1);
1681 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001682 strcpy(ffu_args->name, path);
1683 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001684 fd = open(device, O_RDWR);
1685 if (fd < 0) {
1686 perror("open");
1687 exit(1);
1688 }
1689
Gwendal Grignou0f757342014-10-16 16:52:46 -07001690 /* prepare and send ioctl */
1691 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1692 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001693 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001694 mmc_ioc_cmd.blocks = 1;
1695 mmc_ioc_cmd.arg = 0;
1696 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1697 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001698 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001699 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001700 if (ret) {
1701 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1702 exit(1);
1703 }
1704
1705 close(fd);
1706 return 0;
1707}
1708
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001709#define DO_IO(func, fd, buf, nbyte) \
1710 ({ \
1711 ssize_t ret = 0, r; \
1712 do { \
1713 r = func(fd, buf + ret, nbyte - ret); \
1714 if (r < 0 && errno != EINTR) { \
1715 ret = -1; \
1716 break; \
1717 } \
1718 else if (r > 0) \
1719 ret += r; \
1720 } while (r != 0 && (size_t)ret != nbyte); \
1721 \
1722 ret; \
1723 })
1724
1725enum rpmb_op_type {
1726 MMC_RPMB_WRITE_KEY = 0x01,
1727 MMC_RPMB_READ_CNT = 0x02,
1728 MMC_RPMB_WRITE = 0x03,
1729 MMC_RPMB_READ = 0x04,
1730
1731 /* For internal usage only, do not use it directly */
1732 MMC_RPMB_READ_RESP = 0x05
1733};
1734
1735struct rpmb_frame {
1736 u_int8_t stuff[196];
1737 u_int8_t key_mac[32];
1738 u_int8_t data[256];
1739 u_int8_t nonce[16];
1740 u_int32_t write_counter;
1741 u_int16_t addr;
1742 u_int16_t block_count;
1743 u_int16_t result;
1744 u_int16_t req_resp;
1745};
1746
1747/* Performs RPMB operation.
1748 *
1749 * @fd: RPMB device on which we should perform ioctl command
1750 * @frame_in: input RPMB frame, should be properly inited
1751 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1752 * result and req_resp for output frame.
1753 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1754 * in the other cases -EINVAL will be returned.
1755 */
1756static int do_rpmb_op(int fd,
1757 const struct rpmb_frame *frame_in,
1758 struct rpmb_frame *frame_out,
1759 unsigned int out_cnt)
1760{
1761 int err;
1762 u_int16_t rpmb_type;
1763
1764 struct mmc_ioc_cmd ioc = {
1765 .arg = 0x0,
1766 .blksz = 512,
1767 .blocks = 1,
1768 .write_flag = 1,
1769 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1770 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1771 .data_ptr = (uintptr_t)frame_in
1772 };
1773
1774 if (!frame_in || !frame_out || !out_cnt)
1775 return -EINVAL;
1776
1777 rpmb_type = be16toh(frame_in->req_resp);
1778
1779 switch(rpmb_type) {
1780 case MMC_RPMB_WRITE:
1781 case MMC_RPMB_WRITE_KEY:
1782 if (out_cnt != 1) {
1783 err = -EINVAL;
1784 goto out;
1785 }
1786
1787 /* Write request */
1788 ioc.write_flag |= (1<<31);
1789 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1790 if (err < 0) {
1791 err = -errno;
1792 goto out;
1793 }
1794
1795 /* Result request */
1796 memset(frame_out, 0, sizeof(*frame_out));
1797 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
1798 ioc.write_flag = 1;
1799 ioc.data_ptr = (uintptr_t)frame_out;
1800 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1801 if (err < 0) {
1802 err = -errno;
1803 goto out;
1804 }
1805
1806 /* Get response */
1807 ioc.write_flag = 0;
1808 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1809 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1810 if (err < 0) {
1811 err = -errno;
1812 goto out;
1813 }
1814
1815 break;
1816 case MMC_RPMB_READ_CNT:
1817 if (out_cnt != 1) {
1818 err = -EINVAL;
1819 goto out;
1820 }
1821 /* fall through */
1822
1823 case MMC_RPMB_READ:
1824 /* Request */
1825 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1826 if (err < 0) {
1827 err = -errno;
1828 goto out;
1829 }
1830
1831 /* Get response */
1832 ioc.write_flag = 0;
1833 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1834 ioc.blocks = out_cnt;
1835 ioc.data_ptr = (uintptr_t)frame_out;
1836 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1837 if (err < 0) {
1838 err = -errno;
1839 goto out;
1840 }
1841
1842 break;
1843 default:
1844 err = -EINVAL;
1845 goto out;
1846 }
1847
1848out:
1849 return err;
1850}
1851
1852int do_rpmb_write_key(int nargs, char **argv)
1853{
1854 int ret, dev_fd, key_fd;
1855 struct rpmb_frame frame_in = {
1856 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
1857 }, frame_out;
1858
1859 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
1860 exit(1));
1861
1862 dev_fd = open(argv[1], O_RDWR);
1863 if (dev_fd < 0) {
1864 perror("device open");
1865 exit(1);
1866 }
1867
1868 if (0 == strcmp(argv[2], "-"))
1869 key_fd = STDIN_FILENO;
1870 else {
1871 key_fd = open(argv[2], O_RDONLY);
1872 if (key_fd < 0) {
1873 perror("can't open key file");
1874 exit(1);
1875 }
1876 }
1877
1878 /* Read the auth key */
1879 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
1880 if (ret < 0) {
1881 perror("read the key");
1882 exit(1);
1883 } else if (ret != sizeof(frame_in.key_mac)) {
1884 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1885 (unsigned long)sizeof(frame_in.key_mac),
1886 ret);
1887 exit(1);
1888 }
1889
1890 /* Execute RPMB op */
1891 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1892 if (ret != 0) {
1893 perror("RPMB ioctl failed");
1894 exit(1);
1895 }
1896
1897 /* Check RPMB response */
1898 if (frame_out.result != 0) {
1899 printf("RPMB operation failed, retcode 0x%04x\n",
1900 be16toh(frame_out.result));
1901 exit(1);
1902 }
1903
1904 close(dev_fd);
1905 if (key_fd != STDIN_FILENO)
1906 close(key_fd);
1907
1908 return ret;
1909}
1910
1911int rpmb_read_counter(int dev_fd, unsigned int *cnt)
1912{
1913 int ret;
1914 struct rpmb_frame frame_in = {
1915 .req_resp = htobe16(MMC_RPMB_READ_CNT)
1916 }, frame_out;
1917
1918 /* Execute RPMB op */
1919 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1920 if (ret != 0) {
1921 perror("RPMB ioctl failed");
1922 exit(1);
1923 }
1924
1925 /* Check RPMB response */
1926 if (frame_out.result != 0)
1927 return be16toh(frame_out.result);
1928
1929 *cnt = be32toh(frame_out.write_counter);
1930
1931 return 0;
1932}
1933
1934int do_rpmb_read_counter(int nargs, char **argv)
1935{
1936 int ret, dev_fd;
1937 unsigned int cnt;
1938
1939 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
1940 exit(1));
1941
1942 dev_fd = open(argv[1], O_RDWR);
1943 if (dev_fd < 0) {
1944 perror("device open");
1945 exit(1);
1946 }
1947
1948 ret = rpmb_read_counter(dev_fd, &cnt);
1949
1950 /* Check RPMB response */
1951 if (ret != 0) {
1952 printf("RPMB operation failed, retcode 0x%04x\n", ret);
1953 exit(1);
1954 }
1955
1956 close(dev_fd);
1957
1958 printf("Counter value: 0x%08x\n", cnt);
1959
1960 return ret;
1961}
1962
1963int do_rpmb_read_block(int nargs, char **argv)
1964{
1965 int i, ret, dev_fd, data_fd, key_fd = -1;
1966 uint16_t addr, blocks_cnt;
1967 unsigned char key[32];
1968 struct rpmb_frame frame_in = {
1969 .req_resp = htobe16(MMC_RPMB_READ),
1970 }, *frame_out_p;
1971
1972 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
1973 exit(1));
1974
1975 dev_fd = open(argv[1], O_RDWR);
1976 if (dev_fd < 0) {
1977 perror("device open");
1978 exit(1);
1979 }
1980
1981 /* Get block address */
1982 errno = 0;
1983 addr = strtol(argv[2], NULL, 0);
1984 if (errno) {
1985 perror("incorrect address");
1986 exit(1);
1987 }
1988 frame_in.addr = htobe16(addr);
1989
1990 /* Get blocks count */
1991 errno = 0;
1992 blocks_cnt = strtol(argv[3], NULL, 0);
1993 if (errno) {
1994 perror("incorrect blocks count");
1995 exit(1);
1996 }
1997
1998 if (!blocks_cnt) {
1999 printf("please, specify valid blocks count number\n");
2000 exit(1);
2001 }
2002
2003 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2004 if (!frame_out_p) {
2005 printf("can't allocate memory for RPMB outer frames\n");
2006 exit(1);
2007 }
2008
2009 /* Write 256b data */
2010 if (0 == strcmp(argv[4], "-"))
2011 data_fd = STDOUT_FILENO;
2012 else {
2013 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2014 S_IRUSR | S_IWUSR);
2015 if (data_fd < 0) {
2016 perror("can't open output file");
2017 exit(1);
2018 }
2019 }
2020
2021 /* Key is specified */
2022 if (nargs == 6) {
2023 if (0 == strcmp(argv[5], "-"))
2024 key_fd = STDIN_FILENO;
2025 else {
2026 key_fd = open(argv[5], O_RDONLY);
2027 if (key_fd < 0) {
2028 perror("can't open input key file");
2029 exit(1);
2030 }
2031 }
2032
2033 ret = DO_IO(read, key_fd, key, sizeof(key));
2034 if (ret < 0) {
2035 perror("read the key data");
2036 exit(1);
2037 } else if (ret != sizeof(key)) {
2038 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2039 (unsigned long)sizeof(key),
2040 ret);
2041 exit(1);
2042 }
2043 }
2044
2045 /* Execute RPMB op */
2046 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2047 if (ret != 0) {
2048 perror("RPMB ioctl failed");
2049 exit(1);
2050 }
2051
2052 /* Check RPMB response */
2053 if (frame_out_p[blocks_cnt - 1].result != 0) {
2054 printf("RPMB operation failed, retcode 0x%04x\n",
2055 be16toh(frame_out_p[blocks_cnt - 1].result));
2056 exit(1);
2057 }
2058
2059 /* Do we have to verify data against key? */
2060 if (nargs == 6) {
2061 unsigned char mac[32];
2062 hmac_sha256_ctx ctx;
2063 struct rpmb_frame *frame_out = NULL;
2064
2065 hmac_sha256_init(&ctx, key, sizeof(key));
2066 for (i = 0; i < blocks_cnt; i++) {
2067 frame_out = &frame_out_p[i];
2068 hmac_sha256_update(&ctx, frame_out->data,
2069 sizeof(*frame_out) -
2070 offsetof(struct rpmb_frame, data));
2071 }
2072
2073 hmac_sha256_final(&ctx, mac, sizeof(mac));
2074
2075 /* Impossible */
2076 assert(frame_out);
2077
2078 /* Compare calculated MAC and MAC from last frame */
2079 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2080 printf("RPMB MAC missmatch\n");
2081 exit(1);
2082 }
2083 }
2084
2085 /* Write data */
2086 for (i = 0; i < blocks_cnt; i++) {
2087 struct rpmb_frame *frame_out = &frame_out_p[i];
2088 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2089 if (ret < 0) {
2090 perror("write the data");
2091 exit(1);
2092 } else if (ret != sizeof(frame_out->data)) {
2093 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2094 (unsigned long)sizeof(frame_out->data),
2095 ret);
2096 exit(1);
2097 }
2098 }
2099
2100 free(frame_out_p);
2101 close(dev_fd);
2102 if (data_fd != STDOUT_FILENO)
2103 close(data_fd);
2104 if (key_fd != -1 && key_fd != STDIN_FILENO)
2105 close(key_fd);
2106
2107 return ret;
2108}
2109
2110int do_rpmb_write_block(int nargs, char **argv)
2111{
2112 int ret, dev_fd, key_fd, data_fd;
2113 unsigned char key[32];
2114 uint16_t addr;
2115 unsigned int cnt;
2116 struct rpmb_frame frame_in = {
2117 .req_resp = htobe16(MMC_RPMB_WRITE),
2118 .block_count = htobe16(1)
2119 }, frame_out;
2120
2121 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
2122 exit(1));
2123
2124 dev_fd = open(argv[1], O_RDWR);
2125 if (dev_fd < 0) {
2126 perror("device open");
2127 exit(1);
2128 }
2129
2130 ret = rpmb_read_counter(dev_fd, &cnt);
2131 /* Check RPMB response */
2132 if (ret != 0) {
2133 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2134 exit(1);
2135 }
2136 frame_in.write_counter = htobe32(cnt);
2137
2138 /* Get block address */
2139 errno = 0;
2140 addr = strtol(argv[2], NULL, 0);
2141 if (errno) {
2142 perror("incorrect address");
2143 exit(1);
2144 }
2145 frame_in.addr = htobe16(addr);
2146
2147 /* Read 256b data */
2148 if (0 == strcmp(argv[3], "-"))
2149 data_fd = STDIN_FILENO;
2150 else {
2151 data_fd = open(argv[3], O_RDONLY);
2152 if (data_fd < 0) {
2153 perror("can't open input file");
2154 exit(1);
2155 }
2156 }
2157
2158 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2159 if (ret < 0) {
2160 perror("read the data");
2161 exit(1);
2162 } else if (ret != sizeof(frame_in.data)) {
2163 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2164 (unsigned long)sizeof(frame_in.data),
2165 ret);
2166 exit(1);
2167 }
2168
2169 /* Read the auth key */
2170 if (0 == strcmp(argv[4], "-"))
2171 key_fd = STDIN_FILENO;
2172 else {
2173 key_fd = open(argv[4], O_RDONLY);
2174 if (key_fd < 0) {
2175 perror("can't open key file");
2176 exit(1);
2177 }
2178 }
2179
2180 ret = DO_IO(read, key_fd, key, sizeof(key));
2181 if (ret < 0) {
2182 perror("read the key");
2183 exit(1);
2184 } else if (ret != sizeof(key)) {
2185 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2186 (unsigned long)sizeof(key),
2187 ret);
2188 exit(1);
2189 }
2190
2191 /* Calculate HMAC SHA256 */
2192 hmac_sha256(
2193 key, sizeof(key),
2194 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2195 frame_in.key_mac, sizeof(frame_in.key_mac));
2196
2197 /* Execute RPMB op */
2198 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2199 if (ret != 0) {
2200 perror("RPMB ioctl failed");
2201 exit(1);
2202 }
2203
2204 /* Check RPMB response */
2205 if (frame_out.result != 0) {
2206 printf("RPMB operation failed, retcode 0x%04x\n",
2207 be16toh(frame_out.result));
2208 exit(1);
2209 }
2210
2211 close(dev_fd);
2212 if (data_fd != STDIN_FILENO)
2213 close(data_fd);
2214 if (key_fd != STDIN_FILENO)
2215 close(key_fd);
2216
2217 return ret;
2218}
Al Cooper786418c2015-04-29 18:12:35 -04002219
2220int do_cache_ctrl(int value, int nargs, char **argv)
2221{
2222 __u8 ext_csd[512];
2223 int fd, ret;
2224 char *device;
2225
2226 CHECK(nargs != 2, "Usage: mmc cache enable </path/to/mmcblkX>\n",
2227 exit(1));
2228
2229 device = argv[1];
2230
2231 fd = open(device, O_RDWR);
2232 if (fd < 0) {
2233 perror("open");
2234 exit(1);
2235 }
2236
2237 ret = read_extcsd(fd, ext_csd);
2238 if (ret) {
2239 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2240 exit(1);
2241 }
2242
2243 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2244 fprintf(stderr,
2245 "The CACHE option is only availabe on devices >= "
2246 "MMC 4.5 %s\n", device);
2247 exit(1);
2248 }
2249
2250 /* If the cache size is zero, this device does not have a cache */
2251 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2252 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2253 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2254 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2255 fprintf(stderr,
2256 "The CACHE option is not available on %s\n",
2257 device);
2258 exit(1);
2259 }
2260 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2261 if (ret) {
2262 fprintf(stderr,
2263 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2264 value, EXT_CSD_CACHE_CTRL, device);
2265 exit(1);
2266 }
2267
2268 return ret;
2269}
2270
2271int do_cache_en(int nargs, char **argv)
2272{
2273 return do_cache_ctrl(1, nargs, argv);
2274}
2275
2276int do_cache_dis(int nargs, char **argv)
2277{
2278 return do_cache_ctrl(0, nargs, argv);
2279}