blob: 797b5727e2b34a2b715259bfd93d548f05a6180c [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.
Avi Shchislowskidc7ab962016-03-08 14:22:41 -050015 *
16 * Modified to add field firmware update support,
17 * those modifications are Copyright (c) 2016 SanDisk Corp.
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050018 */
19
Gwendal Grignou771984c2014-07-01 12:46:18 -070020#include <errno.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050021#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/ioctl.h>
Gwendal Grignou771984c2014-07-01 12:46:18 -070025#include <sys/param.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050026#include <sys/types.h>
27#include <dirent.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <libgen.h>
32#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050033#include <ctype.h>
Roman Peniaev023cc7c2014-08-12 23:25:45 +090034#include <errno.h>
35#include <stdint.h>
36#include <assert.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050037
38#include "mmc.h"
39#include "mmc_cmds.h"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080040#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050041
Nick Sanders9d57aa72014-03-05 21:38:54 -080042#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080043#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080044#define CID_SIZE 16
45
Julius Wernerbcc3e2e2016-04-21 16:53:02 -070046/* Sending several commands too close together seems to cause timeouts. */
47#define INTER_COMMAND_GAP_US (50 * 1000)
48
Roman Peniaev023cc7c2014-08-12 23:25:45 +090049#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080050
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050051int read_extcsd(int fd, __u8 *ext_csd)
52{
53 int ret = 0;
54 struct mmc_ioc_cmd idata;
55 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080056 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050057 idata.write_flag = 0;
58 idata.opcode = MMC_SEND_EXT_CSD;
59 idata.arg = 0;
60 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080061 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050062 idata.blocks = 1;
63 mmc_ioc_cmd_set_data(idata, ext_csd);
64
65 ret = ioctl(fd, MMC_IOC_CMD, &idata);
66 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080067 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050068
69 return ret;
70}
71
72int write_extcsd_value(int fd, __u8 index, __u8 value)
73{
74 int ret = 0;
75 struct mmc_ioc_cmd idata;
76
77 memset(&idata, 0, sizeof(idata));
78 idata.write_flag = 1;
79 idata.opcode = MMC_SWITCH;
80 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
81 (index << 16) |
82 (value << 8) |
83 EXT_CSD_CMD_SET_NORMAL;
84 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
85
86 ret = ioctl(fd, MMC_IOC_CMD, &idata);
87 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080088 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050089
90 return ret;
91}
92
Ben Gardiner27c357d2013-05-30 17:12:47 -040093int send_status(int fd, __u32 *response)
94{
95 int ret = 0;
96 struct mmc_ioc_cmd idata;
97
98 memset(&idata, 0, sizeof(idata));
99 idata.opcode = MMC_SEND_STATUS;
100 idata.arg = (1 << 16);
101 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
102
103 ret = ioctl(fd, MMC_IOC_CMD, &idata);
104 if (ret)
105 perror("ioctl");
106
107 *response = idata.response[0];
108
109 return ret;
110}
111
Chris Ballb9c7a172012-02-20 12:34:25 -0500112void print_writeprotect_status(__u8 *ext_csd)
113{
114 __u8 reg;
Al Cooper786418c2015-04-29 18:12:35 -0400115 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500116
117 /* A43: reserved [174:0] */
118 if (ext_csd_rev >= 5) {
119 printf("Boot write protection status registers"
120 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
121
122 reg = ext_csd[EXT_CSD_BOOT_WP];
123 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
124 printf(" Power ro locking: ");
125 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
126 printf("not possible\n");
127 else
128 printf("possible\n");
129
130 printf(" Permanent ro locking: ");
131 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
132 printf("not possible\n");
133 else
134 printf("possible\n");
135
136 printf(" ro lock status: ");
137 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
138 printf("locked until next power on\n");
139 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
140 printf("locked permanently\n");
141 else
142 printf("not locked\n");
143 }
144}
145
146int do_writeprotect_get(int nargs, char **argv)
147{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800148 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500149 int fd, ret;
150 char *device;
151
Chris Ball8ba44662012-04-19 13:22:54 -0400152 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
153 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500154
155 device = argv[1];
156
157 fd = open(device, O_RDWR);
158 if (fd < 0) {
159 perror("open");
160 exit(1);
161 }
162
163 ret = read_extcsd(fd, ext_csd);
164 if (ret) {
165 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
166 exit(1);
167 }
168
169 print_writeprotect_status(ext_csd);
170
171 return ret;
172}
173
174int do_writeprotect_set(int nargs, char **argv)
175{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800176 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500177 int fd, ret;
178 char *device;
179
Chris Ball8ba44662012-04-19 13:22:54 -0400180 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
181 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500182
183 device = argv[1];
184
185 fd = open(device, O_RDWR);
186 if (fd < 0) {
187 perror("open");
188 exit(1);
189 }
190
191 ret = read_extcsd(fd, ext_csd);
192 if (ret) {
193 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
194 exit(1);
195 }
196
197 value = ext_csd[EXT_CSD_BOOT_WP] |
198 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
199 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
200 if (ret) {
201 fprintf(stderr, "Could not write 0x%02x to "
202 "EXT_CSD[%d] in %s\n",
203 value, EXT_CSD_BOOT_WP, device);
204 exit(1);
205 }
206
207 return ret;
208}
209
Saugata Dasb7e25992012-05-17 09:26:34 -0400210int do_disable_512B_emulation(int nargs, char **argv)
211{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800212 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400213 int fd, ret;
214 char *device;
215
216 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
217 device = argv[1];
218
219 fd = open(device, O_RDWR);
220 if (fd < 0) {
221 perror("open");
222 exit(1);
223 }
224
225 ret = read_extcsd(fd, ext_csd);
226 if (ret) {
227 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
228 exit(1);
229 }
230
231 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
232 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
233 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
234
235 if (native_sector_size && !data_sector_size &&
236 (wr_rel_param & EN_REL_WR)) {
237 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
238
239 if (ret) {
240 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
241 1, EXT_CSD_BOOT_WP, device);
242 exit(1);
243 }
244 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
245 } else if (native_sector_size && data_sector_size) {
246 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
247 } else {
248 printf("MMC does not support disabling 512B emulation mode.\n");
249 }
250
251 return ret;
252}
253
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200254int do_write_boot_en(int nargs, char **argv)
255{
256 __u8 ext_csd[512];
257 __u8 value = 0;
258 int fd, ret;
259 char *device;
260 int boot_area, send_ack;
261
262 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
263 "<send_ack> </path/to/mmcblkX>\n", exit(1));
264
265 /*
266 * If <send_ack> is 1, the device will send acknowledgment
267 * pattern "010" to the host when boot operation begins.
268 * If <send_ack> is 0, it won't.
269 */
270 boot_area = strtol(argv[1], NULL, 10);
271 send_ack = strtol(argv[2], NULL, 10);
272 device = argv[3];
273
274 fd = open(device, O_RDWR);
275 if (fd < 0) {
276 perror("open");
277 exit(1);
278 }
279
280 ret = read_extcsd(fd, ext_csd);
281 if (ret) {
282 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
283 exit(1);
284 }
285
286 value = ext_csd[EXT_CSD_PART_CONFIG];
287
288 switch (boot_area) {
289 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
290 value |= (1 << 3);
291 value &= ~(3 << 4);
292 break;
293 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
294 value |= (1 << 4);
295 value &= ~(1 << 3);
296 value &= ~(1 << 5);
297 break;
298 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
299 value |= (boot_area << 3);
300 break;
301 default:
302 fprintf(stderr, "Cannot enable the boot area\n");
303 exit(1);
304 }
305 if (send_ack)
306 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
307 else
308 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
309
310 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
311 if (ret) {
312 fprintf(stderr, "Could not write 0x%02x to "
313 "EXT_CSD[%d] in %s\n",
314 value, EXT_CSD_PART_CONFIG, device);
315 exit(1);
316 }
317 return ret;
318}
319
Al Cooper794314c2015-05-01 08:24:37 -0400320int do_boot_bus_conditions_set(int nargs, char **argv)
321{
322 __u8 ext_csd[512];
323 __u8 value = 0;
324 int fd, ret;
325 char *device;
326
327 CHECK(nargs != 5, "Usage: mmc: bootbus set <boot_mode> "
328 "<reset_boot_bus_conditions> <boot_bus_width> <device>\n",
329 exit(1));
330
331 if (strcmp(argv[1], "single_backward") == 0)
332 value |= 0;
333 else if (strcmp(argv[1], "single_hs") == 0)
334 value |= 0x8;
335 else if (strcmp(argv[1], "dual") == 0)
336 value |= 0x10;
337 else {
338 fprintf(stderr, "illegal <boot_mode> specified\n");
339 exit(1);
340 }
341
342 if (strcmp(argv[2], "x1") == 0)
343 value |= 0;
344 else if (strcmp(argv[2], "retain") == 0)
345 value |= 0x4;
346 else {
347 fprintf(stderr,
348 "illegal <reset_boot_bus_conditions> specified\n");
349 exit(1);
350 }
351
352 if (strcmp(argv[3], "x1") == 0)
353 value |= 0;
354 else if (strcmp(argv[3], "x4") == 0)
355 value |= 0x1;
356 else if (strcmp(argv[3], "x8") == 0)
357 value |= 0x2;
358 else {
359 fprintf(stderr, "illegal <boot_bus_width> specified\n");
360 exit(1);
361 }
362
363 device = argv[4];
364 fd = open(device, O_RDWR);
365 if (fd < 0) {
366 perror("open");
367 exit(1);
368 }
369
370 ret = read_extcsd(fd, ext_csd);
371 if (ret) {
372 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
373 exit(1);
374 }
375 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
376 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
377
378 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
379 if (ret) {
380 fprintf(stderr, "Could not write 0x%02x to "
381 "EXT_CSD[%d] in %s\n",
382 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
383 exit(1);
384 }
385 close(fd);
386 return ret;
387}
388
Chris Ballf74dfe22012-10-19 16:49:55 -0400389int do_hwreset(int value, int nargs, char **argv)
390{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800391 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400392 int fd, ret;
393 char *device;
394
395 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
396 exit(1));
397
398 device = argv[1];
399
400 fd = open(device, O_RDWR);
401 if (fd < 0) {
402 perror("open");
403 exit(1);
404 }
405
406 ret = read_extcsd(fd, ext_csd);
407 if (ret) {
408 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
409 exit(1);
410 }
411
412 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
413 EXT_CSD_HW_RESET_EN) {
414 fprintf(stderr,
415 "H/W Reset is already permanently enabled on %s\n",
416 device);
417 exit(1);
418 }
419 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
420 EXT_CSD_HW_RESET_DIS) {
421 fprintf(stderr,
422 "H/W Reset is already permanently disabled on %s\n",
423 device);
424 exit(1);
425 }
426
427 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
428 if (ret) {
429 fprintf(stderr,
430 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
431 value, EXT_CSD_RST_N_FUNCTION, device);
432 exit(1);
433 }
434
435 return ret;
436}
437
438int do_hwreset_en(int nargs, char **argv)
439{
440 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
441}
442
443int do_hwreset_dis(int nargs, char **argv)
444{
445 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
446}
447
Jaehoon Chung86496512012-09-21 10:08:05 +0000448int do_write_bkops_en(int nargs, char **argv)
449{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800450 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000451 int fd, ret;
452 char *device;
453
454 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
455 exit(1));
456
457 device = argv[1];
458
459 fd = open(device, O_RDWR);
460 if (fd < 0) {
461 perror("open");
462 exit(1);
463 }
464
465 ret = read_extcsd(fd, ext_csd);
466 if (ret) {
467 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
468 exit(1);
469 }
470
471 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
472 fprintf(stderr, "%s doesn't support BKOPS\n", device);
473 exit(1);
474 }
475
476 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
477 if (ret) {
478 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
479 value, EXT_CSD_BKOPS_EN, device);
480 exit(1);
481 }
482
483 return ret;
484}
485
Ben Gardiner27c357d2013-05-30 17:12:47 -0400486int do_status_get(int nargs, char **argv)
487{
488 __u32 response;
489 int fd, ret;
490 char *device;
491
492 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
493 exit(1));
494
495 device = argv[1];
496
497 fd = open(device, O_RDWR);
498 if (fd < 0) {
499 perror("open");
500 exit(1);
501 }
502
503 ret = send_status(fd, &response);
504 if (ret) {
505 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
506 exit(1);
507 }
508
509 printf("SEND_STATUS response: 0x%08x\n", response);
510
511 return ret;
512}
513
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800514__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
515{
516 return (ext_csd_loc[3] << 24) |
517 (ext_csd_loc[2] << 16) |
518 (ext_csd_loc[1] << 8) |
519 ext_csd_loc[0];
520}
521
Ben Gardiner4e850232013-05-30 17:12:49 -0400522unsigned int get_sector_count(__u8 *ext_csd)
523{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800524 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400525}
526
527int is_blockaddresed(__u8 *ext_csd)
528{
529 unsigned int sectors = get_sector_count(ext_csd);
530
531 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
532}
533
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400534unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
535{
536 return ext_csd[221];
537}
538
539unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
540{
541 return ext_csd[224];
542}
543
Ben Gardinere6e84e92013-09-19 11:14:27 -0400544int set_partitioning_setting_completed(int dry_run, const char * const device,
545 int fd)
546{
547 int ret;
548
549 if (dry_run) {
550 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
551 fprintf(stderr, "These changes will not take effect neither "
552 "now nor after a power cycle\n");
553 return 1;
554 }
555
556 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
557 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
558 if (ret) {
559 fprintf(stderr, "Could not write 0x1 to "
560 "EXT_CSD[%d] in %s\n",
561 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
562 return 1;
563 }
564
565 __u32 response;
566 ret = send_status(fd, &response);
567 if (ret) {
568 fprintf(stderr, "Could not get response to SEND_STATUS "
569 "from %s\n", device);
570 return 1;
571 }
572
573 if (response & R1_SWITCH_ERROR) {
574 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
575 "failed on %s\n", device);
576 return 1;
577 }
578
579 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
580 "%s SUCCESS\n", device);
581 fprintf(stderr, "Device power cycle needed for settings to "
582 "take effect.\n"
583 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
584 "using 'extcsd read' after power cycle\n");
585
586 return 0;
587}
588
Balaji T K1fdb7f92015-04-29 18:12:32 -0400589int check_enhanced_area_total_limit(const char * const device, int fd)
590{
591 __u8 ext_csd[512];
592 __u32 regl;
593 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
594 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400595 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400596 unsigned int wp_sz, erase_sz;
597 int ret;
598
599 ret = read_extcsd(fd, ext_csd);
600 if (ret) {
601 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
602 exit(1);
603 }
604 wp_sz = get_hc_wp_grp_size(ext_csd);
605 erase_sz = get_hc_erase_grp_size(ext_csd);
606
607 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
608 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
609 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
610 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
611 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
612 enh_area_sz += gp4_part_sz;
613 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
614 printf(" i.e. %lu KiB\n", gp4_part_sz);
615 }
616
617 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
618 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
619 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
620 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
621 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
622 enh_area_sz += gp3_part_sz;
623 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
624 printf(" i.e. %lu KiB\n", gp3_part_sz);
625 }
626
627 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
628 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
629 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
630 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
631 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
632 enh_area_sz += gp2_part_sz;
633 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
634 printf(" i.e. %lu KiB\n", gp2_part_sz);
635 }
636
637 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
638 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
639 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
640 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
641 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
642 enh_area_sz += gp1_part_sz;
643 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
644 printf(" i.e. %lu KiB\n", gp1_part_sz);
645 }
646
647 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
648 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
649 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
650 user_area_sz = 512l * regl * erase_sz * wp_sz;
651 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
652 enh_area_sz += user_area_sz;
653 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
654 printf(" i.e. %lu KiB\n", user_area_sz);
655 }
656
657 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
658 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
659 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
660 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
661 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
662 printf(" i.e. %lu KiB\n", max_enh_area_sz);
663 if (enh_area_sz > max_enh_area_sz) {
664 fprintf(stderr,
665 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
666 enh_area_sz, max_enh_area_sz, device);
667 return 1;
668 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400669 total_sz = get_sector_count(ext_csd) / 2;
670 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
671 gp1_part_sz + user_area_sz;
672 if (total_gp_user_sz > total_sz) {
673 fprintf(stderr,
674 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
675 total_gp_user_sz, total_sz, device);
676 return 1;
677 }
678
679 return 0;
680}
681
682int do_create_gp_partition(int nargs, char **argv)
683{
684 __u8 value;
685 __u8 ext_csd[512];
686 __u8 address;
687 int fd, ret;
688 char *device;
689 int dry_run = 1;
690 int partition, enh_attr, ext_attr;
691 unsigned int length_kib, gp_size_mult;
692 unsigned long align;
693
694 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n> <length KiB> "
695 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
696
697 if (!strcmp("-y", argv[1]))
698 dry_run = 0;
699
700 length_kib = strtol(argv[2], NULL, 10);
701 partition = strtol(argv[3], NULL, 10);
702 enh_attr = strtol(argv[4], NULL, 10);
703 ext_attr = strtol(argv[5], NULL, 10);
704 device = argv[6];
705
Marcus Folkessoncb04fde2015-11-18 15:06:16 -0500706 if (partition < 1 || partition > 4) {
707 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -0400708 exit(1);
709 }
710
711 if (enh_attr && ext_attr) {
712 printf("Not allowed to set both enhanced attribute and extended attribute\n");
713 exit(1);
714 }
715
716 fd = open(device, O_RDWR);
717 if (fd < 0) {
718 perror("open");
719 exit(1);
720 }
721
722 ret = read_extcsd(fd, ext_csd);
723 if (ret) {
724 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
725 exit(1);
726 }
727
728 /* assert not PARTITION_SETTING_COMPLETED */
729 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
730 printf(" Device is already partitioned\n");
731 exit(1);
732 }
733
734 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
735 gp_size_mult = (length_kib + align/2l) / align;
736
737 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
738 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
739 if (ret) {
740 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
741 EXT_CSD_ERASE_GROUP_DEF, device);
742 exit(1);
743 }
744
745 value = (gp_size_mult >> 16) & 0xff;
746 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
747 ret = write_extcsd_value(fd, address, value);
748 if (ret) {
749 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
750 value, address, device);
751 exit(1);
752 }
753 value = (gp_size_mult >> 8) & 0xff;
754 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
755 ret = write_extcsd_value(fd, address, value);
756 if (ret) {
757 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
758 value, address, device);
759 exit(1);
760 }
761 value = gp_size_mult & 0xff;
762 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
763 ret = write_extcsd_value(fd, address, value);
764 if (ret) {
765 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
766 value, address, device);
767 exit(1);
768 }
769
770 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
771 if (enh_attr)
772 value |= (1 << partition);
773 else
774 value &= ~(1 << partition);
775
776 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
777 if (ret) {
778 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
779 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
780 exit(1);
781 }
782
783 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
784 value = ext_csd[address];
785 if (ext_attr)
786 value |= (ext_attr << (4 * ((partition - 1) % 2)));
787 else
788 value &= (0xF << (4 * ((partition % 2))));
789
790 ret = write_extcsd_value(fd, address, value);
791 if (ret) {
792 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
793 value, address, device);
794 exit(1);
795 }
796
797 ret = check_enhanced_area_total_limit(device, fd);
798 if (ret)
799 exit(1);
800
801 if (!set_partitioning_setting_completed(dry_run, device, fd))
802 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -0400803
804 return 0;
805}
806
Ben Gardinerd91d3692013-05-30 17:12:51 -0400807int do_enh_area_set(int nargs, char **argv)
808{
809 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800810 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400811 int fd, ret;
812 char *device;
813 int dry_run = 1;
814 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
815 unsigned long align;
816
817 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
818 "</path/to/mmcblkX>\n", exit(1));
819
820 if (!strcmp("-y", argv[1]))
821 dry_run = 0;
822
823 start_kib = strtol(argv[2], NULL, 10);
824 length_kib = strtol(argv[3], NULL, 10);
825 device = argv[4];
826
827 fd = open(device, O_RDWR);
828 if (fd < 0) {
829 perror("open");
830 exit(1);
831 }
832
833 ret = read_extcsd(fd, ext_csd);
834 if (ret) {
835 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
836 exit(1);
837 }
838
839 /* assert ENH_ATTRIBUTE_EN */
840 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
841 {
842 printf(" Device cannot have enhanced tech.\n");
843 exit(1);
844 }
845
846 /* assert not PARTITION_SETTING_COMPLETED */
847 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
848 {
849 printf(" Device is already partitioned\n");
850 exit(1);
851 }
852
853 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
854
855 enh_size_mult = (length_kib + align/2l) / align;
856
857 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
858 enh_start_addr /= align;
859 enh_start_addr *= align;
860
861 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
862 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
863 if (ret) {
864 fprintf(stderr, "Could not write 0x1 to "
865 "EXT_CSD[%d] in %s\n",
866 EXT_CSD_ERASE_GROUP_DEF, device);
867 exit(1);
868 }
869
870 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
871 value = (enh_start_addr >> 24) & 0xff;
872 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
873 if (ret) {
874 fprintf(stderr, "Could not write 0x%02x to "
875 "EXT_CSD[%d] in %s\n", value,
876 EXT_CSD_ENH_START_ADDR_3, device);
877 exit(1);
878 }
879 value = (enh_start_addr >> 16) & 0xff;
880 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
881 if (ret) {
882 fprintf(stderr, "Could not write 0x%02x to "
883 "EXT_CSD[%d] in %s\n", value,
884 EXT_CSD_ENH_START_ADDR_2, device);
885 exit(1);
886 }
887 value = (enh_start_addr >> 8) & 0xff;
888 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
889 if (ret) {
890 fprintf(stderr, "Could not write 0x%02x to "
891 "EXT_CSD[%d] in %s\n", value,
892 EXT_CSD_ENH_START_ADDR_1, device);
893 exit(1);
894 }
895 value = enh_start_addr & 0xff;
896 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
897 if (ret) {
898 fprintf(stderr, "Could not write 0x%02x to "
899 "EXT_CSD[%d] in %s\n", value,
900 EXT_CSD_ENH_START_ADDR_0, device);
901 exit(1);
902 }
903
904 value = (enh_size_mult >> 16) & 0xff;
905 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
906 if (ret) {
907 fprintf(stderr, "Could not write 0x%02x to "
908 "EXT_CSD[%d] in %s\n", value,
909 EXT_CSD_ENH_SIZE_MULT_2, device);
910 exit(1);
911 }
912 value = (enh_size_mult >> 8) & 0xff;
913 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
914 if (ret) {
915 fprintf(stderr, "Could not write 0x%02x to "
916 "EXT_CSD[%d] in %s\n", value,
917 EXT_CSD_ENH_SIZE_MULT_1, device);
918 exit(1);
919 }
920 value = enh_size_mult & 0xff;
921 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
922 if (ret) {
923 fprintf(stderr, "Could not write 0x%02x to "
924 "EXT_CSD[%d] in %s\n", value,
925 EXT_CSD_ENH_SIZE_MULT_0, device);
926 exit(1);
927 }
Balaji T K1fdb7f92015-04-29 18:12:32 -0400928 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
929 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400930 if (ret) {
931 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
932 "EXT_CSD[%d] in %s\n",
933 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
934 exit(1);
935 }
936
Balaji T K1fdb7f92015-04-29 18:12:32 -0400937 ret = check_enhanced_area_total_limit(device, fd);
938 if (ret)
939 exit(1);
940
Ben Gardinere6e84e92013-09-19 11:14:27 -0400941 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400942
Ben Gardinere6e84e92013-09-19 11:14:27 -0400943 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400944 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400945
946 return 0;
947}
948
Ben Gardiner196d0d22013-09-19 11:14:29 -0400949int do_write_reliability_set(int nargs, char **argv)
950{
951 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800952 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400953 int fd, ret;
954
955 int dry_run = 1;
956 int partition;
957 char *device;
958
959 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
960 "<partition> </path/to/mmcblkX>\n", exit(1));
961
962 if (!strcmp("-y", argv[1]))
963 dry_run = 0;
964
965 partition = strtol(argv[2], NULL, 10);
966 device = argv[3];
967
968 fd = open(device, O_RDWR);
969 if (fd < 0) {
970 perror("open");
971 exit(1);
972 }
973
974 ret = read_extcsd(fd, ext_csd);
975 if (ret) {
976 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
977 exit(1);
978 }
979
980 /* assert not PARTITION_SETTING_COMPLETED */
981 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
982 {
983 printf(" Device is already partitioned\n");
984 exit(1);
985 }
986
987 /* assert HS_CTRL_REL */
988 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
989 printf("Cannot set write reliability parameters, WR_REL_SET is "
990 "read-only\n");
991 exit(1);
992 }
993
994 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
995 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
996 if (ret) {
997 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
998 value, EXT_CSD_WR_REL_SET, device);
999 exit(1);
1000 }
1001
1002 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1003 value, device);
1004
1005 if (!set_partitioning_setting_completed(dry_run, device, fd))
1006 exit(1);
1007
1008 return 0;
1009}
1010
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001011int do_read_extcsd(int nargs, char **argv)
1012{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001013 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001014 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001015 int fd, ret;
1016 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001017 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001018 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001019 "4.0", /* 0 */
1020 "4.1", /* 1 */
1021 "4.2", /* 2 */
1022 "4.3", /* 3 */
1023 "Obsolete", /* 4 */
1024 "4.41", /* 5 */
1025 "4.5", /* 6 */
1026 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001027 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001028 };
1029 int boot_access;
1030 const char* boot_access_str[] = {
1031 "No access to boot partition", /* 0 */
1032 "R/W Boot Partition 1", /* 1 */
1033 "R/W Boot Partition 2", /* 2 */
1034 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1035 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001036
Chris Ball8ba44662012-04-19 13:22:54 -04001037 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
1038 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001039
1040 device = argv[1];
1041
1042 fd = open(device, O_RDWR);
1043 if (fd < 0) {
1044 perror("open");
1045 exit(1);
1046 }
1047
1048 ret = read_extcsd(fd, ext_csd);
1049 if (ret) {
1050 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1051 exit(1);
1052 }
1053
Al Cooper786418c2015-04-29 18:12:35 -04001054 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001055
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001056 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1057 (ext_csd_rev != 4))
1058 str = ver_str[ext_csd_rev];
1059 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001060 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001061
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001062 printf("=============================================\n");
1063 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1064 printf("=============================================\n\n");
1065
1066 if (ext_csd_rev < 3)
1067 goto out_free; /* No ext_csd */
1068
1069 /* Parse the Extended CSD registers.
1070 * Reserved bit should be read as "0" in case of spec older
1071 * than A441.
1072 */
1073 reg = ext_csd[EXT_CSD_S_CMD_SET];
1074 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1075 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001076 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001077
1078 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1079 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1080 if (reg & EXT_CSD_HPI_SUPP) {
1081 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001082 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001083 else
1084 printf("implementation based on CMD13\n");
1085 }
1086
1087 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1088 ext_csd[502]);
1089
1090 if (ext_csd_rev >= 6) {
1091 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1092 ext_csd[501]);
1093 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1094 ext_csd[500]);
1095 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1096 ext_csd[499]);
1097
1098 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1099 ext_csd[498]);
1100 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1101 ext_csd[497]);
1102 printf("Context Management Capabilities"
1103 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1104 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1105 ext_csd[495]);
1106 printf("Extended partition attribute support"
1107 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001108 }
1109 if (ext_csd_rev >= 7) {
1110 int j;
1111 int eol_info;
1112 char* eol_info_str[] = {
1113 "Not Defined", /* 0 */
1114 "Normal", /* 1 */
1115 "Warning", /* 2 */
1116 "Urgent", /* 3 */
1117 };
1118
1119 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1120 ext_csd[493]);
1121 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1122 ext_csd[492]);
1123 printf("Operation codes timeout"
1124 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1125 ext_csd[491]);
1126 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1127 get_word_from_ext_csd(&ext_csd[487]));
1128 printf("Number of FW sectors correctly programmed"
1129 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1130 get_word_from_ext_csd(&ext_csd[302]));
1131 printf("Vendor proprietary health report:\n");
1132 for (j = 301; j >= 270; j--)
1133 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1134 " 0x%02x\n", j, ext_csd[j]);
1135 for (j = 269; j >= 268; j--) {
1136 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001137 char est_type = 'B' + (j - 269);
1138 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001139 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001140 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001141 if (life_used >= 0x1 && life_used <= 0xa)
1142 printf(" i.e. %d%% - %d%% device life time"
1143 " used\n",
1144 (life_used - 1) * 10, life_used * 10);
1145 else if (life_used == 0xb)
1146 printf(" i.e. Exceeded its maximum estimated"
1147 " device life time\n");
1148 }
1149 eol_info = ext_csd[267];
1150 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1151 eol_info);
1152 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1153 printf(" i.e. %s\n", eol_info_str[eol_info]);
1154 else
1155 printf(" i.e. Reserved\n");
1156
1157 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1158 ext_csd[266]);
1159 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1160 ext_csd[265]);
1161 printf("Optimal trim unit size"
1162 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1163 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1164 ext_csd[263], ext_csd[262]);
1165 printf("Firmware version:\n");
1166 for (j = 261; j >= 254; j--)
1167 printf("[FIRMWARE_VERSION[%d]]:"
1168 " 0x%02x\n", j, ext_csd[j]);
1169
1170 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1171 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1172 }
1173 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001174 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1175 ext_csd[248]);
1176 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1177 ext_csd[247]);
1178 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001179 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001180 }
1181
1182 /* A441: Reserved [501:247]
1183 A43: reserved [246:229] */
1184 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001185 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001186 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001187
1188 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1189
1190 printf("1st Initialisation Time after programmed sector"
1191 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1192
1193 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001194 printf("Power class for 52MHz, DDR at 3.6V"
1195 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1196 printf("Power class for 52MHz, DDR at 1.95V"
1197 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1198
1199 /* A441: reserved [237-236] */
1200
1201 if (ext_csd_rev >= 6) {
1202 printf("Power class for 200MHz at 3.6V"
1203 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1204 printf("Power class for 200MHz, at 1.95V"
1205 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1206 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001207 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001208 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1209 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1210 /* A441: reserved [233] */
1211 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1212 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1213 ext_csd[231]);
1214 }
1215 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1216 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1217 ext_csd[230]);
1218 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1219 ext_csd[229]);
1220 }
1221 reg = ext_csd[EXT_CSD_BOOT_INFO];
1222 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1223 if (reg & EXT_CSD_BOOT_INFO_ALT)
1224 printf(" Device supports alternative boot method\n");
1225 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1226 printf(" Device supports dual data rate during boot\n");
1227 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1228 printf(" Device supports high speed timing during boot\n");
1229
1230 /* A441/A43: reserved [227] */
1231 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1232 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001233
1234 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001235 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001236 reg);
1237 printf(" i.e. %u KiB\n", 512 * reg);
1238
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001239 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1240 ext_csd[223]);
1241 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1242 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001243
1244 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001245 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001246 reg);
1247 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1248
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001249 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1250 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1251 /* A441/A43: reserved [218] */
1252 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1253 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001254
1255 unsigned int sectors = get_sector_count(ext_csd);
1256 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1257 if (is_blockaddresed(ext_csd))
1258 printf(" Device is block-addressed\n");
1259 else
1260 printf(" Device is NOT block-addressed\n");
1261
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001262 /* A441/A43: reserved [211] */
1263 printf("Minimum Write Performance for 8bit:\n");
1264 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1265 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1266 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1267 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1268 printf("Minimum Write Performance for 4bit:\n");
1269 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1270 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1271 /* A441/A43: reserved [204] */
1272 printf("Power classes registers:\n");
1273 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1274 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1275 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1276 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1277
1278 /* A43: reserved [199:198] */
1279 if (ext_csd_rev >= 5) {
1280 printf("Partition switching timing "
1281 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1282 printf("Out-of-interrupt busy timing"
1283 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1284 }
1285
1286 /* A441/A43: reserved [197] [195] [193] [190] [188]
1287 * [186] [184] [182] [180] [176] */
1288
1289 if (ext_csd_rev >= 6)
1290 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1291 ext_csd[197]);
1292
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001293 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001294 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1295 ext_csd[196], ext_csd[195]);
1296 reg = ext_csd[195];
1297 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1298 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001299 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001300 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1301 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001302 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1303 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1304 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1305 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1306 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1307 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001308
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001309 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001310 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001311 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1312 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1313 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1314 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1315 ext_csd[185]);
1316 /* bus_width: ext_csd[183] not readable */
1317 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1318 ext_csd[181]);
1319 reg = ext_csd[EXT_CSD_BOOT_CFG];
1320 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001321 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001322 case 0x0:
1323 printf(" Not boot enable\n");
1324 break;
1325 case 0x1:
1326 printf(" Boot Partition 1 enabled\n");
1327 break;
1328 case 0x2:
1329 printf(" Boot Partition 2 enabled\n");
1330 break;
1331 case 0x7:
1332 printf(" User Area Enabled for boot\n");
1333 break;
1334 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001335 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1336 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1337 printf(" %s\n", boot_access_str[boot_access]);
1338 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001339 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001340 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001341
1342 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1343 ext_csd[178]);
1344 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1345 ext_csd[177]);
1346 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001347 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001348
Chris Ballb9c7a172012-02-20 12:34:25 -05001349 print_writeprotect_status(ext_csd);
1350
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001351 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001352 /* A441]: reserved [172] */
1353 printf("User area write protection register"
1354 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1355 /* A441]: reserved [170] */
1356 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1357 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001358
1359 reg = ext_csd[EXT_CSD_WR_REL_SET];
1360 const char * const fast = "existing data is at risk if a power "
1361 "failure occurs during a write operation";
1362 const char * const reliable = "the device protects existing "
1363 "data if a power failure occurs during a write "
1364 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001365 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001366 " [WR_REL_SET]: 0x%02x\n", reg);
1367
1368 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1369 int i;
1370 for (i = 1; i <= 4; i++) {
1371 printf(" partition %d: %s\n", i,
1372 reg & (1<<i) ? reliable : fast);
1373 }
1374
1375 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001376 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001377 " [WR_REL_PARAM]: 0x%02x\n", reg);
1378 if (reg & 0x01)
1379 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1380 if (reg & 0x04)
1381 printf(" Device supports the enhanced def. of reliable "
1382 "write\n");
1383
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001384 /* sanitize_start ext_csd[165]]: not readable
1385 * bkops_start ext_csd[164]]: only writable */
1386 printf("Enable background operations handshake"
1387 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1388 printf("H/W reset function"
1389 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1390 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001391 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001392 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1393 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001394 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001395 printf(" Device support partitioning feature\n");
1396 else
1397 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001398 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001399 printf(" Device can have enhanced tech.\n");
1400 else
1401 printf(" Device cannot have enhanced tech.\n");
1402
Oliver Metz11f2cea2013-09-23 08:40:52 +02001403 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001404 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1405 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1406
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001407 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001408 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001409 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1410 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001411 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001412
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001413 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001414 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001415 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001416 printf("Partitioning Setting"
1417 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001418 reg);
1419 if (reg)
1420 printf(" Device partition setting complete\n");
1421 else
1422 printf(" Device partition setting NOT complete\n");
1423
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001424 printf("General Purpose Partition Size\n"
1425 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1426 (ext_csd[153] << 8) | ext_csd[152]);
1427 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1428 (ext_csd[150] << 8) | ext_csd[149]);
1429 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1430 (ext_csd[147] << 8) | ext_csd[146]);
1431 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1432 (ext_csd[144] << 8) | ext_csd[143]);
1433
Oliver Metz11f2cea2013-09-23 08:40:52 +02001434 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001435 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1436 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001437 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001438 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1439 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001440 get_hc_erase_grp_size(ext_csd) *
1441 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001442
Oliver Metz11f2cea2013-09-23 08:40:52 +02001443 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001444 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1445 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1446 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001447 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001448 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001449 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001450 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001451
1452 /* A441]: reserved [135] */
1453 printf("Bad Block Management mode"
1454 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1455 /* A441: reserved [133:0] */
1456 }
1457 /* B45 */
1458 if (ext_csd_rev >= 6) {
1459 int j;
1460 /* tcase_support ext_csd[132] not readable */
1461 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1462 ext_csd[131]);
1463 printf("Program CID/CSD in DDR mode support"
1464 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1465 ext_csd[130]);
1466
1467 for (j = 127; j >= 64; j--)
1468 printf("Vendor Specific Fields"
1469 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1470 j, ext_csd[j]);
1471
Gwendal Grignoue966e672014-07-07 14:03:13 -07001472 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001473 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001474 reg);
1475 if (reg == 0x00)
1476 printf(" i.e. 512 B\n");
1477 else if (reg == 0x01)
1478 printf(" i.e. 4 KiB\n");
1479 else
1480 printf(" i.e. Reserved\n");
1481
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001482 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1483 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001484 reg = ext_csd[61];
1485 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1486 if (reg == 0x00)
1487 printf(" i.e. 512 B\n");
1488 else if (reg == 0x01)
1489 printf(" i.e. 4 KiB\n");
1490 else
1491 printf(" i.e. Reserved\n");
1492
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001493 printf("1st initialization after disabling sector"
1494 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1495 ext_csd[60]);
1496 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1497 ext_csd[59]);
1498 printf("Number of addressed group to be Released"
1499 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1500 printf("Exception events control"
1501 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1502 (ext_csd[57] << 8) | ext_csd[56]);
1503 printf("Exception events status"
1504 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1505 (ext_csd[55] << 8) | ext_csd[54]);
1506 printf("Extended Partitions Attribute"
1507 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1508 (ext_csd[53] << 8) | ext_csd[52]);
1509
1510 for (j = 51; j >= 37; j--)
1511 printf("Context configuration"
1512 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1513
1514 printf("Packed command status"
1515 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1516 printf("Packed command failure index"
1517 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1518 printf("Power Off Notification"
1519 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001520 printf("Control to turn the Cache ON/OFF"
1521 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001522 /* flush_cache ext_csd[32] not readable */
1523 /*Reserved [31:0] */
1524 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001525 if (ext_csd_rev >= 7) {
1526 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1527 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1528 ext_csd[29]);
1529
1530 reg = ext_csd[26];
1531 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1532 switch (reg) {
1533 case 0x00:
1534 printf(" Success\n");
1535 break;
1536 case 0x10:
1537 printf(" General error\n");
1538 break;
1539 case 0x11:
1540 printf(" Firmware install error\n");
1541 break;
1542 case 0x12:
1543 printf(" Error in downloading firmware\n");
1544 break;
1545 default:
1546 printf(" Reserved\n");
1547 }
1548 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1549 " %d sector size\n",
1550 get_word_from_ext_csd(&ext_csd[22]));
1551 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1552 " %d sector size\n",
1553 get_word_from_ext_csd(&ext_csd[18]));
1554 printf("Product state awareness enablement"
1555 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1556 ext_csd[17]);
1557 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1558 ext_csd[16]);
1559 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001560
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05001561 if (ext_csd_rev >= 7) {
1562 printf("eMMC Firmware Version: %s\n",
1563 (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]);
1564 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001565out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001566 return ret;
1567}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001568
Nick Sanders9d57aa72014-03-05 21:38:54 -08001569int do_dump_extcsd(int nargs, char **argv)
1570{
1571 __u8 ext_csd[EXT_CSD_SIZE];
1572 int fd, ret;
1573 char *device;
1574 int i, j;
1575
1576 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1577 exit(1));
1578
1579 device = argv[1];
1580
1581 fd = open(device, O_RDWR);
1582 if (fd < 0) {
1583 perror("Failed to open mmc device");
1584 exit(1);
1585 }
1586
1587 ret = read_extcsd(fd, ext_csd);
1588 if (ret) {
1589 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1590 exit(1);
1591 }
1592
1593 /* Dump all bytes so that any undecoded or proprietary registers */
1594 /* can be acessed. */
1595 printf("EXT_CSD binary dump:\n");
1596 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1597 printf(" %3d: %3x: ", i, i);
1598 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1599 printf(" %02x", ext_csd[i+j]);
1600 }
1601 printf("\n");
1602 }
1603
1604 return ret;
1605}
1606
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001607int do_sanitize(int nargs, char **argv)
1608{
1609 int fd, ret;
1610 char *device;
1611
1612 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1613 exit(1));
1614
1615 device = argv[1];
1616
1617 fd = open(device, O_RDWR);
1618 if (fd < 0) {
1619 perror("open");
1620 exit(1);
1621 }
1622
1623 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1624 if (ret) {
1625 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1626 1, EXT_CSD_SANITIZE_START, device);
1627 exit(1);
1628 }
1629
1630 return ret;
1631
1632}
1633
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07001634enum blockprotect_mode {
1635 BLOCKPROTECT_TEMPORARY = 0,
1636 BLOCKPROTECT_POWERON,
1637 BLOCKPROTECT_PERMANENT,
1638};
1639
1640int write_blockprotect(int fd, __u32 sector, int enable)
1641{
1642 struct mmc_ioc_cmd cmd;
1643 int ret;
1644
1645 memset(&cmd, 0, sizeof(cmd));
1646 cmd.write_flag = 1;
1647 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
1648 cmd.arg = sector;
1649 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
1650
1651 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
1652 if (ret)
1653 perror("SET/CLR_WRITE_PROT command");
1654 return ret;
1655}
1656
1657int do_blockprotect_enable(int nargs, char **argv)
1658{
1659 __u8 ext_csd[EXT_CSD_SIZE];
1660 __u8 user_wp;
1661 __u32 sector;
1662 char *end;
1663 int ret, fd;
1664 int arg_index = 0;
1665 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
1666
1667 if (nargs > 0 && !strcmp(argv[1], "-r")) {
1668 arg_index++;
1669 mode = BLOCKPROTECT_POWERON;
1670 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
1671 arg_index++;
1672 mode = BLOCKPROTECT_PERMANENT;
1673 }
1674
1675 CHECK(nargs != 3 + arg_index, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n", exit(1));
1676
1677 sector = strtoul(argv[2 + arg_index], &end, 0);
1678 if (*end != '\0') {
1679 fprintf(stderr, "Not a block number: %s\n",
1680 argv[2 + arg_index]);
1681 exit(1);
1682 }
1683
1684 fd = open(argv[1 + arg_index], O_RDWR);
1685 if (fd < 0) {
1686 perror("open");
1687 exit(1);
1688 }
1689
1690 if (read_extcsd(fd, ext_csd))
1691 exit(1);
1692
1693 user_wp = ext_csd[EXT_CSD_USER_WP];
1694 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
1695 if (mode == BLOCKPROTECT_POWERON)
1696 user_wp |= EXT_CSD_US_PWR_WP_EN;
1697 else if (mode == BLOCKPROTECT_PERMANENT)
1698 user_wp |= EXT_CSD_US_PERM_WP_EN;
1699
1700 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
1701 if (ret) {
1702 perror("update EXT_CSD[USER_WP]");
1703 exit(1);
1704 }
1705
1706 usleep(INTER_COMMAND_GAP_US);
1707
1708 ret = write_blockprotect(fd, sector, 1);
1709
1710 usleep(INTER_COMMAND_GAP_US);
1711
1712 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
1713 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
1714 perror("reset EXT_CSD[USER_WP]");
1715 if (!ret)
1716 ret = -1;
1717 }
1718
1719 return ret;
1720}
1721
1722int do_blockprotect_disable(int nargs, char **argv)
1723{
1724 __u32 sector;
1725 char *end;
1726 int fd;
1727
1728 CHECK(nargs != 3, "Usage: mmc blockprotect disable <device> <write protect block>\n", exit(1));
1729
1730 sector = strtoul(argv[2], &end, 0);
1731 if (*end != '\0') {
1732 fprintf(stderr, "Not a block number: %s\n", argv[2]);
1733 exit(1);
1734 }
1735
1736
1737 fd = open(argv[1], O_RDWR);
1738 if (fd < 0) {
1739 perror("open");
1740 exit(1);
1741 }
1742
1743 return write_blockprotect(fd, sector, 0);
1744}
1745
1746int do_blockprotect_read(int nargs, char **argv)
1747{
1748 __u8 wp_bits[8];
1749 __u32 sector;
1750 char *end;
1751 int fd;
1752 struct mmc_ioc_cmd cmd;
1753
1754 CHECK(nargs != 3, "Usage: mmc blockprotect read <device> <write protect block>\n", exit(1));
1755
1756 fd = open(argv[1], O_RDWR);
1757 if (fd < 0) {
1758 perror("open");
1759 exit(1);
1760 }
1761
1762 sector = strtoul(argv[2], &end, 0);
1763 if (*end != '\0') {
1764 fprintf(stderr, "Not a block number: %s\n", argv[2]);
1765 exit(1);
1766 }
1767
1768 memset(&cmd, 0, sizeof(cmd));
1769 cmd.write_flag = 0;
1770 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
1771 cmd.arg = sector;
1772 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1773 cmd.blksz = sizeof(wp_bits);
1774 cmd.blocks = 1;
1775 mmc_ioc_cmd_set_data(cmd, wp_bits);
1776
1777 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
1778 perror("SEND_WRITE_PROT_TYPE command");
1779 exit(1);
1780 }
1781
1782 printf("Sector %u write protection: ", sector);
1783 switch (wp_bits[7] & 3) {
1784 case 0:
1785 printf("NONE\n");
1786 break;
1787 case 1:
1788 printf("TEMPORARY\n");
1789 break;
1790 case 2:
1791 printf("POWER-ON\n");
1792 break;
1793 case 3:
1794 printf("PERMANENT\n");
1795 break;
1796 }
1797
1798 return 0;
1799}
1800
1801int do_blockprotect_info(int nargs, char **argv)
1802{
1803 __u8 ext_csd[EXT_CSD_SIZE];
1804 __u8 user_wp;
1805 int fd, wp_sz, erase_sz;
1806
1807 CHECK(nargs != 2, "Usage: mmc blockprotect info <device>\n", exit(1));
1808
1809 fd = open(argv[1], O_RDWR);
1810 if (fd < 0) {
1811 perror("open");
1812 exit(1);
1813 }
1814
1815 if (read_extcsd(fd, ext_csd))
1816 exit(1);
1817
1818 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
1819 fprintf(stderr, "Block protection commands not supported: "
1820 "CLASS_6_CTRL set.\n");
1821 exit(1);
1822 }
1823
1824 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
1825 fprintf(stderr, "Block protection commands not supported: "
1826 "high-capacity sizes not enabled.\n");
1827 exit(1);
1828 }
1829
1830 wp_sz = get_hc_wp_grp_size(ext_csd);
1831 erase_sz = get_hc_erase_grp_size(ext_csd);
1832
1833 if (erase_sz == 0 || wp_sz == 0) {
1834 fprintf(stderr, "Block protection commands not supported: "
1835 "no high-capacity size for erase or WP blocks.\n");
1836 exit(1);
1837 }
1838
1839 printf("Write protect block size in sectors: %d\n",
1840 erase_sz * wp_sz * 1024);
1841
1842 user_wp = ext_csd[EXT_CSD_USER_WP];
1843 printf("Permanent write protection: %s\n",
1844 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
1845 printf("Power-on write protection: %s\n",
1846 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
1847
1848 return 0;
1849}
1850
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001851static const char* const mmc_ffu_hack_names[] = {
1852 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1853};
1854
Gwendal Grignou771984c2014-07-01 12:46:18 -07001855int do_emmc50_ffu (int nargs, char **argv)
1856{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001857 int fd, ret, i, argc=1, ffu_hack=0;
1858 char *device, *type, *path;
1859 __u64 value;
1860 union {
1861 __u8 data[FFU_DATA_SIZE];
1862 struct mmc_ffu_args ffu_args;
1863 } ffu_data;
1864 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001865 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001866
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001867 while (!strcmp("-k", argv[argc])) {
1868 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1869 if (ret < 1) {
1870 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1871 exit(1);
1872 }
1873 for (i = 0; i < MMC_HACK_LEN; i++) {
1874 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1875 ffu_args->hack[ffu_hack].type = i;
1876 if (ret == 2) {
1877 ffu_args->hack[ffu_hack].value = value;
1878 }
1879 ffu_hack++;
1880 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1881 sizeof(struct mmc_ffu_args) >
1882 FFU_DATA_SIZE) {
1883 fprintf(stderr, "Too many %d hacks",
1884 ffu_hack);
1885 exit(1);
1886 }
1887 break;
1888 }
1889 }
1890 if (i == MMC_HACK_LEN) {
1891 fprintf(stderr, "Hack type %s not found\n", type);
1892 fprintf(stderr, "Supported types are: ");
1893 for (i = 0; i < MMC_HACK_LEN; i++)
1894 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1895 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001896
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001897 exit(1);
1898 }
1899 free(type);
1900 argc++;
1901 }
1902 ffu_args->hack_nb = ffu_hack;
1903
1904 path = argv[argc++];
1905 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001906 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1907 exit(1);
1908 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001909 strcpy(ffu_args->name, path);
1910 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001911 fd = open(device, O_RDWR);
1912 if (fd < 0) {
1913 perror("open");
1914 exit(1);
1915 }
1916
Gwendal Grignou0f757342014-10-16 16:52:46 -07001917 /* prepare and send ioctl */
1918 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1919 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001920 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001921 mmc_ioc_cmd.blocks = 1;
1922 mmc_ioc_cmd.arg = 0;
1923 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1924 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001925 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001926 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001927 if (ret) {
1928 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1929 exit(1);
1930 }
1931
1932 close(fd);
1933 return 0;
1934}
1935
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001936#define DO_IO(func, fd, buf, nbyte) \
1937 ({ \
1938 ssize_t ret = 0, r; \
1939 do { \
1940 r = func(fd, buf + ret, nbyte - ret); \
1941 if (r < 0 && errno != EINTR) { \
1942 ret = -1; \
1943 break; \
1944 } \
1945 else if (r > 0) \
1946 ret += r; \
1947 } while (r != 0 && (size_t)ret != nbyte); \
1948 \
1949 ret; \
1950 })
1951
1952enum rpmb_op_type {
1953 MMC_RPMB_WRITE_KEY = 0x01,
1954 MMC_RPMB_READ_CNT = 0x02,
1955 MMC_RPMB_WRITE = 0x03,
1956 MMC_RPMB_READ = 0x04,
1957
1958 /* For internal usage only, do not use it directly */
1959 MMC_RPMB_READ_RESP = 0x05
1960};
1961
1962struct rpmb_frame {
1963 u_int8_t stuff[196];
1964 u_int8_t key_mac[32];
1965 u_int8_t data[256];
1966 u_int8_t nonce[16];
1967 u_int32_t write_counter;
1968 u_int16_t addr;
1969 u_int16_t block_count;
1970 u_int16_t result;
1971 u_int16_t req_resp;
1972};
1973
1974/* Performs RPMB operation.
1975 *
1976 * @fd: RPMB device on which we should perform ioctl command
1977 * @frame_in: input RPMB frame, should be properly inited
1978 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1979 * result and req_resp for output frame.
1980 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1981 * in the other cases -EINVAL will be returned.
1982 */
1983static int do_rpmb_op(int fd,
1984 const struct rpmb_frame *frame_in,
1985 struct rpmb_frame *frame_out,
1986 unsigned int out_cnt)
1987{
1988 int err;
1989 u_int16_t rpmb_type;
1990
1991 struct mmc_ioc_cmd ioc = {
1992 .arg = 0x0,
1993 .blksz = 512,
1994 .blocks = 1,
1995 .write_flag = 1,
1996 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1997 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1998 .data_ptr = (uintptr_t)frame_in
1999 };
2000
2001 if (!frame_in || !frame_out || !out_cnt)
2002 return -EINVAL;
2003
2004 rpmb_type = be16toh(frame_in->req_resp);
2005
2006 switch(rpmb_type) {
2007 case MMC_RPMB_WRITE:
2008 case MMC_RPMB_WRITE_KEY:
2009 if (out_cnt != 1) {
2010 err = -EINVAL;
2011 goto out;
2012 }
2013
2014 /* Write request */
2015 ioc.write_flag |= (1<<31);
2016 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2017 if (err < 0) {
2018 err = -errno;
2019 goto out;
2020 }
2021
2022 /* Result request */
2023 memset(frame_out, 0, sizeof(*frame_out));
2024 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2025 ioc.write_flag = 1;
2026 ioc.data_ptr = (uintptr_t)frame_out;
2027 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2028 if (err < 0) {
2029 err = -errno;
2030 goto out;
2031 }
2032
2033 /* Get response */
2034 ioc.write_flag = 0;
2035 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2036 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2037 if (err < 0) {
2038 err = -errno;
2039 goto out;
2040 }
2041
2042 break;
2043 case MMC_RPMB_READ_CNT:
2044 if (out_cnt != 1) {
2045 err = -EINVAL;
2046 goto out;
2047 }
2048 /* fall through */
2049
2050 case MMC_RPMB_READ:
2051 /* Request */
2052 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2053 if (err < 0) {
2054 err = -errno;
2055 goto out;
2056 }
2057
2058 /* Get response */
2059 ioc.write_flag = 0;
2060 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2061 ioc.blocks = out_cnt;
2062 ioc.data_ptr = (uintptr_t)frame_out;
2063 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2064 if (err < 0) {
2065 err = -errno;
2066 goto out;
2067 }
2068
2069 break;
2070 default:
2071 err = -EINVAL;
2072 goto out;
2073 }
2074
2075out:
2076 return err;
2077}
2078
2079int do_rpmb_write_key(int nargs, char **argv)
2080{
2081 int ret, dev_fd, key_fd;
2082 struct rpmb_frame frame_in = {
2083 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2084 }, frame_out;
2085
2086 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
2087 exit(1));
2088
2089 dev_fd = open(argv[1], O_RDWR);
2090 if (dev_fd < 0) {
2091 perror("device open");
2092 exit(1);
2093 }
2094
2095 if (0 == strcmp(argv[2], "-"))
2096 key_fd = STDIN_FILENO;
2097 else {
2098 key_fd = open(argv[2], O_RDONLY);
2099 if (key_fd < 0) {
2100 perror("can't open key file");
2101 exit(1);
2102 }
2103 }
2104
2105 /* Read the auth key */
2106 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2107 if (ret < 0) {
2108 perror("read the key");
2109 exit(1);
2110 } else if (ret != sizeof(frame_in.key_mac)) {
2111 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2112 (unsigned long)sizeof(frame_in.key_mac),
2113 ret);
2114 exit(1);
2115 }
2116
2117 /* Execute RPMB op */
2118 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2119 if (ret != 0) {
2120 perror("RPMB ioctl failed");
2121 exit(1);
2122 }
2123
2124 /* Check RPMB response */
2125 if (frame_out.result != 0) {
2126 printf("RPMB operation failed, retcode 0x%04x\n",
2127 be16toh(frame_out.result));
2128 exit(1);
2129 }
2130
2131 close(dev_fd);
2132 if (key_fd != STDIN_FILENO)
2133 close(key_fd);
2134
2135 return ret;
2136}
2137
2138int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2139{
2140 int ret;
2141 struct rpmb_frame frame_in = {
2142 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2143 }, frame_out;
2144
2145 /* Execute RPMB op */
2146 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2147 if (ret != 0) {
2148 perror("RPMB ioctl failed");
2149 exit(1);
2150 }
2151
2152 /* Check RPMB response */
2153 if (frame_out.result != 0)
2154 return be16toh(frame_out.result);
2155
2156 *cnt = be32toh(frame_out.write_counter);
2157
2158 return 0;
2159}
2160
2161int do_rpmb_read_counter(int nargs, char **argv)
2162{
2163 int ret, dev_fd;
2164 unsigned int cnt;
2165
2166 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
2167 exit(1));
2168
2169 dev_fd = open(argv[1], O_RDWR);
2170 if (dev_fd < 0) {
2171 perror("device open");
2172 exit(1);
2173 }
2174
2175 ret = rpmb_read_counter(dev_fd, &cnt);
2176
2177 /* Check RPMB response */
2178 if (ret != 0) {
2179 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2180 exit(1);
2181 }
2182
2183 close(dev_fd);
2184
2185 printf("Counter value: 0x%08x\n", cnt);
2186
2187 return ret;
2188}
2189
2190int do_rpmb_read_block(int nargs, char **argv)
2191{
2192 int i, ret, dev_fd, data_fd, key_fd = -1;
2193 uint16_t addr, blocks_cnt;
2194 unsigned char key[32];
2195 struct rpmb_frame frame_in = {
2196 .req_resp = htobe16(MMC_RPMB_READ),
2197 }, *frame_out_p;
2198
2199 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
2200 exit(1));
2201
2202 dev_fd = open(argv[1], O_RDWR);
2203 if (dev_fd < 0) {
2204 perror("device open");
2205 exit(1);
2206 }
2207
2208 /* Get block address */
2209 errno = 0;
2210 addr = strtol(argv[2], NULL, 0);
2211 if (errno) {
2212 perror("incorrect address");
2213 exit(1);
2214 }
2215 frame_in.addr = htobe16(addr);
2216
2217 /* Get blocks count */
2218 errno = 0;
2219 blocks_cnt = strtol(argv[3], NULL, 0);
2220 if (errno) {
2221 perror("incorrect blocks count");
2222 exit(1);
2223 }
2224
2225 if (!blocks_cnt) {
2226 printf("please, specify valid blocks count number\n");
2227 exit(1);
2228 }
2229
2230 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2231 if (!frame_out_p) {
2232 printf("can't allocate memory for RPMB outer frames\n");
2233 exit(1);
2234 }
2235
2236 /* Write 256b data */
2237 if (0 == strcmp(argv[4], "-"))
2238 data_fd = STDOUT_FILENO;
2239 else {
2240 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2241 S_IRUSR | S_IWUSR);
2242 if (data_fd < 0) {
2243 perror("can't open output file");
2244 exit(1);
2245 }
2246 }
2247
2248 /* Key is specified */
2249 if (nargs == 6) {
2250 if (0 == strcmp(argv[5], "-"))
2251 key_fd = STDIN_FILENO;
2252 else {
2253 key_fd = open(argv[5], O_RDONLY);
2254 if (key_fd < 0) {
2255 perror("can't open input key file");
2256 exit(1);
2257 }
2258 }
2259
2260 ret = DO_IO(read, key_fd, key, sizeof(key));
2261 if (ret < 0) {
2262 perror("read the key data");
2263 exit(1);
2264 } else if (ret != sizeof(key)) {
2265 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2266 (unsigned long)sizeof(key),
2267 ret);
2268 exit(1);
2269 }
2270 }
2271
2272 /* Execute RPMB op */
2273 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2274 if (ret != 0) {
2275 perror("RPMB ioctl failed");
2276 exit(1);
2277 }
2278
2279 /* Check RPMB response */
2280 if (frame_out_p[blocks_cnt - 1].result != 0) {
2281 printf("RPMB operation failed, retcode 0x%04x\n",
2282 be16toh(frame_out_p[blocks_cnt - 1].result));
2283 exit(1);
2284 }
2285
2286 /* Do we have to verify data against key? */
2287 if (nargs == 6) {
2288 unsigned char mac[32];
2289 hmac_sha256_ctx ctx;
2290 struct rpmb_frame *frame_out = NULL;
2291
2292 hmac_sha256_init(&ctx, key, sizeof(key));
2293 for (i = 0; i < blocks_cnt; i++) {
2294 frame_out = &frame_out_p[i];
2295 hmac_sha256_update(&ctx, frame_out->data,
2296 sizeof(*frame_out) -
2297 offsetof(struct rpmb_frame, data));
2298 }
2299
2300 hmac_sha256_final(&ctx, mac, sizeof(mac));
2301
2302 /* Impossible */
2303 assert(frame_out);
2304
2305 /* Compare calculated MAC and MAC from last frame */
2306 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2307 printf("RPMB MAC missmatch\n");
2308 exit(1);
2309 }
2310 }
2311
2312 /* Write data */
2313 for (i = 0; i < blocks_cnt; i++) {
2314 struct rpmb_frame *frame_out = &frame_out_p[i];
2315 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2316 if (ret < 0) {
2317 perror("write the data");
2318 exit(1);
2319 } else if (ret != sizeof(frame_out->data)) {
2320 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2321 (unsigned long)sizeof(frame_out->data),
2322 ret);
2323 exit(1);
2324 }
2325 }
2326
2327 free(frame_out_p);
2328 close(dev_fd);
2329 if (data_fd != STDOUT_FILENO)
2330 close(data_fd);
2331 if (key_fd != -1 && key_fd != STDIN_FILENO)
2332 close(key_fd);
2333
2334 return ret;
2335}
2336
2337int do_rpmb_write_block(int nargs, char **argv)
2338{
2339 int ret, dev_fd, key_fd, data_fd;
2340 unsigned char key[32];
2341 uint16_t addr;
2342 unsigned int cnt;
2343 struct rpmb_frame frame_in = {
2344 .req_resp = htobe16(MMC_RPMB_WRITE),
2345 .block_count = htobe16(1)
2346 }, frame_out;
2347
2348 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
2349 exit(1));
2350
2351 dev_fd = open(argv[1], O_RDWR);
2352 if (dev_fd < 0) {
2353 perror("device open");
2354 exit(1);
2355 }
2356
2357 ret = rpmb_read_counter(dev_fd, &cnt);
2358 /* Check RPMB response */
2359 if (ret != 0) {
2360 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2361 exit(1);
2362 }
2363 frame_in.write_counter = htobe32(cnt);
2364
2365 /* Get block address */
2366 errno = 0;
2367 addr = strtol(argv[2], NULL, 0);
2368 if (errno) {
2369 perror("incorrect address");
2370 exit(1);
2371 }
2372 frame_in.addr = htobe16(addr);
2373
2374 /* Read 256b data */
2375 if (0 == strcmp(argv[3], "-"))
2376 data_fd = STDIN_FILENO;
2377 else {
2378 data_fd = open(argv[3], O_RDONLY);
2379 if (data_fd < 0) {
2380 perror("can't open input file");
2381 exit(1);
2382 }
2383 }
2384
2385 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2386 if (ret < 0) {
2387 perror("read the data");
2388 exit(1);
2389 } else if (ret != sizeof(frame_in.data)) {
2390 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2391 (unsigned long)sizeof(frame_in.data),
2392 ret);
2393 exit(1);
2394 }
2395
2396 /* Read the auth key */
2397 if (0 == strcmp(argv[4], "-"))
2398 key_fd = STDIN_FILENO;
2399 else {
2400 key_fd = open(argv[4], O_RDONLY);
2401 if (key_fd < 0) {
2402 perror("can't open key file");
2403 exit(1);
2404 }
2405 }
2406
2407 ret = DO_IO(read, key_fd, key, sizeof(key));
2408 if (ret < 0) {
2409 perror("read the key");
2410 exit(1);
2411 } else if (ret != sizeof(key)) {
2412 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2413 (unsigned long)sizeof(key),
2414 ret);
2415 exit(1);
2416 }
2417
2418 /* Calculate HMAC SHA256 */
2419 hmac_sha256(
2420 key, sizeof(key),
2421 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2422 frame_in.key_mac, sizeof(frame_in.key_mac));
2423
2424 /* Execute RPMB op */
2425 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2426 if (ret != 0) {
2427 perror("RPMB ioctl failed");
2428 exit(1);
2429 }
2430
2431 /* Check RPMB response */
2432 if (frame_out.result != 0) {
2433 printf("RPMB operation failed, retcode 0x%04x\n",
2434 be16toh(frame_out.result));
2435 exit(1);
2436 }
2437
2438 close(dev_fd);
2439 if (data_fd != STDIN_FILENO)
2440 close(data_fd);
2441 if (key_fd != STDIN_FILENO)
2442 close(key_fd);
2443
2444 return ret;
2445}
Al Cooper786418c2015-04-29 18:12:35 -04002446
2447int do_cache_ctrl(int value, int nargs, char **argv)
2448{
2449 __u8 ext_csd[512];
2450 int fd, ret;
2451 char *device;
2452
2453 CHECK(nargs != 2, "Usage: mmc cache enable </path/to/mmcblkX>\n",
2454 exit(1));
2455
2456 device = argv[1];
2457
2458 fd = open(device, O_RDWR);
2459 if (fd < 0) {
2460 perror("open");
2461 exit(1);
2462 }
2463
2464 ret = read_extcsd(fd, ext_csd);
2465 if (ret) {
2466 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2467 exit(1);
2468 }
2469
2470 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2471 fprintf(stderr,
2472 "The CACHE option is only availabe on devices >= "
2473 "MMC 4.5 %s\n", device);
2474 exit(1);
2475 }
2476
2477 /* If the cache size is zero, this device does not have a cache */
2478 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2479 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2480 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2481 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2482 fprintf(stderr,
2483 "The CACHE option is not available on %s\n",
2484 device);
2485 exit(1);
2486 }
2487 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2488 if (ret) {
2489 fprintf(stderr,
2490 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2491 value, EXT_CSD_CACHE_CTRL, device);
2492 exit(1);
2493 }
2494
2495 return ret;
2496}
2497
2498int do_cache_en(int nargs, char **argv)
2499{
2500 return do_cache_ctrl(1, nargs, argv);
2501}
2502
2503int do_cache_dis(int nargs, char **argv)
2504{
2505 return do_cache_ctrl(0, nargs, argv);
2506}
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002507
2508int do_ffu(int nargs, char **argv)
2509{
2510#ifndef MMC_IOC_MULTI_CMD
2511 fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
2512 " support, needed by FFU.\n");
2513 exit(1);
2514#else
2515 int dev_fd, img_fd;
2516 int sect_done = 0, retry = 3, ret = -EINVAL;
2517 unsigned int sect_size;
2518 __u8 ext_csd[EXT_CSD_SIZE];
2519 __u8 *buf;
2520 __u32 arg;
2521 off_t fw_size;
2522 ssize_t chunk_size;
2523 char *device;
2524 struct mmc_ioc_multi_cmd *multi_cmd;
2525
2526 CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n",
2527 exit(1));
2528
2529 device = argv[2];
2530 dev_fd = open(device, O_RDWR);
2531 if (dev_fd < 0) {
2532 perror("device open failed");
2533 exit(1);
2534 }
2535 img_fd = open(argv[1], O_RDONLY);
2536 if (img_fd < 0) {
2537 perror("image open failed");
2538 close(dev_fd);
2539 exit(1);
2540 }
2541
2542 buf = malloc(512);
2543 multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
2544 3 * sizeof(struct mmc_ioc_cmd));
2545 if (!buf || !multi_cmd) {
2546 perror("failed to allocate memory");
2547 goto out;
2548 }
2549
2550 ret = read_extcsd(dev_fd, ext_csd);
2551 if (ret) {
2552 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2553 goto out;
2554 }
2555
2556 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
2557 fprintf(stderr,
2558 "The FFU feature is only available on devices >= "
2559 "MMC 5.0, not supported in %s\n", device);
2560 goto out;
2561 }
2562
2563 if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
2564 fprintf(stderr, "FFU is not supported in %s\n", device);
2565 goto out;
2566 }
2567
2568 if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
2569 fprintf(stderr, "Firmware update was disabled in %s\n", device);
2570 goto out;
2571 }
2572
2573 fw_size = lseek(img_fd, 0, SEEK_END);
2574
2575 if (fw_size == 0) {
2576 fprintf(stderr, "Firmware image is empty");
2577 goto out;
2578 }
2579
2580 sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
2581 if (fw_size % sect_size) {
2582 fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
2583 goto out;
2584 }
2585
2586 /* set CMD ARG */
2587 arg = ext_csd[EXT_CSD_FFU_ARG_0] |
2588 ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
2589 ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
2590 ext_csd[EXT_CSD_FFU_ARG_3] << 24;
2591
2592 /* prepare multi_cmd to be sent */
2593 multi_cmd->num_of_cmds = 3;
2594
2595 /* put device into ffu mode */
2596 multi_cmd->cmds[0].opcode = MMC_SWITCH;
2597 multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2598 (EXT_CSD_MODE_CONFIG << 16) |
2599 (EXT_CSD_FFU_MODE << 8) |
2600 EXT_CSD_CMD_SET_NORMAL;
2601 multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2602 multi_cmd->cmds[0].write_flag = 1;
2603
2604 /* send image chunk */
2605 multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
2606 multi_cmd->cmds[1].blksz = sect_size;
2607 multi_cmd->cmds[1].blocks = 1;
2608 multi_cmd->cmds[1].arg = arg;
2609 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2610 multi_cmd->cmds[1].write_flag = 1;
2611 mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);
2612
2613 /* return device into normal mode */
2614 multi_cmd->cmds[2].opcode = MMC_SWITCH;
2615 multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2616 (EXT_CSD_MODE_CONFIG << 16) |
2617 (EXT_CSD_NORMAL_MODE << 8) |
2618 EXT_CSD_CMD_SET_NORMAL;
2619 multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2620 multi_cmd->cmds[2].write_flag = 1;
2621
2622do_retry:
2623 /* read firmware chunk */
2624 lseek(img_fd, 0, SEEK_SET);
2625 chunk_size = read(img_fd, buf, 512);
2626
2627 while (chunk_size > 0) {
2628 /* send ioctl with multi-cmd */
2629 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2630
2631 if (ret) {
2632 perror("Multi-cmd ioctl");
2633 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2634 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2635 goto out;
2636 }
2637
2638 ret = read_extcsd(dev_fd, ext_csd);
2639 if (ret) {
2640 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2641 goto out;
2642 }
2643
2644 /* Test if we need to restart the download */
2645 sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
2646 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
2647 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
2648 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
2649 /* By spec, host should re-start download from the first sector if sect_done is 0 */
2650 if (sect_done == 0) {
2651 if (retry > 0) {
2652 retry--;
2653 fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
2654 goto do_retry;
2655 }
2656 fprintf(stderr, "Programming failed! Aborting...\n");
2657 goto out;
2658 } else {
2659 fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
2660 }
2661
2662 /* read the next firmware chunk (if any) */
2663 chunk_size = read(img_fd, buf, 512);
2664 }
2665
2666 if ((sect_done * sect_size) == fw_size) {
2667 fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
2668 fprintf(stderr, "Programming finished with status %d \n", ret);
2669 }
2670 else {
2671 fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
2672 goto out;
2673 }
2674
2675 /* check mode operation for ffu install*/
2676 if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
2677 fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
2678 } else {
2679 fprintf(stderr, "Installing firmware on %s...\n", device);
2680 /* Re-enter ffu mode and install the firmware */
2681 multi_cmd->num_of_cmds = 2;
2682
2683 /* set ext_csd to install mode */
2684 multi_cmd->cmds[1].opcode = MMC_SWITCH;
2685 multi_cmd->cmds[1].blksz = 0;
2686 multi_cmd->cmds[1].blocks = 0;
2687 multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2688 (EXT_CSD_MODE_OPERATION_CODES << 16) |
2689 (EXT_CSD_FFU_INSTALL << 8) |
2690 EXT_CSD_CMD_SET_NORMAL;
2691 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2692 multi_cmd->cmds[1].write_flag = 1;
2693
2694 /* send ioctl with multi-cmd */
2695 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2696
2697 if (ret) {
2698 perror("Multi-cmd ioctl failed setting install mode");
2699 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2700 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2701 goto out;
2702 }
2703
2704 ret = read_extcsd(dev_fd, ext_csd);
2705 if (ret) {
2706 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2707 goto out;
2708 }
2709
2710 /* return status */
2711 ret = ext_csd[EXT_CSD_FFU_STATUS];
2712 if (ret) {
2713 fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
2714 goto out;
2715 } else {
2716 fprintf(stderr, "FFU finished successfully\n");
2717 }
2718 }
2719
2720out:
2721 free(buf);
2722 free(multi_cmd);
2723 close(img_fd);
2724 close(dev_fd);
2725 return ret;
2726#endif
2727}