blob: 0c6d69f1f3a4d100266278339481cb091b895d1c [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>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050027#include <sys/stat.h>
28#include <unistd.h>
29#include <fcntl.h>
Roman Peniaev023cc7c2014-08-12 23:25:45 +090030#include <errno.h>
31#include <stdint.h>
32#include <assert.h>
Uwe Kleine-König8f68bc32017-12-21 13:57:13 +010033#include <linux/fs.h> /* for BLKGETSIZE */
Xingyu Wu7be960e2020-12-29 17:34:36 +080034#include <ctype.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050035
36#include "mmc.h"
37#include "mmc_cmds.h"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080038#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050039
Nick Sanders9d57aa72014-03-05 21:38:54 -080040#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080041#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080042#define CID_SIZE 16
43
Julius Wernerbcc3e2e2016-04-21 16:53:02 -070044/* Sending several commands too close together seems to cause timeouts. */
45#define INTER_COMMAND_GAP_US (50 * 1000)
46
Roman Peniaev023cc7c2014-08-12 23:25:45 +090047#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080048
Uwe Kleine-Königf16e4662017-12-21 11:35:08 +010049#ifndef offsetof
50#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
51#endif
52
Al Cooper1b7f5d72016-06-07 16:35:46 -040053#define WP_BLKS_PER_QUERY 32
54
55#define USER_WP_PERM_PSWD_DIS 0x80
56#define USER_WP_CD_PERM_WP_DIS 0x40
57#define USER_WP_US_PERM_WP_DIS 0x10
58#define USER_WP_US_PWR_WP_DIS 0x08
59#define USER_WP_US_PERM_WP_EN 0x04
60#define USER_WP_US_PWR_WP_EN 0x01
61#define USER_WP_CLEAR (USER_WP_US_PERM_WP_DIS | USER_WP_US_PWR_WP_DIS \
62 | USER_WP_US_PERM_WP_EN | USER_WP_US_PWR_WP_EN)
63
64#define WPTYPE_NONE 0
65#define WPTYPE_TEMP 1
66#define WPTYPE_PWRON 2
67#define WPTYPE_PERM 3
68
69
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050070int read_extcsd(int fd, __u8 *ext_csd)
71{
72 int ret = 0;
73 struct mmc_ioc_cmd idata;
74 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080075 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050076 idata.write_flag = 0;
77 idata.opcode = MMC_SEND_EXT_CSD;
78 idata.arg = 0;
79 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080080 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050081 idata.blocks = 1;
82 mmc_ioc_cmd_set_data(idata, ext_csd);
83
84 ret = ioctl(fd, MMC_IOC_CMD, &idata);
85 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080086 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050087
88 return ret;
89}
90
91int write_extcsd_value(int fd, __u8 index, __u8 value)
92{
93 int ret = 0;
94 struct mmc_ioc_cmd idata;
95
96 memset(&idata, 0, sizeof(idata));
97 idata.write_flag = 1;
98 idata.opcode = MMC_SWITCH;
99 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
100 (index << 16) |
101 (value << 8) |
102 EXT_CSD_CMD_SET_NORMAL;
103 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
104
105 ret = ioctl(fd, MMC_IOC_CMD, &idata);
106 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -0800107 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500108
109 return ret;
110}
111
Ben Gardiner27c357d2013-05-30 17:12:47 -0400112int send_status(int fd, __u32 *response)
113{
114 int ret = 0;
115 struct mmc_ioc_cmd idata;
116
117 memset(&idata, 0, sizeof(idata));
118 idata.opcode = MMC_SEND_STATUS;
119 idata.arg = (1 << 16);
120 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
121
122 ret = ioctl(fd, MMC_IOC_CMD, &idata);
123 if (ret)
124 perror("ioctl");
125
126 *response = idata.response[0];
127
128 return ret;
129}
130
Al Cooper1b7f5d72016-06-07 16:35:46 -0400131static __u32 get_size_in_blks(int fd)
132{
133 int res;
134 int size;
135
136 res = ioctl(fd, BLKGETSIZE, &size);
137 if (res) {
138 fprintf(stderr, "Error getting device size, errno: %d\n",
139 errno);
140 perror("");
141 return -1;
142 }
143 return size;
144}
145
146static int set_write_protect(int fd, __u32 blk_addr, int on_off)
147{
148 int ret = 0;
149 struct mmc_ioc_cmd idata;
150
151 memset(&idata, 0, sizeof(idata));
152 idata.write_flag = 1;
153 if (on_off)
154 idata.opcode = MMC_SET_WRITE_PROT;
155 else
156 idata.opcode = MMC_CLEAR_WRITE_PROT;
157 idata.arg = blk_addr;
158 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
159
160 ret = ioctl(fd, MMC_IOC_CMD, &idata);
161 if (ret)
162 perror("ioctl");
163
164 return ret;
165}
166
167static int send_write_protect_type(int fd, __u32 blk_addr, __u64 *group_bits)
168{
169 int ret = 0;
170 struct mmc_ioc_cmd idata;
171 __u8 buf[8];
172 __u64 bits = 0;
173 int x;
174
175 memset(&idata, 0, sizeof(idata));
176 idata.write_flag = 0;
177 idata.opcode = MMC_SEND_WRITE_PROT_TYPE;
178 idata.blksz = 8,
179 idata.blocks = 1,
180 idata.arg = blk_addr;
181 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
182 mmc_ioc_cmd_set_data(idata, buf);
183
184 ret = ioctl(fd, MMC_IOC_CMD, &idata);
185 if (ret)
186 perror("ioctl");
187 for (x = 0; x < sizeof(buf); x++)
188 bits |= (__u64)(buf[7 - x]) << (x * 8);
189 *group_bits = bits;
190 return ret;
191}
192
193static void print_writeprotect_boot_status(__u8 *ext_csd)
Chris Ballb9c7a172012-02-20 12:34:25 -0500194{
195 __u8 reg;
Al Cooper786418c2015-04-29 18:12:35 -0400196 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500197
198 /* A43: reserved [174:0] */
199 if (ext_csd_rev >= 5) {
200 printf("Boot write protection status registers"
201 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
202
203 reg = ext_csd[EXT_CSD_BOOT_WP];
204 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
205 printf(" Power ro locking: ");
206 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
207 printf("not possible\n");
208 else
209 printf("possible\n");
210
211 printf(" Permanent ro locking: ");
212 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
213 printf("not possible\n");
214 else
215 printf("possible\n");
216
Julius Werner7ddcb232020-03-13 14:21:48 -0700217 /* Note: We call the partitions 0 and 1 (like Linux does), but
218 * the eMMC spec calls them AREA 1 and AREA 2. */
219 reg = ext_csd[EXT_CSD_BOOT_WP_STATUS];
220 printf(" partition 0 ro lock status: ");
221 if (reg & EXT_CSD_BOOT_WP_S_AREA_1_PERM)
Chris Ballb9c7a172012-02-20 12:34:25 -0500222 printf("locked permanently\n");
Julius Werner7ddcb232020-03-13 14:21:48 -0700223 else if (reg & EXT_CSD_BOOT_WP_S_AREA_1_PWR)
224 printf("locked until next power on\n");
225 else
226 printf("not locked\n");
227 printf(" partition 1 ro lock status: ");
228 if (reg & EXT_CSD_BOOT_WP_S_AREA_2_PERM)
229 printf("locked permanently\n");
230 else if (reg & EXT_CSD_BOOT_WP_S_AREA_2_PWR)
231 printf("locked until next power on\n");
Chris Ballb9c7a172012-02-20 12:34:25 -0500232 else
233 printf("not locked\n");
234 }
235}
236
Al Cooper1b7f5d72016-06-07 16:35:46 -0400237static int get_wp_group_size_in_blks(__u8 *ext_csd, __u32 *size)
238{
239 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
240
241 if ((ext_csd_rev < 5) || (ext_csd[EXT_CSD_ERASE_GROUP_DEF] == 0))
242 return 1;
243
244 *size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
245 ext_csd[EXT_CSD_HC_WP_GRP_SIZE] * 1024;
246 return 0;
247}
248
249
250int do_writeprotect_boot_get(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500251{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800252 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500253 int fd, ret;
254 char *device;
255
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100256 if (nargs != 2) {
257 fprintf(stderr, "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n");
258 exit(1);
259 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500260
261 device = argv[1];
262
263 fd = open(device, O_RDWR);
264 if (fd < 0) {
265 perror("open");
266 exit(1);
267 }
268
269 ret = read_extcsd(fd, ext_csd);
270 if (ret) {
271 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
272 exit(1);
273 }
274
Al Cooper1b7f5d72016-06-07 16:35:46 -0400275 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -0500276
277 return ret;
278}
279
Al Cooper1b7f5d72016-06-07 16:35:46 -0400280int do_writeprotect_boot_set(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500281{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800282 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500283 int fd, ret;
284 char *device;
Julius Werner7ddcb232020-03-13 14:21:48 -0700285 char *end;
286 int argi = 1;
287 int permanent = 0;
288 int partition = -1;
Chris Ballb9c7a172012-02-20 12:34:25 -0500289
Julius Werner7ddcb232020-03-13 14:21:48 -0700290 if (!strcmp(argv[argi], "-p")){
291 permanent = 1;
292 argi++;
293 }
294
295 if (nargs < 1 + argi || nargs > 2 + argi) {
296 fprintf(stderr, "Usage: mmc writeprotect boot set [-p] </path/to/mmcblkX> [0|1]\n");
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100297 exit(1);
298 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500299
Julius Werner7ddcb232020-03-13 14:21:48 -0700300 device = argv[argi++];
Chris Ballb9c7a172012-02-20 12:34:25 -0500301
302 fd = open(device, O_RDWR);
303 if (fd < 0) {
304 perror("open");
305 exit(1);
306 }
307
Julius Werner7ddcb232020-03-13 14:21:48 -0700308 if (nargs == 1 + argi) {
309 partition = strtoul(argv[argi], &end, 0);
310 if (*end != '\0' || !(partition == 0 || partition == 1)) {
311 fprintf(stderr, "Invalid partition number (must be 0 or 1): %s\n",
312 argv[argi]);
313 exit(1);
314 }
315 }
316
Chris Ballb9c7a172012-02-20 12:34:25 -0500317 ret = read_extcsd(fd, ext_csd);
318 if (ret) {
319 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
320 exit(1);
321 }
322
Julius Werner7ddcb232020-03-13 14:21:48 -0700323 value = ext_csd[EXT_CSD_BOOT_WP];
324 /*
325 * If permanent protection is already on for one partition and we're
326 * trying to enable power-reset protection for the other we need to make
327 * sure the selection bit for permanent protection still points to the
328 * former or we'll accidentally permanently protect the latter.
329 */
330 if ((value & EXT_CSD_BOOT_WP_B_PERM_WP_EN) && !permanent) {
331 if (ext_csd[EXT_CSD_BOOT_WP_STATUS] &
332 EXT_CSD_BOOT_WP_S_AREA_2_PERM) {
333 value |= EXT_CSD_BOOT_WP_B_PERM_WP_SEC_SEL;
334 if (partition != 1)
335 partition = 0;
336 } else {
337 /* PERM_WP_SEC_SEL cleared -> pointing to partition 0 */
338 if (partition != 0)
339 partition = 1;
340 }
341 }
342 if (partition != -1) {
343 value |= EXT_CSD_BOOT_WP_B_SEC_WP_SEL;
344 if (partition == 1)
345 value |= permanent ? EXT_CSD_BOOT_WP_B_PERM_WP_SEC_SEL
346 : EXT_CSD_BOOT_WP_B_PWR_WP_SEC_SEL;
347 }
348 value |= permanent ? EXT_CSD_BOOT_WP_B_PERM_WP_EN
349 : EXT_CSD_BOOT_WP_B_PWR_WP_EN;
350
Chris Ballb9c7a172012-02-20 12:34:25 -0500351 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
352 if (ret) {
353 fprintf(stderr, "Could not write 0x%02x to "
354 "EXT_CSD[%d] in %s\n",
355 value, EXT_CSD_BOOT_WP, device);
356 exit(1);
357 }
358
359 return ret;
360}
361
Al Cooper1b7f5d72016-06-07 16:35:46 -0400362static char *prot_desc[] = {
363 "No",
364 "Temporary",
365 "Power-on",
366 "Permanent"
367};
368
369static void print_wp_status(__u32 wp_sizeblks, __u32 start_group,
370 __u32 end_group, int rptype)
371{
372 printf("Write Protect Groups %d-%d (Blocks %d-%d), ",
373 start_group, end_group,
374 start_group * wp_sizeblks, ((end_group + 1) * wp_sizeblks) - 1);
375 printf("%s Write Protection\n", prot_desc[rptype]);
376}
377
378
379int do_writeprotect_user_get(int nargs, char **argv)
380{
381 __u8 ext_csd[512];
382 int fd, ret;
383 char *device;
384 int x;
385 int y = 0;
386 __u32 wp_sizeblks;
387 __u32 dev_sizeblks;
388 __u32 cnt;
389 __u64 bits;
390 __u32 wpblk;
391 __u32 last_wpblk = 0;
392 __u32 prot;
393 __u32 last_prot = -1;
394 int remain;
395
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100396 if (nargs != 2) {
397 fprintf(stderr, "Usage: mmc writeprotect user get </path/to/mmcblkX>\n");
398 exit(1);
399 }
Al Cooper1b7f5d72016-06-07 16:35:46 -0400400
401 device = argv[1];
402
403 fd = open(device, O_RDWR);
404 if (fd < 0) {
405 perror("open");
406 exit(1);
407 }
408 ret = read_extcsd(fd, ext_csd);
409 if (ret) {
410 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
411 exit(1);
412 }
413
414 ret = get_wp_group_size_in_blks(ext_csd, &wp_sizeblks);
415 if (ret)
416 exit(1);
417 printf("Write Protect Group size in blocks/bytes: %d/%d\n",
418 wp_sizeblks, wp_sizeblks * 512);
419 dev_sizeblks = get_size_in_blks(fd);
420 cnt = dev_sizeblks / wp_sizeblks;
421 for (x = 0; x < cnt; x += WP_BLKS_PER_QUERY) {
422 ret = send_write_protect_type(fd, x * wp_sizeblks, &bits);
423 if (ret)
424 break;
425 remain = cnt - x;
426 if (remain > WP_BLKS_PER_QUERY)
427 remain = WP_BLKS_PER_QUERY;
428 for (y = 0; y < remain; y++) {
429 prot = (bits >> (y * 2)) & 0x3;
430 if (prot != last_prot) {
431 /* not first time */
432 if (last_prot != -1) {
433 wpblk = x + y;
434 print_wp_status(wp_sizeblks,
435 last_wpblk,
436 wpblk - 1,
437 last_prot);
438 last_wpblk = wpblk;
439 }
440 last_prot = prot;
441 }
442 }
443 }
444 if (last_wpblk != (x + y - 1))
445 print_wp_status(wp_sizeblks, last_wpblk, cnt - 1, last_prot);
446
447 return ret;
448}
449
450int do_writeprotect_user_set(int nargs, char **argv)
451{
452 __u8 ext_csd[512];
453 int fd, ret;
454 char *device;
455 int blk_start;
456 int blk_cnt;
457 __u32 wp_blks;
458 __u8 user_wp;
459 int x;
460 int wptype;
461
462 if (nargs != 5)
463 goto usage;
464 device = argv[4];
465 fd = open(device, O_RDWR);
466 if (fd < 0) {
467 perror("open");
468 exit(1);
469 }
470 if (!strcmp(argv[1], "none")) {
471 wptype = WPTYPE_NONE;
472 } else if (!strcmp(argv[1], "temp")) {
473 wptype = WPTYPE_TEMP;
474 } else if (!strcmp(argv[1], "pwron")) {
475 wptype = WPTYPE_PWRON;
476#ifdef DANGEROUS_COMMANDS_ENABLED
477 } else if (!strcmp(argv[1], "perm")) {
478 wptype = WPTYPE_PERM;
479#endif /* DANGEROUS_COMMANDS_ENABLED */
480 } else {
481 fprintf(stderr, "Error, invalid \"type\"\n");
482 goto usage;
483 }
484 ret = read_extcsd(fd, ext_csd);
485 if (ret) {
486 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
487 exit(1);
488 }
489 ret = get_wp_group_size_in_blks(ext_csd, &wp_blks);
490 if (ret) {
491 fprintf(stderr, "Operation not supported for this device\n");
492 exit(1);
493 }
494 blk_start = strtol(argv[2], NULL, 0);
495 blk_cnt = strtol(argv[3], NULL, 0);
496 if ((blk_start % wp_blks) || (blk_cnt % wp_blks)) {
497 fprintf(stderr, "<start block> and <blocks> must be a ");
498 fprintf(stderr, "multiple of the Write Protect Group (%d)\n",
499 wp_blks);
500 exit(1);
501 }
502 if (wptype != WPTYPE_NONE) {
503 user_wp = ext_csd[EXT_CSD_USER_WP];
504 user_wp &= ~USER_WP_CLEAR;
505 switch (wptype) {
506 case WPTYPE_TEMP:
507 break;
508 case WPTYPE_PWRON:
509 user_wp |= USER_WP_US_PWR_WP_EN;
510 break;
511 case WPTYPE_PERM:
512 user_wp |= USER_WP_US_PERM_WP_EN;
513 break;
514 }
515 if (user_wp != ext_csd[EXT_CSD_USER_WP]) {
516 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
517 if (ret) {
518 fprintf(stderr, "Error setting EXT_CSD\n");
519 exit(1);
520 }
521 }
522 }
523 for (x = 0; x < blk_cnt; x += wp_blks) {
524 ret = set_write_protect(fd, blk_start + x,
525 wptype != WPTYPE_NONE);
526 if (ret) {
527 fprintf(stderr,
528 "Could not set write protect for %s\n", device);
529 exit(1);
530 }
531 }
532 if (wptype != WPTYPE_NONE) {
533 ret = write_extcsd_value(fd, EXT_CSD_USER_WP,
534 ext_csd[EXT_CSD_USER_WP]);
535 if (ret) {
536 fprintf(stderr, "Error restoring EXT_CSD\n");
537 exit(1);
538 }
539 }
540 return ret;
541
542usage:
543 fprintf(stderr,
544 "Usage: mmc writeprotect user set <type><start block><blocks><device>\n");
545 exit(1);
546}
547
Saugata Dasb7e25992012-05-17 09:26:34 -0400548int do_disable_512B_emulation(int nargs, char **argv)
549{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800550 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400551 int fd, ret;
552 char *device;
553
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100554 if (nargs != 2) {
555 fprintf(stderr, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n");
556 exit(1);
557 }
558
Saugata Dasb7e25992012-05-17 09:26:34 -0400559 device = argv[1];
560
561 fd = open(device, O_RDWR);
562 if (fd < 0) {
563 perror("open");
564 exit(1);
565 }
566
567 ret = read_extcsd(fd, ext_csd);
568 if (ret) {
569 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
570 exit(1);
571 }
572
573 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
574 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
575 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
576
577 if (native_sector_size && !data_sector_size &&
578 (wr_rel_param & EN_REL_WR)) {
579 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
580
581 if (ret) {
582 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
Tomas Melin295dd7a2016-08-29 11:45:44 -0400583 1, EXT_CSD_NATIVE_SECTOR_SIZE, device);
Saugata Dasb7e25992012-05-17 09:26:34 -0400584 exit(1);
585 }
586 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
587 } else if (native_sector_size && data_sector_size) {
588 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
589 } else {
590 printf("MMC does not support disabling 512B emulation mode.\n");
591 }
592
593 return ret;
594}
595
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200596int do_write_boot_en(int nargs, char **argv)
597{
598 __u8 ext_csd[512];
599 __u8 value = 0;
600 int fd, ret;
601 char *device;
602 int boot_area, send_ack;
603
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100604 if (nargs != 4) {
605 fprintf(stderr, "Usage: mmc bootpart enable <partition_number> <send_ack> </path/to/mmcblkX>\n");
606 exit(1);
607 }
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200608
609 /*
610 * If <send_ack> is 1, the device will send acknowledgment
611 * pattern "010" to the host when boot operation begins.
612 * If <send_ack> is 0, it won't.
613 */
614 boot_area = strtol(argv[1], NULL, 10);
615 send_ack = strtol(argv[2], NULL, 10);
616 device = argv[3];
617
618 fd = open(device, O_RDWR);
619 if (fd < 0) {
620 perror("open");
621 exit(1);
622 }
623
624 ret = read_extcsd(fd, ext_csd);
625 if (ret) {
626 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
627 exit(1);
628 }
629
630 value = ext_csd[EXT_CSD_PART_CONFIG];
631
632 switch (boot_area) {
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100633 case EXT_CSD_PART_CONFIG_ACC_NONE:
634 value &= ~(7 << 3);
635 break;
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200636 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
637 value |= (1 << 3);
638 value &= ~(3 << 4);
639 break;
640 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
641 value |= (1 << 4);
642 value &= ~(1 << 3);
643 value &= ~(1 << 5);
644 break;
645 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
646 value |= (boot_area << 3);
647 break;
648 default:
649 fprintf(stderr, "Cannot enable the boot area\n");
650 exit(1);
651 }
652 if (send_ack)
653 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
654 else
655 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
656
657 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
658 if (ret) {
659 fprintf(stderr, "Could not write 0x%02x to "
660 "EXT_CSD[%d] in %s\n",
661 value, EXT_CSD_PART_CONFIG, device);
662 exit(1);
663 }
664 return ret;
665}
666
Al Cooper794314c2015-05-01 08:24:37 -0400667int do_boot_bus_conditions_set(int nargs, char **argv)
668{
669 __u8 ext_csd[512];
670 __u8 value = 0;
671 int fd, ret;
672 char *device;
673
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100674 if (nargs != 5) {
675 fprintf(stderr, "Usage: mmc: bootbus set <boot_mode> <reset_boot_bus_conditions> <boot_bus_width> <device>\n");
676 exit(1);
677 }
Al Cooper794314c2015-05-01 08:24:37 -0400678
679 if (strcmp(argv[1], "single_backward") == 0)
680 value |= 0;
681 else if (strcmp(argv[1], "single_hs") == 0)
682 value |= 0x8;
683 else if (strcmp(argv[1], "dual") == 0)
684 value |= 0x10;
685 else {
686 fprintf(stderr, "illegal <boot_mode> specified\n");
687 exit(1);
688 }
689
690 if (strcmp(argv[2], "x1") == 0)
691 value |= 0;
692 else if (strcmp(argv[2], "retain") == 0)
693 value |= 0x4;
694 else {
695 fprintf(stderr,
696 "illegal <reset_boot_bus_conditions> specified\n");
697 exit(1);
698 }
699
700 if (strcmp(argv[3], "x1") == 0)
701 value |= 0;
702 else if (strcmp(argv[3], "x4") == 0)
703 value |= 0x1;
704 else if (strcmp(argv[3], "x8") == 0)
705 value |= 0x2;
706 else {
707 fprintf(stderr, "illegal <boot_bus_width> specified\n");
708 exit(1);
709 }
710
711 device = argv[4];
712 fd = open(device, O_RDWR);
713 if (fd < 0) {
714 perror("open");
715 exit(1);
716 }
717
718 ret = read_extcsd(fd, ext_csd);
719 if (ret) {
720 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
721 exit(1);
722 }
723 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
724 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
725
726 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
727 if (ret) {
728 fprintf(stderr, "Could not write 0x%02x to "
729 "EXT_CSD[%d] in %s\n",
730 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
731 exit(1);
732 }
733 close(fd);
734 return ret;
735}
736
Chris Ballf74dfe22012-10-19 16:49:55 -0400737int do_hwreset(int value, int nargs, char **argv)
738{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800739 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400740 int fd, ret;
741 char *device;
742
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100743 if (nargs != 2) {
744 fprintf(stderr, "Usage: mmc hwreset enable </path/to/mmcblkX>\n");
745 exit(1);
746 }
Chris Ballf74dfe22012-10-19 16:49:55 -0400747
748 device = argv[1];
749
750 fd = open(device, O_RDWR);
751 if (fd < 0) {
752 perror("open");
753 exit(1);
754 }
755
756 ret = read_extcsd(fd, ext_csd);
757 if (ret) {
758 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
759 exit(1);
760 }
761
762 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
763 EXT_CSD_HW_RESET_EN) {
764 fprintf(stderr,
765 "H/W Reset is already permanently enabled on %s\n",
766 device);
767 exit(1);
768 }
769 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
770 EXT_CSD_HW_RESET_DIS) {
771 fprintf(stderr,
772 "H/W Reset is already permanently disabled on %s\n",
773 device);
774 exit(1);
775 }
776
777 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
778 if (ret) {
779 fprintf(stderr,
780 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
781 value, EXT_CSD_RST_N_FUNCTION, device);
782 exit(1);
783 }
784
785 return ret;
786}
787
788int do_hwreset_en(int nargs, char **argv)
789{
790 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
791}
792
793int do_hwreset_dis(int nargs, char **argv)
794{
795 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
796}
797
Jaehoon Chung86496512012-09-21 10:08:05 +0000798int do_write_bkops_en(int nargs, char **argv)
799{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800800 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000801 int fd, ret;
802 char *device;
803
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100804 if (nargs != 2) {
805 fprintf(stderr, "Usage: mmc bkops enable </path/to/mmcblkX>\n");
806 exit(1);
807 }
Jaehoon Chung86496512012-09-21 10:08:05 +0000808
809 device = argv[1];
810
811 fd = open(device, O_RDWR);
812 if (fd < 0) {
813 perror("open");
814 exit(1);
815 }
816
817 ret = read_extcsd(fd, ext_csd);
818 if (ret) {
819 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
820 exit(1);
821 }
822
823 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
824 fprintf(stderr, "%s doesn't support BKOPS\n", device);
825 exit(1);
826 }
827
828 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
829 if (ret) {
830 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
831 value, EXT_CSD_BKOPS_EN, device);
832 exit(1);
833 }
834
835 return ret;
836}
837
Ben Gardiner27c357d2013-05-30 17:12:47 -0400838int do_status_get(int nargs, char **argv)
839{
840 __u32 response;
841 int fd, ret;
842 char *device;
843
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100844 if (nargs != 2) {
845 fprintf(stderr, "Usage: mmc status get </path/to/mmcblkX>\n");
846 exit(1);
847 }
Ben Gardiner27c357d2013-05-30 17:12:47 -0400848
849 device = argv[1];
850
851 fd = open(device, O_RDWR);
852 if (fd < 0) {
853 perror("open");
854 exit(1);
855 }
856
857 ret = send_status(fd, &response);
858 if (ret) {
859 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
860 exit(1);
861 }
862
863 printf("SEND_STATUS response: 0x%08x\n", response);
864
865 return ret;
866}
867
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800868__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
869{
870 return (ext_csd_loc[3] << 24) |
871 (ext_csd_loc[2] << 16) |
872 (ext_csd_loc[1] << 8) |
873 ext_csd_loc[0];
874}
875
Ben Gardiner4e850232013-05-30 17:12:49 -0400876unsigned int get_sector_count(__u8 *ext_csd)
877{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800878 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400879}
880
881int is_blockaddresed(__u8 *ext_csd)
882{
883 unsigned int sectors = get_sector_count(ext_csd);
884
Tomas Melin2e610662016-08-29 11:41:10 -0400885 /* over 2GiB devices are block-addressed */
Ben Gardiner4e850232013-05-30 17:12:49 -0400886 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
887}
888
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400889unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
890{
891 return ext_csd[221];
892}
893
894unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
895{
896 return ext_csd[224];
897}
898
Ben Gardinere6e84e92013-09-19 11:14:27 -0400899int set_partitioning_setting_completed(int dry_run, const char * const device,
900 int fd)
901{
902 int ret;
903
Tomas Melin4dadd872016-08-29 11:55:08 -0400904 if (dry_run == 1) {
Ben Gardinere6e84e92013-09-19 11:14:27 -0400905 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
906 fprintf(stderr, "These changes will not take effect neither "
907 "now nor after a power cycle\n");
908 return 1;
Tomas Melin4dadd872016-08-29 11:55:08 -0400909 } else if (dry_run == 2) {
910 printf("-c given, expecting more partition settings before "
911 "writing PARTITION_SETTING_COMPLETED\n");
912 return 0;
Ben Gardinere6e84e92013-09-19 11:14:27 -0400913 }
914
915 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
916 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
917 if (ret) {
918 fprintf(stderr, "Could not write 0x1 to "
919 "EXT_CSD[%d] in %s\n",
920 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
921 return 1;
922 }
923
924 __u32 response;
925 ret = send_status(fd, &response);
926 if (ret) {
927 fprintf(stderr, "Could not get response to SEND_STATUS "
928 "from %s\n", device);
929 return 1;
930 }
931
932 if (response & R1_SWITCH_ERROR) {
933 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
934 "failed on %s\n", device);
935 return 1;
936 }
937
938 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
939 "%s SUCCESS\n", device);
940 fprintf(stderr, "Device power cycle needed for settings to "
941 "take effect.\n"
942 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
943 "using 'extcsd read' after power cycle\n");
944
945 return 0;
946}
947
Balaji T K1fdb7f92015-04-29 18:12:32 -0400948int check_enhanced_area_total_limit(const char * const device, int fd)
949{
950 __u8 ext_csd[512];
951 __u32 regl;
952 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
953 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400954 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400955 unsigned int wp_sz, erase_sz;
956 int ret;
957
958 ret = read_extcsd(fd, ext_csd);
959 if (ret) {
960 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
961 exit(1);
962 }
963 wp_sz = get_hc_wp_grp_size(ext_csd);
964 erase_sz = get_hc_erase_grp_size(ext_csd);
965
966 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
967 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
968 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
969 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
970 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
971 enh_area_sz += gp4_part_sz;
972 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
973 printf(" i.e. %lu KiB\n", gp4_part_sz);
974 }
975
976 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
977 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
978 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
979 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
980 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
981 enh_area_sz += gp3_part_sz;
982 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
983 printf(" i.e. %lu KiB\n", gp3_part_sz);
984 }
985
986 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
987 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
988 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
989 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
990 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
991 enh_area_sz += gp2_part_sz;
992 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
993 printf(" i.e. %lu KiB\n", gp2_part_sz);
994 }
995
996 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
997 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
998 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
999 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
1000 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
1001 enh_area_sz += gp1_part_sz;
1002 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
1003 printf(" i.e. %lu KiB\n", gp1_part_sz);
1004 }
1005
1006 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
1007 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1008 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
1009 user_area_sz = 512l * regl * erase_sz * wp_sz;
1010 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
1011 enh_area_sz += user_area_sz;
1012 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
1013 printf(" i.e. %lu KiB\n", user_area_sz);
1014 }
1015
1016 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
1017 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1018 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1019 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
1020 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
1021 printf(" i.e. %lu KiB\n", max_enh_area_sz);
1022 if (enh_area_sz > max_enh_area_sz) {
1023 fprintf(stderr,
1024 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
1025 enh_area_sz, max_enh_area_sz, device);
1026 return 1;
1027 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001028 total_sz = get_sector_count(ext_csd) / 2;
1029 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
1030 gp1_part_sz + user_area_sz;
1031 if (total_gp_user_sz > total_sz) {
1032 fprintf(stderr,
1033 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
1034 total_gp_user_sz, total_sz, device);
1035 return 1;
1036 }
1037
1038 return 0;
1039}
1040
1041int do_create_gp_partition(int nargs, char **argv)
1042{
1043 __u8 value;
1044 __u8 ext_csd[512];
1045 __u8 address;
1046 int fd, ret;
1047 char *device;
1048 int dry_run = 1;
1049 int partition, enh_attr, ext_attr;
1050 unsigned int length_kib, gp_size_mult;
1051 unsigned long align;
1052
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001053 if (nargs != 7) {
1054 fprintf(stderr, "Usage: mmc gp create <-y|-n|-c> <length KiB> <partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n");
1055 exit(1);
1056 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001057
Tomas Melin4dadd872016-08-29 11:55:08 -04001058 if (!strcmp("-y", argv[1])) {
Balaji T Kd78ce082015-04-29 18:12:33 -04001059 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001060 } else if (!strcmp("-c", argv[1])) {
1061 dry_run = 2;
1062 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001063
1064 length_kib = strtol(argv[2], NULL, 10);
1065 partition = strtol(argv[3], NULL, 10);
1066 enh_attr = strtol(argv[4], NULL, 10);
1067 ext_attr = strtol(argv[5], NULL, 10);
1068 device = argv[6];
1069
Marcus Folkessoncb04fde2015-11-18 15:06:16 -05001070 if (partition < 1 || partition > 4) {
1071 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -04001072 exit(1);
1073 }
1074
1075 if (enh_attr && ext_attr) {
1076 printf("Not allowed to set both enhanced attribute and extended attribute\n");
1077 exit(1);
1078 }
1079
1080 fd = open(device, O_RDWR);
1081 if (fd < 0) {
1082 perror("open");
1083 exit(1);
1084 }
1085
1086 ret = read_extcsd(fd, ext_csd);
1087 if (ret) {
1088 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1089 exit(1);
1090 }
1091
1092 /* assert not PARTITION_SETTING_COMPLETED */
1093 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
1094 printf(" Device is already partitioned\n");
1095 exit(1);
1096 }
1097
1098 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1099 gp_size_mult = (length_kib + align/2l) / align;
1100
1101 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1102 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1103 if (ret) {
1104 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
1105 EXT_CSD_ERASE_GROUP_DEF, device);
1106 exit(1);
1107 }
1108
1109 value = (gp_size_mult >> 16) & 0xff;
1110 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
1111 ret = write_extcsd_value(fd, address, value);
1112 if (ret) {
1113 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1114 value, address, device);
1115 exit(1);
1116 }
1117 value = (gp_size_mult >> 8) & 0xff;
1118 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
1119 ret = write_extcsd_value(fd, address, value);
1120 if (ret) {
1121 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1122 value, address, device);
1123 exit(1);
1124 }
1125 value = gp_size_mult & 0xff;
1126 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
1127 ret = write_extcsd_value(fd, address, value);
1128 if (ret) {
1129 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1130 value, address, device);
1131 exit(1);
1132 }
1133
1134 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1135 if (enh_attr)
1136 value |= (1 << partition);
1137 else
1138 value &= ~(1 << partition);
1139
1140 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
1141 if (ret) {
1142 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
1143 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1144 exit(1);
1145 }
1146
1147 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
1148 value = ext_csd[address];
1149 if (ext_attr)
1150 value |= (ext_attr << (4 * ((partition - 1) % 2)));
1151 else
1152 value &= (0xF << (4 * ((partition % 2))));
1153
1154 ret = write_extcsd_value(fd, address, value);
1155 if (ret) {
1156 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
1157 value, address, device);
1158 exit(1);
1159 }
1160
1161 ret = check_enhanced_area_total_limit(device, fd);
1162 if (ret)
1163 exit(1);
1164
Tomas Melin20379fa2016-08-29 12:02:28 -04001165 if (set_partitioning_setting_completed(dry_run, device, fd))
Balaji T Kd78ce082015-04-29 18:12:33 -04001166 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -04001167
1168 return 0;
1169}
1170
Ben Gardinerd91d3692013-05-30 17:12:51 -04001171int do_enh_area_set(int nargs, char **argv)
1172{
1173 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001174 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -04001175 int fd, ret;
1176 char *device;
1177 int dry_run = 1;
1178 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
1179 unsigned long align;
1180
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001181 if (nargs != 5) {
1182 fprintf(stderr, "Usage: mmc enh_area set <-y|-n|-c> <start KiB> <length KiB> </path/to/mmcblkX>\n");
1183 exit(1);
1184 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001185
Tomas Melin4dadd872016-08-29 11:55:08 -04001186 if (!strcmp("-y", argv[1])) {
Ben Gardinerd91d3692013-05-30 17:12:51 -04001187 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001188 } else if (!strcmp("-c", argv[1])) {
1189 dry_run = 2;
1190 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001191
1192 start_kib = strtol(argv[2], NULL, 10);
1193 length_kib = strtol(argv[3], NULL, 10);
1194 device = argv[4];
1195
1196 fd = open(device, O_RDWR);
1197 if (fd < 0) {
1198 perror("open");
1199 exit(1);
1200 }
1201
1202 ret = read_extcsd(fd, ext_csd);
1203 if (ret) {
1204 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1205 exit(1);
1206 }
1207
1208 /* assert ENH_ATTRIBUTE_EN */
1209 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
1210 {
1211 printf(" Device cannot have enhanced tech.\n");
1212 exit(1);
1213 }
1214
1215 /* assert not PARTITION_SETTING_COMPLETED */
1216 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1217 {
1218 printf(" Device is already partitioned\n");
1219 exit(1);
1220 }
1221
1222 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1223
1224 enh_size_mult = (length_kib + align/2l) / align;
1225
jason.zeng1c553062018-03-27 02:17:10 -04001226 enh_start_addr = start_kib * (1024 / (is_blockaddresed(ext_csd) ? 512 : 1));
Ben Gardinerd91d3692013-05-30 17:12:51 -04001227 enh_start_addr /= align;
1228 enh_start_addr *= align;
1229
1230 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1231 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1232 if (ret) {
1233 fprintf(stderr, "Could not write 0x1 to "
1234 "EXT_CSD[%d] in %s\n",
1235 EXT_CSD_ERASE_GROUP_DEF, device);
1236 exit(1);
1237 }
1238
1239 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
1240 value = (enh_start_addr >> 24) & 0xff;
1241 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
1242 if (ret) {
1243 fprintf(stderr, "Could not write 0x%02x to "
1244 "EXT_CSD[%d] in %s\n", value,
1245 EXT_CSD_ENH_START_ADDR_3, device);
1246 exit(1);
1247 }
1248 value = (enh_start_addr >> 16) & 0xff;
1249 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
1250 if (ret) {
1251 fprintf(stderr, "Could not write 0x%02x to "
1252 "EXT_CSD[%d] in %s\n", value,
1253 EXT_CSD_ENH_START_ADDR_2, device);
1254 exit(1);
1255 }
1256 value = (enh_start_addr >> 8) & 0xff;
1257 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
1258 if (ret) {
1259 fprintf(stderr, "Could not write 0x%02x to "
1260 "EXT_CSD[%d] in %s\n", value,
1261 EXT_CSD_ENH_START_ADDR_1, device);
1262 exit(1);
1263 }
1264 value = enh_start_addr & 0xff;
1265 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
1266 if (ret) {
1267 fprintf(stderr, "Could not write 0x%02x to "
1268 "EXT_CSD[%d] in %s\n", value,
1269 EXT_CSD_ENH_START_ADDR_0, device);
1270 exit(1);
1271 }
1272
1273 value = (enh_size_mult >> 16) & 0xff;
1274 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
1275 if (ret) {
1276 fprintf(stderr, "Could not write 0x%02x to "
1277 "EXT_CSD[%d] in %s\n", value,
1278 EXT_CSD_ENH_SIZE_MULT_2, device);
1279 exit(1);
1280 }
1281 value = (enh_size_mult >> 8) & 0xff;
1282 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
1283 if (ret) {
1284 fprintf(stderr, "Could not write 0x%02x to "
1285 "EXT_CSD[%d] in %s\n", value,
1286 EXT_CSD_ENH_SIZE_MULT_1, device);
1287 exit(1);
1288 }
1289 value = enh_size_mult & 0xff;
1290 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
1291 if (ret) {
1292 fprintf(stderr, "Could not write 0x%02x to "
1293 "EXT_CSD[%d] in %s\n", value,
1294 EXT_CSD_ENH_SIZE_MULT_0, device);
1295 exit(1);
1296 }
Balaji T K1fdb7f92015-04-29 18:12:32 -04001297 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
1298 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001299 if (ret) {
1300 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
1301 "EXT_CSD[%d] in %s\n",
1302 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1303 exit(1);
1304 }
1305
Balaji T K1fdb7f92015-04-29 18:12:32 -04001306 ret = check_enhanced_area_total_limit(device, fd);
1307 if (ret)
1308 exit(1);
1309
Ben Gardinere6e84e92013-09-19 11:14:27 -04001310 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001311
Tomas Melin20379fa2016-08-29 12:02:28 -04001312 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -04001313 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001314
1315 return 0;
1316}
1317
Ben Gardiner196d0d22013-09-19 11:14:29 -04001318int do_write_reliability_set(int nargs, char **argv)
1319{
1320 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001321 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -04001322 int fd, ret;
1323
1324 int dry_run = 1;
1325 int partition;
1326 char *device;
1327
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001328 if (nargs != 4) {
1329 fprintf(stderr,"Usage: mmc write_reliability set <-y|-n|-c> <partition> </path/to/mmcblkX>\n");
1330 exit(1);
1331 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001332
Tomas Melin77c22a82016-09-01 11:49:01 -04001333 if (!strcmp("-y", argv[1])) {
Ben Gardiner196d0d22013-09-19 11:14:29 -04001334 dry_run = 0;
Tomas Melin77c22a82016-09-01 11:49:01 -04001335 } else if (!strcmp("-c", argv[1])) {
1336 dry_run = 2;
1337 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001338
1339 partition = strtol(argv[2], NULL, 10);
1340 device = argv[3];
1341
1342 fd = open(device, O_RDWR);
1343 if (fd < 0) {
1344 perror("open");
1345 exit(1);
1346 }
1347
1348 ret = read_extcsd(fd, ext_csd);
1349 if (ret) {
1350 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1351 exit(1);
1352 }
1353
1354 /* assert not PARTITION_SETTING_COMPLETED */
1355 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1356 {
1357 printf(" Device is already partitioned\n");
1358 exit(1);
1359 }
1360
1361 /* assert HS_CTRL_REL */
1362 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
1363 printf("Cannot set write reliability parameters, WR_REL_SET is "
1364 "read-only\n");
1365 exit(1);
1366 }
1367
1368 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
1369 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
1370 if (ret) {
1371 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1372 value, EXT_CSD_WR_REL_SET, device);
1373 exit(1);
1374 }
1375
1376 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1377 value, device);
1378
Tomas Melin20379fa2016-08-29 12:02:28 -04001379 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardiner196d0d22013-09-19 11:14:29 -04001380 exit(1);
1381
1382 return 0;
1383}
1384
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001385int do_read_extcsd(int nargs, char **argv)
1386{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001387 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001388 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001389 int fd, ret;
1390 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001391 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001392 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001393 "4.0", /* 0 */
1394 "4.1", /* 1 */
1395 "4.2", /* 2 */
1396 "4.3", /* 3 */
1397 "Obsolete", /* 4 */
1398 "4.41", /* 5 */
1399 "4.5", /* 6 */
1400 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001401 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001402 };
1403 int boot_access;
1404 const char* boot_access_str[] = {
1405 "No access to boot partition", /* 0 */
1406 "R/W Boot Partition 1", /* 1 */
1407 "R/W Boot Partition 2", /* 2 */
1408 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1409 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001410
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001411 if (nargs != 2) {
1412 fprintf(stderr, "Usage: mmc extcsd read </path/to/mmcblkX>\n");
1413 exit(1);
1414 }
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001415
1416 device = argv[1];
1417
1418 fd = open(device, O_RDWR);
1419 if (fd < 0) {
1420 perror("open");
1421 exit(1);
1422 }
1423
1424 ret = read_extcsd(fd, ext_csd);
1425 if (ret) {
1426 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1427 exit(1);
1428 }
1429
Al Cooper786418c2015-04-29 18:12:35 -04001430 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001431
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001432 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1433 (ext_csd_rev != 4))
1434 str = ver_str[ext_csd_rev];
1435 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001436 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001437
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001438 printf("=============================================\n");
1439 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1440 printf("=============================================\n\n");
1441
1442 if (ext_csd_rev < 3)
1443 goto out_free; /* No ext_csd */
1444
1445 /* Parse the Extended CSD registers.
1446 * Reserved bit should be read as "0" in case of spec older
1447 * than A441.
1448 */
1449 reg = ext_csd[EXT_CSD_S_CMD_SET];
1450 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1451 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001452 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001453
1454 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1455 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1456 if (reg & EXT_CSD_HPI_SUPP) {
1457 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001458 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001459 else
1460 printf("implementation based on CMD13\n");
1461 }
1462
1463 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1464 ext_csd[502]);
1465
1466 if (ext_csd_rev >= 6) {
1467 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1468 ext_csd[501]);
1469 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1470 ext_csd[500]);
1471 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1472 ext_csd[499]);
1473
1474 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1475 ext_csd[498]);
1476 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1477 ext_csd[497]);
1478 printf("Context Management Capabilities"
1479 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1480 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1481 ext_csd[495]);
1482 printf("Extended partition attribute support"
1483 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001484 }
1485 if (ext_csd_rev >= 7) {
1486 int j;
1487 int eol_info;
1488 char* eol_info_str[] = {
1489 "Not Defined", /* 0 */
1490 "Normal", /* 1 */
1491 "Warning", /* 2 */
1492 "Urgent", /* 3 */
1493 };
1494
1495 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1496 ext_csd[493]);
1497 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1498 ext_csd[492]);
1499 printf("Operation codes timeout"
1500 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1501 ext_csd[491]);
1502 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1503 get_word_from_ext_csd(&ext_csd[487]));
1504 printf("Number of FW sectors correctly programmed"
1505 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1506 get_word_from_ext_csd(&ext_csd[302]));
1507 printf("Vendor proprietary health report:\n");
1508 for (j = 301; j >= 270; j--)
1509 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1510 " 0x%02x\n", j, ext_csd[j]);
1511 for (j = 269; j >= 268; j--) {
1512 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001513 char est_type = 'B' + (j - 269);
1514 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001515 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001516 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001517 if (life_used >= 0x1 && life_used <= 0xa)
1518 printf(" i.e. %d%% - %d%% device life time"
1519 " used\n",
1520 (life_used - 1) * 10, life_used * 10);
1521 else if (life_used == 0xb)
1522 printf(" i.e. Exceeded its maximum estimated"
1523 " device life time\n");
1524 }
1525 eol_info = ext_csd[267];
1526 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1527 eol_info);
1528 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1529 printf(" i.e. %s\n", eol_info_str[eol_info]);
1530 else
1531 printf(" i.e. Reserved\n");
1532
1533 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1534 ext_csd[266]);
1535 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1536 ext_csd[265]);
1537 printf("Optimal trim unit size"
1538 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1539 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1540 ext_csd[263], ext_csd[262]);
1541 printf("Firmware version:\n");
1542 for (j = 261; j >= 254; j--)
1543 printf("[FIRMWARE_VERSION[%d]]:"
1544 " 0x%02x\n", j, ext_csd[j]);
1545
1546 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1547 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1548 }
1549 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001550 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1551 ext_csd[248]);
1552 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1553 ext_csd[247]);
1554 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001555 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001556 }
1557
1558 /* A441: Reserved [501:247]
1559 A43: reserved [246:229] */
1560 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001561 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001562 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001563
1564 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1565
1566 printf("1st Initialisation Time after programmed sector"
1567 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1568
1569 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001570 printf("Power class for 52MHz, DDR at 3.6V"
1571 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1572 printf("Power class for 52MHz, DDR at 1.95V"
1573 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1574
1575 /* A441: reserved [237-236] */
1576
1577 if (ext_csd_rev >= 6) {
1578 printf("Power class for 200MHz at 3.6V"
1579 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1580 printf("Power class for 200MHz, at 1.95V"
1581 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1582 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001583 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001584 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1585 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1586 /* A441: reserved [233] */
1587 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1588 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1589 ext_csd[231]);
1590 }
1591 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1592 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1593 ext_csd[230]);
1594 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1595 ext_csd[229]);
1596 }
1597 reg = ext_csd[EXT_CSD_BOOT_INFO];
1598 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1599 if (reg & EXT_CSD_BOOT_INFO_ALT)
1600 printf(" Device supports alternative boot method\n");
1601 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1602 printf(" Device supports dual data rate during boot\n");
1603 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1604 printf(" Device supports high speed timing during boot\n");
1605
1606 /* A441/A43: reserved [227] */
1607 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1608 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001609
1610 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001611 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001612 reg);
1613 printf(" i.e. %u KiB\n", 512 * reg);
1614
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001615 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1616 ext_csd[223]);
1617 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1618 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001619
1620 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001621 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001622 reg);
1623 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1624
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001625 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1626 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1627 /* A441/A43: reserved [218] */
1628 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1629 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001630
1631 unsigned int sectors = get_sector_count(ext_csd);
1632 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1633 if (is_blockaddresed(ext_csd))
1634 printf(" Device is block-addressed\n");
1635 else
1636 printf(" Device is NOT block-addressed\n");
1637
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001638 /* A441/A43: reserved [211] */
1639 printf("Minimum Write Performance for 8bit:\n");
1640 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1641 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1642 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1643 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1644 printf("Minimum Write Performance for 4bit:\n");
1645 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1646 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1647 /* A441/A43: reserved [204] */
1648 printf("Power classes registers:\n");
1649 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1650 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1651 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1652 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1653
1654 /* A43: reserved [199:198] */
1655 if (ext_csd_rev >= 5) {
1656 printf("Partition switching timing "
1657 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1658 printf("Out-of-interrupt busy timing"
1659 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1660 }
1661
1662 /* A441/A43: reserved [197] [195] [193] [190] [188]
1663 * [186] [184] [182] [180] [176] */
1664
1665 if (ext_csd_rev >= 6)
1666 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1667 ext_csd[197]);
1668
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001669 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001670 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1671 ext_csd[196], ext_csd[195]);
1672 reg = ext_csd[195];
1673 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1674 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001675 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001676 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1677 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001678 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1679 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1680 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1681 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1682 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1683 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001684
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001685 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001686 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001687 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1688 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1689 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1690 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1691 ext_csd[185]);
1692 /* bus_width: ext_csd[183] not readable */
1693 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1694 ext_csd[181]);
1695 reg = ext_csd[EXT_CSD_BOOT_CFG];
1696 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001697 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001698 case 0x0:
1699 printf(" Not boot enable\n");
1700 break;
1701 case 0x1:
1702 printf(" Boot Partition 1 enabled\n");
1703 break;
1704 case 0x2:
1705 printf(" Boot Partition 2 enabled\n");
1706 break;
1707 case 0x7:
1708 printf(" User Area Enabled for boot\n");
1709 break;
1710 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001711 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1712 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1713 printf(" %s\n", boot_access_str[boot_access]);
1714 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001715 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001716 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001717
1718 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1719 ext_csd[178]);
1720 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1721 ext_csd[177]);
1722 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001723 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001724
Al Cooper1b7f5d72016-06-07 16:35:46 -04001725 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -05001726
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001727 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001728 /* A441]: reserved [172] */
1729 printf("User area write protection register"
1730 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1731 /* A441]: reserved [170] */
1732 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1733 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001734
1735 reg = ext_csd[EXT_CSD_WR_REL_SET];
1736 const char * const fast = "existing data is at risk if a power "
1737 "failure occurs during a write operation";
1738 const char * const reliable = "the device protects existing "
1739 "data if a power failure occurs during a write "
1740 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001741 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001742 " [WR_REL_SET]: 0x%02x\n", reg);
1743
1744 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1745 int i;
1746 for (i = 1; i <= 4; i++) {
1747 printf(" partition %d: %s\n", i,
1748 reg & (1<<i) ? reliable : fast);
1749 }
1750
1751 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001752 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001753 " [WR_REL_PARAM]: 0x%02x\n", reg);
1754 if (reg & 0x01)
1755 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1756 if (reg & 0x04)
1757 printf(" Device supports the enhanced def. of reliable "
1758 "write\n");
1759
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001760 /* sanitize_start ext_csd[165]]: not readable
1761 * bkops_start ext_csd[164]]: only writable */
1762 printf("Enable background operations handshake"
1763 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1764 printf("H/W reset function"
1765 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1766 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001767 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001768 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1769 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001770 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001771 printf(" Device support partitioning feature\n");
1772 else
1773 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001774 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001775 printf(" Device can have enhanced tech.\n");
1776 else
1777 printf(" Device cannot have enhanced tech.\n");
1778
Oliver Metz11f2cea2013-09-23 08:40:52 +02001779 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001780 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1781 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1782
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001783 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001784 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001785 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1786 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001787 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001788
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001789 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001790 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001791 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001792 printf("Partitioning Setting"
1793 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001794 reg);
1795 if (reg)
1796 printf(" Device partition setting complete\n");
1797 else
1798 printf(" Device partition setting NOT complete\n");
1799
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001800 printf("General Purpose Partition Size\n"
1801 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1802 (ext_csd[153] << 8) | ext_csd[152]);
1803 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1804 (ext_csd[150] << 8) | ext_csd[149]);
1805 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1806 (ext_csd[147] << 8) | ext_csd[146]);
1807 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1808 (ext_csd[144] << 8) | ext_csd[143]);
1809
Oliver Metz11f2cea2013-09-23 08:40:52 +02001810 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001811 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1812 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001813 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001814 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1815 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001816 get_hc_erase_grp_size(ext_csd) *
1817 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001818
Oliver Metz11f2cea2013-09-23 08:40:52 +02001819 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001820 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1821 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1822 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001823 printf("Enhanced User Data Start Address"
jason.zeng1c553062018-03-27 02:17:10 -04001824 " [ENH_START_ADDR]: 0x%08x\n", regl);
1825 printf(" i.e. %llu bytes offset\n", (is_blockaddresed(ext_csd) ?
1826 512ll : 1ll) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001827
1828 /* A441]: reserved [135] */
1829 printf("Bad Block Management mode"
1830 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1831 /* A441: reserved [133:0] */
1832 }
1833 /* B45 */
1834 if (ext_csd_rev >= 6) {
1835 int j;
1836 /* tcase_support ext_csd[132] not readable */
1837 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1838 ext_csd[131]);
1839 printf("Program CID/CSD in DDR mode support"
1840 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1841 ext_csd[130]);
1842
1843 for (j = 127; j >= 64; j--)
1844 printf("Vendor Specific Fields"
1845 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1846 j, ext_csd[j]);
1847
Gwendal Grignoue966e672014-07-07 14:03:13 -07001848 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001849 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001850 reg);
1851 if (reg == 0x00)
1852 printf(" i.e. 512 B\n");
1853 else if (reg == 0x01)
1854 printf(" i.e. 4 KiB\n");
1855 else
1856 printf(" i.e. Reserved\n");
1857
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001858 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1859 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001860 reg = ext_csd[61];
1861 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1862 if (reg == 0x00)
1863 printf(" i.e. 512 B\n");
1864 else if (reg == 0x01)
1865 printf(" i.e. 4 KiB\n");
1866 else
1867 printf(" i.e. Reserved\n");
1868
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001869 printf("1st initialization after disabling sector"
1870 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1871 ext_csd[60]);
1872 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1873 ext_csd[59]);
1874 printf("Number of addressed group to be Released"
1875 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1876 printf("Exception events control"
1877 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1878 (ext_csd[57] << 8) | ext_csd[56]);
1879 printf("Exception events status"
1880 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1881 (ext_csd[55] << 8) | ext_csd[54]);
1882 printf("Extended Partitions Attribute"
1883 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1884 (ext_csd[53] << 8) | ext_csd[52]);
1885
1886 for (j = 51; j >= 37; j--)
1887 printf("Context configuration"
1888 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1889
1890 printf("Packed command status"
1891 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1892 printf("Packed command failure index"
1893 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1894 printf("Power Off Notification"
1895 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001896 printf("Control to turn the Cache ON/OFF"
1897 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001898 /* flush_cache ext_csd[32] not readable */
1899 /*Reserved [31:0] */
1900 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001901 if (ext_csd_rev >= 7) {
1902 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1903 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1904 ext_csd[29]);
1905
1906 reg = ext_csd[26];
1907 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1908 switch (reg) {
1909 case 0x00:
1910 printf(" Success\n");
1911 break;
1912 case 0x10:
1913 printf(" General error\n");
1914 break;
1915 case 0x11:
1916 printf(" Firmware install error\n");
1917 break;
1918 case 0x12:
1919 printf(" Error in downloading firmware\n");
1920 break;
1921 default:
1922 printf(" Reserved\n");
1923 }
1924 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1925 " %d sector size\n",
1926 get_word_from_ext_csd(&ext_csd[22]));
1927 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1928 " %d sector size\n",
1929 get_word_from_ext_csd(&ext_csd[18]));
1930 printf("Product state awareness enablement"
1931 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1932 ext_csd[17]);
1933 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1934 ext_csd[16]);
1935 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001936
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05001937 if (ext_csd_rev >= 7) {
Xingyu Wu7be960e2020-12-29 17:34:36 +08001938 printf("eMMC Firmware Version: ");
1939 for (int i = EXT_CSD_FIRMWARE_VERSION; i < EXT_CSD_PRE_EOL_INFO; i++) {
1940 char c = ext_csd[i];
1941
1942 if (isprint(c))
1943 printf("%c", c);
1944 else if (c != 0)
1945 printf("\\x%02x", c);
1946 }
1947 printf("\n");
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001948 printf("eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x%02x\n",
1949 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]);
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001950 printf("eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x%02x\n",
1951 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]);
Alexander Steincce3d882017-03-20 14:43:00 +01001952 printf("eMMC Pre EOL information [EXT_CSD_PRE_EOL_INFO]: 0x%02x\n",
1953 ext_csd[EXT_CSD_PRE_EOL_INFO]);
1954 }
1955
Adrian Hunterbb1600b2016-06-10 11:28:59 +03001956 if (ext_csd_rev >= 8) {
1957 printf("Command Queue Support [CMDQ_SUPPORT]: 0x%02x\n",
1958 ext_csd[EXT_CSD_CMDQ_SUPPORT]);
1959 printf("Command Queue Depth [CMDQ_DEPTH]: %u\n",
1960 (ext_csd[EXT_CSD_CMDQ_DEPTH] & 0x1f) + 1);
1961 printf("Command Enabled [CMDQ_MODE_EN]: 0x%02x\n",
1962 ext_csd[EXT_CSD_CMDQ_MODE_EN]);
1963 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001964out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001965 return ret;
1966}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001967
Nick Sanders9d57aa72014-03-05 21:38:54 -08001968int do_dump_extcsd(int nargs, char **argv)
1969{
1970 __u8 ext_csd[EXT_CSD_SIZE];
1971 int fd, ret;
1972 char *device;
1973 int i, j;
1974
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001975 if (nargs != 2) {
1976 fprintf(stderr, "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n");
1977 exit(1);
1978 }
Nick Sanders9d57aa72014-03-05 21:38:54 -08001979 device = argv[1];
1980
1981 fd = open(device, O_RDWR);
1982 if (fd < 0) {
1983 perror("Failed to open mmc device");
1984 exit(1);
1985 }
1986
1987 ret = read_extcsd(fd, ext_csd);
1988 if (ret) {
1989 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1990 exit(1);
1991 }
1992
1993 /* Dump all bytes so that any undecoded or proprietary registers */
1994 /* can be acessed. */
1995 printf("EXT_CSD binary dump:\n");
1996 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1997 printf(" %3d: %3x: ", i, i);
1998 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1999 printf(" %02x", ext_csd[i+j]);
2000 }
2001 printf("\n");
2002 }
2003
2004 return ret;
2005}
2006
Yaniv Gardi21bb4732013-05-26 13:25:33 -04002007int do_sanitize(int nargs, char **argv)
2008{
2009 int fd, ret;
2010 char *device;
2011
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002012 if (nargs != 2) {
2013 fprintf(stderr, "Usage: mmc sanitize </path/to/mmcblkX>\n");
2014 exit(1);
2015 }
Yaniv Gardi21bb4732013-05-26 13:25:33 -04002016
2017 device = argv[1];
2018
2019 fd = open(device, O_RDWR);
2020 if (fd < 0) {
2021 perror("open");
2022 exit(1);
2023 }
2024
2025 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
2026 if (ret) {
2027 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2028 1, EXT_CSD_SANITIZE_START, device);
2029 exit(1);
2030 }
2031
2032 return ret;
2033
2034}
2035
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002036enum blockprotect_mode {
2037 BLOCKPROTECT_TEMPORARY = 0,
2038 BLOCKPROTECT_POWERON,
2039 BLOCKPROTECT_PERMANENT,
2040};
2041
2042int write_blockprotect(int fd, __u32 sector, int enable)
2043{
2044 struct mmc_ioc_cmd cmd;
2045 int ret;
2046
2047 memset(&cmd, 0, sizeof(cmd));
2048 cmd.write_flag = 1;
2049 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
2050 cmd.arg = sector;
2051 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2052
2053 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
2054 if (ret)
2055 perror("SET/CLR_WRITE_PROT command");
2056 return ret;
2057}
2058
2059int do_blockprotect_enable(int nargs, char **argv)
2060{
2061 __u8 ext_csd[EXT_CSD_SIZE];
2062 __u8 user_wp;
2063 __u32 sector;
2064 char *end;
2065 int ret, fd;
2066 int arg_index = 0;
2067 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
2068
2069 if (nargs > 0 && !strcmp(argv[1], "-r")) {
2070 arg_index++;
2071 mode = BLOCKPROTECT_POWERON;
2072 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
2073 arg_index++;
2074 mode = BLOCKPROTECT_PERMANENT;
2075 }
2076
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002077 if (nargs != 3 + arg_index) {
2078 fprintf(stderr, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n");
2079 exit(1);
2080 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002081 sector = strtoul(argv[2 + arg_index], &end, 0);
2082 if (*end != '\0') {
2083 fprintf(stderr, "Not a block number: %s\n",
2084 argv[2 + arg_index]);
2085 exit(1);
2086 }
2087
2088 fd = open(argv[1 + arg_index], O_RDWR);
2089 if (fd < 0) {
2090 perror("open");
2091 exit(1);
2092 }
2093
2094 if (read_extcsd(fd, ext_csd))
2095 exit(1);
2096
2097 user_wp = ext_csd[EXT_CSD_USER_WP];
2098 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2099 if (mode == BLOCKPROTECT_POWERON)
2100 user_wp |= EXT_CSD_US_PWR_WP_EN;
2101 else if (mode == BLOCKPROTECT_PERMANENT)
2102 user_wp |= EXT_CSD_US_PERM_WP_EN;
2103
2104 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
2105 if (ret) {
2106 perror("update EXT_CSD[USER_WP]");
2107 exit(1);
2108 }
2109
2110 usleep(INTER_COMMAND_GAP_US);
2111
2112 ret = write_blockprotect(fd, sector, 1);
2113
2114 usleep(INTER_COMMAND_GAP_US);
2115
2116 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2117 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
2118 perror("reset EXT_CSD[USER_WP]");
2119 if (!ret)
2120 ret = -1;
2121 }
2122
2123 return ret;
2124}
2125
2126int do_blockprotect_disable(int nargs, char **argv)
2127{
2128 __u32 sector;
2129 char *end;
2130 int fd;
2131
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002132 if (nargs != 3) {
2133 fprintf(stderr, "Usage: mmc blockprotect disable <device> <write protect block>\n");
2134 exit(1);
2135 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002136 sector = strtoul(argv[2], &end, 0);
2137 if (*end != '\0') {
2138 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2139 exit(1);
2140 }
2141
2142
2143 fd = open(argv[1], O_RDWR);
2144 if (fd < 0) {
2145 perror("open");
2146 exit(1);
2147 }
2148
2149 return write_blockprotect(fd, sector, 0);
2150}
2151
2152int do_blockprotect_read(int nargs, char **argv)
2153{
2154 __u8 wp_bits[8];
2155 __u32 sector;
2156 char *end;
2157 int fd;
2158 struct mmc_ioc_cmd cmd;
2159
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002160 if (nargs != 3) {
2161 fprintf(stderr, "Usage: mmc blockprotect read <device> <write protect block>\n");
2162 exit(1);
2163 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002164 fd = open(argv[1], O_RDWR);
2165 if (fd < 0) {
2166 perror("open");
2167 exit(1);
2168 }
2169
2170 sector = strtoul(argv[2], &end, 0);
2171 if (*end != '\0') {
2172 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2173 exit(1);
2174 }
2175
2176 memset(&cmd, 0, sizeof(cmd));
2177 cmd.write_flag = 0;
2178 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
2179 cmd.arg = sector;
2180 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2181 cmd.blksz = sizeof(wp_bits);
2182 cmd.blocks = 1;
2183 mmc_ioc_cmd_set_data(cmd, wp_bits);
2184
2185 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
2186 perror("SEND_WRITE_PROT_TYPE command");
2187 exit(1);
2188 }
2189
2190 printf("Sector %u write protection: ", sector);
2191 switch (wp_bits[7] & 3) {
2192 case 0:
2193 printf("NONE\n");
2194 break;
2195 case 1:
2196 printf("TEMPORARY\n");
2197 break;
2198 case 2:
2199 printf("POWER-ON\n");
2200 break;
2201 case 3:
2202 printf("PERMANENT\n");
2203 break;
2204 }
2205
2206 return 0;
2207}
2208
2209int do_blockprotect_info(int nargs, char **argv)
2210{
2211 __u8 ext_csd[EXT_CSD_SIZE];
2212 __u8 user_wp;
2213 int fd, wp_sz, erase_sz;
2214
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002215 if (nargs != 2) {
2216 fprintf(stderr, "Usage: mmc blockprotect info <device>\n");
2217 exit(1);
2218 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002219 fd = open(argv[1], O_RDWR);
2220 if (fd < 0) {
2221 perror("open");
2222 exit(1);
2223 }
2224
2225 if (read_extcsd(fd, ext_csd))
2226 exit(1);
2227
2228 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
2229 fprintf(stderr, "Block protection commands not supported: "
2230 "CLASS_6_CTRL set.\n");
2231 exit(1);
2232 }
2233
2234 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
2235 fprintf(stderr, "Block protection commands not supported: "
2236 "high-capacity sizes not enabled.\n");
2237 exit(1);
2238 }
2239
2240 wp_sz = get_hc_wp_grp_size(ext_csd);
2241 erase_sz = get_hc_erase_grp_size(ext_csd);
2242
2243 if (erase_sz == 0 || wp_sz == 0) {
2244 fprintf(stderr, "Block protection commands not supported: "
2245 "no high-capacity size for erase or WP blocks.\n");
2246 exit(1);
2247 }
2248
2249 printf("Write protect block size in sectors: %d\n",
2250 erase_sz * wp_sz * 1024);
2251
2252 user_wp = ext_csd[EXT_CSD_USER_WP];
2253 printf("Permanent write protection: %s\n",
2254 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
2255 printf("Power-on write protection: %s\n",
2256 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
2257
2258 return 0;
2259}
2260
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002261static const char* const mmc_ffu_hack_names[] = {
2262 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
2263};
2264
Gwendal Grignou771984c2014-07-01 12:46:18 -07002265int do_emmc50_ffu (int nargs, char **argv)
2266{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002267 int fd, ret, i, argc=1, ffu_hack=0;
2268 char *device, *type, *path;
2269 __u64 value;
2270 union {
2271 __u8 data[FFU_DATA_SIZE];
2272 struct mmc_ffu_args ffu_args;
2273 } ffu_data;
2274 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002275 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07002276
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002277 while (!strcmp("-k", argv[argc])) {
2278 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
2279 if (ret < 1) {
2280 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
2281 exit(1);
2282 }
2283 for (i = 0; i < MMC_HACK_LEN; i++) {
2284 if (!strcmp(type, mmc_ffu_hack_names[i])) {
2285 ffu_args->hack[ffu_hack].type = i;
2286 if (ret == 2) {
2287 ffu_args->hack[ffu_hack].value = value;
2288 }
2289 ffu_hack++;
2290 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
2291 sizeof(struct mmc_ffu_args) >
2292 FFU_DATA_SIZE) {
2293 fprintf(stderr, "Too many %d hacks",
2294 ffu_hack);
2295 exit(1);
2296 }
2297 break;
2298 }
2299 }
2300 if (i == MMC_HACK_LEN) {
2301 fprintf(stderr, "Hack type %s not found\n", type);
2302 fprintf(stderr, "Supported types are: ");
2303 for (i = 0; i < MMC_HACK_LEN; i++)
2304 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
2305 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07002306
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002307 exit(1);
2308 }
2309 free(type);
2310 argc++;
2311 }
2312 ffu_args->hack_nb = ffu_hack;
2313
2314 path = argv[argc++];
2315 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07002316 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
2317 exit(1);
2318 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002319 strcpy(ffu_args->name, path);
2320 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07002321 fd = open(device, O_RDWR);
2322 if (fd < 0) {
2323 perror("open");
2324 exit(1);
2325 }
2326
Gwendal Grignou0f757342014-10-16 16:52:46 -07002327 /* prepare and send ioctl */
2328 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
2329 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002330 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002331 mmc_ioc_cmd.blocks = 1;
2332 mmc_ioc_cmd.arg = 0;
2333 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2334 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002335 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07002336 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07002337 if (ret) {
2338 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
2339 exit(1);
2340 }
2341
2342 close(fd);
2343 return 0;
2344}
2345
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002346#define DO_IO(func, fd, buf, nbyte) \
2347 ({ \
2348 ssize_t ret = 0, r; \
2349 do { \
2350 r = func(fd, buf + ret, nbyte - ret); \
2351 if (r < 0 && errno != EINTR) { \
2352 ret = -1; \
2353 break; \
2354 } \
2355 else if (r > 0) \
2356 ret += r; \
2357 } while (r != 0 && (size_t)ret != nbyte); \
2358 \
2359 ret; \
2360 })
2361
2362enum rpmb_op_type {
2363 MMC_RPMB_WRITE_KEY = 0x01,
2364 MMC_RPMB_READ_CNT = 0x02,
2365 MMC_RPMB_WRITE = 0x03,
2366 MMC_RPMB_READ = 0x04,
2367
2368 /* For internal usage only, do not use it directly */
2369 MMC_RPMB_READ_RESP = 0x05
2370};
2371
2372struct rpmb_frame {
2373 u_int8_t stuff[196];
2374 u_int8_t key_mac[32];
2375 u_int8_t data[256];
2376 u_int8_t nonce[16];
2377 u_int32_t write_counter;
2378 u_int16_t addr;
2379 u_int16_t block_count;
2380 u_int16_t result;
2381 u_int16_t req_resp;
2382};
2383
2384/* Performs RPMB operation.
2385 *
2386 * @fd: RPMB device on which we should perform ioctl command
2387 * @frame_in: input RPMB frame, should be properly inited
2388 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
2389 * result and req_resp for output frame.
2390 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
2391 * in the other cases -EINVAL will be returned.
2392 */
2393static int do_rpmb_op(int fd,
2394 const struct rpmb_frame *frame_in,
2395 struct rpmb_frame *frame_out,
2396 unsigned int out_cnt)
2397{
2398 int err;
2399 u_int16_t rpmb_type;
2400
2401 struct mmc_ioc_cmd ioc = {
2402 .arg = 0x0,
2403 .blksz = 512,
2404 .blocks = 1,
2405 .write_flag = 1,
2406 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
2407 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
2408 .data_ptr = (uintptr_t)frame_in
2409 };
2410
2411 if (!frame_in || !frame_out || !out_cnt)
2412 return -EINVAL;
2413
2414 rpmb_type = be16toh(frame_in->req_resp);
2415
2416 switch(rpmb_type) {
2417 case MMC_RPMB_WRITE:
2418 case MMC_RPMB_WRITE_KEY:
2419 if (out_cnt != 1) {
2420 err = -EINVAL;
2421 goto out;
2422 }
2423
2424 /* Write request */
2425 ioc.write_flag |= (1<<31);
2426 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2427 if (err < 0) {
2428 err = -errno;
2429 goto out;
2430 }
2431
2432 /* Result request */
2433 memset(frame_out, 0, sizeof(*frame_out));
2434 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2435 ioc.write_flag = 1;
2436 ioc.data_ptr = (uintptr_t)frame_out;
2437 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2438 if (err < 0) {
2439 err = -errno;
2440 goto out;
2441 }
2442
2443 /* Get response */
2444 ioc.write_flag = 0;
2445 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2446 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2447 if (err < 0) {
2448 err = -errno;
2449 goto out;
2450 }
2451
2452 break;
2453 case MMC_RPMB_READ_CNT:
2454 if (out_cnt != 1) {
2455 err = -EINVAL;
2456 goto out;
2457 }
2458 /* fall through */
2459
2460 case MMC_RPMB_READ:
2461 /* Request */
2462 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2463 if (err < 0) {
2464 err = -errno;
2465 goto out;
2466 }
2467
2468 /* Get response */
2469 ioc.write_flag = 0;
2470 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2471 ioc.blocks = out_cnt;
2472 ioc.data_ptr = (uintptr_t)frame_out;
2473 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2474 if (err < 0) {
2475 err = -errno;
2476 goto out;
2477 }
2478
2479 break;
2480 default:
2481 err = -EINVAL;
2482 goto out;
2483 }
2484
2485out:
2486 return err;
2487}
2488
2489int do_rpmb_write_key(int nargs, char **argv)
2490{
2491 int ret, dev_fd, key_fd;
2492 struct rpmb_frame frame_in = {
2493 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2494 }, frame_out;
2495
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002496 if (nargs != 3) {
2497 fprintf(stderr, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n");
2498 exit(1);
2499 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002500
2501 dev_fd = open(argv[1], O_RDWR);
2502 if (dev_fd < 0) {
2503 perror("device open");
2504 exit(1);
2505 }
2506
2507 if (0 == strcmp(argv[2], "-"))
2508 key_fd = STDIN_FILENO;
2509 else {
2510 key_fd = open(argv[2], O_RDONLY);
2511 if (key_fd < 0) {
2512 perror("can't open key file");
2513 exit(1);
2514 }
2515 }
2516
2517 /* Read the auth key */
2518 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2519 if (ret < 0) {
2520 perror("read the key");
2521 exit(1);
2522 } else if (ret != sizeof(frame_in.key_mac)) {
2523 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2524 (unsigned long)sizeof(frame_in.key_mac),
2525 ret);
2526 exit(1);
2527 }
2528
2529 /* Execute RPMB op */
2530 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2531 if (ret != 0) {
2532 perror("RPMB ioctl failed");
2533 exit(1);
2534 }
2535
2536 /* Check RPMB response */
2537 if (frame_out.result != 0) {
2538 printf("RPMB operation failed, retcode 0x%04x\n",
2539 be16toh(frame_out.result));
2540 exit(1);
2541 }
2542
2543 close(dev_fd);
2544 if (key_fd != STDIN_FILENO)
2545 close(key_fd);
2546
2547 return ret;
2548}
2549
2550int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2551{
2552 int ret;
2553 struct rpmb_frame frame_in = {
2554 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2555 }, frame_out;
2556
2557 /* Execute RPMB op */
2558 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2559 if (ret != 0) {
2560 perror("RPMB ioctl failed");
2561 exit(1);
2562 }
2563
2564 /* Check RPMB response */
2565 if (frame_out.result != 0)
2566 return be16toh(frame_out.result);
2567
2568 *cnt = be32toh(frame_out.write_counter);
2569
2570 return 0;
2571}
2572
2573int do_rpmb_read_counter(int nargs, char **argv)
2574{
2575 int ret, dev_fd;
2576 unsigned int cnt;
2577
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002578 if (nargs != 2) {
2579 fprintf(stderr, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n");
2580 exit(1);
2581 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002582
2583 dev_fd = open(argv[1], O_RDWR);
2584 if (dev_fd < 0) {
2585 perror("device open");
2586 exit(1);
2587 }
2588
2589 ret = rpmb_read_counter(dev_fd, &cnt);
2590
2591 /* Check RPMB response */
2592 if (ret != 0) {
2593 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2594 exit(1);
2595 }
2596
2597 close(dev_fd);
2598
2599 printf("Counter value: 0x%08x\n", cnt);
2600
2601 return ret;
2602}
2603
2604int do_rpmb_read_block(int nargs, char **argv)
2605{
2606 int i, ret, dev_fd, data_fd, key_fd = -1;
2607 uint16_t addr, blocks_cnt;
2608 unsigned char key[32];
2609 struct rpmb_frame frame_in = {
2610 .req_resp = htobe16(MMC_RPMB_READ),
2611 }, *frame_out_p;
2612
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002613 if (nargs != 5 && nargs != 6) {
2614 fprintf(stderr, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n");
2615 exit(1);
2616 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002617
2618 dev_fd = open(argv[1], O_RDWR);
2619 if (dev_fd < 0) {
2620 perror("device open");
2621 exit(1);
2622 }
2623
2624 /* Get block address */
2625 errno = 0;
2626 addr = strtol(argv[2], NULL, 0);
2627 if (errno) {
2628 perror("incorrect address");
2629 exit(1);
2630 }
2631 frame_in.addr = htobe16(addr);
2632
2633 /* Get blocks count */
2634 errno = 0;
2635 blocks_cnt = strtol(argv[3], NULL, 0);
2636 if (errno) {
2637 perror("incorrect blocks count");
2638 exit(1);
2639 }
2640
2641 if (!blocks_cnt) {
2642 printf("please, specify valid blocks count number\n");
2643 exit(1);
2644 }
2645
2646 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2647 if (!frame_out_p) {
2648 printf("can't allocate memory for RPMB outer frames\n");
2649 exit(1);
2650 }
2651
2652 /* Write 256b data */
2653 if (0 == strcmp(argv[4], "-"))
2654 data_fd = STDOUT_FILENO;
2655 else {
2656 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2657 S_IRUSR | S_IWUSR);
2658 if (data_fd < 0) {
2659 perror("can't open output file");
2660 exit(1);
2661 }
2662 }
2663
2664 /* Key is specified */
2665 if (nargs == 6) {
2666 if (0 == strcmp(argv[5], "-"))
2667 key_fd = STDIN_FILENO;
2668 else {
2669 key_fd = open(argv[5], O_RDONLY);
2670 if (key_fd < 0) {
2671 perror("can't open input key file");
2672 exit(1);
2673 }
2674 }
2675
2676 ret = DO_IO(read, key_fd, key, sizeof(key));
2677 if (ret < 0) {
2678 perror("read the key data");
2679 exit(1);
2680 } else if (ret != sizeof(key)) {
2681 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2682 (unsigned long)sizeof(key),
2683 ret);
2684 exit(1);
2685 }
2686 }
2687
2688 /* Execute RPMB op */
2689 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2690 if (ret != 0) {
2691 perror("RPMB ioctl failed");
2692 exit(1);
2693 }
2694
2695 /* Check RPMB response */
2696 if (frame_out_p[blocks_cnt - 1].result != 0) {
2697 printf("RPMB operation failed, retcode 0x%04x\n",
2698 be16toh(frame_out_p[blocks_cnt - 1].result));
2699 exit(1);
2700 }
2701
2702 /* Do we have to verify data against key? */
2703 if (nargs == 6) {
2704 unsigned char mac[32];
2705 hmac_sha256_ctx ctx;
2706 struct rpmb_frame *frame_out = NULL;
2707
2708 hmac_sha256_init(&ctx, key, sizeof(key));
2709 for (i = 0; i < blocks_cnt; i++) {
2710 frame_out = &frame_out_p[i];
2711 hmac_sha256_update(&ctx, frame_out->data,
2712 sizeof(*frame_out) -
2713 offsetof(struct rpmb_frame, data));
2714 }
2715
2716 hmac_sha256_final(&ctx, mac, sizeof(mac));
2717
2718 /* Impossible */
2719 assert(frame_out);
2720
2721 /* Compare calculated MAC and MAC from last frame */
2722 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2723 printf("RPMB MAC missmatch\n");
2724 exit(1);
2725 }
2726 }
2727
2728 /* Write data */
2729 for (i = 0; i < blocks_cnt; i++) {
2730 struct rpmb_frame *frame_out = &frame_out_p[i];
2731 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2732 if (ret < 0) {
2733 perror("write the data");
2734 exit(1);
2735 } else if (ret != sizeof(frame_out->data)) {
2736 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2737 (unsigned long)sizeof(frame_out->data),
2738 ret);
2739 exit(1);
2740 }
2741 }
2742
2743 free(frame_out_p);
2744 close(dev_fd);
2745 if (data_fd != STDOUT_FILENO)
2746 close(data_fd);
2747 if (key_fd != -1 && key_fd != STDIN_FILENO)
2748 close(key_fd);
2749
2750 return ret;
2751}
2752
2753int do_rpmb_write_block(int nargs, char **argv)
2754{
2755 int ret, dev_fd, key_fd, data_fd;
2756 unsigned char key[32];
2757 uint16_t addr;
2758 unsigned int cnt;
2759 struct rpmb_frame frame_in = {
2760 .req_resp = htobe16(MMC_RPMB_WRITE),
2761 .block_count = htobe16(1)
2762 }, frame_out;
2763
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002764 if (nargs != 5) {
2765 fprintf(stderr, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n");
2766 exit(1);
2767 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002768
2769 dev_fd = open(argv[1], O_RDWR);
2770 if (dev_fd < 0) {
2771 perror("device open");
2772 exit(1);
2773 }
2774
2775 ret = rpmb_read_counter(dev_fd, &cnt);
2776 /* Check RPMB response */
2777 if (ret != 0) {
2778 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2779 exit(1);
2780 }
2781 frame_in.write_counter = htobe32(cnt);
2782
2783 /* Get block address */
2784 errno = 0;
2785 addr = strtol(argv[2], NULL, 0);
2786 if (errno) {
2787 perror("incorrect address");
2788 exit(1);
2789 }
2790 frame_in.addr = htobe16(addr);
2791
2792 /* Read 256b data */
2793 if (0 == strcmp(argv[3], "-"))
2794 data_fd = STDIN_FILENO;
2795 else {
2796 data_fd = open(argv[3], O_RDONLY);
2797 if (data_fd < 0) {
2798 perror("can't open input file");
2799 exit(1);
2800 }
2801 }
2802
2803 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2804 if (ret < 0) {
2805 perror("read the data");
2806 exit(1);
2807 } else if (ret != sizeof(frame_in.data)) {
2808 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2809 (unsigned long)sizeof(frame_in.data),
2810 ret);
2811 exit(1);
2812 }
2813
2814 /* Read the auth key */
2815 if (0 == strcmp(argv[4], "-"))
2816 key_fd = STDIN_FILENO;
2817 else {
2818 key_fd = open(argv[4], O_RDONLY);
2819 if (key_fd < 0) {
2820 perror("can't open key file");
2821 exit(1);
2822 }
2823 }
2824
2825 ret = DO_IO(read, key_fd, key, sizeof(key));
2826 if (ret < 0) {
2827 perror("read the key");
2828 exit(1);
2829 } else if (ret != sizeof(key)) {
2830 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2831 (unsigned long)sizeof(key),
2832 ret);
2833 exit(1);
2834 }
2835
2836 /* Calculate HMAC SHA256 */
2837 hmac_sha256(
2838 key, sizeof(key),
2839 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2840 frame_in.key_mac, sizeof(frame_in.key_mac));
2841
2842 /* Execute RPMB op */
2843 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2844 if (ret != 0) {
2845 perror("RPMB ioctl failed");
2846 exit(1);
2847 }
2848
2849 /* Check RPMB response */
2850 if (frame_out.result != 0) {
2851 printf("RPMB operation failed, retcode 0x%04x\n",
2852 be16toh(frame_out.result));
2853 exit(1);
2854 }
2855
2856 close(dev_fd);
2857 if (data_fd != STDIN_FILENO)
2858 close(data_fd);
2859 if (key_fd != STDIN_FILENO)
2860 close(key_fd);
2861
2862 return ret;
2863}
Al Cooper786418c2015-04-29 18:12:35 -04002864
2865int do_cache_ctrl(int value, int nargs, char **argv)
2866{
2867 __u8 ext_csd[512];
2868 int fd, ret;
2869 char *device;
2870
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002871 if (nargs != 2) {
2872 fprintf(stderr, "Usage: mmc cache enable </path/to/mmcblkX>\n");
2873 exit(1);
2874 }
Al Cooper786418c2015-04-29 18:12:35 -04002875
2876 device = argv[1];
2877
2878 fd = open(device, O_RDWR);
2879 if (fd < 0) {
2880 perror("open");
2881 exit(1);
2882 }
2883
2884 ret = read_extcsd(fd, ext_csd);
2885 if (ret) {
2886 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2887 exit(1);
2888 }
2889
2890 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2891 fprintf(stderr,
2892 "The CACHE option is only availabe on devices >= "
2893 "MMC 4.5 %s\n", device);
2894 exit(1);
2895 }
2896
2897 /* If the cache size is zero, this device does not have a cache */
2898 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2899 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2900 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2901 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2902 fprintf(stderr,
2903 "The CACHE option is not available on %s\n",
2904 device);
2905 exit(1);
2906 }
2907 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2908 if (ret) {
2909 fprintf(stderr,
2910 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2911 value, EXT_CSD_CACHE_CTRL, device);
2912 exit(1);
2913 }
2914
2915 return ret;
2916}
2917
2918int do_cache_en(int nargs, char **argv)
2919{
2920 return do_cache_ctrl(1, nargs, argv);
2921}
2922
2923int do_cache_dis(int nargs, char **argv)
2924{
2925 return do_cache_ctrl(0, nargs, argv);
2926}
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002927
2928int do_ffu(int nargs, char **argv)
2929{
2930#ifndef MMC_IOC_MULTI_CMD
2931 fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
2932 " support, needed by FFU.\n");
2933 exit(1);
2934#else
2935 int dev_fd, img_fd;
2936 int sect_done = 0, retry = 3, ret = -EINVAL;
2937 unsigned int sect_size;
2938 __u8 ext_csd[EXT_CSD_SIZE];
2939 __u8 *buf;
2940 __u32 arg;
2941 off_t fw_size;
2942 ssize_t chunk_size;
2943 char *device;
2944 struct mmc_ioc_multi_cmd *multi_cmd;
2945
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002946 if (nargs != 3) {
2947 fprintf(stderr, "Usage: ffu <image name> </path/to/mmcblkX> \n");
2948 exit(1);
2949 }
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002950
2951 device = argv[2];
2952 dev_fd = open(device, O_RDWR);
2953 if (dev_fd < 0) {
2954 perror("device open failed");
2955 exit(1);
2956 }
2957 img_fd = open(argv[1], O_RDONLY);
2958 if (img_fd < 0) {
2959 perror("image open failed");
2960 close(dev_fd);
2961 exit(1);
2962 }
2963
2964 buf = malloc(512);
2965 multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
2966 3 * sizeof(struct mmc_ioc_cmd));
2967 if (!buf || !multi_cmd) {
2968 perror("failed to allocate memory");
2969 goto out;
2970 }
2971
2972 ret = read_extcsd(dev_fd, ext_csd);
2973 if (ret) {
2974 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2975 goto out;
2976 }
2977
2978 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
2979 fprintf(stderr,
2980 "The FFU feature is only available on devices >= "
2981 "MMC 5.0, not supported in %s\n", device);
2982 goto out;
2983 }
2984
2985 if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
2986 fprintf(stderr, "FFU is not supported in %s\n", device);
2987 goto out;
2988 }
2989
2990 if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
2991 fprintf(stderr, "Firmware update was disabled in %s\n", device);
2992 goto out;
2993 }
2994
2995 fw_size = lseek(img_fd, 0, SEEK_END);
2996
2997 if (fw_size == 0) {
2998 fprintf(stderr, "Firmware image is empty");
2999 goto out;
3000 }
3001
3002 sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
3003 if (fw_size % sect_size) {
3004 fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
3005 goto out;
3006 }
3007
3008 /* set CMD ARG */
3009 arg = ext_csd[EXT_CSD_FFU_ARG_0] |
3010 ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
3011 ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
3012 ext_csd[EXT_CSD_FFU_ARG_3] << 24;
3013
3014 /* prepare multi_cmd to be sent */
3015 multi_cmd->num_of_cmds = 3;
3016
3017 /* put device into ffu mode */
3018 multi_cmd->cmds[0].opcode = MMC_SWITCH;
3019 multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
3020 (EXT_CSD_MODE_CONFIG << 16) |
3021 (EXT_CSD_FFU_MODE << 8) |
3022 EXT_CSD_CMD_SET_NORMAL;
3023 multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
3024 multi_cmd->cmds[0].write_flag = 1;
3025
3026 /* send image chunk */
3027 multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
3028 multi_cmd->cmds[1].blksz = sect_size;
3029 multi_cmd->cmds[1].blocks = 1;
3030 multi_cmd->cmds[1].arg = arg;
3031 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
3032 multi_cmd->cmds[1].write_flag = 1;
3033 mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);
3034
3035 /* return device into normal mode */
3036 multi_cmd->cmds[2].opcode = MMC_SWITCH;
3037 multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
3038 (EXT_CSD_MODE_CONFIG << 16) |
3039 (EXT_CSD_NORMAL_MODE << 8) |
3040 EXT_CSD_CMD_SET_NORMAL;
3041 multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
3042 multi_cmd->cmds[2].write_flag = 1;
3043
3044do_retry:
3045 /* read firmware chunk */
3046 lseek(img_fd, 0, SEEK_SET);
3047 chunk_size = read(img_fd, buf, 512);
3048
3049 while (chunk_size > 0) {
3050 /* send ioctl with multi-cmd */
3051 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
3052
3053 if (ret) {
3054 perror("Multi-cmd ioctl");
3055 /* In case multi-cmd ioctl failed before exiting from ffu mode */
3056 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
3057 goto out;
3058 }
3059
3060 ret = read_extcsd(dev_fd, ext_csd);
3061 if (ret) {
3062 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3063 goto out;
3064 }
3065
3066 /* Test if we need to restart the download */
3067 sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
3068 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
3069 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
3070 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
3071 /* By spec, host should re-start download from the first sector if sect_done is 0 */
3072 if (sect_done == 0) {
3073 if (retry > 0) {
3074 retry--;
3075 fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
3076 goto do_retry;
3077 }
3078 fprintf(stderr, "Programming failed! Aborting...\n");
3079 goto out;
3080 } else {
3081 fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
3082 }
3083
3084 /* read the next firmware chunk (if any) */
3085 chunk_size = read(img_fd, buf, 512);
3086 }
3087
3088 if ((sect_done * sect_size) == fw_size) {
3089 fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
3090 fprintf(stderr, "Programming finished with status %d \n", ret);
3091 }
3092 else {
3093 fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
3094 goto out;
3095 }
3096
3097 /* check mode operation for ffu install*/
3098 if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
3099 fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
3100 } else {
3101 fprintf(stderr, "Installing firmware on %s...\n", device);
3102 /* Re-enter ffu mode and install the firmware */
3103 multi_cmd->num_of_cmds = 2;
3104
3105 /* set ext_csd to install mode */
3106 multi_cmd->cmds[1].opcode = MMC_SWITCH;
3107 multi_cmd->cmds[1].blksz = 0;
3108 multi_cmd->cmds[1].blocks = 0;
3109 multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
3110 (EXT_CSD_MODE_OPERATION_CODES << 16) |
3111 (EXT_CSD_FFU_INSTALL << 8) |
3112 EXT_CSD_CMD_SET_NORMAL;
3113 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
3114 multi_cmd->cmds[1].write_flag = 1;
3115
3116 /* send ioctl with multi-cmd */
3117 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
3118
3119 if (ret) {
3120 perror("Multi-cmd ioctl failed setting install mode");
3121 /* In case multi-cmd ioctl failed before exiting from ffu mode */
3122 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
3123 goto out;
3124 }
3125
3126 ret = read_extcsd(dev_fd, ext_csd);
3127 if (ret) {
3128 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3129 goto out;
3130 }
3131
3132 /* return status */
3133 ret = ext_csd[EXT_CSD_FFU_STATUS];
3134 if (ret) {
3135 fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
3136 goto out;
3137 } else {
3138 fprintf(stderr, "FFU finished successfully\n");
3139 }
3140 }
3141
3142out:
3143 free(buf);
3144 free(multi_cmd);
3145 close(img_fd);
3146 close(dev_fd);
3147 return ret;
3148#endif
3149}