RE: [PATCH v2] mmc-utils: add eMMC 5.0 FFU support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Grant,

We will contribute shortly our FFU implementation code (RFC) , in which we are addressing both issues discussed below:

- FW File size limitation
- Atomic FFU Process

However our implementation is in both layers - kernel driver and mmc-utils.
We will be happy to get your comments.

Thanks,
Alex and Avi

> -----Original Message-----
> From: grundler@xxxxxxxxxx [mailto:grundler@xxxxxxxxxx] On Behalf Of
> Grant Grundler
> Sent: Thursday, February 06, 2014 8:41 PM
> To: Alex Lemberg
> Cc: Grant Grundler; Chris Ball; linux-mmc; Puthikorn Voravootivat; Gwendal
> Grignou; Avi Shchislowski
> Subject: Re: [PATCH v2] mmc-utils: add eMMC 5.0 FFU support
> 
> On Thu, Feb 6, 2014 at 3:57 AM, Alex Lemberg <Alex.Lemberg@xxxxxxxxxxx>
> wrote:
> > Hi Grant,
> >
> > We have few comments on the code below
> >
> >> +     fwsize = fwfilestat.st_size;
> >> +     if (fwsize > MMC_IOC_MAX_BYTES) {
> >> +             fprintf(stderr,"ERROR: %s is > %ld bytes long (max
> >> allowed)\n",
> >> +                     fwfilename, MMC_IOC_MAX_BYTES);
> >> +             goto out_fw;
> >> +     }
> >
> > By this solution, the FW file size is limited to MMC_IOC_MAX_BYTES
> (128KB???). Is there a guarantee that all vendors will have FW image file in
> size < MMC_IOC_MAX_BYTES?
> 
> MMC_IOC_MAX_BYTES is something that existed in mmc-utils before and I
> don't see any reference to a "max size" in the eMMC 5.0 spec. The eMMC
> 5.0 spec does provide an additional method to send multiple blocks that I
> haven't implemented. So I don't believe there is any particular size limit for
> firmware (longer term - mmc-utils needs more work to support >
> MMC_IOC_MAX_BYTES).
> 
> 
> >> +int do_emmc5_fw_update(int fd, __u8 *cid, __u8 *ext_csd,
> >> +                             char *emmc_fw, size_t fwsize) {
> >> +     struct mmc_ioc_cmd idata;
> >> +     int retcode = 0;
> >> +     int ret;
> >> +
> >> +     /* 2) Host send CMD6 to set MODE_CONFIG[30] = 0x01 */
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     idata.opcode = MMC_SWITCH;
> >> +        idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
> >> +                     (30 << 16) |    /* index */
> >> +                     (1 << 8) |      /* value */
> >> +                     EXT_CSD_CMD_SET_NORMAL;
> >> +     idata.flags = MMC_RSP_R1B | MMC_CMD_AC;;
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             printf("ioctl: MMC_SWITCH (eMMC 5.0 FW Update) %m\n");
> >> +             goto abort_update;
> >> +     }
> >> +
> >
> > Can we be certain that no NORMAL commands (not related to FFU) will be
> sent by the host, after switching to FFU mode?
> 
> Yes and No :)
> 
> Yes: get exclusive access to the device - e.g . boot from other media and
> then perform the update.
> 
> No: If the FFU is targeting the boot device, other commands can be sent
> when in FFU mode and FFU should gracefully fail according my
> (novice) reading of the eMMC 5.0 spec. I'm really not sure what can go
> wrong here  But early in the system boot (e.g. init hasn't gone multiple user
> yet) we can cache the FW image and be reasonably sure no other IOs will be
> issued during the FFU.
> 
> My point is there are a range of opportunities to mitigate this risk depending
> on how much trouble you want to go through WITHOUT making any
> additional changes to the tool or kernel.
> 
> > Is it possible that MMC driver will send R/W requests in the middle (Steps
> #1 - #4)?
> 
> Yes - as outlined above. This can certainly not a perfect solution - it's just the
> first one.
> 
> Long term, to make FFU more robust and secure on a "live" (ie boot) device,
> Gwendal Grignou and Kees Cook (co-workers) would like to implement an "in
> kernel" hook to guarantee the FW image is an "atomic operation" (not
> interrupted by other IO) and the firmware image is pulled from a "well
> known" location (e.g. /lib/firmware). On a chromebook, that means the
> firmware is part of a signed OS image (using dm-verity).
> 
> >
> >> +     /* 2) send CMD25 0x0000FFFF */
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     idata.write_flag = 1;
> >> +     idata.data_ptr = (__u64) ((unsigned long) emmc_fw); /* Write
> >> + this
> >> FW */
> >> +     idata.opcode = MMC_WRITE_MULTIPLE_BLOCK;
> >> +     idata.arg = 0x0000FFFF;
> >> +     idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;;
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             printf("ioctl:MMC_WRITE_MULTIPLE_BLOCK (eMMC 5.0 FW
> >> Update) %m\n");
> >> +             goto abort_update;
> >> +     }
> >> +
> >> +     /* 3) check FFU_STATUS[26] */
> >> +     ret = read_extcsd(fd, ext_csd);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             fprintf(stderr, "read_extcsd error (%d): %m\n", ret);
> >> +             goto abort_update;
> >> +     }
> >> +
> >> +     switch(ext_csd[26]) {
> >> +     case 0:  break;
> >> +     case 0x10:
> >> +             fprintf(stderr, "eMMC 5.0 FFU had general error and
> >> + failed
> >> (EIO).\n");
> >> +             retcode = -EIO;
> >> +             break;
> >> +     case 0x11:
> >> +             fprintf(stderr, "eMMC 5.0 FFU did not complete
> >> (EAGAIN).\n");
> >> +             retcode = -EAGAIN;
> >> +             break;
> >> +     case 0x12:
> >> +             fprintf(stderr, "eMMC 5.0 FFU download failed: checksum "
> >> +                     "mismatch or was interrupted (EINTR).\n");
> >> +             retcode = -EINTR;
> >> +             break;
> >> +     default:
> >> +             fprintf(stderr, "eMMC 5.0 FFU unknown error: %x\n",
> >> ext_csd[26]);
> >> +             retcode = -EIO;
> >> +     }
> >> +
> >> +     /* 4) check
> >> NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED[305-302] */
> >> +     ret = ext_csd[302] << 0 | (ext_csd[303] << 8) |
> >> +             (ext_csd[304] << 16) | (ext_csd[305] << 24);
> >> +
> >> +     /* convert that gibberish to bytes */
> >> +     ret *= 512;
> >> +     if (ext_csd[61] == 1)
> >> +             ret *= 8;       /* 4K sectors */
> >> +
> >> +     if (ret != fwsize) {
> >> +             fprintf(stderr, "eMMC 5.0 FFU Failed? (fwsize: %d,"
> >> +                     " PROGRAMMED_SECTORS: %d)\n", fwsize, ret);
> >> +     }
> >> +
> >
> > By the spec, there are two ways to install new FW. The right way should be
> set according to MODE_OPERATION_CODES field of EXT_CSD.
> 
> I'm reworking this code now based on similar feedback. If I still don't get this
> right, please please please send a patch to make it work right.  I would in no
> way be offended by anyone fixing up code I've got wrong.
> 
> >
> >> +abort_update:
> >> +     /* 4) send CMD0
> >> +      * This should reset the device and force use of the new firmware.
> >> +      */
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     idata.opcode = MMC_GO_IDLE_STATE;
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             perror("ioctl:MMC_GO_IDLE_STATE (eMMC 5.0 FFU)");
> >> +     }
> >> +
> >> +     return retcode;
> >> +}
> >
> >
> > Thanks,
> > Alex, Avi
> 
> Thanks Alex, Avi! I appreciate the review.
> 
> cheers,
> grant
> 
> >
> >
> >> -----Original Message-----
> >> From: linux-mmc-owner@xxxxxxxxxxxxxxx [mailto:linux-mmc-
> >> owner@xxxxxxxxxxxxxxx] On Behalf Of Grant Grundler
> >> Sent: Thursday, January 23, 2014 9:44 PM
> >> To: Chris Ball
> >> Cc: linux-mmc; Puthikorn Voravootivat; Gwendal Grignou; Grant
> >> Grundler
> >> Subject: [PATCH v2] mmc-utils: add eMMC 5.0 FFU support
> >>
> >> Field Firmware Update feature is new for 5.0 spec.
> >> Code written per JESD84-B50.pdf spec available from:
> >>     http://www.jedec.org/standards-documents/technology-focus-
> >> areas/flash-memory-ssds-ufs-emmc/e-mmc
> >>
> >> Change-Id: I0a22e9862876421630f53ac27fc0a161a9c70131
> >> Signed-off-by: Grant Grundler <grundler@xxxxxxxxxxxx>
> >> ---
> >> V2:
> >> Fix compiler borkage in use of strncmp().
> >> Use '\0' instead of 0 in char assignment.
> >>
> >> V1:
> >> This patch needs to be reviewed and tested by _ANY_ eMMC 5.0 HW
> vendor.
> >>
> >> If you represent an eMMC 5.0 HW vendor and expect your part will be
> >> used in a ChromeOS device, make sure FFU works with mmc-utils.
> >>
> >> mmc-utils is now shipping with ChromeOS (starting with R34 or R35
> release):
> >>     https://chromium-review.googlesource.com/#/c/182716/
> >>
> >> Please also make sure your FFU implementation works reliably and is
> >> robust (e.g. sign your binaries and check the signature).
> >>
> >> Two general trends in eMMC are driving the requirement for FFU support:
> >> 1) longer support life time for devices (Chromebooks are > 5 years).
> >> I.e. odds of requiring a bug fix later is higher.
> >> I believe this trend applies to tablets and phones as well.
> >>
> >> 2) eMMC 5.0 devices are more complex --> testing will not find all the
> bugs.
> >> e.g. TLC vs MLC vs SLC error handling _and_ implementations which
> >> support a write cache, reliable write, and the plethora of other eMMC
> features.
> >> More code means more interactions and more bugs. Fact of (SW) Life.
> >> It's no longer a question of whether code will have bugs. It's a
> >> question of how severe and can they be mitigated (FFU is just one way to
> mitigate).
> >>
> >> I've written similar FFU patches for three different eMMC 4.5 products.
> >> Some of those will get published and will conflict with this patch.
> >> I'm happy resolve those conflicts and repost.
> >> Please just make sure I'm CC'd.
> >>
> >> I'm also willing to write additional mmc-utils FFU patches for any
> >> other eMMC 4.5 vendor.  Please send technical details to my
> >> google.com email and promise to test and publish the result.
> >>
> >> Thanks for reading this far! :)
> >>
> >>
> >>  Makefile         |   4 +-
> >>  mmc.c            |  31 ++++++--
> >>  mmc.h            |  29 ++++++-
> >>  mmc_cmds-emmc5.c | 129 +++++++++++++++++++++++++++++++
> >>  mmc_cmds.c       | 232
> >> ++++++++++++++++++++++++++++++++++++++++++++-----------
> >>  mmc_cmds.h       |   6 ++
> >>  6 files changed, 378 insertions(+), 53 deletions(-)  create mode
> >> 100644 mmc_cmds-emmc5.c
> >>
> >> diff --git a/Makefile b/Makefile
> >> index 91cfc35..1cdd97f 100644
> >> --- a/Makefile
> >> +++ b/Makefile
> >> @@ -1,7 +1,7 @@
> >>  CC ?= gcc
> >>  AM_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2
> CFLAGS ?= -g
> >> -O2 -objects = mmc.o mmc_cmds.o
> >> +objects = mmc.o mmc_cmds.o mmc_cmds-emmc5.o
> >>
> >>  CHECKFLAGS = -Wall -Werror -Wuninitialized -Wundef
> >>
> >> @@ -44,7 +44,7 @@ clean:
> >>       $(MAKE) -C man clean
> >>
> >>  install: $(progs) install-man
> >> -     $(INSTALL) -m755 -d $(DESTDIR)$(bindir)
> >> +     $(INSTALL) -m 755 -d $(DESTDIR)$(bindir)
> >>       $(INSTALL) $(progs) $(DESTDIR)$(bindir)
> >>
> >>  .PHONY: all clean install manpages install-man diff --git a/mmc.c
> >> b/mmc.c index 926e92f..01a5074 100644
> >> --- a/mmc.c
> >> +++ b/mmc.c
> >> @@ -20,7 +20,10 @@
> >>  #include <stdio.h>
> >>  #include <stdlib.h>
> >>  #include <string.h>
> >> +#include <errno.h>
> >> +#include <linux/types.h>
> >>
> >> +#include "mmc.h"
> >>  #include "mmc_cmds.h"
> >>
> >>  #define MMC_VERSION  "0.1"
> >> @@ -37,9 +40,9 @@ struct Command {
> >>                                  if < 0, _minimum_ number of arguments */
> >>       char    *verb;          /* verb */
> >>       char    *help;          /* help lines; from the 2nd line onward they
> >> -                                   are automatically indented */
> >> -        char    *adv_help;      /* advanced help message; from the 2nd line
> >> -                                   onward they are automatically indented */
> >> +                                are automatically indented */
> >> +     char    *adv_help;      /* advanced help message; from the 2nd line
> >> +                                onward they are automatically
> >> + indented */
> >>
> >>       /* the following fields are run-time filled by the program */
> >>       char    **cmds;         /* array of subcommands */
> >> @@ -110,9 +113,24 @@ static struct Command commands[] = {
> >>               "Send Sanitize command to the <device>.\nThis will
> >> delete the unmapped memory region of the device.",
> >>         NULL
> >>       },
> >> +     { do_firmware_update, -2,
> >> +       "firmware upload", "<firmware_file> " "<device>\n"
> >> +             "Upload/Update firmware on <device> using
> >> <firmware_file>.\n",
> >> +       NULL
> >> +     },
> >>       { 0, 0, 0, 0 }
> >>  };
> >>
> >> +const char *manfid_lookup[0x100] = {
> >> +     [MANFID_PANASONIC] = "Panasonic",
> >> +     [MANFID_KINGSTON] = "Kingston",
> >> +     [MANFID_SANDISK] = "Sandisk",
> >> +     [MANFID_SAMSUNG] = "Samsung",
> >> +     [MANFID_TOSHIBA] = "Toshiba",
> >> +     [MANFID_SANDISK_SEM] = "Sandisk_SEM",
> >> +     [MANFID_KINGSTON_MMC] = "Kingston_MMC"
> >> +};
> >> +
> >>  static char *get_prgname(char *programname)  {
> >>       char    *np;
> >> @@ -363,11 +381,10 @@ static int parse_args(int argc, char **argv,
> >>                       return -2;
> >>       }
> >>
> >> -        if (prepare_args( nargs_, args_, prgname, matchcmd )){
> >> -                fprintf(stderr, "ERROR: not enough memory\\n");
> >> +     if (prepare_args( nargs_, args_, prgname, matchcmd )){
> >> +             fprintf(stderr, "ERROR: not enough memory\\n");
> >>               return -20;
> >> -        }
> >> -
> >> +     }
> >>
> >>       return 1;
> >>  }
> >> diff --git a/mmc.h b/mmc.h
> >> index 9871d62..61c0303 100644
> >> --- a/mmc.h
> >> +++ b/mmc.h
> >> @@ -24,12 +24,25 @@
> >>  #define MMC_BLOCK_MAJOR                      179
> >>
> >>  /* From kernel linux/mmc/mmc.h */
> >> +#define MMC_GO_IDLE_STATE    0       /* bc                               */
> >> +#define MMC_ALL_SEND_CID     2       /* bcr                          R2
> >> */
> >>  #define MMC_SWITCH           6       /* ac   [31:0] See below        R1b
> >> */
> >> +#define MMC_SELECT_CARD              7       /* ac   [31:16] RCA
> >>       R1  */
> >>  #define MMC_SEND_EXT_CSD     8       /* adtc                         R1
> >> */
> >> -#define MMC_SEND_STATUS              13      /* ac   [31:16] RCA        R1  */
> >> +#define MMC_SEND_CID         10      /* ac   [31:16] RCA
> >>       R2  */
> >> +#define MMC_STOP_TRANSMISSION        12      /* ac
> >>       R1b */
> >> +#define MMC_SEND_STATUS              13      /* ac   [31:16] RCA
> >>       R1  */
> >> +#define MMC_READ_SINGLE_BLOCK        17      /* adtc [31:0] data addr
> >>       R1  */
> >> +#define MMC_READ_MULTIPLE_BLOCK      18      /* adtc [31:0] data
> >> addr  R1  */
> >> +#define MMC_WRITE_BLOCK              24      /* adtc [31:0] data addr
> >>       R1  */
> >> +#define MMC_WRITE_MULTIPLE_BLOCK 25  /* adtc
> >>       R1  */
> >> +
> >> +#define MMC_GEN_CMD          56      /* adtc [0] RD/WR
> >>       R1  */
> >> +
> >>  #define R1_SWITCH_ERROR   (1 << 7)  /* sx, c */
> >>  #define MMC_SWITCH_MODE_WRITE_BYTE   0x03    /* Set target to
> value
> >> */
> >>
> >> +
> >>  /*
> >>   * EXT_CSD fields
> >>   */
> >> @@ -125,3 +138,17 @@
> >>
> >>  #define MMC_RSP_R1
> >>       (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
> >>  #define MMC_RSP_R1B
> >>       (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_R
> >> SP_BUSY)
> >> +
> >> +
> >> +/* Manufacturer IDs as shown by /sys/block/mmcblk0/device/manfid */
> >> +#define MANFID_PANASONIC     0x000001
> >> +#define MANFID_KINGSTON              0x000002
> >> +#define MANFID_SANDISK               0x000003
> >> +#define MANFID_SAMSUNG               0x000015
> >> +#define MANFID_TOSHIBA               0x00001d
> >> +#define MANFID_SANDISK_SEM   0x000045        /* seen with
> >> "SEM16G" */
> >> +#define MANFID_KINGSTON_MMC  0x000070        /* Seen on
> >> embedded device */
> >> +
> >> +#if 0
> >> +const char *manfid_lookup[];
> >> +#endif
> >> diff --git a/mmc_cmds-emmc5.c b/mmc_cmds-emmc5.c new file mode
> >> 100644 index 0000000..43fe3f2
> >> --- /dev/null
> >> +++ b/mmc_cmds-emmc5.c
> >> @@ -0,0 +1,129 @@
> >> +/*
> >> + * eMMC 5.0+ Specific tools
> >> + *
> >> + * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public
> >> + * License v2 as published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> GNU
> >> + * General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU General Public
> >> + * License along with this program; if not, write to the
> >> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> >> + * Boston, MA 021110-1307, USA.
> >> + */
> >> +
> >> +#include <stdio.h>
> >> +#include <stdlib.h>
> >> +#include <string.h>  /* for memset() */ #include <errno.h> #include
> >> +<linux/types.h> #include <sys/ioctl.h> #include <dirent.h> #include
> >> +<fcntl.h> #include <libgen.h> #include <limits.h> #include <ctype.h>
> >> +
> >> +#include "mmc.h"
> >> +#include "mmc_cmds.h"
> >> +
> >> +
> >> +
> >> +int do_emmc5_fw_update(int fd, __u8 *cid, __u8 *ext_csd,
> >> +                             char *emmc_fw, size_t fwsize) {
> >> +     struct mmc_ioc_cmd idata;
> >> +     int retcode = 0;
> >> +     int ret;
> >> +
> >> +     /* 2) Host send CMD6 to set MODE_CONFIG[30] = 0x01 */
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     idata.opcode = MMC_SWITCH;
> >> +        idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
> >> +                     (30 << 16) |    /* index */
> >> +                     (1 << 8) |      /* value */
> >> +                     EXT_CSD_CMD_SET_NORMAL;
> >> +     idata.flags = MMC_RSP_R1B | MMC_CMD_AC;;
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             printf("ioctl: MMC_SWITCH (eMMC 5.0 FW Update) %m\n");
> >> +             goto abort_update;
> >> +     }
> >> +
> >> +     /* 2) send CMD25 0x0000FFFF */
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     idata.write_flag = 1;
> >> +     idata.data_ptr = (__u64) ((unsigned long) emmc_fw); /* Write
> >> + this
> >> FW */
> >> +     idata.opcode = MMC_WRITE_MULTIPLE_BLOCK;
> >> +     idata.arg = 0x0000FFFF;
> >> +     idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;;
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             printf("ioctl:MMC_WRITE_MULTIPLE_BLOCK (eMMC 5.0 FW
> >> Update) %m\n");
> >> +             goto abort_update;
> >> +     }
> >> +
> >> +     /* 3) check FFU_STATUS[26] */
> >> +     ret = read_extcsd(fd, ext_csd);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             fprintf(stderr, "read_extcsd error (%d): %m\n", ret);
> >> +             goto abort_update;
> >> +     }
> >> +
> >> +     switch(ext_csd[26]) {
> >> +     case 0:  break;
> >> +     case 0x10:
> >> +             fprintf(stderr, "eMMC 5.0 FFU had general error and
> >> + failed
> >> (EIO).\n");
> >> +             retcode = -EIO;
> >> +             break;
> >> +     case 0x11:
> >> +             fprintf(stderr, "eMMC 5.0 FFU did not complete
> >> (EAGAIN).\n");
> >> +             retcode = -EAGAIN;
> >> +             break;
> >> +     case 0x12:
> >> +             fprintf(stderr, "eMMC 5.0 FFU download failed: checksum "
> >> +                     "mismatch or was interrupted (EINTR).\n");
> >> +             retcode = -EINTR;
> >> +             break;
> >> +     default:
> >> +             fprintf(stderr, "eMMC 5.0 FFU unknown error: %x\n",
> >> ext_csd[26]);
> >> +             retcode = -EIO;
> >> +     }
> >> +
> >> +     /* 4) check
> >> NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED[305-302] */
> >> +     ret = ext_csd[302] << 0 | (ext_csd[303] << 8) |
> >> +             (ext_csd[304] << 16) | (ext_csd[305] << 24);
> >> +
> >> +     /* convert that gibberish to bytes */
> >> +     ret *= 512;
> >> +     if (ext_csd[61] == 1)
> >> +             ret *= 8;       /* 4K sectors */
> >> +
> >> +     if (ret != fwsize) {
> >> +             fprintf(stderr, "eMMC 5.0 FFU Failed? (fwsize: %d,"
> >> +                     " PROGRAMMED_SECTORS: %d)\n", fwsize, ret);
> >> +     }
> >> +
> >> +abort_update:
> >> +     /* 4) send CMD0
> >> +      * This should reset the device and force use of the new firmware.
> >> +      */
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     idata.opcode = MMC_GO_IDLE_STATE;
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret) {
> >> +             retcode = ret;
> >> +             perror("ioctl:MMC_GO_IDLE_STATE (eMMC 5.0 FFU)");
> >> +     }
> >> +
> >> +     return retcode;
> >> +}
> >> diff --git a/mmc_cmds.c b/mmc_cmds.c
> >> index b8afa74..afbed77 100644
> >> --- a/mmc_cmds.c
> >> +++ b/mmc_cmds.c
> >> @@ -1,3 +1,4 @@
> >> +
> >>  /*
> >>   * This program is free software; you can redistribute it and/or
> >>   * modify it under the terms of the GNU General Public @@ -17,9
> >> +18,10 @@  #include <stdio.h>  #include <stdlib.h>  #include
> >> <string.h> -#include <sys/ioctl.h> -#include <sys/types.h>
> >> +#include <errno.h>
> >>  #include <dirent.h>
> >> +#include <sys/ioctl.h>
> >> +#include <linux/types.h>
> >>  #include <sys/stat.h>
> >>  #include <unistd.h>
> >>  #include <fcntl.h>
> >> @@ -30,23 +32,27 @@
> >>  #include "mmc.h"
> >>  #include "mmc_cmds.h"
> >>
> >> +#define EXT_CSD_SIZE 512
> >> +#define CID_SIZE 16
> >> +
> >> +
> >>  int read_extcsd(int fd, __u8 *ext_csd)  {
> >>       int ret = 0;
> >>       struct mmc_ioc_cmd idata;
> >>       memset(&idata, 0, sizeof(idata));
> >> -     memset(ext_csd, 0, sizeof(__u8) * 512);
> >> +     memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
> >>       idata.write_flag = 0;
> >>       idata.opcode = MMC_SEND_EXT_CSD;
> >>       idata.arg = 0;
> >>       idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
> >> -     idata.blksz = 512;
> >> +     idata.blksz = EXT_CSD_SIZE;
> >>       idata.blocks = 1;
> >>       mmc_ioc_cmd_set_data(idata, ext_csd);
> >>
> >>       ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >>       if (ret)
> >> -             perror("ioctl");
> >> +             perror("ioctl SEND_EXT_CSD");
> >>
> >>       return ret;
> >>  }
> >> @@ -67,7 +73,30 @@ int write_extcsd_value(int fd, __u8 index, __u8
> >> value)
> >>
> >>       ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >>       if (ret)
> >> -             perror("ioctl");
> >> +             perror("ioctl Write EXT CSD");
> >> +
> >> +     return ret;
> >> +}
> >> +
> >> +int read_cid(int fd, __u8 *cid)
> >> +{
> >> +     int ret = 0;
> >> +     struct mmc_ioc_cmd idata;
> >> +
> >> +     memset(&idata, 0, sizeof(idata));
> >> +     memset(cid, 0, sizeof(__u8) * CID_SIZE);
> >> +
> >> +     idata.write_flag = 0;
> >> +     idata.opcode = MMC_SEND_CID;
> >> +     idata.arg    = 0;
> >> +     idata.flags  = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
> >> +     idata.blksz  = CID_SIZE;
> >> +     idata.blocks = 1;
> >> +     mmc_ioc_cmd_set_data(idata, cid);
> >> +
> >> +     ret = ioctl(fd, MMC_IOC_CMD, &idata);
> >> +     if (ret)
> >> +             perror("ioctl SEND_CID");
> >>
> >>       return ret;
> >>  }
> >> @@ -103,17 +132,11 @@ void print_writeprotect_status(__u8 *ext_csd)
> >>
> >>               reg = ext_csd[EXT_CSD_BOOT_WP];
> >>               printf("Boot Area Write protection [BOOT_WP]:
> >> 0x%02x\n", reg);
> >> -             printf(" Power ro locking: ");
> >> -             if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
> >> -                     printf("not possible\n");
> >> -             else
> >> -                     printf("possible\n");
> >> +             printf(" Power ro locking: %spossible\n",
> >> +                     (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS) ? "not
> >> " : "");
> >>
> >> -             printf(" Permanent ro locking: ");
> >> -             if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
> >> -                     printf("not possible\n");
> >> -             else
> >> -                     printf("possible\n");
> >> +             printf(" Permanent ro locking: %spossible\n",
> >> +                     (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS) ?
> >> "not " : "");
> >>
> >>               printf(" ro lock status: ");
> >>               if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN) @@ -127,7
> >> +150,7 @@ void print_writeprotect_status(__u8 *ext_csd)
> >>
> >>  int do_writeprotect_get(int nargs, char **argv)  {
> >> -     __u8 ext_csd[512];
> >> +     __u8 ext_csd[EXT_CSD_SIZE];
> >>       int fd, ret;
> >>       char *device;
> >>
> >> @@ -155,7 +178,7 @@ int do_writeprotect_get(int nargs, char **argv)
> >>
> >>  int do_writeprotect_set(int nargs, char **argv)  {
> >> -     __u8 ext_csd[512], value;
> >> +     __u8 ext_csd[EXT_CSD_SIZE], value;
> >>       int fd, ret;
> >>       char *device;
> >>
> >> @@ -191,7 +214,7 @@ int do_writeprotect_set(int nargs, char **argv)
> >>
> >>  int do_disable_512B_emulation(int nargs, char **argv)  {
> >> -     __u8 ext_csd[512], native_sector_size, data_sector_size,
> >> wr_rel_param;
> >> +     __u8 ext_csd[EXT_CSD_SIZE], native_sector_size,
> >> +data_sector_size, wr_rel_param;
> >>       int fd, ret;
> >>       char *device;
> >>
> >> @@ -301,7 +324,7 @@ int do_write_boot_en(int nargs, char **argv)
> >>
> >>  int do_hwreset(int value, int nargs, char **argv)  {
> >> -     __u8 ext_csd[512];
> >> +     __u8 ext_csd[EXT_CSD_SIZE];
> >>       int fd, ret;
> >>       char *device;
> >>
> >> @@ -360,7 +383,7 @@ int do_hwreset_dis(int nargs, char **argv)
> >>
> >>  int do_write_bkops_en(int nargs, char **argv)  {
> >> -     __u8 ext_csd[512], value = 0;
> >> +     __u8 ext_csd[EXT_CSD_SIZE], value = 0;
> >>       int fd, ret;
> >>       char *device;
> >>
> >> @@ -497,7 +520,7 @@ int set_partitioning_setting_completed(int
> >> dry_run, const char * const device,  int do_enh_area_set(int nargs, char
> **argv)  {
> >>       __u8 value;
> >> -     __u8 ext_csd[512];
> >> +     __u8 ext_csd[EXT_CSD_SIZE];
> >>       int fd, ret;
> >>       char *device;
> >>       int dry_run = 1;
> >> @@ -635,7 +658,7 @@ int do_enh_area_set(int nargs, char **argv)  int
> >> do_write_reliability_set(int nargs, char **argv)  {
> >>       __u8 value;
> >> -     __u8 ext_csd[512];
> >> +     __u8 ext_csd[EXT_CSD_SIZE];
> >>       int fd, ret;
> >>
> >>       int dry_run = 1;
> >> @@ -696,7 +719,7 @@ int do_write_reliability_set(int nargs, char
> >> **argv)
> >>
> >>  int do_read_extcsd(int nargs, char **argv)  {
> >> -     __u8 ext_csd[512], ext_csd_rev, reg;
> >> +     __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
> >>       __u32 regl;
> >>       int fd, ret;
> >>       char *device;
> >> @@ -722,24 +745,13 @@ int do_read_extcsd(int nargs, char **argv)
> >>       ext_csd_rev = ext_csd[192];
> >>
> >>       switch (ext_csd_rev) {
> >> -     case 6:
> >> -             str = "4.5";
> >> -             break;
> >> -     case 5:
> >> -             str = "4.41";
> >> -             break;
> >> -     case 3:
> >> -             str = "4.3";
> >> -             break;
> >> -     case 2:
> >> -             str = "4.2";
> >> -             break;
> >> -     case 1:
> >> -             str = "4.1";
> >> -             break;
> >> -     case 0:
> >> -             str = "4.0";
> >> -             break;
> >> +     case 7: str = "5.0"; break;
> >> +     case 6: str = "4.5"; break;
> >> +     case 5: str = "4.41"; break;
> >> +     case 3: str = "4.3"; break;
> >> +     case 2: str = "4.2"; break;
> >> +     case 1: str = "4.1"; break;
> >> +     case 0: str = "4.0"; break;
> >>       default:
> >>               goto out_free;
> >>       }
> >> @@ -1160,6 +1172,140 @@ int do_sanitize(int nargs, char **argv)
> >>       }
> >>
> >>       return ret;
> >> -
> >>  }
> >>
> >> +int do_firmware_update(int nargs, char **argv) {
> >> +     char *emmc_fw;
> >> +     char *device;
> >> +     char *fwfilename;
> >> +     struct stat fwfilestat;
> >> +     size_t fwsize;
> >> +     int ret = 0;
> >> +     int devfd,fwfd;
> >> +     __u8 ext_csd[EXT_CSD_SIZE];
> >> +     __u8 cid[CID_SIZE];
> >> +     __u8 ext_csd_rev;
> >> +
> >> +
> >> +     CHECK(nargs != 4, "Usage: mmc firmware update"
> >> +                     " --I_want_to_destroy_my_drive"
> >> +                     " </path/to/mmcblkX> </path/to/FW.bin>\n",
> >> +                       exit(1));
> >> +
> >> +
> >> +     /* Lesson from hdparm: user must be aware of the risks
> >> +      * Key here is the additional command line flag be UNDOCUMENTED.
> >> +      * User _must_ read and KNOW this is risky at runtime.
> >> +      */
> >> +     if (strncmp(argv[4], "--I_want_to_destroy_my_drive", 28)) {
> >> +             fprintf(stderr,"ERROR: Please specify --
> >> I_want_to_destroy_my_drive"
> >> +             " as first parameter to firmware update.\n");
> >> +
> >> +             exit(1);
> >> +     }
> >> +
> >> +     emmc_fw = malloc(MMC_IOC_MAX_BYTES);
> >> +     if (!emmc_fw)
> >> +             return -ENOMEM;
> >> +
> >> +     device = argv[5];
> >> +
> >> +     devfd = open(device, O_RDWR);
> >> +     if (devfd < 0) {
> >> +             fprintf(stderr,"ERROR: open %s: %m\n", device); /* %m =
> >> errno */
> >> +             goto out_free;
> >> +     }
> >> +
> >> +     /* 1) read device version and attributes */
> >> +     ret = read_extcsd(devfd, ext_csd);
> >> +     if (ret) {
> >> +             fprintf(stderr, "ERROR: Read EXT_CSD from %s: %m\n",
> >> device);
> >> +             goto out_dev;
> >> +     }
> >> +
> >> +     ext_csd_rev = ext_csd[192];
> >> +
> >> +     if (ext_csd_rev < 7) {
> >> +             fprintf(stderr, "ERROR: Can not update firmware"
> >> +                     ": %s is pre-emmc 5.0 vintage\n", device);
> >> +             goto out_dev;
> >> +     }
> >> +
> >> +     printf("%s: FW is currently %8s
> >> (0x%02x%02x%02x%02x%02x%02x%02x%02x)\n",
> >> +             device, &(ext_csd[254]),
> >> +             ext_csd[254], ext_csd[255], ext_csd[256], ext_csd[257],
> >> +             ext_csd[258], ext_csd[259], ext_csd[260], ext_csd[261]
> >> +             );
> >> +
> >> +     /* 2) confirm SUPPORTED_MODES has FFU bit set */
> >> +     if (!(ext_csd[493] & 1)) {
> >> +             fprintf(stderr, "ERROR: %s is eMMC 5.0 device"
> >> +                     "but does not support FFU.\n", device);
> >> +             goto out_dev;
> >> +     }
> >> +
> >> +     /* 3) confirm FW updated is NOT disabled on this device */
> >> +     if (ext_csd[169] & 1) {
> >> +             fprintf(stderr, "ERROR: %s is eMMC 5.0 device"
> >> +                     "but FFU is disabled.\n", device);
> >> +             goto out_dev;
> >> +     }
> >> +
> >> +     /* 4) read the device manfid */
> >> +     ret = read_cid(devfd, cid);
> >> +     if (ret) {
> >> +             fprintf(stderr, "ERROR: Read EXT_CSD from %s: %m\n",
> >> device);
> >> +             goto out_dev;
> >> +     }
> >> +
> >> +     /* 5) Fetch the FW image */
> >> +     fwfilename = argv[2];
> >> +
> >> +     fwfd = open(fwfilename, O_RDONLY);
> >> +     if (fwfd < 0) {
> >> +             /* %m = errno */
> >> +             fprintf(stderr,"ERROR: open %s: %m", fwfilename);
> >> +             goto out_dev;
> >> +     }
> >> +
> >> +     ret = fstat(fwfd, &fwfilestat);
> >> +     if (ret) {
> >> +             /* %m = errno */
> >> +             fprintf(stderr,"ERROR: fstat %s: %m", fwfilename);
> >> +             goto out_fw;
> >> +     }
> >> +
> >> +     fwsize = fwfilestat.st_size;
> >> +     if (fwsize > MMC_IOC_MAX_BYTES) {
> >> +             fprintf(stderr,"ERROR: %s is > %ld bytes long (max
> >> allowed)\n",
> >> +                     fwfilename, MMC_IOC_MAX_BYTES);
> >> +             goto out_fw;
> >> +     }
> >> +
> >> +     ret = read(fwfd, emmc_fw, fwsize);
> >> +     if (ret < fwsize) {
> >> +             fprintf(stderr,"ERROR: did not read all %d bytes of %s"
> >> +                             "(%d bytes long)\n",
> >> +                     fwsize, fwfilename, ret);
> >> +             goto out_fw;
> >> +     }
> >> +
> >> +     do_emmc5_fw_update(devfd, cid, ext_csd, emmc_fw, fwsize);
> >> +
> >> +     cid[13] = '\0'; /* make sure string is NULL terminated */
> >> +
> >> +     printf("%s: FW updated to %8s
> >> (0x%2x%2x%2x%2x%2x%2x%2x%2x)\n",
> >> +             device, &(ext_csd[254]),
> >> +             ext_csd[254], ext_csd[255], ext_csd[256], ext_csd[257],
> >> +             ext_csd[258], ext_csd[259], ext_csd[260], ext_csd[261]
> >> +             );
> >> +
> >> +out_fw:
> >> +     close(fwfd);
> >> +out_dev:
> >> +     close(devfd);
> >> +out_free:
> >> +     free(emmc_fw);
> >> +     return ret;
> >> +}
> >> diff --git a/mmc_cmds.h b/mmc_cmds.h
> >> index f06cc10..549b851 100644
> >> --- a/mmc_cmds.h
> >> +++ b/mmc_cmds.h
> >> @@ -28,3 +28,9 @@ int do_sanitize(int nargs, char **argv);  int
> >> do_status_get(int nargs, char **argv);  int do_enh_area_set(int
> >> nargs, char **argv);  int do_write_reliability_set(int nargs, char
> >> **argv);
> >> +int do_firmware_update(int nargs, char **argv);
> >> +
> >> +int read_extcsd(int fd, __u8 *ext_csd); int write_extcsd_value(int
> >> +fd,
> >> +__u8 index, __u8 value);
> >> +
> >> +int do_emmc5_fw_update(int devfd, __u8 *cid, __u8 *ext_csd, char
> >> +*emmc_fw, size_t fwsize);
> >> --
> >> 1.8.1.5
> >>
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> >> in the body of a message to majordomo@xxxxxxxxxxxxxxx More
> majordomo
> >> info at http://vger.kernel.org/majordomo-info.html
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> > in the body of a message to majordomo@xxxxxxxxxxxxxxx More
> majordomo
> > info at  http://vger.kernel.org/majordomo-info.html



��.n��������+%������w��{.n�����{��i��)��jg��������ݢj����G�������j:+v���w�m������w�������h�����٥





[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux