blob: b1581067460d2f3ba24c0502e08e76f807a38e3e [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
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100245 if (nargs != 2) {
246 fprintf(stderr, "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n");
247 exit(1);
248 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500249
250 device = argv[1];
251
252 fd = open(device, O_RDWR);
253 if (fd < 0) {
254 perror("open");
255 exit(1);
256 }
257
258 ret = read_extcsd(fd, ext_csd);
259 if (ret) {
260 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
261 exit(1);
262 }
263
Al Cooper1b7f5d72016-06-07 16:35:46 -0400264 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -0500265
266 return ret;
267}
268
Al Cooper1b7f5d72016-06-07 16:35:46 -0400269int do_writeprotect_boot_set(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500270{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800271 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500272 int fd, ret;
273 char *device;
274
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100275 if (nargs != 2) {
276 fprintf(stderr, "Usage: mmc writeprotect boot set </path/to/mmcblkX>\n");
277 exit(1);
278 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500279
280 device = argv[1];
281
282 fd = open(device, O_RDWR);
283 if (fd < 0) {
284 perror("open");
285 exit(1);
286 }
287
288 ret = read_extcsd(fd, ext_csd);
289 if (ret) {
290 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
291 exit(1);
292 }
293
294 value = ext_csd[EXT_CSD_BOOT_WP] |
295 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
296 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
297 if (ret) {
298 fprintf(stderr, "Could not write 0x%02x to "
299 "EXT_CSD[%d] in %s\n",
300 value, EXT_CSD_BOOT_WP, device);
301 exit(1);
302 }
303
304 return ret;
305}
306
Al Cooper1b7f5d72016-06-07 16:35:46 -0400307static char *prot_desc[] = {
308 "No",
309 "Temporary",
310 "Power-on",
311 "Permanent"
312};
313
314static void print_wp_status(__u32 wp_sizeblks, __u32 start_group,
315 __u32 end_group, int rptype)
316{
317 printf("Write Protect Groups %d-%d (Blocks %d-%d), ",
318 start_group, end_group,
319 start_group * wp_sizeblks, ((end_group + 1) * wp_sizeblks) - 1);
320 printf("%s Write Protection\n", prot_desc[rptype]);
321}
322
323
324int do_writeprotect_user_get(int nargs, char **argv)
325{
326 __u8 ext_csd[512];
327 int fd, ret;
328 char *device;
329 int x;
330 int y = 0;
331 __u32 wp_sizeblks;
332 __u32 dev_sizeblks;
333 __u32 cnt;
334 __u64 bits;
335 __u32 wpblk;
336 __u32 last_wpblk = 0;
337 __u32 prot;
338 __u32 last_prot = -1;
339 int remain;
340
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100341 if (nargs != 2) {
342 fprintf(stderr, "Usage: mmc writeprotect user get </path/to/mmcblkX>\n");
343 exit(1);
344 }
Al Cooper1b7f5d72016-06-07 16:35:46 -0400345
346 device = argv[1];
347
348 fd = open(device, O_RDWR);
349 if (fd < 0) {
350 perror("open");
351 exit(1);
352 }
353 ret = read_extcsd(fd, ext_csd);
354 if (ret) {
355 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
356 exit(1);
357 }
358
359 ret = get_wp_group_size_in_blks(ext_csd, &wp_sizeblks);
360 if (ret)
361 exit(1);
362 printf("Write Protect Group size in blocks/bytes: %d/%d\n",
363 wp_sizeblks, wp_sizeblks * 512);
364 dev_sizeblks = get_size_in_blks(fd);
365 cnt = dev_sizeblks / wp_sizeblks;
366 for (x = 0; x < cnt; x += WP_BLKS_PER_QUERY) {
367 ret = send_write_protect_type(fd, x * wp_sizeblks, &bits);
368 if (ret)
369 break;
370 remain = cnt - x;
371 if (remain > WP_BLKS_PER_QUERY)
372 remain = WP_BLKS_PER_QUERY;
373 for (y = 0; y < remain; y++) {
374 prot = (bits >> (y * 2)) & 0x3;
375 if (prot != last_prot) {
376 /* not first time */
377 if (last_prot != -1) {
378 wpblk = x + y;
379 print_wp_status(wp_sizeblks,
380 last_wpblk,
381 wpblk - 1,
382 last_prot);
383 last_wpblk = wpblk;
384 }
385 last_prot = prot;
386 }
387 }
388 }
389 if (last_wpblk != (x + y - 1))
390 print_wp_status(wp_sizeblks, last_wpblk, cnt - 1, last_prot);
391
392 return ret;
393}
394
395int do_writeprotect_user_set(int nargs, char **argv)
396{
397 __u8 ext_csd[512];
398 int fd, ret;
399 char *device;
400 int blk_start;
401 int blk_cnt;
402 __u32 wp_blks;
403 __u8 user_wp;
404 int x;
405 int wptype;
406
407 if (nargs != 5)
408 goto usage;
409 device = argv[4];
410 fd = open(device, O_RDWR);
411 if (fd < 0) {
412 perror("open");
413 exit(1);
414 }
415 if (!strcmp(argv[1], "none")) {
416 wptype = WPTYPE_NONE;
417 } else if (!strcmp(argv[1], "temp")) {
418 wptype = WPTYPE_TEMP;
419 } else if (!strcmp(argv[1], "pwron")) {
420 wptype = WPTYPE_PWRON;
421#ifdef DANGEROUS_COMMANDS_ENABLED
422 } else if (!strcmp(argv[1], "perm")) {
423 wptype = WPTYPE_PERM;
424#endif /* DANGEROUS_COMMANDS_ENABLED */
425 } else {
426 fprintf(stderr, "Error, invalid \"type\"\n");
427 goto usage;
428 }
429 ret = read_extcsd(fd, ext_csd);
430 if (ret) {
431 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
432 exit(1);
433 }
434 ret = get_wp_group_size_in_blks(ext_csd, &wp_blks);
435 if (ret) {
436 fprintf(stderr, "Operation not supported for this device\n");
437 exit(1);
438 }
439 blk_start = strtol(argv[2], NULL, 0);
440 blk_cnt = strtol(argv[3], NULL, 0);
441 if ((blk_start % wp_blks) || (blk_cnt % wp_blks)) {
442 fprintf(stderr, "<start block> and <blocks> must be a ");
443 fprintf(stderr, "multiple of the Write Protect Group (%d)\n",
444 wp_blks);
445 exit(1);
446 }
447 if (wptype != WPTYPE_NONE) {
448 user_wp = ext_csd[EXT_CSD_USER_WP];
449 user_wp &= ~USER_WP_CLEAR;
450 switch (wptype) {
451 case WPTYPE_TEMP:
452 break;
453 case WPTYPE_PWRON:
454 user_wp |= USER_WP_US_PWR_WP_EN;
455 break;
456 case WPTYPE_PERM:
457 user_wp |= USER_WP_US_PERM_WP_EN;
458 break;
459 }
460 if (user_wp != ext_csd[EXT_CSD_USER_WP]) {
461 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
462 if (ret) {
463 fprintf(stderr, "Error setting EXT_CSD\n");
464 exit(1);
465 }
466 }
467 }
468 for (x = 0; x < blk_cnt; x += wp_blks) {
469 ret = set_write_protect(fd, blk_start + x,
470 wptype != WPTYPE_NONE);
471 if (ret) {
472 fprintf(stderr,
473 "Could not set write protect for %s\n", device);
474 exit(1);
475 }
476 }
477 if (wptype != WPTYPE_NONE) {
478 ret = write_extcsd_value(fd, EXT_CSD_USER_WP,
479 ext_csd[EXT_CSD_USER_WP]);
480 if (ret) {
481 fprintf(stderr, "Error restoring EXT_CSD\n");
482 exit(1);
483 }
484 }
485 return ret;
486
487usage:
488 fprintf(stderr,
489 "Usage: mmc writeprotect user set <type><start block><blocks><device>\n");
490 exit(1);
491}
492
Saugata Dasb7e25992012-05-17 09:26:34 -0400493int do_disable_512B_emulation(int nargs, char **argv)
494{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800495 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400496 int fd, ret;
497 char *device;
498
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100499 if (nargs != 2) {
500 fprintf(stderr, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n");
501 exit(1);
502 }
503
Saugata Dasb7e25992012-05-17 09:26:34 -0400504 device = argv[1];
505
506 fd = open(device, O_RDWR);
507 if (fd < 0) {
508 perror("open");
509 exit(1);
510 }
511
512 ret = read_extcsd(fd, ext_csd);
513 if (ret) {
514 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
515 exit(1);
516 }
517
518 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
519 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
520 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
521
522 if (native_sector_size && !data_sector_size &&
523 (wr_rel_param & EN_REL_WR)) {
524 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
525
526 if (ret) {
527 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
Tomas Melin295dd7a2016-08-29 11:45:44 -0400528 1, EXT_CSD_NATIVE_SECTOR_SIZE, device);
Saugata Dasb7e25992012-05-17 09:26:34 -0400529 exit(1);
530 }
531 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
532 } else if (native_sector_size && data_sector_size) {
533 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
534 } else {
535 printf("MMC does not support disabling 512B emulation mode.\n");
536 }
537
538 return ret;
539}
540
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200541int do_write_boot_en(int nargs, char **argv)
542{
543 __u8 ext_csd[512];
544 __u8 value = 0;
545 int fd, ret;
546 char *device;
547 int boot_area, send_ack;
548
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100549 if (nargs != 4) {
550 fprintf(stderr, "Usage: mmc bootpart enable <partition_number> <send_ack> </path/to/mmcblkX>\n");
551 exit(1);
552 }
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200553
554 /*
555 * If <send_ack> is 1, the device will send acknowledgment
556 * pattern "010" to the host when boot operation begins.
557 * If <send_ack> is 0, it won't.
558 */
559 boot_area = strtol(argv[1], NULL, 10);
560 send_ack = strtol(argv[2], NULL, 10);
561 device = argv[3];
562
563 fd = open(device, O_RDWR);
564 if (fd < 0) {
565 perror("open");
566 exit(1);
567 }
568
569 ret = read_extcsd(fd, ext_csd);
570 if (ret) {
571 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
572 exit(1);
573 }
574
575 value = ext_csd[EXT_CSD_PART_CONFIG];
576
577 switch (boot_area) {
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100578 case EXT_CSD_PART_CONFIG_ACC_NONE:
579 value &= ~(7 << 3);
580 break;
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200581 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
582 value |= (1 << 3);
583 value &= ~(3 << 4);
584 break;
585 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
586 value |= (1 << 4);
587 value &= ~(1 << 3);
588 value &= ~(1 << 5);
589 break;
590 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
591 value |= (boot_area << 3);
592 break;
593 default:
594 fprintf(stderr, "Cannot enable the boot area\n");
595 exit(1);
596 }
597 if (send_ack)
598 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
599 else
600 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
601
602 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
603 if (ret) {
604 fprintf(stderr, "Could not write 0x%02x to "
605 "EXT_CSD[%d] in %s\n",
606 value, EXT_CSD_PART_CONFIG, device);
607 exit(1);
608 }
609 return ret;
610}
611
Al Cooper794314c2015-05-01 08:24:37 -0400612int do_boot_bus_conditions_set(int nargs, char **argv)
613{
614 __u8 ext_csd[512];
615 __u8 value = 0;
616 int fd, ret;
617 char *device;
618
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100619 if (nargs != 5) {
620 fprintf(stderr, "Usage: mmc: bootbus set <boot_mode> <reset_boot_bus_conditions> <boot_bus_width> <device>\n");
621 exit(1);
622 }
Al Cooper794314c2015-05-01 08:24:37 -0400623
624 if (strcmp(argv[1], "single_backward") == 0)
625 value |= 0;
626 else if (strcmp(argv[1], "single_hs") == 0)
627 value |= 0x8;
628 else if (strcmp(argv[1], "dual") == 0)
629 value |= 0x10;
630 else {
631 fprintf(stderr, "illegal <boot_mode> specified\n");
632 exit(1);
633 }
634
635 if (strcmp(argv[2], "x1") == 0)
636 value |= 0;
637 else if (strcmp(argv[2], "retain") == 0)
638 value |= 0x4;
639 else {
640 fprintf(stderr,
641 "illegal <reset_boot_bus_conditions> specified\n");
642 exit(1);
643 }
644
645 if (strcmp(argv[3], "x1") == 0)
646 value |= 0;
647 else if (strcmp(argv[3], "x4") == 0)
648 value |= 0x1;
649 else if (strcmp(argv[3], "x8") == 0)
650 value |= 0x2;
651 else {
652 fprintf(stderr, "illegal <boot_bus_width> specified\n");
653 exit(1);
654 }
655
656 device = argv[4];
657 fd = open(device, O_RDWR);
658 if (fd < 0) {
659 perror("open");
660 exit(1);
661 }
662
663 ret = read_extcsd(fd, ext_csd);
664 if (ret) {
665 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
666 exit(1);
667 }
668 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
669 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
670
671 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
672 if (ret) {
673 fprintf(stderr, "Could not write 0x%02x to "
674 "EXT_CSD[%d] in %s\n",
675 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
676 exit(1);
677 }
678 close(fd);
679 return ret;
680}
681
Chris Ballf74dfe22012-10-19 16:49:55 -0400682int do_hwreset(int value, int nargs, char **argv)
683{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800684 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400685 int fd, ret;
686 char *device;
687
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100688 if (nargs != 2) {
689 fprintf(stderr, "Usage: mmc hwreset enable </path/to/mmcblkX>\n");
690 exit(1);
691 }
Chris Ballf74dfe22012-10-19 16:49:55 -0400692
693 device = argv[1];
694
695 fd = open(device, O_RDWR);
696 if (fd < 0) {
697 perror("open");
698 exit(1);
699 }
700
701 ret = read_extcsd(fd, ext_csd);
702 if (ret) {
703 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
704 exit(1);
705 }
706
707 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
708 EXT_CSD_HW_RESET_EN) {
709 fprintf(stderr,
710 "H/W Reset is already permanently enabled on %s\n",
711 device);
712 exit(1);
713 }
714 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
715 EXT_CSD_HW_RESET_DIS) {
716 fprintf(stderr,
717 "H/W Reset is already permanently disabled on %s\n",
718 device);
719 exit(1);
720 }
721
722 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
723 if (ret) {
724 fprintf(stderr,
725 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
726 value, EXT_CSD_RST_N_FUNCTION, device);
727 exit(1);
728 }
729
730 return ret;
731}
732
733int do_hwreset_en(int nargs, char **argv)
734{
735 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
736}
737
738int do_hwreset_dis(int nargs, char **argv)
739{
740 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
741}
742
Jaehoon Chung86496512012-09-21 10:08:05 +0000743int do_write_bkops_en(int nargs, char **argv)
744{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800745 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000746 int fd, ret;
747 char *device;
748
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100749 if (nargs != 2) {
750 fprintf(stderr, "Usage: mmc bkops enable </path/to/mmcblkX>\n");
751 exit(1);
752 }
Jaehoon Chung86496512012-09-21 10:08:05 +0000753
754 device = argv[1];
755
756 fd = open(device, O_RDWR);
757 if (fd < 0) {
758 perror("open");
759 exit(1);
760 }
761
762 ret = read_extcsd(fd, ext_csd);
763 if (ret) {
764 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
765 exit(1);
766 }
767
768 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
769 fprintf(stderr, "%s doesn't support BKOPS\n", device);
770 exit(1);
771 }
772
773 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
774 if (ret) {
775 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
776 value, EXT_CSD_BKOPS_EN, device);
777 exit(1);
778 }
779
780 return ret;
781}
782
Ben Gardiner27c357d2013-05-30 17:12:47 -0400783int do_status_get(int nargs, char **argv)
784{
785 __u32 response;
786 int fd, ret;
787 char *device;
788
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100789 if (nargs != 2) {
790 fprintf(stderr, "Usage: mmc status get </path/to/mmcblkX>\n");
791 exit(1);
792 }
Ben Gardiner27c357d2013-05-30 17:12:47 -0400793
794 device = argv[1];
795
796 fd = open(device, O_RDWR);
797 if (fd < 0) {
798 perror("open");
799 exit(1);
800 }
801
802 ret = send_status(fd, &response);
803 if (ret) {
804 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
805 exit(1);
806 }
807
808 printf("SEND_STATUS response: 0x%08x\n", response);
809
810 return ret;
811}
812
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800813__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
814{
815 return (ext_csd_loc[3] << 24) |
816 (ext_csd_loc[2] << 16) |
817 (ext_csd_loc[1] << 8) |
818 ext_csd_loc[0];
819}
820
Ben Gardiner4e850232013-05-30 17:12:49 -0400821unsigned int get_sector_count(__u8 *ext_csd)
822{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800823 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400824}
825
826int is_blockaddresed(__u8 *ext_csd)
827{
828 unsigned int sectors = get_sector_count(ext_csd);
829
Tomas Melin2e610662016-08-29 11:41:10 -0400830 /* over 2GiB devices are block-addressed */
Ben Gardiner4e850232013-05-30 17:12:49 -0400831 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
832}
833
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400834unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
835{
836 return ext_csd[221];
837}
838
839unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
840{
841 return ext_csd[224];
842}
843
Ben Gardinere6e84e92013-09-19 11:14:27 -0400844int set_partitioning_setting_completed(int dry_run, const char * const device,
845 int fd)
846{
847 int ret;
848
Tomas Melin4dadd872016-08-29 11:55:08 -0400849 if (dry_run == 1) {
Ben Gardinere6e84e92013-09-19 11:14:27 -0400850 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
851 fprintf(stderr, "These changes will not take effect neither "
852 "now nor after a power cycle\n");
853 return 1;
Tomas Melin4dadd872016-08-29 11:55:08 -0400854 } else if (dry_run == 2) {
855 printf("-c given, expecting more partition settings before "
856 "writing PARTITION_SETTING_COMPLETED\n");
857 return 0;
Ben Gardinere6e84e92013-09-19 11:14:27 -0400858 }
859
860 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
861 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
862 if (ret) {
863 fprintf(stderr, "Could not write 0x1 to "
864 "EXT_CSD[%d] in %s\n",
865 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
866 return 1;
867 }
868
869 __u32 response;
870 ret = send_status(fd, &response);
871 if (ret) {
872 fprintf(stderr, "Could not get response to SEND_STATUS "
873 "from %s\n", device);
874 return 1;
875 }
876
877 if (response & R1_SWITCH_ERROR) {
878 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
879 "failed on %s\n", device);
880 return 1;
881 }
882
883 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
884 "%s SUCCESS\n", device);
885 fprintf(stderr, "Device power cycle needed for settings to "
886 "take effect.\n"
887 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
888 "using 'extcsd read' after power cycle\n");
889
890 return 0;
891}
892
Balaji T K1fdb7f92015-04-29 18:12:32 -0400893int check_enhanced_area_total_limit(const char * const device, int fd)
894{
895 __u8 ext_csd[512];
896 __u32 regl;
897 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
898 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400899 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400900 unsigned int wp_sz, erase_sz;
901 int ret;
902
903 ret = read_extcsd(fd, ext_csd);
904 if (ret) {
905 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
906 exit(1);
907 }
908 wp_sz = get_hc_wp_grp_size(ext_csd);
909 erase_sz = get_hc_erase_grp_size(ext_csd);
910
911 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
912 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
913 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
914 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
915 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
916 enh_area_sz += gp4_part_sz;
917 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
918 printf(" i.e. %lu KiB\n", gp4_part_sz);
919 }
920
921 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
922 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
923 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
924 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
925 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
926 enh_area_sz += gp3_part_sz;
927 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
928 printf(" i.e. %lu KiB\n", gp3_part_sz);
929 }
930
931 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
932 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
933 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
934 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
935 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
936 enh_area_sz += gp2_part_sz;
937 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
938 printf(" i.e. %lu KiB\n", gp2_part_sz);
939 }
940
941 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
942 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
943 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
944 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
945 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
946 enh_area_sz += gp1_part_sz;
947 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
948 printf(" i.e. %lu KiB\n", gp1_part_sz);
949 }
950
951 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
952 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
953 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
954 user_area_sz = 512l * regl * erase_sz * wp_sz;
955 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
956 enh_area_sz += user_area_sz;
957 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
958 printf(" i.e. %lu KiB\n", user_area_sz);
959 }
960
961 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
962 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
963 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
964 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
965 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
966 printf(" i.e. %lu KiB\n", max_enh_area_sz);
967 if (enh_area_sz > max_enh_area_sz) {
968 fprintf(stderr,
969 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
970 enh_area_sz, max_enh_area_sz, device);
971 return 1;
972 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400973 total_sz = get_sector_count(ext_csd) / 2;
974 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
975 gp1_part_sz + user_area_sz;
976 if (total_gp_user_sz > total_sz) {
977 fprintf(stderr,
978 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
979 total_gp_user_sz, total_sz, device);
980 return 1;
981 }
982
983 return 0;
984}
985
986int do_create_gp_partition(int nargs, char **argv)
987{
988 __u8 value;
989 __u8 ext_csd[512];
990 __u8 address;
991 int fd, ret;
992 char *device;
993 int dry_run = 1;
994 int partition, enh_attr, ext_attr;
995 unsigned int length_kib, gp_size_mult;
996 unsigned long align;
997
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100998 if (nargs != 7) {
999 fprintf(stderr, "Usage: mmc gp create <-y|-n|-c> <length KiB> <partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n");
1000 exit(1);
1001 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001002
Tomas Melin4dadd872016-08-29 11:55:08 -04001003 if (!strcmp("-y", argv[1])) {
Balaji T Kd78ce082015-04-29 18:12:33 -04001004 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001005 } else if (!strcmp("-c", argv[1])) {
1006 dry_run = 2;
1007 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001008
1009 length_kib = strtol(argv[2], NULL, 10);
1010 partition = strtol(argv[3], NULL, 10);
1011 enh_attr = strtol(argv[4], NULL, 10);
1012 ext_attr = strtol(argv[5], NULL, 10);
1013 device = argv[6];
1014
Marcus Folkessoncb04fde2015-11-18 15:06:16 -05001015 if (partition < 1 || partition > 4) {
1016 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -04001017 exit(1);
1018 }
1019
1020 if (enh_attr && ext_attr) {
1021 printf("Not allowed to set both enhanced attribute and extended attribute\n");
1022 exit(1);
1023 }
1024
1025 fd = open(device, O_RDWR);
1026 if (fd < 0) {
1027 perror("open");
1028 exit(1);
1029 }
1030
1031 ret = read_extcsd(fd, ext_csd);
1032 if (ret) {
1033 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1034 exit(1);
1035 }
1036
1037 /* assert not PARTITION_SETTING_COMPLETED */
1038 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
1039 printf(" Device is already partitioned\n");
1040 exit(1);
1041 }
1042
1043 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1044 gp_size_mult = (length_kib + align/2l) / align;
1045
1046 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1047 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1048 if (ret) {
1049 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
1050 EXT_CSD_ERASE_GROUP_DEF, device);
1051 exit(1);
1052 }
1053
1054 value = (gp_size_mult >> 16) & 0xff;
1055 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
1056 ret = write_extcsd_value(fd, address, value);
1057 if (ret) {
1058 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1059 value, address, device);
1060 exit(1);
1061 }
1062 value = (gp_size_mult >> 8) & 0xff;
1063 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
1064 ret = write_extcsd_value(fd, address, value);
1065 if (ret) {
1066 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1067 value, address, device);
1068 exit(1);
1069 }
1070 value = gp_size_mult & 0xff;
1071 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
1072 ret = write_extcsd_value(fd, address, value);
1073 if (ret) {
1074 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1075 value, address, device);
1076 exit(1);
1077 }
1078
1079 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1080 if (enh_attr)
1081 value |= (1 << partition);
1082 else
1083 value &= ~(1 << partition);
1084
1085 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
1086 if (ret) {
1087 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
1088 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1089 exit(1);
1090 }
1091
1092 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
1093 value = ext_csd[address];
1094 if (ext_attr)
1095 value |= (ext_attr << (4 * ((partition - 1) % 2)));
1096 else
1097 value &= (0xF << (4 * ((partition % 2))));
1098
1099 ret = write_extcsd_value(fd, address, value);
1100 if (ret) {
1101 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
1102 value, address, device);
1103 exit(1);
1104 }
1105
1106 ret = check_enhanced_area_total_limit(device, fd);
1107 if (ret)
1108 exit(1);
1109
Tomas Melin20379fa2016-08-29 12:02:28 -04001110 if (set_partitioning_setting_completed(dry_run, device, fd))
Balaji T Kd78ce082015-04-29 18:12:33 -04001111 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -04001112
1113 return 0;
1114}
1115
Ben Gardinerd91d3692013-05-30 17:12:51 -04001116int do_enh_area_set(int nargs, char **argv)
1117{
1118 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001119 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -04001120 int fd, ret;
1121 char *device;
1122 int dry_run = 1;
1123 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
1124 unsigned long align;
1125
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001126 if (nargs != 5) {
1127 fprintf(stderr, "Usage: mmc enh_area set <-y|-n|-c> <start KiB> <length KiB> </path/to/mmcblkX>\n");
1128 exit(1);
1129 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001130
Tomas Melin4dadd872016-08-29 11:55:08 -04001131 if (!strcmp("-y", argv[1])) {
Ben Gardinerd91d3692013-05-30 17:12:51 -04001132 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001133 } else if (!strcmp("-c", argv[1])) {
1134 dry_run = 2;
1135 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001136
1137 start_kib = strtol(argv[2], NULL, 10);
1138 length_kib = strtol(argv[3], NULL, 10);
1139 device = argv[4];
1140
1141 fd = open(device, O_RDWR);
1142 if (fd < 0) {
1143 perror("open");
1144 exit(1);
1145 }
1146
1147 ret = read_extcsd(fd, ext_csd);
1148 if (ret) {
1149 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1150 exit(1);
1151 }
1152
1153 /* assert ENH_ATTRIBUTE_EN */
1154 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
1155 {
1156 printf(" Device cannot have enhanced tech.\n");
1157 exit(1);
1158 }
1159
1160 /* assert not PARTITION_SETTING_COMPLETED */
1161 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1162 {
1163 printf(" Device is already partitioned\n");
1164 exit(1);
1165 }
1166
1167 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1168
1169 enh_size_mult = (length_kib + align/2l) / align;
1170
1171 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
1172 enh_start_addr /= align;
1173 enh_start_addr *= align;
1174
1175 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1176 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1177 if (ret) {
1178 fprintf(stderr, "Could not write 0x1 to "
1179 "EXT_CSD[%d] in %s\n",
1180 EXT_CSD_ERASE_GROUP_DEF, device);
1181 exit(1);
1182 }
1183
1184 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
1185 value = (enh_start_addr >> 24) & 0xff;
1186 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
1187 if (ret) {
1188 fprintf(stderr, "Could not write 0x%02x to "
1189 "EXT_CSD[%d] in %s\n", value,
1190 EXT_CSD_ENH_START_ADDR_3, device);
1191 exit(1);
1192 }
1193 value = (enh_start_addr >> 16) & 0xff;
1194 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
1195 if (ret) {
1196 fprintf(stderr, "Could not write 0x%02x to "
1197 "EXT_CSD[%d] in %s\n", value,
1198 EXT_CSD_ENH_START_ADDR_2, device);
1199 exit(1);
1200 }
1201 value = (enh_start_addr >> 8) & 0xff;
1202 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
1203 if (ret) {
1204 fprintf(stderr, "Could not write 0x%02x to "
1205 "EXT_CSD[%d] in %s\n", value,
1206 EXT_CSD_ENH_START_ADDR_1, device);
1207 exit(1);
1208 }
1209 value = enh_start_addr & 0xff;
1210 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
1211 if (ret) {
1212 fprintf(stderr, "Could not write 0x%02x to "
1213 "EXT_CSD[%d] in %s\n", value,
1214 EXT_CSD_ENH_START_ADDR_0, device);
1215 exit(1);
1216 }
1217
1218 value = (enh_size_mult >> 16) & 0xff;
1219 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
1220 if (ret) {
1221 fprintf(stderr, "Could not write 0x%02x to "
1222 "EXT_CSD[%d] in %s\n", value,
1223 EXT_CSD_ENH_SIZE_MULT_2, device);
1224 exit(1);
1225 }
1226 value = (enh_size_mult >> 8) & 0xff;
1227 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
1228 if (ret) {
1229 fprintf(stderr, "Could not write 0x%02x to "
1230 "EXT_CSD[%d] in %s\n", value,
1231 EXT_CSD_ENH_SIZE_MULT_1, device);
1232 exit(1);
1233 }
1234 value = enh_size_mult & 0xff;
1235 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
1236 if (ret) {
1237 fprintf(stderr, "Could not write 0x%02x to "
1238 "EXT_CSD[%d] in %s\n", value,
1239 EXT_CSD_ENH_SIZE_MULT_0, device);
1240 exit(1);
1241 }
Balaji T K1fdb7f92015-04-29 18:12:32 -04001242 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
1243 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001244 if (ret) {
1245 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
1246 "EXT_CSD[%d] in %s\n",
1247 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1248 exit(1);
1249 }
1250
Balaji T K1fdb7f92015-04-29 18:12:32 -04001251 ret = check_enhanced_area_total_limit(device, fd);
1252 if (ret)
1253 exit(1);
1254
Ben Gardinere6e84e92013-09-19 11:14:27 -04001255 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001256
Tomas Melin20379fa2016-08-29 12:02:28 -04001257 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -04001258 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001259
1260 return 0;
1261}
1262
Ben Gardiner196d0d22013-09-19 11:14:29 -04001263int do_write_reliability_set(int nargs, char **argv)
1264{
1265 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001266 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -04001267 int fd, ret;
1268
1269 int dry_run = 1;
1270 int partition;
1271 char *device;
1272
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001273 if (nargs != 4) {
1274 fprintf(stderr,"Usage: mmc write_reliability set <-y|-n|-c> <partition> </path/to/mmcblkX>\n");
1275 exit(1);
1276 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001277
Tomas Melin77c22a82016-09-01 11:49:01 -04001278 if (!strcmp("-y", argv[1])) {
Ben Gardiner196d0d22013-09-19 11:14:29 -04001279 dry_run = 0;
Tomas Melin77c22a82016-09-01 11:49:01 -04001280 } else if (!strcmp("-c", argv[1])) {
1281 dry_run = 2;
1282 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001283
1284 partition = strtol(argv[2], NULL, 10);
1285 device = argv[3];
1286
1287 fd = open(device, O_RDWR);
1288 if (fd < 0) {
1289 perror("open");
1290 exit(1);
1291 }
1292
1293 ret = read_extcsd(fd, ext_csd);
1294 if (ret) {
1295 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1296 exit(1);
1297 }
1298
1299 /* assert not PARTITION_SETTING_COMPLETED */
1300 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1301 {
1302 printf(" Device is already partitioned\n");
1303 exit(1);
1304 }
1305
1306 /* assert HS_CTRL_REL */
1307 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
1308 printf("Cannot set write reliability parameters, WR_REL_SET is "
1309 "read-only\n");
1310 exit(1);
1311 }
1312
1313 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
1314 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
1315 if (ret) {
1316 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1317 value, EXT_CSD_WR_REL_SET, device);
1318 exit(1);
1319 }
1320
1321 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1322 value, device);
1323
Tomas Melin20379fa2016-08-29 12:02:28 -04001324 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardiner196d0d22013-09-19 11:14:29 -04001325 exit(1);
1326
1327 return 0;
1328}
1329
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001330int do_read_extcsd(int nargs, char **argv)
1331{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001332 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001333 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001334 int fd, ret;
1335 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001336 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001337 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001338 "4.0", /* 0 */
1339 "4.1", /* 1 */
1340 "4.2", /* 2 */
1341 "4.3", /* 3 */
1342 "Obsolete", /* 4 */
1343 "4.41", /* 5 */
1344 "4.5", /* 6 */
1345 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001346 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001347 };
1348 int boot_access;
1349 const char* boot_access_str[] = {
1350 "No access to boot partition", /* 0 */
1351 "R/W Boot Partition 1", /* 1 */
1352 "R/W Boot Partition 2", /* 2 */
1353 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1354 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001355
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001356 if (nargs != 2) {
1357 fprintf(stderr, "Usage: mmc extcsd read </path/to/mmcblkX>\n");
1358 exit(1);
1359 }
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001360
1361 device = argv[1];
1362
1363 fd = open(device, O_RDWR);
1364 if (fd < 0) {
1365 perror("open");
1366 exit(1);
1367 }
1368
1369 ret = read_extcsd(fd, ext_csd);
1370 if (ret) {
1371 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1372 exit(1);
1373 }
1374
Al Cooper786418c2015-04-29 18:12:35 -04001375 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001376
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001377 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1378 (ext_csd_rev != 4))
1379 str = ver_str[ext_csd_rev];
1380 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001381 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001382
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001383 printf("=============================================\n");
1384 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1385 printf("=============================================\n\n");
1386
1387 if (ext_csd_rev < 3)
1388 goto out_free; /* No ext_csd */
1389
1390 /* Parse the Extended CSD registers.
1391 * Reserved bit should be read as "0" in case of spec older
1392 * than A441.
1393 */
1394 reg = ext_csd[EXT_CSD_S_CMD_SET];
1395 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1396 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001397 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001398
1399 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1400 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1401 if (reg & EXT_CSD_HPI_SUPP) {
1402 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001403 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001404 else
1405 printf("implementation based on CMD13\n");
1406 }
1407
1408 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1409 ext_csd[502]);
1410
1411 if (ext_csd_rev >= 6) {
1412 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1413 ext_csd[501]);
1414 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1415 ext_csd[500]);
1416 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1417 ext_csd[499]);
1418
1419 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1420 ext_csd[498]);
1421 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1422 ext_csd[497]);
1423 printf("Context Management Capabilities"
1424 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1425 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1426 ext_csd[495]);
1427 printf("Extended partition attribute support"
1428 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001429 }
1430 if (ext_csd_rev >= 7) {
1431 int j;
1432 int eol_info;
1433 char* eol_info_str[] = {
1434 "Not Defined", /* 0 */
1435 "Normal", /* 1 */
1436 "Warning", /* 2 */
1437 "Urgent", /* 3 */
1438 };
1439
1440 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1441 ext_csd[493]);
1442 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1443 ext_csd[492]);
1444 printf("Operation codes timeout"
1445 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1446 ext_csd[491]);
1447 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1448 get_word_from_ext_csd(&ext_csd[487]));
1449 printf("Number of FW sectors correctly programmed"
1450 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1451 get_word_from_ext_csd(&ext_csd[302]));
1452 printf("Vendor proprietary health report:\n");
1453 for (j = 301; j >= 270; j--)
1454 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1455 " 0x%02x\n", j, ext_csd[j]);
1456 for (j = 269; j >= 268; j--) {
1457 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001458 char est_type = 'B' + (j - 269);
1459 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001460 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001461 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001462 if (life_used >= 0x1 && life_used <= 0xa)
1463 printf(" i.e. %d%% - %d%% device life time"
1464 " used\n",
1465 (life_used - 1) * 10, life_used * 10);
1466 else if (life_used == 0xb)
1467 printf(" i.e. Exceeded its maximum estimated"
1468 " device life time\n");
1469 }
1470 eol_info = ext_csd[267];
1471 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1472 eol_info);
1473 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1474 printf(" i.e. %s\n", eol_info_str[eol_info]);
1475 else
1476 printf(" i.e. Reserved\n");
1477
1478 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1479 ext_csd[266]);
1480 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1481 ext_csd[265]);
1482 printf("Optimal trim unit size"
1483 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1484 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1485 ext_csd[263], ext_csd[262]);
1486 printf("Firmware version:\n");
1487 for (j = 261; j >= 254; j--)
1488 printf("[FIRMWARE_VERSION[%d]]:"
1489 " 0x%02x\n", j, ext_csd[j]);
1490
1491 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1492 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1493 }
1494 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001495 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1496 ext_csd[248]);
1497 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1498 ext_csd[247]);
1499 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001500 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001501 }
1502
1503 /* A441: Reserved [501:247]
1504 A43: reserved [246:229] */
1505 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001506 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001507 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001508
1509 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1510
1511 printf("1st Initialisation Time after programmed sector"
1512 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1513
1514 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001515 printf("Power class for 52MHz, DDR at 3.6V"
1516 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1517 printf("Power class for 52MHz, DDR at 1.95V"
1518 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1519
1520 /* A441: reserved [237-236] */
1521
1522 if (ext_csd_rev >= 6) {
1523 printf("Power class for 200MHz at 3.6V"
1524 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1525 printf("Power class for 200MHz, at 1.95V"
1526 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1527 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001528 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001529 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1530 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1531 /* A441: reserved [233] */
1532 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1533 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1534 ext_csd[231]);
1535 }
1536 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1537 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1538 ext_csd[230]);
1539 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1540 ext_csd[229]);
1541 }
1542 reg = ext_csd[EXT_CSD_BOOT_INFO];
1543 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1544 if (reg & EXT_CSD_BOOT_INFO_ALT)
1545 printf(" Device supports alternative boot method\n");
1546 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1547 printf(" Device supports dual data rate during boot\n");
1548 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1549 printf(" Device supports high speed timing during boot\n");
1550
1551 /* A441/A43: reserved [227] */
1552 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1553 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001554
1555 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001556 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001557 reg);
1558 printf(" i.e. %u KiB\n", 512 * reg);
1559
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001560 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1561 ext_csd[223]);
1562 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1563 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001564
1565 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001566 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001567 reg);
1568 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1569
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001570 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1571 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1572 /* A441/A43: reserved [218] */
1573 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1574 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001575
1576 unsigned int sectors = get_sector_count(ext_csd);
1577 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1578 if (is_blockaddresed(ext_csd))
1579 printf(" Device is block-addressed\n");
1580 else
1581 printf(" Device is NOT block-addressed\n");
1582
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001583 /* A441/A43: reserved [211] */
1584 printf("Minimum Write Performance for 8bit:\n");
1585 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1586 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1587 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1588 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1589 printf("Minimum Write Performance for 4bit:\n");
1590 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1591 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1592 /* A441/A43: reserved [204] */
1593 printf("Power classes registers:\n");
1594 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1595 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1596 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1597 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1598
1599 /* A43: reserved [199:198] */
1600 if (ext_csd_rev >= 5) {
1601 printf("Partition switching timing "
1602 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1603 printf("Out-of-interrupt busy timing"
1604 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1605 }
1606
1607 /* A441/A43: reserved [197] [195] [193] [190] [188]
1608 * [186] [184] [182] [180] [176] */
1609
1610 if (ext_csd_rev >= 6)
1611 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1612 ext_csd[197]);
1613
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001614 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001615 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1616 ext_csd[196], ext_csd[195]);
1617 reg = ext_csd[195];
1618 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1619 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001620 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001621 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1622 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001623 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1624 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1625 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1626 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1627 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1628 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001629
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001630 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001631 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001632 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1633 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1634 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1635 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1636 ext_csd[185]);
1637 /* bus_width: ext_csd[183] not readable */
1638 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1639 ext_csd[181]);
1640 reg = ext_csd[EXT_CSD_BOOT_CFG];
1641 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001642 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001643 case 0x0:
1644 printf(" Not boot enable\n");
1645 break;
1646 case 0x1:
1647 printf(" Boot Partition 1 enabled\n");
1648 break;
1649 case 0x2:
1650 printf(" Boot Partition 2 enabled\n");
1651 break;
1652 case 0x7:
1653 printf(" User Area Enabled for boot\n");
1654 break;
1655 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001656 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1657 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1658 printf(" %s\n", boot_access_str[boot_access]);
1659 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001660 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001661 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001662
1663 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1664 ext_csd[178]);
1665 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1666 ext_csd[177]);
1667 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001668 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001669
Al Cooper1b7f5d72016-06-07 16:35:46 -04001670 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -05001671
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001672 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001673 /* A441]: reserved [172] */
1674 printf("User area write protection register"
1675 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1676 /* A441]: reserved [170] */
1677 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1678 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001679
1680 reg = ext_csd[EXT_CSD_WR_REL_SET];
1681 const char * const fast = "existing data is at risk if a power "
1682 "failure occurs during a write operation";
1683 const char * const reliable = "the device protects existing "
1684 "data if a power failure occurs during a write "
1685 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001686 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001687 " [WR_REL_SET]: 0x%02x\n", reg);
1688
1689 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1690 int i;
1691 for (i = 1; i <= 4; i++) {
1692 printf(" partition %d: %s\n", i,
1693 reg & (1<<i) ? reliable : fast);
1694 }
1695
1696 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001697 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001698 " [WR_REL_PARAM]: 0x%02x\n", reg);
1699 if (reg & 0x01)
1700 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1701 if (reg & 0x04)
1702 printf(" Device supports the enhanced def. of reliable "
1703 "write\n");
1704
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001705 /* sanitize_start ext_csd[165]]: not readable
1706 * bkops_start ext_csd[164]]: only writable */
1707 printf("Enable background operations handshake"
1708 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1709 printf("H/W reset function"
1710 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1711 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001712 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001713 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1714 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001715 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001716 printf(" Device support partitioning feature\n");
1717 else
1718 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001719 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001720 printf(" Device can have enhanced tech.\n");
1721 else
1722 printf(" Device cannot have enhanced tech.\n");
1723
Oliver Metz11f2cea2013-09-23 08:40:52 +02001724 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001725 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1726 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1727
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001728 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001729 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001730 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1731 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001732 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001733
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001734 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001735 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001736 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001737 printf("Partitioning Setting"
1738 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001739 reg);
1740 if (reg)
1741 printf(" Device partition setting complete\n");
1742 else
1743 printf(" Device partition setting NOT complete\n");
1744
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001745 printf("General Purpose Partition Size\n"
1746 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1747 (ext_csd[153] << 8) | ext_csd[152]);
1748 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1749 (ext_csd[150] << 8) | ext_csd[149]);
1750 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1751 (ext_csd[147] << 8) | ext_csd[146]);
1752 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1753 (ext_csd[144] << 8) | ext_csd[143]);
1754
Oliver Metz11f2cea2013-09-23 08:40:52 +02001755 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001756 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1757 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001758 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001759 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1760 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001761 get_hc_erase_grp_size(ext_csd) *
1762 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001763
Oliver Metz11f2cea2013-09-23 08:40:52 +02001764 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001765 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1766 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1767 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001768 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001769 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001770 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Tomas Melin2e610662016-08-29 11:41:10 -04001771 512l : 1l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001772
1773 /* A441]: reserved [135] */
1774 printf("Bad Block Management mode"
1775 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1776 /* A441: reserved [133:0] */
1777 }
1778 /* B45 */
1779 if (ext_csd_rev >= 6) {
1780 int j;
1781 /* tcase_support ext_csd[132] not readable */
1782 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1783 ext_csd[131]);
1784 printf("Program CID/CSD in DDR mode support"
1785 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1786 ext_csd[130]);
1787
1788 for (j = 127; j >= 64; j--)
1789 printf("Vendor Specific Fields"
1790 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1791 j, ext_csd[j]);
1792
Gwendal Grignoue966e672014-07-07 14:03:13 -07001793 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001794 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001795 reg);
1796 if (reg == 0x00)
1797 printf(" i.e. 512 B\n");
1798 else if (reg == 0x01)
1799 printf(" i.e. 4 KiB\n");
1800 else
1801 printf(" i.e. Reserved\n");
1802
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001803 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1804 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001805 reg = ext_csd[61];
1806 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1807 if (reg == 0x00)
1808 printf(" i.e. 512 B\n");
1809 else if (reg == 0x01)
1810 printf(" i.e. 4 KiB\n");
1811 else
1812 printf(" i.e. Reserved\n");
1813
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001814 printf("1st initialization after disabling sector"
1815 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1816 ext_csd[60]);
1817 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1818 ext_csd[59]);
1819 printf("Number of addressed group to be Released"
1820 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1821 printf("Exception events control"
1822 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1823 (ext_csd[57] << 8) | ext_csd[56]);
1824 printf("Exception events status"
1825 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1826 (ext_csd[55] << 8) | ext_csd[54]);
1827 printf("Extended Partitions Attribute"
1828 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1829 (ext_csd[53] << 8) | ext_csd[52]);
1830
1831 for (j = 51; j >= 37; j--)
1832 printf("Context configuration"
1833 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1834
1835 printf("Packed command status"
1836 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1837 printf("Packed command failure index"
1838 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1839 printf("Power Off Notification"
1840 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001841 printf("Control to turn the Cache ON/OFF"
1842 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001843 /* flush_cache ext_csd[32] not readable */
1844 /*Reserved [31:0] */
1845 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001846 if (ext_csd_rev >= 7) {
1847 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1848 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1849 ext_csd[29]);
1850
1851 reg = ext_csd[26];
1852 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1853 switch (reg) {
1854 case 0x00:
1855 printf(" Success\n");
1856 break;
1857 case 0x10:
1858 printf(" General error\n");
1859 break;
1860 case 0x11:
1861 printf(" Firmware install error\n");
1862 break;
1863 case 0x12:
1864 printf(" Error in downloading firmware\n");
1865 break;
1866 default:
1867 printf(" Reserved\n");
1868 }
1869 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1870 " %d sector size\n",
1871 get_word_from_ext_csd(&ext_csd[22]));
1872 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1873 " %d sector size\n",
1874 get_word_from_ext_csd(&ext_csd[18]));
1875 printf("Product state awareness enablement"
1876 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1877 ext_csd[17]);
1878 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1879 ext_csd[16]);
1880 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001881
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05001882 if (ext_csd_rev >= 7) {
1883 printf("eMMC Firmware Version: %s\n",
1884 (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]);
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001885 printf("eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x%02x\n",
1886 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]);
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001887 printf("eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x%02x\n",
1888 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]);
Alexander Steincce3d882017-03-20 14:43:00 +01001889 printf("eMMC Pre EOL information [EXT_CSD_PRE_EOL_INFO]: 0x%02x\n",
1890 ext_csd[EXT_CSD_PRE_EOL_INFO]);
1891 }
1892
Adrian Hunterbb1600b2016-06-10 11:28:59 +03001893 if (ext_csd_rev >= 8) {
1894 printf("Command Queue Support [CMDQ_SUPPORT]: 0x%02x\n",
1895 ext_csd[EXT_CSD_CMDQ_SUPPORT]);
1896 printf("Command Queue Depth [CMDQ_DEPTH]: %u\n",
1897 (ext_csd[EXT_CSD_CMDQ_DEPTH] & 0x1f) + 1);
1898 printf("Command Enabled [CMDQ_MODE_EN]: 0x%02x\n",
1899 ext_csd[EXT_CSD_CMDQ_MODE_EN]);
1900 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001901out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001902 return ret;
1903}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001904
Nick Sanders9d57aa72014-03-05 21:38:54 -08001905int do_dump_extcsd(int nargs, char **argv)
1906{
1907 __u8 ext_csd[EXT_CSD_SIZE];
1908 int fd, ret;
1909 char *device;
1910 int i, j;
1911
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001912 if (nargs != 2) {
1913 fprintf(stderr, "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n");
1914 exit(1);
1915 }
Nick Sanders9d57aa72014-03-05 21:38:54 -08001916 device = argv[1];
1917
1918 fd = open(device, O_RDWR);
1919 if (fd < 0) {
1920 perror("Failed to open mmc device");
1921 exit(1);
1922 }
1923
1924 ret = read_extcsd(fd, ext_csd);
1925 if (ret) {
1926 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1927 exit(1);
1928 }
1929
1930 /* Dump all bytes so that any undecoded or proprietary registers */
1931 /* can be acessed. */
1932 printf("EXT_CSD binary dump:\n");
1933 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1934 printf(" %3d: %3x: ", i, i);
1935 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1936 printf(" %02x", ext_csd[i+j]);
1937 }
1938 printf("\n");
1939 }
1940
1941 return ret;
1942}
1943
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001944int do_sanitize(int nargs, char **argv)
1945{
1946 int fd, ret;
1947 char *device;
1948
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001949 if (nargs != 2) {
1950 fprintf(stderr, "Usage: mmc sanitize </path/to/mmcblkX>\n");
1951 exit(1);
1952 }
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001953
1954 device = argv[1];
1955
1956 fd = open(device, O_RDWR);
1957 if (fd < 0) {
1958 perror("open");
1959 exit(1);
1960 }
1961
1962 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1963 if (ret) {
1964 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1965 1, EXT_CSD_SANITIZE_START, device);
1966 exit(1);
1967 }
1968
1969 return ret;
1970
1971}
1972
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07001973enum blockprotect_mode {
1974 BLOCKPROTECT_TEMPORARY = 0,
1975 BLOCKPROTECT_POWERON,
1976 BLOCKPROTECT_PERMANENT,
1977};
1978
1979int write_blockprotect(int fd, __u32 sector, int enable)
1980{
1981 struct mmc_ioc_cmd cmd;
1982 int ret;
1983
1984 memset(&cmd, 0, sizeof(cmd));
1985 cmd.write_flag = 1;
1986 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
1987 cmd.arg = sector;
1988 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
1989
1990 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
1991 if (ret)
1992 perror("SET/CLR_WRITE_PROT command");
1993 return ret;
1994}
1995
1996int do_blockprotect_enable(int nargs, char **argv)
1997{
1998 __u8 ext_csd[EXT_CSD_SIZE];
1999 __u8 user_wp;
2000 __u32 sector;
2001 char *end;
2002 int ret, fd;
2003 int arg_index = 0;
2004 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
2005
2006 if (nargs > 0 && !strcmp(argv[1], "-r")) {
2007 arg_index++;
2008 mode = BLOCKPROTECT_POWERON;
2009 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
2010 arg_index++;
2011 mode = BLOCKPROTECT_PERMANENT;
2012 }
2013
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002014 if (nargs != 3 + arg_index) {
2015 fprintf(stderr, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n");
2016 exit(1);
2017 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002018 sector = strtoul(argv[2 + arg_index], &end, 0);
2019 if (*end != '\0') {
2020 fprintf(stderr, "Not a block number: %s\n",
2021 argv[2 + arg_index]);
2022 exit(1);
2023 }
2024
2025 fd = open(argv[1 + arg_index], O_RDWR);
2026 if (fd < 0) {
2027 perror("open");
2028 exit(1);
2029 }
2030
2031 if (read_extcsd(fd, ext_csd))
2032 exit(1);
2033
2034 user_wp = ext_csd[EXT_CSD_USER_WP];
2035 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2036 if (mode == BLOCKPROTECT_POWERON)
2037 user_wp |= EXT_CSD_US_PWR_WP_EN;
2038 else if (mode == BLOCKPROTECT_PERMANENT)
2039 user_wp |= EXT_CSD_US_PERM_WP_EN;
2040
2041 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
2042 if (ret) {
2043 perror("update EXT_CSD[USER_WP]");
2044 exit(1);
2045 }
2046
2047 usleep(INTER_COMMAND_GAP_US);
2048
2049 ret = write_blockprotect(fd, sector, 1);
2050
2051 usleep(INTER_COMMAND_GAP_US);
2052
2053 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2054 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
2055 perror("reset EXT_CSD[USER_WP]");
2056 if (!ret)
2057 ret = -1;
2058 }
2059
2060 return ret;
2061}
2062
2063int do_blockprotect_disable(int nargs, char **argv)
2064{
2065 __u32 sector;
2066 char *end;
2067 int fd;
2068
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002069 if (nargs != 3) {
2070 fprintf(stderr, "Usage: mmc blockprotect disable <device> <write protect block>\n");
2071 exit(1);
2072 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002073 sector = strtoul(argv[2], &end, 0);
2074 if (*end != '\0') {
2075 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2076 exit(1);
2077 }
2078
2079
2080 fd = open(argv[1], O_RDWR);
2081 if (fd < 0) {
2082 perror("open");
2083 exit(1);
2084 }
2085
2086 return write_blockprotect(fd, sector, 0);
2087}
2088
2089int do_blockprotect_read(int nargs, char **argv)
2090{
2091 __u8 wp_bits[8];
2092 __u32 sector;
2093 char *end;
2094 int fd;
2095 struct mmc_ioc_cmd cmd;
2096
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002097 if (nargs != 3) {
2098 fprintf(stderr, "Usage: mmc blockprotect read <device> <write protect block>\n");
2099 exit(1);
2100 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002101 fd = open(argv[1], O_RDWR);
2102 if (fd < 0) {
2103 perror("open");
2104 exit(1);
2105 }
2106
2107 sector = strtoul(argv[2], &end, 0);
2108 if (*end != '\0') {
2109 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2110 exit(1);
2111 }
2112
2113 memset(&cmd, 0, sizeof(cmd));
2114 cmd.write_flag = 0;
2115 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
2116 cmd.arg = sector;
2117 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2118 cmd.blksz = sizeof(wp_bits);
2119 cmd.blocks = 1;
2120 mmc_ioc_cmd_set_data(cmd, wp_bits);
2121
2122 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
2123 perror("SEND_WRITE_PROT_TYPE command");
2124 exit(1);
2125 }
2126
2127 printf("Sector %u write protection: ", sector);
2128 switch (wp_bits[7] & 3) {
2129 case 0:
2130 printf("NONE\n");
2131 break;
2132 case 1:
2133 printf("TEMPORARY\n");
2134 break;
2135 case 2:
2136 printf("POWER-ON\n");
2137 break;
2138 case 3:
2139 printf("PERMANENT\n");
2140 break;
2141 }
2142
2143 return 0;
2144}
2145
2146int do_blockprotect_info(int nargs, char **argv)
2147{
2148 __u8 ext_csd[EXT_CSD_SIZE];
2149 __u8 user_wp;
2150 int fd, wp_sz, erase_sz;
2151
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002152 if (nargs != 2) {
2153 fprintf(stderr, "Usage: mmc blockprotect info <device>\n");
2154 exit(1);
2155 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002156 fd = open(argv[1], O_RDWR);
2157 if (fd < 0) {
2158 perror("open");
2159 exit(1);
2160 }
2161
2162 if (read_extcsd(fd, ext_csd))
2163 exit(1);
2164
2165 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
2166 fprintf(stderr, "Block protection commands not supported: "
2167 "CLASS_6_CTRL set.\n");
2168 exit(1);
2169 }
2170
2171 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
2172 fprintf(stderr, "Block protection commands not supported: "
2173 "high-capacity sizes not enabled.\n");
2174 exit(1);
2175 }
2176
2177 wp_sz = get_hc_wp_grp_size(ext_csd);
2178 erase_sz = get_hc_erase_grp_size(ext_csd);
2179
2180 if (erase_sz == 0 || wp_sz == 0) {
2181 fprintf(stderr, "Block protection commands not supported: "
2182 "no high-capacity size for erase or WP blocks.\n");
2183 exit(1);
2184 }
2185
2186 printf("Write protect block size in sectors: %d\n",
2187 erase_sz * wp_sz * 1024);
2188
2189 user_wp = ext_csd[EXT_CSD_USER_WP];
2190 printf("Permanent write protection: %s\n",
2191 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
2192 printf("Power-on write protection: %s\n",
2193 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
2194
2195 return 0;
2196}
2197
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002198static const char* const mmc_ffu_hack_names[] = {
2199 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
2200};
2201
Gwendal Grignou771984c2014-07-01 12:46:18 -07002202int do_emmc50_ffu (int nargs, char **argv)
2203{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002204 int fd, ret, i, argc=1, ffu_hack=0;
2205 char *device, *type, *path;
2206 __u64 value;
2207 union {
2208 __u8 data[FFU_DATA_SIZE];
2209 struct mmc_ffu_args ffu_args;
2210 } ffu_data;
2211 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002212 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07002213
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002214 while (!strcmp("-k", argv[argc])) {
2215 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
2216 if (ret < 1) {
2217 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
2218 exit(1);
2219 }
2220 for (i = 0; i < MMC_HACK_LEN; i++) {
2221 if (!strcmp(type, mmc_ffu_hack_names[i])) {
2222 ffu_args->hack[ffu_hack].type = i;
2223 if (ret == 2) {
2224 ffu_args->hack[ffu_hack].value = value;
2225 }
2226 ffu_hack++;
2227 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
2228 sizeof(struct mmc_ffu_args) >
2229 FFU_DATA_SIZE) {
2230 fprintf(stderr, "Too many %d hacks",
2231 ffu_hack);
2232 exit(1);
2233 }
2234 break;
2235 }
2236 }
2237 if (i == MMC_HACK_LEN) {
2238 fprintf(stderr, "Hack type %s not found\n", type);
2239 fprintf(stderr, "Supported types are: ");
2240 for (i = 0; i < MMC_HACK_LEN; i++)
2241 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
2242 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07002243
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002244 exit(1);
2245 }
2246 free(type);
2247 argc++;
2248 }
2249 ffu_args->hack_nb = ffu_hack;
2250
2251 path = argv[argc++];
2252 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07002253 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
2254 exit(1);
2255 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002256 strcpy(ffu_args->name, path);
2257 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07002258 fd = open(device, O_RDWR);
2259 if (fd < 0) {
2260 perror("open");
2261 exit(1);
2262 }
2263
Gwendal Grignou0f757342014-10-16 16:52:46 -07002264 /* prepare and send ioctl */
2265 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
2266 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002267 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002268 mmc_ioc_cmd.blocks = 1;
2269 mmc_ioc_cmd.arg = 0;
2270 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2271 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002272 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07002273 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07002274 if (ret) {
2275 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
2276 exit(1);
2277 }
2278
2279 close(fd);
2280 return 0;
2281}
2282
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002283#define DO_IO(func, fd, buf, nbyte) \
2284 ({ \
2285 ssize_t ret = 0, r; \
2286 do { \
2287 r = func(fd, buf + ret, nbyte - ret); \
2288 if (r < 0 && errno != EINTR) { \
2289 ret = -1; \
2290 break; \
2291 } \
2292 else if (r > 0) \
2293 ret += r; \
2294 } while (r != 0 && (size_t)ret != nbyte); \
2295 \
2296 ret; \
2297 })
2298
2299enum rpmb_op_type {
2300 MMC_RPMB_WRITE_KEY = 0x01,
2301 MMC_RPMB_READ_CNT = 0x02,
2302 MMC_RPMB_WRITE = 0x03,
2303 MMC_RPMB_READ = 0x04,
2304
2305 /* For internal usage only, do not use it directly */
2306 MMC_RPMB_READ_RESP = 0x05
2307};
2308
2309struct rpmb_frame {
2310 u_int8_t stuff[196];
2311 u_int8_t key_mac[32];
2312 u_int8_t data[256];
2313 u_int8_t nonce[16];
2314 u_int32_t write_counter;
2315 u_int16_t addr;
2316 u_int16_t block_count;
2317 u_int16_t result;
2318 u_int16_t req_resp;
2319};
2320
2321/* Performs RPMB operation.
2322 *
2323 * @fd: RPMB device on which we should perform ioctl command
2324 * @frame_in: input RPMB frame, should be properly inited
2325 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
2326 * result and req_resp for output frame.
2327 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
2328 * in the other cases -EINVAL will be returned.
2329 */
2330static int do_rpmb_op(int fd,
2331 const struct rpmb_frame *frame_in,
2332 struct rpmb_frame *frame_out,
2333 unsigned int out_cnt)
2334{
2335 int err;
2336 u_int16_t rpmb_type;
2337
2338 struct mmc_ioc_cmd ioc = {
2339 .arg = 0x0,
2340 .blksz = 512,
2341 .blocks = 1,
2342 .write_flag = 1,
2343 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
2344 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
2345 .data_ptr = (uintptr_t)frame_in
2346 };
2347
2348 if (!frame_in || !frame_out || !out_cnt)
2349 return -EINVAL;
2350
2351 rpmb_type = be16toh(frame_in->req_resp);
2352
2353 switch(rpmb_type) {
2354 case MMC_RPMB_WRITE:
2355 case MMC_RPMB_WRITE_KEY:
2356 if (out_cnt != 1) {
2357 err = -EINVAL;
2358 goto out;
2359 }
2360
2361 /* Write request */
2362 ioc.write_flag |= (1<<31);
2363 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2364 if (err < 0) {
2365 err = -errno;
2366 goto out;
2367 }
2368
2369 /* Result request */
2370 memset(frame_out, 0, sizeof(*frame_out));
2371 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2372 ioc.write_flag = 1;
2373 ioc.data_ptr = (uintptr_t)frame_out;
2374 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2375 if (err < 0) {
2376 err = -errno;
2377 goto out;
2378 }
2379
2380 /* Get response */
2381 ioc.write_flag = 0;
2382 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2383 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2384 if (err < 0) {
2385 err = -errno;
2386 goto out;
2387 }
2388
2389 break;
2390 case MMC_RPMB_READ_CNT:
2391 if (out_cnt != 1) {
2392 err = -EINVAL;
2393 goto out;
2394 }
2395 /* fall through */
2396
2397 case MMC_RPMB_READ:
2398 /* Request */
2399 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2400 if (err < 0) {
2401 err = -errno;
2402 goto out;
2403 }
2404
2405 /* Get response */
2406 ioc.write_flag = 0;
2407 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2408 ioc.blocks = out_cnt;
2409 ioc.data_ptr = (uintptr_t)frame_out;
2410 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2411 if (err < 0) {
2412 err = -errno;
2413 goto out;
2414 }
2415
2416 break;
2417 default:
2418 err = -EINVAL;
2419 goto out;
2420 }
2421
2422out:
2423 return err;
2424}
2425
2426int do_rpmb_write_key(int nargs, char **argv)
2427{
2428 int ret, dev_fd, key_fd;
2429 struct rpmb_frame frame_in = {
2430 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2431 }, frame_out;
2432
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002433 if (nargs != 3) {
2434 fprintf(stderr, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n");
2435 exit(1);
2436 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002437
2438 dev_fd = open(argv[1], O_RDWR);
2439 if (dev_fd < 0) {
2440 perror("device open");
2441 exit(1);
2442 }
2443
2444 if (0 == strcmp(argv[2], "-"))
2445 key_fd = STDIN_FILENO;
2446 else {
2447 key_fd = open(argv[2], O_RDONLY);
2448 if (key_fd < 0) {
2449 perror("can't open key file");
2450 exit(1);
2451 }
2452 }
2453
2454 /* Read the auth key */
2455 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2456 if (ret < 0) {
2457 perror("read the key");
2458 exit(1);
2459 } else if (ret != sizeof(frame_in.key_mac)) {
2460 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2461 (unsigned long)sizeof(frame_in.key_mac),
2462 ret);
2463 exit(1);
2464 }
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 printf("RPMB operation failed, retcode 0x%04x\n",
2476 be16toh(frame_out.result));
2477 exit(1);
2478 }
2479
2480 close(dev_fd);
2481 if (key_fd != STDIN_FILENO)
2482 close(key_fd);
2483
2484 return ret;
2485}
2486
2487int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2488{
2489 int ret;
2490 struct rpmb_frame frame_in = {
2491 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2492 }, frame_out;
2493
2494 /* Execute RPMB op */
2495 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2496 if (ret != 0) {
2497 perror("RPMB ioctl failed");
2498 exit(1);
2499 }
2500
2501 /* Check RPMB response */
2502 if (frame_out.result != 0)
2503 return be16toh(frame_out.result);
2504
2505 *cnt = be32toh(frame_out.write_counter);
2506
2507 return 0;
2508}
2509
2510int do_rpmb_read_counter(int nargs, char **argv)
2511{
2512 int ret, dev_fd;
2513 unsigned int cnt;
2514
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002515 if (nargs != 2) {
2516 fprintf(stderr, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n");
2517 exit(1);
2518 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002519
2520 dev_fd = open(argv[1], O_RDWR);
2521 if (dev_fd < 0) {
2522 perror("device open");
2523 exit(1);
2524 }
2525
2526 ret = rpmb_read_counter(dev_fd, &cnt);
2527
2528 /* Check RPMB response */
2529 if (ret != 0) {
2530 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2531 exit(1);
2532 }
2533
2534 close(dev_fd);
2535
2536 printf("Counter value: 0x%08x\n", cnt);
2537
2538 return ret;
2539}
2540
2541int do_rpmb_read_block(int nargs, char **argv)
2542{
2543 int i, ret, dev_fd, data_fd, key_fd = -1;
2544 uint16_t addr, blocks_cnt;
2545 unsigned char key[32];
2546 struct rpmb_frame frame_in = {
2547 .req_resp = htobe16(MMC_RPMB_READ),
2548 }, *frame_out_p;
2549
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002550 if (nargs != 5 && nargs != 6) {
2551 fprintf(stderr, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n");
2552 exit(1);
2553 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002554
2555 dev_fd = open(argv[1], O_RDWR);
2556 if (dev_fd < 0) {
2557 perror("device open");
2558 exit(1);
2559 }
2560
2561 /* Get block address */
2562 errno = 0;
2563 addr = strtol(argv[2], NULL, 0);
2564 if (errno) {
2565 perror("incorrect address");
2566 exit(1);
2567 }
2568 frame_in.addr = htobe16(addr);
2569
2570 /* Get blocks count */
2571 errno = 0;
2572 blocks_cnt = strtol(argv[3], NULL, 0);
2573 if (errno) {
2574 perror("incorrect blocks count");
2575 exit(1);
2576 }
2577
2578 if (!blocks_cnt) {
2579 printf("please, specify valid blocks count number\n");
2580 exit(1);
2581 }
2582
2583 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2584 if (!frame_out_p) {
2585 printf("can't allocate memory for RPMB outer frames\n");
2586 exit(1);
2587 }
2588
2589 /* Write 256b data */
2590 if (0 == strcmp(argv[4], "-"))
2591 data_fd = STDOUT_FILENO;
2592 else {
2593 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2594 S_IRUSR | S_IWUSR);
2595 if (data_fd < 0) {
2596 perror("can't open output file");
2597 exit(1);
2598 }
2599 }
2600
2601 /* Key is specified */
2602 if (nargs == 6) {
2603 if (0 == strcmp(argv[5], "-"))
2604 key_fd = STDIN_FILENO;
2605 else {
2606 key_fd = open(argv[5], O_RDONLY);
2607 if (key_fd < 0) {
2608 perror("can't open input key file");
2609 exit(1);
2610 }
2611 }
2612
2613 ret = DO_IO(read, key_fd, key, sizeof(key));
2614 if (ret < 0) {
2615 perror("read the key data");
2616 exit(1);
2617 } else if (ret != sizeof(key)) {
2618 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2619 (unsigned long)sizeof(key),
2620 ret);
2621 exit(1);
2622 }
2623 }
2624
2625 /* Execute RPMB op */
2626 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2627 if (ret != 0) {
2628 perror("RPMB ioctl failed");
2629 exit(1);
2630 }
2631
2632 /* Check RPMB response */
2633 if (frame_out_p[blocks_cnt - 1].result != 0) {
2634 printf("RPMB operation failed, retcode 0x%04x\n",
2635 be16toh(frame_out_p[blocks_cnt - 1].result));
2636 exit(1);
2637 }
2638
2639 /* Do we have to verify data against key? */
2640 if (nargs == 6) {
2641 unsigned char mac[32];
2642 hmac_sha256_ctx ctx;
2643 struct rpmb_frame *frame_out = NULL;
2644
2645 hmac_sha256_init(&ctx, key, sizeof(key));
2646 for (i = 0; i < blocks_cnt; i++) {
2647 frame_out = &frame_out_p[i];
2648 hmac_sha256_update(&ctx, frame_out->data,
2649 sizeof(*frame_out) -
2650 offsetof(struct rpmb_frame, data));
2651 }
2652
2653 hmac_sha256_final(&ctx, mac, sizeof(mac));
2654
2655 /* Impossible */
2656 assert(frame_out);
2657
2658 /* Compare calculated MAC and MAC from last frame */
2659 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2660 printf("RPMB MAC missmatch\n");
2661 exit(1);
2662 }
2663 }
2664
2665 /* Write data */
2666 for (i = 0; i < blocks_cnt; i++) {
2667 struct rpmb_frame *frame_out = &frame_out_p[i];
2668 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2669 if (ret < 0) {
2670 perror("write the data");
2671 exit(1);
2672 } else if (ret != sizeof(frame_out->data)) {
2673 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2674 (unsigned long)sizeof(frame_out->data),
2675 ret);
2676 exit(1);
2677 }
2678 }
2679
2680 free(frame_out_p);
2681 close(dev_fd);
2682 if (data_fd != STDOUT_FILENO)
2683 close(data_fd);
2684 if (key_fd != -1 && key_fd != STDIN_FILENO)
2685 close(key_fd);
2686
2687 return ret;
2688}
2689
2690int do_rpmb_write_block(int nargs, char **argv)
2691{
2692 int ret, dev_fd, key_fd, data_fd;
2693 unsigned char key[32];
2694 uint16_t addr;
2695 unsigned int cnt;
2696 struct rpmb_frame frame_in = {
2697 .req_resp = htobe16(MMC_RPMB_WRITE),
2698 .block_count = htobe16(1)
2699 }, frame_out;
2700
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002701 if (nargs != 5) {
2702 fprintf(stderr, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n");
2703 exit(1);
2704 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002705
2706 dev_fd = open(argv[1], O_RDWR);
2707 if (dev_fd < 0) {
2708 perror("device open");
2709 exit(1);
2710 }
2711
2712 ret = rpmb_read_counter(dev_fd, &cnt);
2713 /* Check RPMB response */
2714 if (ret != 0) {
2715 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2716 exit(1);
2717 }
2718 frame_in.write_counter = htobe32(cnt);
2719
2720 /* Get block address */
2721 errno = 0;
2722 addr = strtol(argv[2], NULL, 0);
2723 if (errno) {
2724 perror("incorrect address");
2725 exit(1);
2726 }
2727 frame_in.addr = htobe16(addr);
2728
2729 /* Read 256b data */
2730 if (0 == strcmp(argv[3], "-"))
2731 data_fd = STDIN_FILENO;
2732 else {
2733 data_fd = open(argv[3], O_RDONLY);
2734 if (data_fd < 0) {
2735 perror("can't open input file");
2736 exit(1);
2737 }
2738 }
2739
2740 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2741 if (ret < 0) {
2742 perror("read the data");
2743 exit(1);
2744 } else if (ret != sizeof(frame_in.data)) {
2745 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2746 (unsigned long)sizeof(frame_in.data),
2747 ret);
2748 exit(1);
2749 }
2750
2751 /* Read the auth key */
2752 if (0 == strcmp(argv[4], "-"))
2753 key_fd = STDIN_FILENO;
2754 else {
2755 key_fd = open(argv[4], O_RDONLY);
2756 if (key_fd < 0) {
2757 perror("can't open key file");
2758 exit(1);
2759 }
2760 }
2761
2762 ret = DO_IO(read, key_fd, key, sizeof(key));
2763 if (ret < 0) {
2764 perror("read the key");
2765 exit(1);
2766 } else if (ret != sizeof(key)) {
2767 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2768 (unsigned long)sizeof(key),
2769 ret);
2770 exit(1);
2771 }
2772
2773 /* Calculate HMAC SHA256 */
2774 hmac_sha256(
2775 key, sizeof(key),
2776 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2777 frame_in.key_mac, sizeof(frame_in.key_mac));
2778
2779 /* Execute RPMB op */
2780 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2781 if (ret != 0) {
2782 perror("RPMB ioctl failed");
2783 exit(1);
2784 }
2785
2786 /* Check RPMB response */
2787 if (frame_out.result != 0) {
2788 printf("RPMB operation failed, retcode 0x%04x\n",
2789 be16toh(frame_out.result));
2790 exit(1);
2791 }
2792
2793 close(dev_fd);
2794 if (data_fd != STDIN_FILENO)
2795 close(data_fd);
2796 if (key_fd != STDIN_FILENO)
2797 close(key_fd);
2798
2799 return ret;
2800}
Al Cooper786418c2015-04-29 18:12:35 -04002801
2802int do_cache_ctrl(int value, int nargs, char **argv)
2803{
2804 __u8 ext_csd[512];
2805 int fd, ret;
2806 char *device;
2807
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002808 if (nargs != 2) {
2809 fprintf(stderr, "Usage: mmc cache enable </path/to/mmcblkX>\n");
2810 exit(1);
2811 }
Al Cooper786418c2015-04-29 18:12:35 -04002812
2813 device = argv[1];
2814
2815 fd = open(device, O_RDWR);
2816 if (fd < 0) {
2817 perror("open");
2818 exit(1);
2819 }
2820
2821 ret = read_extcsd(fd, ext_csd);
2822 if (ret) {
2823 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2824 exit(1);
2825 }
2826
2827 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2828 fprintf(stderr,
2829 "The CACHE option is only availabe on devices >= "
2830 "MMC 4.5 %s\n", device);
2831 exit(1);
2832 }
2833
2834 /* If the cache size is zero, this device does not have a cache */
2835 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2836 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2837 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2838 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2839 fprintf(stderr,
2840 "The CACHE option is not available on %s\n",
2841 device);
2842 exit(1);
2843 }
2844 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2845 if (ret) {
2846 fprintf(stderr,
2847 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2848 value, EXT_CSD_CACHE_CTRL, device);
2849 exit(1);
2850 }
2851
2852 return ret;
2853}
2854
2855int do_cache_en(int nargs, char **argv)
2856{
2857 return do_cache_ctrl(1, nargs, argv);
2858}
2859
2860int do_cache_dis(int nargs, char **argv)
2861{
2862 return do_cache_ctrl(0, nargs, argv);
2863}
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002864
2865int do_ffu(int nargs, char **argv)
2866{
2867#ifndef MMC_IOC_MULTI_CMD
2868 fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
2869 " support, needed by FFU.\n");
2870 exit(1);
2871#else
2872 int dev_fd, img_fd;
2873 int sect_done = 0, retry = 3, ret = -EINVAL;
2874 unsigned int sect_size;
2875 __u8 ext_csd[EXT_CSD_SIZE];
2876 __u8 *buf;
2877 __u32 arg;
2878 off_t fw_size;
2879 ssize_t chunk_size;
2880 char *device;
2881 struct mmc_ioc_multi_cmd *multi_cmd;
2882
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002883 if (nargs != 3) {
2884 fprintf(stderr, "Usage: ffu <image name> </path/to/mmcblkX> \n");
2885 exit(1);
2886 }
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002887
2888 device = argv[2];
2889 dev_fd = open(device, O_RDWR);
2890 if (dev_fd < 0) {
2891 perror("device open failed");
2892 exit(1);
2893 }
2894 img_fd = open(argv[1], O_RDONLY);
2895 if (img_fd < 0) {
2896 perror("image open failed");
2897 close(dev_fd);
2898 exit(1);
2899 }
2900
2901 buf = malloc(512);
2902 multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
2903 3 * sizeof(struct mmc_ioc_cmd));
2904 if (!buf || !multi_cmd) {
2905 perror("failed to allocate memory");
2906 goto out;
2907 }
2908
2909 ret = read_extcsd(dev_fd, ext_csd);
2910 if (ret) {
2911 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2912 goto out;
2913 }
2914
2915 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
2916 fprintf(stderr,
2917 "The FFU feature is only available on devices >= "
2918 "MMC 5.0, not supported in %s\n", device);
2919 goto out;
2920 }
2921
2922 if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
2923 fprintf(stderr, "FFU is not supported in %s\n", device);
2924 goto out;
2925 }
2926
2927 if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
2928 fprintf(stderr, "Firmware update was disabled in %s\n", device);
2929 goto out;
2930 }
2931
2932 fw_size = lseek(img_fd, 0, SEEK_END);
2933
2934 if (fw_size == 0) {
2935 fprintf(stderr, "Firmware image is empty");
2936 goto out;
2937 }
2938
2939 sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
2940 if (fw_size % sect_size) {
2941 fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
2942 goto out;
2943 }
2944
2945 /* set CMD ARG */
2946 arg = ext_csd[EXT_CSD_FFU_ARG_0] |
2947 ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
2948 ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
2949 ext_csd[EXT_CSD_FFU_ARG_3] << 24;
2950
2951 /* prepare multi_cmd to be sent */
2952 multi_cmd->num_of_cmds = 3;
2953
2954 /* put device into ffu mode */
2955 multi_cmd->cmds[0].opcode = MMC_SWITCH;
2956 multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2957 (EXT_CSD_MODE_CONFIG << 16) |
2958 (EXT_CSD_FFU_MODE << 8) |
2959 EXT_CSD_CMD_SET_NORMAL;
2960 multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2961 multi_cmd->cmds[0].write_flag = 1;
2962
2963 /* send image chunk */
2964 multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
2965 multi_cmd->cmds[1].blksz = sect_size;
2966 multi_cmd->cmds[1].blocks = 1;
2967 multi_cmd->cmds[1].arg = arg;
2968 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2969 multi_cmd->cmds[1].write_flag = 1;
2970 mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);
2971
2972 /* return device into normal mode */
2973 multi_cmd->cmds[2].opcode = MMC_SWITCH;
2974 multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2975 (EXT_CSD_MODE_CONFIG << 16) |
2976 (EXT_CSD_NORMAL_MODE << 8) |
2977 EXT_CSD_CMD_SET_NORMAL;
2978 multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2979 multi_cmd->cmds[2].write_flag = 1;
2980
2981do_retry:
2982 /* read firmware chunk */
2983 lseek(img_fd, 0, SEEK_SET);
2984 chunk_size = read(img_fd, buf, 512);
2985
2986 while (chunk_size > 0) {
2987 /* send ioctl with multi-cmd */
2988 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2989
2990 if (ret) {
2991 perror("Multi-cmd ioctl");
2992 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2993 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2994 goto out;
2995 }
2996
2997 ret = read_extcsd(dev_fd, ext_csd);
2998 if (ret) {
2999 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3000 goto out;
3001 }
3002
3003 /* Test if we need to restart the download */
3004 sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
3005 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
3006 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
3007 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
3008 /* By spec, host should re-start download from the first sector if sect_done is 0 */
3009 if (sect_done == 0) {
3010 if (retry > 0) {
3011 retry--;
3012 fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
3013 goto do_retry;
3014 }
3015 fprintf(stderr, "Programming failed! Aborting...\n");
3016 goto out;
3017 } else {
3018 fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
3019 }
3020
3021 /* read the next firmware chunk (if any) */
3022 chunk_size = read(img_fd, buf, 512);
3023 }
3024
3025 if ((sect_done * sect_size) == fw_size) {
3026 fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
3027 fprintf(stderr, "Programming finished with status %d \n", ret);
3028 }
3029 else {
3030 fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
3031 goto out;
3032 }
3033
3034 /* check mode operation for ffu install*/
3035 if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
3036 fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
3037 } else {
3038 fprintf(stderr, "Installing firmware on %s...\n", device);
3039 /* Re-enter ffu mode and install the firmware */
3040 multi_cmd->num_of_cmds = 2;
3041
3042 /* set ext_csd to install mode */
3043 multi_cmd->cmds[1].opcode = MMC_SWITCH;
3044 multi_cmd->cmds[1].blksz = 0;
3045 multi_cmd->cmds[1].blocks = 0;
3046 multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
3047 (EXT_CSD_MODE_OPERATION_CODES << 16) |
3048 (EXT_CSD_FFU_INSTALL << 8) |
3049 EXT_CSD_CMD_SET_NORMAL;
3050 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
3051 multi_cmd->cmds[1].write_flag = 1;
3052
3053 /* send ioctl with multi-cmd */
3054 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
3055
3056 if (ret) {
3057 perror("Multi-cmd ioctl failed setting install mode");
3058 /* In case multi-cmd ioctl failed before exiting from ffu mode */
3059 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
3060 goto out;
3061 }
3062
3063 ret = read_extcsd(dev_fd, ext_csd);
3064 if (ret) {
3065 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3066 goto out;
3067 }
3068
3069 /* return status */
3070 ret = ext_csd[EXT_CSD_FFU_STATUS];
3071 if (ret) {
3072 fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
3073 goto out;
3074 } else {
3075 fprintf(stderr, "FFU finished successfully\n");
3076 }
3077 }
3078
3079out:
3080 free(buf);
3081 free(multi_cmd);
3082 close(img_fd);
3083 close(dev_fd);
3084 return ret;
3085#endif
3086}