blob: ee5a80247021993f466b5dcd2844528ceb4c2e77 [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
Uwe Kleine-Königf16e4662017-12-21 11:35:08 +010052#ifndef offsetof
53#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
54#endif
55
Al Cooper1b7f5d72016-06-07 16:35:46 -040056#define WP_BLKS_PER_QUERY 32
57
58#define USER_WP_PERM_PSWD_DIS 0x80
59#define USER_WP_CD_PERM_WP_DIS 0x40
60#define USER_WP_US_PERM_WP_DIS 0x10
61#define USER_WP_US_PWR_WP_DIS 0x08
62#define USER_WP_US_PERM_WP_EN 0x04
63#define USER_WP_US_PWR_WP_EN 0x01
64#define USER_WP_CLEAR (USER_WP_US_PERM_WP_DIS | USER_WP_US_PWR_WP_DIS \
65 | USER_WP_US_PERM_WP_EN | USER_WP_US_PWR_WP_EN)
66
67#define WPTYPE_NONE 0
68#define WPTYPE_TEMP 1
69#define WPTYPE_PWRON 2
70#define WPTYPE_PERM 3
71
72
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050073int read_extcsd(int fd, __u8 *ext_csd)
74{
75 int ret = 0;
76 struct mmc_ioc_cmd idata;
77 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080078 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050079 idata.write_flag = 0;
80 idata.opcode = MMC_SEND_EXT_CSD;
81 idata.arg = 0;
82 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080083 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050084 idata.blocks = 1;
85 mmc_ioc_cmd_set_data(idata, ext_csd);
86
87 ret = ioctl(fd, MMC_IOC_CMD, &idata);
88 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080089 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050090
91 return ret;
92}
93
94int write_extcsd_value(int fd, __u8 index, __u8 value)
95{
96 int ret = 0;
97 struct mmc_ioc_cmd idata;
98
99 memset(&idata, 0, sizeof(idata));
100 idata.write_flag = 1;
101 idata.opcode = MMC_SWITCH;
102 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
103 (index << 16) |
104 (value << 8) |
105 EXT_CSD_CMD_SET_NORMAL;
106 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
107
108 ret = ioctl(fd, MMC_IOC_CMD, &idata);
109 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -0800110 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500111
112 return ret;
113}
114
Ben Gardiner27c357d2013-05-30 17:12:47 -0400115int send_status(int fd, __u32 *response)
116{
117 int ret = 0;
118 struct mmc_ioc_cmd idata;
119
120 memset(&idata, 0, sizeof(idata));
121 idata.opcode = MMC_SEND_STATUS;
122 idata.arg = (1 << 16);
123 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
124
125 ret = ioctl(fd, MMC_IOC_CMD, &idata);
126 if (ret)
127 perror("ioctl");
128
129 *response = idata.response[0];
130
131 return ret;
132}
133
Al Cooper1b7f5d72016-06-07 16:35:46 -0400134static __u32 get_size_in_blks(int fd)
135{
136 int res;
137 int size;
138
139 res = ioctl(fd, BLKGETSIZE, &size);
140 if (res) {
141 fprintf(stderr, "Error getting device size, errno: %d\n",
142 errno);
143 perror("");
144 return -1;
145 }
146 return size;
147}
148
149static int set_write_protect(int fd, __u32 blk_addr, int on_off)
150{
151 int ret = 0;
152 struct mmc_ioc_cmd idata;
153
154 memset(&idata, 0, sizeof(idata));
155 idata.write_flag = 1;
156 if (on_off)
157 idata.opcode = MMC_SET_WRITE_PROT;
158 else
159 idata.opcode = MMC_CLEAR_WRITE_PROT;
160 idata.arg = blk_addr;
161 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
162
163 ret = ioctl(fd, MMC_IOC_CMD, &idata);
164 if (ret)
165 perror("ioctl");
166
167 return ret;
168}
169
170static int send_write_protect_type(int fd, __u32 blk_addr, __u64 *group_bits)
171{
172 int ret = 0;
173 struct mmc_ioc_cmd idata;
174 __u8 buf[8];
175 __u64 bits = 0;
176 int x;
177
178 memset(&idata, 0, sizeof(idata));
179 idata.write_flag = 0;
180 idata.opcode = MMC_SEND_WRITE_PROT_TYPE;
181 idata.blksz = 8,
182 idata.blocks = 1,
183 idata.arg = blk_addr;
184 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
185 mmc_ioc_cmd_set_data(idata, buf);
186
187 ret = ioctl(fd, MMC_IOC_CMD, &idata);
188 if (ret)
189 perror("ioctl");
190 for (x = 0; x < sizeof(buf); x++)
191 bits |= (__u64)(buf[7 - x]) << (x * 8);
192 *group_bits = bits;
193 return ret;
194}
195
196static void print_writeprotect_boot_status(__u8 *ext_csd)
Chris Ballb9c7a172012-02-20 12:34:25 -0500197{
198 __u8 reg;
Al Cooper786418c2015-04-29 18:12:35 -0400199 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500200
201 /* A43: reserved [174:0] */
202 if (ext_csd_rev >= 5) {
203 printf("Boot write protection status registers"
204 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
205
206 reg = ext_csd[EXT_CSD_BOOT_WP];
207 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
208 printf(" Power ro locking: ");
209 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
210 printf("not possible\n");
211 else
212 printf("possible\n");
213
214 printf(" Permanent ro locking: ");
215 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
216 printf("not possible\n");
217 else
218 printf("possible\n");
219
220 printf(" ro lock status: ");
221 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
222 printf("locked until next power on\n");
223 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
224 printf("locked permanently\n");
225 else
226 printf("not locked\n");
227 }
228}
229
Al Cooper1b7f5d72016-06-07 16:35:46 -0400230static int get_wp_group_size_in_blks(__u8 *ext_csd, __u32 *size)
231{
232 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
233
234 if ((ext_csd_rev < 5) || (ext_csd[EXT_CSD_ERASE_GROUP_DEF] == 0))
235 return 1;
236
237 *size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
238 ext_csd[EXT_CSD_HC_WP_GRP_SIZE] * 1024;
239 return 0;
240}
241
242
243int do_writeprotect_boot_get(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500244{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800245 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500246 int fd, ret;
247 char *device;
248
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100249 if (nargs != 2) {
250 fprintf(stderr, "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n");
251 exit(1);
252 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500253
254 device = argv[1];
255
256 fd = open(device, O_RDWR);
257 if (fd < 0) {
258 perror("open");
259 exit(1);
260 }
261
262 ret = read_extcsd(fd, ext_csd);
263 if (ret) {
264 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
265 exit(1);
266 }
267
Al Cooper1b7f5d72016-06-07 16:35:46 -0400268 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -0500269
270 return ret;
271}
272
Al Cooper1b7f5d72016-06-07 16:35:46 -0400273int do_writeprotect_boot_set(int nargs, char **argv)
Chris Ballb9c7a172012-02-20 12:34:25 -0500274{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800275 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500276 int fd, ret;
277 char *device;
278
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100279 if (nargs != 2) {
280 fprintf(stderr, "Usage: mmc writeprotect boot set </path/to/mmcblkX>\n");
281 exit(1);
282 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500283
284 device = argv[1];
285
286 fd = open(device, O_RDWR);
287 if (fd < 0) {
288 perror("open");
289 exit(1);
290 }
291
292 ret = read_extcsd(fd, ext_csd);
293 if (ret) {
294 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
295 exit(1);
296 }
297
298 value = ext_csd[EXT_CSD_BOOT_WP] |
299 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
300 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
301 if (ret) {
302 fprintf(stderr, "Could not write 0x%02x to "
303 "EXT_CSD[%d] in %s\n",
304 value, EXT_CSD_BOOT_WP, device);
305 exit(1);
306 }
307
308 return ret;
309}
310
Al Cooper1b7f5d72016-06-07 16:35:46 -0400311static char *prot_desc[] = {
312 "No",
313 "Temporary",
314 "Power-on",
315 "Permanent"
316};
317
318static void print_wp_status(__u32 wp_sizeblks, __u32 start_group,
319 __u32 end_group, int rptype)
320{
321 printf("Write Protect Groups %d-%d (Blocks %d-%d), ",
322 start_group, end_group,
323 start_group * wp_sizeblks, ((end_group + 1) * wp_sizeblks) - 1);
324 printf("%s Write Protection\n", prot_desc[rptype]);
325}
326
327
328int do_writeprotect_user_get(int nargs, char **argv)
329{
330 __u8 ext_csd[512];
331 int fd, ret;
332 char *device;
333 int x;
334 int y = 0;
335 __u32 wp_sizeblks;
336 __u32 dev_sizeblks;
337 __u32 cnt;
338 __u64 bits;
339 __u32 wpblk;
340 __u32 last_wpblk = 0;
341 __u32 prot;
342 __u32 last_prot = -1;
343 int remain;
344
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100345 if (nargs != 2) {
346 fprintf(stderr, "Usage: mmc writeprotect user get </path/to/mmcblkX>\n");
347 exit(1);
348 }
Al Cooper1b7f5d72016-06-07 16:35:46 -0400349
350 device = argv[1];
351
352 fd = open(device, O_RDWR);
353 if (fd < 0) {
354 perror("open");
355 exit(1);
356 }
357 ret = read_extcsd(fd, ext_csd);
358 if (ret) {
359 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
360 exit(1);
361 }
362
363 ret = get_wp_group_size_in_blks(ext_csd, &wp_sizeblks);
364 if (ret)
365 exit(1);
366 printf("Write Protect Group size in blocks/bytes: %d/%d\n",
367 wp_sizeblks, wp_sizeblks * 512);
368 dev_sizeblks = get_size_in_blks(fd);
369 cnt = dev_sizeblks / wp_sizeblks;
370 for (x = 0; x < cnt; x += WP_BLKS_PER_QUERY) {
371 ret = send_write_protect_type(fd, x * wp_sizeblks, &bits);
372 if (ret)
373 break;
374 remain = cnt - x;
375 if (remain > WP_BLKS_PER_QUERY)
376 remain = WP_BLKS_PER_QUERY;
377 for (y = 0; y < remain; y++) {
378 prot = (bits >> (y * 2)) & 0x3;
379 if (prot != last_prot) {
380 /* not first time */
381 if (last_prot != -1) {
382 wpblk = x + y;
383 print_wp_status(wp_sizeblks,
384 last_wpblk,
385 wpblk - 1,
386 last_prot);
387 last_wpblk = wpblk;
388 }
389 last_prot = prot;
390 }
391 }
392 }
393 if (last_wpblk != (x + y - 1))
394 print_wp_status(wp_sizeblks, last_wpblk, cnt - 1, last_prot);
395
396 return ret;
397}
398
399int do_writeprotect_user_set(int nargs, char **argv)
400{
401 __u8 ext_csd[512];
402 int fd, ret;
403 char *device;
404 int blk_start;
405 int blk_cnt;
406 __u32 wp_blks;
407 __u8 user_wp;
408 int x;
409 int wptype;
410
411 if (nargs != 5)
412 goto usage;
413 device = argv[4];
414 fd = open(device, O_RDWR);
415 if (fd < 0) {
416 perror("open");
417 exit(1);
418 }
419 if (!strcmp(argv[1], "none")) {
420 wptype = WPTYPE_NONE;
421 } else if (!strcmp(argv[1], "temp")) {
422 wptype = WPTYPE_TEMP;
423 } else if (!strcmp(argv[1], "pwron")) {
424 wptype = WPTYPE_PWRON;
425#ifdef DANGEROUS_COMMANDS_ENABLED
426 } else if (!strcmp(argv[1], "perm")) {
427 wptype = WPTYPE_PERM;
428#endif /* DANGEROUS_COMMANDS_ENABLED */
429 } else {
430 fprintf(stderr, "Error, invalid \"type\"\n");
431 goto usage;
432 }
433 ret = read_extcsd(fd, ext_csd);
434 if (ret) {
435 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
436 exit(1);
437 }
438 ret = get_wp_group_size_in_blks(ext_csd, &wp_blks);
439 if (ret) {
440 fprintf(stderr, "Operation not supported for this device\n");
441 exit(1);
442 }
443 blk_start = strtol(argv[2], NULL, 0);
444 blk_cnt = strtol(argv[3], NULL, 0);
445 if ((blk_start % wp_blks) || (blk_cnt % wp_blks)) {
446 fprintf(stderr, "<start block> and <blocks> must be a ");
447 fprintf(stderr, "multiple of the Write Protect Group (%d)\n",
448 wp_blks);
449 exit(1);
450 }
451 if (wptype != WPTYPE_NONE) {
452 user_wp = ext_csd[EXT_CSD_USER_WP];
453 user_wp &= ~USER_WP_CLEAR;
454 switch (wptype) {
455 case WPTYPE_TEMP:
456 break;
457 case WPTYPE_PWRON:
458 user_wp |= USER_WP_US_PWR_WP_EN;
459 break;
460 case WPTYPE_PERM:
461 user_wp |= USER_WP_US_PERM_WP_EN;
462 break;
463 }
464 if (user_wp != ext_csd[EXT_CSD_USER_WP]) {
465 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
466 if (ret) {
467 fprintf(stderr, "Error setting EXT_CSD\n");
468 exit(1);
469 }
470 }
471 }
472 for (x = 0; x < blk_cnt; x += wp_blks) {
473 ret = set_write_protect(fd, blk_start + x,
474 wptype != WPTYPE_NONE);
475 if (ret) {
476 fprintf(stderr,
477 "Could not set write protect for %s\n", device);
478 exit(1);
479 }
480 }
481 if (wptype != WPTYPE_NONE) {
482 ret = write_extcsd_value(fd, EXT_CSD_USER_WP,
483 ext_csd[EXT_CSD_USER_WP]);
484 if (ret) {
485 fprintf(stderr, "Error restoring EXT_CSD\n");
486 exit(1);
487 }
488 }
489 return ret;
490
491usage:
492 fprintf(stderr,
493 "Usage: mmc writeprotect user set <type><start block><blocks><device>\n");
494 exit(1);
495}
496
Saugata Dasb7e25992012-05-17 09:26:34 -0400497int do_disable_512B_emulation(int nargs, char **argv)
498{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800499 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400500 int fd, ret;
501 char *device;
502
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100503 if (nargs != 2) {
504 fprintf(stderr, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n");
505 exit(1);
506 }
507
Saugata Dasb7e25992012-05-17 09:26:34 -0400508 device = argv[1];
509
510 fd = open(device, O_RDWR);
511 if (fd < 0) {
512 perror("open");
513 exit(1);
514 }
515
516 ret = read_extcsd(fd, ext_csd);
517 if (ret) {
518 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
519 exit(1);
520 }
521
522 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
523 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
524 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
525
526 if (native_sector_size && !data_sector_size &&
527 (wr_rel_param & EN_REL_WR)) {
528 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
529
530 if (ret) {
531 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
Tomas Melin295dd7a2016-08-29 11:45:44 -0400532 1, EXT_CSD_NATIVE_SECTOR_SIZE, device);
Saugata Dasb7e25992012-05-17 09:26:34 -0400533 exit(1);
534 }
535 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
536 } else if (native_sector_size && data_sector_size) {
537 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
538 } else {
539 printf("MMC does not support disabling 512B emulation mode.\n");
540 }
541
542 return ret;
543}
544
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200545int do_write_boot_en(int nargs, char **argv)
546{
547 __u8 ext_csd[512];
548 __u8 value = 0;
549 int fd, ret;
550 char *device;
551 int boot_area, send_ack;
552
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100553 if (nargs != 4) {
554 fprintf(stderr, "Usage: mmc bootpart enable <partition_number> <send_ack> </path/to/mmcblkX>\n");
555 exit(1);
556 }
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200557
558 /*
559 * If <send_ack> is 1, the device will send acknowledgment
560 * pattern "010" to the host when boot operation begins.
561 * If <send_ack> is 0, it won't.
562 */
563 boot_area = strtol(argv[1], NULL, 10);
564 send_ack = strtol(argv[2], NULL, 10);
565 device = argv[3];
566
567 fd = open(device, O_RDWR);
568 if (fd < 0) {
569 perror("open");
570 exit(1);
571 }
572
573 ret = read_extcsd(fd, ext_csd);
574 if (ret) {
575 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
576 exit(1);
577 }
578
579 value = ext_csd[EXT_CSD_PART_CONFIG];
580
581 switch (boot_area) {
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100582 case EXT_CSD_PART_CONFIG_ACC_NONE:
583 value &= ~(7 << 3);
584 break;
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200585 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
586 value |= (1 << 3);
587 value &= ~(3 << 4);
588 break;
589 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
590 value |= (1 << 4);
591 value &= ~(1 << 3);
592 value &= ~(1 << 5);
593 break;
594 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
595 value |= (boot_area << 3);
596 break;
597 default:
598 fprintf(stderr, "Cannot enable the boot area\n");
599 exit(1);
600 }
601 if (send_ack)
602 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
603 else
604 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
605
606 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
607 if (ret) {
608 fprintf(stderr, "Could not write 0x%02x to "
609 "EXT_CSD[%d] in %s\n",
610 value, EXT_CSD_PART_CONFIG, device);
611 exit(1);
612 }
613 return ret;
614}
615
Al Cooper794314c2015-05-01 08:24:37 -0400616int do_boot_bus_conditions_set(int nargs, char **argv)
617{
618 __u8 ext_csd[512];
619 __u8 value = 0;
620 int fd, ret;
621 char *device;
622
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100623 if (nargs != 5) {
624 fprintf(stderr, "Usage: mmc: bootbus set <boot_mode> <reset_boot_bus_conditions> <boot_bus_width> <device>\n");
625 exit(1);
626 }
Al Cooper794314c2015-05-01 08:24:37 -0400627
628 if (strcmp(argv[1], "single_backward") == 0)
629 value |= 0;
630 else if (strcmp(argv[1], "single_hs") == 0)
631 value |= 0x8;
632 else if (strcmp(argv[1], "dual") == 0)
633 value |= 0x10;
634 else {
635 fprintf(stderr, "illegal <boot_mode> specified\n");
636 exit(1);
637 }
638
639 if (strcmp(argv[2], "x1") == 0)
640 value |= 0;
641 else if (strcmp(argv[2], "retain") == 0)
642 value |= 0x4;
643 else {
644 fprintf(stderr,
645 "illegal <reset_boot_bus_conditions> specified\n");
646 exit(1);
647 }
648
649 if (strcmp(argv[3], "x1") == 0)
650 value |= 0;
651 else if (strcmp(argv[3], "x4") == 0)
652 value |= 0x1;
653 else if (strcmp(argv[3], "x8") == 0)
654 value |= 0x2;
655 else {
656 fprintf(stderr, "illegal <boot_bus_width> specified\n");
657 exit(1);
658 }
659
660 device = argv[4];
661 fd = open(device, O_RDWR);
662 if (fd < 0) {
663 perror("open");
664 exit(1);
665 }
666
667 ret = read_extcsd(fd, ext_csd);
668 if (ret) {
669 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
670 exit(1);
671 }
672 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
673 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
674
675 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
676 if (ret) {
677 fprintf(stderr, "Could not write 0x%02x to "
678 "EXT_CSD[%d] in %s\n",
679 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
680 exit(1);
681 }
682 close(fd);
683 return ret;
684}
685
Chris Ballf74dfe22012-10-19 16:49:55 -0400686int do_hwreset(int value, int nargs, char **argv)
687{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800688 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400689 int fd, ret;
690 char *device;
691
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100692 if (nargs != 2) {
693 fprintf(stderr, "Usage: mmc hwreset enable </path/to/mmcblkX>\n");
694 exit(1);
695 }
Chris Ballf74dfe22012-10-19 16:49:55 -0400696
697 device = argv[1];
698
699 fd = open(device, O_RDWR);
700 if (fd < 0) {
701 perror("open");
702 exit(1);
703 }
704
705 ret = read_extcsd(fd, ext_csd);
706 if (ret) {
707 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
708 exit(1);
709 }
710
711 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
712 EXT_CSD_HW_RESET_EN) {
713 fprintf(stderr,
714 "H/W Reset is already permanently enabled on %s\n",
715 device);
716 exit(1);
717 }
718 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
719 EXT_CSD_HW_RESET_DIS) {
720 fprintf(stderr,
721 "H/W Reset is already permanently disabled on %s\n",
722 device);
723 exit(1);
724 }
725
726 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
727 if (ret) {
728 fprintf(stderr,
729 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
730 value, EXT_CSD_RST_N_FUNCTION, device);
731 exit(1);
732 }
733
734 return ret;
735}
736
737int do_hwreset_en(int nargs, char **argv)
738{
739 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
740}
741
742int do_hwreset_dis(int nargs, char **argv)
743{
744 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
745}
746
Jaehoon Chung86496512012-09-21 10:08:05 +0000747int do_write_bkops_en(int nargs, char **argv)
748{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800749 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000750 int fd, ret;
751 char *device;
752
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100753 if (nargs != 2) {
754 fprintf(stderr, "Usage: mmc bkops enable </path/to/mmcblkX>\n");
755 exit(1);
756 }
Jaehoon Chung86496512012-09-21 10:08:05 +0000757
758 device = argv[1];
759
760 fd = open(device, O_RDWR);
761 if (fd < 0) {
762 perror("open");
763 exit(1);
764 }
765
766 ret = read_extcsd(fd, ext_csd);
767 if (ret) {
768 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
769 exit(1);
770 }
771
772 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
773 fprintf(stderr, "%s doesn't support BKOPS\n", device);
774 exit(1);
775 }
776
777 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
778 if (ret) {
779 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
780 value, EXT_CSD_BKOPS_EN, device);
781 exit(1);
782 }
783
784 return ret;
785}
786
Ben Gardiner27c357d2013-05-30 17:12:47 -0400787int do_status_get(int nargs, char **argv)
788{
789 __u32 response;
790 int fd, ret;
791 char *device;
792
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +0100793 if (nargs != 2) {
794 fprintf(stderr, "Usage: mmc status get </path/to/mmcblkX>\n");
795 exit(1);
796 }
Ben Gardiner27c357d2013-05-30 17:12:47 -0400797
798 device = argv[1];
799
800 fd = open(device, O_RDWR);
801 if (fd < 0) {
802 perror("open");
803 exit(1);
804 }
805
806 ret = send_status(fd, &response);
807 if (ret) {
808 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
809 exit(1);
810 }
811
812 printf("SEND_STATUS response: 0x%08x\n", response);
813
814 return ret;
815}
816
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800817__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
818{
819 return (ext_csd_loc[3] << 24) |
820 (ext_csd_loc[2] << 16) |
821 (ext_csd_loc[1] << 8) |
822 ext_csd_loc[0];
823}
824
Ben Gardiner4e850232013-05-30 17:12:49 -0400825unsigned int get_sector_count(__u8 *ext_csd)
826{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800827 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400828}
829
830int is_blockaddresed(__u8 *ext_csd)
831{
832 unsigned int sectors = get_sector_count(ext_csd);
833
Tomas Melin2e610662016-08-29 11:41:10 -0400834 /* over 2GiB devices are block-addressed */
Ben Gardiner4e850232013-05-30 17:12:49 -0400835 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
836}
837
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400838unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
839{
840 return ext_csd[221];
841}
842
843unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
844{
845 return ext_csd[224];
846}
847
Ben Gardinere6e84e92013-09-19 11:14:27 -0400848int set_partitioning_setting_completed(int dry_run, const char * const device,
849 int fd)
850{
851 int ret;
852
Tomas Melin4dadd872016-08-29 11:55:08 -0400853 if (dry_run == 1) {
Ben Gardinere6e84e92013-09-19 11:14:27 -0400854 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
855 fprintf(stderr, "These changes will not take effect neither "
856 "now nor after a power cycle\n");
857 return 1;
Tomas Melin4dadd872016-08-29 11:55:08 -0400858 } else if (dry_run == 2) {
859 printf("-c given, expecting more partition settings before "
860 "writing PARTITION_SETTING_COMPLETED\n");
861 return 0;
Ben Gardinere6e84e92013-09-19 11:14:27 -0400862 }
863
864 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
865 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
866 if (ret) {
867 fprintf(stderr, "Could not write 0x1 to "
868 "EXT_CSD[%d] in %s\n",
869 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
870 return 1;
871 }
872
873 __u32 response;
874 ret = send_status(fd, &response);
875 if (ret) {
876 fprintf(stderr, "Could not get response to SEND_STATUS "
877 "from %s\n", device);
878 return 1;
879 }
880
881 if (response & R1_SWITCH_ERROR) {
882 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
883 "failed on %s\n", device);
884 return 1;
885 }
886
887 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
888 "%s SUCCESS\n", device);
889 fprintf(stderr, "Device power cycle needed for settings to "
890 "take effect.\n"
891 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
892 "using 'extcsd read' after power cycle\n");
893
894 return 0;
895}
896
Balaji T K1fdb7f92015-04-29 18:12:32 -0400897int check_enhanced_area_total_limit(const char * const device, int fd)
898{
899 __u8 ext_csd[512];
900 __u32 regl;
901 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
902 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400903 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400904 unsigned int wp_sz, erase_sz;
905 int ret;
906
907 ret = read_extcsd(fd, ext_csd);
908 if (ret) {
909 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
910 exit(1);
911 }
912 wp_sz = get_hc_wp_grp_size(ext_csd);
913 erase_sz = get_hc_erase_grp_size(ext_csd);
914
915 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
916 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
917 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
918 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
919 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
920 enh_area_sz += gp4_part_sz;
921 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
922 printf(" i.e. %lu KiB\n", gp4_part_sz);
923 }
924
925 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
926 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
927 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
928 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
929 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
930 enh_area_sz += gp3_part_sz;
931 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
932 printf(" i.e. %lu KiB\n", gp3_part_sz);
933 }
934
935 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
936 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
937 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
938 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
939 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
940 enh_area_sz += gp2_part_sz;
941 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
942 printf(" i.e. %lu KiB\n", gp2_part_sz);
943 }
944
945 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
946 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
947 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
948 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
949 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
950 enh_area_sz += gp1_part_sz;
951 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
952 printf(" i.e. %lu KiB\n", gp1_part_sz);
953 }
954
955 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
956 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
957 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
958 user_area_sz = 512l * regl * erase_sz * wp_sz;
959 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
960 enh_area_sz += user_area_sz;
961 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
962 printf(" i.e. %lu KiB\n", user_area_sz);
963 }
964
965 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
966 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
967 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
968 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
969 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
970 printf(" i.e. %lu KiB\n", max_enh_area_sz);
971 if (enh_area_sz > max_enh_area_sz) {
972 fprintf(stderr,
973 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
974 enh_area_sz, max_enh_area_sz, device);
975 return 1;
976 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400977 total_sz = get_sector_count(ext_csd) / 2;
978 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
979 gp1_part_sz + user_area_sz;
980 if (total_gp_user_sz > total_sz) {
981 fprintf(stderr,
982 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
983 total_gp_user_sz, total_sz, device);
984 return 1;
985 }
986
987 return 0;
988}
989
990int do_create_gp_partition(int nargs, char **argv)
991{
992 __u8 value;
993 __u8 ext_csd[512];
994 __u8 address;
995 int fd, ret;
996 char *device;
997 int dry_run = 1;
998 int partition, enh_attr, ext_attr;
999 unsigned int length_kib, gp_size_mult;
1000 unsigned long align;
1001
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001002 if (nargs != 7) {
1003 fprintf(stderr, "Usage: mmc gp create <-y|-n|-c> <length KiB> <partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n");
1004 exit(1);
1005 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001006
Tomas Melin4dadd872016-08-29 11:55:08 -04001007 if (!strcmp("-y", argv[1])) {
Balaji T Kd78ce082015-04-29 18:12:33 -04001008 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001009 } else if (!strcmp("-c", argv[1])) {
1010 dry_run = 2;
1011 }
Balaji T Kd78ce082015-04-29 18:12:33 -04001012
1013 length_kib = strtol(argv[2], NULL, 10);
1014 partition = strtol(argv[3], NULL, 10);
1015 enh_attr = strtol(argv[4], NULL, 10);
1016 ext_attr = strtol(argv[5], NULL, 10);
1017 device = argv[6];
1018
Marcus Folkessoncb04fde2015-11-18 15:06:16 -05001019 if (partition < 1 || partition > 4) {
1020 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -04001021 exit(1);
1022 }
1023
1024 if (enh_attr && ext_attr) {
1025 printf("Not allowed to set both enhanced attribute and extended attribute\n");
1026 exit(1);
1027 }
1028
1029 fd = open(device, O_RDWR);
1030 if (fd < 0) {
1031 perror("open");
1032 exit(1);
1033 }
1034
1035 ret = read_extcsd(fd, ext_csd);
1036 if (ret) {
1037 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1038 exit(1);
1039 }
1040
1041 /* assert not PARTITION_SETTING_COMPLETED */
1042 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
1043 printf(" Device is already partitioned\n");
1044 exit(1);
1045 }
1046
1047 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1048 gp_size_mult = (length_kib + align/2l) / align;
1049
1050 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1051 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1052 if (ret) {
1053 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
1054 EXT_CSD_ERASE_GROUP_DEF, device);
1055 exit(1);
1056 }
1057
1058 value = (gp_size_mult >> 16) & 0xff;
1059 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
1060 ret = write_extcsd_value(fd, address, value);
1061 if (ret) {
1062 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1063 value, address, device);
1064 exit(1);
1065 }
1066 value = (gp_size_mult >> 8) & 0xff;
1067 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
1068 ret = write_extcsd_value(fd, address, value);
1069 if (ret) {
1070 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1071 value, address, device);
1072 exit(1);
1073 }
1074 value = gp_size_mult & 0xff;
1075 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
1076 ret = write_extcsd_value(fd, address, value);
1077 if (ret) {
1078 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1079 value, address, device);
1080 exit(1);
1081 }
1082
1083 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1084 if (enh_attr)
1085 value |= (1 << partition);
1086 else
1087 value &= ~(1 << partition);
1088
1089 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
1090 if (ret) {
1091 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
1092 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1093 exit(1);
1094 }
1095
1096 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
1097 value = ext_csd[address];
1098 if (ext_attr)
1099 value |= (ext_attr << (4 * ((partition - 1) % 2)));
1100 else
1101 value &= (0xF << (4 * ((partition % 2))));
1102
1103 ret = write_extcsd_value(fd, address, value);
1104 if (ret) {
1105 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
1106 value, address, device);
1107 exit(1);
1108 }
1109
1110 ret = check_enhanced_area_total_limit(device, fd);
1111 if (ret)
1112 exit(1);
1113
Tomas Melin20379fa2016-08-29 12:02:28 -04001114 if (set_partitioning_setting_completed(dry_run, device, fd))
Balaji T Kd78ce082015-04-29 18:12:33 -04001115 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -04001116
1117 return 0;
1118}
1119
Ben Gardinerd91d3692013-05-30 17:12:51 -04001120int do_enh_area_set(int nargs, char **argv)
1121{
1122 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001123 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -04001124 int fd, ret;
1125 char *device;
1126 int dry_run = 1;
1127 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
1128 unsigned long align;
1129
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001130 if (nargs != 5) {
1131 fprintf(stderr, "Usage: mmc enh_area set <-y|-n|-c> <start KiB> <length KiB> </path/to/mmcblkX>\n");
1132 exit(1);
1133 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001134
Tomas Melin4dadd872016-08-29 11:55:08 -04001135 if (!strcmp("-y", argv[1])) {
Ben Gardinerd91d3692013-05-30 17:12:51 -04001136 dry_run = 0;
Tomas Melin4dadd872016-08-29 11:55:08 -04001137 } else if (!strcmp("-c", argv[1])) {
1138 dry_run = 2;
1139 }
Ben Gardinerd91d3692013-05-30 17:12:51 -04001140
1141 start_kib = strtol(argv[2], NULL, 10);
1142 length_kib = strtol(argv[3], NULL, 10);
1143 device = argv[4];
1144
1145 fd = open(device, O_RDWR);
1146 if (fd < 0) {
1147 perror("open");
1148 exit(1);
1149 }
1150
1151 ret = read_extcsd(fd, ext_csd);
1152 if (ret) {
1153 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1154 exit(1);
1155 }
1156
1157 /* assert ENH_ATTRIBUTE_EN */
1158 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
1159 {
1160 printf(" Device cannot have enhanced tech.\n");
1161 exit(1);
1162 }
1163
1164 /* assert not PARTITION_SETTING_COMPLETED */
1165 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1166 {
1167 printf(" Device is already partitioned\n");
1168 exit(1);
1169 }
1170
1171 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
1172
1173 enh_size_mult = (length_kib + align/2l) / align;
1174
1175 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
1176 enh_start_addr /= align;
1177 enh_start_addr *= align;
1178
1179 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
1180 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
1181 if (ret) {
1182 fprintf(stderr, "Could not write 0x1 to "
1183 "EXT_CSD[%d] in %s\n",
1184 EXT_CSD_ERASE_GROUP_DEF, device);
1185 exit(1);
1186 }
1187
1188 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
1189 value = (enh_start_addr >> 24) & 0xff;
1190 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
1191 if (ret) {
1192 fprintf(stderr, "Could not write 0x%02x to "
1193 "EXT_CSD[%d] in %s\n", value,
1194 EXT_CSD_ENH_START_ADDR_3, device);
1195 exit(1);
1196 }
1197 value = (enh_start_addr >> 16) & 0xff;
1198 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
1199 if (ret) {
1200 fprintf(stderr, "Could not write 0x%02x to "
1201 "EXT_CSD[%d] in %s\n", value,
1202 EXT_CSD_ENH_START_ADDR_2, device);
1203 exit(1);
1204 }
1205 value = (enh_start_addr >> 8) & 0xff;
1206 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
1207 if (ret) {
1208 fprintf(stderr, "Could not write 0x%02x to "
1209 "EXT_CSD[%d] in %s\n", value,
1210 EXT_CSD_ENH_START_ADDR_1, device);
1211 exit(1);
1212 }
1213 value = enh_start_addr & 0xff;
1214 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
1215 if (ret) {
1216 fprintf(stderr, "Could not write 0x%02x to "
1217 "EXT_CSD[%d] in %s\n", value,
1218 EXT_CSD_ENH_START_ADDR_0, device);
1219 exit(1);
1220 }
1221
1222 value = (enh_size_mult >> 16) & 0xff;
1223 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
1224 if (ret) {
1225 fprintf(stderr, "Could not write 0x%02x to "
1226 "EXT_CSD[%d] in %s\n", value,
1227 EXT_CSD_ENH_SIZE_MULT_2, device);
1228 exit(1);
1229 }
1230 value = (enh_size_mult >> 8) & 0xff;
1231 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
1232 if (ret) {
1233 fprintf(stderr, "Could not write 0x%02x to "
1234 "EXT_CSD[%d] in %s\n", value,
1235 EXT_CSD_ENH_SIZE_MULT_1, device);
1236 exit(1);
1237 }
1238 value = enh_size_mult & 0xff;
1239 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
1240 if (ret) {
1241 fprintf(stderr, "Could not write 0x%02x to "
1242 "EXT_CSD[%d] in %s\n", value,
1243 EXT_CSD_ENH_SIZE_MULT_0, device);
1244 exit(1);
1245 }
Balaji T K1fdb7f92015-04-29 18:12:32 -04001246 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
1247 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001248 if (ret) {
1249 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
1250 "EXT_CSD[%d] in %s\n",
1251 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
1252 exit(1);
1253 }
1254
Balaji T K1fdb7f92015-04-29 18:12:32 -04001255 ret = check_enhanced_area_total_limit(device, fd);
1256 if (ret)
1257 exit(1);
1258
Ben Gardinere6e84e92013-09-19 11:14:27 -04001259 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001260
Tomas Melin20379fa2016-08-29 12:02:28 -04001261 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -04001262 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -04001263
1264 return 0;
1265}
1266
Ben Gardiner196d0d22013-09-19 11:14:29 -04001267int do_write_reliability_set(int nargs, char **argv)
1268{
1269 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -08001270 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -04001271 int fd, ret;
1272
1273 int dry_run = 1;
1274 int partition;
1275 char *device;
1276
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001277 if (nargs != 4) {
1278 fprintf(stderr,"Usage: mmc write_reliability set <-y|-n|-c> <partition> </path/to/mmcblkX>\n");
1279 exit(1);
1280 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001281
Tomas Melin77c22a82016-09-01 11:49:01 -04001282 if (!strcmp("-y", argv[1])) {
Ben Gardiner196d0d22013-09-19 11:14:29 -04001283 dry_run = 0;
Tomas Melin77c22a82016-09-01 11:49:01 -04001284 } else if (!strcmp("-c", argv[1])) {
1285 dry_run = 2;
1286 }
Ben Gardiner196d0d22013-09-19 11:14:29 -04001287
1288 partition = strtol(argv[2], NULL, 10);
1289 device = argv[3];
1290
1291 fd = open(device, O_RDWR);
1292 if (fd < 0) {
1293 perror("open");
1294 exit(1);
1295 }
1296
1297 ret = read_extcsd(fd, ext_csd);
1298 if (ret) {
1299 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1300 exit(1);
1301 }
1302
1303 /* assert not PARTITION_SETTING_COMPLETED */
1304 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
1305 {
1306 printf(" Device is already partitioned\n");
1307 exit(1);
1308 }
1309
1310 /* assert HS_CTRL_REL */
1311 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
1312 printf("Cannot set write reliability parameters, WR_REL_SET is "
1313 "read-only\n");
1314 exit(1);
1315 }
1316
1317 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
1318 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
1319 if (ret) {
1320 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1321 value, EXT_CSD_WR_REL_SET, device);
1322 exit(1);
1323 }
1324
1325 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1326 value, device);
1327
Tomas Melin20379fa2016-08-29 12:02:28 -04001328 if (set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardiner196d0d22013-09-19 11:14:29 -04001329 exit(1);
1330
1331 return 0;
1332}
1333
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001334int do_read_extcsd(int nargs, char **argv)
1335{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001336 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001337 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001338 int fd, ret;
1339 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001340 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001341 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001342 "4.0", /* 0 */
1343 "4.1", /* 1 */
1344 "4.2", /* 2 */
1345 "4.3", /* 3 */
1346 "Obsolete", /* 4 */
1347 "4.41", /* 5 */
1348 "4.5", /* 6 */
1349 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001350 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001351 };
1352 int boot_access;
1353 const char* boot_access_str[] = {
1354 "No access to boot partition", /* 0 */
1355 "R/W Boot Partition 1", /* 1 */
1356 "R/W Boot Partition 2", /* 2 */
1357 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1358 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001359
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001360 if (nargs != 2) {
1361 fprintf(stderr, "Usage: mmc extcsd read </path/to/mmcblkX>\n");
1362 exit(1);
1363 }
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001364
1365 device = argv[1];
1366
1367 fd = open(device, O_RDWR);
1368 if (fd < 0) {
1369 perror("open");
1370 exit(1);
1371 }
1372
1373 ret = read_extcsd(fd, ext_csd);
1374 if (ret) {
1375 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1376 exit(1);
1377 }
1378
Al Cooper786418c2015-04-29 18:12:35 -04001379 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001380
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001381 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1382 (ext_csd_rev != 4))
1383 str = ver_str[ext_csd_rev];
1384 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001385 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001386
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001387 printf("=============================================\n");
1388 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1389 printf("=============================================\n\n");
1390
1391 if (ext_csd_rev < 3)
1392 goto out_free; /* No ext_csd */
1393
1394 /* Parse the Extended CSD registers.
1395 * Reserved bit should be read as "0" in case of spec older
1396 * than A441.
1397 */
1398 reg = ext_csd[EXT_CSD_S_CMD_SET];
1399 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1400 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001401 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001402
1403 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1404 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1405 if (reg & EXT_CSD_HPI_SUPP) {
1406 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001407 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001408 else
1409 printf("implementation based on CMD13\n");
1410 }
1411
1412 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1413 ext_csd[502]);
1414
1415 if (ext_csd_rev >= 6) {
1416 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1417 ext_csd[501]);
1418 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1419 ext_csd[500]);
1420 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1421 ext_csd[499]);
1422
1423 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1424 ext_csd[498]);
1425 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1426 ext_csd[497]);
1427 printf("Context Management Capabilities"
1428 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1429 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1430 ext_csd[495]);
1431 printf("Extended partition attribute support"
1432 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001433 }
1434 if (ext_csd_rev >= 7) {
1435 int j;
1436 int eol_info;
1437 char* eol_info_str[] = {
1438 "Not Defined", /* 0 */
1439 "Normal", /* 1 */
1440 "Warning", /* 2 */
1441 "Urgent", /* 3 */
1442 };
1443
1444 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1445 ext_csd[493]);
1446 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1447 ext_csd[492]);
1448 printf("Operation codes timeout"
1449 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1450 ext_csd[491]);
1451 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1452 get_word_from_ext_csd(&ext_csd[487]));
1453 printf("Number of FW sectors correctly programmed"
1454 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1455 get_word_from_ext_csd(&ext_csd[302]));
1456 printf("Vendor proprietary health report:\n");
1457 for (j = 301; j >= 270; j--)
1458 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1459 " 0x%02x\n", j, ext_csd[j]);
1460 for (j = 269; j >= 268; j--) {
1461 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001462 char est_type = 'B' + (j - 269);
1463 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001464 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001465 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001466 if (life_used >= 0x1 && life_used <= 0xa)
1467 printf(" i.e. %d%% - %d%% device life time"
1468 " used\n",
1469 (life_used - 1) * 10, life_used * 10);
1470 else if (life_used == 0xb)
1471 printf(" i.e. Exceeded its maximum estimated"
1472 " device life time\n");
1473 }
1474 eol_info = ext_csd[267];
1475 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1476 eol_info);
1477 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1478 printf(" i.e. %s\n", eol_info_str[eol_info]);
1479 else
1480 printf(" i.e. Reserved\n");
1481
1482 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1483 ext_csd[266]);
1484 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1485 ext_csd[265]);
1486 printf("Optimal trim unit size"
1487 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1488 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1489 ext_csd[263], ext_csd[262]);
1490 printf("Firmware version:\n");
1491 for (j = 261; j >= 254; j--)
1492 printf("[FIRMWARE_VERSION[%d]]:"
1493 " 0x%02x\n", j, ext_csd[j]);
1494
1495 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1496 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1497 }
1498 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001499 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1500 ext_csd[248]);
1501 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1502 ext_csd[247]);
1503 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001504 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001505 }
1506
1507 /* A441: Reserved [501:247]
1508 A43: reserved [246:229] */
1509 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001510 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001511 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001512
1513 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1514
1515 printf("1st Initialisation Time after programmed sector"
1516 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1517
1518 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001519 printf("Power class for 52MHz, DDR at 3.6V"
1520 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1521 printf("Power class for 52MHz, DDR at 1.95V"
1522 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1523
1524 /* A441: reserved [237-236] */
1525
1526 if (ext_csd_rev >= 6) {
1527 printf("Power class for 200MHz at 3.6V"
1528 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1529 printf("Power class for 200MHz, at 1.95V"
1530 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1531 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001532 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001533 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1534 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1535 /* A441: reserved [233] */
1536 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1537 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1538 ext_csd[231]);
1539 }
1540 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1541 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1542 ext_csd[230]);
1543 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1544 ext_csd[229]);
1545 }
1546 reg = ext_csd[EXT_CSD_BOOT_INFO];
1547 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1548 if (reg & EXT_CSD_BOOT_INFO_ALT)
1549 printf(" Device supports alternative boot method\n");
1550 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1551 printf(" Device supports dual data rate during boot\n");
1552 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1553 printf(" Device supports high speed timing during boot\n");
1554
1555 /* A441/A43: reserved [227] */
1556 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1557 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001558
1559 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001560 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001561 reg);
1562 printf(" i.e. %u KiB\n", 512 * reg);
1563
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001564 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1565 ext_csd[223]);
1566 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1567 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001568
1569 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001570 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001571 reg);
1572 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1573
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001574 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1575 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1576 /* A441/A43: reserved [218] */
1577 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1578 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001579
1580 unsigned int sectors = get_sector_count(ext_csd);
1581 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1582 if (is_blockaddresed(ext_csd))
1583 printf(" Device is block-addressed\n");
1584 else
1585 printf(" Device is NOT block-addressed\n");
1586
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001587 /* A441/A43: reserved [211] */
1588 printf("Minimum Write Performance for 8bit:\n");
1589 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1590 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1591 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1592 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1593 printf("Minimum Write Performance for 4bit:\n");
1594 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1595 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1596 /* A441/A43: reserved [204] */
1597 printf("Power classes registers:\n");
1598 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1599 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1600 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1601 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1602
1603 /* A43: reserved [199:198] */
1604 if (ext_csd_rev >= 5) {
1605 printf("Partition switching timing "
1606 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1607 printf("Out-of-interrupt busy timing"
1608 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1609 }
1610
1611 /* A441/A43: reserved [197] [195] [193] [190] [188]
1612 * [186] [184] [182] [180] [176] */
1613
1614 if (ext_csd_rev >= 6)
1615 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1616 ext_csd[197]);
1617
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001618 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001619 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1620 ext_csd[196], ext_csd[195]);
1621 reg = ext_csd[195];
1622 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1623 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001624 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001625 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1626 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001627 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1628 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1629 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1630 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1631 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1632 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001633
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001634 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001635 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001636 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1637 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1638 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1639 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1640 ext_csd[185]);
1641 /* bus_width: ext_csd[183] not readable */
1642 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1643 ext_csd[181]);
1644 reg = ext_csd[EXT_CSD_BOOT_CFG];
1645 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001646 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001647 case 0x0:
1648 printf(" Not boot enable\n");
1649 break;
1650 case 0x1:
1651 printf(" Boot Partition 1 enabled\n");
1652 break;
1653 case 0x2:
1654 printf(" Boot Partition 2 enabled\n");
1655 break;
1656 case 0x7:
1657 printf(" User Area Enabled for boot\n");
1658 break;
1659 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001660 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1661 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1662 printf(" %s\n", boot_access_str[boot_access]);
1663 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001664 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001665 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001666
1667 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1668 ext_csd[178]);
1669 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1670 ext_csd[177]);
1671 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001672 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001673
Al Cooper1b7f5d72016-06-07 16:35:46 -04001674 print_writeprotect_boot_status(ext_csd);
Chris Ballb9c7a172012-02-20 12:34:25 -05001675
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001676 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001677 /* A441]: reserved [172] */
1678 printf("User area write protection register"
1679 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1680 /* A441]: reserved [170] */
1681 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1682 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001683
1684 reg = ext_csd[EXT_CSD_WR_REL_SET];
1685 const char * const fast = "existing data is at risk if a power "
1686 "failure occurs during a write operation";
1687 const char * const reliable = "the device protects existing "
1688 "data if a power failure occurs during a write "
1689 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001690 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001691 " [WR_REL_SET]: 0x%02x\n", reg);
1692
1693 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1694 int i;
1695 for (i = 1; i <= 4; i++) {
1696 printf(" partition %d: %s\n", i,
1697 reg & (1<<i) ? reliable : fast);
1698 }
1699
1700 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001701 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001702 " [WR_REL_PARAM]: 0x%02x\n", reg);
1703 if (reg & 0x01)
1704 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1705 if (reg & 0x04)
1706 printf(" Device supports the enhanced def. of reliable "
1707 "write\n");
1708
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001709 /* sanitize_start ext_csd[165]]: not readable
1710 * bkops_start ext_csd[164]]: only writable */
1711 printf("Enable background operations handshake"
1712 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1713 printf("H/W reset function"
1714 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1715 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001716 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001717 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1718 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001719 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001720 printf(" Device support partitioning feature\n");
1721 else
1722 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001723 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001724 printf(" Device can have enhanced tech.\n");
1725 else
1726 printf(" Device cannot have enhanced tech.\n");
1727
Oliver Metz11f2cea2013-09-23 08:40:52 +02001728 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001729 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1730 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1731
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001732 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001733 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001734 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1735 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001736 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001737
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001738 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001739 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001740 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001741 printf("Partitioning Setting"
1742 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001743 reg);
1744 if (reg)
1745 printf(" Device partition setting complete\n");
1746 else
1747 printf(" Device partition setting NOT complete\n");
1748
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001749 printf("General Purpose Partition Size\n"
1750 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1751 (ext_csd[153] << 8) | ext_csd[152]);
1752 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1753 (ext_csd[150] << 8) | ext_csd[149]);
1754 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1755 (ext_csd[147] << 8) | ext_csd[146]);
1756 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1757 (ext_csd[144] << 8) | ext_csd[143]);
1758
Oliver Metz11f2cea2013-09-23 08:40:52 +02001759 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001760 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1761 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001762 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001763 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1764 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001765 get_hc_erase_grp_size(ext_csd) *
1766 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001767
Oliver Metz11f2cea2013-09-23 08:40:52 +02001768 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001769 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1770 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1771 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001772 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001773 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001774 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Tomas Melin2e610662016-08-29 11:41:10 -04001775 512l : 1l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001776
1777 /* A441]: reserved [135] */
1778 printf("Bad Block Management mode"
1779 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1780 /* A441: reserved [133:0] */
1781 }
1782 /* B45 */
1783 if (ext_csd_rev >= 6) {
1784 int j;
1785 /* tcase_support ext_csd[132] not readable */
1786 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1787 ext_csd[131]);
1788 printf("Program CID/CSD in DDR mode support"
1789 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1790 ext_csd[130]);
1791
1792 for (j = 127; j >= 64; j--)
1793 printf("Vendor Specific Fields"
1794 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1795 j, ext_csd[j]);
1796
Gwendal Grignoue966e672014-07-07 14:03:13 -07001797 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001798 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001799 reg);
1800 if (reg == 0x00)
1801 printf(" i.e. 512 B\n");
1802 else if (reg == 0x01)
1803 printf(" i.e. 4 KiB\n");
1804 else
1805 printf(" i.e. Reserved\n");
1806
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001807 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1808 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001809 reg = ext_csd[61];
1810 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1811 if (reg == 0x00)
1812 printf(" i.e. 512 B\n");
1813 else if (reg == 0x01)
1814 printf(" i.e. 4 KiB\n");
1815 else
1816 printf(" i.e. Reserved\n");
1817
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001818 printf("1st initialization after disabling sector"
1819 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1820 ext_csd[60]);
1821 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1822 ext_csd[59]);
1823 printf("Number of addressed group to be Released"
1824 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1825 printf("Exception events control"
1826 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1827 (ext_csd[57] << 8) | ext_csd[56]);
1828 printf("Exception events status"
1829 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1830 (ext_csd[55] << 8) | ext_csd[54]);
1831 printf("Extended Partitions Attribute"
1832 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1833 (ext_csd[53] << 8) | ext_csd[52]);
1834
1835 for (j = 51; j >= 37; j--)
1836 printf("Context configuration"
1837 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1838
1839 printf("Packed command status"
1840 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1841 printf("Packed command failure index"
1842 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1843 printf("Power Off Notification"
1844 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001845 printf("Control to turn the Cache ON/OFF"
1846 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001847 /* flush_cache ext_csd[32] not readable */
1848 /*Reserved [31:0] */
1849 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001850 if (ext_csd_rev >= 7) {
1851 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1852 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1853 ext_csd[29]);
1854
1855 reg = ext_csd[26];
1856 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1857 switch (reg) {
1858 case 0x00:
1859 printf(" Success\n");
1860 break;
1861 case 0x10:
1862 printf(" General error\n");
1863 break;
1864 case 0x11:
1865 printf(" Firmware install error\n");
1866 break;
1867 case 0x12:
1868 printf(" Error in downloading firmware\n");
1869 break;
1870 default:
1871 printf(" Reserved\n");
1872 }
1873 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1874 " %d sector size\n",
1875 get_word_from_ext_csd(&ext_csd[22]));
1876 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1877 " %d sector size\n",
1878 get_word_from_ext_csd(&ext_csd[18]));
1879 printf("Product state awareness enablement"
1880 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1881 ext_csd[17]);
1882 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1883 ext_csd[16]);
1884 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001885
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05001886 if (ext_csd_rev >= 7) {
1887 printf("eMMC Firmware Version: %s\n",
1888 (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]);
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001889 printf("eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x%02x\n",
1890 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]);
Boris Schmidtf91b88e2017-03-14 14:03:22 +01001891 printf("eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x%02x\n",
1892 ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]);
Alexander Steincce3d882017-03-20 14:43:00 +01001893 printf("eMMC Pre EOL information [EXT_CSD_PRE_EOL_INFO]: 0x%02x\n",
1894 ext_csd[EXT_CSD_PRE_EOL_INFO]);
1895 }
1896
Adrian Hunterbb1600b2016-06-10 11:28:59 +03001897 if (ext_csd_rev >= 8) {
1898 printf("Command Queue Support [CMDQ_SUPPORT]: 0x%02x\n",
1899 ext_csd[EXT_CSD_CMDQ_SUPPORT]);
1900 printf("Command Queue Depth [CMDQ_DEPTH]: %u\n",
1901 (ext_csd[EXT_CSD_CMDQ_DEPTH] & 0x1f) + 1);
1902 printf("Command Enabled [CMDQ_MODE_EN]: 0x%02x\n",
1903 ext_csd[EXT_CSD_CMDQ_MODE_EN]);
1904 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001905out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001906 return ret;
1907}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001908
Nick Sanders9d57aa72014-03-05 21:38:54 -08001909int do_dump_extcsd(int nargs, char **argv)
1910{
1911 __u8 ext_csd[EXT_CSD_SIZE];
1912 int fd, ret;
1913 char *device;
1914 int i, j;
1915
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001916 if (nargs != 2) {
1917 fprintf(stderr, "Usage: mmc writeprotect boot get </path/to/mmcblkX>\n");
1918 exit(1);
1919 }
Nick Sanders9d57aa72014-03-05 21:38:54 -08001920 device = argv[1];
1921
1922 fd = open(device, O_RDWR);
1923 if (fd < 0) {
1924 perror("Failed to open mmc device");
1925 exit(1);
1926 }
1927
1928 ret = read_extcsd(fd, ext_csd);
1929 if (ret) {
1930 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1931 exit(1);
1932 }
1933
1934 /* Dump all bytes so that any undecoded or proprietary registers */
1935 /* can be acessed. */
1936 printf("EXT_CSD binary dump:\n");
1937 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1938 printf(" %3d: %3x: ", i, i);
1939 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1940 printf(" %02x", ext_csd[i+j]);
1941 }
1942 printf("\n");
1943 }
1944
1945 return ret;
1946}
1947
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001948int do_sanitize(int nargs, char **argv)
1949{
1950 int fd, ret;
1951 char *device;
1952
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01001953 if (nargs != 2) {
1954 fprintf(stderr, "Usage: mmc sanitize </path/to/mmcblkX>\n");
1955 exit(1);
1956 }
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001957
1958 device = argv[1];
1959
1960 fd = open(device, O_RDWR);
1961 if (fd < 0) {
1962 perror("open");
1963 exit(1);
1964 }
1965
1966 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1967 if (ret) {
1968 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1969 1, EXT_CSD_SANITIZE_START, device);
1970 exit(1);
1971 }
1972
1973 return ret;
1974
1975}
1976
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07001977enum blockprotect_mode {
1978 BLOCKPROTECT_TEMPORARY = 0,
1979 BLOCKPROTECT_POWERON,
1980 BLOCKPROTECT_PERMANENT,
1981};
1982
1983int write_blockprotect(int fd, __u32 sector, int enable)
1984{
1985 struct mmc_ioc_cmd cmd;
1986 int ret;
1987
1988 memset(&cmd, 0, sizeof(cmd));
1989 cmd.write_flag = 1;
1990 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
1991 cmd.arg = sector;
1992 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
1993
1994 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
1995 if (ret)
1996 perror("SET/CLR_WRITE_PROT command");
1997 return ret;
1998}
1999
2000int do_blockprotect_enable(int nargs, char **argv)
2001{
2002 __u8 ext_csd[EXT_CSD_SIZE];
2003 __u8 user_wp;
2004 __u32 sector;
2005 char *end;
2006 int ret, fd;
2007 int arg_index = 0;
2008 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
2009
2010 if (nargs > 0 && !strcmp(argv[1], "-r")) {
2011 arg_index++;
2012 mode = BLOCKPROTECT_POWERON;
2013 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
2014 arg_index++;
2015 mode = BLOCKPROTECT_PERMANENT;
2016 }
2017
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002018 if (nargs != 3 + arg_index) {
2019 fprintf(stderr, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n");
2020 exit(1);
2021 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002022 sector = strtoul(argv[2 + arg_index], &end, 0);
2023 if (*end != '\0') {
2024 fprintf(stderr, "Not a block number: %s\n",
2025 argv[2 + arg_index]);
2026 exit(1);
2027 }
2028
2029 fd = open(argv[1 + arg_index], O_RDWR);
2030 if (fd < 0) {
2031 perror("open");
2032 exit(1);
2033 }
2034
2035 if (read_extcsd(fd, ext_csd))
2036 exit(1);
2037
2038 user_wp = ext_csd[EXT_CSD_USER_WP];
2039 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2040 if (mode == BLOCKPROTECT_POWERON)
2041 user_wp |= EXT_CSD_US_PWR_WP_EN;
2042 else if (mode == BLOCKPROTECT_PERMANENT)
2043 user_wp |= EXT_CSD_US_PERM_WP_EN;
2044
2045 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
2046 if (ret) {
2047 perror("update EXT_CSD[USER_WP]");
2048 exit(1);
2049 }
2050
2051 usleep(INTER_COMMAND_GAP_US);
2052
2053 ret = write_blockprotect(fd, sector, 1);
2054
2055 usleep(INTER_COMMAND_GAP_US);
2056
2057 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
2058 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
2059 perror("reset EXT_CSD[USER_WP]");
2060 if (!ret)
2061 ret = -1;
2062 }
2063
2064 return ret;
2065}
2066
2067int do_blockprotect_disable(int nargs, char **argv)
2068{
2069 __u32 sector;
2070 char *end;
2071 int fd;
2072
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002073 if (nargs != 3) {
2074 fprintf(stderr, "Usage: mmc blockprotect disable <device> <write protect block>\n");
2075 exit(1);
2076 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002077 sector = strtoul(argv[2], &end, 0);
2078 if (*end != '\0') {
2079 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2080 exit(1);
2081 }
2082
2083
2084 fd = open(argv[1], O_RDWR);
2085 if (fd < 0) {
2086 perror("open");
2087 exit(1);
2088 }
2089
2090 return write_blockprotect(fd, sector, 0);
2091}
2092
2093int do_blockprotect_read(int nargs, char **argv)
2094{
2095 __u8 wp_bits[8];
2096 __u32 sector;
2097 char *end;
2098 int fd;
2099 struct mmc_ioc_cmd cmd;
2100
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002101 if (nargs != 3) {
2102 fprintf(stderr, "Usage: mmc blockprotect read <device> <write protect block>\n");
2103 exit(1);
2104 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002105 fd = open(argv[1], O_RDWR);
2106 if (fd < 0) {
2107 perror("open");
2108 exit(1);
2109 }
2110
2111 sector = strtoul(argv[2], &end, 0);
2112 if (*end != '\0') {
2113 fprintf(stderr, "Not a block number: %s\n", argv[2]);
2114 exit(1);
2115 }
2116
2117 memset(&cmd, 0, sizeof(cmd));
2118 cmd.write_flag = 0;
2119 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
2120 cmd.arg = sector;
2121 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2122 cmd.blksz = sizeof(wp_bits);
2123 cmd.blocks = 1;
2124 mmc_ioc_cmd_set_data(cmd, wp_bits);
2125
2126 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
2127 perror("SEND_WRITE_PROT_TYPE command");
2128 exit(1);
2129 }
2130
2131 printf("Sector %u write protection: ", sector);
2132 switch (wp_bits[7] & 3) {
2133 case 0:
2134 printf("NONE\n");
2135 break;
2136 case 1:
2137 printf("TEMPORARY\n");
2138 break;
2139 case 2:
2140 printf("POWER-ON\n");
2141 break;
2142 case 3:
2143 printf("PERMANENT\n");
2144 break;
2145 }
2146
2147 return 0;
2148}
2149
2150int do_blockprotect_info(int nargs, char **argv)
2151{
2152 __u8 ext_csd[EXT_CSD_SIZE];
2153 __u8 user_wp;
2154 int fd, wp_sz, erase_sz;
2155
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002156 if (nargs != 2) {
2157 fprintf(stderr, "Usage: mmc blockprotect info <device>\n");
2158 exit(1);
2159 }
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07002160 fd = open(argv[1], O_RDWR);
2161 if (fd < 0) {
2162 perror("open");
2163 exit(1);
2164 }
2165
2166 if (read_extcsd(fd, ext_csd))
2167 exit(1);
2168
2169 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
2170 fprintf(stderr, "Block protection commands not supported: "
2171 "CLASS_6_CTRL set.\n");
2172 exit(1);
2173 }
2174
2175 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
2176 fprintf(stderr, "Block protection commands not supported: "
2177 "high-capacity sizes not enabled.\n");
2178 exit(1);
2179 }
2180
2181 wp_sz = get_hc_wp_grp_size(ext_csd);
2182 erase_sz = get_hc_erase_grp_size(ext_csd);
2183
2184 if (erase_sz == 0 || wp_sz == 0) {
2185 fprintf(stderr, "Block protection commands not supported: "
2186 "no high-capacity size for erase or WP blocks.\n");
2187 exit(1);
2188 }
2189
2190 printf("Write protect block size in sectors: %d\n",
2191 erase_sz * wp_sz * 1024);
2192
2193 user_wp = ext_csd[EXT_CSD_USER_WP];
2194 printf("Permanent write protection: %s\n",
2195 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
2196 printf("Power-on write protection: %s\n",
2197 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
2198
2199 return 0;
2200}
2201
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002202static const char* const mmc_ffu_hack_names[] = {
2203 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
2204};
2205
Gwendal Grignou771984c2014-07-01 12:46:18 -07002206int do_emmc50_ffu (int nargs, char **argv)
2207{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002208 int fd, ret, i, argc=1, ffu_hack=0;
2209 char *device, *type, *path;
2210 __u64 value;
2211 union {
2212 __u8 data[FFU_DATA_SIZE];
2213 struct mmc_ffu_args ffu_args;
2214 } ffu_data;
2215 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002216 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07002217
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002218 while (!strcmp("-k", argv[argc])) {
2219 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
2220 if (ret < 1) {
2221 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
2222 exit(1);
2223 }
2224 for (i = 0; i < MMC_HACK_LEN; i++) {
2225 if (!strcmp(type, mmc_ffu_hack_names[i])) {
2226 ffu_args->hack[ffu_hack].type = i;
2227 if (ret == 2) {
2228 ffu_args->hack[ffu_hack].value = value;
2229 }
2230 ffu_hack++;
2231 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
2232 sizeof(struct mmc_ffu_args) >
2233 FFU_DATA_SIZE) {
2234 fprintf(stderr, "Too many %d hacks",
2235 ffu_hack);
2236 exit(1);
2237 }
2238 break;
2239 }
2240 }
2241 if (i == MMC_HACK_LEN) {
2242 fprintf(stderr, "Hack type %s not found\n", type);
2243 fprintf(stderr, "Supported types are: ");
2244 for (i = 0; i < MMC_HACK_LEN; i++)
2245 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
2246 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07002247
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002248 exit(1);
2249 }
2250 free(type);
2251 argc++;
2252 }
2253 ffu_args->hack_nb = ffu_hack;
2254
2255 path = argv[argc++];
2256 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07002257 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
2258 exit(1);
2259 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002260 strcpy(ffu_args->name, path);
2261 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07002262 fd = open(device, O_RDWR);
2263 if (fd < 0) {
2264 perror("open");
2265 exit(1);
2266 }
2267
Gwendal Grignou0f757342014-10-16 16:52:46 -07002268 /* prepare and send ioctl */
2269 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
2270 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002271 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07002272 mmc_ioc_cmd.blocks = 1;
2273 mmc_ioc_cmd.arg = 0;
2274 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2275 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08002276 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07002277 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07002278 if (ret) {
2279 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
2280 exit(1);
2281 }
2282
2283 close(fd);
2284 return 0;
2285}
2286
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002287#define DO_IO(func, fd, buf, nbyte) \
2288 ({ \
2289 ssize_t ret = 0, r; \
2290 do { \
2291 r = func(fd, buf + ret, nbyte - ret); \
2292 if (r < 0 && errno != EINTR) { \
2293 ret = -1; \
2294 break; \
2295 } \
2296 else if (r > 0) \
2297 ret += r; \
2298 } while (r != 0 && (size_t)ret != nbyte); \
2299 \
2300 ret; \
2301 })
2302
2303enum rpmb_op_type {
2304 MMC_RPMB_WRITE_KEY = 0x01,
2305 MMC_RPMB_READ_CNT = 0x02,
2306 MMC_RPMB_WRITE = 0x03,
2307 MMC_RPMB_READ = 0x04,
2308
2309 /* For internal usage only, do not use it directly */
2310 MMC_RPMB_READ_RESP = 0x05
2311};
2312
2313struct rpmb_frame {
2314 u_int8_t stuff[196];
2315 u_int8_t key_mac[32];
2316 u_int8_t data[256];
2317 u_int8_t nonce[16];
2318 u_int32_t write_counter;
2319 u_int16_t addr;
2320 u_int16_t block_count;
2321 u_int16_t result;
2322 u_int16_t req_resp;
2323};
2324
2325/* Performs RPMB operation.
2326 *
2327 * @fd: RPMB device on which we should perform ioctl command
2328 * @frame_in: input RPMB frame, should be properly inited
2329 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
2330 * result and req_resp for output frame.
2331 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
2332 * in the other cases -EINVAL will be returned.
2333 */
2334static int do_rpmb_op(int fd,
2335 const struct rpmb_frame *frame_in,
2336 struct rpmb_frame *frame_out,
2337 unsigned int out_cnt)
2338{
2339 int err;
2340 u_int16_t rpmb_type;
2341
2342 struct mmc_ioc_cmd ioc = {
2343 .arg = 0x0,
2344 .blksz = 512,
2345 .blocks = 1,
2346 .write_flag = 1,
2347 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
2348 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
2349 .data_ptr = (uintptr_t)frame_in
2350 };
2351
2352 if (!frame_in || !frame_out || !out_cnt)
2353 return -EINVAL;
2354
2355 rpmb_type = be16toh(frame_in->req_resp);
2356
2357 switch(rpmb_type) {
2358 case MMC_RPMB_WRITE:
2359 case MMC_RPMB_WRITE_KEY:
2360 if (out_cnt != 1) {
2361 err = -EINVAL;
2362 goto out;
2363 }
2364
2365 /* Write request */
2366 ioc.write_flag |= (1<<31);
2367 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2368 if (err < 0) {
2369 err = -errno;
2370 goto out;
2371 }
2372
2373 /* Result request */
2374 memset(frame_out, 0, sizeof(*frame_out));
2375 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2376 ioc.write_flag = 1;
2377 ioc.data_ptr = (uintptr_t)frame_out;
2378 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2379 if (err < 0) {
2380 err = -errno;
2381 goto out;
2382 }
2383
2384 /* Get response */
2385 ioc.write_flag = 0;
2386 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2387 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2388 if (err < 0) {
2389 err = -errno;
2390 goto out;
2391 }
2392
2393 break;
2394 case MMC_RPMB_READ_CNT:
2395 if (out_cnt != 1) {
2396 err = -EINVAL;
2397 goto out;
2398 }
2399 /* fall through */
2400
2401 case MMC_RPMB_READ:
2402 /* Request */
2403 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2404 if (err < 0) {
2405 err = -errno;
2406 goto out;
2407 }
2408
2409 /* Get response */
2410 ioc.write_flag = 0;
2411 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2412 ioc.blocks = out_cnt;
2413 ioc.data_ptr = (uintptr_t)frame_out;
2414 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2415 if (err < 0) {
2416 err = -errno;
2417 goto out;
2418 }
2419
2420 break;
2421 default:
2422 err = -EINVAL;
2423 goto out;
2424 }
2425
2426out:
2427 return err;
2428}
2429
2430int do_rpmb_write_key(int nargs, char **argv)
2431{
2432 int ret, dev_fd, key_fd;
2433 struct rpmb_frame frame_in = {
2434 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2435 }, frame_out;
2436
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002437 if (nargs != 3) {
2438 fprintf(stderr, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n");
2439 exit(1);
2440 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002441
2442 dev_fd = open(argv[1], O_RDWR);
2443 if (dev_fd < 0) {
2444 perror("device open");
2445 exit(1);
2446 }
2447
2448 if (0 == strcmp(argv[2], "-"))
2449 key_fd = STDIN_FILENO;
2450 else {
2451 key_fd = open(argv[2], O_RDONLY);
2452 if (key_fd < 0) {
2453 perror("can't open key file");
2454 exit(1);
2455 }
2456 }
2457
2458 /* Read the auth key */
2459 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2460 if (ret < 0) {
2461 perror("read the key");
2462 exit(1);
2463 } else if (ret != sizeof(frame_in.key_mac)) {
2464 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2465 (unsigned long)sizeof(frame_in.key_mac),
2466 ret);
2467 exit(1);
2468 }
2469
2470 /* Execute RPMB op */
2471 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2472 if (ret != 0) {
2473 perror("RPMB ioctl failed");
2474 exit(1);
2475 }
2476
2477 /* Check RPMB response */
2478 if (frame_out.result != 0) {
2479 printf("RPMB operation failed, retcode 0x%04x\n",
2480 be16toh(frame_out.result));
2481 exit(1);
2482 }
2483
2484 close(dev_fd);
2485 if (key_fd != STDIN_FILENO)
2486 close(key_fd);
2487
2488 return ret;
2489}
2490
2491int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2492{
2493 int ret;
2494 struct rpmb_frame frame_in = {
2495 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2496 }, frame_out;
2497
2498 /* Execute RPMB op */
2499 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2500 if (ret != 0) {
2501 perror("RPMB ioctl failed");
2502 exit(1);
2503 }
2504
2505 /* Check RPMB response */
2506 if (frame_out.result != 0)
2507 return be16toh(frame_out.result);
2508
2509 *cnt = be32toh(frame_out.write_counter);
2510
2511 return 0;
2512}
2513
2514int do_rpmb_read_counter(int nargs, char **argv)
2515{
2516 int ret, dev_fd;
2517 unsigned int cnt;
2518
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002519 if (nargs != 2) {
2520 fprintf(stderr, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n");
2521 exit(1);
2522 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002523
2524 dev_fd = open(argv[1], O_RDWR);
2525 if (dev_fd < 0) {
2526 perror("device open");
2527 exit(1);
2528 }
2529
2530 ret = rpmb_read_counter(dev_fd, &cnt);
2531
2532 /* Check RPMB response */
2533 if (ret != 0) {
2534 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2535 exit(1);
2536 }
2537
2538 close(dev_fd);
2539
2540 printf("Counter value: 0x%08x\n", cnt);
2541
2542 return ret;
2543}
2544
2545int do_rpmb_read_block(int nargs, char **argv)
2546{
2547 int i, ret, dev_fd, data_fd, key_fd = -1;
2548 uint16_t addr, blocks_cnt;
2549 unsigned char key[32];
2550 struct rpmb_frame frame_in = {
2551 .req_resp = htobe16(MMC_RPMB_READ),
2552 }, *frame_out_p;
2553
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002554 if (nargs != 5 && nargs != 6) {
2555 fprintf(stderr, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n");
2556 exit(1);
2557 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002558
2559 dev_fd = open(argv[1], O_RDWR);
2560 if (dev_fd < 0) {
2561 perror("device open");
2562 exit(1);
2563 }
2564
2565 /* Get block address */
2566 errno = 0;
2567 addr = strtol(argv[2], NULL, 0);
2568 if (errno) {
2569 perror("incorrect address");
2570 exit(1);
2571 }
2572 frame_in.addr = htobe16(addr);
2573
2574 /* Get blocks count */
2575 errno = 0;
2576 blocks_cnt = strtol(argv[3], NULL, 0);
2577 if (errno) {
2578 perror("incorrect blocks count");
2579 exit(1);
2580 }
2581
2582 if (!blocks_cnt) {
2583 printf("please, specify valid blocks count number\n");
2584 exit(1);
2585 }
2586
2587 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2588 if (!frame_out_p) {
2589 printf("can't allocate memory for RPMB outer frames\n");
2590 exit(1);
2591 }
2592
2593 /* Write 256b data */
2594 if (0 == strcmp(argv[4], "-"))
2595 data_fd = STDOUT_FILENO;
2596 else {
2597 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2598 S_IRUSR | S_IWUSR);
2599 if (data_fd < 0) {
2600 perror("can't open output file");
2601 exit(1);
2602 }
2603 }
2604
2605 /* Key is specified */
2606 if (nargs == 6) {
2607 if (0 == strcmp(argv[5], "-"))
2608 key_fd = STDIN_FILENO;
2609 else {
2610 key_fd = open(argv[5], O_RDONLY);
2611 if (key_fd < 0) {
2612 perror("can't open input key file");
2613 exit(1);
2614 }
2615 }
2616
2617 ret = DO_IO(read, key_fd, key, sizeof(key));
2618 if (ret < 0) {
2619 perror("read the key data");
2620 exit(1);
2621 } else if (ret != sizeof(key)) {
2622 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2623 (unsigned long)sizeof(key),
2624 ret);
2625 exit(1);
2626 }
2627 }
2628
2629 /* Execute RPMB op */
2630 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2631 if (ret != 0) {
2632 perror("RPMB ioctl failed");
2633 exit(1);
2634 }
2635
2636 /* Check RPMB response */
2637 if (frame_out_p[blocks_cnt - 1].result != 0) {
2638 printf("RPMB operation failed, retcode 0x%04x\n",
2639 be16toh(frame_out_p[blocks_cnt - 1].result));
2640 exit(1);
2641 }
2642
2643 /* Do we have to verify data against key? */
2644 if (nargs == 6) {
2645 unsigned char mac[32];
2646 hmac_sha256_ctx ctx;
2647 struct rpmb_frame *frame_out = NULL;
2648
2649 hmac_sha256_init(&ctx, key, sizeof(key));
2650 for (i = 0; i < blocks_cnt; i++) {
2651 frame_out = &frame_out_p[i];
2652 hmac_sha256_update(&ctx, frame_out->data,
2653 sizeof(*frame_out) -
2654 offsetof(struct rpmb_frame, data));
2655 }
2656
2657 hmac_sha256_final(&ctx, mac, sizeof(mac));
2658
2659 /* Impossible */
2660 assert(frame_out);
2661
2662 /* Compare calculated MAC and MAC from last frame */
2663 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2664 printf("RPMB MAC missmatch\n");
2665 exit(1);
2666 }
2667 }
2668
2669 /* Write data */
2670 for (i = 0; i < blocks_cnt; i++) {
2671 struct rpmb_frame *frame_out = &frame_out_p[i];
2672 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2673 if (ret < 0) {
2674 perror("write the data");
2675 exit(1);
2676 } else if (ret != sizeof(frame_out->data)) {
2677 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2678 (unsigned long)sizeof(frame_out->data),
2679 ret);
2680 exit(1);
2681 }
2682 }
2683
2684 free(frame_out_p);
2685 close(dev_fd);
2686 if (data_fd != STDOUT_FILENO)
2687 close(data_fd);
2688 if (key_fd != -1 && key_fd != STDIN_FILENO)
2689 close(key_fd);
2690
2691 return ret;
2692}
2693
2694int do_rpmb_write_block(int nargs, char **argv)
2695{
2696 int ret, dev_fd, key_fd, data_fd;
2697 unsigned char key[32];
2698 uint16_t addr;
2699 unsigned int cnt;
2700 struct rpmb_frame frame_in = {
2701 .req_resp = htobe16(MMC_RPMB_WRITE),
2702 .block_count = htobe16(1)
2703 }, frame_out;
2704
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002705 if (nargs != 5) {
2706 fprintf(stderr, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n");
2707 exit(1);
2708 }
Roman Peniaev023cc7c2014-08-12 23:25:45 +09002709
2710 dev_fd = open(argv[1], O_RDWR);
2711 if (dev_fd < 0) {
2712 perror("device open");
2713 exit(1);
2714 }
2715
2716 ret = rpmb_read_counter(dev_fd, &cnt);
2717 /* Check RPMB response */
2718 if (ret != 0) {
2719 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2720 exit(1);
2721 }
2722 frame_in.write_counter = htobe32(cnt);
2723
2724 /* Get block address */
2725 errno = 0;
2726 addr = strtol(argv[2], NULL, 0);
2727 if (errno) {
2728 perror("incorrect address");
2729 exit(1);
2730 }
2731 frame_in.addr = htobe16(addr);
2732
2733 /* Read 256b data */
2734 if (0 == strcmp(argv[3], "-"))
2735 data_fd = STDIN_FILENO;
2736 else {
2737 data_fd = open(argv[3], O_RDONLY);
2738 if (data_fd < 0) {
2739 perror("can't open input file");
2740 exit(1);
2741 }
2742 }
2743
2744 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2745 if (ret < 0) {
2746 perror("read the data");
2747 exit(1);
2748 } else if (ret != sizeof(frame_in.data)) {
2749 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2750 (unsigned long)sizeof(frame_in.data),
2751 ret);
2752 exit(1);
2753 }
2754
2755 /* Read the auth key */
2756 if (0 == strcmp(argv[4], "-"))
2757 key_fd = STDIN_FILENO;
2758 else {
2759 key_fd = open(argv[4], O_RDONLY);
2760 if (key_fd < 0) {
2761 perror("can't open key file");
2762 exit(1);
2763 }
2764 }
2765
2766 ret = DO_IO(read, key_fd, key, sizeof(key));
2767 if (ret < 0) {
2768 perror("read the key");
2769 exit(1);
2770 } else if (ret != sizeof(key)) {
2771 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2772 (unsigned long)sizeof(key),
2773 ret);
2774 exit(1);
2775 }
2776
2777 /* Calculate HMAC SHA256 */
2778 hmac_sha256(
2779 key, sizeof(key),
2780 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2781 frame_in.key_mac, sizeof(frame_in.key_mac));
2782
2783 /* Execute RPMB op */
2784 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2785 if (ret != 0) {
2786 perror("RPMB ioctl failed");
2787 exit(1);
2788 }
2789
2790 /* Check RPMB response */
2791 if (frame_out.result != 0) {
2792 printf("RPMB operation failed, retcode 0x%04x\n",
2793 be16toh(frame_out.result));
2794 exit(1);
2795 }
2796
2797 close(dev_fd);
2798 if (data_fd != STDIN_FILENO)
2799 close(data_fd);
2800 if (key_fd != STDIN_FILENO)
2801 close(key_fd);
2802
2803 return ret;
2804}
Al Cooper786418c2015-04-29 18:12:35 -04002805
2806int do_cache_ctrl(int value, int nargs, char **argv)
2807{
2808 __u8 ext_csd[512];
2809 int fd, ret;
2810 char *device;
2811
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002812 if (nargs != 2) {
2813 fprintf(stderr, "Usage: mmc cache enable </path/to/mmcblkX>\n");
2814 exit(1);
2815 }
Al Cooper786418c2015-04-29 18:12:35 -04002816
2817 device = argv[1];
2818
2819 fd = open(device, O_RDWR);
2820 if (fd < 0) {
2821 perror("open");
2822 exit(1);
2823 }
2824
2825 ret = read_extcsd(fd, ext_csd);
2826 if (ret) {
2827 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2828 exit(1);
2829 }
2830
2831 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2832 fprintf(stderr,
2833 "The CACHE option is only availabe on devices >= "
2834 "MMC 4.5 %s\n", device);
2835 exit(1);
2836 }
2837
2838 /* If the cache size is zero, this device does not have a cache */
2839 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2840 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2841 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2842 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2843 fprintf(stderr,
2844 "The CACHE option is not available on %s\n",
2845 device);
2846 exit(1);
2847 }
2848 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2849 if (ret) {
2850 fprintf(stderr,
2851 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2852 value, EXT_CSD_CACHE_CTRL, device);
2853 exit(1);
2854 }
2855
2856 return ret;
2857}
2858
2859int do_cache_en(int nargs, char **argv)
2860{
2861 return do_cache_ctrl(1, nargs, argv);
2862}
2863
2864int do_cache_dis(int nargs, char **argv)
2865{
2866 return do_cache_ctrl(0, nargs, argv);
2867}
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002868
2869int do_ffu(int nargs, char **argv)
2870{
2871#ifndef MMC_IOC_MULTI_CMD
2872 fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
2873 " support, needed by FFU.\n");
2874 exit(1);
2875#else
2876 int dev_fd, img_fd;
2877 int sect_done = 0, retry = 3, ret = -EINVAL;
2878 unsigned int sect_size;
2879 __u8 ext_csd[EXT_CSD_SIZE];
2880 __u8 *buf;
2881 __u32 arg;
2882 off_t fw_size;
2883 ssize_t chunk_size;
2884 char *device;
2885 struct mmc_ioc_multi_cmd *multi_cmd;
2886
Uwe Kleine-Königcd2fea72017-12-21 11:00:11 +01002887 if (nargs != 3) {
2888 fprintf(stderr, "Usage: ffu <image name> </path/to/mmcblkX> \n");
2889 exit(1);
2890 }
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002891
2892 device = argv[2];
2893 dev_fd = open(device, O_RDWR);
2894 if (dev_fd < 0) {
2895 perror("device open failed");
2896 exit(1);
2897 }
2898 img_fd = open(argv[1], O_RDONLY);
2899 if (img_fd < 0) {
2900 perror("image open failed");
2901 close(dev_fd);
2902 exit(1);
2903 }
2904
2905 buf = malloc(512);
2906 multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
2907 3 * sizeof(struct mmc_ioc_cmd));
2908 if (!buf || !multi_cmd) {
2909 perror("failed to allocate memory");
2910 goto out;
2911 }
2912
2913 ret = read_extcsd(dev_fd, ext_csd);
2914 if (ret) {
2915 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2916 goto out;
2917 }
2918
2919 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
2920 fprintf(stderr,
2921 "The FFU feature is only available on devices >= "
2922 "MMC 5.0, not supported in %s\n", device);
2923 goto out;
2924 }
2925
2926 if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
2927 fprintf(stderr, "FFU is not supported in %s\n", device);
2928 goto out;
2929 }
2930
2931 if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
2932 fprintf(stderr, "Firmware update was disabled in %s\n", device);
2933 goto out;
2934 }
2935
2936 fw_size = lseek(img_fd, 0, SEEK_END);
2937
2938 if (fw_size == 0) {
2939 fprintf(stderr, "Firmware image is empty");
2940 goto out;
2941 }
2942
2943 sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
2944 if (fw_size % sect_size) {
2945 fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
2946 goto out;
2947 }
2948
2949 /* set CMD ARG */
2950 arg = ext_csd[EXT_CSD_FFU_ARG_0] |
2951 ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
2952 ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
2953 ext_csd[EXT_CSD_FFU_ARG_3] << 24;
2954
2955 /* prepare multi_cmd to be sent */
2956 multi_cmd->num_of_cmds = 3;
2957
2958 /* put device into ffu mode */
2959 multi_cmd->cmds[0].opcode = MMC_SWITCH;
2960 multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2961 (EXT_CSD_MODE_CONFIG << 16) |
2962 (EXT_CSD_FFU_MODE << 8) |
2963 EXT_CSD_CMD_SET_NORMAL;
2964 multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2965 multi_cmd->cmds[0].write_flag = 1;
2966
2967 /* send image chunk */
2968 multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
2969 multi_cmd->cmds[1].blksz = sect_size;
2970 multi_cmd->cmds[1].blocks = 1;
2971 multi_cmd->cmds[1].arg = arg;
2972 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2973 multi_cmd->cmds[1].write_flag = 1;
2974 mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);
2975
2976 /* return device into normal mode */
2977 multi_cmd->cmds[2].opcode = MMC_SWITCH;
2978 multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2979 (EXT_CSD_MODE_CONFIG << 16) |
2980 (EXT_CSD_NORMAL_MODE << 8) |
2981 EXT_CSD_CMD_SET_NORMAL;
2982 multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2983 multi_cmd->cmds[2].write_flag = 1;
2984
2985do_retry:
2986 /* read firmware chunk */
2987 lseek(img_fd, 0, SEEK_SET);
2988 chunk_size = read(img_fd, buf, 512);
2989
2990 while (chunk_size > 0) {
2991 /* send ioctl with multi-cmd */
2992 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2993
2994 if (ret) {
2995 perror("Multi-cmd ioctl");
2996 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2997 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2998 goto out;
2999 }
3000
3001 ret = read_extcsd(dev_fd, ext_csd);
3002 if (ret) {
3003 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3004 goto out;
3005 }
3006
3007 /* Test if we need to restart the download */
3008 sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
3009 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
3010 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
3011 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
3012 /* By spec, host should re-start download from the first sector if sect_done is 0 */
3013 if (sect_done == 0) {
3014 if (retry > 0) {
3015 retry--;
3016 fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
3017 goto do_retry;
3018 }
3019 fprintf(stderr, "Programming failed! Aborting...\n");
3020 goto out;
3021 } else {
3022 fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
3023 }
3024
3025 /* read the next firmware chunk (if any) */
3026 chunk_size = read(img_fd, buf, 512);
3027 }
3028
3029 if ((sect_done * sect_size) == fw_size) {
3030 fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
3031 fprintf(stderr, "Programming finished with status %d \n", ret);
3032 }
3033 else {
3034 fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
3035 goto out;
3036 }
3037
3038 /* check mode operation for ffu install*/
3039 if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
3040 fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
3041 } else {
3042 fprintf(stderr, "Installing firmware on %s...\n", device);
3043 /* Re-enter ffu mode and install the firmware */
3044 multi_cmd->num_of_cmds = 2;
3045
3046 /* set ext_csd to install mode */
3047 multi_cmd->cmds[1].opcode = MMC_SWITCH;
3048 multi_cmd->cmds[1].blksz = 0;
3049 multi_cmd->cmds[1].blocks = 0;
3050 multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
3051 (EXT_CSD_MODE_OPERATION_CODES << 16) |
3052 (EXT_CSD_FFU_INSTALL << 8) |
3053 EXT_CSD_CMD_SET_NORMAL;
3054 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
3055 multi_cmd->cmds[1].write_flag = 1;
3056
3057 /* send ioctl with multi-cmd */
3058 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
3059
3060 if (ret) {
3061 perror("Multi-cmd ioctl failed setting install mode");
3062 /* In case multi-cmd ioctl failed before exiting from ffu mode */
3063 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
3064 goto out;
3065 }
3066
3067 ret = read_extcsd(dev_fd, ext_csd);
3068 if (ret) {
3069 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
3070 goto out;
3071 }
3072
3073 /* return status */
3074 ret = ext_csd[EXT_CSD_FFU_STATUS];
3075 if (ret) {
3076 fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
3077 goto out;
3078 } else {
3079 fprintf(stderr, "FFU finished successfully\n");
3080 }
3081 }
3082
3083out:
3084 free(buf);
3085 free(multi_cmd);
3086 close(img_fd);
3087 close(dev_fd);
3088 return ret;
3089#endif
3090}