blob: 2bc78d4df6d689bae1ca6b9d6861f1285c4c1450 [file] [log] [blame]
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
Avi Shchislowskidc7ab962016-03-08 14:22:41 -050015 *
16 * Modified to add field firmware update support,
17 * those modifications are Copyright (c) 2016 SanDisk Corp.
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050018 */
19
Gwendal Grignou771984c2014-07-01 12:46:18 -070020#include <errno.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050021#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/ioctl.h>
Gwendal Grignou771984c2014-07-01 12:46:18 -070025#include <sys/param.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050026#include <sys/types.h>
27#include <dirent.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <libgen.h>
32#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050033#include <ctype.h>
Roman Peniaev023cc7c2014-08-12 23:25:45 +090034#include <errno.h>
35#include <stdint.h>
36#include <assert.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050037
38#include "mmc.h"
39#include "mmc_cmds.h"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080040#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050041
Nick Sanders9d57aa72014-03-05 21:38:54 -080042#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080043#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080044#define CID_SIZE 16
45
Julius Wernerbcc3e2e2016-04-21 16:53:02 -070046/* Sending several commands too close together seems to cause timeouts. */
47#define INTER_COMMAND_GAP_US (50 * 1000)
48
Roman Peniaev023cc7c2014-08-12 23:25:45 +090049#include "3rdparty/hmac_sha/hmac_sha2.h"
Nick Sanders9d57aa72014-03-05 21:38:54 -080050
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050051int read_extcsd(int fd, __u8 *ext_csd)
52{
53 int ret = 0;
54 struct mmc_ioc_cmd idata;
55 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080056 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050057 idata.write_flag = 0;
58 idata.opcode = MMC_SEND_EXT_CSD;
59 idata.arg = 0;
60 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080061 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050062 idata.blocks = 1;
63 mmc_ioc_cmd_set_data(idata, ext_csd);
64
65 ret = ioctl(fd, MMC_IOC_CMD, &idata);
66 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080067 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050068
69 return ret;
70}
71
72int write_extcsd_value(int fd, __u8 index, __u8 value)
73{
74 int ret = 0;
75 struct mmc_ioc_cmd idata;
76
77 memset(&idata, 0, sizeof(idata));
78 idata.write_flag = 1;
79 idata.opcode = MMC_SWITCH;
80 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
81 (index << 16) |
82 (value << 8) |
83 EXT_CSD_CMD_SET_NORMAL;
84 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
85
86 ret = ioctl(fd, MMC_IOC_CMD, &idata);
87 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080088 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050089
90 return ret;
91}
92
Ben Gardiner27c357d2013-05-30 17:12:47 -040093int send_status(int fd, __u32 *response)
94{
95 int ret = 0;
96 struct mmc_ioc_cmd idata;
97
98 memset(&idata, 0, sizeof(idata));
99 idata.opcode = MMC_SEND_STATUS;
100 idata.arg = (1 << 16);
101 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
102
103 ret = ioctl(fd, MMC_IOC_CMD, &idata);
104 if (ret)
105 perror("ioctl");
106
107 *response = idata.response[0];
108
109 return ret;
110}
111
Chris Ballb9c7a172012-02-20 12:34:25 -0500112void print_writeprotect_status(__u8 *ext_csd)
113{
114 __u8 reg;
Al Cooper786418c2015-04-29 18:12:35 -0400115 __u8 ext_csd_rev = ext_csd[EXT_CSD_REV];
Chris Ballb9c7a172012-02-20 12:34:25 -0500116
117 /* A43: reserved [174:0] */
118 if (ext_csd_rev >= 5) {
119 printf("Boot write protection status registers"
120 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
121
122 reg = ext_csd[EXT_CSD_BOOT_WP];
123 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
124 printf(" Power ro locking: ");
125 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
126 printf("not possible\n");
127 else
128 printf("possible\n");
129
130 printf(" Permanent ro locking: ");
131 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
132 printf("not possible\n");
133 else
134 printf("possible\n");
135
136 printf(" ro lock status: ");
137 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
138 printf("locked until next power on\n");
139 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
140 printf("locked permanently\n");
141 else
142 printf("not locked\n");
143 }
144}
145
146int do_writeprotect_get(int nargs, char **argv)
147{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800148 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500149 int fd, ret;
150 char *device;
151
Chris Ball8ba44662012-04-19 13:22:54 -0400152 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
153 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500154
155 device = argv[1];
156
157 fd = open(device, O_RDWR);
158 if (fd < 0) {
159 perror("open");
160 exit(1);
161 }
162
163 ret = read_extcsd(fd, ext_csd);
164 if (ret) {
165 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
166 exit(1);
167 }
168
169 print_writeprotect_status(ext_csd);
170
171 return ret;
172}
173
174int do_writeprotect_set(int nargs, char **argv)
175{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800176 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500177 int fd, ret;
178 char *device;
179
Chris Ball8ba44662012-04-19 13:22:54 -0400180 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
181 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500182
183 device = argv[1];
184
185 fd = open(device, O_RDWR);
186 if (fd < 0) {
187 perror("open");
188 exit(1);
189 }
190
191 ret = read_extcsd(fd, ext_csd);
192 if (ret) {
193 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
194 exit(1);
195 }
196
197 value = ext_csd[EXT_CSD_BOOT_WP] |
198 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
199 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
200 if (ret) {
201 fprintf(stderr, "Could not write 0x%02x to "
202 "EXT_CSD[%d] in %s\n",
203 value, EXT_CSD_BOOT_WP, device);
204 exit(1);
205 }
206
207 return ret;
208}
209
Saugata Dasb7e25992012-05-17 09:26:34 -0400210int do_disable_512B_emulation(int nargs, char **argv)
211{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800212 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400213 int fd, ret;
214 char *device;
215
216 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
217 device = argv[1];
218
219 fd = open(device, O_RDWR);
220 if (fd < 0) {
221 perror("open");
222 exit(1);
223 }
224
225 ret = read_extcsd(fd, ext_csd);
226 if (ret) {
227 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
228 exit(1);
229 }
230
231 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
232 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
233 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
234
235 if (native_sector_size && !data_sector_size &&
236 (wr_rel_param & EN_REL_WR)) {
237 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
238
239 if (ret) {
240 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
241 1, EXT_CSD_BOOT_WP, device);
242 exit(1);
243 }
244 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
245 } else if (native_sector_size && data_sector_size) {
246 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
247 } else {
248 printf("MMC does not support disabling 512B emulation mode.\n");
249 }
250
251 return ret;
252}
253
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200254int do_write_boot_en(int nargs, char **argv)
255{
256 __u8 ext_csd[512];
257 __u8 value = 0;
258 int fd, ret;
259 char *device;
260 int boot_area, send_ack;
261
262 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
263 "<send_ack> </path/to/mmcblkX>\n", exit(1));
264
265 /*
266 * If <send_ack> is 1, the device will send acknowledgment
267 * pattern "010" to the host when boot operation begins.
268 * If <send_ack> is 0, it won't.
269 */
270 boot_area = strtol(argv[1], NULL, 10);
271 send_ack = strtol(argv[2], NULL, 10);
272 device = argv[3];
273
274 fd = open(device, O_RDWR);
275 if (fd < 0) {
276 perror("open");
277 exit(1);
278 }
279
280 ret = read_extcsd(fd, ext_csd);
281 if (ret) {
282 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
283 exit(1);
284 }
285
286 value = ext_csd[EXT_CSD_PART_CONFIG];
287
288 switch (boot_area) {
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100289 case EXT_CSD_PART_CONFIG_ACC_NONE:
290 value &= ~(7 << 3);
291 break;
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200292 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
293 value |= (1 << 3);
294 value &= ~(3 << 4);
295 break;
296 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
297 value |= (1 << 4);
298 value &= ~(1 << 3);
299 value &= ~(1 << 5);
300 break;
301 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
302 value |= (boot_area << 3);
303 break;
304 default:
305 fprintf(stderr, "Cannot enable the boot area\n");
306 exit(1);
307 }
308 if (send_ack)
309 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
310 else
311 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
312
313 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
314 if (ret) {
315 fprintf(stderr, "Could not write 0x%02x to "
316 "EXT_CSD[%d] in %s\n",
317 value, EXT_CSD_PART_CONFIG, device);
318 exit(1);
319 }
320 return ret;
321}
322
Al Cooper794314c2015-05-01 08:24:37 -0400323int do_boot_bus_conditions_set(int nargs, char **argv)
324{
325 __u8 ext_csd[512];
326 __u8 value = 0;
327 int fd, ret;
328 char *device;
329
330 CHECK(nargs != 5, "Usage: mmc: bootbus set <boot_mode> "
331 "<reset_boot_bus_conditions> <boot_bus_width> <device>\n",
332 exit(1));
333
334 if (strcmp(argv[1], "single_backward") == 0)
335 value |= 0;
336 else if (strcmp(argv[1], "single_hs") == 0)
337 value |= 0x8;
338 else if (strcmp(argv[1], "dual") == 0)
339 value |= 0x10;
340 else {
341 fprintf(stderr, "illegal <boot_mode> specified\n");
342 exit(1);
343 }
344
345 if (strcmp(argv[2], "x1") == 0)
346 value |= 0;
347 else if (strcmp(argv[2], "retain") == 0)
348 value |= 0x4;
349 else {
350 fprintf(stderr,
351 "illegal <reset_boot_bus_conditions> specified\n");
352 exit(1);
353 }
354
355 if (strcmp(argv[3], "x1") == 0)
356 value |= 0;
357 else if (strcmp(argv[3], "x4") == 0)
358 value |= 0x1;
359 else if (strcmp(argv[3], "x8") == 0)
360 value |= 0x2;
361 else {
362 fprintf(stderr, "illegal <boot_bus_width> specified\n");
363 exit(1);
364 }
365
366 device = argv[4];
367 fd = open(device, O_RDWR);
368 if (fd < 0) {
369 perror("open");
370 exit(1);
371 }
372
373 ret = read_extcsd(fd, ext_csd);
374 if (ret) {
375 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
376 exit(1);
377 }
378 printf("Changing ext_csd[BOOT_BUS_CONDITIONS] from 0x%02x to 0x%02x\n",
379 ext_csd[EXT_CSD_BOOT_BUS_CONDITIONS], value);
380
381 ret = write_extcsd_value(fd, EXT_CSD_BOOT_BUS_CONDITIONS, value);
382 if (ret) {
383 fprintf(stderr, "Could not write 0x%02x to "
384 "EXT_CSD[%d] in %s\n",
385 value, EXT_CSD_BOOT_BUS_CONDITIONS, device);
386 exit(1);
387 }
388 close(fd);
389 return ret;
390}
391
Chris Ballf74dfe22012-10-19 16:49:55 -0400392int do_hwreset(int value, int nargs, char **argv)
393{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800394 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400395 int fd, ret;
396 char *device;
397
398 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
399 exit(1));
400
401 device = argv[1];
402
403 fd = open(device, O_RDWR);
404 if (fd < 0) {
405 perror("open");
406 exit(1);
407 }
408
409 ret = read_extcsd(fd, ext_csd);
410 if (ret) {
411 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
412 exit(1);
413 }
414
415 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
416 EXT_CSD_HW_RESET_EN) {
417 fprintf(stderr,
418 "H/W Reset is already permanently enabled on %s\n",
419 device);
420 exit(1);
421 }
422 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
423 EXT_CSD_HW_RESET_DIS) {
424 fprintf(stderr,
425 "H/W Reset is already permanently disabled on %s\n",
426 device);
427 exit(1);
428 }
429
430 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
431 if (ret) {
432 fprintf(stderr,
433 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
434 value, EXT_CSD_RST_N_FUNCTION, device);
435 exit(1);
436 }
437
438 return ret;
439}
440
441int do_hwreset_en(int nargs, char **argv)
442{
443 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
444}
445
446int do_hwreset_dis(int nargs, char **argv)
447{
448 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
449}
450
Jaehoon Chung86496512012-09-21 10:08:05 +0000451int do_write_bkops_en(int nargs, char **argv)
452{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800453 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000454 int fd, ret;
455 char *device;
456
457 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
458 exit(1));
459
460 device = argv[1];
461
462 fd = open(device, O_RDWR);
463 if (fd < 0) {
464 perror("open");
465 exit(1);
466 }
467
468 ret = read_extcsd(fd, ext_csd);
469 if (ret) {
470 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
471 exit(1);
472 }
473
474 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
475 fprintf(stderr, "%s doesn't support BKOPS\n", device);
476 exit(1);
477 }
478
479 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
480 if (ret) {
481 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
482 value, EXT_CSD_BKOPS_EN, device);
483 exit(1);
484 }
485
486 return ret;
487}
488
Ben Gardiner27c357d2013-05-30 17:12:47 -0400489int do_status_get(int nargs, char **argv)
490{
491 __u32 response;
492 int fd, ret;
493 char *device;
494
495 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
496 exit(1));
497
498 device = argv[1];
499
500 fd = open(device, O_RDWR);
501 if (fd < 0) {
502 perror("open");
503 exit(1);
504 }
505
506 ret = send_status(fd, &response);
507 if (ret) {
508 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
509 exit(1);
510 }
511
512 printf("SEND_STATUS response: 0x%08x\n", response);
513
514 return ret;
515}
516
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800517__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
518{
519 return (ext_csd_loc[3] << 24) |
520 (ext_csd_loc[2] << 16) |
521 (ext_csd_loc[1] << 8) |
522 ext_csd_loc[0];
523}
524
Ben Gardiner4e850232013-05-30 17:12:49 -0400525unsigned int get_sector_count(__u8 *ext_csd)
526{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800527 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400528}
529
530int is_blockaddresed(__u8 *ext_csd)
531{
532 unsigned int sectors = get_sector_count(ext_csd);
533
534 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
535}
536
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400537unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
538{
539 return ext_csd[221];
540}
541
542unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
543{
544 return ext_csd[224];
545}
546
Ben Gardinere6e84e92013-09-19 11:14:27 -0400547int set_partitioning_setting_completed(int dry_run, const char * const device,
548 int fd)
549{
550 int ret;
551
552 if (dry_run) {
553 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
554 fprintf(stderr, "These changes will not take effect neither "
555 "now nor after a power cycle\n");
556 return 1;
557 }
558
559 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
560 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
561 if (ret) {
562 fprintf(stderr, "Could not write 0x1 to "
563 "EXT_CSD[%d] in %s\n",
564 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
565 return 1;
566 }
567
568 __u32 response;
569 ret = send_status(fd, &response);
570 if (ret) {
571 fprintf(stderr, "Could not get response to SEND_STATUS "
572 "from %s\n", device);
573 return 1;
574 }
575
576 if (response & R1_SWITCH_ERROR) {
577 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
578 "failed on %s\n", device);
579 return 1;
580 }
581
582 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
583 "%s SUCCESS\n", device);
584 fprintf(stderr, "Device power cycle needed for settings to "
585 "take effect.\n"
586 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
587 "using 'extcsd read' after power cycle\n");
588
589 return 0;
590}
591
Balaji T K1fdb7f92015-04-29 18:12:32 -0400592int check_enhanced_area_total_limit(const char * const device, int fd)
593{
594 __u8 ext_csd[512];
595 __u32 regl;
596 unsigned long max_enh_area_sz, user_area_sz, enh_area_sz = 0;
597 unsigned long gp4_part_sz, gp3_part_sz, gp2_part_sz, gp1_part_sz;
Balaji T Kd78ce082015-04-29 18:12:33 -0400598 unsigned long total_sz, total_gp_user_sz;
Balaji T K1fdb7f92015-04-29 18:12:32 -0400599 unsigned int wp_sz, erase_sz;
600 int ret;
601
602 ret = read_extcsd(fd, ext_csd);
603 if (ret) {
604 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
605 exit(1);
606 }
607 wp_sz = get_hc_wp_grp_size(ext_csd);
608 erase_sz = get_hc_erase_grp_size(ext_csd);
609
610 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_4_2] << 16) |
611 (ext_csd[EXT_CSD_GP_SIZE_MULT_4_1] << 8) |
612 ext_csd[EXT_CSD_GP_SIZE_MULT_4_0];
613 gp4_part_sz = 512l * regl * erase_sz * wp_sz;
614 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_4) {
615 enh_area_sz += gp4_part_sz;
616 printf("Enhanced GP4 Partition Size [GP_SIZE_MULT_4]: 0x%06x\n", regl);
617 printf(" i.e. %lu KiB\n", gp4_part_sz);
618 }
619
620 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_3_2] << 16) |
621 (ext_csd[EXT_CSD_GP_SIZE_MULT_3_1] << 8) |
622 ext_csd[EXT_CSD_GP_SIZE_MULT_3_0];
623 gp3_part_sz = 512l * regl * erase_sz * wp_sz;
624 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_3) {
625 enh_area_sz += gp3_part_sz;
626 printf("Enhanced GP3 Partition Size [GP_SIZE_MULT_3]: 0x%06x\n", regl);
627 printf(" i.e. %lu KiB\n", gp3_part_sz);
628 }
629
630 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_2_2] << 16) |
631 (ext_csd[EXT_CSD_GP_SIZE_MULT_2_1] << 8) |
632 ext_csd[EXT_CSD_GP_SIZE_MULT_2_0];
633 gp2_part_sz = 512l * regl * erase_sz * wp_sz;
634 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_2) {
635 enh_area_sz += gp2_part_sz;
636 printf("Enhanced GP2 Partition Size [GP_SIZE_MULT_2]: 0x%06x\n", regl);
637 printf(" i.e. %lu KiB\n", gp2_part_sz);
638 }
639
640 regl = (ext_csd[EXT_CSD_GP_SIZE_MULT_1_2] << 16) |
641 (ext_csd[EXT_CSD_GP_SIZE_MULT_1_1] << 8) |
642 ext_csd[EXT_CSD_GP_SIZE_MULT_1_0];
643 gp1_part_sz = 512l * regl * erase_sz * wp_sz;
644 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_1) {
645 enh_area_sz += gp1_part_sz;
646 printf("Enhanced GP1 Partition Size [GP_SIZE_MULT_1]: 0x%06x\n", regl);
647 printf(" i.e. %lu KiB\n", gp1_part_sz);
648 }
649
650 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
651 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
652 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
653 user_area_sz = 512l * regl * erase_sz * wp_sz;
654 if (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & EXT_CSD_ENH_USR) {
655 enh_area_sz += user_area_sz;
656 printf("Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x%06x\n", regl);
657 printf(" i.e. %lu KiB\n", user_area_sz);
658 }
659
660 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
661 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
662 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
663 max_enh_area_sz = 512l * regl * erase_sz * wp_sz;
664 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", regl);
665 printf(" i.e. %lu KiB\n", max_enh_area_sz);
666 if (enh_area_sz > max_enh_area_sz) {
667 fprintf(stderr,
668 "Programmed total enhanced size %lu KiB cannot exceed max enhanced area %lu KiB %s\n",
669 enh_area_sz, max_enh_area_sz, device);
670 return 1;
671 }
Balaji T Kd78ce082015-04-29 18:12:33 -0400672 total_sz = get_sector_count(ext_csd) / 2;
673 total_gp_user_sz = gp4_part_sz + gp3_part_sz + gp2_part_sz +
674 gp1_part_sz + user_area_sz;
675 if (total_gp_user_sz > total_sz) {
676 fprintf(stderr,
677 "requested total partition size %lu KiB cannot exceed card capacity %lu KiB %s\n",
678 total_gp_user_sz, total_sz, device);
679 return 1;
680 }
681
682 return 0;
683}
684
685int do_create_gp_partition(int nargs, char **argv)
686{
687 __u8 value;
688 __u8 ext_csd[512];
689 __u8 address;
690 int fd, ret;
691 char *device;
692 int dry_run = 1;
693 int partition, enh_attr, ext_attr;
694 unsigned int length_kib, gp_size_mult;
695 unsigned long align;
696
697 CHECK(nargs != 7, "Usage: mmc gp create <-y|-n> <length KiB> "
698 "<partition> <enh_attr> <ext_attr> </path/to/mmcblkX>\n", exit(1));
699
700 if (!strcmp("-y", argv[1]))
701 dry_run = 0;
702
703 length_kib = strtol(argv[2], NULL, 10);
704 partition = strtol(argv[3], NULL, 10);
705 enh_attr = strtol(argv[4], NULL, 10);
706 ext_attr = strtol(argv[5], NULL, 10);
707 device = argv[6];
708
Marcus Folkessoncb04fde2015-11-18 15:06:16 -0500709 if (partition < 1 || partition > 4) {
710 printf("Invalid gp partition number; valid range [1-4].\n");
Balaji T Kd78ce082015-04-29 18:12:33 -0400711 exit(1);
712 }
713
714 if (enh_attr && ext_attr) {
715 printf("Not allowed to set both enhanced attribute and extended attribute\n");
716 exit(1);
717 }
718
719 fd = open(device, O_RDWR);
720 if (fd < 0) {
721 perror("open");
722 exit(1);
723 }
724
725 ret = read_extcsd(fd, ext_csd);
726 if (ret) {
727 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
728 exit(1);
729 }
730
731 /* assert not PARTITION_SETTING_COMPLETED */
732 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
733 printf(" Device is already partitioned\n");
734 exit(1);
735 }
736
737 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
738 gp_size_mult = (length_kib + align/2l) / align;
739
740 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
741 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
742 if (ret) {
743 fprintf(stderr, "Could not write 0x1 to EXT_CSD[%d] in %s\n",
744 EXT_CSD_ERASE_GROUP_DEF, device);
745 exit(1);
746 }
747
748 value = (gp_size_mult >> 16) & 0xff;
749 address = EXT_CSD_GP_SIZE_MULT_1_2 + (partition - 1) * 3;
750 ret = write_extcsd_value(fd, address, value);
751 if (ret) {
752 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
753 value, address, device);
754 exit(1);
755 }
756 value = (gp_size_mult >> 8) & 0xff;
757 address = EXT_CSD_GP_SIZE_MULT_1_1 + (partition - 1) * 3;
758 ret = write_extcsd_value(fd, address, value);
759 if (ret) {
760 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
761 value, address, device);
762 exit(1);
763 }
764 value = gp_size_mult & 0xff;
765 address = EXT_CSD_GP_SIZE_MULT_1_0 + (partition - 1) * 3;
766 ret = write_extcsd_value(fd, address, value);
767 if (ret) {
768 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
769 value, address, device);
770 exit(1);
771 }
772
773 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
774 if (enh_attr)
775 value |= (1 << partition);
776 else
777 value &= ~(1 << partition);
778
779 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
780 if (ret) {
781 fprintf(stderr, "Could not write EXT_CSD_ENH_%x to EXT_CSD[%d] in %s\n",
782 partition, EXT_CSD_PARTITIONS_ATTRIBUTE, device);
783 exit(1);
784 }
785
786 address = EXT_CSD_EXT_PARTITIONS_ATTRIBUTE_0 + (partition - 1) / 2;
787 value = ext_csd[address];
788 if (ext_attr)
789 value |= (ext_attr << (4 * ((partition - 1) % 2)));
790 else
791 value &= (0xF << (4 * ((partition % 2))));
792
793 ret = write_extcsd_value(fd, address, value);
794 if (ret) {
795 fprintf(stderr, "Could not write 0x%x to EXT_CSD[%d] in %s\n",
796 value, address, device);
797 exit(1);
798 }
799
800 ret = check_enhanced_area_total_limit(device, fd);
801 if (ret)
802 exit(1);
803
804 if (!set_partitioning_setting_completed(dry_run, device, fd))
805 exit(1);
Balaji T K1fdb7f92015-04-29 18:12:32 -0400806
807 return 0;
808}
809
Ben Gardinerd91d3692013-05-30 17:12:51 -0400810int do_enh_area_set(int nargs, char **argv)
811{
812 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800813 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400814 int fd, ret;
815 char *device;
816 int dry_run = 1;
817 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
818 unsigned long align;
819
820 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
821 "</path/to/mmcblkX>\n", exit(1));
822
823 if (!strcmp("-y", argv[1]))
824 dry_run = 0;
825
826 start_kib = strtol(argv[2], NULL, 10);
827 length_kib = strtol(argv[3], NULL, 10);
828 device = argv[4];
829
830 fd = open(device, O_RDWR);
831 if (fd < 0) {
832 perror("open");
833 exit(1);
834 }
835
836 ret = read_extcsd(fd, ext_csd);
837 if (ret) {
838 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
839 exit(1);
840 }
841
842 /* assert ENH_ATTRIBUTE_EN */
843 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
844 {
845 printf(" Device cannot have enhanced tech.\n");
846 exit(1);
847 }
848
849 /* assert not PARTITION_SETTING_COMPLETED */
850 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
851 {
852 printf(" Device is already partitioned\n");
853 exit(1);
854 }
855
856 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
857
858 enh_size_mult = (length_kib + align/2l) / align;
859
860 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
861 enh_start_addr /= align;
862 enh_start_addr *= align;
863
864 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
865 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
866 if (ret) {
867 fprintf(stderr, "Could not write 0x1 to "
868 "EXT_CSD[%d] in %s\n",
869 EXT_CSD_ERASE_GROUP_DEF, device);
870 exit(1);
871 }
872
873 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
874 value = (enh_start_addr >> 24) & 0xff;
875 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
876 if (ret) {
877 fprintf(stderr, "Could not write 0x%02x to "
878 "EXT_CSD[%d] in %s\n", value,
879 EXT_CSD_ENH_START_ADDR_3, device);
880 exit(1);
881 }
882 value = (enh_start_addr >> 16) & 0xff;
883 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
884 if (ret) {
885 fprintf(stderr, "Could not write 0x%02x to "
886 "EXT_CSD[%d] in %s\n", value,
887 EXT_CSD_ENH_START_ADDR_2, device);
888 exit(1);
889 }
890 value = (enh_start_addr >> 8) & 0xff;
891 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
892 if (ret) {
893 fprintf(stderr, "Could not write 0x%02x to "
894 "EXT_CSD[%d] in %s\n", value,
895 EXT_CSD_ENH_START_ADDR_1, device);
896 exit(1);
897 }
898 value = enh_start_addr & 0xff;
899 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
900 if (ret) {
901 fprintf(stderr, "Could not write 0x%02x to "
902 "EXT_CSD[%d] in %s\n", value,
903 EXT_CSD_ENH_START_ADDR_0, device);
904 exit(1);
905 }
906
907 value = (enh_size_mult >> 16) & 0xff;
908 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
909 if (ret) {
910 fprintf(stderr, "Could not write 0x%02x to "
911 "EXT_CSD[%d] in %s\n", value,
912 EXT_CSD_ENH_SIZE_MULT_2, device);
913 exit(1);
914 }
915 value = (enh_size_mult >> 8) & 0xff;
916 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
917 if (ret) {
918 fprintf(stderr, "Could not write 0x%02x to "
919 "EXT_CSD[%d] in %s\n", value,
920 EXT_CSD_ENH_SIZE_MULT_1, device);
921 exit(1);
922 }
923 value = enh_size_mult & 0xff;
924 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
925 if (ret) {
926 fprintf(stderr, "Could not write 0x%02x to "
927 "EXT_CSD[%d] in %s\n", value,
928 EXT_CSD_ENH_SIZE_MULT_0, device);
929 exit(1);
930 }
Balaji T K1fdb7f92015-04-29 18:12:32 -0400931 value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR;
932 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, value);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400933 if (ret) {
934 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
935 "EXT_CSD[%d] in %s\n",
936 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
937 exit(1);
938 }
939
Balaji T K1fdb7f92015-04-29 18:12:32 -0400940 ret = check_enhanced_area_total_limit(device, fd);
941 if (ret)
942 exit(1);
943
Ben Gardinere6e84e92013-09-19 11:14:27 -0400944 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400945
Ben Gardinere6e84e92013-09-19 11:14:27 -0400946 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400947 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400948
949 return 0;
950}
951
Ben Gardiner196d0d22013-09-19 11:14:29 -0400952int do_write_reliability_set(int nargs, char **argv)
953{
954 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800955 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400956 int fd, ret;
957
958 int dry_run = 1;
959 int partition;
960 char *device;
961
962 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
963 "<partition> </path/to/mmcblkX>\n", exit(1));
964
965 if (!strcmp("-y", argv[1]))
966 dry_run = 0;
967
968 partition = strtol(argv[2], NULL, 10);
969 device = argv[3];
970
971 fd = open(device, O_RDWR);
972 if (fd < 0) {
973 perror("open");
974 exit(1);
975 }
976
977 ret = read_extcsd(fd, ext_csd);
978 if (ret) {
979 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
980 exit(1);
981 }
982
983 /* assert not PARTITION_SETTING_COMPLETED */
984 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
985 {
986 printf(" Device is already partitioned\n");
987 exit(1);
988 }
989
990 /* assert HS_CTRL_REL */
991 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
992 printf("Cannot set write reliability parameters, WR_REL_SET is "
993 "read-only\n");
994 exit(1);
995 }
996
997 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
998 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
999 if (ret) {
1000 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1001 value, EXT_CSD_WR_REL_SET, device);
1002 exit(1);
1003 }
1004
1005 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
1006 value, device);
1007
1008 if (!set_partitioning_setting_completed(dry_run, device, fd))
1009 exit(1);
1010
1011 return 0;
1012}
1013
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001014int do_read_extcsd(int nargs, char **argv)
1015{
Nick Sanders9d57aa72014-03-05 21:38:54 -08001016 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +02001017 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001018 int fd, ret;
1019 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001020 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -08001021 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001022 "4.0", /* 0 */
1023 "4.1", /* 1 */
1024 "4.2", /* 2 */
1025 "4.3", /* 3 */
1026 "Obsolete", /* 4 */
1027 "4.41", /* 5 */
1028 "4.5", /* 6 */
1029 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -07001030 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001031 };
1032 int boot_access;
1033 const char* boot_access_str[] = {
1034 "No access to boot partition", /* 0 */
1035 "R/W Boot Partition 1", /* 1 */
1036 "R/W Boot Partition 2", /* 2 */
1037 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
1038 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001039
Chris Ball8ba44662012-04-19 13:22:54 -04001040 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
1041 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001042
1043 device = argv[1];
1044
1045 fd = open(device, O_RDWR);
1046 if (fd < 0) {
1047 perror("open");
1048 exit(1);
1049 }
1050
1051 ret = read_extcsd(fd, ext_csd);
1052 if (ret) {
1053 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1054 exit(1);
1055 }
1056
Al Cooper786418c2015-04-29 18:12:35 -04001057 ext_csd_rev = ext_csd[EXT_CSD_REV];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001058
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001059 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
1060 (ext_csd_rev != 4))
1061 str = ver_str[ext_csd_rev];
1062 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001063 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001064
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001065 printf("=============================================\n");
1066 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
1067 printf("=============================================\n\n");
1068
1069 if (ext_csd_rev < 3)
1070 goto out_free; /* No ext_csd */
1071
1072 /* Parse the Extended CSD registers.
1073 * Reserved bit should be read as "0" in case of spec older
1074 * than A441.
1075 */
1076 reg = ext_csd[EXT_CSD_S_CMD_SET];
1077 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
1078 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -05001079 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001080
1081 reg = ext_csd[EXT_CSD_HPI_FEATURE];
1082 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
1083 if (reg & EXT_CSD_HPI_SUPP) {
1084 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -05001085 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001086 else
1087 printf("implementation based on CMD13\n");
1088 }
1089
1090 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
1091 ext_csd[502]);
1092
1093 if (ext_csd_rev >= 6) {
1094 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
1095 ext_csd[501]);
1096 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
1097 ext_csd[500]);
1098 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
1099 ext_csd[499]);
1100
1101 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
1102 ext_csd[498]);
1103 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
1104 ext_csd[497]);
1105 printf("Context Management Capabilities"
1106 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
1107 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
1108 ext_csd[495]);
1109 printf("Extended partition attribute support"
1110 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001111 }
1112 if (ext_csd_rev >= 7) {
1113 int j;
1114 int eol_info;
1115 char* eol_info_str[] = {
1116 "Not Defined", /* 0 */
1117 "Normal", /* 1 */
1118 "Warning", /* 2 */
1119 "Urgent", /* 3 */
1120 };
1121
1122 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
1123 ext_csd[493]);
1124 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
1125 ext_csd[492]);
1126 printf("Operation codes timeout"
1127 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
1128 ext_csd[491]);
1129 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
1130 get_word_from_ext_csd(&ext_csd[487]));
1131 printf("Number of FW sectors correctly programmed"
1132 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
1133 get_word_from_ext_csd(&ext_csd[302]));
1134 printf("Vendor proprietary health report:\n");
1135 for (j = 301; j >= 270; j--)
1136 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
1137 " 0x%02x\n", j, ext_csd[j]);
1138 for (j = 269; j >= 268; j--) {
1139 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001140 char est_type = 'B' + (j - 269);
1141 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001142 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -08001143 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001144 if (life_used >= 0x1 && life_used <= 0xa)
1145 printf(" i.e. %d%% - %d%% device life time"
1146 " used\n",
1147 (life_used - 1) * 10, life_used * 10);
1148 else if (life_used == 0xb)
1149 printf(" i.e. Exceeded its maximum estimated"
1150 " device life time\n");
1151 }
1152 eol_info = ext_csd[267];
1153 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
1154 eol_info);
1155 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
1156 printf(" i.e. %s\n", eol_info_str[eol_info]);
1157 else
1158 printf(" i.e. Reserved\n");
1159
1160 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
1161 ext_csd[266]);
1162 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
1163 ext_csd[265]);
1164 printf("Optimal trim unit size"
1165 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
1166 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
1167 ext_csd[263], ext_csd[262]);
1168 printf("Firmware version:\n");
1169 for (j = 261; j >= 254; j--)
1170 printf("[FIRMWARE_VERSION[%d]]:"
1171 " 0x%02x\n", j, ext_csd[j]);
1172
1173 printf("Power class for 200MHz, DDR at VCC= 3.6V"
1174 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
1175 }
1176 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001177 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
1178 ext_csd[248]);
1179 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
1180 ext_csd[247]);
1181 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001182 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001183 }
1184
1185 /* A441: Reserved [501:247]
1186 A43: reserved [246:229] */
1187 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001188 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -05001189 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001190
1191 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
1192
1193 printf("1st Initialisation Time after programmed sector"
1194 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
1195
1196 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001197 printf("Power class for 52MHz, DDR at 3.6V"
1198 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
1199 printf("Power class for 52MHz, DDR at 1.95V"
1200 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
1201
1202 /* A441: reserved [237-236] */
1203
1204 if (ext_csd_rev >= 6) {
1205 printf("Power class for 200MHz at 3.6V"
1206 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
1207 printf("Power class for 200MHz, at 1.95V"
1208 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
1209 }
Chris Ballb9c7a172012-02-20 12:34:25 -05001210 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001211 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
1212 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
1213 /* A441: reserved [233] */
1214 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
1215 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
1216 ext_csd[231]);
1217 }
1218 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
1219 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
1220 ext_csd[230]);
1221 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
1222 ext_csd[229]);
1223 }
1224 reg = ext_csd[EXT_CSD_BOOT_INFO];
1225 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
1226 if (reg & EXT_CSD_BOOT_INFO_ALT)
1227 printf(" Device supports alternative boot method\n");
1228 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
1229 printf(" Device supports dual data rate during boot\n");
1230 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
1231 printf(" Device supports high speed timing during boot\n");
1232
1233 /* A441/A43: reserved [227] */
1234 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
1235 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001236
1237 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001238 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001239 reg);
1240 printf(" i.e. %u KiB\n", 512 * reg);
1241
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001242 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
1243 ext_csd[223]);
1244 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
1245 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001246
1247 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001248 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001249 reg);
1250 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
1251
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001252 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
1253 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
1254 /* A441/A43: reserved [218] */
1255 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
1256 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -04001257
1258 unsigned int sectors = get_sector_count(ext_csd);
1259 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
1260 if (is_blockaddresed(ext_csd))
1261 printf(" Device is block-addressed\n");
1262 else
1263 printf(" Device is NOT block-addressed\n");
1264
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001265 /* A441/A43: reserved [211] */
1266 printf("Minimum Write Performance for 8bit:\n");
1267 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
1268 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
1269 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
1270 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
1271 printf("Minimum Write Performance for 4bit:\n");
1272 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
1273 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
1274 /* A441/A43: reserved [204] */
1275 printf("Power classes registers:\n");
1276 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
1277 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
1278 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
1279 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
1280
1281 /* A43: reserved [199:198] */
1282 if (ext_csd_rev >= 5) {
1283 printf("Partition switching timing "
1284 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
1285 printf("Out-of-interrupt busy timing"
1286 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
1287 }
1288
1289 /* A441/A43: reserved [197] [195] [193] [190] [188]
1290 * [186] [184] [182] [180] [176] */
1291
1292 if (ext_csd_rev >= 6)
1293 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
1294 ext_csd[197]);
1295
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001296 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001297 printf("Card Type [CARD_TYPE: 0x%02x - %02x]\n",
1298 ext_csd[196], ext_csd[195]);
1299 reg = ext_csd[195];
1300 if (reg & 0x02) printf(" HS533 Dual Data Rate eMMC @266MHz 1.2VI/O\n");
1301 if (reg & 0x01) printf(" HS533 Dual Data Rate eMMC @266MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001302 reg = ext_csd[196];
Gwendal Grignouc2faa3d2015-04-28 10:00:45 -07001303 if (reg & 0x80) printf(" HS400 Dual Data Rate eMMC @200MHz 1.2VI/O\n");
1304 if (reg & 0x40) printf(" HS400 Dual Data Rate eMMC @200MHz 1.8VI/O\n");
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001305 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
1306 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
1307 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
1308 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
1309 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1310 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001311
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001312 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
Al Cooper786418c2015-04-29 18:12:35 -04001313 /* ext_csd_rev = ext_csd[EXT_CSD_REV] (already done!!!) */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001314 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1315 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1316 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1317 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1318 ext_csd[185]);
1319 /* bus_width: ext_csd[183] not readable */
1320 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1321 ext_csd[181]);
1322 reg = ext_csd[EXT_CSD_BOOT_CFG];
1323 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001324 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001325 case 0x0:
1326 printf(" Not boot enable\n");
1327 break;
1328 case 0x1:
1329 printf(" Boot Partition 1 enabled\n");
1330 break;
1331 case 0x2:
1332 printf(" Boot Partition 2 enabled\n");
1333 break;
1334 case 0x7:
1335 printf(" User Area Enabled for boot\n");
1336 break;
1337 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001338 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1339 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1340 printf(" %s\n", boot_access_str[boot_access]);
1341 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001342 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001343 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001344
1345 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1346 ext_csd[178]);
1347 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1348 ext_csd[177]);
1349 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001350 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001351
Chris Ballb9c7a172012-02-20 12:34:25 -05001352 print_writeprotect_status(ext_csd);
1353
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001354 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001355 /* A441]: reserved [172] */
1356 printf("User area write protection register"
1357 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1358 /* A441]: reserved [170] */
1359 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1360 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001361
1362 reg = ext_csd[EXT_CSD_WR_REL_SET];
1363 const char * const fast = "existing data is at risk if a power "
1364 "failure occurs during a write operation";
1365 const char * const reliable = "the device protects existing "
1366 "data if a power failure occurs during a write "
1367 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001368 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001369 " [WR_REL_SET]: 0x%02x\n", reg);
1370
1371 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1372 int i;
1373 for (i = 1; i <= 4; i++) {
1374 printf(" partition %d: %s\n", i,
1375 reg & (1<<i) ? reliable : fast);
1376 }
1377
1378 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001379 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001380 " [WR_REL_PARAM]: 0x%02x\n", reg);
1381 if (reg & 0x01)
1382 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1383 if (reg & 0x04)
1384 printf(" Device supports the enhanced def. of reliable "
1385 "write\n");
1386
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001387 /* sanitize_start ext_csd[165]]: not readable
1388 * bkops_start ext_csd[164]]: only writable */
1389 printf("Enable background operations handshake"
1390 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1391 printf("H/W reset function"
1392 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1393 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001394 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001395 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1396 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001397 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001398 printf(" Device support partitioning feature\n");
1399 else
1400 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001401 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001402 printf(" Device can have enhanced tech.\n");
1403 else
1404 printf(" Device cannot have enhanced tech.\n");
1405
Oliver Metz11f2cea2013-09-23 08:40:52 +02001406 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001407 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1408 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1409
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001410 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001411 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001412 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1413 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001414 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001415
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001416 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001417 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001418 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001419 printf("Partitioning Setting"
1420 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001421 reg);
1422 if (reg)
1423 printf(" Device partition setting complete\n");
1424 else
1425 printf(" Device partition setting NOT complete\n");
1426
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001427 printf("General Purpose Partition Size\n"
1428 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1429 (ext_csd[153] << 8) | ext_csd[152]);
1430 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1431 (ext_csd[150] << 8) | ext_csd[149]);
1432 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1433 (ext_csd[147] << 8) | ext_csd[146]);
1434 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1435 (ext_csd[144] << 8) | ext_csd[143]);
1436
Oliver Metz11f2cea2013-09-23 08:40:52 +02001437 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001438 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1439 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001440 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001441 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1442 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001443 get_hc_erase_grp_size(ext_csd) *
1444 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001445
Oliver Metz11f2cea2013-09-23 08:40:52 +02001446 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001447 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1448 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1449 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001450 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001451 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001452 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001453 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001454
1455 /* A441]: reserved [135] */
1456 printf("Bad Block Management mode"
1457 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1458 /* A441: reserved [133:0] */
1459 }
1460 /* B45 */
1461 if (ext_csd_rev >= 6) {
1462 int j;
1463 /* tcase_support ext_csd[132] not readable */
1464 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1465 ext_csd[131]);
1466 printf("Program CID/CSD in DDR mode support"
1467 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1468 ext_csd[130]);
1469
1470 for (j = 127; j >= 64; j--)
1471 printf("Vendor Specific Fields"
1472 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1473 j, ext_csd[j]);
1474
Gwendal Grignoue966e672014-07-07 14:03:13 -07001475 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001476 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001477 reg);
1478 if (reg == 0x00)
1479 printf(" i.e. 512 B\n");
1480 else if (reg == 0x01)
1481 printf(" i.e. 4 KiB\n");
1482 else
1483 printf(" i.e. Reserved\n");
1484
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001485 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1486 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001487 reg = ext_csd[61];
1488 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1489 if (reg == 0x00)
1490 printf(" i.e. 512 B\n");
1491 else if (reg == 0x01)
1492 printf(" i.e. 4 KiB\n");
1493 else
1494 printf(" i.e. Reserved\n");
1495
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001496 printf("1st initialization after disabling sector"
1497 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1498 ext_csd[60]);
1499 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1500 ext_csd[59]);
1501 printf("Number of addressed group to be Released"
1502 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1503 printf("Exception events control"
1504 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1505 (ext_csd[57] << 8) | ext_csd[56]);
1506 printf("Exception events status"
1507 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1508 (ext_csd[55] << 8) | ext_csd[54]);
1509 printf("Extended Partitions Attribute"
1510 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1511 (ext_csd[53] << 8) | ext_csd[52]);
1512
1513 for (j = 51; j >= 37; j--)
1514 printf("Context configuration"
1515 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1516
1517 printf("Packed command status"
1518 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1519 printf("Packed command failure index"
1520 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1521 printf("Power Off Notification"
1522 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001523 printf("Control to turn the Cache ON/OFF"
1524 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001525 /* flush_cache ext_csd[32] not readable */
1526 /*Reserved [31:0] */
1527 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001528 if (ext_csd_rev >= 7) {
1529 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1530 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1531 ext_csd[29]);
1532
1533 reg = ext_csd[26];
1534 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1535 switch (reg) {
1536 case 0x00:
1537 printf(" Success\n");
1538 break;
1539 case 0x10:
1540 printf(" General error\n");
1541 break;
1542 case 0x11:
1543 printf(" Firmware install error\n");
1544 break;
1545 case 0x12:
1546 printf(" Error in downloading firmware\n");
1547 break;
1548 default:
1549 printf(" Reserved\n");
1550 }
1551 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1552 " %d sector size\n",
1553 get_word_from_ext_csd(&ext_csd[22]));
1554 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1555 " %d sector size\n",
1556 get_word_from_ext_csd(&ext_csd[18]));
1557 printf("Product state awareness enablement"
1558 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1559 ext_csd[17]);
1560 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1561 ext_csd[16]);
1562 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001563
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05001564 if (ext_csd_rev >= 7) {
1565 printf("eMMC Firmware Version: %s\n",
1566 (char*)&ext_csd[EXT_CSD_FIRMWARE_VERSION]);
1567 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001568out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001569 return ret;
1570}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001571
Nick Sanders9d57aa72014-03-05 21:38:54 -08001572int do_dump_extcsd(int nargs, char **argv)
1573{
1574 __u8 ext_csd[EXT_CSD_SIZE];
1575 int fd, ret;
1576 char *device;
1577 int i, j;
1578
1579 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1580 exit(1));
1581
1582 device = argv[1];
1583
1584 fd = open(device, O_RDWR);
1585 if (fd < 0) {
1586 perror("Failed to open mmc device");
1587 exit(1);
1588 }
1589
1590 ret = read_extcsd(fd, ext_csd);
1591 if (ret) {
1592 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1593 exit(1);
1594 }
1595
1596 /* Dump all bytes so that any undecoded or proprietary registers */
1597 /* can be acessed. */
1598 printf("EXT_CSD binary dump:\n");
1599 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1600 printf(" %3d: %3x: ", i, i);
1601 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1602 printf(" %02x", ext_csd[i+j]);
1603 }
1604 printf("\n");
1605 }
1606
1607 return ret;
1608}
1609
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001610int do_sanitize(int nargs, char **argv)
1611{
1612 int fd, ret;
1613 char *device;
1614
1615 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1616 exit(1));
1617
1618 device = argv[1];
1619
1620 fd = open(device, O_RDWR);
1621 if (fd < 0) {
1622 perror("open");
1623 exit(1);
1624 }
1625
1626 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1627 if (ret) {
1628 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1629 1, EXT_CSD_SANITIZE_START, device);
1630 exit(1);
1631 }
1632
1633 return ret;
1634
1635}
1636
Julius Wernerbcc3e2e2016-04-21 16:53:02 -07001637enum blockprotect_mode {
1638 BLOCKPROTECT_TEMPORARY = 0,
1639 BLOCKPROTECT_POWERON,
1640 BLOCKPROTECT_PERMANENT,
1641};
1642
1643int write_blockprotect(int fd, __u32 sector, int enable)
1644{
1645 struct mmc_ioc_cmd cmd;
1646 int ret;
1647
1648 memset(&cmd, 0, sizeof(cmd));
1649 cmd.write_flag = 1;
1650 cmd.opcode = enable ? MMC_SET_WRITE_PROT : MMC_CLR_WRITE_PROT;
1651 cmd.arg = sector;
1652 cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
1653
1654 ret = ioctl(fd, MMC_IOC_CMD, &cmd);
1655 if (ret)
1656 perror("SET/CLR_WRITE_PROT command");
1657 return ret;
1658}
1659
1660int do_blockprotect_enable(int nargs, char **argv)
1661{
1662 __u8 ext_csd[EXT_CSD_SIZE];
1663 __u8 user_wp;
1664 __u32 sector;
1665 char *end;
1666 int ret, fd;
1667 int arg_index = 0;
1668 enum blockprotect_mode mode = BLOCKPROTECT_TEMPORARY;
1669
1670 if (nargs > 0 && !strcmp(argv[1], "-r")) {
1671 arg_index++;
1672 mode = BLOCKPROTECT_POWERON;
1673 } else if (nargs > 0 && !strcmp(argv[1], "-p")) {
1674 arg_index++;
1675 mode = BLOCKPROTECT_PERMANENT;
1676 }
1677
1678 CHECK(nargs != 3 + arg_index, "Usage: mmc blockprotect enable [-p|-r] <device> <write protect block>\n", exit(1));
1679
1680 sector = strtoul(argv[2 + arg_index], &end, 0);
1681 if (*end != '\0') {
1682 fprintf(stderr, "Not a block number: %s\n",
1683 argv[2 + arg_index]);
1684 exit(1);
1685 }
1686
1687 fd = open(argv[1 + arg_index], O_RDWR);
1688 if (fd < 0) {
1689 perror("open");
1690 exit(1);
1691 }
1692
1693 if (read_extcsd(fd, ext_csd))
1694 exit(1);
1695
1696 user_wp = ext_csd[EXT_CSD_USER_WP];
1697 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
1698 if (mode == BLOCKPROTECT_POWERON)
1699 user_wp |= EXT_CSD_US_PWR_WP_EN;
1700 else if (mode == BLOCKPROTECT_PERMANENT)
1701 user_wp |= EXT_CSD_US_PERM_WP_EN;
1702
1703 ret = write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp);
1704 if (ret) {
1705 perror("update EXT_CSD[USER_WP]");
1706 exit(1);
1707 }
1708
1709 usleep(INTER_COMMAND_GAP_US);
1710
1711 ret = write_blockprotect(fd, sector, 1);
1712
1713 usleep(INTER_COMMAND_GAP_US);
1714
1715 user_wp &= ~(EXT_CSD_US_PERM_WP_EN | EXT_CSD_US_PWR_WP_EN);
1716 if (write_extcsd_value(fd, EXT_CSD_USER_WP, user_wp)) {
1717 perror("reset EXT_CSD[USER_WP]");
1718 if (!ret)
1719 ret = -1;
1720 }
1721
1722 return ret;
1723}
1724
1725int do_blockprotect_disable(int nargs, char **argv)
1726{
1727 __u32 sector;
1728 char *end;
1729 int fd;
1730
1731 CHECK(nargs != 3, "Usage: mmc blockprotect disable <device> <write protect block>\n", exit(1));
1732
1733 sector = strtoul(argv[2], &end, 0);
1734 if (*end != '\0') {
1735 fprintf(stderr, "Not a block number: %s\n", argv[2]);
1736 exit(1);
1737 }
1738
1739
1740 fd = open(argv[1], O_RDWR);
1741 if (fd < 0) {
1742 perror("open");
1743 exit(1);
1744 }
1745
1746 return write_blockprotect(fd, sector, 0);
1747}
1748
1749int do_blockprotect_read(int nargs, char **argv)
1750{
1751 __u8 wp_bits[8];
1752 __u32 sector;
1753 char *end;
1754 int fd;
1755 struct mmc_ioc_cmd cmd;
1756
1757 CHECK(nargs != 3, "Usage: mmc blockprotect read <device> <write protect block>\n", exit(1));
1758
1759 fd = open(argv[1], O_RDWR);
1760 if (fd < 0) {
1761 perror("open");
1762 exit(1);
1763 }
1764
1765 sector = strtoul(argv[2], &end, 0);
1766 if (*end != '\0') {
1767 fprintf(stderr, "Not a block number: %s\n", argv[2]);
1768 exit(1);
1769 }
1770
1771 memset(&cmd, 0, sizeof(cmd));
1772 cmd.write_flag = 0;
1773 cmd.opcode = MMC_SEND_WRITE_PROT_TYPE;
1774 cmd.arg = sector;
1775 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1776 cmd.blksz = sizeof(wp_bits);
1777 cmd.blocks = 1;
1778 mmc_ioc_cmd_set_data(cmd, wp_bits);
1779
1780 if (ioctl(fd, MMC_IOC_CMD, &cmd)) {
1781 perror("SEND_WRITE_PROT_TYPE command");
1782 exit(1);
1783 }
1784
1785 printf("Sector %u write protection: ", sector);
1786 switch (wp_bits[7] & 3) {
1787 case 0:
1788 printf("NONE\n");
1789 break;
1790 case 1:
1791 printf("TEMPORARY\n");
1792 break;
1793 case 2:
1794 printf("POWER-ON\n");
1795 break;
1796 case 3:
1797 printf("PERMANENT\n");
1798 break;
1799 }
1800
1801 return 0;
1802}
1803
1804int do_blockprotect_info(int nargs, char **argv)
1805{
1806 __u8 ext_csd[EXT_CSD_SIZE];
1807 __u8 user_wp;
1808 int fd, wp_sz, erase_sz;
1809
1810 CHECK(nargs != 2, "Usage: mmc blockprotect info <device>\n", exit(1));
1811
1812 fd = open(argv[1], O_RDWR);
1813 if (fd < 0) {
1814 perror("open");
1815 exit(1);
1816 }
1817
1818 if (read_extcsd(fd, ext_csd))
1819 exit(1);
1820
1821 if (ext_csd[EXT_CSD_CLASS_6_CTRL] != 0) {
1822 fprintf(stderr, "Block protection commands not supported: "
1823 "CLASS_6_CTRL set.\n");
1824 exit(1);
1825 }
1826
1827 if ((ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1) != 0x1) {
1828 fprintf(stderr, "Block protection commands not supported: "
1829 "high-capacity sizes not enabled.\n");
1830 exit(1);
1831 }
1832
1833 wp_sz = get_hc_wp_grp_size(ext_csd);
1834 erase_sz = get_hc_erase_grp_size(ext_csd);
1835
1836 if (erase_sz == 0 || wp_sz == 0) {
1837 fprintf(stderr, "Block protection commands not supported: "
1838 "no high-capacity size for erase or WP blocks.\n");
1839 exit(1);
1840 }
1841
1842 printf("Write protect block size in sectors: %d\n",
1843 erase_sz * wp_sz * 1024);
1844
1845 user_wp = ext_csd[EXT_CSD_USER_WP];
1846 printf("Permanent write protection: %s\n",
1847 user_wp & EXT_CSD_US_PERM_WP_DIS ? "forbidden" : "allowed");
1848 printf("Power-on write protection: %s\n",
1849 user_wp & EXT_CSD_US_PWR_WP_DIS ? "forbidden" : "allowed");
1850
1851 return 0;
1852}
1853
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001854static const char* const mmc_ffu_hack_names[] = {
1855 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1856};
1857
Gwendal Grignou771984c2014-07-01 12:46:18 -07001858int do_emmc50_ffu (int nargs, char **argv)
1859{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001860 int fd, ret, i, argc=1, ffu_hack=0;
1861 char *device, *type, *path;
1862 __u64 value;
1863 union {
1864 __u8 data[FFU_DATA_SIZE];
1865 struct mmc_ffu_args ffu_args;
1866 } ffu_data;
1867 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001868 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001869
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001870 while (!strcmp("-k", argv[argc])) {
1871 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1872 if (ret < 1) {
1873 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1874 exit(1);
1875 }
1876 for (i = 0; i < MMC_HACK_LEN; i++) {
1877 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1878 ffu_args->hack[ffu_hack].type = i;
1879 if (ret == 2) {
1880 ffu_args->hack[ffu_hack].value = value;
1881 }
1882 ffu_hack++;
1883 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1884 sizeof(struct mmc_ffu_args) >
1885 FFU_DATA_SIZE) {
1886 fprintf(stderr, "Too many %d hacks",
1887 ffu_hack);
1888 exit(1);
1889 }
1890 break;
1891 }
1892 }
1893 if (i == MMC_HACK_LEN) {
1894 fprintf(stderr, "Hack type %s not found\n", type);
1895 fprintf(stderr, "Supported types are: ");
1896 for (i = 0; i < MMC_HACK_LEN; i++)
1897 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1898 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001899
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001900 exit(1);
1901 }
1902 free(type);
1903 argc++;
1904 }
1905 ffu_args->hack_nb = ffu_hack;
1906
1907 path = argv[argc++];
1908 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001909 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1910 exit(1);
1911 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001912 strcpy(ffu_args->name, path);
1913 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001914 fd = open(device, O_RDWR);
1915 if (fd < 0) {
1916 perror("open");
1917 exit(1);
1918 }
1919
Gwendal Grignou0f757342014-10-16 16:52:46 -07001920 /* prepare and send ioctl */
1921 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1922 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001923 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001924 mmc_ioc_cmd.blocks = 1;
1925 mmc_ioc_cmd.arg = 0;
1926 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1927 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001928 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001929 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001930 if (ret) {
1931 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1932 exit(1);
1933 }
1934
1935 close(fd);
1936 return 0;
1937}
1938
Roman Peniaev023cc7c2014-08-12 23:25:45 +09001939#define DO_IO(func, fd, buf, nbyte) \
1940 ({ \
1941 ssize_t ret = 0, r; \
1942 do { \
1943 r = func(fd, buf + ret, nbyte - ret); \
1944 if (r < 0 && errno != EINTR) { \
1945 ret = -1; \
1946 break; \
1947 } \
1948 else if (r > 0) \
1949 ret += r; \
1950 } while (r != 0 && (size_t)ret != nbyte); \
1951 \
1952 ret; \
1953 })
1954
1955enum rpmb_op_type {
1956 MMC_RPMB_WRITE_KEY = 0x01,
1957 MMC_RPMB_READ_CNT = 0x02,
1958 MMC_RPMB_WRITE = 0x03,
1959 MMC_RPMB_READ = 0x04,
1960
1961 /* For internal usage only, do not use it directly */
1962 MMC_RPMB_READ_RESP = 0x05
1963};
1964
1965struct rpmb_frame {
1966 u_int8_t stuff[196];
1967 u_int8_t key_mac[32];
1968 u_int8_t data[256];
1969 u_int8_t nonce[16];
1970 u_int32_t write_counter;
1971 u_int16_t addr;
1972 u_int16_t block_count;
1973 u_int16_t result;
1974 u_int16_t req_resp;
1975};
1976
1977/* Performs RPMB operation.
1978 *
1979 * @fd: RPMB device on which we should perform ioctl command
1980 * @frame_in: input RPMB frame, should be properly inited
1981 * @frame_out: output (result) RPMB frame. Caller is responsible for checking
1982 * result and req_resp for output frame.
1983 * @out_cnt: count of outer frames. Used only for multiple blocks reading,
1984 * in the other cases -EINVAL will be returned.
1985 */
1986static int do_rpmb_op(int fd,
1987 const struct rpmb_frame *frame_in,
1988 struct rpmb_frame *frame_out,
1989 unsigned int out_cnt)
1990{
1991 int err;
1992 u_int16_t rpmb_type;
1993
1994 struct mmc_ioc_cmd ioc = {
1995 .arg = 0x0,
1996 .blksz = 512,
1997 .blocks = 1,
1998 .write_flag = 1,
1999 .opcode = MMC_WRITE_MULTIPLE_BLOCK,
2000 .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
2001 .data_ptr = (uintptr_t)frame_in
2002 };
2003
2004 if (!frame_in || !frame_out || !out_cnt)
2005 return -EINVAL;
2006
2007 rpmb_type = be16toh(frame_in->req_resp);
2008
2009 switch(rpmb_type) {
2010 case MMC_RPMB_WRITE:
2011 case MMC_RPMB_WRITE_KEY:
2012 if (out_cnt != 1) {
2013 err = -EINVAL;
2014 goto out;
2015 }
2016
2017 /* Write request */
2018 ioc.write_flag |= (1<<31);
2019 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2020 if (err < 0) {
2021 err = -errno;
2022 goto out;
2023 }
2024
2025 /* Result request */
2026 memset(frame_out, 0, sizeof(*frame_out));
2027 frame_out->req_resp = htobe16(MMC_RPMB_READ_RESP);
2028 ioc.write_flag = 1;
2029 ioc.data_ptr = (uintptr_t)frame_out;
2030 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2031 if (err < 0) {
2032 err = -errno;
2033 goto out;
2034 }
2035
2036 /* Get response */
2037 ioc.write_flag = 0;
2038 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2039 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2040 if (err < 0) {
2041 err = -errno;
2042 goto out;
2043 }
2044
2045 break;
2046 case MMC_RPMB_READ_CNT:
2047 if (out_cnt != 1) {
2048 err = -EINVAL;
2049 goto out;
2050 }
2051 /* fall through */
2052
2053 case MMC_RPMB_READ:
2054 /* Request */
2055 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2056 if (err < 0) {
2057 err = -errno;
2058 goto out;
2059 }
2060
2061 /* Get response */
2062 ioc.write_flag = 0;
2063 ioc.opcode = MMC_READ_MULTIPLE_BLOCK;
2064 ioc.blocks = out_cnt;
2065 ioc.data_ptr = (uintptr_t)frame_out;
2066 err = ioctl(fd, MMC_IOC_CMD, &ioc);
2067 if (err < 0) {
2068 err = -errno;
2069 goto out;
2070 }
2071
2072 break;
2073 default:
2074 err = -EINVAL;
2075 goto out;
2076 }
2077
2078out:
2079 return err;
2080}
2081
2082int do_rpmb_write_key(int nargs, char **argv)
2083{
2084 int ret, dev_fd, key_fd;
2085 struct rpmb_frame frame_in = {
2086 .req_resp = htobe16(MMC_RPMB_WRITE_KEY)
2087 }, frame_out;
2088
2089 CHECK(nargs != 3, "Usage: mmc rpmb write-key </path/to/mmcblkXrpmb> </path/to/key>\n",
2090 exit(1));
2091
2092 dev_fd = open(argv[1], O_RDWR);
2093 if (dev_fd < 0) {
2094 perror("device open");
2095 exit(1);
2096 }
2097
2098 if (0 == strcmp(argv[2], "-"))
2099 key_fd = STDIN_FILENO;
2100 else {
2101 key_fd = open(argv[2], O_RDONLY);
2102 if (key_fd < 0) {
2103 perror("can't open key file");
2104 exit(1);
2105 }
2106 }
2107
2108 /* Read the auth key */
2109 ret = DO_IO(read, key_fd, frame_in.key_mac, sizeof(frame_in.key_mac));
2110 if (ret < 0) {
2111 perror("read the key");
2112 exit(1);
2113 } else if (ret != sizeof(frame_in.key_mac)) {
2114 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2115 (unsigned long)sizeof(frame_in.key_mac),
2116 ret);
2117 exit(1);
2118 }
2119
2120 /* Execute RPMB op */
2121 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2122 if (ret != 0) {
2123 perror("RPMB ioctl failed");
2124 exit(1);
2125 }
2126
2127 /* Check RPMB response */
2128 if (frame_out.result != 0) {
2129 printf("RPMB operation failed, retcode 0x%04x\n",
2130 be16toh(frame_out.result));
2131 exit(1);
2132 }
2133
2134 close(dev_fd);
2135 if (key_fd != STDIN_FILENO)
2136 close(key_fd);
2137
2138 return ret;
2139}
2140
2141int rpmb_read_counter(int dev_fd, unsigned int *cnt)
2142{
2143 int ret;
2144 struct rpmb_frame frame_in = {
2145 .req_resp = htobe16(MMC_RPMB_READ_CNT)
2146 }, frame_out;
2147
2148 /* Execute RPMB op */
2149 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2150 if (ret != 0) {
2151 perror("RPMB ioctl failed");
2152 exit(1);
2153 }
2154
2155 /* Check RPMB response */
2156 if (frame_out.result != 0)
2157 return be16toh(frame_out.result);
2158
2159 *cnt = be32toh(frame_out.write_counter);
2160
2161 return 0;
2162}
2163
2164int do_rpmb_read_counter(int nargs, char **argv)
2165{
2166 int ret, dev_fd;
2167 unsigned int cnt;
2168
2169 CHECK(nargs != 2, "Usage: mmc rpmb read-counter </path/to/mmcblkXrpmb>\n",
2170 exit(1));
2171
2172 dev_fd = open(argv[1], O_RDWR);
2173 if (dev_fd < 0) {
2174 perror("device open");
2175 exit(1);
2176 }
2177
2178 ret = rpmb_read_counter(dev_fd, &cnt);
2179
2180 /* Check RPMB response */
2181 if (ret != 0) {
2182 printf("RPMB operation failed, retcode 0x%04x\n", ret);
2183 exit(1);
2184 }
2185
2186 close(dev_fd);
2187
2188 printf("Counter value: 0x%08x\n", cnt);
2189
2190 return ret;
2191}
2192
2193int do_rpmb_read_block(int nargs, char **argv)
2194{
2195 int i, ret, dev_fd, data_fd, key_fd = -1;
2196 uint16_t addr, blocks_cnt;
2197 unsigned char key[32];
2198 struct rpmb_frame frame_in = {
2199 .req_resp = htobe16(MMC_RPMB_READ),
2200 }, *frame_out_p;
2201
2202 CHECK(nargs != 5 && nargs != 6, "Usage: mmc rpmb read-block </path/to/mmcblkXrpmb> <address> <blocks count> </path/to/output_file> [/path/to/key]\n",
2203 exit(1));
2204
2205 dev_fd = open(argv[1], O_RDWR);
2206 if (dev_fd < 0) {
2207 perror("device open");
2208 exit(1);
2209 }
2210
2211 /* Get block address */
2212 errno = 0;
2213 addr = strtol(argv[2], NULL, 0);
2214 if (errno) {
2215 perror("incorrect address");
2216 exit(1);
2217 }
2218 frame_in.addr = htobe16(addr);
2219
2220 /* Get blocks count */
2221 errno = 0;
2222 blocks_cnt = strtol(argv[3], NULL, 0);
2223 if (errno) {
2224 perror("incorrect blocks count");
2225 exit(1);
2226 }
2227
2228 if (!blocks_cnt) {
2229 printf("please, specify valid blocks count number\n");
2230 exit(1);
2231 }
2232
2233 frame_out_p = calloc(sizeof(*frame_out_p), blocks_cnt);
2234 if (!frame_out_p) {
2235 printf("can't allocate memory for RPMB outer frames\n");
2236 exit(1);
2237 }
2238
2239 /* Write 256b data */
2240 if (0 == strcmp(argv[4], "-"))
2241 data_fd = STDOUT_FILENO;
2242 else {
2243 data_fd = open(argv[4], O_WRONLY | O_CREAT | O_APPEND,
2244 S_IRUSR | S_IWUSR);
2245 if (data_fd < 0) {
2246 perror("can't open output file");
2247 exit(1);
2248 }
2249 }
2250
2251 /* Key is specified */
2252 if (nargs == 6) {
2253 if (0 == strcmp(argv[5], "-"))
2254 key_fd = STDIN_FILENO;
2255 else {
2256 key_fd = open(argv[5], O_RDONLY);
2257 if (key_fd < 0) {
2258 perror("can't open input key file");
2259 exit(1);
2260 }
2261 }
2262
2263 ret = DO_IO(read, key_fd, key, sizeof(key));
2264 if (ret < 0) {
2265 perror("read the key data");
2266 exit(1);
2267 } else if (ret != sizeof(key)) {
2268 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2269 (unsigned long)sizeof(key),
2270 ret);
2271 exit(1);
2272 }
2273 }
2274
2275 /* Execute RPMB op */
2276 ret = do_rpmb_op(dev_fd, &frame_in, frame_out_p, blocks_cnt);
2277 if (ret != 0) {
2278 perror("RPMB ioctl failed");
2279 exit(1);
2280 }
2281
2282 /* Check RPMB response */
2283 if (frame_out_p[blocks_cnt - 1].result != 0) {
2284 printf("RPMB operation failed, retcode 0x%04x\n",
2285 be16toh(frame_out_p[blocks_cnt - 1].result));
2286 exit(1);
2287 }
2288
2289 /* Do we have to verify data against key? */
2290 if (nargs == 6) {
2291 unsigned char mac[32];
2292 hmac_sha256_ctx ctx;
2293 struct rpmb_frame *frame_out = NULL;
2294
2295 hmac_sha256_init(&ctx, key, sizeof(key));
2296 for (i = 0; i < blocks_cnt; i++) {
2297 frame_out = &frame_out_p[i];
2298 hmac_sha256_update(&ctx, frame_out->data,
2299 sizeof(*frame_out) -
2300 offsetof(struct rpmb_frame, data));
2301 }
2302
2303 hmac_sha256_final(&ctx, mac, sizeof(mac));
2304
2305 /* Impossible */
2306 assert(frame_out);
2307
2308 /* Compare calculated MAC and MAC from last frame */
2309 if (memcmp(mac, frame_out->key_mac, sizeof(mac))) {
2310 printf("RPMB MAC missmatch\n");
2311 exit(1);
2312 }
2313 }
2314
2315 /* Write data */
2316 for (i = 0; i < blocks_cnt; i++) {
2317 struct rpmb_frame *frame_out = &frame_out_p[i];
2318 ret = DO_IO(write, data_fd, frame_out->data, sizeof(frame_out->data));
2319 if (ret < 0) {
2320 perror("write the data");
2321 exit(1);
2322 } else if (ret != sizeof(frame_out->data)) {
2323 printf("Data must be %lu bytes length, but we wrote only %d, exit\n",
2324 (unsigned long)sizeof(frame_out->data),
2325 ret);
2326 exit(1);
2327 }
2328 }
2329
2330 free(frame_out_p);
2331 close(dev_fd);
2332 if (data_fd != STDOUT_FILENO)
2333 close(data_fd);
2334 if (key_fd != -1 && key_fd != STDIN_FILENO)
2335 close(key_fd);
2336
2337 return ret;
2338}
2339
2340int do_rpmb_write_block(int nargs, char **argv)
2341{
2342 int ret, dev_fd, key_fd, data_fd;
2343 unsigned char key[32];
2344 uint16_t addr;
2345 unsigned int cnt;
2346 struct rpmb_frame frame_in = {
2347 .req_resp = htobe16(MMC_RPMB_WRITE),
2348 .block_count = htobe16(1)
2349 }, frame_out;
2350
2351 CHECK(nargs != 5, "Usage: mmc rpmb write-block </path/to/mmcblkXrpmb> <address> </path/to/input_file> </path/to/key>\n",
2352 exit(1));
2353
2354 dev_fd = open(argv[1], O_RDWR);
2355 if (dev_fd < 0) {
2356 perror("device open");
2357 exit(1);
2358 }
2359
2360 ret = rpmb_read_counter(dev_fd, &cnt);
2361 /* Check RPMB response */
2362 if (ret != 0) {
2363 printf("RPMB read counter operation failed, retcode 0x%04x\n", ret);
2364 exit(1);
2365 }
2366 frame_in.write_counter = htobe32(cnt);
2367
2368 /* Get block address */
2369 errno = 0;
2370 addr = strtol(argv[2], NULL, 0);
2371 if (errno) {
2372 perror("incorrect address");
2373 exit(1);
2374 }
2375 frame_in.addr = htobe16(addr);
2376
2377 /* Read 256b data */
2378 if (0 == strcmp(argv[3], "-"))
2379 data_fd = STDIN_FILENO;
2380 else {
2381 data_fd = open(argv[3], O_RDONLY);
2382 if (data_fd < 0) {
2383 perror("can't open input file");
2384 exit(1);
2385 }
2386 }
2387
2388 ret = DO_IO(read, data_fd, frame_in.data, sizeof(frame_in.data));
2389 if (ret < 0) {
2390 perror("read the data");
2391 exit(1);
2392 } else if (ret != sizeof(frame_in.data)) {
2393 printf("Data must be %lu bytes length, but we read only %d, exit\n",
2394 (unsigned long)sizeof(frame_in.data),
2395 ret);
2396 exit(1);
2397 }
2398
2399 /* Read the auth key */
2400 if (0 == strcmp(argv[4], "-"))
2401 key_fd = STDIN_FILENO;
2402 else {
2403 key_fd = open(argv[4], O_RDONLY);
2404 if (key_fd < 0) {
2405 perror("can't open key file");
2406 exit(1);
2407 }
2408 }
2409
2410 ret = DO_IO(read, key_fd, key, sizeof(key));
2411 if (ret < 0) {
2412 perror("read the key");
2413 exit(1);
2414 } else if (ret != sizeof(key)) {
2415 printf("Auth key must be %lu bytes length, but we read only %d, exit\n",
2416 (unsigned long)sizeof(key),
2417 ret);
2418 exit(1);
2419 }
2420
2421 /* Calculate HMAC SHA256 */
2422 hmac_sha256(
2423 key, sizeof(key),
2424 frame_in.data, sizeof(frame_in) - offsetof(struct rpmb_frame, data),
2425 frame_in.key_mac, sizeof(frame_in.key_mac));
2426
2427 /* Execute RPMB op */
2428 ret = do_rpmb_op(dev_fd, &frame_in, &frame_out, 1);
2429 if (ret != 0) {
2430 perror("RPMB ioctl failed");
2431 exit(1);
2432 }
2433
2434 /* Check RPMB response */
2435 if (frame_out.result != 0) {
2436 printf("RPMB operation failed, retcode 0x%04x\n",
2437 be16toh(frame_out.result));
2438 exit(1);
2439 }
2440
2441 close(dev_fd);
2442 if (data_fd != STDIN_FILENO)
2443 close(data_fd);
2444 if (key_fd != STDIN_FILENO)
2445 close(key_fd);
2446
2447 return ret;
2448}
Al Cooper786418c2015-04-29 18:12:35 -04002449
2450int do_cache_ctrl(int value, int nargs, char **argv)
2451{
2452 __u8 ext_csd[512];
2453 int fd, ret;
2454 char *device;
2455
2456 CHECK(nargs != 2, "Usage: mmc cache enable </path/to/mmcblkX>\n",
2457 exit(1));
2458
2459 device = argv[1];
2460
2461 fd = open(device, O_RDWR);
2462 if (fd < 0) {
2463 perror("open");
2464 exit(1);
2465 }
2466
2467 ret = read_extcsd(fd, ext_csd);
2468 if (ret) {
2469 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2470 exit(1);
2471 }
2472
2473 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V4_5) {
2474 fprintf(stderr,
2475 "The CACHE option is only availabe on devices >= "
2476 "MMC 4.5 %s\n", device);
2477 exit(1);
2478 }
2479
2480 /* If the cache size is zero, this device does not have a cache */
2481 if (!(ext_csd[EXT_CSD_CACHE_SIZE_3] ||
2482 ext_csd[EXT_CSD_CACHE_SIZE_2] ||
2483 ext_csd[EXT_CSD_CACHE_SIZE_1] ||
2484 ext_csd[EXT_CSD_CACHE_SIZE_0])) {
2485 fprintf(stderr,
2486 "The CACHE option is not available on %s\n",
2487 device);
2488 exit(1);
2489 }
2490 ret = write_extcsd_value(fd, EXT_CSD_CACHE_CTRL, value);
2491 if (ret) {
2492 fprintf(stderr,
2493 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
2494 value, EXT_CSD_CACHE_CTRL, device);
2495 exit(1);
2496 }
2497
2498 return ret;
2499}
2500
2501int do_cache_en(int nargs, char **argv)
2502{
2503 return do_cache_ctrl(1, nargs, argv);
2504}
2505
2506int do_cache_dis(int nargs, char **argv)
2507{
2508 return do_cache_ctrl(0, nargs, argv);
2509}
Avi Shchislowskidc7ab962016-03-08 14:22:41 -05002510
2511int do_ffu(int nargs, char **argv)
2512{
2513#ifndef MMC_IOC_MULTI_CMD
2514 fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
2515 " support, needed by FFU.\n");
2516 exit(1);
2517#else
2518 int dev_fd, img_fd;
2519 int sect_done = 0, retry = 3, ret = -EINVAL;
2520 unsigned int sect_size;
2521 __u8 ext_csd[EXT_CSD_SIZE];
2522 __u8 *buf;
2523 __u32 arg;
2524 off_t fw_size;
2525 ssize_t chunk_size;
2526 char *device;
2527 struct mmc_ioc_multi_cmd *multi_cmd;
2528
2529 CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n",
2530 exit(1));
2531
2532 device = argv[2];
2533 dev_fd = open(device, O_RDWR);
2534 if (dev_fd < 0) {
2535 perror("device open failed");
2536 exit(1);
2537 }
2538 img_fd = open(argv[1], O_RDONLY);
2539 if (img_fd < 0) {
2540 perror("image open failed");
2541 close(dev_fd);
2542 exit(1);
2543 }
2544
2545 buf = malloc(512);
2546 multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
2547 3 * sizeof(struct mmc_ioc_cmd));
2548 if (!buf || !multi_cmd) {
2549 perror("failed to allocate memory");
2550 goto out;
2551 }
2552
2553 ret = read_extcsd(dev_fd, ext_csd);
2554 if (ret) {
2555 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2556 goto out;
2557 }
2558
2559 if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
2560 fprintf(stderr,
2561 "The FFU feature is only available on devices >= "
2562 "MMC 5.0, not supported in %s\n", device);
2563 goto out;
2564 }
2565
2566 if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
2567 fprintf(stderr, "FFU is not supported in %s\n", device);
2568 goto out;
2569 }
2570
2571 if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
2572 fprintf(stderr, "Firmware update was disabled in %s\n", device);
2573 goto out;
2574 }
2575
2576 fw_size = lseek(img_fd, 0, SEEK_END);
2577
2578 if (fw_size == 0) {
2579 fprintf(stderr, "Firmware image is empty");
2580 goto out;
2581 }
2582
2583 sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
2584 if (fw_size % sect_size) {
2585 fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
2586 goto out;
2587 }
2588
2589 /* set CMD ARG */
2590 arg = ext_csd[EXT_CSD_FFU_ARG_0] |
2591 ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
2592 ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
2593 ext_csd[EXT_CSD_FFU_ARG_3] << 24;
2594
2595 /* prepare multi_cmd to be sent */
2596 multi_cmd->num_of_cmds = 3;
2597
2598 /* put device into ffu mode */
2599 multi_cmd->cmds[0].opcode = MMC_SWITCH;
2600 multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2601 (EXT_CSD_MODE_CONFIG << 16) |
2602 (EXT_CSD_FFU_MODE << 8) |
2603 EXT_CSD_CMD_SET_NORMAL;
2604 multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2605 multi_cmd->cmds[0].write_flag = 1;
2606
2607 /* send image chunk */
2608 multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
2609 multi_cmd->cmds[1].blksz = sect_size;
2610 multi_cmd->cmds[1].blocks = 1;
2611 multi_cmd->cmds[1].arg = arg;
2612 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
2613 multi_cmd->cmds[1].write_flag = 1;
2614 mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);
2615
2616 /* return device into normal mode */
2617 multi_cmd->cmds[2].opcode = MMC_SWITCH;
2618 multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2619 (EXT_CSD_MODE_CONFIG << 16) |
2620 (EXT_CSD_NORMAL_MODE << 8) |
2621 EXT_CSD_CMD_SET_NORMAL;
2622 multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2623 multi_cmd->cmds[2].write_flag = 1;
2624
2625do_retry:
2626 /* read firmware chunk */
2627 lseek(img_fd, 0, SEEK_SET);
2628 chunk_size = read(img_fd, buf, 512);
2629
2630 while (chunk_size > 0) {
2631 /* send ioctl with multi-cmd */
2632 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2633
2634 if (ret) {
2635 perror("Multi-cmd ioctl");
2636 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2637 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2638 goto out;
2639 }
2640
2641 ret = read_extcsd(dev_fd, ext_csd);
2642 if (ret) {
2643 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2644 goto out;
2645 }
2646
2647 /* Test if we need to restart the download */
2648 sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
2649 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
2650 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
2651 ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
2652 /* By spec, host should re-start download from the first sector if sect_done is 0 */
2653 if (sect_done == 0) {
2654 if (retry > 0) {
2655 retry--;
2656 fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
2657 goto do_retry;
2658 }
2659 fprintf(stderr, "Programming failed! Aborting...\n");
2660 goto out;
2661 } else {
2662 fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
2663 }
2664
2665 /* read the next firmware chunk (if any) */
2666 chunk_size = read(img_fd, buf, 512);
2667 }
2668
2669 if ((sect_done * sect_size) == fw_size) {
2670 fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
2671 fprintf(stderr, "Programming finished with status %d \n", ret);
2672 }
2673 else {
2674 fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
2675 goto out;
2676 }
2677
2678 /* check mode operation for ffu install*/
2679 if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
2680 fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
2681 } else {
2682 fprintf(stderr, "Installing firmware on %s...\n", device);
2683 /* Re-enter ffu mode and install the firmware */
2684 multi_cmd->num_of_cmds = 2;
2685
2686 /* set ext_csd to install mode */
2687 multi_cmd->cmds[1].opcode = MMC_SWITCH;
2688 multi_cmd->cmds[1].blksz = 0;
2689 multi_cmd->cmds[1].blocks = 0;
2690 multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
2691 (EXT_CSD_MODE_OPERATION_CODES << 16) |
2692 (EXT_CSD_FFU_INSTALL << 8) |
2693 EXT_CSD_CMD_SET_NORMAL;
2694 multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
2695 multi_cmd->cmds[1].write_flag = 1;
2696
2697 /* send ioctl with multi-cmd */
2698 ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);
2699
2700 if (ret) {
2701 perror("Multi-cmd ioctl failed setting install mode");
2702 /* In case multi-cmd ioctl failed before exiting from ffu mode */
2703 ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
2704 goto out;
2705 }
2706
2707 ret = read_extcsd(dev_fd, ext_csd);
2708 if (ret) {
2709 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
2710 goto out;
2711 }
2712
2713 /* return status */
2714 ret = ext_csd[EXT_CSD_FFU_STATUS];
2715 if (ret) {
2716 fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
2717 goto out;
2718 } else {
2719 fprintf(stderr, "FFU finished successfully\n");
2720 }
2721 }
2722
2723out:
2724 free(buf);
2725 free(multi_cmd);
2726 close(img_fd);
2727 close(dev_fd);
2728 return ret;
2729#endif
2730}