blob: 93f1fef87221cb319623c52a8966e0b30e3ecf34 [file] [log] [blame]
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
Avi Shchislowskidc7ab962016-03-08 14:22:41 -050015 *
16 * Modified to add field firmware update support,
17 * those modifications are Copyright (c) 2016 SanDisk Corp.
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050018 */
19
Gwendal Grignou771984c2014-07-01 12:46:18 -070020#include <errno.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050021#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/ioctl.h>
Gwendal Grignou771984c2014-07-01 12:46:18 -070025#include <sys/param.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050026#include <sys/types.h>
27#include <dirent.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <libgen.h>
32#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050033#include <ctype.h>
Roman Peniaev023cc7c2014-08-12 23:25:45 +090034#include <errno.h>
35#include <stdint.h>
36#include <assert.h>
Al Cooper1b7f5d72016-06-07 16:35:46 -040037#include <linux/fs.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050038
39#include "mmc.h"
40#include "mmc_cmds.h"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080041#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050042
Nick Sanders9d57aa72014-03-05 21:38:54 -080043#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080044#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080045#define CID_SIZE 16
46
Julius Wernerbcc3e2e2016-04-21 16:53:02 -070047/* Sending several commands too close together seems to cause timeouts. */
48#define INTER_COMMAND_GAP_US (50 * 1000)
49
Roman Peniaev023cc7c2014-08-12 23:25:45 +090050#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080051
Al Cooper1b7f5d72016-06-07 16:35:46 -040052#define WP_BLKS_PER_QUERY 32
53
54#define USER_WP_PERM_PSWD_DIS 0x80
55#define USER_WP_CD_PERM_WP_DIS 0x40
56#define USER_WP_US_PERM_WP_DIS 0x10
57#define USER_WP_US_PWR_WP_DIS 0x08
58#define USER_WP_US_PERM_WP_EN 0x04
59#define USER_WP_US_PWR_WP_EN 0x01
60#define USER_WP_CLEAR (USER_WP_US_PERM_WP_DIS | USER_WP_US_PWR_WP_DIS \
61 | USER_WP_US_PERM_WP_EN | USER_WP_US_PWR_WP_EN)
62
63#define WPTYPE_NONE 0
64#define WPTYPE_TEMP 1
65#define WPTYPE_PWRON 2
66#define WPTYPE_PERM 3
67
68
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050069int read_extcsd(int fd, __u8 *ext_csd)
70{
71 int ret = 0;
72 struct mmc_ioc_cmd idata;
73 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080074 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050075 idata.write_flag = 0;
76 idata.opcode = MMC_SEND_EXT_CSD;
77 idata.arg = 0;
78 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080079 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050080 idata.blocks = 1;
81 mmc_ioc_cmd_set_data(idata, ext_csd);
82
83 ret = ioctl(fd, MMC_IOC_CMD, &idata);
84 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080085 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050086
87 return ret;
88}
89
90int write_extcsd_value(int fd, __u8 index, __u8 value)
91{
92 int ret = 0;
93 struct mmc_ioc_cmd idata;
94
95 memset(&idata, 0, sizeof(idata));
96 idata.write_flag = 1;
97 idata.opcode = MMC_SWITCH;
98 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
99 (index << 16) |
100 (value << 8) |
101 EXT_CSD_CMD_SET_NORMAL;
102 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
103
104 ret = ioctl(fd, MMC_IOC_CMD, &idata);
105 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -0800106 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500107
108 return ret;
109}
110
Ben Gardiner27c357d2013-05-30 17:12:47 -0400111int send_status(int fd, __u32 *response)
112{
113 int ret = 0;
114 struct mmc_ioc_cmd idata;
115
116 memset(&idata, 0, sizeof(idata));
117 idata.opcode = MMC_SEND_STATUS;
118 idata.arg = (1 << 16);
119 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
120
121 ret = ioctl(fd, MMC_IOC_CMD, &idata);
122 if (ret)
123 perror("ioctl");
124
125 *response = idata.response[0];
126
127 return ret;
128}
129
Al Cooper1b7f5d72016-06-07 16:35:46 -0400130static __u32 get_size_in_blks(int fd)
131{
132 int res;
133 int size;
134
135 res = ioctl(fd, BLKGETSIZE, &size);
136 if (res) {
137 fprintf(stderr, "Error getting device size, errno: %d\n",
138 errno);
139 perror("");
140 return -1;
141 }
142 return size;
143}
144
145static int set_write_protect(int fd, __u32 blk_addr, int on_off)
146{
147 int ret = 0;
148 struct mmc_ioc_cmd idata;
149
150 memset(&idata, 0, sizeof(idata));
151 idata.write_flag = 1;
152 if (on_off)
153 idata.opcode = MMC_SET_WRITE_PROT;
154 else
155 idata.opcode = MMC_CLEAR_WRITE_PROT;
156 idata.arg = blk_addr;
157 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
158
159 ret = ioctl(fd, MMC_IOC_CMD, &idata);
160 if (ret)
161 perror("ioctl");
162
163 return ret;
164}
165
166static int send_write_protect_type(int fd, __u32 blk_addr, __u64 *group_bits)
167{
168 int ret = 0;
169 struct mmc_ioc_cmd idata;
170 __u8 buf[8];
171 __u64 bits = 0;
172 int x;
173
174 memset(&idata, 0, sizeof(idata));
175 idata.write_flag = 0;
176 idata.opcode = MMC_SEND_WRITE_PROT_TYPE;
177 idata.blksz = 8,
178 idata.blocks = 1,
179 idata.arg = blk_addr;
180 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
181 mmc_ioc_cmd_set_data(idata, buf);
182
183 ret = ioctl(fd, MMC_IOC_CMD, &idata);
184 if (ret)
185 perror("ioctl");
186 for (x = 0; x < sizeof(buf); x++)
187 bits |= (__u64)(buf[7 - x]) << (x * 8);
188 *group_bits = bits;
189 return ret;
190}
191
192static void print_writeprotect_boot_status(__u8 *ext_csd)
Chris Ballb9c7a172012-02-20 12:34:25 -0500193{
194 __u8 reg;
Al Cooper786418c2015-04-29 18:12:35 -0400195 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500196
197 /* A43: reserved [174:0] */
198 if (ext_csd_rev >= 5) {
199 printf("Boot write protection status registers"
200 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
201
202 reg = ext_csd[EXT_CSD_BOOT_WP];
203 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
204 printf(" Power ro locking: ");
205 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
206 printf("not possible\n");
207 else
208 printf("possible\n");
209
210 printf(" Permanent ro locking: ");
211 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
212 printf("not possible\n");
213 else
214 printf("possible\n");
215
216 printf(" ro lock status: ");
217 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
218 printf("locked until next power on\n");
219 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
220 printf("locked permanently\n");
221 else
222 printf("not locked\n");
223 }
224}
225
Al Cooper1b7f5d72016-06-07 16:35:46 -0400226static int get_wp_group_size_in_blks(__u8 *ext_csd, __u32 *size)
227{
228 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
229
230 if ((ext_csd_rev < 5) || (ext_csd[EXT_CSD_ERASE_GROUP_DEF] == 0))
231 return 1;
232
233 *size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
234 ext_csd[EXT_CSD_HC_WP_GRP_SIZE] * 1024;
235 return 0;
236}
237
238
239int do_writeprotect_boot_get(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500240{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800241 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500242 int fd, ret;
243 char *device;
244
Al Cooper1b7f5d72016-06-07 16:35:46 -0400245 CHECK(nargs != 2,
246 "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n",
247 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500248
249 device = argv[1];
250
251 fd = open(device, O_RDWR);
252 if (fd < 0) {
253 perror("open");
254 exit(1);
255 }
256
257 ret = read_extcsd(fd, ext_csd);
258 if (ret) {
259 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
260 exit(1);
261 }
262
Al Cooper1b7f5d72016-06-07 16:35:46 -0400263 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -0500264
265 return ret;
266}
267
Al Cooper1b7f5d72016-06-07 16:35:46 -0400268int do_writeprotect_boot_set(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500269{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800270 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500271 int fd, ret;
272 char *device;
273
Al Cooper1b7f5d72016-06-07 16:35:46 -0400274 CHECK(nargs != 2,
275 "Usage: mmc writeprotect boot set </path/to/mmcblkX>\n",
276 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500277
278 device = argv[1];
279
280 fd = open(device, O_RDWR);
281 if (fd < 0) {
282 perror("open");
283 exit(1);
284 }
285
286 ret = read_extcsd(fd, ext_csd);
287 if (ret) {
288 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
289 exit(1);
290 }
291
292 value = ext_csd[EXT_CSD_BOOT_WP] |
293 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
294 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
295 if (ret) {
296 fprintf(stderr, "Could not write 0x%02x to "
297 "EXT_CSD[%d] in %s\n",
298 value, EXT_CSD_BOOT_WP, device);
299 exit(1);
300 }
301
302 return ret;
303}
304
Al Cooper1b7f5d72016-06-07 16:35:46 -0400305static char *prot_desc[] = {
306 "No",
307 "Temporary",
308 "Power-on",
309 "Permanent"
310};
311
312static void print_wp_status(__u32 wp_sizeblks, __u32 start_group,
313 __u32 end_group, int rptype)
314{
315 printf("Write Protect Groups %d-%d (Blocks %d-%d), ",
316 start_group, end_group,
317 start_group * wp_sizeblks, ((end_group + 1) * wp_sizeblks) - 1);
318 printf("%s Write Protection\n", prot_desc[rptype]);
319}
320
321
322int do_writeprotect_user_get(int nargs, char **argv)
323{
324 __u8 ext_csd[512];
325 int fd, ret;
326 char *device;
327 int x;
328 int y = 0;
329 __u32 wp_sizeblks;
330 __u32 dev_sizeblks;
331 __u32 cnt;
332 __u64 bits;
333 __u32 wpblk;
334 __u32 last_wpblk = 0;
335 __u32 prot;
336 __u32 last_prot = -1;
337 int remain;
338
339 CHECK(nargs != 2,
340 "Usage: mmc writeprotect user get </path/to/mmcblkX>\n",
341 exit(1));
342
343 device = argv[1];
344
345 fd = open(device, O_RDWR);
346 if (fd < 0) {
347 perror("open");
348 exit(1);
349 }
350 ret = read_extcsd(fd, ext_csd);
351 if (ret) {
352 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
353 exit(1);
354 }
355
356 ret = get_wp_group_size_in_blks(ext_csd, &wp_sizeblks);
357 if (ret)
358 exit(1);
359 printf("Write Protect Group size in blocks/bytes: %d/%d\n",
360 wp_sizeblks, wp_sizeblks * 512);
361 dev_sizeblks = get_size_in_blks(fd);
362 cnt = dev_sizeblks / wp_sizeblks;
363 for (x = 0; x < cnt; x += WP_BLKS_PER_QUERY) {
364 ret = send_write_protect_type(fd, x * wp_sizeblks, &bits);
365 if (ret)
366 break;
367 remain = cnt - x;
368 if (remain > WP_BLKS_PER_QUERY)
369 remain = WP_BLKS_PER_QUERY;
370 for (y = 0; y < remain; y++) {
371 prot = (bits >> (y * 2)) & 0x3;
372 if (prot != last_prot) {
373 /* not first time */
374 if (last_prot != -1) {
375 wpblk = x + y;
376 print_wp_status(wp_sizeblks,
377 last_wpblk,
378 wpblk - 1,
379 last_prot);
380 last_wpblk = wpblk;
381 }
382 last_prot = prot;
383 }
384 }
385 }
386 if (last_wpblk != (x + y - 1))
387 print_wp_status(wp_sizeblks, last_wpblk, cnt - 1, last_prot);
388
389 return ret;
390}
391
392int do_writeprotect_user_set(int nargs, char **argv)
393{
394 __u8 ext_csd[512];
395 int fd, ret;
396 char *device;
397 int blk_start;
398 int blk_cnt;
399 __u32 wp_blks;
400 __u8 user_wp;
401 int x;
402 int wptype;
403
404 if (nargs != 5)
405 goto usage;
406 device = argv[4];
407 fd = open(device, O_RDWR);
408 if (fd < 0) {
409 perror("open");
410 exit(1);
411 }
412 if (!strcmp(argv[1], "none")) {
413 wptype = WPTYPE_NONE;
414 } else if (!strcmp(argv[1], "temp")) {
415 wptype = WPTYPE_TEMP;
416 } else if (!strcmp(argv[1], "pwron")) {
417 wptype = WPTYPE_PWRON;
418#ifdef DANGEROUS_COMMANDS_ENABLED
419 } else if (!strcmp(argv[1], "perm")) {
420 wptype = WPTYPE_PERM;
421#endif /* DANGEROUS_COMMANDS_ENABLED */
422 } else {
423 fprintf(stderr, "Error, invalid \"type\"\n");
424 goto usage;
425 }
426 ret = read_extcsd(fd, ext_csd);
427 if (ret) {
428 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
429 exit(1);
430 }
431 ret = get_wp_group_size_in_blks(ext_csd, &wp_blks);
432 if (ret) {
433 fprintf(stderr, "Operation not supported for this device\n");
434 exit(1);
435 }
436 blk_start = strtol(argv[2], NULL, 0);
437 blk_cnt = strtol(argv[3], NULL, 0);
438 if ((blk_start % wp_blks) || (blk_cnt % wp_blks)) {
439 fprintf(stderr, "<start block> and <blocks> must be a ");
440 fprintf(stderr, "multiple of the Write Protect Group (%d)\n",
441 wp_blks);
442 exit(1);
443 }
444 if (wptype != WPTYPE_NONE) {
445 user_wp = ext_csd[EXT_CSD_USER_WP];
446 user_wp &= ~USER_WP_CLEAR;
447 switch (wptype) {
448 case WPTYPE_TEMP:
449 break;
450 case WPTYPE_PWRON:
451 user_wp |= USER_WP_US_PWR_WP_EN;
452 break;
453 case WPTYPE_PERM:
454 user_wp |= USER_WP_US_PERM_WP_EN;
455 break;
456 }
457 if (user_wp != ext_csd[EXT_CSD_USER_WP]) {
458 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
459 if (ret) {
460 fprintf(stderr, "Error setting EXT_CSD\n");
461 exit(1);
462 }
463 }
464 }
465 for (x = 0; x < blk_cnt; x += wp_blks) {
466 ret = set_write_protect(fd, blk_start + x,
467 wptype != WPTYPE_NONE);
468 if (ret) {
469 fprintf(stderr,
470 "Could not set write protect for %s\n", device);
471 exit(1);
472 }
473 }
474 if (wptype != WPTYPE_NONE) {
475 ret = write_extcsd_value(fd, EXT_CSD_USER_WP,
476 ext_csd[EXT_CSD_USER_WP]);
477 if (ret) {
478 fprintf(stderr, "Error restoring EXT_CSD\n");
479 exit(1);
480 }
481 }
482 return ret;
483
484usage:
485 fprintf(stderr,
486 "Usage: mmc writeprotect user set <type><start block><blocks><device>\n");
487 exit(1);
488}
489
Saugata Dasb7e25992012-05-17 09:26:34 -0400490int do_disable_512B_emulation(int nargs, char **argv)
491{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800492 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400493 int fd, ret;
494 char *device;
495
496 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
497 device = argv[1];
498
499 fd = open(device, O_RDWR);
500 if (fd < 0) {
501 perror("open");
502 exit(1);
503 }
504
505 ret = read_extcsd(fd, ext_csd);
506 if (ret) {
507 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
508 exit(1);
509 }
510
511 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
512 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
513 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
514
515 if (native_sector_size && !data_sector_size &&
516 (wr_rel_param & EN_REL_WR)) {
517 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
518
519 if (ret) {
520 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
Tomas Melin295dd7a2016-08-29 11:45:44 -0400521 1, EXT_CSD_NATIVE_SECTOR_SIZE, device);
Saugata Dasb7e25992012-05-17 09:26:34 -0400522 exit(1);
523 }
524 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
525 } else if (native_sector_size && data_sector_size) {
526 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
527 } else {
528 printf("MMC does not support disabling 512B emulation mode.\n");
529 }
530
531 return ret;
532}
533
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200534int do_write_boot_en(int nargs, char **argv)
535{
536 __u8 ext_csd[512];
537 __u8 value = 0;
538 int fd, ret;
539 char *device;
540 int boot_area, send_ack;
541
542 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
543 "<send_ack> </path/to/mmcblkX>\n", exit(1));
544
545 /*
546 * If <send_ack> is 1, the device will send acknowledgment
547 * pattern "010" to the host when boot operation begins.
548 * If <send_ack> is 0, it won't.
549 */
550 boot_area = strtol(argv[1], NULL, 10);
551 send_ack = strtol(argv[2], NULL, 10);
552 device = argv[3];
553
554 fd = open(device, O_RDWR);
555 if (fd < 0) {
556 perror("open");
557 exit(1);
558 }
559
560 ret = read_extcsd(fd, ext_csd);
561 if (ret) {
562 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
563 exit(1);
564 }
565
566 value = ext_csd[EXT_CSD_PART_CONFIG];
567
568 switch (boot_area) {
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100569 case EXT_CSD_PART_CONFIG_ACC_NONE:
570 value &= ~(7 << 3);
571 break;
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200572 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
573 value |= (1 << 3);
574 value &= ~(3 << 4);
575 break;
576 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
577 value |= (1 << 4);
578 value &= ~(1 << 3);
579 value &= ~(1 << 5);
580 break;
581 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
582 value |= (boot_area << 3);
583 break;
584 default:
585 fprintf(stderr, "Cannot enable the boot area\n");
586 exit(1);
587 }
588 if (send_ack)
589 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
590 else
591 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
592
593 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
594 if (ret) {
595 fprintf(stderr, "Could not write 0x%02x to "
596 "EXT_CSD[%d] in %s\n",
597 value, EXT_CSD_PART_CONFIG, device);
598 exit(1);
599 }
600 return ret;
601}
602
Al Cooper794314c2015-05-01 08:24:37 -0400603int do_boot_bus_conditions_set(int nargs, char **argv)
604{
605 __u8 ext_csd[512];
606 __u8 value = 0;
607 int fd, ret;
608 char *device;
609
610 CHECK(nargs != 5, "Usage: mmc: bootbus set <boot_mode> "
611 "<reset_boot_bus_conditions> <boot_bus_width> <device>\n",
612 exit(1));
613
614 if (strcmp(argv[1], "single_backward") == 0)
615 value |= 0;
616 else if (strcmp(argv[1], "single_hs") == 0)
617 value |= 0x8;
618 else if (strcmp(argv[1], "dual") == 0)
619 value |= 0x10;
620 else {
621 fprintf(stderr, "illegal <boot_mode> specified\n");
622 exit(1);
623 }
624
625 if (strcmp(argv[2], "x1") == 0)
626 value |= 0;
627 else if (strcmp(argv[2], "retain") == 0)
628 value |= 0x4;
629 else {
630 fprintf(stderr,
631 "illegal <reset_boot_bus_conditions> specified\n");
632 exit(1);
633 }
634
635 if (strcmp(argv[3], "x1") == 0)
636 value |= 0;
637 else if (strcmp(argv[3], "x4") == 0)
638 value |= 0x1;
639 else if (strcmp(argv[3], "x8") == 0)
640 value |= 0x2;
641 else {
642 fprintf(stderr, "illegal <boot_bus_width> specified\n");
643 exit(1);
644 }
645
646 device = argv[4];
647 fd = open(device, O_RDWR);
648 if (fd < 0) {
649 perror("open");
650 exit(1);
651 }
652
653 ret = read_extcsd(fd, ext_csd);
654 if (ret) {
655 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
656 exit(1);
657 }
658 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
659 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
660
661 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
662 if (ret) {
663 fprintf(stderr, "Could not write 0x%02x to "
664 "EXT_CSD[%d] in %s\n",
665 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
666 exit(1);
667 }
668 close(fd);
669 return ret;
670}
671
Chris Ballf74dfe22012-10-19 16:49:55 -0400672int do_hwreset(int value, int nargs, char **argv)
673{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800674 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400675 int fd, ret;
676 char *device;
677
678 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
679 exit(1));
680
681 device = argv[1];
682
683 fd = open(device, O_RDWR);
684 if (fd < 0) {
685 perror("open");
686 exit(1);
687 }
688
689 ret = read_extcsd(fd, ext_csd);
690 if (ret) {
691 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
692 exit(1);
693 }
694
695 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
696 EXT_CSD_HW_RESET_EN) {
697 fprintf(stderr,
698 "H/W Reset is already permanently enabled on %s\n",
699 device);
700 exit(1);
701 }
702 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
703 EXT_CSD_HW_RESET_DIS) {
704 fprintf(stderr,
705 "H/W Reset is already permanently disabled on %s\n",
706 device);
707 exit(1);
708 }
709
710 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
711 if (ret) {
712 fprintf(stderr,
713 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
714 value, EXT_CSD_RST_N_FUNCTION, device);
715 exit(1);
716 }
717
718 return ret;
719}
720
721int do_hwreset_en(int nargs, char **argv)
722{
723 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
724}
725
726int do_hwreset_dis(int nargs, char **argv)
727{
728 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
729}
730
Jaehoon Chung86496512012-09-21 10:08:05 +0000731int do_write_bkops_en(int nargs, char **argv)
732{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800733 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000734 int fd, ret;
735 char *device;
736
737 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
738 exit(1));
739
740 device = argv[1];
741
742 fd = open(device, O_RDWR);
743 if (fd < 0) {
744 perror("open");
745 exit(1);
746 }
747
748 ret = read_extcsd(fd, ext_csd);
749 if (ret) {
750 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
751 exit(1);
752 }
753
754 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
755 fprintf(stderr, "%s doesn't support BKOPS\n", device);
756 exit(1);
757 }
758
759 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
760 if (ret) {
761 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
762 value, EXT_CSD_BKOPS_EN, device);
763 exit(1);
764 }
765
766 return ret;
767}
768
Ben Gardiner27c357d2013-05-30 17:12:47 -0400769int do_status_get(int nargs, char **argv)
770{
771 __u32 response;
772 int fd, ret;
773 char *device;
774
775 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
776 exit(1));
777
778 device = argv[1];
779
780 fd = open(device, O_RDWR);
781 if (fd < 0) {
782 perror("open");
783 exit(1);
784 }
785
786 ret = send_status(fd, &response);
787 if (ret) {
788 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
789 exit(1);
790 }
791
792 printf("SEND_STATUS response: 0x%08x\n", response);
793
794 return ret;
795}
796
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800797__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
798{
799 return (ext_csd_loc[3] << 24) |
800 (ext_csd_loc[2] << 16) |
801 (ext_csd_loc[1] << 8) |
802 ext_csd_loc[0];
803}
804
Ben Gardiner4e850232013-05-30 17:12:49 -0400805unsigned int get_sector_count(__u8 *ext_csd)
806{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800807 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400808}
809
810int is_blockaddresed(__u8 *ext_csd)
811{
812 unsigned int sectors = get_sector_count(ext_csd);
813
Tomas Melin2e610662016-08-29 11:41:10 -0400814 /* over 2GiB devices are block-addressed */
Ben Gardiner4e850232013-05-30 17:12:49 -0400815 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
816}
817
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400818unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
819{
820 return ext_csd[221];
821}
822
823unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
824{
825 return ext_csd[224];
826}
827
Ben Gardinere6e84e92013-09-19 11:14:27 -0400828int set_partitioning_setting_completed(int dry_run, const char * const device,
829 int fd)
830{
831 int ret;
832
Tomas Melin4dadd872016-08-29 11:55:08 -0400833 if (dry_run == 1) {
Ben Gardinere6e84e92013-09-19 11:14:27 -0400834 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
835 fprintf(stderr, "These changes will not take effect neither "
836 "now nor after a power cycle\n");
837 return 1;
Tomas Melin4dadd872016-08-29 11:55:08 -0400838 } else if (dry_run == 2) {
839 printf("-c given, expecting more partition settings before "
840 "writing PARTITION_SETTING_COMPLETED\n");
841 return 0;
Ben Gardinere6e84e92013-09-19 11:14:27 -0400842 }
843
844 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
845 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
846 if (ret) {
847 fprintf(stderr, "Could not write 0x1 to "
848 "EXT_CSD[%d] in %s\n",
849 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
850 return 1;
851 }
852
853 __u32 response;
854 ret = send_status(fd, &response);
855 if (ret) {
856 fprintf(stderr, "Could not get response to SEND_STATUS "
857 "from %s\n", device);
858 return 1;
859 }
860
861 if (response & R1_SWITCH_ERROR) {
862 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
863 "failed on %s\n", device);
864 return 1;
865 }
866
867 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
868 "%s SUCCESS\n", device);
869 fprintf(stderr, "Device power cycle needed for settings to "
870 "take effect.\n"
871 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
872 "using 'extcsd read' after power cycle\n");
873
874 return 0;
875}
876
Balaji T K1fdb7f92015-04-29 18:12:32 -0400877int check_enhanced_area_total_limit(const char * const device, int fd)
878{
879 __u8 ext_csd[512];
880 __u32 regl;
881 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
882 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400883 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400884 unsigned int wp_sz, erase_sz;
885 int ret;
886
887 ret = read_extcsd(fd, ext_csd);
888 if (ret) {
889 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
890 exit(1);
891 }
892 wp_sz = get_hc_wp_grp_size(ext_csd);
893 erase_sz = get_hc_erase_grp_size(ext_csd);
894
895 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
896 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
897 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
898 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
899 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
900 enh_area_sz += gp4_part_sz;
901 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
902 printf(" i.e. %lu KiB\n", gp4_part_sz);
903 }
904
905 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
906 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
907 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
908 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
909 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
910 enh_area_sz += gp3_part_sz;
911 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
912 printf(" i.e. %lu KiB\n", gp3_part_sz);
913 }
914
915 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
916 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
917 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
918 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
919 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
920 enh_area_sz += gp2_part_sz;
921 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
922 printf(" i.e. %lu KiB\n", gp2_part_sz);
923 }
924
925 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
926 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
927 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
928 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
929 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
930 enh_area_sz += gp1_part_sz;
931 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
932 printf(" i.e. %lu KiB\n", gp1_part_sz);
933 }
934
935 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
936 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
937 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
938 user_area_sz = 512l * regl * erase_sz * wp_sz;
939 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
940 enh_area_sz += user_area_sz;
941 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
942 printf(" i.e. %lu KiB\n", user_area_sz);
943 }
944
945 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
946 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
947 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
948 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
949 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
950 printf(" i.e. %lu KiB\n", max_enh_area_sz);
951 if (enh_area_sz > max_enh_area_sz) {
952 fprintf(stderr,
953 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
954 enh_area_sz, max_enh_area_sz, device);
955 return 1;
956 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400957 total_sz = get_sector_count(ext_csd) / 2;
958 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
959 gp1_part_sz + user_area_sz;
960 if (total_gp_user_sz > total_sz) {
961 fprintf(stderr,
962 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
963 total_gp_user_sz, total_sz, device);
964 return 1;
965 }
966
967 return 0;
968}
969
970int do_create_gp_partition(int nargs, char **argv)
971{
972 __u8 value;
973 __u8 ext_csd[512];
974 __u8 address;
975 int fd, ret;
976 char *device;
977 int dry_run = 1;
978 int partition, enh_attr, ext_attr;
979 unsigned int length_kib, gp_size_mult;
980 unsigned long align;
981
Tomas Melin4dadd872016-08-29 11:55:08 -0400982 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n|-c> <length KiB> "
Balaji T Kd78ce082015-04-29 18:12:33 -0400983 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
984
Tomas Melin4dadd872016-08-29 11:55:08 -0400985 if (!strcmp("-y", argv[1])) {
Balaji T Kd78ce082015-04-29 18:12:33 -0400986 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -0400987 } else if (!strcmp("-c", argv[1])) {
988 dry_run = 2;
989 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400990
991 length_kib = strtol(argv[2], NULL, 10);
992 partition = strtol(argv[3], NULL, 10);
993 enh_attr = strtol(argv[4], NULL, 10);
994 ext_attr = strtol(argv[5], NULL, 10);
995 device = argv[6];
996
Marcus Folkessoncb04fde2015-11-18 15:06:16 -0500997 if (partition < 1 || partition > 4) {
998 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -0400999 exit(1);
1000 }
1001
1002 if (enh_attr && ext_attr) {
1003 printf("Not allowed to set both enhanced attribute and extended attribute\n");
1004 exit(1);
1005 }
1006
1007 fd = open(device, O_RDWR);
1008 if (fd < 0) {
1009 perror("open");
1010 exit(1);
1011 }
1012
1013 ret = read_extcsd(fd, ext_csd);
1014 if (ret) {
1015 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1016 exit(1);
1017 }
1018
1019 /* assert not PARTITION_SETTING_COMPLETED */
1020 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
1021 printf(" Device is already partitioned\n");
1022 exit(1);
1023 }
1024
1025 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1026 gp_size_mult = (length_kib + align/2l) / align;
1027
1028 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1029 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1030 if (ret) {
1031 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
1032 EXT_CSD_ERASE_GROUP_DEF, device);
1033 exit(1);
1034 }
1035
1036 value = (gp_size_mult >> 16) & 0xff;
1037 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
1038 ret = write_extcsd_value(fd, address, value);
1039 if (ret) {
1040 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1041 value, address, device);
1042 exit(1);
1043 }
1044 value = (gp_size_mult >> 8) & 0xff;
1045 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
1046 ret = write_extcsd_value(fd, address, value);
1047 if (ret) {
1048 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1049 value, address, device);
1050 exit(1);
1051 }
1052 value = gp_size_mult & 0xff;
1053 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
1054 ret = write_extcsd_value(fd, address, value);
1055 if (ret) {
1056 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1057 value, address, device);
1058 exit(1);
1059 }
1060
1061 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1062 if (enh_attr)
1063 value |= (1 << partition);
1064 else
1065 value &= ~(1 << partition);
1066
1067 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
1068 if (ret) {
1069 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
1070 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1071 exit(1);
1072 }
1073
1074 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
1075 value = ext_csd[address];
1076 if (ext_attr)
1077 value |= (ext_attr << (4 * ((partition - 1) % 2)));
1078 else
1079 value &= (0xF << (4 * ((partition % 2))));
1080
1081 ret = write_extcsd_value(fd, address, value);
1082 if (ret) {
1083 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
1084 value, address, device);
1085 exit(1);
1086 }
1087
1088 ret = check_enhanced_area_total_limit(device, fd);
1089 if (ret)
1090 exit(1);
1091
Tomas Melin20379fa2016-08-29 12:02:28 -04001092 if (set_partitioning_setting_completed(dry_run, device, fd))
Balaji T Kd78ce082015-04-29 18:12:33 -04001093 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -04001094
1095 return 0;
1096}
1097
Ben Gardinerd91d3692013-05-30 17:12:51 -04001098int do_enh_area_set(int nargs, char **argv)
1099{
1100 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001101 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -04001102 int fd, ret;
1103 char *device;
1104 int dry_run = 1;
1105 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
1106 unsigned long align;
1107
Tomas Melin4dadd872016-08-29 11:55:08 -04001108 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n|-c> <start KiB> <length KiB> "
Ben Gardinerd91d3692013-05-30 17:12:51 -04001109 "</path/to/mmcblkX>\n", exit(1));
1110
Tomas Melin4dadd872016-08-29 11:55:08 -04001111 if (!strcmp("-y", argv[1])) {
Ben Gardinerd91d3692013-05-30 17:12:51 -04001112 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001113 } else if (!strcmp("-c", argv[1])) {
1114 dry_run = 2;
1115 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001116
1117 start_kib = strtol(argv[2], NULL, 10);
1118 length_kib = strtol(argv[3], NULL, 10);
1119 device = argv[4];
1120
1121 fd = open(device, O_RDWR);
1122 if (fd < 0) {
1123 perror("open");
1124 exit(1);
1125 }
1126
1127 ret = read_extcsd(fd, ext_csd);
1128 if (ret) {
1129 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1130 exit(1);
1131 }
1132
1133 /* assert ENH_ATTRIBUTE_EN */
1134 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
1135 {
1136 printf(" Device cannot have enhanced tech.\n");
1137 exit(1);
1138 }
1139
1140 /* assert not PARTITION_SETTING_COMPLETED */
1141 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1142 {
1143 printf(" Device is already partitioned\n");
1144 exit(1);
1145 }
1146
1147 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1148
1149 enh_size_mult = (length_kib + align/2l) / align;
1150
1151 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
1152 enh_start_addr /= align;
1153 enh_start_addr *= align;
1154
1155 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1156 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1157 if (ret) {
1158 fprintf(stderr, "Could not write 0x1 to "
1159 "EXT_CSD[%d] in %s\n",
1160 EXT_CSD_ERASE_GROUP_DEF, device);
1161 exit(1);
1162 }
1163
1164 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
1165 value = (enh_start_addr >> 24) & 0xff;
1166 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
1167 if (ret) {
1168 fprintf(stderr, "Could not write 0x%02x to "
1169 "EXT_CSD[%d] in %s\n", value,
1170 EXT_CSD_ENH_START_ADDR_3, device);
1171 exit(1);
1172 }
1173 value = (enh_start_addr >> 16) & 0xff;
1174 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
1175 if (ret) {
1176 fprintf(stderr, "Could not write 0x%02x to "
1177 "EXT_CSD[%d] in %s\n", value,
1178 EXT_CSD_ENH_START_ADDR_2, device);
1179 exit(1);
1180 }
1181 value = (enh_start_addr >> 8) & 0xff;
1182 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
1183 if (ret) {
1184 fprintf(stderr, "Could not write 0x%02x to "
1185 "EXT_CSD[%d] in %s\n", value,
1186 EXT_CSD_ENH_START_ADDR_1, device);
1187 exit(1);
1188 }
1189 value = enh_start_addr & 0xff;
1190 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
1191 if (ret) {
1192 fprintf(stderr, "Could not write 0x%02x to "
1193 "EXT_CSD[%d] in %s\n", value,
1194 EXT_CSD_ENH_START_ADDR_0, device);
1195 exit(1);
1196 }
1197
1198 value = (enh_size_mult >> 16) & 0xff;
1199 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
1200 if (ret) {
1201 fprintf(stderr, "Could not write 0x%02x to "
1202 "EXT_CSD[%d] in %s\n", value,
1203 EXT_CSD_ENH_SIZE_MULT_2, device);
1204 exit(1);
1205 }
1206 value = (enh_size_mult >> 8) & 0xff;
1207 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
1208 if (ret) {
1209 fprintf(stderr, "Could not write 0x%02x to "
1210 "EXT_CSD[%d] in %s\n", value,
1211 EXT_CSD_ENH_SIZE_MULT_1, device);
1212 exit(1);
1213 }
1214 value = enh_size_mult & 0xff;
1215 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
1216 if (ret) {
1217 fprintf(stderr, "Could not write 0x%02x to "
1218 "EXT_CSD[%d] in %s\n", value,
1219 EXT_CSD_ENH_SIZE_MULT_0, device);
1220 exit(1);
1221 }
Balaji T K1fdb7f92015-04-29 18:12:32 -04001222 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
1223 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001224 if (ret) {
1225 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
1226 "EXT_CSD[%d] in %s\n",
1227 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1228 exit(1);
1229 }
1230
Balaji T K1fdb7f92015-04-29 18:12:32 -04001231 ret = check_enhanced_area_total_limit(device, fd);
1232 if (ret)
1233 exit(1);
1234
Ben Gardinere6e84e92013-09-19 11:14:27 -04001235 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001236
Tomas Melin20379fa2016-08-29 12:02:28 -04001237 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -04001238 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001239
1240 return 0;
1241}
1242
Ben Gardiner196d0d22013-09-19 11:14:29 -04001243int do_write_reliability_set(int nargs, char **argv)
1244{
1245 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001246 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -04001247 int fd, ret;
1248
1249 int dry_run = 1;
1250 int partition;
1251 char *device;
1252
Tomas Melin77c22a82016-09-01 11:49:01 -04001253 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n|-c> "
Ben Gardiner196d0d22013-09-19 11:14:29 -04001254 "<partition> </path/to/mmcblkX>\n", exit(1));
1255
Tomas Melin77c22a82016-09-01 11:49:01 -04001256 if (!strcmp("-y", argv[1])) {
Ben Gardiner196d0d22013-09-19 11:14:29 -04001257 dry_run = 0;
Tomas Melin77c22a82016-09-01 11:49:01 -04001258 } else if (!strcmp("-c", argv[1])) {
1259 dry_run = 2;
1260 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001261
1262 partition = strtol(argv[2], NULL, 10);
1263 device = argv[3];
1264
1265 fd = open(device, O_RDWR);
1266 if (fd < 0) {
1267 perror("open");
1268 exit(1);
1269 }
1270
1271 ret = read_extcsd(fd, ext_csd);
1272 if (ret) {
1273 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1274 exit(1);
1275 }
1276
1277 /* assert not PARTITION_SETTING_COMPLETED */
1278 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1279 {
1280 printf(" Device is already partitioned\n");
1281 exit(1);
1282 }
1283
1284 /* assert HS_CTRL_REL */
1285 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
1286 printf("Cannot set write reliability parameters, WR_REL_SET is "
1287 "read-only\n");
1288 exit(1);
1289 }
1290
1291 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
1292 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
1293 if (ret) {
1294 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1295 value, EXT_CSD_WR_REL_SET, device);
1296 exit(1);
1297 }
1298
1299 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1300 value, device);
1301
Tomas Melin20379fa2016-08-29 12:02:28 -04001302 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardiner196d0d22013-09-19 11:14:29 -04001303 exit(1);
1304
1305 return 0;
1306}
1307
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001308int do_read_extcsd(int nargs, char **argv)
1309{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001310 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001311 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001312 int fd, ret;
1313 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001314 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001315 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001316 "4.0", /* 0 */
1317 "4.1", /* 1 */
1318 "4.2", /* 2 */
1319 "4.3", /* 3 */
1320 "Obsolete", /* 4 */
1321 "4.41", /* 5 */
1322 "4.5", /* 6 */
1323 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001324 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001325 };
1326 int boot_access;
1327 const char* boot_access_str[] = {
1328 "No access to boot partition", /* 0 */
1329 "R/W Boot Partition 1", /* 1 */
1330 "R/W Boot Partition 2", /* 2 */
1331 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1332 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001333
Chris Ball8ba44662012-04-19 13:22:54 -04001334 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
1335 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001336
1337 device = argv[1];
1338
1339 fd = open(device, O_RDWR);
1340 if (fd < 0) {
1341 perror("open");
1342 exit(1);
1343 }
1344
1345 ret = read_extcsd(fd, ext_csd);
1346 if (ret) {
1347 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1348 exit(1);
1349 }
1350
Al Cooper786418c2015-04-29 18:12:35 -04001351 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001352
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001353 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1354 (ext_csd_rev != 4))
1355 str = ver_str[ext_csd_rev];
1356 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001357 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001358
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001359 printf("=============================================\n");
1360 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1361 printf("=============================================\n\n");
1362
1363 if (ext_csd_rev < 3)
1364 goto out_free; /* No ext_csd */
1365
1366 /* Parse the Extended CSD registers.
1367 * Reserved bit should be read as "0" in case of spec older
1368 * than A441.
1369 */
1370 reg = ext_csd[EXT_CSD_S_CMD_SET];
1371 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1372 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001373 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001374
1375 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1376 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1377 if (reg & EXT_CSD_HPI_SUPP) {
1378 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001379 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001380 else
1381 printf("implementation based on CMD13\n");
1382 }
1383
1384 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1385 ext_csd[502]);
1386
1387 if (ext_csd_rev >= 6) {
1388 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1389 ext_csd[501]);
1390 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1391 ext_csd[500]);
1392 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1393 ext_csd[499]);
1394
1395 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1396 ext_csd[498]);
1397 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1398 ext_csd[497]);
1399 printf("Context Management Capabilities"
1400 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1401 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1402 ext_csd[495]);
1403 printf("Extended partition attribute support"
1404 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001405 }
1406 if (ext_csd_rev >= 7) {
1407 int j;
1408 int eol_info;
1409 char* eol_info_str[] = {
1410 "Not Defined", /* 0 */
1411 "Normal", /* 1 */
1412 "Warning", /* 2 */
1413 "Urgent", /* 3 */
1414 };
1415
1416 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1417 ext_csd[493]);
1418 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1419 ext_csd[492]);
1420 printf("Operation codes timeout"
1421 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1422 ext_csd[491]);
1423 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1424 get_word_from_ext_csd(&ext_csd[487]));
1425 printf("Number of FW sectors correctly programmed"
1426 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1427 get_word_from_ext_csd(&ext_csd[302]));
1428 printf("Vendor proprietary health report:\n");
1429 for (j = 301; j >= 270; j--)
1430 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1431 " 0x%02x\n", j, ext_csd[j]);
1432 for (j = 269; j >= 268; j--) {
1433 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001434 char est_type = 'B' + (j - 269);
1435 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001436 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001437 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001438 if (life_used >= 0x1 && life_used <= 0xa)
1439 printf(" i.e. %d%% - %d%% device life time"
1440 " used\n",
1441 (life_used - 1) * 10, life_used * 10);
1442 else if (life_used == 0xb)
1443 printf(" i.e. Exceeded its maximum estimated"
1444 " device life time\n");
1445 }
1446 eol_info = ext_csd[267];
1447 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1448 eol_info);
1449 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1450 printf(" i.e. %s\n", eol_info_str[eol_info]);
1451 else
1452 printf(" i.e. Reserved\n");
1453
1454 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1455 ext_csd[266]);
1456 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1457 ext_csd[265]);
1458 printf("Optimal trim unit size"
1459 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1460 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1461 ext_csd[263], ext_csd[262]);
1462 printf("Firmware version:\n");
1463 for (j = 261; j >= 254; j--)
1464 printf("[FIRMWARE_VERSION[%d]]:"
1465 " 0x%02x\n", j, ext_csd[j]);
1466
1467 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1468 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1469 }
1470 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001471 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1472 ext_csd[248]);
1473 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1474 ext_csd[247]);
1475 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001476 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001477 }
1478
1479 /* A441: Reserved [501:247]
1480 A43: reserved [246:229] */
1481 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001482 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001483 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001484
1485 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1486
1487 printf("1st Initialisation Time after programmed sector"
1488 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1489
1490 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001491 printf("Power class for 52MHz, DDR at 3.6V"
1492 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1493 printf("Power class for 52MHz, DDR at 1.95V"
1494 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1495
1496 /* A441: reserved [237-236] */
1497
1498 if (ext_csd_rev >= 6) {
1499 printf("Power class for 200MHz at 3.6V"
1500 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1501 printf("Power class for 200MHz, at 1.95V"
1502 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1503 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001504 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001505 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1506 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1507 /* A441: reserved [233] */
1508 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1509 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1510 ext_csd[231]);
1511 }
1512 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1513 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1514 ext_csd[230]);
1515 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1516 ext_csd[229]);
1517 }
1518 reg = ext_csd[EXT_CSD_BOOT_INFO];
1519 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1520 if (reg & EXT_CSD_BOOT_INFO_ALT)
1521 printf(" Device supports alternative boot method\n");
1522 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1523 printf(" Device supports dual data rate during boot\n");
1524 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1525 printf(" Device supports high speed timing during boot\n");
1526
1527 /* A441/A43: reserved [227] */
1528 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1529 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001530
1531 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001532 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001533 reg);
1534 printf(" i.e. %u KiB\n", 512 * reg);
1535
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001536 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1537 ext_csd[223]);
1538 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1539 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001540
1541 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001542 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001543 reg);
1544 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1545
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001546 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1547 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1548 /* A441/A43: reserved [218] */
1549 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1550 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001551
1552 unsigned int sectors = get_sector_count(ext_csd);
1553 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1554 if (is_blockaddresed(ext_csd))
1555 printf(" Device is block-addressed\n");
1556 else
1557 printf(" Device is NOT block-addressed\n");
1558
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001559 /* A441/A43: reserved [211] */
1560 printf("Minimum Write Performance for 8bit:\n");
1561 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1562 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1563 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1564 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1565 printf("Minimum Write Performance for 4bit:\n");
1566 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1567 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1568 /* A441/A43: reserved [204] */
1569 printf("Power classes registers:\n");
1570 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1571 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1572 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1573 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1574
1575 /* A43: reserved [199:198] */
1576 if (ext_csd_rev >= 5) {
1577 printf("Partition switching timing "
1578 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1579 printf("Out-of-interrupt busy timing"
1580 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1581 }
1582
1583 /* A441/A43: reserved [197] [195] [193] [190] [188]
1584 * [186] [184] [182] [180] [176] */
1585
1586 if (ext_csd_rev >= 6)
1587 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1588 ext_csd[197]);
1589
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001590 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001591 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1592 ext_csd[196], ext_csd[195]);
1593 reg = ext_csd[195];
1594 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1595 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001596 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001597 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1598 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001599 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1600 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1601 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1602 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1603 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1604 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001605
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001606 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001607 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001608 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1609 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1610 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1611 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1612 ext_csd[185]);
1613 /* bus_width: ext_csd[183] not readable */
1614 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1615 ext_csd[181]);
1616 reg = ext_csd[EXT_CSD_BOOT_CFG];
1617 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001618 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001619 case 0x0:
1620 printf(" Not boot enable\n");
1621 break;
1622 case 0x1:
1623 printf(" Boot Partition 1 enabled\n");
1624 break;
1625 case 0x2:
1626 printf(" Boot Partition 2 enabled\n");
1627 break;
1628 case 0x7:
1629 printf(" User Area Enabled for boot\n");
1630 break;
1631 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001632 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1633 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1634 printf(" %s\n", boot_access_str[boot_access]);
1635 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001636 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001637 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001638
1639 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1640 ext_csd[178]);
1641 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1642 ext_csd[177]);
1643 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001644 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001645
Al Cooper1b7f5d72016-06-07 16:35:46 -04001646 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -05001647
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001648 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001649 /* A441]: reserved [172] */
1650 printf("User area write protection register"
1651 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1652 /* A441]: reserved [170] */
1653 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1654 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001655
1656 reg = ext_csd[EXT_CSD_WR_REL_SET];
1657 const char * const fast = "existing data is at risk if a power "
1658 "failure occurs during a write operation";
1659 const char * const reliable = "the device protects existing "
1660 "data if a power failure occurs during a write "
1661 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001662 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001663 " [WR_REL_SET]: 0x%02x\n", reg);
1664
1665 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1666 int i;
1667 for (i = 1; i <= 4; i++) {
1668 printf(" partition %d: %s\n", i,
1669 reg & (1<<i) ? reliable : fast);
1670 }
1671
1672 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001673 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001674 " [WR_REL_PARAM]: 0x%02x\n", reg);
1675 if (reg & 0x01)
1676 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1677 if (reg & 0x04)
1678 printf(" Device supports the enhanced def. of reliable "
1679 "write\n");
1680
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001681 /* sanitize_start ext_csd[165]]: not readable
1682 * bkops_start ext_csd[164]]: only writable */
1683 printf("Enable background operations handshake"
1684 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1685 printf("H/W reset function"
1686 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1687 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001688 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001689 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1690 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001691 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001692 printf(" Device support partitioning feature\n");
1693 else
1694 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001695 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001696 printf(" Device can have enhanced tech.\n");
1697 else
1698 printf(" Device cannot have enhanced tech.\n");
1699
Oliver Metz11f2cea2013-09-23 08:40:52 +02001700 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001701 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1702 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1703
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001704 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001705 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001706 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1707 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001708 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001709
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001710 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001711 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001712 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001713 printf("Partitioning Setting"
1714 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001715 reg);
1716 if (reg)
1717 printf(" Device partition setting complete\n");
1718 else
1719 printf(" Device partition setting NOT complete\n");
1720
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001721 printf("General Purpose Partition Size\n"
1722 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1723 (ext_csd[153] << 8) | ext_csd[152]);
1724 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1725 (ext_csd[150] << 8) | ext_csd[149]);
1726 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1727 (ext_csd[147] << 8) | ext_csd[146]);
1728 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1729 (ext_csd[144] << 8) | ext_csd[143]);
1730
Oliver Metz11f2cea2013-09-23 08:40:52 +02001731 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001732 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1733 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001734 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001735 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1736 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001737 get_hc_erase_grp_size(ext_csd) *
1738 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001739
Oliver Metz11f2cea2013-09-23 08:40:52 +02001740 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001741 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1742 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1743 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001744 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001745 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001746 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Tomas Melin2e610662016-08-29 11:41:10 -04001747 512l : 1l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001748
1749 /* A441]: reserved [135] */
1750 printf("Bad Block Management mode"
1751 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1752 /* A441: reserved [133:0] */
1753 }
1754 /* B45 */
1755 if (ext_csd_rev >= 6) {
1756 int j;
1757 /* tcase_support ext_csd[132] not readable */
1758 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1759 ext_csd[131]);
1760 printf("Program CID/CSD in DDR mode support"
1761 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1762 ext_csd[130]);
1763
1764 for (j = 127; j >= 64; j--)
1765 printf("Vendor Specific Fields"
1766 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1767 j, ext_csd[j]);
1768
Gwendal Grignoue966e672014-07-07 14:03:13 -07001769 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001770 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001771 reg);
1772 if (reg == 0x00)
1773 printf(" i.e. 512 B\n");
1774 else if (reg == 0x01)
1775 printf(" i.e. 4 KiB\n");
1776 else
1777 printf(" i.e. Reserved\n");
1778
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001779 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1780 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001781 reg = ext_csd[61];
1782 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1783 if (reg == 0x00)
1784 printf(" i.e. 512 B\n");
1785 else if (reg == 0x01)
1786 printf(" i.e. 4 KiB\n");
1787 else
1788 printf(" i.e. Reserved\n");
1789
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001790 printf("1st initialization after disabling sector"
1791 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1792 ext_csd[60]);
1793 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1794 ext_csd[59]);
1795 printf("Number of addressed group to be Released"
1796 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1797 printf("Exception events control"
1798 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1799 (ext_csd[57] << 8) | ext_csd[56]);
1800 printf("Exception events status"
1801 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1802 (ext_csd[55] << 8) | ext_csd[54]);
1803 printf("Extended Partitions Attribute"
1804 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1805 (ext_csd[53] << 8) | ext_csd[52]);
1806
1807 for (j = 51; j >= 37; j--)
1808 printf("Context configuration"
1809 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1810
1811 printf("Packed command status"
1812 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1813 printf("Packed command failure index"
1814 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1815 printf("Power Off Notification"
1816 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001817 printf("Control to turn the Cache ON/OFF"
1818 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001819 /* flush_cache ext_csd[32] not readable */
1820 /*Reserved [31:0] */
1821 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001822 if (ext_csd_rev >= 7) {
1823 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1824 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1825 ext_csd[29]);
1826
1827 reg = ext_csd[26];
1828 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1829 switch (reg) {
1830 case 0x00:
1831 printf(" Success\n");
1832 break;
1833 case 0x10:
1834 printf(" General error\n");
1835 break;
1836 case 0x11:
1837 printf(" Firmware install error\n");
1838 break;
1839 case 0x12:
1840 printf(" Error in downloading firmware\n");
1841 break;
1842 default:
1843 printf(" Reserved\n");
1844 }
1845 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1846 " %d sector size\n",
1847 get_word_from_ext_csd(&ext_csd[22]));
1848 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1849 " %d sector size\n",
1850 get_word_from_ext_csd(&ext_csd[18]));
1851 printf("Product state awareness enablement"
1852 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1853 ext_csd[17]);
1854 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1855 ext_csd[16]);
1856 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001857
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05001858 if (ext_csd_rev >= 7) {
1859 printf("eMMC Firmware Version: %s\n",
1860 (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]);
1861 }
Adrian Hunterbb1600b2016-06-10 11:28:59 +03001862
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001863 if (ext_csd_rev >= 7) {
1864 printf("eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x%02x\n",
1865 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]);
1866 }
1867
1868 if (ext_csd_rev >= 7) {
1869 printf("eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x%02x\n",
1870 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]);
1871 }
1872
Alexander Steincce3d882017-03-20 14:43:00 +01001873 if (ext_csd_rev >= 7) {
1874 printf("eMMC Pre EOL information [EXT_CSD_PRE_EOL_INFO]: 0x%02x\n",
1875 ext_csd[EXT_CSD_PRE_EOL_INFO]);
1876 }
1877
Adrian Hunterbb1600b2016-06-10 11:28:59 +03001878 if (ext_csd_rev >= 8) {
1879 printf("Command Queue Support [CMDQ_SUPPORT]: 0x%02x\n",
1880 ext_csd[EXT_CSD_CMDQ_SUPPORT]);
1881 printf("Command Queue Depth [CMDQ_DEPTH]: %u\n",
1882 (ext_csd[EXT_CSD_CMDQ_DEPTH] & 0x1f) + 1);
1883 printf("Command Enabled [CMDQ_MODE_EN]: 0x%02x\n",
1884 ext_csd[EXT_CSD_CMDQ_MODE_EN]);
1885 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001886out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001887 return ret;
1888}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001889
Nick Sanders9d57aa72014-03-05 21:38:54 -08001890int do_dump_extcsd(int nargs, char **argv)
1891{
1892 __u8 ext_csd[EXT_CSD_SIZE];
1893 int fd, ret;
1894 char *device;
1895 int i, j;
1896
1897 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1898 exit(1));
1899
1900 device = argv[1];
1901
1902 fd = open(device, O_RDWR);
1903 if (fd < 0) {
1904 perror("Failed to open mmc device");
1905 exit(1);
1906 }
1907
1908 ret = read_extcsd(fd, ext_csd);
1909 if (ret) {
1910 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1911 exit(1);
1912 }
1913
1914 /* Dump all bytes so that any undecoded or proprietary registers */
1915 /* can be acessed. */
1916 printf("EXT_CSD binary dump:\n");
1917 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1918 printf(" %3d: %3x: ", i, i);
1919 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1920 printf(" %02x", ext_csd[i+j]);
1921 }
1922 printf("\n");
1923 }
1924
1925 return ret;
1926}
1927
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001928int do_sanitize(int nargs, char **argv)
1929{
1930 int fd, ret;
1931 char *device;
1932
1933 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1934 exit(1));
1935
1936 device = argv[1];
1937
1938 fd = open(device, O_RDWR);
1939 if (fd < 0) {
1940 perror("open");
1941 exit(1);
1942 }
1943
1944 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1945 if (ret) {
1946 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1947 1, EXT_CSD_SANITIZE_START, device);
1948 exit(1);
1949 }
1950
1951 return ret;
1952
1953}
1954
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07001955enum blockprotect_mode {
1956 BLOCKPROTECT_TEMPORARY = 0,
1957 BLOCKPROTECT_POWERON,
1958 BLOCKPROTECT_PERMANENT,
1959};
1960
1961int write_blockprotect(int fd, __u32 sector, int enable)
1962{
1963 struct mmc_ioc_cmd cmd;
1964 int ret;
1965
1966 memset(&cmd, 0, sizeof(cmd));
1967 cmd.write_flag = 1;
1968 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
1969 cmd.arg = sector;
1970 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
1971
1972 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
1973 if (ret)
1974 perror("SET/CLR_WRITE_PROT command");
1975 return ret;
1976}
1977
1978int do_blockprotect_enable(int nargs, char **argv)
1979{
1980 __u8 ext_csd[EXT_CSD_SIZE];
1981 __u8 user_wp;
1982 __u32 sector;
1983 char *end;
1984 int ret, fd;
1985 int arg_index = 0;
1986 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
1987
1988 if (nargs > 0 && !strcmp(argv[1], "-r")) {
1989 arg_index++;
1990 mode = BLOCKPROTECT_POWERON;
1991 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
1992 arg_index++;
1993 mode = BLOCKPROTECT_PERMANENT;
1994 }
1995
1996 CHECK(nargs != 3 + arg_index, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n", exit(1));
1997
1998 sector = strtoul(argv[2 + arg_index], &end, 0);
1999 if (*end != '\0') {
2000 fprintf(stderr, "Not a block number: %s\n",
2001 argv[2 + arg_index]);
2002 exit(1);
2003 }
2004
2005 fd = open(argv[1 + arg_index], O_RDWR);
2006 if (fd < 0) {
2007 perror("open");
2008 exit(1);
2009 }
2010
2011 if (read_extcsd(fd, ext_csd))
2012 exit(1);
2013
2014 user_wp = ext_csd[EXT_CSD_USER_WP];
2015 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2016 if (mode == BLOCKPROTECT_POWERON)
2017 user_wp |= EXT_CSD_US_PWR_WP_EN;
2018 else if (mode == BLOCKPROTECT_PERMANENT)
2019 user_wp |= EXT_CSD_US_PERM_WP_EN;
2020
2021 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
2022 if (ret) {
2023 perror("update EXT_CSD[USER_WP]");
2024 exit(1);
2025 }
2026
2027 usleep(INTER_COMMAND_GAP_US);
2028
2029 ret = write_blockprotect(fd, sector, 1);
2030
2031 usleep(INTER_COMMAND_GAP_US);
2032
2033 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2034 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
2035 perror("reset EXT_CSD[USER_WP]");
2036 if (!ret)
2037 ret = -1;
2038 }
2039
2040 return ret;
2041}
2042
2043int do_blockprotect_disable(int nargs, char **argv)
2044{
2045 __u32 sector;
2046 char *end;
2047 int fd;
2048
2049 CHECK(nargs != 3, "Usage: mmc blockprotect disable <device> <write protect block>\n", exit(1));
2050
2051 sector = strtoul(argv[2], &end, 0);
2052 if (*end != '\0') {
2053 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2054 exit(1);
2055 }
2056
2057
2058 fd = open(argv[1], O_RDWR);
2059 if (fd < 0) {
2060 perror("open");
2061 exit(1);
2062 }
2063
2064 return write_blockprotect(fd, sector, 0);
2065}
2066
2067int do_blockprotect_read(int nargs, char **argv)
2068{
2069 __u8 wp_bits[8];
2070 __u32 sector;
2071 char *end;
2072 int fd;
2073 struct mmc_ioc_cmd cmd;
2074
2075 CHECK(nargs != 3, "Usage: mmc blockprotect read <device> <write protect block>\n", exit(1));
2076
2077 fd = open(argv[1], O_RDWR);
2078 if (fd < 0) {
2079 perror("open");
2080 exit(1);
2081 }
2082
2083 sector = strtoul(argv[2], &end, 0);
2084 if (*end != '\0') {
2085 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2086 exit(1);
2087 }
2088
2089 memset(&cmd, 0, sizeof(cmd));
2090 cmd.write_flag = 0;
2091 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
2092 cmd.arg = sector;
2093 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2094 cmd.blksz = sizeof(wp_bits);
2095 cmd.blocks = 1;
2096 mmc_ioc_cmd_set_data(cmd, wp_bits);
2097
2098 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
2099 perror("SEND_WRITE_PROT_TYPE command");
2100 exit(1);
2101 }
2102
2103 printf("Sector %u write protection: ", sector);
2104 switch (wp_bits[7] & 3) {
2105 case 0:
2106 printf("NONE\n");
2107 break;
2108 case 1:
2109 printf("TEMPORARY\n");
2110 break;
2111 case 2:
2112 printf("POWER-ON\n");
2113 break;
2114 case 3:
2115 printf("PERMANENT\n");
2116 break;
2117 }
2118
2119 return 0;
2120}
2121
2122int do_blockprotect_info(int nargs, char **argv)
2123{
2124 __u8 ext_csd[EXT_CSD_SIZE];
2125 __u8 user_wp;
2126 int fd, wp_sz, erase_sz;
2127
2128 CHECK(nargs != 2, "Usage: mmc blockprotect info <device>\n", exit(1));
2129
2130 fd = open(argv[1], O_RDWR);
2131 if (fd < 0) {
2132 perror("open");
2133 exit(1);
2134 }
2135
2136 if (read_extcsd(fd, ext_csd))
2137 exit(1);
2138
2139 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
2140 fprintf(stderr, "Block protection commands not supported: "
2141 "CLASS_6_CTRL set.\n");
2142 exit(1);
2143 }
2144
2145 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
2146 fprintf(stderr, "Block protection commands not supported: "
2147 "high-capacity sizes not enabled.\n");
2148 exit(1);
2149 }
2150
2151 wp_sz = get_hc_wp_grp_size(ext_csd);
2152 erase_sz = get_hc_erase_grp_size(ext_csd);
2153
2154 if (erase_sz == 0 || wp_sz == 0) {
2155 fprintf(stderr, "Block protection commands not supported: "
2156 "no high-capacity size for erase or WP blocks.\n");
2157 exit(1);
2158 }
2159
2160 printf("Write protect block size in sectors: %d\n",
2161 erase_sz * wp_sz * 1024);
2162
2163 user_wp = ext_csd[EXT_CSD_USER_WP];
2164 printf("Permanent write protection: %s\n",
2165 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
2166 printf("Power-on write protection: %s\n",
2167 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
2168
2169 return 0;
2170}
2171
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002172static const char* const mmc_ffu_hack_names[] = {
2173 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
2174};
2175
Gwendal Grignou771984c2014-07-01 12:46:18 -07002176int do_emmc50_ffu (int nargs, char **argv)
2177{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002178 int fd, ret, i, argc=1, ffu_hack=0;
2179 char *device, *type, *path;
2180 __u64 value;
2181 union {
2182 __u8 data[FFU_DATA_SIZE];
2183 struct mmc_ffu_args ffu_args;
2184 } ffu_data;
2185 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002186 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07002187
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002188 while (!strcmp("-k", argv[argc])) {
2189 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
2190 if (ret < 1) {
2191 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
2192 exit(1);
2193 }
2194 for (i = 0; i < MMC_HACK_LEN; i++) {
2195 if (!strcmp(type, mmc_ffu_hack_names[i])) {
2196 ffu_args->hack[ffu_hack].type = i;
2197 if (ret == 2) {
2198 ffu_args->hack[ffu_hack].value = value;
2199 }
2200 ffu_hack++;
2201 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
2202 sizeof(struct mmc_ffu_args) >
2203 FFU_DATA_SIZE) {
2204 fprintf(stderr, "Too many %d hacks",
2205 ffu_hack);
2206 exit(1);
2207 }
2208 break;
2209 }
2210 }
2211 if (i == MMC_HACK_LEN) {
2212 fprintf(stderr, "Hack type %s not found\n", type);
2213 fprintf(stderr, "Supported types are: ");
2214 for (i = 0; i < MMC_HACK_LEN; i++)
2215 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
2216 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07002217
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002218 exit(1);
2219 }
2220 free(type);
2221 argc++;
2222 }
2223 ffu_args->hack_nb = ffu_hack;
2224
2225 path = argv[argc++];
2226 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07002227 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
2228 exit(1);
2229 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002230 strcpy(ffu_args->name, path);
2231 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07002232 fd = open(device, O_RDWR);
2233 if (fd < 0) {
2234 perror("open");
2235 exit(1);
2236 }
2237
Gwendal Grignou0f757342014-10-16 16:52:46 -07002238 /* prepare and send ioctl */
2239 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
2240 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002241 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002242 mmc_ioc_cmd.blocks = 1;
2243 mmc_ioc_cmd.arg = 0;
2244 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2245 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002246 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07002247 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07002248 if (ret) {
2249 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
2250 exit(1);
2251 }
2252
2253 close(fd);
2254 return 0;
2255}
2256
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002257#define DO_IO(func, fd, buf, nbyte) \
2258 ({ \
2259 ssize_t ret = 0, r; \
2260 do { \
2261 r = func(fd, buf + ret, nbyte - ret); \
2262 if (r < 0 && errno != EINTR) { \
2263 ret = -1; \
2264 break; \
2265 } \
2266 else if (r > 0) \
2267 ret += r; \
2268 } while (r != 0 && (size_t)ret != nbyte); \
2269 \
2270 ret; \
2271 })
2272
2273enum rpmb_op_type {
2274 MMC_RPMB_WRITE_KEY = 0x01,
2275 MMC_RPMB_READ_CNT = 0x02,
2276 MMC_RPMB_WRITE = 0x03,
2277 MMC_RPMB_READ = 0x04,
2278
2279 /* For internal usage only, do not use it directly */
2280 MMC_RPMB_READ_RESP = 0x05
2281};
2282
2283struct rpmb_frame {
2284 u_int8_t stuff[196];
2285 u_int8_t key_mac[32];
2286 u_int8_t data[256];
2287 u_int8_t nonce[16];
2288 u_int32_t write_counter;
2289 u_int16_t addr;
2290 u_int16_t block_count;
2291 u_int16_t result;
2292 u_int16_t req_resp;
2293};
2294
2295/* Performs RPMB operation.
2296 *
2297 * @fd: RPMB device on which we should perform ioctl command
2298 * @frame_in: input RPMB frame, should be properly inited
2299 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
2300 * result and req_resp for output frame.
2301 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
2302 * in the other cases -EINVAL will be returned.
2303 */
2304static int do_rpmb_op(int fd,
2305 const struct rpmb_frame *frame_in,
2306 struct rpmb_frame *frame_out,
2307 unsigned int out_cnt)
2308{
2309 int err;
2310 u_int16_t rpmb_type;
2311
2312 struct mmc_ioc_cmd ioc = {
2313 .arg = 0x0,
2314 .blksz = 512,
2315 .blocks = 1,
2316 .write_flag = 1,
2317 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
2318 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
2319 .data_ptr = (uintptr_t)frame_in
2320 };
2321
2322 if (!frame_in || !frame_out || !out_cnt)
2323 return -EINVAL;
2324
2325 rpmb_type = be16toh(frame_in->req_resp);
2326
2327 switch(rpmb_type) {
2328 case MMC_RPMB_WRITE:
2329 case MMC_RPMB_WRITE_KEY:
2330 if (out_cnt != 1) {
2331 err = -EINVAL;
2332 goto out;
2333 }
2334
2335 /* Write request */
2336 ioc.write_flag |= (1<<31);
2337 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2338 if (err < 0) {
2339 err = -errno;
2340 goto out;
2341 }
2342
2343 /* Result request */
2344 memset(frame_out, 0, sizeof(*frame_out));
2345 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2346 ioc.write_flag = 1;
2347 ioc.data_ptr = (uintptr_t)frame_out;
2348 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2349 if (err < 0) {
2350 err = -errno;
2351 goto out;
2352 }
2353
2354 /* Get response */
2355 ioc.write_flag = 0;
2356 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2357 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2358 if (err < 0) {
2359 err = -errno;
2360 goto out;
2361 }
2362
2363 break;
2364 case MMC_RPMB_READ_CNT:
2365 if (out_cnt != 1) {
2366 err = -EINVAL;
2367 goto out;
2368 }
2369 /* fall through */
2370
2371 case MMC_RPMB_READ:
2372 /* Request */
2373 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2374 if (err < 0) {
2375 err = -errno;
2376 goto out;
2377 }
2378
2379 /* Get response */
2380 ioc.write_flag = 0;
2381 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2382 ioc.blocks = out_cnt;
2383 ioc.data_ptr = (uintptr_t)frame_out;
2384 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2385 if (err < 0) {
2386 err = -errno;
2387 goto out;
2388 }
2389
2390 break;
2391 default:
2392 err = -EINVAL;
2393 goto out;
2394 }
2395
2396out:
2397 return err;
2398}
2399
2400int do_rpmb_write_key(int nargs, char **argv)
2401{
2402 int ret, dev_fd, key_fd;
2403 struct rpmb_frame frame_in = {
2404 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2405 }, frame_out;
2406
2407 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
2408 exit(1));
2409
2410 dev_fd = open(argv[1], O_RDWR);
2411 if (dev_fd < 0) {
2412 perror("device open");
2413 exit(1);
2414 }
2415
2416 if (0 == strcmp(argv[2], "-"))
2417 key_fd = STDIN_FILENO;
2418 else {
2419 key_fd = open(argv[2], O_RDONLY);
2420 if (key_fd < 0) {
2421 perror("can't open key file");
2422 exit(1);
2423 }
2424 }
2425
2426 /* Read the auth key */
2427 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2428 if (ret < 0) {
2429 perror("read the key");
2430 exit(1);
2431 } else if (ret != sizeof(frame_in.key_mac)) {
2432 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2433 (unsigned long)sizeof(frame_in.key_mac),
2434 ret);
2435 exit(1);
2436 }
2437
2438 /* Execute RPMB op */
2439 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2440 if (ret != 0) {
2441 perror("RPMB ioctl failed");
2442 exit(1);
2443 }
2444
2445 /* Check RPMB response */
2446 if (frame_out.result != 0) {
2447 printf("RPMB operation failed, retcode 0x%04x\n",
2448 be16toh(frame_out.result));
2449 exit(1);
2450 }
2451
2452 close(dev_fd);
2453 if (key_fd != STDIN_FILENO)
2454 close(key_fd);
2455
2456 return ret;
2457}
2458
2459int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2460{
2461 int ret;
2462 struct rpmb_frame frame_in = {
2463 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2464 }, frame_out;
2465
2466 /* Execute RPMB op */
2467 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2468 if (ret != 0) {
2469 perror("RPMB ioctl failed");
2470 exit(1);
2471 }
2472
2473 /* Check RPMB response */
2474 if (frame_out.result != 0)
2475 return be16toh(frame_out.result);
2476
2477 *cnt = be32toh(frame_out.write_counter);
2478
2479 return 0;
2480}
2481
2482int do_rpmb_read_counter(int nargs, char **argv)
2483{
2484 int ret, dev_fd;
2485 unsigned int cnt;
2486
2487 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
2488 exit(1));
2489
2490 dev_fd = open(argv[1], O_RDWR);
2491 if (dev_fd < 0) {
2492 perror("device open");
2493 exit(1);
2494 }
2495
2496 ret = rpmb_read_counter(dev_fd, &cnt);
2497
2498 /* Check RPMB response */
2499 if (ret != 0) {
2500 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2501 exit(1);
2502 }
2503
2504 close(dev_fd);
2505
2506 printf("Counter value: 0x%08x\n", cnt);
2507
2508 return ret;
2509}
2510
2511int do_rpmb_read_block(int nargs, char **argv)
2512{
2513 int i, ret, dev_fd, data_fd, key_fd = -1;
2514 uint16_t addr, blocks_cnt;
2515 unsigned char key[32];
2516 struct rpmb_frame frame_in = {
2517 .req_resp = htobe16(MMC_RPMB_READ),
2518 }, *frame_out_p;
2519
2520 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
2521 exit(1));
2522
2523 dev_fd = open(argv[1], O_RDWR);
2524 if (dev_fd < 0) {
2525 perror("device open");
2526 exit(1);
2527 }
2528
2529 /* Get block address */
2530 errno = 0;
2531 addr = strtol(argv[2], NULL, 0);
2532 if (errno) {
2533 perror("incorrect address");
2534 exit(1);
2535 }
2536 frame_in.addr = htobe16(addr);
2537
2538 /* Get blocks count */
2539 errno = 0;
2540 blocks_cnt = strtol(argv[3], NULL, 0);
2541 if (errno) {
2542 perror("incorrect blocks count");
2543 exit(1);
2544 }
2545
2546 if (!blocks_cnt) {
2547 printf("please, specify valid blocks count number\n");
2548 exit(1);
2549 }
2550
2551 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2552 if (!frame_out_p) {
2553 printf("can't allocate memory for RPMB outer frames\n");
2554 exit(1);
2555 }
2556
2557 /* Write 256b data */
2558 if (0 == strcmp(argv[4], "-"))
2559 data_fd = STDOUT_FILENO;
2560 else {
2561 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2562 S_IRUSR | S_IWUSR);
2563 if (data_fd < 0) {
2564 perror("can't open output file");
2565 exit(1);
2566 }
2567 }
2568
2569 /* Key is specified */
2570 if (nargs == 6) {
2571 if (0 == strcmp(argv[5], "-"))
2572 key_fd = STDIN_FILENO;
2573 else {
2574 key_fd = open(argv[5], O_RDONLY);
2575 if (key_fd < 0) {
2576 perror("can't open input key file");
2577 exit(1);
2578 }
2579 }
2580
2581 ret = DO_IO(read, key_fd, key, sizeof(key));
2582 if (ret < 0) {
2583 perror("read the key data");
2584 exit(1);
2585 } else if (ret != sizeof(key)) {
2586 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2587 (unsigned long)sizeof(key),
2588 ret);
2589 exit(1);
2590 }
2591 }
2592
2593 /* Execute RPMB op */
2594 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2595 if (ret != 0) {
2596 perror("RPMB ioctl failed");
2597 exit(1);
2598 }
2599
2600 /* Check RPMB response */
2601 if (frame_out_p[blocks_cnt - 1].result != 0) {
2602 printf("RPMB operation failed, retcode 0x%04x\n",
2603 be16toh(frame_out_p[blocks_cnt - 1].result));
2604 exit(1);
2605 }
2606
2607 /* Do we have to verify data against key? */
2608 if (nargs == 6) {
2609 unsigned char mac[32];
2610 hmac_sha256_ctx ctx;
2611 struct rpmb_frame *frame_out = NULL;
2612
2613 hmac_sha256_init(&ctx, key, sizeof(key));
2614 for (i = 0; i < blocks_cnt; i++) {
2615 frame_out = &frame_out_p[i];
2616 hmac_sha256_update(&ctx, frame_out->data,
2617 sizeof(*frame_out) -
2618 offsetof(struct rpmb_frame, data));
2619 }
2620
2621 hmac_sha256_final(&ctx, mac, sizeof(mac));
2622
2623 /* Impossible */
2624 assert(frame_out);
2625
2626 /* Compare calculated MAC and MAC from last frame */
2627 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2628 printf("RPMB MAC missmatch\n");
2629 exit(1);
2630 }
2631 }
2632
2633 /* Write data */
2634 for (i = 0; i < blocks_cnt; i++) {
2635 struct rpmb_frame *frame_out = &frame_out_p[i];
2636 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2637 if (ret < 0) {
2638 perror("write the data");
2639 exit(1);
2640 } else if (ret != sizeof(frame_out->data)) {
2641 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2642 (unsigned long)sizeof(frame_out->data),
2643 ret);
2644 exit(1);
2645 }
2646 }
2647
2648 free(frame_out_p);
2649 close(dev_fd);
2650 if (data_fd != STDOUT_FILENO)
2651 close(data_fd);
2652 if (key_fd != -1 && key_fd != STDIN_FILENO)
2653 close(key_fd);
2654
2655 return ret;
2656}
2657
2658int do_rpmb_write_block(int nargs, char **argv)
2659{
2660 int ret, dev_fd, key_fd, data_fd;
2661 unsigned char key[32];
2662 uint16_t addr;
2663 unsigned int cnt;
2664 struct rpmb_frame frame_in = {
2665 .req_resp = htobe16(MMC_RPMB_WRITE),
2666 .block_count = htobe16(1)
2667 }, frame_out;
2668
2669 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
2670 exit(1));
2671
2672 dev_fd = open(argv[1], O_RDWR);
2673 if (dev_fd < 0) {
2674 perror("device open");
2675 exit(1);
2676 }
2677
2678 ret = rpmb_read_counter(dev_fd, &cnt);
2679 /* Check RPMB response */
2680 if (ret != 0) {
2681 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2682 exit(1);
2683 }
2684 frame_in.write_counter = htobe32(cnt);
2685
2686 /* Get block address */
2687 errno = 0;
2688 addr = strtol(argv[2], NULL, 0);
2689 if (errno) {
2690 perror("incorrect address");
2691 exit(1);
2692 }
2693 frame_in.addr = htobe16(addr);
2694
2695 /* Read 256b data */
2696 if (0 == strcmp(argv[3], "-"))
2697 data_fd = STDIN_FILENO;
2698 else {
2699 data_fd = open(argv[3], O_RDONLY);
2700 if (data_fd < 0) {
2701 perror("can't open input file");
2702 exit(1);
2703 }
2704 }
2705
2706 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2707 if (ret < 0) {
2708 perror("read the data");
2709 exit(1);
2710 } else if (ret != sizeof(frame_in.data)) {
2711 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2712 (unsigned long)sizeof(frame_in.data),
2713 ret);
2714 exit(1);
2715 }
2716
2717 /* Read the auth key */
2718 if (0 == strcmp(argv[4], "-"))
2719 key_fd = STDIN_FILENO;
2720 else {
2721 key_fd = open(argv[4], O_RDONLY);
2722 if (key_fd < 0) {
2723 perror("can't open key file");
2724 exit(1);
2725 }
2726 }
2727
2728 ret = DO_IO(read, key_fd, key, sizeof(key));
2729 if (ret < 0) {
2730 perror("read the key");
2731 exit(1);
2732 } else if (ret != sizeof(key)) {
2733 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2734 (unsigned long)sizeof(key),
2735 ret);
2736 exit(1);
2737 }
2738
2739 /* Calculate HMAC SHA256 */
2740 hmac_sha256(
2741 key, sizeof(key),
2742 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2743 frame_in.key_mac, sizeof(frame_in.key_mac));
2744
2745 /* Execute RPMB op */
2746 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2747 if (ret != 0) {
2748 perror("RPMB ioctl failed");
2749 exit(1);
2750 }
2751
2752 /* Check RPMB response */
2753 if (frame_out.result != 0) {
2754 printf("RPMB operation failed, retcode 0x%04x\n",
2755 be16toh(frame_out.result));
2756 exit(1);
2757 }
2758
2759 close(dev_fd);
2760 if (data_fd != STDIN_FILENO)
2761 close(data_fd);
2762 if (key_fd != STDIN_FILENO)
2763 close(key_fd);
2764
2765 return ret;
2766}
Al Cooper786418c2015-04-29 18:12:35 -04002767
2768int do_cache_ctrl(int value, int nargs, char **argv)
2769{
2770 __u8 ext_csd[512];
2771 int fd, ret;
2772 char *device;
2773
2774 CHECK(nargs != 2, "Usage: mmc cache enable </path/to/mmcblkX>\n",
2775 exit(1));
2776
2777 device = argv[1];
2778
2779 fd = open(device, O_RDWR);
2780 if (fd < 0) {
2781 perror("open");
2782 exit(1);
2783 }
2784
2785 ret = read_extcsd(fd, ext_csd);
2786 if (ret) {
2787 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2788 exit(1);
2789 }
2790
2791 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2792 fprintf(stderr,
2793 "The CACHE option is only availabe on devices >= "
2794 "MMC 4.5 %s\n", device);
2795 exit(1);
2796 }
2797
2798 /* If the cache size is zero, this device does not have a cache */
2799 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2800 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2801 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2802 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2803 fprintf(stderr,
2804 "The CACHE option is not available on %s\n",
2805 device);
2806 exit(1);
2807 }
2808 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2809 if (ret) {
2810 fprintf(stderr,
2811 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2812 value, EXT_CSD_CACHE_CTRL, device);
2813 exit(1);
2814 }
2815
2816 return ret;
2817}
2818
2819int do_cache_en(int nargs, char **argv)
2820{
2821 return do_cache_ctrl(1, nargs, argv);
2822}
2823
2824int do_cache_dis(int nargs, char **argv)
2825{
2826 return do_cache_ctrl(0, nargs, argv);
2827}
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002828
2829int do_ffu(int nargs, char **argv)
2830{
2831#ifndef MMC_IOC_MULTI_CMD
2832 fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
2833 " support, needed by FFU.\n");
2834 exit(1);
2835#else
2836 int dev_fd, img_fd;
2837 int sect_done = 0, retry = 3, ret = -EINVAL;
2838 unsigned int sect_size;
2839 __u8 ext_csd[EXT_CSD_SIZE];
2840 __u8 *buf;
2841 __u32 arg;
2842 off_t fw_size;
2843 ssize_t chunk_size;
2844 char *device;
2845 struct mmc_ioc_multi_cmd *multi_cmd;
2846
2847 CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n",
2848 exit(1));
2849
2850 device = argv[2];
2851 dev_fd = open(device, O_RDWR);
2852 if (dev_fd < 0) {
2853 perror("device open failed");
2854 exit(1);
2855 }
2856 img_fd = open(argv[1], O_RDONLY);
2857 if (img_fd < 0) {
2858 perror("image open failed");
2859 close(dev_fd);
2860 exit(1);
2861 }
2862
2863 buf = malloc(512);
2864 multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
2865 3 * sizeof(struct mmc_ioc_cmd));
2866 if (!buf || !multi_cmd) {
2867 perror("failed to allocate memory");
2868 goto out;
2869 }
2870
2871 ret = read_extcsd(dev_fd, ext_csd);
2872 if (ret) {
2873 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2874 goto out;
2875 }
2876
2877 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
2878 fprintf(stderr,
2879 "The FFU feature is only available on devices >= "
2880 "MMC 5.0, not supported in %s\n", device);
2881 goto out;
2882 }
2883
2884 if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
2885 fprintf(stderr, "FFU is not supported in %s\n", device);
2886 goto out;
2887 }
2888
2889 if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
2890 fprintf(stderr, "Firmware update was disabled in %s\n", device);
2891 goto out;
2892 }
2893
2894 fw_size = lseek(img_fd, 0, SEEK_END);
2895
2896 if (fw_size == 0) {
2897 fprintf(stderr, "Firmware image is empty");
2898 goto out;
2899 }
2900
2901 sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
2902 if (fw_size % sect_size) {
2903 fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
2904 goto out;
2905 }
2906
2907 /* set CMD ARG */
2908 arg = ext_csd[EXT_CSD_FFU_ARG_0] |
2909 ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
2910 ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
2911 ext_csd[EXT_CSD_FFU_ARG_3] << 24;
2912
2913 /* prepare multi_cmd to be sent */
2914 multi_cmd->num_of_cmds = 3;
2915
2916 /* put device into ffu mode */
2917 multi_cmd->cmds[0].opcode = MMC_SWITCH;
2918 multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2919 (EXT_CSD_MODE_CONFIG << 16) |
2920 (EXT_CSD_FFU_MODE << 8) |
2921 EXT_CSD_CMD_SET_NORMAL;
2922 multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2923 multi_cmd->cmds[0].write_flag = 1;
2924
2925 /* send image chunk */
2926 multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
2927 multi_cmd->cmds[1].blksz = sect_size;
2928 multi_cmd->cmds[1].blocks = 1;
2929 multi_cmd->cmds[1].arg = arg;
2930 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2931 multi_cmd->cmds[1].write_flag = 1;
2932 mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);
2933
2934 /* return device into normal mode */
2935 multi_cmd->cmds[2].opcode = MMC_SWITCH;
2936 multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2937 (EXT_CSD_MODE_CONFIG << 16) |
2938 (EXT_CSD_NORMAL_MODE << 8) |
2939 EXT_CSD_CMD_SET_NORMAL;
2940 multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2941 multi_cmd->cmds[2].write_flag = 1;
2942
2943do_retry:
2944 /* read firmware chunk */
2945 lseek(img_fd, 0, SEEK_SET);
2946 chunk_size = read(img_fd, buf, 512);
2947
2948 while (chunk_size > 0) {
2949 /* send ioctl with multi-cmd */
2950 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2951
2952 if (ret) {
2953 perror("Multi-cmd ioctl");
2954 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2955 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2956 goto out;
2957 }
2958
2959 ret = read_extcsd(dev_fd, ext_csd);
2960 if (ret) {
2961 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2962 goto out;
2963 }
2964
2965 /* Test if we need to restart the download */
2966 sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
2967 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
2968 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
2969 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
2970 /* By spec, host should re-start download from the first sector if sect_done is 0 */
2971 if (sect_done == 0) {
2972 if (retry > 0) {
2973 retry--;
2974 fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
2975 goto do_retry;
2976 }
2977 fprintf(stderr, "Programming failed! Aborting...\n");
2978 goto out;
2979 } else {
2980 fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
2981 }
2982
2983 /* read the next firmware chunk (if any) */
2984 chunk_size = read(img_fd, buf, 512);
2985 }
2986
2987 if ((sect_done * sect_size) == fw_size) {
2988 fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
2989 fprintf(stderr, "Programming finished with status %d \n", ret);
2990 }
2991 else {
2992 fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
2993 goto out;
2994 }
2995
2996 /* check mode operation for ffu install*/
2997 if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
2998 fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
2999 } else {
3000 fprintf(stderr, "Installing firmware on %s...\n", device);
3001 /* Re-enter ffu mode and install the firmware */
3002 multi_cmd->num_of_cmds = 2;
3003
3004 /* set ext_csd to install mode */
3005 multi_cmd->cmds[1].opcode = MMC_SWITCH;
3006 multi_cmd->cmds[1].blksz = 0;
3007 multi_cmd->cmds[1].blocks = 0;
3008 multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
3009 (EXT_CSD_MODE_OPERATION_CODES << 16) |
3010 (EXT_CSD_FFU_INSTALL << 8) |
3011 EXT_CSD_CMD_SET_NORMAL;
3012 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
3013 multi_cmd->cmds[1].write_flag = 1;
3014
3015 /* send ioctl with multi-cmd */
3016 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
3017
3018 if (ret) {
3019 perror("Multi-cmd ioctl failed setting install mode");
3020 /* In case multi-cmd ioctl failed before exiting from ffu mode */
3021 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
3022 goto out;
3023 }
3024
3025 ret = read_extcsd(dev_fd, ext_csd);
3026 if (ret) {
3027 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3028 goto out;
3029 }
3030
3031 /* return status */
3032 ret = ext_csd[EXT_CSD_FFU_STATUS];
3033 if (ret) {
3034 fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
3035 goto out;
3036 } else {
3037 fprintf(stderr, "FFU finished successfully\n");
3038 }
3039 }
3040
3041out:
3042 free(buf);
3043 free(multi_cmd);
3044 close(img_fd);
3045 close(dev_fd);
3046 return ret;
3047#endif
3048}