blob: 3a34bd4f9109aa5a379f68f67a1f90c91ada445b [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
Balaji T K1fdb7f92015-04-29 18:12:32 -0400514int check_enhanced_area_total_limit(const char * const device, int fd)
515{
516 __u8 ext_csd[512];
517 __u32 regl;
518 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
519 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400520 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400521 unsigned int wp_sz, erase_sz;
522 int ret;
523
524 ret = read_extcsd(fd, ext_csd);
525 if (ret) {
526 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
527 exit(1);
528 }
529 wp_sz = get_hc_wp_grp_size(ext_csd);
530 erase_sz = get_hc_erase_grp_size(ext_csd);
531
532 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
533 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
534 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
535 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
536 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
537 enh_area_sz += gp4_part_sz;
538 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
539 printf(" i.e. %lu KiB\n", gp4_part_sz);
540 }
541
542 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
543 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
544 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
545 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
546 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
547 enh_area_sz += gp3_part_sz;
548 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
549 printf(" i.e. %lu KiB\n", gp3_part_sz);
550 }
551
552 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
553 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
554 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
555 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
556 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
557 enh_area_sz += gp2_part_sz;
558 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
559 printf(" i.e. %lu KiB\n", gp2_part_sz);
560 }
561
562 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
563 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
564 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
565 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
566 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
567 enh_area_sz += gp1_part_sz;
568 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
569 printf(" i.e. %lu KiB\n", gp1_part_sz);
570 }
571
572 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
573 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
574 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
575 user_area_sz = 512l * regl * erase_sz * wp_sz;
576 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
577 enh_area_sz += user_area_sz;
578 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
579 printf(" i.e. %lu KiB\n", user_area_sz);
580 }
581
582 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
583 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
584 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
585 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
586 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
587 printf(" i.e. %lu KiB\n", max_enh_area_sz);
588 if (enh_area_sz > max_enh_area_sz) {
589 fprintf(stderr,
590 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
591 enh_area_sz, max_enh_area_sz, device);
592 return 1;
593 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400594 total_sz = get_sector_count(ext_csd) / 2;
595 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
596 gp1_part_sz + user_area_sz;
597 if (total_gp_user_sz > total_sz) {
598 fprintf(stderr,
599 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
600 total_gp_user_sz, total_sz, device);
601 return 1;
602 }
603
604 return 0;
605}
606
607int do_create_gp_partition(int nargs, char **argv)
608{
609 __u8 value;
610 __u8 ext_csd[512];
611 __u8 address;
612 int fd, ret;
613 char *device;
614 int dry_run = 1;
615 int partition, enh_attr, ext_attr;
616 unsigned int length_kib, gp_size_mult;
617 unsigned long align;
618
619 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n> <length KiB> "
620 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
621
622 if (!strcmp("-y", argv[1]))
623 dry_run = 0;
624
625 length_kib = strtol(argv[2], NULL, 10);
626 partition = strtol(argv[3], NULL, 10);
627 enh_attr = strtol(argv[4], NULL, 10);
628 ext_attr = strtol(argv[5], NULL, 10);
629 device = argv[6];
630
631 if (partition < 0 || partition > 4) {
632 printf("Invalid gp parition number valid range [1-4]\n");
633 exit(1);
634 }
635
636 if (enh_attr && ext_attr) {
637 printf("Not allowed to set both enhanced attribute and extended attribute\n");
638 exit(1);
639 }
640
641 fd = open(device, O_RDWR);
642 if (fd < 0) {
643 perror("open");
644 exit(1);
645 }
646
647 ret = read_extcsd(fd, ext_csd);
648 if (ret) {
649 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
650 exit(1);
651 }
652
653 /* assert not PARTITION_SETTING_COMPLETED */
654 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
655 printf(" Device is already partitioned\n");
656 exit(1);
657 }
658
659 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
660 gp_size_mult = (length_kib + align/2l) / align;
661
662 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
663 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
664 if (ret) {
665 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
666 EXT_CSD_ERASE_GROUP_DEF, device);
667 exit(1);
668 }
669
670 value = (gp_size_mult >> 16) & 0xff;
671 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
672 ret = write_extcsd_value(fd, address, value);
673 if (ret) {
674 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
675 value, address, device);
676 exit(1);
677 }
678 value = (gp_size_mult >> 8) & 0xff;
679 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
680 ret = write_extcsd_value(fd, address, value);
681 if (ret) {
682 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
683 value, address, device);
684 exit(1);
685 }
686 value = gp_size_mult & 0xff;
687 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
688 ret = write_extcsd_value(fd, address, value);
689 if (ret) {
690 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
691 value, address, device);
692 exit(1);
693 }
694
695 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
696 if (enh_attr)
697 value |= (1 << partition);
698 else
699 value &= ~(1 << partition);
700
701 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
702 if (ret) {
703 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
704 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
705 exit(1);
706 }
707
708 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
709 value = ext_csd[address];
710 if (ext_attr)
711 value |= (ext_attr << (4 * ((partition - 1) % 2)));
712 else
713 value &= (0xF << (4 * ((partition % 2))));
714
715 ret = write_extcsd_value(fd, address, value);
716 if (ret) {
717 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
718 value, address, device);
719 exit(1);
720 }
721
722 ret = check_enhanced_area_total_limit(device, fd);
723 if (ret)
724 exit(1);
725
726 if (!set_partitioning_setting_completed(dry_run, device, fd))
727 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -0400728
729 return 0;
730}
731
Ben Gardinerd91d3692013-05-30 17:12:51 -0400732int do_enh_area_set(int nargs, char **argv)
733{
734 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800735 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400736 int fd, ret;
737 char *device;
738 int dry_run = 1;
739 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
740 unsigned long align;
741
742 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
743 "</path/to/mmcblkX>\n", exit(1));
744
745 if (!strcmp("-y", argv[1]))
746 dry_run = 0;
747
748 start_kib = strtol(argv[2], NULL, 10);
749 length_kib = strtol(argv[3], NULL, 10);
750 device = argv[4];
751
752 fd = open(device, O_RDWR);
753 if (fd < 0) {
754 perror("open");
755 exit(1);
756 }
757
758 ret = read_extcsd(fd, ext_csd);
759 if (ret) {
760 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
761 exit(1);
762 }
763
764 /* assert ENH_ATTRIBUTE_EN */
765 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
766 {
767 printf(" Device cannot have enhanced tech.\n");
768 exit(1);
769 }
770
771 /* assert not PARTITION_SETTING_COMPLETED */
772 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
773 {
774 printf(" Device is already partitioned\n");
775 exit(1);
776 }
777
778 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
779
780 enh_size_mult = (length_kib + align/2l) / align;
781
782 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
783 enh_start_addr /= align;
784 enh_start_addr *= align;
785
786 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
787 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
788 if (ret) {
789 fprintf(stderr, "Could not write 0x1 to "
790 "EXT_CSD[%d] in %s\n",
791 EXT_CSD_ERASE_GROUP_DEF, device);
792 exit(1);
793 }
794
795 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
796 value = (enh_start_addr >> 24) & 0xff;
797 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
798 if (ret) {
799 fprintf(stderr, "Could not write 0x%02x to "
800 "EXT_CSD[%d] in %s\n", value,
801 EXT_CSD_ENH_START_ADDR_3, device);
802 exit(1);
803 }
804 value = (enh_start_addr >> 16) & 0xff;
805 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
806 if (ret) {
807 fprintf(stderr, "Could not write 0x%02x to "
808 "EXT_CSD[%d] in %s\n", value,
809 EXT_CSD_ENH_START_ADDR_2, device);
810 exit(1);
811 }
812 value = (enh_start_addr >> 8) & 0xff;
813 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
814 if (ret) {
815 fprintf(stderr, "Could not write 0x%02x to "
816 "EXT_CSD[%d] in %s\n", value,
817 EXT_CSD_ENH_START_ADDR_1, device);
818 exit(1);
819 }
820 value = enh_start_addr & 0xff;
821 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
822 if (ret) {
823 fprintf(stderr, "Could not write 0x%02x to "
824 "EXT_CSD[%d] in %s\n", value,
825 EXT_CSD_ENH_START_ADDR_0, device);
826 exit(1);
827 }
828
829 value = (enh_size_mult >> 16) & 0xff;
830 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
831 if (ret) {
832 fprintf(stderr, "Could not write 0x%02x to "
833 "EXT_CSD[%d] in %s\n", value,
834 EXT_CSD_ENH_SIZE_MULT_2, device);
835 exit(1);
836 }
837 value = (enh_size_mult >> 8) & 0xff;
838 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
839 if (ret) {
840 fprintf(stderr, "Could not write 0x%02x to "
841 "EXT_CSD[%d] in %s\n", value,
842 EXT_CSD_ENH_SIZE_MULT_1, device);
843 exit(1);
844 }
845 value = enh_size_mult & 0xff;
846 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
847 if (ret) {
848 fprintf(stderr, "Could not write 0x%02x to "
849 "EXT_CSD[%d] in %s\n", value,
850 EXT_CSD_ENH_SIZE_MULT_0, device);
851 exit(1);
852 }
Balaji T K1fdb7f92015-04-29 18:12:32 -0400853 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
854 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400855 if (ret) {
856 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
857 "EXT_CSD[%d] in %s\n",
858 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
859 exit(1);
860 }
861
Balaji T K1fdb7f92015-04-29 18:12:32 -0400862 ret = check_enhanced_area_total_limit(device, fd);
863 if (ret)
864 exit(1);
865
Ben Gardinere6e84e92013-09-19 11:14:27 -0400866 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400867
Ben Gardinere6e84e92013-09-19 11:14:27 -0400868 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400869 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400870
871 return 0;
872}
873
Ben Gardiner196d0d22013-09-19 11:14:29 -0400874int do_write_reliability_set(int nargs, char **argv)
875{
876 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800877 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400878 int fd, ret;
879
880 int dry_run = 1;
881 int partition;
882 char *device;
883
884 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
885 "<partition> </path/to/mmcblkX>\n", exit(1));
886
887 if (!strcmp("-y", argv[1]))
888 dry_run = 0;
889
890 partition = strtol(argv[2], NULL, 10);
891 device = argv[3];
892
893 fd = open(device, O_RDWR);
894 if (fd < 0) {
895 perror("open");
896 exit(1);
897 }
898
899 ret = read_extcsd(fd, ext_csd);
900 if (ret) {
901 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
902 exit(1);
903 }
904
905 /* assert not PARTITION_SETTING_COMPLETED */
906 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
907 {
908 printf(" Device is already partitioned\n");
909 exit(1);
910 }
911
912 /* assert HS_CTRL_REL */
913 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
914 printf("Cannot set write reliability parameters, WR_REL_SET is "
915 "read-only\n");
916 exit(1);
917 }
918
919 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
920 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
921 if (ret) {
922 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
923 value, EXT_CSD_WR_REL_SET, device);
924 exit(1);
925 }
926
927 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
928 value, device);
929
930 if (!set_partitioning_setting_completed(dry_run, device, fd))
931 exit(1);
932
933 return 0;
934}
935
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500936int do_read_extcsd(int nargs, char **argv)
937{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800938 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +0200939 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500940 int fd, ret;
941 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100942 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800943 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800944 "4.0", /* 0 */
945 "4.1", /* 1 */
946 "4.2", /* 2 */
947 "4.3", /* 3 */
948 "Obsolete", /* 4 */
949 "4.41", /* 5 */
950 "4.5", /* 6 */
951 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -0700952 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800953 };
954 int boot_access;
955 const char* boot_access_str[] = {
956 "No access to boot partition", /* 0 */
957 "R/W Boot Partition 1", /* 1 */
958 "R/W Boot Partition 2", /* 2 */
959 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
960 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500961
Chris Ball8ba44662012-04-19 13:22:54 -0400962 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
963 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500964
965 device = argv[1];
966
967 fd = open(device, O_RDWR);
968 if (fd < 0) {
969 perror("open");
970 exit(1);
971 }
972
973 ret = read_extcsd(fd, ext_csd);
974 if (ret) {
975 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
976 exit(1);
977 }
978
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100979 ext_csd_rev = ext_csd[192];
980
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800981 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
982 (ext_csd_rev != 4))
983 str = ver_str[ext_csd_rev];
984 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100985 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800986
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100987 printf("=============================================\n");
988 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
989 printf("=============================================\n\n");
990
991 if (ext_csd_rev < 3)
992 goto out_free; /* No ext_csd */
993
994 /* Parse the Extended CSD registers.
995 * Reserved bit should be read as "0" in case of spec older
996 * than A441.
997 */
998 reg = ext_csd[EXT_CSD_S_CMD_SET];
999 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1000 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001001 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001002
1003 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1004 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1005 if (reg & EXT_CSD_HPI_SUPP) {
1006 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001007 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001008 else
1009 printf("implementation based on CMD13\n");
1010 }
1011
1012 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1013 ext_csd[502]);
1014
1015 if (ext_csd_rev >= 6) {
1016 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1017 ext_csd[501]);
1018 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1019 ext_csd[500]);
1020 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1021 ext_csd[499]);
1022
1023 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1024 ext_csd[498]);
1025 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1026 ext_csd[497]);
1027 printf("Context Management Capabilities"
1028 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1029 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1030 ext_csd[495]);
1031 printf("Extended partition attribute support"
1032 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001033 }
1034 if (ext_csd_rev >= 7) {
1035 int j;
1036 int eol_info;
1037 char* eol_info_str[] = {
1038 "Not Defined", /* 0 */
1039 "Normal", /* 1 */
1040 "Warning", /* 2 */
1041 "Urgent", /* 3 */
1042 };
1043
1044 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1045 ext_csd[493]);
1046 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1047 ext_csd[492]);
1048 printf("Operation codes timeout"
1049 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1050 ext_csd[491]);
1051 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1052 get_word_from_ext_csd(&ext_csd[487]));
1053 printf("Number of FW sectors correctly programmed"
1054 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1055 get_word_from_ext_csd(&ext_csd[302]));
1056 printf("Vendor proprietary health report:\n");
1057 for (j = 301; j >= 270; j--)
1058 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1059 " 0x%02x\n", j, ext_csd[j]);
1060 for (j = 269; j >= 268; j--) {
1061 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001062 char est_type = 'B' + (j - 269);
1063 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001064 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001065 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001066 if (life_used >= 0x1 && life_used <= 0xa)
1067 printf(" i.e. %d%% - %d%% device life time"
1068 " used\n",
1069 (life_used - 1) * 10, life_used * 10);
1070 else if (life_used == 0xb)
1071 printf(" i.e. Exceeded its maximum estimated"
1072 " device life time\n");
1073 }
1074 eol_info = ext_csd[267];
1075 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1076 eol_info);
1077 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1078 printf(" i.e. %s\n", eol_info_str[eol_info]);
1079 else
1080 printf(" i.e. Reserved\n");
1081
1082 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1083 ext_csd[266]);
1084 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1085 ext_csd[265]);
1086 printf("Optimal trim unit size"
1087 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1088 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1089 ext_csd[263], ext_csd[262]);
1090 printf("Firmware version:\n");
1091 for (j = 261; j >= 254; j--)
1092 printf("[FIRMWARE_VERSION[%d]]:"
1093 " 0x%02x\n", j, ext_csd[j]);
1094
1095 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1096 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1097 }
1098 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001099 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1100 ext_csd[248]);
1101 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1102 ext_csd[247]);
1103 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001104 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001105 }
1106
1107 /* A441: Reserved [501:247]
1108 A43: reserved [246:229] */
1109 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001110 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001111 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001112
1113 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1114
1115 printf("1st Initialisation Time after programmed sector"
1116 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1117
1118 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001119 printf("Power class for 52MHz, DDR at 3.6V"
1120 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1121 printf("Power class for 52MHz, DDR at 1.95V"
1122 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1123
1124 /* A441: reserved [237-236] */
1125
1126 if (ext_csd_rev >= 6) {
1127 printf("Power class for 200MHz at 3.6V"
1128 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1129 printf("Power class for 200MHz, at 1.95V"
1130 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1131 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001132 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001133 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1134 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1135 /* A441: reserved [233] */
1136 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1137 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1138 ext_csd[231]);
1139 }
1140 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1141 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1142 ext_csd[230]);
1143 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1144 ext_csd[229]);
1145 }
1146 reg = ext_csd[EXT_CSD_BOOT_INFO];
1147 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1148 if (reg & EXT_CSD_BOOT_INFO_ALT)
1149 printf(" Device supports alternative boot method\n");
1150 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1151 printf(" Device supports dual data rate during boot\n");
1152 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1153 printf(" Device supports high speed timing during boot\n");
1154
1155 /* A441/A43: reserved [227] */
1156 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1157 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001158
1159 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001160 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001161 reg);
1162 printf(" i.e. %u KiB\n", 512 * reg);
1163
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001164 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1165 ext_csd[223]);
1166 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1167 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001168
1169 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001170 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001171 reg);
1172 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1173
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001174 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1175 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1176 /* A441/A43: reserved [218] */
1177 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1178 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001179
1180 unsigned int sectors = get_sector_count(ext_csd);
1181 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1182 if (is_blockaddresed(ext_csd))
1183 printf(" Device is block-addressed\n");
1184 else
1185 printf(" Device is NOT block-addressed\n");
1186
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001187 /* A441/A43: reserved [211] */
1188 printf("Minimum Write Performance for 8bit:\n");
1189 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1190 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1191 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1192 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1193 printf("Minimum Write Performance for 4bit:\n");
1194 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1195 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1196 /* A441/A43: reserved [204] */
1197 printf("Power classes registers:\n");
1198 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1199 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1200 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1201 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1202
1203 /* A43: reserved [199:198] */
1204 if (ext_csd_rev >= 5) {
1205 printf("Partition switching timing "
1206 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1207 printf("Out-of-interrupt busy timing"
1208 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1209 }
1210
1211 /* A441/A43: reserved [197] [195] [193] [190] [188]
1212 * [186] [184] [182] [180] [176] */
1213
1214 if (ext_csd_rev >= 6)
1215 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1216 ext_csd[197]);
1217
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001218 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001219 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1220 ext_csd[196], ext_csd[195]);
1221 reg = ext_csd[195];
1222 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1223 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001224 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001225 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1226 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001227 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1228 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1229 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1230 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1231 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1232 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001233
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001234 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1235 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1236 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1237 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1238 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1239 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1240 ext_csd[185]);
1241 /* bus_width: ext_csd[183] not readable */
1242 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1243 ext_csd[181]);
1244 reg = ext_csd[EXT_CSD_BOOT_CFG];
1245 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001246 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001247 case 0x0:
1248 printf(" Not boot enable\n");
1249 break;
1250 case 0x1:
1251 printf(" Boot Partition 1 enabled\n");
1252 break;
1253 case 0x2:
1254 printf(" Boot Partition 2 enabled\n");
1255 break;
1256 case 0x7:
1257 printf(" User Area Enabled for boot\n");
1258 break;
1259 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001260 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1261 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1262 printf(" %s\n", boot_access_str[boot_access]);
1263 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001264 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001265 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001266
1267 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1268 ext_csd[178]);
1269 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1270 ext_csd[177]);
1271 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001272 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001273
Chris Ballb9c7a172012-02-20 12:34:25 -05001274 print_writeprotect_status(ext_csd);
1275
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001276 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001277 /* A441]: reserved [172] */
1278 printf("User area write protection register"
1279 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1280 /* A441]: reserved [170] */
1281 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1282 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001283
1284 reg = ext_csd[EXT_CSD_WR_REL_SET];
1285 const char * const fast = "existing data is at risk if a power "
1286 "failure occurs during a write operation";
1287 const char * const reliable = "the device protects existing "
1288 "data if a power failure occurs during a write "
1289 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001290 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001291 " [WR_REL_SET]: 0x%02x\n", reg);
1292
1293 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1294 int i;
1295 for (i = 1; i <= 4; i++) {
1296 printf(" partition %d: %s\n", i,
1297 reg & (1<<i) ? reliable : fast);
1298 }
1299
1300 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001301 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001302 " [WR_REL_PARAM]: 0x%02x\n", reg);
1303 if (reg & 0x01)
1304 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1305 if (reg & 0x04)
1306 printf(" Device supports the enhanced def. of reliable "
1307 "write\n");
1308
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001309 /* sanitize_start ext_csd[165]]: not readable
1310 * bkops_start ext_csd[164]]: only writable */
1311 printf("Enable background operations handshake"
1312 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1313 printf("H/W reset function"
1314 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1315 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001316 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001317 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1318 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001319 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001320 printf(" Device support partitioning feature\n");
1321 else
1322 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001323 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001324 printf(" Device can have enhanced tech.\n");
1325 else
1326 printf(" Device cannot have enhanced tech.\n");
1327
Oliver Metz11f2cea2013-09-23 08:40:52 +02001328 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001329 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1330 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1331
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001332 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001333 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001334 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1335 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001336 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001337
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001338 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001339 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001340 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001341 printf("Partitioning Setting"
1342 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001343 reg);
1344 if (reg)
1345 printf(" Device partition setting complete\n");
1346 else
1347 printf(" Device partition setting NOT complete\n");
1348
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001349 printf("General Purpose Partition Size\n"
1350 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1351 (ext_csd[153] << 8) | ext_csd[152]);
1352 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1353 (ext_csd[150] << 8) | ext_csd[149]);
1354 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1355 (ext_csd[147] << 8) | ext_csd[146]);
1356 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1357 (ext_csd[144] << 8) | ext_csd[143]);
1358
Oliver Metz11f2cea2013-09-23 08:40:52 +02001359 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001360 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1361 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001362 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001363 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1364 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001365 get_hc_erase_grp_size(ext_csd) *
1366 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001367
Oliver Metz11f2cea2013-09-23 08:40:52 +02001368 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001369 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1370 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1371 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001372 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001373 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001374 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001375 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001376
1377 /* A441]: reserved [135] */
1378 printf("Bad Block Management mode"
1379 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1380 /* A441: reserved [133:0] */
1381 }
1382 /* B45 */
1383 if (ext_csd_rev >= 6) {
1384 int j;
1385 /* tcase_support ext_csd[132] not readable */
1386 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1387 ext_csd[131]);
1388 printf("Program CID/CSD in DDR mode support"
1389 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1390 ext_csd[130]);
1391
1392 for (j = 127; j >= 64; j--)
1393 printf("Vendor Specific Fields"
1394 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1395 j, ext_csd[j]);
1396
Gwendal Grignoue966e672014-07-07 14:03:13 -07001397 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001398 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001399 reg);
1400 if (reg == 0x00)
1401 printf(" i.e. 512 B\n");
1402 else if (reg == 0x01)
1403 printf(" i.e. 4 KiB\n");
1404 else
1405 printf(" i.e. Reserved\n");
1406
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001407 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1408 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001409 reg = ext_csd[61];
1410 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1411 if (reg == 0x00)
1412 printf(" i.e. 512 B\n");
1413 else if (reg == 0x01)
1414 printf(" i.e. 4 KiB\n");
1415 else
1416 printf(" i.e. Reserved\n");
1417
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001418 printf("1st initialization after disabling sector"
1419 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1420 ext_csd[60]);
1421 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1422 ext_csd[59]);
1423 printf("Number of addressed group to be Released"
1424 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1425 printf("Exception events control"
1426 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1427 (ext_csd[57] << 8) | ext_csd[56]);
1428 printf("Exception events status"
1429 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1430 (ext_csd[55] << 8) | ext_csd[54]);
1431 printf("Extended Partitions Attribute"
1432 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1433 (ext_csd[53] << 8) | ext_csd[52]);
1434
1435 for (j = 51; j >= 37; j--)
1436 printf("Context configuration"
1437 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1438
1439 printf("Packed command status"
1440 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1441 printf("Packed command failure index"
1442 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1443 printf("Power Off Notification"
1444 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001445 printf("Control to turn the Cache ON/OFF"
1446 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001447 /* flush_cache ext_csd[32] not readable */
1448 /*Reserved [31:0] */
1449 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001450 if (ext_csd_rev >= 7) {
1451 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1452 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1453 ext_csd[29]);
1454
1455 reg = ext_csd[26];
1456 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1457 switch (reg) {
1458 case 0x00:
1459 printf(" Success\n");
1460 break;
1461 case 0x10:
1462 printf(" General error\n");
1463 break;
1464 case 0x11:
1465 printf(" Firmware install error\n");
1466 break;
1467 case 0x12:
1468 printf(" Error in downloading firmware\n");
1469 break;
1470 default:
1471 printf(" Reserved\n");
1472 }
1473 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1474 " %d sector size\n",
1475 get_word_from_ext_csd(&ext_csd[22]));
1476 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1477 " %d sector size\n",
1478 get_word_from_ext_csd(&ext_csd[18]));
1479 printf("Product state awareness enablement"
1480 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1481 ext_csd[17]);
1482 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1483 ext_csd[16]);
1484 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001485
1486out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001487 return ret;
1488}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001489
Nick Sanders9d57aa72014-03-05 21:38:54 -08001490int do_dump_extcsd(int nargs, char **argv)
1491{
1492 __u8 ext_csd[EXT_CSD_SIZE];
1493 int fd, ret;
1494 char *device;
1495 int i, j;
1496
1497 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1498 exit(1));
1499
1500 device = argv[1];
1501
1502 fd = open(device, O_RDWR);
1503 if (fd < 0) {
1504 perror("Failed to open mmc device");
1505 exit(1);
1506 }
1507
1508 ret = read_extcsd(fd, ext_csd);
1509 if (ret) {
1510 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1511 exit(1);
1512 }
1513
1514 /* Dump all bytes so that any undecoded or proprietary registers */
1515 /* can be acessed. */
1516 printf("EXT_CSD binary dump:\n");
1517 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1518 printf(" %3d: %3x: ", i, i);
1519 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1520 printf(" %02x", ext_csd[i+j]);
1521 }
1522 printf("\n");
1523 }
1524
1525 return ret;
1526}
1527
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001528int do_sanitize(int nargs, char **argv)
1529{
1530 int fd, ret;
1531 char *device;
1532
1533 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1534 exit(1));
1535
1536 device = argv[1];
1537
1538 fd = open(device, O_RDWR);
1539 if (fd < 0) {
1540 perror("open");
1541 exit(1);
1542 }
1543
1544 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1545 if (ret) {
1546 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1547 1, EXT_CSD_SANITIZE_START, device);
1548 exit(1);
1549 }
1550
1551 return ret;
1552
1553}
1554
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001555static const char* const mmc_ffu_hack_names[] = {
1556 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1557};
1558
Gwendal Grignou771984c2014-07-01 12:46:18 -07001559int do_emmc50_ffu (int nargs, char **argv)
1560{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001561 int fd, ret, i, argc=1, ffu_hack=0;
1562 char *device, *type, *path;
1563 __u64 value;
1564 union {
1565 __u8 data[FFU_DATA_SIZE];
1566 struct mmc_ffu_args ffu_args;
1567 } ffu_data;
1568 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001569 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001570
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001571 while (!strcmp("-k", argv[argc])) {
1572 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1573 if (ret < 1) {
1574 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1575 exit(1);
1576 }
1577 for (i = 0; i < MMC_HACK_LEN; i++) {
1578 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1579 ffu_args->hack[ffu_hack].type = i;
1580 if (ret == 2) {
1581 ffu_args->hack[ffu_hack].value = value;
1582 }
1583 ffu_hack++;
1584 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1585 sizeof(struct mmc_ffu_args) >
1586 FFU_DATA_SIZE) {
1587 fprintf(stderr, "Too many %d hacks",
1588 ffu_hack);
1589 exit(1);
1590 }
1591 break;
1592 }
1593 }
1594 if (i == MMC_HACK_LEN) {
1595 fprintf(stderr, "Hack type %s not found\n", type);
1596 fprintf(stderr, "Supported types are: ");
1597 for (i = 0; i < MMC_HACK_LEN; i++)
1598 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1599 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001600
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001601 exit(1);
1602 }
1603 free(type);
1604 argc++;
1605 }
1606 ffu_args->hack_nb = ffu_hack;
1607
1608 path = argv[argc++];
1609 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001610 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1611 exit(1);
1612 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001613 strcpy(ffu_args->name, path);
1614 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001615 fd = open(device, O_RDWR);
1616 if (fd < 0) {
1617 perror("open");
1618 exit(1);
1619 }
1620
Gwendal Grignou0f757342014-10-16 16:52:46 -07001621 /* prepare and send ioctl */
1622 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1623 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001624 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001625 mmc_ioc_cmd.blocks = 1;
1626 mmc_ioc_cmd.arg = 0;
1627 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1628 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001629 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001630 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001631 if (ret) {
1632 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1633 exit(1);
1634 }
1635
1636 close(fd);
1637 return 0;
1638}
1639
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001640#define DO_IO(func, fd, buf, nbyte) \
1641 ({ \
1642 ssize_t ret = 0, r; \
1643 do { \
1644 r = func(fd, buf + ret, nbyte - ret); \
1645 if (r < 0 && errno != EINTR) { \
1646 ret = -1; \
1647 break; \
1648 } \
1649 else if (r > 0) \
1650 ret += r; \
1651 } while (r != 0 && (size_t)ret != nbyte); \
1652 \
1653 ret; \
1654 })
1655
1656enum rpmb_op_type {
1657 MMC_RPMB_WRITE_KEY = 0x01,
1658 MMC_RPMB_READ_CNT = 0x02,
1659 MMC_RPMB_WRITE = 0x03,
1660 MMC_RPMB_READ = 0x04,
1661
1662 /* For internal usage only, do not use it directly */
1663 MMC_RPMB_READ_RESP = 0x05
1664};
1665
1666struct rpmb_frame {
1667 u_int8_t stuff[196];
1668 u_int8_t key_mac[32];
1669 u_int8_t data[256];
1670 u_int8_t nonce[16];
1671 u_int32_t write_counter;
1672 u_int16_t addr;
1673 u_int16_t block_count;
1674 u_int16_t result;
1675 u_int16_t req_resp;
1676};
1677
1678/* Performs RPMB operation.
1679 *
1680 * @fd: RPMB device on which we should perform ioctl command
1681 * @frame_in: input RPMB frame, should be properly inited
1682 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1683 * result and req_resp for output frame.
1684 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1685 * in the other cases -EINVAL will be returned.
1686 */
1687static int do_rpmb_op(int fd,
1688 const struct rpmb_frame *frame_in,
1689 struct rpmb_frame *frame_out,
1690 unsigned int out_cnt)
1691{
1692 int err;
1693 u_int16_t rpmb_type;
1694
1695 struct mmc_ioc_cmd ioc = {
1696 .arg = 0x0,
1697 .blksz = 512,
1698 .blocks = 1,
1699 .write_flag = 1,
1700 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
1701 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
1702 .data_ptr = (uintptr_t)frame_in
1703 };
1704
1705 if (!frame_in || !frame_out || !out_cnt)
1706 return -EINVAL;
1707
1708 rpmb_type = be16toh(frame_in->req_resp);
1709
1710 switch(rpmb_type) {
1711 case MMC_RPMB_WRITE:
1712 case MMC_RPMB_WRITE_KEY:
1713 if (out_cnt != 1) {
1714 err = -EINVAL;
1715 goto out;
1716 }
1717
1718 /* Write request */
1719 ioc.write_flag |= (1<<31);
1720 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1721 if (err < 0) {
1722 err = -errno;
1723 goto out;
1724 }
1725
1726 /* Result request */
1727 memset(frame_out, 0, sizeof(*frame_out));
1728 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
1729 ioc.write_flag = 1;
1730 ioc.data_ptr = (uintptr_t)frame_out;
1731 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1732 if (err < 0) {
1733 err = -errno;
1734 goto out;
1735 }
1736
1737 /* Get response */
1738 ioc.write_flag = 0;
1739 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1740 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1741 if (err < 0) {
1742 err = -errno;
1743 goto out;
1744 }
1745
1746 break;
1747 case MMC_RPMB_READ_CNT:
1748 if (out_cnt != 1) {
1749 err = -EINVAL;
1750 goto out;
1751 }
1752 /* fall through */
1753
1754 case MMC_RPMB_READ:
1755 /* Request */
1756 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1757 if (err < 0) {
1758 err = -errno;
1759 goto out;
1760 }
1761
1762 /* Get response */
1763 ioc.write_flag = 0;
1764 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
1765 ioc.blocks = out_cnt;
1766 ioc.data_ptr = (uintptr_t)frame_out;
1767 err = ioctl(fd, MMC_IOC_CMD, &ioc);
1768 if (err < 0) {
1769 err = -errno;
1770 goto out;
1771 }
1772
1773 break;
1774 default:
1775 err = -EINVAL;
1776 goto out;
1777 }
1778
1779out:
1780 return err;
1781}
1782
1783int do_rpmb_write_key(int nargs, char **argv)
1784{
1785 int ret, dev_fd, key_fd;
1786 struct rpmb_frame frame_in = {
1787 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
1788 }, frame_out;
1789
1790 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
1791 exit(1));
1792
1793 dev_fd = open(argv[1], O_RDWR);
1794 if (dev_fd < 0) {
1795 perror("device open");
1796 exit(1);
1797 }
1798
1799 if (0 == strcmp(argv[2], "-"))
1800 key_fd = STDIN_FILENO;
1801 else {
1802 key_fd = open(argv[2], O_RDONLY);
1803 if (key_fd < 0) {
1804 perror("can't open key file");
1805 exit(1);
1806 }
1807 }
1808
1809 /* Read the auth key */
1810 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
1811 if (ret < 0) {
1812 perror("read the key");
1813 exit(1);
1814 } else if (ret != sizeof(frame_in.key_mac)) {
1815 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
1816 (unsigned long)sizeof(frame_in.key_mac),
1817 ret);
1818 exit(1);
1819 }
1820
1821 /* Execute RPMB op */
1822 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1823 if (ret != 0) {
1824 perror("RPMB ioctl failed");
1825 exit(1);
1826 }
1827
1828 /* Check RPMB response */
1829 if (frame_out.result != 0) {
1830 printf("RPMB operation failed, retcode 0x%04x\n",
1831 be16toh(frame_out.result));
1832 exit(1);
1833 }
1834
1835 close(dev_fd);
1836 if (key_fd != STDIN_FILENO)
1837 close(key_fd);
1838
1839 return ret;
1840}
1841
1842int rpmb_read_counter(int dev_fd, unsigned int *cnt)
1843{
1844 int ret;
1845 struct rpmb_frame frame_in = {
1846 .req_resp = htobe16(MMC_RPMB_READ_CNT)
1847 }, frame_out;
1848
1849 /* Execute RPMB op */
1850 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
1851 if (ret != 0) {
1852 perror("RPMB ioctl failed");
1853 exit(1);
1854 }
1855
1856 /* Check RPMB response */
1857 if (frame_out.result != 0)
1858 return be16toh(frame_out.result);
1859
1860 *cnt = be32toh(frame_out.write_counter);
1861
1862 return 0;
1863}
1864
1865int do_rpmb_read_counter(int nargs, char **argv)
1866{
1867 int ret, dev_fd;
1868 unsigned int cnt;
1869
1870 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
1871 exit(1));
1872
1873 dev_fd = open(argv[1], O_RDWR);
1874 if (dev_fd < 0) {
1875 perror("device open");
1876 exit(1);
1877 }
1878
1879 ret = rpmb_read_counter(dev_fd, &cnt);
1880
1881 /* Check RPMB response */
1882 if (ret != 0) {
1883 printf("RPMB operation failed, retcode 0x%04x\n", ret);
1884 exit(1);
1885 }
1886
1887 close(dev_fd);
1888
1889 printf("Counter value: 0x%08x\n", cnt);
1890
1891 return ret;
1892}
1893
1894int do_rpmb_read_block(int nargs, char **argv)
1895{
1896 int i, ret, dev_fd, data_fd, key_fd = -1;
1897 uint16_t addr, blocks_cnt;
1898 unsigned char key[32];
1899 struct rpmb_frame frame_in = {
1900 .req_resp = htobe16(MMC_RPMB_READ),
1901 }, *frame_out_p;
1902
1903 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
1904 exit(1));
1905
1906 dev_fd = open(argv[1], O_RDWR);
1907 if (dev_fd < 0) {
1908 perror("device open");
1909 exit(1);
1910 }
1911
1912 /* Get block address */
1913 errno = 0;
1914 addr = strtol(argv[2], NULL, 0);
1915 if (errno) {
1916 perror("incorrect address");
1917 exit(1);
1918 }
1919 frame_in.addr = htobe16(addr);
1920
1921 /* Get blocks count */
1922 errno = 0;
1923 blocks_cnt = strtol(argv[3], NULL, 0);
1924 if (errno) {
1925 perror("incorrect blocks count");
1926 exit(1);
1927 }
1928
1929 if (!blocks_cnt) {
1930 printf("please, specify valid blocks count number\n");
1931 exit(1);
1932 }
1933
1934 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
1935 if (!frame_out_p) {
1936 printf("can't allocate memory for RPMB outer frames\n");
1937 exit(1);
1938 }
1939
1940 /* Write 256b data */
1941 if (0 == strcmp(argv[4], "-"))
1942 data_fd = STDOUT_FILENO;
1943 else {
1944 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
1945 S_IRUSR | S_IWUSR);
1946 if (data_fd < 0) {
1947 perror("can't open output file");
1948 exit(1);
1949 }
1950 }
1951
1952 /* Key is specified */
1953 if (nargs == 6) {
1954 if (0 == strcmp(argv[5], "-"))
1955 key_fd = STDIN_FILENO;
1956 else {
1957 key_fd = open(argv[5], O_RDONLY);
1958 if (key_fd < 0) {
1959 perror("can't open input key file");
1960 exit(1);
1961 }
1962 }
1963
1964 ret = DO_IO(read, key_fd, key, sizeof(key));
1965 if (ret < 0) {
1966 perror("read the key data");
1967 exit(1);
1968 } else if (ret != sizeof(key)) {
1969 printf("Data must be %lu bytes length, but we read only %d, exit\n",
1970 (unsigned long)sizeof(key),
1971 ret);
1972 exit(1);
1973 }
1974 }
1975
1976 /* Execute RPMB op */
1977 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
1978 if (ret != 0) {
1979 perror("RPMB ioctl failed");
1980 exit(1);
1981 }
1982
1983 /* Check RPMB response */
1984 if (frame_out_p[blocks_cnt - 1].result != 0) {
1985 printf("RPMB operation failed, retcode 0x%04x\n",
1986 be16toh(frame_out_p[blocks_cnt - 1].result));
1987 exit(1);
1988 }
1989
1990 /* Do we have to verify data against key? */
1991 if (nargs == 6) {
1992 unsigned char mac[32];
1993 hmac_sha256_ctx ctx;
1994 struct rpmb_frame *frame_out = NULL;
1995
1996 hmac_sha256_init(&ctx, key, sizeof(key));
1997 for (i = 0; i < blocks_cnt; i++) {
1998 frame_out = &frame_out_p[i];
1999 hmac_sha256_update(&ctx, frame_out->data,
2000 sizeof(*frame_out) -
2001 offsetof(struct rpmb_frame, data));
2002 }
2003
2004 hmac_sha256_final(&ctx, mac, sizeof(mac));
2005
2006 /* Impossible */
2007 assert(frame_out);
2008
2009 /* Compare calculated MAC and MAC from last frame */
2010 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2011 printf("RPMB MAC missmatch\n");
2012 exit(1);
2013 }
2014 }
2015
2016 /* Write data */
2017 for (i = 0; i < blocks_cnt; i++) {
2018 struct rpmb_frame *frame_out = &frame_out_p[i];
2019 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2020 if (ret < 0) {
2021 perror("write the data");
2022 exit(1);
2023 } else if (ret != sizeof(frame_out->data)) {
2024 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2025 (unsigned long)sizeof(frame_out->data),
2026 ret);
2027 exit(1);
2028 }
2029 }
2030
2031 free(frame_out_p);
2032 close(dev_fd);
2033 if (data_fd != STDOUT_FILENO)
2034 close(data_fd);
2035 if (key_fd != -1 && key_fd != STDIN_FILENO)
2036 close(key_fd);
2037
2038 return ret;
2039}
2040
2041int do_rpmb_write_block(int nargs, char **argv)
2042{
2043 int ret, dev_fd, key_fd, data_fd;
2044 unsigned char key[32];
2045 uint16_t addr;
2046 unsigned int cnt;
2047 struct rpmb_frame frame_in = {
2048 .req_resp = htobe16(MMC_RPMB_WRITE),
2049 .block_count = htobe16(1)
2050 }, frame_out;
2051
2052 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
2053 exit(1));
2054
2055 dev_fd = open(argv[1], O_RDWR);
2056 if (dev_fd < 0) {
2057 perror("device open");
2058 exit(1);
2059 }
2060
2061 ret = rpmb_read_counter(dev_fd, &cnt);
2062 /* Check RPMB response */
2063 if (ret != 0) {
2064 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2065 exit(1);
2066 }
2067 frame_in.write_counter = htobe32(cnt);
2068
2069 /* Get block address */
2070 errno = 0;
2071 addr = strtol(argv[2], NULL, 0);
2072 if (errno) {
2073 perror("incorrect address");
2074 exit(1);
2075 }
2076 frame_in.addr = htobe16(addr);
2077
2078 /* Read 256b data */
2079 if (0 == strcmp(argv[3], "-"))
2080 data_fd = STDIN_FILENO;
2081 else {
2082 data_fd = open(argv[3], O_RDONLY);
2083 if (data_fd < 0) {
2084 perror("can't open input file");
2085 exit(1);
2086 }
2087 }
2088
2089 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2090 if (ret < 0) {
2091 perror("read the data");
2092 exit(1);
2093 } else if (ret != sizeof(frame_in.data)) {
2094 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2095 (unsigned long)sizeof(frame_in.data),
2096 ret);
2097 exit(1);
2098 }
2099
2100 /* Read the auth key */
2101 if (0 == strcmp(argv[4], "-"))
2102 key_fd = STDIN_FILENO;
2103 else {
2104 key_fd = open(argv[4], O_RDONLY);
2105 if (key_fd < 0) {
2106 perror("can't open key file");
2107 exit(1);
2108 }
2109 }
2110
2111 ret = DO_IO(read, key_fd, key, sizeof(key));
2112 if (ret < 0) {
2113 perror("read the key");
2114 exit(1);
2115 } else if (ret != sizeof(key)) {
2116 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2117 (unsigned long)sizeof(key),
2118 ret);
2119 exit(1);
2120 }
2121
2122 /* Calculate HMAC SHA256 */
2123 hmac_sha256(
2124 key, sizeof(key),
2125 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2126 frame_in.key_mac, sizeof(frame_in.key_mac));
2127
2128 /* Execute RPMB op */
2129 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2130 if (ret != 0) {
2131 perror("RPMB ioctl failed");
2132 exit(1);
2133 }
2134
2135 /* Check RPMB response */
2136 if (frame_out.result != 0) {
2137 printf("RPMB operation failed, retcode 0x%04x\n",
2138 be16toh(frame_out.result));
2139 exit(1);
2140 }
2141
2142 close(dev_fd);
2143 if (data_fd != STDIN_FILENO)
2144 close(data_fd);
2145 if (key_fd != STDIN_FILENO)
2146 close(key_fd);
2147
2148 return ret;
2149}