blob: 2471eb055ecc508618f1628631e9f83f461d5867 [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
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/ioctl.h>
21#include <sys/types.h>
22#include <dirent.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <libgen.h>
27#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050028#include <ctype.h>
Roman Peniaevc6cb0532014-08-12 23:25:45 +090029#include <errno.h>
30#include <stdint.h>
31#include <assert.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050032
33#include "mmc.h"
34#include "mmc_cmds.h"
Roman Peniaevc6cb0532014-08-12 23:25:45 +090035#include "3rdparty/hmac_sha/hmac_sha2.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050036
37int read_extcsd(int fd, __u8 *ext_csd)
38{
39 int ret = 0;
40 struct mmc_ioc_cmd idata;
41 memset(&idata, 0, sizeof(idata));
42 memset(ext_csd, 0, sizeof(__u8) * 512);
43 idata.write_flag = 0;
44 idata.opcode = MMC_SEND_EXT_CSD;
45 idata.arg = 0;
46 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
47 idata.blksz = 512;
48 idata.blocks = 1;
49 mmc_ioc_cmd_set_data(idata, ext_csd);
50
51 ret = ioctl(fd, MMC_IOC_CMD, &idata);
52 if (ret)
53 perror("ioctl");
54
55 return ret;
56}
57
58int write_extcsd_value(int fd, __u8 index, __u8 value)
59{
60 int ret = 0;
61 struct mmc_ioc_cmd idata;
62
63 memset(&idata, 0, sizeof(idata));
64 idata.write_flag = 1;
65 idata.opcode = MMC_SWITCH;
66 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
67 (index << 16) |
68 (value << 8) |
69 EXT_CSD_CMD_SET_NORMAL;
70 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
71
72 ret = ioctl(fd, MMC_IOC_CMD, &idata);
73 if (ret)
74 perror("ioctl");
75
76 return ret;
77}
78
Ben Gardiner27c357d2013-05-30 17:12:47 -040079int send_status(int fd, __u32 *response)
80{
81 int ret = 0;
82 struct mmc_ioc_cmd idata;
83
84 memset(&idata, 0, sizeof(idata));
85 idata.opcode = MMC_SEND_STATUS;
86 idata.arg = (1 << 16);
87 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
88
89 ret = ioctl(fd, MMC_IOC_CMD, &idata);
90 if (ret)
91 perror("ioctl");
92
93 *response = idata.response[0];
94
95 return ret;
96}
97
Chris Ballb9c7a172012-02-20 12:34:25 -050098void print_writeprotect_status(__u8 *ext_csd)
99{
100 __u8 reg;
101 __u8 ext_csd_rev = ext_csd[192];
102
103 /* A43: reserved [174:0] */
104 if (ext_csd_rev >= 5) {
105 printf("Boot write protection status registers"
106 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
107
108 reg = ext_csd[EXT_CSD_BOOT_WP];
109 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
110 printf(" Power ro locking: ");
111 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
112 printf("not possible\n");
113 else
114 printf("possible\n");
115
116 printf(" Permanent ro locking: ");
117 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
118 printf("not possible\n");
119 else
120 printf("possible\n");
121
122 printf(" ro lock status: ");
123 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
124 printf("locked until next power on\n");
125 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
126 printf("locked permanently\n");
127 else
128 printf("not locked\n");
129 }
130}
131
132int do_writeprotect_get(int nargs, char **argv)
133{
134 __u8 ext_csd[512];
135 int fd, ret;
136 char *device;
137
Chris Ball8ba44662012-04-19 13:22:54 -0400138 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
139 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500140
141 device = argv[1];
142
143 fd = open(device, O_RDWR);
144 if (fd < 0) {
145 perror("open");
146 exit(1);
147 }
148
149 ret = read_extcsd(fd, ext_csd);
150 if (ret) {
151 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
152 exit(1);
153 }
154
155 print_writeprotect_status(ext_csd);
156
157 return ret;
158}
159
160int do_writeprotect_set(int nargs, char **argv)
161{
162 __u8 ext_csd[512], value;
163 int fd, ret;
164 char *device;
165
Chris Ball8ba44662012-04-19 13:22:54 -0400166 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
167 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500168
169 device = argv[1];
170
171 fd = open(device, O_RDWR);
172 if (fd < 0) {
173 perror("open");
174 exit(1);
175 }
176
177 ret = read_extcsd(fd, ext_csd);
178 if (ret) {
179 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
180 exit(1);
181 }
182
183 value = ext_csd[EXT_CSD_BOOT_WP] |
184 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
185 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
186 if (ret) {
187 fprintf(stderr, "Could not write 0x%02x to "
188 "EXT_CSD[%d] in %s\n",
189 value, EXT_CSD_BOOT_WP, device);
190 exit(1);
191 }
192
193 return ret;
194}
195
Saugata Dasb7e25992012-05-17 09:26:34 -0400196int do_disable_512B_emulation(int nargs, char **argv)
197{
198 __u8 ext_csd[512], native_sector_size, data_sector_size, wr_rel_param;
199 int fd, ret;
200 char *device;
201
202 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
203 device = argv[1];
204
205 fd = open(device, O_RDWR);
206 if (fd < 0) {
207 perror("open");
208 exit(1);
209 }
210
211 ret = read_extcsd(fd, ext_csd);
212 if (ret) {
213 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
214 exit(1);
215 }
216
217 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
218 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
219 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
220
221 if (native_sector_size && !data_sector_size &&
222 (wr_rel_param & EN_REL_WR)) {
223 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
224
225 if (ret) {
226 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
227 1, EXT_CSD_BOOT_WP, device);
228 exit(1);
229 }
230 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
231 } else if (native_sector_size && data_sector_size) {
232 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
233 } else {
234 printf("MMC does not support disabling 512B emulation mode.\n");
235 }
236
237 return ret;
238}
239
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200240int do_write_boot_en(int nargs, char **argv)
241{
242 __u8 ext_csd[512];
243 __u8 value = 0;
244 int fd, ret;
245 char *device;
246 int boot_area, send_ack;
247
248 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
249 "<send_ack> </path/to/mmcblkX>\n", exit(1));
250
251 /*
252 * If <send_ack> is 1, the device will send acknowledgment
253 * pattern "010" to the host when boot operation begins.
254 * If <send_ack> is 0, it won't.
255 */
256 boot_area = strtol(argv[1], NULL, 10);
257 send_ack = strtol(argv[2], NULL, 10);
258 device = argv[3];
259
260 fd = open(device, O_RDWR);
261 if (fd < 0) {
262 perror("open");
263 exit(1);
264 }
265
266 ret = read_extcsd(fd, ext_csd);
267 if (ret) {
268 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
269 exit(1);
270 }
271
272 value = ext_csd[EXT_CSD_PART_CONFIG];
273
274 switch (boot_area) {
275 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
276 value |= (1 << 3);
277 value &= ~(3 << 4);
278 break;
279 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
280 value |= (1 << 4);
281 value &= ~(1 << 3);
282 value &= ~(1 << 5);
283 break;
284 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
285 value |= (boot_area << 3);
286 break;
287 default:
288 fprintf(stderr, "Cannot enable the boot area\n");
289 exit(1);
290 }
291 if (send_ack)
292 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
293 else
294 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
295
296 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
297 if (ret) {
298 fprintf(stderr, "Could not write 0x%02x to "
299 "EXT_CSD[%d] in %s\n",
300 value, EXT_CSD_PART_CONFIG, device);
301 exit(1);
302 }
303 return ret;
304}
305
Chris Ballf74dfe22012-10-19 16:49:55 -0400306int do_hwreset(int value, int nargs, char **argv)
307{
308 __u8 ext_csd[512];
309 int fd, ret;
310 char *device;
311
312 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
313 exit(1));
314
315 device = argv[1];
316
317 fd = open(device, O_RDWR);
318 if (fd < 0) {
319 perror("open");
320 exit(1);
321 }
322
323 ret = read_extcsd(fd, ext_csd);
324 if (ret) {
325 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
326 exit(1);
327 }
328
329 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
330 EXT_CSD_HW_RESET_EN) {
331 fprintf(stderr,
332 "H/W Reset is already permanently enabled on %s\n",
333 device);
334 exit(1);
335 }
336 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
337 EXT_CSD_HW_RESET_DIS) {
338 fprintf(stderr,
339 "H/W Reset is already permanently disabled on %s\n",
340 device);
341 exit(1);
342 }
343
344 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
345 if (ret) {
346 fprintf(stderr,
347 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
348 value, EXT_CSD_RST_N_FUNCTION, device);
349 exit(1);
350 }
351
352 return ret;
353}
354
355int do_hwreset_en(int nargs, char **argv)
356{
357 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
358}
359
360int do_hwreset_dis(int nargs, char **argv)
361{
362 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
363}
364
Jaehoon Chung86496512012-09-21 10:08:05 +0000365int do_write_bkops_en(int nargs, char **argv)
366{
367 __u8 ext_csd[512], value = 0;
368 int fd, ret;
369 char *device;
370
371 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
372 exit(1));
373
374 device = argv[1];
375
376 fd = open(device, O_RDWR);
377 if (fd < 0) {
378 perror("open");
379 exit(1);
380 }
381
382 ret = read_extcsd(fd, ext_csd);
383 if (ret) {
384 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
385 exit(1);
386 }
387
388 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
389 fprintf(stderr, "%s doesn't support BKOPS\n", device);
390 exit(1);
391 }
392
393 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
394 if (ret) {
395 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
396 value, EXT_CSD_BKOPS_EN, device);
397 exit(1);
398 }
399
400 return ret;
401}
402
Ben Gardiner27c357d2013-05-30 17:12:47 -0400403int do_status_get(int nargs, char **argv)
404{
405 __u32 response;
406 int fd, ret;
407 char *device;
408
409 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
410 exit(1));
411
412 device = argv[1];
413
414 fd = open(device, O_RDWR);
415 if (fd < 0) {
416 perror("open");
417 exit(1);
418 }
419
420 ret = send_status(fd, &response);
421 if (ret) {
422 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
423 exit(1);
424 }
425
426 printf("SEND_STATUS response: 0x%08x\n", response);
427
428 return ret;
429}
430
Ben Gardiner4e850232013-05-30 17:12:49 -0400431unsigned int get_sector_count(__u8 *ext_csd)
432{
433 return (ext_csd[EXT_CSD_SEC_COUNT_3] << 24) |
434 (ext_csd[EXT_CSD_SEC_COUNT_2] << 16) |
435 (ext_csd[EXT_CSD_SEC_COUNT_1] << 8) |
436 ext_csd[EXT_CSD_SEC_COUNT_0];
437}
438
439int is_blockaddresed(__u8 *ext_csd)
440{
441 unsigned int sectors = get_sector_count(ext_csd);
442
443 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
444}
445
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400446unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
447{
448 return ext_csd[221];
449}
450
451unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
452{
453 return ext_csd[224];
454}
455
Ben Gardinere6e84e92013-09-19 11:14:27 -0400456int set_partitioning_setting_completed(int dry_run, const char * const device,
457 int fd)
458{
459 int ret;
460
461 if (dry_run) {
462 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
463 fprintf(stderr, "These changes will not take effect neither "
464 "now nor after a power cycle\n");
465 return 1;
466 }
467
468 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
469 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
470 if (ret) {
471 fprintf(stderr, "Could not write 0x1 to "
472 "EXT_CSD[%d] in %s\n",
473 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
474 return 1;
475 }
476
477 __u32 response;
478 ret = send_status(fd, &response);
479 if (ret) {
480 fprintf(stderr, "Could not get response to SEND_STATUS "
481 "from %s\n", device);
482 return 1;
483 }
484
485 if (response & R1_SWITCH_ERROR) {
486 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
487 "failed on %s\n", device);
488 return 1;
489 }
490
491 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
492 "%s SUCCESS\n", device);
493 fprintf(stderr, "Device power cycle needed for settings to "
494 "take effect.\n"
495 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
496 "using 'extcsd read' after power cycle\n");
497
498 return 0;
499}
500
Balaji T K4afc8c82015-04-29 18:12:32 -0400501int check_enhanced_area_total_limit(const char * const device, int fd)
502{
503 __u8 ext_csd[512];
504 __u32 regl;
505 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
506 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T K78912362015-04-29 18:12:33 -0400507 unsigned long total_sz, total_gp_user_sz;
Balaji T K4afc8c82015-04-29 18:12:32 -0400508 unsigned int wp_sz, erase_sz;
509 int ret;
510
511 ret = read_extcsd(fd, ext_csd);
512 if (ret) {
513 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
514 exit(1);
515 }
516 wp_sz = get_hc_wp_grp_size(ext_csd);
517 erase_sz = get_hc_erase_grp_size(ext_csd);
518
519 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
520 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
521 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
522 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
523 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
524 enh_area_sz += gp4_part_sz;
525 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
526 printf(" i.e. %lu KiB\n", gp4_part_sz);
527 }
528
529 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
530 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
531 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
532 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
533 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
534 enh_area_sz += gp3_part_sz;
535 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
536 printf(" i.e. %lu KiB\n", gp3_part_sz);
537 }
538
539 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
540 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
541 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
542 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
543 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
544 enh_area_sz += gp2_part_sz;
545 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
546 printf(" i.e. %lu KiB\n", gp2_part_sz);
547 }
548
549 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
550 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
551 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
552 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
553 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
554 enh_area_sz += gp1_part_sz;
555 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
556 printf(" i.e. %lu KiB\n", gp1_part_sz);
557 }
558
559 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
560 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
561 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
562 user_area_sz = 512l * regl * erase_sz * wp_sz;
563 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
564 enh_area_sz += user_area_sz;
565 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
566 printf(" i.e. %lu KiB\n", user_area_sz);
567 }
568
569 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
570 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
571 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
572 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
573 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
574 printf(" i.e. %lu KiB\n", max_enh_area_sz);
575 if (enh_area_sz > max_enh_area_sz) {
576 fprintf(stderr,
577 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
578 enh_area_sz, max_enh_area_sz, device);
579 return 1;
580 }
Balaji T K78912362015-04-29 18:12:33 -0400581 total_sz = get_sector_count(ext_csd) / 2;
582 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
583 gp1_part_sz + user_area_sz;
584 if (total_gp_user_sz > total_sz) {
585 fprintf(stderr,
586 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
587 total_gp_user_sz, total_sz, device);
588 return 1;
589 }
590
591 return 0;
592}
593
594int do_create_gp_partition(int nargs, char **argv)
595{
596 __u8 value;
597 __u8 ext_csd[512];
598 __u8 address;
599 int fd, ret;
600 char *device;
601 int dry_run = 1;
602 int partition, enh_attr, ext_attr;
603 unsigned int length_kib, gp_size_mult;
604 unsigned long align;
605
606 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n> <length KiB> "
607 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
608
609 if (!strcmp("-y", argv[1]))
610 dry_run = 0;
611
612 length_kib = strtol(argv[2], NULL, 10);
613 partition = strtol(argv[3], NULL, 10);
614 enh_attr = strtol(argv[4], NULL, 10);
615 ext_attr = strtol(argv[5], NULL, 10);
616 device = argv[6];
617
618 if (partition < 0 || partition > 4) {
619 printf("Invalid gp parition number valid range [1-4]\n");
620 exit(1);
621 }
622
623 if (enh_attr && ext_attr) {
624 printf("Not allowed to set both enhanced attribute and extended attribute\n");
625 exit(1);
626 }
627
628 fd = open(device, O_RDWR);
629 if (fd < 0) {
630 perror("open");
631 exit(1);
632 }
633
634 ret = read_extcsd(fd, ext_csd);
635 if (ret) {
636 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
637 exit(1);
638 }
639
640 /* assert not PARTITION_SETTING_COMPLETED */
641 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
642 printf(" Device is already partitioned\n");
643 exit(1);
644 }
645
646 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
647 gp_size_mult = (length_kib + align/2l) / align;
648
649 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
650 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
651 if (ret) {
652 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
653 EXT_CSD_ERASE_GROUP_DEF, device);
654 exit(1);
655 }
656
657 value = (gp_size_mult >> 16) & 0xff;
658 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
659 ret = write_extcsd_value(fd, address, value);
660 if (ret) {
661 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
662 value, address, device);
663 exit(1);
664 }
665 value = (gp_size_mult >> 8) & 0xff;
666 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
667 ret = write_extcsd_value(fd, address, value);
668 if (ret) {
669 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
670 value, address, device);
671 exit(1);
672 }
673 value = gp_size_mult & 0xff;
674 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
675 ret = write_extcsd_value(fd, address, value);
676 if (ret) {
677 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
678 value, address, device);
679 exit(1);
680 }
681
682 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
683 if (enh_attr)
684 value |= (1 << partition);
685 else
686 value &= ~(1 << partition);
687
688 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
689 if (ret) {
690 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
691 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
692 exit(1);
693 }
694
695 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
696 value = ext_csd[address];
697 if (ext_attr)
698 value |= (ext_attr << (4 * ((partition - 1) % 2)));
699 else
700 value &= (0xF << (4 * ((partition % 2))));
701
702 ret = write_extcsd_value(fd, address, value);
703 if (ret) {
704 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
705 value, address, device);
706 exit(1);
707 }
708
709 ret = check_enhanced_area_total_limit(device, fd);
710 if (ret)
711 exit(1);
712
713 if (!set_partitioning_setting_completed(dry_run, device, fd))
714 exit(1);
Balaji T K4afc8c82015-04-29 18:12:32 -0400715
716 return 0;
717}
718
Ben Gardinerd91d3692013-05-30 17:12:51 -0400719int do_enh_area_set(int nargs, char **argv)
720{
721 __u8 value;
722 __u8 ext_csd[512];
723 int fd, ret;
724 char *device;
725 int dry_run = 1;
726 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
727 unsigned long align;
728
729 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
730 "</path/to/mmcblkX>\n", exit(1));
731
732 if (!strcmp("-y", argv[1]))
733 dry_run = 0;
734
735 start_kib = strtol(argv[2], NULL, 10);
736 length_kib = strtol(argv[3], NULL, 10);
737 device = argv[4];
738
739 fd = open(device, O_RDWR);
740 if (fd < 0) {
741 perror("open");
742 exit(1);
743 }
744
745 ret = read_extcsd(fd, ext_csd);
746 if (ret) {
747 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
748 exit(1);
749 }
750
751 /* assert ENH_ATTRIBUTE_EN */
752 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
753 {
754 printf(" Device cannot have enhanced tech.\n");
755 exit(1);
756 }
757
758 /* assert not PARTITION_SETTING_COMPLETED */
759 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
760 {
761 printf(" Device is already partitioned\n");
762 exit(1);
763 }
764
765 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
766
767 enh_size_mult = (length_kib + align/2l) / align;
768
769 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
770 enh_start_addr /= align;
771 enh_start_addr *= align;
772
773 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
774 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
775 if (ret) {
776 fprintf(stderr, "Could not write 0x1 to "
777 "EXT_CSD[%d] in %s\n",
778 EXT_CSD_ERASE_GROUP_DEF, device);
779 exit(1);
780 }
781
782 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
783 value = (enh_start_addr >> 24) & 0xff;
784 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
785 if (ret) {
786 fprintf(stderr, "Could not write 0x%02x to "
787 "EXT_CSD[%d] in %s\n", value,
788 EXT_CSD_ENH_START_ADDR_3, device);
789 exit(1);
790 }
791 value = (enh_start_addr >> 16) & 0xff;
792 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
793 if (ret) {
794 fprintf(stderr, "Could not write 0x%02x to "
795 "EXT_CSD[%d] in %s\n", value,
796 EXT_CSD_ENH_START_ADDR_2, device);
797 exit(1);
798 }
799 value = (enh_start_addr >> 8) & 0xff;
800 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
801 if (ret) {
802 fprintf(stderr, "Could not write 0x%02x to "
803 "EXT_CSD[%d] in %s\n", value,
804 EXT_CSD_ENH_START_ADDR_1, device);
805 exit(1);
806 }
807 value = enh_start_addr & 0xff;
808 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
809 if (ret) {
810 fprintf(stderr, "Could not write 0x%02x to "
811 "EXT_CSD[%d] in %s\n", value,
812 EXT_CSD_ENH_START_ADDR_0, device);
813 exit(1);
814 }
815
816 value = (enh_size_mult >> 16) & 0xff;
817 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
818 if (ret) {
819 fprintf(stderr, "Could not write 0x%02x to "
820 "EXT_CSD[%d] in %s\n", value,
821 EXT_CSD_ENH_SIZE_MULT_2, device);
822 exit(1);
823 }
824 value = (enh_size_mult >> 8) & 0xff;
825 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
826 if (ret) {
827 fprintf(stderr, "Could not write 0x%02x to "
828 "EXT_CSD[%d] in %s\n", value,
829 EXT_CSD_ENH_SIZE_MULT_1, device);
830 exit(1);
831 }
832 value = enh_size_mult & 0xff;
833 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
834 if (ret) {
835 fprintf(stderr, "Could not write 0x%02x to "
836 "EXT_CSD[%d] in %s\n", value,
837 EXT_CSD_ENH_SIZE_MULT_0, device);
838 exit(1);
839 }
Balaji T K4afc8c82015-04-29 18:12:32 -0400840 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
841 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400842 if (ret) {
843 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
844 "EXT_CSD[%d] in %s\n",
845 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
846 exit(1);
847 }
848
Balaji T K4afc8c82015-04-29 18:12:32 -0400849 ret = check_enhanced_area_total_limit(device, fd);
850 if (ret)
851 exit(1);
852
Ben Gardinere6e84e92013-09-19 11:14:27 -0400853 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400854
Ben Gardinere6e84e92013-09-19 11:14:27 -0400855 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400856 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400857
858 return 0;
859}
860
Ben Gardiner196d0d22013-09-19 11:14:29 -0400861int do_write_reliability_set(int nargs, char **argv)
862{
863 __u8 value;
864 __u8 ext_csd[512];
865 int fd, ret;
866
867 int dry_run = 1;
868 int partition;
869 char *device;
870
871 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
872 "<partition> </path/to/mmcblkX>\n", exit(1));
873
874 if (!strcmp("-y", argv[1]))
875 dry_run = 0;
876
877 partition = strtol(argv[2], NULL, 10);
878 device = argv[3];
879
880 fd = open(device, O_RDWR);
881 if (fd < 0) {
882 perror("open");
883 exit(1);
884 }
885
886 ret = read_extcsd(fd, ext_csd);
887 if (ret) {
888 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
889 exit(1);
890 }
891
892 /* assert not PARTITION_SETTING_COMPLETED */
893 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
894 {
895 printf(" Device is already partitioned\n");
896 exit(1);
897 }
898
899 /* assert HS_CTRL_REL */
900 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
901 printf("Cannot set write reliability parameters, WR_REL_SET is "
902 "read-only\n");
903 exit(1);
904 }
905
906 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
907 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
908 if (ret) {
909 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
910 value, EXT_CSD_WR_REL_SET, device);
911 exit(1);
912 }
913
914 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
915 value, device);
916
917 if (!set_partitioning_setting_completed(dry_run, device, fd))
918 exit(1);
919
920 return 0;
921}
922
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500923int do_read_extcsd(int nargs, char **argv)
924{
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100925 __u8 ext_csd[512], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +0200926 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500927 int fd, ret;
928 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100929 const char *str;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500930
Chris Ball8ba44662012-04-19 13:22:54 -0400931 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
932 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500933
934 device = argv[1];
935
936 fd = open(device, O_RDWR);
937 if (fd < 0) {
938 perror("open");
939 exit(1);
940 }
941
942 ret = read_extcsd(fd, ext_csd);
943 if (ret) {
944 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
945 exit(1);
946 }
947
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100948 ext_csd_rev = ext_csd[192];
949
950 switch (ext_csd_rev) {
951 case 6:
952 str = "4.5";
953 break;
954 case 5:
955 str = "4.41";
956 break;
957 case 3:
958 str = "4.3";
959 break;
960 case 2:
961 str = "4.2";
962 break;
963 case 1:
964 str = "4.1";
965 break;
966 case 0:
967 str = "4.0";
968 break;
969 default:
970 goto out_free;
971 }
972 printf("=============================================\n");
973 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
974 printf("=============================================\n\n");
975
976 if (ext_csd_rev < 3)
977 goto out_free; /* No ext_csd */
978
979 /* Parse the Extended CSD registers.
980 * Reserved bit should be read as "0" in case of spec older
981 * than A441.
982 */
983 reg = ext_csd[EXT_CSD_S_CMD_SET];
984 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
985 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -0500986 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100987
988 reg = ext_csd[EXT_CSD_HPI_FEATURE];
989 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
990 if (reg & EXT_CSD_HPI_SUPP) {
991 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -0500992 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100993 else
994 printf("implementation based on CMD13\n");
995 }
996
997 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
998 ext_csd[502]);
999
1000 if (ext_csd_rev >= 6) {
1001 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1002 ext_csd[501]);
1003 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1004 ext_csd[500]);
1005 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1006 ext_csd[499]);
1007
1008 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1009 ext_csd[498]);
1010 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1011 ext_csd[497]);
1012 printf("Context Management Capabilities"
1013 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1014 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1015 ext_csd[495]);
1016 printf("Extended partition attribute support"
1017 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
1018 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1019 ext_csd[248]);
1020 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1021 ext_csd[247]);
1022 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
1023 ext_csd[249] << 0 | (ext_csd[250] << 8) |
1024 (ext_csd[251] << 16) | (ext_csd[252] << 24));
1025 }
1026
1027 /* A441: Reserved [501:247]
1028 A43: reserved [246:229] */
1029 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001030 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001031 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001032
1033 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1034
1035 printf("1st Initialisation Time after programmed sector"
1036 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1037
1038 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001039 printf("Power class for 52MHz, DDR at 3.6V"
1040 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1041 printf("Power class for 52MHz, DDR at 1.95V"
1042 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1043
1044 /* A441: reserved [237-236] */
1045
1046 if (ext_csd_rev >= 6) {
1047 printf("Power class for 200MHz at 3.6V"
1048 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1049 printf("Power class for 200MHz, at 1.95V"
1050 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1051 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001052 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001053 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1054 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1055 /* A441: reserved [233] */
1056 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1057 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1058 ext_csd[231]);
1059 }
1060 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1061 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1062 ext_csd[230]);
1063 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1064 ext_csd[229]);
1065 }
1066 reg = ext_csd[EXT_CSD_BOOT_INFO];
1067 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1068 if (reg & EXT_CSD_BOOT_INFO_ALT)
1069 printf(" Device supports alternative boot method\n");
1070 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1071 printf(" Device supports dual data rate during boot\n");
1072 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1073 printf(" Device supports high speed timing during boot\n");
1074
1075 /* A441/A43: reserved [227] */
1076 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1077 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001078
1079 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001080 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001081 reg);
1082 printf(" i.e. %u KiB\n", 512 * reg);
1083
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001084 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1085 ext_csd[223]);
1086 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1087 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001088
1089 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001090 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001091 reg);
1092 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1093
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001094 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1095 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1096 /* A441/A43: reserved [218] */
1097 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1098 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001099
1100 unsigned int sectors = get_sector_count(ext_csd);
1101 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1102 if (is_blockaddresed(ext_csd))
1103 printf(" Device is block-addressed\n");
1104 else
1105 printf(" Device is NOT block-addressed\n");
1106
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001107 /* A441/A43: reserved [211] */
1108 printf("Minimum Write Performance for 8bit:\n");
1109 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1110 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1111 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1112 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1113 printf("Minimum Write Performance for 4bit:\n");
1114 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1115 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1116 /* A441/A43: reserved [204] */
1117 printf("Power classes registers:\n");
1118 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1119 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1120 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1121 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1122
1123 /* A43: reserved [199:198] */
1124 if (ext_csd_rev >= 5) {
1125 printf("Partition switching timing "
1126 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1127 printf("Out-of-interrupt busy timing"
1128 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1129 }
1130
1131 /* A441/A43: reserved [197] [195] [193] [190] [188]
1132 * [186] [184] [182] [180] [176] */
1133
1134 if (ext_csd_rev >= 6)
1135 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1136 ext_csd[197]);
1137
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001138 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
1139 reg = ext_csd[196];
1140 printf("Card Type [CARD_TYPE: 0x%02x]\n", reg);
1141 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1142 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1143 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1144 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1145 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1146 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001147
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001148 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1149 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1150 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1151 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1152 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1153 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1154 ext_csd[185]);
1155 /* bus_width: ext_csd[183] not readable */
1156 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1157 ext_csd[181]);
1158 reg = ext_csd[EXT_CSD_BOOT_CFG];
1159 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001160 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001161 case 0x0:
1162 printf(" Not boot enable\n");
1163 break;
1164 case 0x1:
1165 printf(" Boot Partition 1 enabled\n");
1166 break;
1167 case 0x2:
1168 printf(" Boot Partition 2 enabled\n");
1169 break;
1170 case 0x7:
1171 printf(" User Area Enabled for boot\n");
1172 break;
1173 }
1174 switch (reg & EXT_CSD_BOOT_CFG_ACC) {
1175 case 0x0:
1176 printf(" No access to boot partition\n");
1177 break;
1178 case 0x1:
1179 printf(" R/W Boot Partition 1\n");
1180 break;
1181 case 0x2:
1182 printf(" R/W Boot Partition 2\n");
1183 break;
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001184 case 0x3:
1185 printf(" R/W Replay Protected Memory Block (RPMB)\n");
1186 break;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001187 default:
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001188 printf(" Access to General Purpose partition %d\n",
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001189 (reg & EXT_CSD_BOOT_CFG_ACC) - 3);
1190 break;
1191 }
1192
1193 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1194 ext_csd[178]);
1195 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1196 ext_csd[177]);
1197 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001198 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001199
Chris Ballb9c7a172012-02-20 12:34:25 -05001200 print_writeprotect_status(ext_csd);
1201
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001202 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001203 /* A441]: reserved [172] */
1204 printf("User area write protection register"
1205 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1206 /* A441]: reserved [170] */
1207 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1208 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001209
1210 reg = ext_csd[EXT_CSD_WR_REL_SET];
1211 const char * const fast = "existing data is at risk if a power "
1212 "failure occurs during a write operation";
1213 const char * const reliable = "the device protects existing "
1214 "data if a power failure occurs during a write "
1215 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001216 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001217 " [WR_REL_SET]: 0x%02x\n", reg);
1218
1219 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1220 int i;
1221 for (i = 1; i <= 4; i++) {
1222 printf(" partition %d: %s\n", i,
1223 reg & (1<<i) ? reliable : fast);
1224 }
1225
1226 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001227 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001228 " [WR_REL_PARAM]: 0x%02x\n", reg);
1229 if (reg & 0x01)
1230 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1231 if (reg & 0x04)
1232 printf(" Device supports the enhanced def. of reliable "
1233 "write\n");
1234
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001235 /* sanitize_start ext_csd[165]]: not readable
1236 * bkops_start ext_csd[164]]: only writable */
1237 printf("Enable background operations handshake"
1238 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1239 printf("H/W reset function"
1240 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1241 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001242 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001243 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1244 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001245 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001246 printf(" Device support partitioning feature\n");
1247 else
1248 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001249 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001250 printf(" Device can have enhanced tech.\n");
1251 else
1252 printf(" Device cannot have enhanced tech.\n");
1253
Oliver Metz11f2cea2013-09-23 08:40:52 +02001254 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001255 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1256 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1257
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001258 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001259 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001260 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1261 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001262 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001263
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001264 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001265 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001266 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001267 printf("Partitioning Setting"
1268 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001269 reg);
1270 if (reg)
1271 printf(" Device partition setting complete\n");
1272 else
1273 printf(" Device partition setting NOT complete\n");
1274
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001275 printf("General Purpose Partition Size\n"
1276 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1277 (ext_csd[153] << 8) | ext_csd[152]);
1278 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1279 (ext_csd[150] << 8) | ext_csd[149]);
1280 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1281 (ext_csd[147] << 8) | ext_csd[146]);
1282 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1283 (ext_csd[144] << 8) | ext_csd[143]);
1284
Oliver Metz11f2cea2013-09-23 08:40:52 +02001285 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001286 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1287 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001288 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001289 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1290 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001291 get_hc_erase_grp_size(ext_csd) *
1292 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001293
Oliver Metz11f2cea2013-09-23 08:40:52 +02001294 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001295 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1296 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1297 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001298 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001299 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001300 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001301 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001302
1303 /* A441]: reserved [135] */
1304 printf("Bad Block Management mode"
1305 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1306 /* A441: reserved [133:0] */
1307 }
1308 /* B45 */
1309 if (ext_csd_rev >= 6) {
1310 int j;
1311 /* tcase_support ext_csd[132] not readable */
1312 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1313 ext_csd[131]);
1314 printf("Program CID/CSD in DDR mode support"
1315 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1316 ext_csd[130]);
1317
1318 for (j = 127; j >= 64; j--)
1319 printf("Vendor Specific Fields"
1320 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1321 j, ext_csd[j]);
1322
1323 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
1324 ext_csd[63]);
1325 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1326 ext_csd[62]);
1327 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", ext_csd[61]);
1328 printf("1st initialization after disabling sector"
1329 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1330 ext_csd[60]);
1331 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1332 ext_csd[59]);
1333 printf("Number of addressed group to be Released"
1334 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1335 printf("Exception events control"
1336 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1337 (ext_csd[57] << 8) | ext_csd[56]);
1338 printf("Exception events status"
1339 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1340 (ext_csd[55] << 8) | ext_csd[54]);
1341 printf("Extended Partitions Attribute"
1342 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1343 (ext_csd[53] << 8) | ext_csd[52]);
1344
1345 for (j = 51; j >= 37; j--)
1346 printf("Context configuration"
1347 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1348
1349 printf("Packed command status"
1350 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1351 printf("Packed command failure index"
1352 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1353 printf("Power Off Notification"
1354 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001355 printf("Control to turn the Cache ON/OFF"
1356 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001357 /* flush_cache ext_csd[32] not readable */
1358 /*Reserved [31:0] */
1359 }
1360
1361out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001362 return ret;
1363}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001364
1365int do_sanitize(int nargs, char **argv)
1366{
1367 int fd, ret;
1368 char *device;
1369
1370 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1371 exit(1));
1372
1373 device = argv[1];
1374
1375 fd = open(device, O_RDWR);
1376 if (fd < 0) {
1377 perror("open");
1378 exit(1);
1379 }
1380
1381 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1382 if (ret) {
1383 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1384 1, EXT_CSD_SANITIZE_START, device);
1385 exit(1);
1386 }
1387
1388 return ret;
1389
1390}
1391
Roman Peniaevc6cb0532014-08-12 23:25:45 +09001392#define DO_IO(func, fd, buf, nbyte) \
1393 ({ \
1394 ssize_t ret = 0, r; \
1395 do { \
1396 r = func(fd, buf + ret, nbyte - ret); \
1397 if (r < 0 && errno != EINTR) { \
1398 ret = -1; \
1399 break; \
1400 } \
1401 else if (r > 0) \
1402 ret += r; \
1403 } while (r != 0 && (size_t)ret != nbyte); \
1404 \
1405 ret; \
1406 })
1407
1408enum rpmb_op_type {
1409 MMC_RPMB_WRITE_KEY = 0x01,
1410 MMC_RPMB_READ_CNT = 0x02,
1411 MMC_RPMB_WRITE = 0x03,
1412 MMC_RPMB_READ = 0x04,
1413
1414 /* For internal usage only, do not use it directly */
1415 MMC_RPMB_READ_RESP = 0x05
1416};
1417
1418struct rpmb_frame {
1419 u_int8_t stuff[196];
1420 u_int8_t key_mac[32];
1421 u_int8_t data[256];
1422 u_int8_t nonce[16];
1423 u_int32_t write_counter;
1424 u_int16_t addr;
1425 u_int16_t block_count;
1426 u_int16_t result;
1427 u_int16_t req_resp;
1428};
1429
1430/* Performs RPMB operation.
1431 *
1432 * @fd: RPMB device on which we should perform ioctl command
1433 * @frame_in: input RPMB frame, should be properly inited
1434 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1435 * result and req_resp for output frame.
1436 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1437 * in the other cases -EINVAL will be returned.
1438 */
1439static int do_rpmb_op(int fd,
1440 const struct rpmb_frame *frame_in,
1441 struct rpmb_frame *frame_out,
1442 unsigned int out_cnt)
1443{
1444 int err;
1445 u_int16_t rpmb_type;
1446
1447 struct mmc_ioc_cmd ioc = {
1448 .arg = 0x0,
1449 .blksz = 512,
1450 .blocks = 1,
1451 .write_flag = 1,
1452 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1453 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1454 .data_ptr = (uintptr_t)frame_in
1455 };
1456
1457 if (!frame_in || !frame_out || !out_cnt)
1458 return -EINVAL;
1459
1460 rpmb_type = be16toh(frame_in->req_resp);
1461
1462 switch(rpmb_type) {
1463 case MMC_RPMB_WRITE:
1464 case MMC_RPMB_WRITE_KEY:
1465 if (out_cnt != 1) {
1466 err = -EINVAL;
1467 goto out;
1468 }
1469
1470 /* Write request */
1471 ioc.write_flag |= (1<<31);
1472 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1473 if (err < 0) {
1474 err = -errno;
1475 goto out;
1476 }
1477
1478 /* Result request */
1479 memset(frame_out, 0, sizeof(*frame_out));
1480 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
1481 ioc.write_flag = 1;
1482 ioc.data_ptr = (uintptr_t)frame_out;
1483 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1484 if (err < 0) {
1485 err = -errno;
1486 goto out;
1487 }
1488
1489 /* Get response */
1490 ioc.write_flag = 0;
1491 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1492 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1493 if (err < 0) {
1494 err = -errno;
1495 goto out;
1496 }
1497
1498 break;
1499 case MMC_RPMB_READ_CNT:
1500 if (out_cnt != 1) {
1501 err = -EINVAL;
1502 goto out;
1503 }
1504 /* fall through */
1505
1506 case MMC_RPMB_READ:
1507 /* Request */
1508 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1509 if (err < 0) {
1510 err = -errno;
1511 goto out;
1512 }
1513
1514 /* Get response */
1515 ioc.write_flag = 0;
1516 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1517 ioc.blocks = out_cnt;
1518 ioc.data_ptr = (uintptr_t)frame_out;
1519 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1520 if (err < 0) {
1521 err = -errno;
1522 goto out;
1523 }
1524
1525 break;
1526 default:
1527 err = -EINVAL;
1528 goto out;
1529 }
1530
1531out:
1532 return err;
1533}
1534
1535int do_rpmb_write_key(int nargs, char **argv)
1536{
1537 int ret, dev_fd, key_fd;
1538 struct rpmb_frame frame_in = {
1539 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
1540 }, frame_out;
1541
1542 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
1543 exit(1));
1544
1545 dev_fd = open(argv[1], O_RDWR);
1546 if (dev_fd < 0) {
1547 perror("device open");
1548 exit(1);
1549 }
1550
1551 if (0 == strcmp(argv[2], "-"))
1552 key_fd = STDIN_FILENO;
1553 else {
1554 key_fd = open(argv[2], O_RDONLY);
1555 if (key_fd < 0) {
1556 perror("can't open key file");
1557 exit(1);
1558 }
1559 }
1560
1561 /* Read the auth key */
1562 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
1563 if (ret < 0) {
1564 perror("read the key");
1565 exit(1);
1566 } else if (ret != sizeof(frame_in.key_mac)) {
1567 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1568 (unsigned long)sizeof(frame_in.key_mac),
1569 ret);
1570 exit(1);
1571 }
1572
1573 /* Execute RPMB op */
1574 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1575 if (ret != 0) {
1576 perror("RPMB ioctl failed");
1577 exit(1);
1578 }
1579
1580 /* Check RPMB response */
1581 if (frame_out.result != 0) {
1582 printf("RPMB operation failed, retcode 0x%04x\n",
1583 be16toh(frame_out.result));
1584 exit(1);
1585 }
1586
1587 close(dev_fd);
1588 if (key_fd != STDIN_FILENO)
1589 close(key_fd);
1590
1591 return ret;
1592}
1593
1594int rpmb_read_counter(int dev_fd, unsigned int *cnt)
1595{
1596 int ret;
1597 struct rpmb_frame frame_in = {
1598 .req_resp = htobe16(MMC_RPMB_READ_CNT)
1599 }, frame_out;
1600
1601 /* Execute RPMB op */
1602 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1603 if (ret != 0) {
1604 perror("RPMB ioctl failed");
1605 exit(1);
1606 }
1607
1608 /* Check RPMB response */
1609 if (frame_out.result != 0)
1610 return be16toh(frame_out.result);
1611
1612 *cnt = be32toh(frame_out.write_counter);
1613
1614 return 0;
1615}
1616
1617int do_rpmb_read_counter(int nargs, char **argv)
1618{
1619 int ret, dev_fd;
1620 unsigned int cnt;
1621
1622 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
1623 exit(1));
1624
1625 dev_fd = open(argv[1], O_RDWR);
1626 if (dev_fd < 0) {
1627 perror("device open");
1628 exit(1);
1629 }
1630
1631 ret = rpmb_read_counter(dev_fd, &cnt);
1632
1633 /* Check RPMB response */
1634 if (ret != 0) {
1635 printf("RPMB operation failed, retcode 0x%04x\n", ret);
1636 exit(1);
1637 }
1638
1639 close(dev_fd);
1640
1641 printf("Counter value: 0x%08x\n", cnt);
1642
1643 return ret;
1644}
1645
1646int do_rpmb_read_block(int nargs, char **argv)
1647{
1648 int i, ret, dev_fd, data_fd, key_fd = -1;
1649 uint16_t addr, blocks_cnt;
1650 unsigned char key[32];
1651 struct rpmb_frame frame_in = {
1652 .req_resp = htobe16(MMC_RPMB_READ),
1653 }, *frame_out_p;
1654
1655 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
1656 exit(1));
1657
1658 dev_fd = open(argv[1], O_RDWR);
1659 if (dev_fd < 0) {
1660 perror("device open");
1661 exit(1);
1662 }
1663
1664 /* Get block address */
1665 errno = 0;
1666 addr = strtol(argv[2], NULL, 0);
1667 if (errno) {
1668 perror("incorrect address");
1669 exit(1);
1670 }
1671 frame_in.addr = htobe16(addr);
1672
1673 /* Get blocks count */
1674 errno = 0;
1675 blocks_cnt = strtol(argv[3], NULL, 0);
1676 if (errno) {
1677 perror("incorrect blocks count");
1678 exit(1);
1679 }
1680
1681 if (!blocks_cnt) {
1682 printf("please, specify valid blocks count number\n");
1683 exit(1);
1684 }
1685
1686 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
1687 if (!frame_out_p) {
1688 printf("can't allocate memory for RPMB outer frames\n");
1689 exit(1);
1690 }
1691
1692 /* Write 256b data */
1693 if (0 == strcmp(argv[4], "-"))
1694 data_fd = STDOUT_FILENO;
1695 else {
1696 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
1697 S_IRUSR | S_IWUSR);
1698 if (data_fd < 0) {
1699 perror("can't open output file");
1700 exit(1);
1701 }
1702 }
1703
1704 /* Key is specified */
1705 if (nargs == 6) {
1706 if (0 == strcmp(argv[5], "-"))
1707 key_fd = STDIN_FILENO;
1708 else {
1709 key_fd = open(argv[5], O_RDONLY);
1710 if (key_fd < 0) {
1711 perror("can't open input key file");
1712 exit(1);
1713 }
1714 }
1715
1716 ret = DO_IO(read, key_fd, key, sizeof(key));
1717 if (ret < 0) {
1718 perror("read the key data");
1719 exit(1);
1720 } else if (ret != sizeof(key)) {
1721 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1722 (unsigned long)sizeof(key),
1723 ret);
1724 exit(1);
1725 }
1726 }
1727
1728 /* Execute RPMB op */
1729 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
1730 if (ret != 0) {
1731 perror("RPMB ioctl failed");
1732 exit(1);
1733 }
1734
1735 /* Check RPMB response */
1736 if (frame_out_p[blocks_cnt - 1].result != 0) {
1737 printf("RPMB operation failed, retcode 0x%04x\n",
1738 be16toh(frame_out_p[blocks_cnt - 1].result));
1739 exit(1);
1740 }
1741
1742 /* Do we have to verify data against key? */
1743 if (nargs == 6) {
1744 unsigned char mac[32];
1745 hmac_sha256_ctx ctx;
1746 struct rpmb_frame *frame_out = NULL;
1747
1748 hmac_sha256_init(&ctx, key, sizeof(key));
1749 for (i = 0; i < blocks_cnt; i++) {
1750 frame_out = &frame_out_p[i];
1751 hmac_sha256_update(&ctx, frame_out->data,
1752 sizeof(*frame_out) -
1753 offsetof(struct rpmb_frame, data));
1754 }
1755
1756 hmac_sha256_final(&ctx, mac, sizeof(mac));
1757
1758 /* Impossible */
1759 assert(frame_out);
1760
1761 /* Compare calculated MAC and MAC from last frame */
1762 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
1763 printf("RPMB MAC missmatch\n");
1764 exit(1);
1765 }
1766 }
1767
1768 /* Write data */
1769 for (i = 0; i < blocks_cnt; i++) {
1770 struct rpmb_frame *frame_out = &frame_out_p[i];
1771 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
1772 if (ret < 0) {
1773 perror("write the data");
1774 exit(1);
1775 } else if (ret != sizeof(frame_out->data)) {
1776 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
1777 (unsigned long)sizeof(frame_out->data),
1778 ret);
1779 exit(1);
1780 }
1781 }
1782
1783 free(frame_out_p);
1784 close(dev_fd);
1785 if (data_fd != STDOUT_FILENO)
1786 close(data_fd);
1787 if (key_fd != -1 && key_fd != STDIN_FILENO)
1788 close(key_fd);
1789
1790 return ret;
1791}
1792
1793int do_rpmb_write_block(int nargs, char **argv)
1794{
1795 int ret, dev_fd, key_fd, data_fd;
1796 unsigned char key[32];
1797 uint16_t addr;
1798 unsigned int cnt;
1799 struct rpmb_frame frame_in = {
1800 .req_resp = htobe16(MMC_RPMB_WRITE),
1801 .block_count = htobe16(1)
1802 }, frame_out;
1803
1804 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
1805 exit(1));
1806
1807 dev_fd = open(argv[1], O_RDWR);
1808 if (dev_fd < 0) {
1809 perror("device open");
1810 exit(1);
1811 }
1812
1813 ret = rpmb_read_counter(dev_fd, &cnt);
1814 /* Check RPMB response */
1815 if (ret != 0) {
1816 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
1817 exit(1);
1818 }
1819 frame_in.write_counter = htobe32(cnt);
1820
1821 /* Get block address */
1822 errno = 0;
1823 addr = strtol(argv[2], NULL, 0);
1824 if (errno) {
1825 perror("incorrect address");
1826 exit(1);
1827 }
1828 frame_in.addr = htobe16(addr);
1829
1830 /* Read 256b data */
1831 if (0 == strcmp(argv[3], "-"))
1832 data_fd = STDIN_FILENO;
1833 else {
1834 data_fd = open(argv[3], O_RDONLY);
1835 if (data_fd < 0) {
1836 perror("can't open input file");
1837 exit(1);
1838 }
1839 }
1840
1841 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
1842 if (ret < 0) {
1843 perror("read the data");
1844 exit(1);
1845 } else if (ret != sizeof(frame_in.data)) {
1846 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1847 (unsigned long)sizeof(frame_in.data),
1848 ret);
1849 exit(1);
1850 }
1851
1852 /* Read the auth key */
1853 if (0 == strcmp(argv[4], "-"))
1854 key_fd = STDIN_FILENO;
1855 else {
1856 key_fd = open(argv[4], O_RDONLY);
1857 if (key_fd < 0) {
1858 perror("can't open key file");
1859 exit(1);
1860 }
1861 }
1862
1863 ret = DO_IO(read, key_fd, key, sizeof(key));
1864 if (ret < 0) {
1865 perror("read the key");
1866 exit(1);
1867 } else if (ret != sizeof(key)) {
1868 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1869 (unsigned long)sizeof(key),
1870 ret);
1871 exit(1);
1872 }
1873
1874 /* Calculate HMAC SHA256 */
1875 hmac_sha256(
1876 key, sizeof(key),
1877 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
1878 frame_in.key_mac, sizeof(frame_in.key_mac));
1879
1880 /* Execute RPMB op */
1881 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1882 if (ret != 0) {
1883 perror("RPMB ioctl failed");
1884 exit(1);
1885 }
1886
1887 /* Check RPMB response */
1888 if (frame_out.result != 0) {
1889 printf("RPMB operation failed, retcode 0x%04x\n",
1890 be16toh(frame_out.result));
1891 exit(1);
1892 }
1893
1894 close(dev_fd);
1895 if (data_fd != STDIN_FILENO)
1896 close(data_fd);
1897 if (key_fd != STDIN_FILENO)
1898 close(key_fd);
1899
1900 return ret;
1901}