blob: a05b3598c9723f25b6459e3e0ca9fc168e9c4632 [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.
15 */
16
Gwendal Grignou771984c2014-07-01 12:46:18 -070017#include <errno.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050018#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/ioctl.h>
Gwendal Grignou771984c2014-07-01 12:46:18 -070022#include <sys/param.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050023#include <sys/types.h>
24#include <dirent.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <fcntl.h>
28#include <libgen.h>
29#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050030#include <ctype.h>
Roman Peniaev023cc7c2014-08-12 23:25:45 +090031#include <errno.h>
32#include <stdint.h>
33#include <assert.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050034
35#include "mmc.h"
36#include "mmc_cmds.h"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080037#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050038
Nick Sanders9d57aa72014-03-05 21:38:54 -080039#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080040#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080041#define CID_SIZE 16
42
Roman Peniaev023cc7c2014-08-12 23:25:45 +090043#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080044
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050045int read_extcsd(int fd, __u8 *ext_csd)
46{
47 int ret = 0;
48 struct mmc_ioc_cmd idata;
49 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080050 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050051 idata.write_flag = 0;
52 idata.opcode = MMC_SEND_EXT_CSD;
53 idata.arg = 0;
54 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080055 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050056 idata.blocks = 1;
57 mmc_ioc_cmd_set_data(idata, ext_csd);
58
59 ret = ioctl(fd, MMC_IOC_CMD, &idata);
60 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080061 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050062
63 return ret;
64}
65
66int write_extcsd_value(int fd, __u8 index, __u8 value)
67{
68 int ret = 0;
69 struct mmc_ioc_cmd idata;
70
71 memset(&idata, 0, sizeof(idata));
72 idata.write_flag = 1;
73 idata.opcode = MMC_SWITCH;
74 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
75 (index << 16) |
76 (value << 8) |
77 EXT_CSD_CMD_SET_NORMAL;
78 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
79
80 ret = ioctl(fd, MMC_IOC_CMD, &idata);
81 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080082 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050083
84 return ret;
85}
86
Ben Gardiner27c357d2013-05-30 17:12:47 -040087int send_status(int fd, __u32 *response)
88{
89 int ret = 0;
90 struct mmc_ioc_cmd idata;
91
92 memset(&idata, 0, sizeof(idata));
93 idata.opcode = MMC_SEND_STATUS;
94 idata.arg = (1 << 16);
95 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
96
97 ret = ioctl(fd, MMC_IOC_CMD, &idata);
98 if (ret)
99 perror("ioctl");
100
101 *response = idata.response[0];
102
103 return ret;
104}
105
Chris Ballb9c7a172012-02-20 12:34:25 -0500106void print_writeprotect_status(__u8 *ext_csd)
107{
108 __u8 reg;
109 __u8 ext_csd_rev = ext_csd[192];
110
111 /* A43: reserved [174:0] */
112 if (ext_csd_rev >= 5) {
113 printf("Boot write protection status registers"
114 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
115
116 reg = ext_csd[EXT_CSD_BOOT_WP];
117 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
118 printf(" Power ro locking: ");
119 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
120 printf("not possible\n");
121 else
122 printf("possible\n");
123
124 printf(" Permanent ro locking: ");
125 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
126 printf("not possible\n");
127 else
128 printf("possible\n");
129
130 printf(" ro lock status: ");
131 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
132 printf("locked until next power on\n");
133 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
134 printf("locked permanently\n");
135 else
136 printf("not locked\n");
137 }
138}
139
140int do_writeprotect_get(int nargs, char **argv)
141{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800142 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500143 int fd, ret;
144 char *device;
145
Chris Ball8ba44662012-04-19 13:22:54 -0400146 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
147 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500148
149 device = argv[1];
150
151 fd = open(device, O_RDWR);
152 if (fd < 0) {
153 perror("open");
154 exit(1);
155 }
156
157 ret = read_extcsd(fd, ext_csd);
158 if (ret) {
159 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
160 exit(1);
161 }
162
163 print_writeprotect_status(ext_csd);
164
165 return ret;
166}
167
168int do_writeprotect_set(int nargs, char **argv)
169{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800170 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500171 int fd, ret;
172 char *device;
173
Chris Ball8ba44662012-04-19 13:22:54 -0400174 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
175 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500176
177 device = argv[1];
178
179 fd = open(device, O_RDWR);
180 if (fd < 0) {
181 perror("open");
182 exit(1);
183 }
184
185 ret = read_extcsd(fd, ext_csd);
186 if (ret) {
187 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
188 exit(1);
189 }
190
191 value = ext_csd[EXT_CSD_BOOT_WP] |
192 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
193 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
194 if (ret) {
195 fprintf(stderr, "Could not write 0x%02x to "
196 "EXT_CSD[%d] in %s\n",
197 value, EXT_CSD_BOOT_WP, device);
198 exit(1);
199 }
200
201 return ret;
202}
203
Saugata Dasb7e25992012-05-17 09:26:34 -0400204int do_disable_512B_emulation(int nargs, char **argv)
205{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800206 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400207 int fd, ret;
208 char *device;
209
210 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
211 device = argv[1];
212
213 fd = open(device, O_RDWR);
214 if (fd < 0) {
215 perror("open");
216 exit(1);
217 }
218
219 ret = read_extcsd(fd, ext_csd);
220 if (ret) {
221 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
222 exit(1);
223 }
224
225 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
226 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
227 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
228
229 if (native_sector_size && !data_sector_size &&
230 (wr_rel_param & EN_REL_WR)) {
231 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
232
233 if (ret) {
234 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
235 1, EXT_CSD_BOOT_WP, device);
236 exit(1);
237 }
238 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
239 } else if (native_sector_size && data_sector_size) {
240 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
241 } else {
242 printf("MMC does not support disabling 512B emulation mode.\n");
243 }
244
245 return ret;
246}
247
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200248int do_write_boot_en(int nargs, char **argv)
249{
250 __u8 ext_csd[512];
251 __u8 value = 0;
252 int fd, ret;
253 char *device;
254 int boot_area, send_ack;
255
256 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
257 "<send_ack> </path/to/mmcblkX>\n", exit(1));
258
259 /*
260 * If <send_ack> is 1, the device will send acknowledgment
261 * pattern "010" to the host when boot operation begins.
262 * If <send_ack> is 0, it won't.
263 */
264 boot_area = strtol(argv[1], NULL, 10);
265 send_ack = strtol(argv[2], NULL, 10);
266 device = argv[3];
267
268 fd = open(device, O_RDWR);
269 if (fd < 0) {
270 perror("open");
271 exit(1);
272 }
273
274 ret = read_extcsd(fd, ext_csd);
275 if (ret) {
276 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
277 exit(1);
278 }
279
280 value = ext_csd[EXT_CSD_PART_CONFIG];
281
282 switch (boot_area) {
283 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
284 value |= (1 << 3);
285 value &= ~(3 << 4);
286 break;
287 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
288 value |= (1 << 4);
289 value &= ~(1 << 3);
290 value &= ~(1 << 5);
291 break;
292 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
293 value |= (boot_area << 3);
294 break;
295 default:
296 fprintf(stderr, "Cannot enable the boot area\n");
297 exit(1);
298 }
299 if (send_ack)
300 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
301 else
302 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
303
304 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
305 if (ret) {
306 fprintf(stderr, "Could not write 0x%02x to "
307 "EXT_CSD[%d] in %s\n",
308 value, EXT_CSD_PART_CONFIG, device);
309 exit(1);
310 }
311 return ret;
312}
313
Chris Ballf74dfe22012-10-19 16:49:55 -0400314int do_hwreset(int value, int nargs, char **argv)
315{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800316 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400317 int fd, ret;
318 char *device;
319
320 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
321 exit(1));
322
323 device = argv[1];
324
325 fd = open(device, O_RDWR);
326 if (fd < 0) {
327 perror("open");
328 exit(1);
329 }
330
331 ret = read_extcsd(fd, ext_csd);
332 if (ret) {
333 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
334 exit(1);
335 }
336
337 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
338 EXT_CSD_HW_RESET_EN) {
339 fprintf(stderr,
340 "H/W Reset is already permanently enabled on %s\n",
341 device);
342 exit(1);
343 }
344 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
345 EXT_CSD_HW_RESET_DIS) {
346 fprintf(stderr,
347 "H/W Reset is already permanently disabled on %s\n",
348 device);
349 exit(1);
350 }
351
352 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
353 if (ret) {
354 fprintf(stderr,
355 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
356 value, EXT_CSD_RST_N_FUNCTION, device);
357 exit(1);
358 }
359
360 return ret;
361}
362
363int do_hwreset_en(int nargs, char **argv)
364{
365 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
366}
367
368int do_hwreset_dis(int nargs, char **argv)
369{
370 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
371}
372
Jaehoon Chung86496512012-09-21 10:08:05 +0000373int do_write_bkops_en(int nargs, char **argv)
374{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800375 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000376 int fd, ret;
377 char *device;
378
379 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
380 exit(1));
381
382 device = argv[1];
383
384 fd = open(device, O_RDWR);
385 if (fd < 0) {
386 perror("open");
387 exit(1);
388 }
389
390 ret = read_extcsd(fd, ext_csd);
391 if (ret) {
392 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
393 exit(1);
394 }
395
396 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
397 fprintf(stderr, "%s doesn't support BKOPS\n", device);
398 exit(1);
399 }
400
401 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
402 if (ret) {
403 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
404 value, EXT_CSD_BKOPS_EN, device);
405 exit(1);
406 }
407
408 return ret;
409}
410
Ben Gardiner27c357d2013-05-30 17:12:47 -0400411int do_status_get(int nargs, char **argv)
412{
413 __u32 response;
414 int fd, ret;
415 char *device;
416
417 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
418 exit(1));
419
420 device = argv[1];
421
422 fd = open(device, O_RDWR);
423 if (fd < 0) {
424 perror("open");
425 exit(1);
426 }
427
428 ret = send_status(fd, &response);
429 if (ret) {
430 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
431 exit(1);
432 }
433
434 printf("SEND_STATUS response: 0x%08x\n", response);
435
436 return ret;
437}
438
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800439__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
440{
441 return (ext_csd_loc[3] << 24) |
442 (ext_csd_loc[2] << 16) |
443 (ext_csd_loc[1] << 8) |
444 ext_csd_loc[0];
445}
446
Ben Gardiner4e850232013-05-30 17:12:49 -0400447unsigned int get_sector_count(__u8 *ext_csd)
448{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800449 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400450}
451
452int is_blockaddresed(__u8 *ext_csd)
453{
454 unsigned int sectors = get_sector_count(ext_csd);
455
456 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
457}
458
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400459unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
460{
461 return ext_csd[221];
462}
463
464unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
465{
466 return ext_csd[224];
467}
468
Ben Gardinere6e84e92013-09-19 11:14:27 -0400469int set_partitioning_setting_completed(int dry_run, const char * const device,
470 int fd)
471{
472 int ret;
473
474 if (dry_run) {
475 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
476 fprintf(stderr, "These changes will not take effect neither "
477 "now nor after a power cycle\n");
478 return 1;
479 }
480
481 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
482 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
483 if (ret) {
484 fprintf(stderr, "Could not write 0x1 to "
485 "EXT_CSD[%d] in %s\n",
486 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
487 return 1;
488 }
489
490 __u32 response;
491 ret = send_status(fd, &response);
492 if (ret) {
493 fprintf(stderr, "Could not get response to SEND_STATUS "
494 "from %s\n", device);
495 return 1;
496 }
497
498 if (response & R1_SWITCH_ERROR) {
499 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
500 "failed on %s\n", device);
501 return 1;
502 }
503
504 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
505 "%s SUCCESS\n", device);
506 fprintf(stderr, "Device power cycle needed for settings to "
507 "take effect.\n"
508 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
509 "using 'extcsd read' after power cycle\n");
510
511 return 0;
512}
513
Ben Gardinerd91d3692013-05-30 17:12:51 -0400514int do_enh_area_set(int nargs, char **argv)
515{
516 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800517 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400518 int fd, ret;
519 char *device;
520 int dry_run = 1;
521 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
522 unsigned long align;
523
524 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
525 "</path/to/mmcblkX>\n", exit(1));
526
527 if (!strcmp("-y", argv[1]))
528 dry_run = 0;
529
530 start_kib = strtol(argv[2], NULL, 10);
531 length_kib = strtol(argv[3], NULL, 10);
532 device = argv[4];
533
534 fd = open(device, O_RDWR);
535 if (fd < 0) {
536 perror("open");
537 exit(1);
538 }
539
540 ret = read_extcsd(fd, ext_csd);
541 if (ret) {
542 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
543 exit(1);
544 }
545
546 /* assert ENH_ATTRIBUTE_EN */
547 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
548 {
549 printf(" Device cannot have enhanced tech.\n");
550 exit(1);
551 }
552
553 /* assert not PARTITION_SETTING_COMPLETED */
554 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
555 {
556 printf(" Device is already partitioned\n");
557 exit(1);
558 }
559
560 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
561
562 enh_size_mult = (length_kib + align/2l) / align;
563
564 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
565 enh_start_addr /= align;
566 enh_start_addr *= align;
567
568 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
569 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
570 if (ret) {
571 fprintf(stderr, "Could not write 0x1 to "
572 "EXT_CSD[%d] in %s\n",
573 EXT_CSD_ERASE_GROUP_DEF, device);
574 exit(1);
575 }
576
577 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
578 value = (enh_start_addr >> 24) & 0xff;
579 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
580 if (ret) {
581 fprintf(stderr, "Could not write 0x%02x to "
582 "EXT_CSD[%d] in %s\n", value,
583 EXT_CSD_ENH_START_ADDR_3, device);
584 exit(1);
585 }
586 value = (enh_start_addr >> 16) & 0xff;
587 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
588 if (ret) {
589 fprintf(stderr, "Could not write 0x%02x to "
590 "EXT_CSD[%d] in %s\n", value,
591 EXT_CSD_ENH_START_ADDR_2, device);
592 exit(1);
593 }
594 value = (enh_start_addr >> 8) & 0xff;
595 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
596 if (ret) {
597 fprintf(stderr, "Could not write 0x%02x to "
598 "EXT_CSD[%d] in %s\n", value,
599 EXT_CSD_ENH_START_ADDR_1, device);
600 exit(1);
601 }
602 value = enh_start_addr & 0xff;
603 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
604 if (ret) {
605 fprintf(stderr, "Could not write 0x%02x to "
606 "EXT_CSD[%d] in %s\n", value,
607 EXT_CSD_ENH_START_ADDR_0, device);
608 exit(1);
609 }
610
611 value = (enh_size_mult >> 16) & 0xff;
612 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
613 if (ret) {
614 fprintf(stderr, "Could not write 0x%02x to "
615 "EXT_CSD[%d] in %s\n", value,
616 EXT_CSD_ENH_SIZE_MULT_2, device);
617 exit(1);
618 }
619 value = (enh_size_mult >> 8) & 0xff;
620 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
621 if (ret) {
622 fprintf(stderr, "Could not write 0x%02x to "
623 "EXT_CSD[%d] in %s\n", value,
624 EXT_CSD_ENH_SIZE_MULT_1, device);
625 exit(1);
626 }
627 value = enh_size_mult & 0xff;
628 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
629 if (ret) {
630 fprintf(stderr, "Could not write 0x%02x to "
631 "EXT_CSD[%d] in %s\n", value,
632 EXT_CSD_ENH_SIZE_MULT_0, device);
633 exit(1);
634 }
635
636 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, EXT_CSD_ENH_USR);
637 if (ret) {
638 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
639 "EXT_CSD[%d] in %s\n",
640 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
641 exit(1);
642 }
643
Ben Gardinere6e84e92013-09-19 11:14:27 -0400644 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400645
Ben Gardinere6e84e92013-09-19 11:14:27 -0400646 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400647 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400648
649 return 0;
650}
651
Ben Gardiner196d0d22013-09-19 11:14:29 -0400652int do_write_reliability_set(int nargs, char **argv)
653{
654 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800655 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400656 int fd, ret;
657
658 int dry_run = 1;
659 int partition;
660 char *device;
661
662 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
663 "<partition> </path/to/mmcblkX>\n", exit(1));
664
665 if (!strcmp("-y", argv[1]))
666 dry_run = 0;
667
668 partition = strtol(argv[2], NULL, 10);
669 device = argv[3];
670
671 fd = open(device, O_RDWR);
672 if (fd < 0) {
673 perror("open");
674 exit(1);
675 }
676
677 ret = read_extcsd(fd, ext_csd);
678 if (ret) {
679 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
680 exit(1);
681 }
682
683 /* assert not PARTITION_SETTING_COMPLETED */
684 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
685 {
686 printf(" Device is already partitioned\n");
687 exit(1);
688 }
689
690 /* assert HS_CTRL_REL */
691 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
692 printf("Cannot set write reliability parameters, WR_REL_SET is "
693 "read-only\n");
694 exit(1);
695 }
696
697 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
698 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
699 if (ret) {
700 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
701 value, EXT_CSD_WR_REL_SET, device);
702 exit(1);
703 }
704
705 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
706 value, device);
707
708 if (!set_partitioning_setting_completed(dry_run, device, fd))
709 exit(1);
710
711 return 0;
712}
713
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500714int do_read_extcsd(int nargs, char **argv)
715{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800716 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +0200717 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500718 int fd, ret;
719 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100720 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800721 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800722 "4.0", /* 0 */
723 "4.1", /* 1 */
724 "4.2", /* 2 */
725 "4.3", /* 3 */
726 "Obsolete", /* 4 */
727 "4.41", /* 5 */
728 "4.5", /* 6 */
729 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -0700730 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800731 };
732 int boot_access;
733 const char* boot_access_str[] = {
734 "No access to boot partition", /* 0 */
735 "R/W Boot Partition 1", /* 1 */
736 "R/W Boot Partition 2", /* 2 */
737 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
738 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500739
Chris Ball8ba44662012-04-19 13:22:54 -0400740 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
741 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500742
743 device = argv[1];
744
745 fd = open(device, O_RDWR);
746 if (fd < 0) {
747 perror("open");
748 exit(1);
749 }
750
751 ret = read_extcsd(fd, ext_csd);
752 if (ret) {
753 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
754 exit(1);
755 }
756
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100757 ext_csd_rev = ext_csd[192];
758
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800759 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
760 (ext_csd_rev != 4))
761 str = ver_str[ext_csd_rev];
762 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100763 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800764
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100765 printf("=============================================\n");
766 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
767 printf("=============================================\n\n");
768
769 if (ext_csd_rev < 3)
770 goto out_free; /* No ext_csd */
771
772 /* Parse the Extended CSD registers.
773 * Reserved bit should be read as "0" in case of spec older
774 * than A441.
775 */
776 reg = ext_csd[EXT_CSD_S_CMD_SET];
777 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
778 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -0500779 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100780
781 reg = ext_csd[EXT_CSD_HPI_FEATURE];
782 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
783 if (reg & EXT_CSD_HPI_SUPP) {
784 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -0500785 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100786 else
787 printf("implementation based on CMD13\n");
788 }
789
790 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
791 ext_csd[502]);
792
793 if (ext_csd_rev >= 6) {
794 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
795 ext_csd[501]);
796 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
797 ext_csd[500]);
798 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
799 ext_csd[499]);
800
801 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
802 ext_csd[498]);
803 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
804 ext_csd[497]);
805 printf("Context Management Capabilities"
806 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
807 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
808 ext_csd[495]);
809 printf("Extended partition attribute support"
810 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800811 }
812 if (ext_csd_rev >= 7) {
813 int j;
814 int eol_info;
815 char* eol_info_str[] = {
816 "Not Defined", /* 0 */
817 "Normal", /* 1 */
818 "Warning", /* 2 */
819 "Urgent", /* 3 */
820 };
821
822 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
823 ext_csd[493]);
824 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
825 ext_csd[492]);
826 printf("Operation codes timeout"
827 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
828 ext_csd[491]);
829 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
830 get_word_from_ext_csd(&ext_csd[487]));
831 printf("Number of FW sectors correctly programmed"
832 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
833 get_word_from_ext_csd(&ext_csd[302]));
834 printf("Vendor proprietary health report:\n");
835 for (j = 301; j >= 270; j--)
836 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
837 " 0x%02x\n", j, ext_csd[j]);
838 for (j = 269; j >= 268; j--) {
839 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800840 char est_type = 'B' + (j - 269);
841 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800842 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800843 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800844 if (life_used >= 0x1 && life_used <= 0xa)
845 printf(" i.e. %d%% - %d%% device life time"
846 " used\n",
847 (life_used - 1) * 10, life_used * 10);
848 else if (life_used == 0xb)
849 printf(" i.e. Exceeded its maximum estimated"
850 " device life time\n");
851 }
852 eol_info = ext_csd[267];
853 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
854 eol_info);
855 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
856 printf(" i.e. %s\n", eol_info_str[eol_info]);
857 else
858 printf(" i.e. Reserved\n");
859
860 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
861 ext_csd[266]);
862 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
863 ext_csd[265]);
864 printf("Optimal trim unit size"
865 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
866 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
867 ext_csd[263], ext_csd[262]);
868 printf("Firmware version:\n");
869 for (j = 261; j >= 254; j--)
870 printf("[FIRMWARE_VERSION[%d]]:"
871 " 0x%02x\n", j, ext_csd[j]);
872
873 printf("Power class for 200MHz, DDR at VCC= 3.6V"
874 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
875 }
876 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100877 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
878 ext_csd[248]);
879 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
880 ext_csd[247]);
881 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800882 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100883 }
884
885 /* A441: Reserved [501:247]
886 A43: reserved [246:229] */
887 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100888 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -0500889 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100890
891 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
892
893 printf("1st Initialisation Time after programmed sector"
894 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
895
896 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100897 printf("Power class for 52MHz, DDR at 3.6V"
898 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
899 printf("Power class for 52MHz, DDR at 1.95V"
900 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
901
902 /* A441: reserved [237-236] */
903
904 if (ext_csd_rev >= 6) {
905 printf("Power class for 200MHz at 3.6V"
906 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
907 printf("Power class for 200MHz, at 1.95V"
908 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
909 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500910 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100911 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
912 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
913 /* A441: reserved [233] */
914 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
915 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
916 ext_csd[231]);
917 }
918 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
919 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
920 ext_csd[230]);
921 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
922 ext_csd[229]);
923 }
924 reg = ext_csd[EXT_CSD_BOOT_INFO];
925 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
926 if (reg & EXT_CSD_BOOT_INFO_ALT)
927 printf(" Device supports alternative boot method\n");
928 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
929 printf(" Device supports dual data rate during boot\n");
930 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
931 printf(" Device supports high speed timing during boot\n");
932
933 /* A441/A43: reserved [227] */
934 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
935 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400936
937 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100938 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400939 reg);
940 printf(" i.e. %u KiB\n", 512 * reg);
941
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100942 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
943 ext_csd[223]);
944 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
945 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400946
947 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100948 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400949 reg);
950 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
951
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100952 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
953 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
954 /* A441/A43: reserved [218] */
955 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
956 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -0400957
958 unsigned int sectors = get_sector_count(ext_csd);
959 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
960 if (is_blockaddresed(ext_csd))
961 printf(" Device is block-addressed\n");
962 else
963 printf(" Device is NOT block-addressed\n");
964
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100965 /* A441/A43: reserved [211] */
966 printf("Minimum Write Performance for 8bit:\n");
967 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
968 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
969 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
970 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
971 printf("Minimum Write Performance for 4bit:\n");
972 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
973 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
974 /* A441/A43: reserved [204] */
975 printf("Power classes registers:\n");
976 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
977 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
978 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
979 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
980
981 /* A43: reserved [199:198] */
982 if (ext_csd_rev >= 5) {
983 printf("Partition switching timing "
984 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
985 printf("Out-of-interrupt busy timing"
986 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
987 }
988
989 /* A441/A43: reserved [197] [195] [193] [190] [188]
990 * [186] [184] [182] [180] [176] */
991
992 if (ext_csd_rev >= 6)
993 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
994 ext_csd[197]);
995
Oleg Matcovschi64f63a32013-05-23 17:11:07 -0700996 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -0700997 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
998 ext_csd[196], ext_csd[195]);
999 reg = ext_csd[195];
1000 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1001 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001002 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001003 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1004 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001005 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1006 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1007 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1008 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1009 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1010 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001011
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001012 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1013 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1014 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1015 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1016 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1017 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1018 ext_csd[185]);
1019 /* bus_width: ext_csd[183] not readable */
1020 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1021 ext_csd[181]);
1022 reg = ext_csd[EXT_CSD_BOOT_CFG];
1023 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001024 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001025 case 0x0:
1026 printf(" Not boot enable\n");
1027 break;
1028 case 0x1:
1029 printf(" Boot Partition 1 enabled\n");
1030 break;
1031 case 0x2:
1032 printf(" Boot Partition 2 enabled\n");
1033 break;
1034 case 0x7:
1035 printf(" User Area Enabled for boot\n");
1036 break;
1037 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001038 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1039 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1040 printf(" %s\n", boot_access_str[boot_access]);
1041 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001042 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001043 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001044
1045 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1046 ext_csd[178]);
1047 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1048 ext_csd[177]);
1049 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001050 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001051
Chris Ballb9c7a172012-02-20 12:34:25 -05001052 print_writeprotect_status(ext_csd);
1053
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001054 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001055 /* A441]: reserved [172] */
1056 printf("User area write protection register"
1057 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1058 /* A441]: reserved [170] */
1059 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1060 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001061
1062 reg = ext_csd[EXT_CSD_WR_REL_SET];
1063 const char * const fast = "existing data is at risk if a power "
1064 "failure occurs during a write operation";
1065 const char * const reliable = "the device protects existing "
1066 "data if a power failure occurs during a write "
1067 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001068 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001069 " [WR_REL_SET]: 0x%02x\n", reg);
1070
1071 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1072 int i;
1073 for (i = 1; i <= 4; i++) {
1074 printf(" partition %d: %s\n", i,
1075 reg & (1<<i) ? reliable : fast);
1076 }
1077
1078 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001079 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001080 " [WR_REL_PARAM]: 0x%02x\n", reg);
1081 if (reg & 0x01)
1082 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1083 if (reg & 0x04)
1084 printf(" Device supports the enhanced def. of reliable "
1085 "write\n");
1086
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001087 /* sanitize_start ext_csd[165]]: not readable
1088 * bkops_start ext_csd[164]]: only writable */
1089 printf("Enable background operations handshake"
1090 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1091 printf("H/W reset function"
1092 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1093 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001094 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001095 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1096 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001097 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001098 printf(" Device support partitioning feature\n");
1099 else
1100 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001101 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001102 printf(" Device can have enhanced tech.\n");
1103 else
1104 printf(" Device cannot have enhanced tech.\n");
1105
Oliver Metz11f2cea2013-09-23 08:40:52 +02001106 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001107 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1108 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1109
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001110 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001111 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001112 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1113 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001114 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001115
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001116 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001117 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001118 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001119 printf("Partitioning Setting"
1120 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001121 reg);
1122 if (reg)
1123 printf(" Device partition setting complete\n");
1124 else
1125 printf(" Device partition setting NOT complete\n");
1126
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001127 printf("General Purpose Partition Size\n"
1128 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1129 (ext_csd[153] << 8) | ext_csd[152]);
1130 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1131 (ext_csd[150] << 8) | ext_csd[149]);
1132 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1133 (ext_csd[147] << 8) | ext_csd[146]);
1134 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1135 (ext_csd[144] << 8) | ext_csd[143]);
1136
Oliver Metz11f2cea2013-09-23 08:40:52 +02001137 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001138 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1139 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001140 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001141 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1142 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001143 get_hc_erase_grp_size(ext_csd) *
1144 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001145
Oliver Metz11f2cea2013-09-23 08:40:52 +02001146 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001147 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1148 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1149 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001150 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001151 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001152 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001153 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001154
1155 /* A441]: reserved [135] */
1156 printf("Bad Block Management mode"
1157 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1158 /* A441: reserved [133:0] */
1159 }
1160 /* B45 */
1161 if (ext_csd_rev >= 6) {
1162 int j;
1163 /* tcase_support ext_csd[132] not readable */
1164 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1165 ext_csd[131]);
1166 printf("Program CID/CSD in DDR mode support"
1167 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1168 ext_csd[130]);
1169
1170 for (j = 127; j >= 64; j--)
1171 printf("Vendor Specific Fields"
1172 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1173 j, ext_csd[j]);
1174
Gwendal Grignoue966e672014-07-07 14:03:13 -07001175 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001176 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001177 reg);
1178 if (reg == 0x00)
1179 printf(" i.e. 512 B\n");
1180 else if (reg == 0x01)
1181 printf(" i.e. 4 KiB\n");
1182 else
1183 printf(" i.e. Reserved\n");
1184
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001185 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1186 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001187 reg = ext_csd[61];
1188 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1189 if (reg == 0x00)
1190 printf(" i.e. 512 B\n");
1191 else if (reg == 0x01)
1192 printf(" i.e. 4 KiB\n");
1193 else
1194 printf(" i.e. Reserved\n");
1195
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001196 printf("1st initialization after disabling sector"
1197 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1198 ext_csd[60]);
1199 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1200 ext_csd[59]);
1201 printf("Number of addressed group to be Released"
1202 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1203 printf("Exception events control"
1204 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1205 (ext_csd[57] << 8) | ext_csd[56]);
1206 printf("Exception events status"
1207 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1208 (ext_csd[55] << 8) | ext_csd[54]);
1209 printf("Extended Partitions Attribute"
1210 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1211 (ext_csd[53] << 8) | ext_csd[52]);
1212
1213 for (j = 51; j >= 37; j--)
1214 printf("Context configuration"
1215 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1216
1217 printf("Packed command status"
1218 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1219 printf("Packed command failure index"
1220 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1221 printf("Power Off Notification"
1222 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001223 printf("Control to turn the Cache ON/OFF"
1224 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001225 /* flush_cache ext_csd[32] not readable */
1226 /*Reserved [31:0] */
1227 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001228 if (ext_csd_rev >= 7) {
1229 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1230 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1231 ext_csd[29]);
1232
1233 reg = ext_csd[26];
1234 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1235 switch (reg) {
1236 case 0x00:
1237 printf(" Success\n");
1238 break;
1239 case 0x10:
1240 printf(" General error\n");
1241 break;
1242 case 0x11:
1243 printf(" Firmware install error\n");
1244 break;
1245 case 0x12:
1246 printf(" Error in downloading firmware\n");
1247 break;
1248 default:
1249 printf(" Reserved\n");
1250 }
1251 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1252 " %d sector size\n",
1253 get_word_from_ext_csd(&ext_csd[22]));
1254 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1255 " %d sector size\n",
1256 get_word_from_ext_csd(&ext_csd[18]));
1257 printf("Product state awareness enablement"
1258 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1259 ext_csd[17]);
1260 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1261 ext_csd[16]);
1262 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001263
1264out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001265 return ret;
1266}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001267
Nick Sanders9d57aa72014-03-05 21:38:54 -08001268int do_dump_extcsd(int nargs, char **argv)
1269{
1270 __u8 ext_csd[EXT_CSD_SIZE];
1271 int fd, ret;
1272 char *device;
1273 int i, j;
1274
1275 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1276 exit(1));
1277
1278 device = argv[1];
1279
1280 fd = open(device, O_RDWR);
1281 if (fd < 0) {
1282 perror("Failed to open mmc device");
1283 exit(1);
1284 }
1285
1286 ret = read_extcsd(fd, ext_csd);
1287 if (ret) {
1288 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1289 exit(1);
1290 }
1291
1292 /* Dump all bytes so that any undecoded or proprietary registers */
1293 /* can be acessed. */
1294 printf("EXT_CSD binary dump:\n");
1295 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1296 printf(" %3d: %3x: ", i, i);
1297 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1298 printf(" %02x", ext_csd[i+j]);
1299 }
1300 printf("\n");
1301 }
1302
1303 return ret;
1304}
1305
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001306int do_sanitize(int nargs, char **argv)
1307{
1308 int fd, ret;
1309 char *device;
1310
1311 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1312 exit(1));
1313
1314 device = argv[1];
1315
1316 fd = open(device, O_RDWR);
1317 if (fd < 0) {
1318 perror("open");
1319 exit(1);
1320 }
1321
1322 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1323 if (ret) {
1324 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1325 1, EXT_CSD_SANITIZE_START, device);
1326 exit(1);
1327 }
1328
1329 return ret;
1330
1331}
1332
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001333static const char* const mmc_ffu_hack_names[] = {
1334 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1335};
1336
Gwendal Grignou771984c2014-07-01 12:46:18 -07001337int do_emmc50_ffu (int nargs, char **argv)
1338{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001339 int fd, ret, i, argc=1, ffu_hack=0;
1340 char *device, *type, *path;
1341 __u64 value;
1342 union {
1343 __u8 data[FFU_DATA_SIZE];
1344 struct mmc_ffu_args ffu_args;
1345 } ffu_data;
1346 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001347 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001348
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001349 while (!strcmp("-k", argv[argc])) {
1350 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1351 if (ret < 1) {
1352 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1353 exit(1);
1354 }
1355 for (i = 0; i < MMC_HACK_LEN; i++) {
1356 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1357 ffu_args->hack[ffu_hack].type = i;
1358 if (ret == 2) {
1359 ffu_args->hack[ffu_hack].value = value;
1360 }
1361 ffu_hack++;
1362 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1363 sizeof(struct mmc_ffu_args) >
1364 FFU_DATA_SIZE) {
1365 fprintf(stderr, "Too many %d hacks",
1366 ffu_hack);
1367 exit(1);
1368 }
1369 break;
1370 }
1371 }
1372 if (i == MMC_HACK_LEN) {
1373 fprintf(stderr, "Hack type %s not found\n", type);
1374 fprintf(stderr, "Supported types are: ");
1375 for (i = 0; i < MMC_HACK_LEN; i++)
1376 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1377 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001378
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001379 exit(1);
1380 }
1381 free(type);
1382 argc++;
1383 }
1384 ffu_args->hack_nb = ffu_hack;
1385
1386 path = argv[argc++];
1387 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001388 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1389 exit(1);
1390 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001391 strcpy(ffu_args->name, path);
1392 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001393 fd = open(device, O_RDWR);
1394 if (fd < 0) {
1395 perror("open");
1396 exit(1);
1397 }
1398
Gwendal Grignou0f757342014-10-16 16:52:46 -07001399 /* prepare and send ioctl */
1400 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1401 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001402 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001403 mmc_ioc_cmd.blocks = 1;
1404 mmc_ioc_cmd.arg = 0;
1405 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1406 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001407 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001408 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001409 if (ret) {
1410 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1411 exit(1);
1412 }
1413
1414 close(fd);
1415 return 0;
1416}
1417
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001418#define DO_IO(func, fd, buf, nbyte) \
1419 ({ \
1420 ssize_t ret = 0, r; \
1421 do { \
1422 r = func(fd, buf + ret, nbyte - ret); \
1423 if (r < 0 && errno != EINTR) { \
1424 ret = -1; \
1425 break; \
1426 } \
1427 else if (r > 0) \
1428 ret += r; \
1429 } while (r != 0 && (size_t)ret != nbyte); \
1430 \
1431 ret; \
1432 })
1433
1434enum rpmb_op_type {
1435 MMC_RPMB_WRITE_KEY = 0x01,
1436 MMC_RPMB_READ_CNT = 0x02,
1437 MMC_RPMB_WRITE = 0x03,
1438 MMC_RPMB_READ = 0x04,
1439
1440 /* For internal usage only, do not use it directly */
1441 MMC_RPMB_READ_RESP = 0x05
1442};
1443
1444struct rpmb_frame {
1445 u_int8_t stuff[196];
1446 u_int8_t key_mac[32];
1447 u_int8_t data[256];
1448 u_int8_t nonce[16];
1449 u_int32_t write_counter;
1450 u_int16_t addr;
1451 u_int16_t block_count;
1452 u_int16_t result;
1453 u_int16_t req_resp;
1454};
1455
1456/* Performs RPMB operation.
1457 *
1458 * @fd: RPMB device on which we should perform ioctl command
1459 * @frame_in: input RPMB frame, should be properly inited
1460 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1461 * result and req_resp for output frame.
1462 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1463 * in the other cases -EINVAL will be returned.
1464 */
1465static int do_rpmb_op(int fd,
1466 const struct rpmb_frame *frame_in,
1467 struct rpmb_frame *frame_out,
1468 unsigned int out_cnt)
1469{
1470 int err;
1471 u_int16_t rpmb_type;
1472
1473 struct mmc_ioc_cmd ioc = {
1474 .arg = 0x0,
1475 .blksz = 512,
1476 .blocks = 1,
1477 .write_flag = 1,
1478 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1479 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1480 .data_ptr = (uintptr_t)frame_in
1481 };
1482
1483 if (!frame_in || !frame_out || !out_cnt)
1484 return -EINVAL;
1485
1486 rpmb_type = be16toh(frame_in->req_resp);
1487
1488 switch(rpmb_type) {
1489 case MMC_RPMB_WRITE:
1490 case MMC_RPMB_WRITE_KEY:
1491 if (out_cnt != 1) {
1492 err = -EINVAL;
1493 goto out;
1494 }
1495
1496 /* Write request */
1497 ioc.write_flag |= (1<<31);
1498 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1499 if (err < 0) {
1500 err = -errno;
1501 goto out;
1502 }
1503
1504 /* Result request */
1505 memset(frame_out, 0, sizeof(*frame_out));
1506 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
1507 ioc.write_flag = 1;
1508 ioc.data_ptr = (uintptr_t)frame_out;
1509 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1510 if (err < 0) {
1511 err = -errno;
1512 goto out;
1513 }
1514
1515 /* Get response */
1516 ioc.write_flag = 0;
1517 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1518 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1519 if (err < 0) {
1520 err = -errno;
1521 goto out;
1522 }
1523
1524 break;
1525 case MMC_RPMB_READ_CNT:
1526 if (out_cnt != 1) {
1527 err = -EINVAL;
1528 goto out;
1529 }
1530 /* fall through */
1531
1532 case MMC_RPMB_READ:
1533 /* Request */
1534 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1535 if (err < 0) {
1536 err = -errno;
1537 goto out;
1538 }
1539
1540 /* Get response */
1541 ioc.write_flag = 0;
1542 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1543 ioc.blocks = out_cnt;
1544 ioc.data_ptr = (uintptr_t)frame_out;
1545 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1546 if (err < 0) {
1547 err = -errno;
1548 goto out;
1549 }
1550
1551 break;
1552 default:
1553 err = -EINVAL;
1554 goto out;
1555 }
1556
1557out:
1558 return err;
1559}
1560
1561int do_rpmb_write_key(int nargs, char **argv)
1562{
1563 int ret, dev_fd, key_fd;
1564 struct rpmb_frame frame_in = {
1565 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
1566 }, frame_out;
1567
1568 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
1569 exit(1));
1570
1571 dev_fd = open(argv[1], O_RDWR);
1572 if (dev_fd < 0) {
1573 perror("device open");
1574 exit(1);
1575 }
1576
1577 if (0 == strcmp(argv[2], "-"))
1578 key_fd = STDIN_FILENO;
1579 else {
1580 key_fd = open(argv[2], O_RDONLY);
1581 if (key_fd < 0) {
1582 perror("can't open key file");
1583 exit(1);
1584 }
1585 }
1586
1587 /* Read the auth key */
1588 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
1589 if (ret < 0) {
1590 perror("read the key");
1591 exit(1);
1592 } else if (ret != sizeof(frame_in.key_mac)) {
1593 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1594 (unsigned long)sizeof(frame_in.key_mac),
1595 ret);
1596 exit(1);
1597 }
1598
1599 /* Execute RPMB op */
1600 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1601 if (ret != 0) {
1602 perror("RPMB ioctl failed");
1603 exit(1);
1604 }
1605
1606 /* Check RPMB response */
1607 if (frame_out.result != 0) {
1608 printf("RPMB operation failed, retcode 0x%04x\n",
1609 be16toh(frame_out.result));
1610 exit(1);
1611 }
1612
1613 close(dev_fd);
1614 if (key_fd != STDIN_FILENO)
1615 close(key_fd);
1616
1617 return ret;
1618}
1619
1620int rpmb_read_counter(int dev_fd, unsigned int *cnt)
1621{
1622 int ret;
1623 struct rpmb_frame frame_in = {
1624 .req_resp = htobe16(MMC_RPMB_READ_CNT)
1625 }, frame_out;
1626
1627 /* Execute RPMB op */
1628 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1629 if (ret != 0) {
1630 perror("RPMB ioctl failed");
1631 exit(1);
1632 }
1633
1634 /* Check RPMB response */
1635 if (frame_out.result != 0)
1636 return be16toh(frame_out.result);
1637
1638 *cnt = be32toh(frame_out.write_counter);
1639
1640 return 0;
1641}
1642
1643int do_rpmb_read_counter(int nargs, char **argv)
1644{
1645 int ret, dev_fd;
1646 unsigned int cnt;
1647
1648 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
1649 exit(1));
1650
1651 dev_fd = open(argv[1], O_RDWR);
1652 if (dev_fd < 0) {
1653 perror("device open");
1654 exit(1);
1655 }
1656
1657 ret = rpmb_read_counter(dev_fd, &cnt);
1658
1659 /* Check RPMB response */
1660 if (ret != 0) {
1661 printf("RPMB operation failed, retcode 0x%04x\n", ret);
1662 exit(1);
1663 }
1664
1665 close(dev_fd);
1666
1667 printf("Counter value: 0x%08x\n", cnt);
1668
1669 return ret;
1670}
1671
1672int do_rpmb_read_block(int nargs, char **argv)
1673{
1674 int i, ret, dev_fd, data_fd, key_fd = -1;
1675 uint16_t addr, blocks_cnt;
1676 unsigned char key[32];
1677 struct rpmb_frame frame_in = {
1678 .req_resp = htobe16(MMC_RPMB_READ),
1679 }, *frame_out_p;
1680
1681 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
1682 exit(1));
1683
1684 dev_fd = open(argv[1], O_RDWR);
1685 if (dev_fd < 0) {
1686 perror("device open");
1687 exit(1);
1688 }
1689
1690 /* Get block address */
1691 errno = 0;
1692 addr = strtol(argv[2], NULL, 0);
1693 if (errno) {
1694 perror("incorrect address");
1695 exit(1);
1696 }
1697 frame_in.addr = htobe16(addr);
1698
1699 /* Get blocks count */
1700 errno = 0;
1701 blocks_cnt = strtol(argv[3], NULL, 0);
1702 if (errno) {
1703 perror("incorrect blocks count");
1704 exit(1);
1705 }
1706
1707 if (!blocks_cnt) {
1708 printf("please, specify valid blocks count number\n");
1709 exit(1);
1710 }
1711
1712 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
1713 if (!frame_out_p) {
1714 printf("can't allocate memory for RPMB outer frames\n");
1715 exit(1);
1716 }
1717
1718 /* Write 256b data */
1719 if (0 == strcmp(argv[4], "-"))
1720 data_fd = STDOUT_FILENO;
1721 else {
1722 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
1723 S_IRUSR | S_IWUSR);
1724 if (data_fd < 0) {
1725 perror("can't open output file");
1726 exit(1);
1727 }
1728 }
1729
1730 /* Key is specified */
1731 if (nargs == 6) {
1732 if (0 == strcmp(argv[5], "-"))
1733 key_fd = STDIN_FILENO;
1734 else {
1735 key_fd = open(argv[5], O_RDONLY);
1736 if (key_fd < 0) {
1737 perror("can't open input key file");
1738 exit(1);
1739 }
1740 }
1741
1742 ret = DO_IO(read, key_fd, key, sizeof(key));
1743 if (ret < 0) {
1744 perror("read the key data");
1745 exit(1);
1746 } else if (ret != sizeof(key)) {
1747 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1748 (unsigned long)sizeof(key),
1749 ret);
1750 exit(1);
1751 }
1752 }
1753
1754 /* Execute RPMB op */
1755 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
1756 if (ret != 0) {
1757 perror("RPMB ioctl failed");
1758 exit(1);
1759 }
1760
1761 /* Check RPMB response */
1762 if (frame_out_p[blocks_cnt - 1].result != 0) {
1763 printf("RPMB operation failed, retcode 0x%04x\n",
1764 be16toh(frame_out_p[blocks_cnt - 1].result));
1765 exit(1);
1766 }
1767
1768 /* Do we have to verify data against key? */
1769 if (nargs == 6) {
1770 unsigned char mac[32];
1771 hmac_sha256_ctx ctx;
1772 struct rpmb_frame *frame_out = NULL;
1773
1774 hmac_sha256_init(&ctx, key, sizeof(key));
1775 for (i = 0; i < blocks_cnt; i++) {
1776 frame_out = &frame_out_p[i];
1777 hmac_sha256_update(&ctx, frame_out->data,
1778 sizeof(*frame_out) -
1779 offsetof(struct rpmb_frame, data));
1780 }
1781
1782 hmac_sha256_final(&ctx, mac, sizeof(mac));
1783
1784 /* Impossible */
1785 assert(frame_out);
1786
1787 /* Compare calculated MAC and MAC from last frame */
1788 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
1789 printf("RPMB MAC missmatch\n");
1790 exit(1);
1791 }
1792 }
1793
1794 /* Write data */
1795 for (i = 0; i < blocks_cnt; i++) {
1796 struct rpmb_frame *frame_out = &frame_out_p[i];
1797 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
1798 if (ret < 0) {
1799 perror("write the data");
1800 exit(1);
1801 } else if (ret != sizeof(frame_out->data)) {
1802 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
1803 (unsigned long)sizeof(frame_out->data),
1804 ret);
1805 exit(1);
1806 }
1807 }
1808
1809 free(frame_out_p);
1810 close(dev_fd);
1811 if (data_fd != STDOUT_FILENO)
1812 close(data_fd);
1813 if (key_fd != -1 && key_fd != STDIN_FILENO)
1814 close(key_fd);
1815
1816 return ret;
1817}
1818
1819int do_rpmb_write_block(int nargs, char **argv)
1820{
1821 int ret, dev_fd, key_fd, data_fd;
1822 unsigned char key[32];
1823 uint16_t addr;
1824 unsigned int cnt;
1825 struct rpmb_frame frame_in = {
1826 .req_resp = htobe16(MMC_RPMB_WRITE),
1827 .block_count = htobe16(1)
1828 }, frame_out;
1829
1830 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
1831 exit(1));
1832
1833 dev_fd = open(argv[1], O_RDWR);
1834 if (dev_fd < 0) {
1835 perror("device open");
1836 exit(1);
1837 }
1838
1839 ret = rpmb_read_counter(dev_fd, &cnt);
1840 /* Check RPMB response */
1841 if (ret != 0) {
1842 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
1843 exit(1);
1844 }
1845 frame_in.write_counter = htobe32(cnt);
1846
1847 /* Get block address */
1848 errno = 0;
1849 addr = strtol(argv[2], NULL, 0);
1850 if (errno) {
1851 perror("incorrect address");
1852 exit(1);
1853 }
1854 frame_in.addr = htobe16(addr);
1855
1856 /* Read 256b data */
1857 if (0 == strcmp(argv[3], "-"))
1858 data_fd = STDIN_FILENO;
1859 else {
1860 data_fd = open(argv[3], O_RDONLY);
1861 if (data_fd < 0) {
1862 perror("can't open input file");
1863 exit(1);
1864 }
1865 }
1866
1867 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
1868 if (ret < 0) {
1869 perror("read the data");
1870 exit(1);
1871 } else if (ret != sizeof(frame_in.data)) {
1872 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1873 (unsigned long)sizeof(frame_in.data),
1874 ret);
1875 exit(1);
1876 }
1877
1878 /* Read the auth key */
1879 if (0 == strcmp(argv[4], "-"))
1880 key_fd = STDIN_FILENO;
1881 else {
1882 key_fd = open(argv[4], O_RDONLY);
1883 if (key_fd < 0) {
1884 perror("can't open key file");
1885 exit(1);
1886 }
1887 }
1888
1889 ret = DO_IO(read, key_fd, key, sizeof(key));
1890 if (ret < 0) {
1891 perror("read the key");
1892 exit(1);
1893 } else if (ret != sizeof(key)) {
1894 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1895 (unsigned long)sizeof(key),
1896 ret);
1897 exit(1);
1898 }
1899
1900 /* Calculate HMAC SHA256 */
1901 hmac_sha256(
1902 key, sizeof(key),
1903 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
1904 frame_in.key_mac, sizeof(frame_in.key_mac));
1905
1906 /* Execute RPMB op */
1907 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1908 if (ret != 0) {
1909 perror("RPMB ioctl failed");
1910 exit(1);
1911 }
1912
1913 /* Check RPMB response */
1914 if (frame_out.result != 0) {
1915 printf("RPMB operation failed, retcode 0x%04x\n",
1916 be16toh(frame_out.result));
1917 exit(1);
1918 }
1919
1920 close(dev_fd);
1921 if (data_fd != STDIN_FILENO)
1922 close(data_fd);
1923 if (key_fd != STDIN_FILENO)
1924 close(key_fd);
1925
1926 return ret;
1927}