Add hack support for FFU
Some device are not fully eMMC 5.0 FFU compliant.
Support ability to send hack to the kernel to support them.
BUG=chrome-os-partner:31155
TEST=Check Samsung upgrade
Change-Id: Icd2efbe0c4a78f1acfebd53ad1f147e5c77a38b1
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/239953
Reviewed-by: Puthikorn Voravootivat <puthik@chromium.org>
diff --git a/ffu.h b/ffu.h
new file mode 100644
index 0000000..e0770a5
--- /dev/null
+++ b/ffu.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef _FFU_H_
+#define _FFU_H_
+
+#include <linux/types.h>
+
+/*
+ * eMMC5.0 Field Firmware Update (FFU) opcodes
+*/
+#define MMC_FFU_INVOKE_OP 302
+
+#define FFU_NAME_LEN 80 /* Name of the firmware file udev should find */
+
+enum mmc_ffu_hack_type {
+ MMC_OVERRIDE_FFU_ARG = 0,
+ MMC_HACK_LEN,
+};
+
+struct mmc_ffu_hack {
+ enum mmc_ffu_hack_type type;
+ __u64 value;
+};
+
+struct mmc_ffu_args {
+ char name[FFU_NAME_LEN];
+ __u32 hack_nb;
+ struct mmc_ffu_hack hack[0];
+};
+
+#endif /* _FFU_H_ */
diff --git a/mmc.c b/mmc.c
index 3d051ce..fb346ef 100644
--- a/mmc.c
+++ b/mmc.c
@@ -116,8 +116,9 @@
NULL
},
{ do_emmc50_ffu, -2,
- "ffu", "<image name> <device>\n"
- "run eMMC 5.0 Field firmware update.\n.",
+ "ffu", "[-k hack_type[:hack_value]] <image name> <device>\n"
+ "run eMMC 5.0 Field firmware update.\n"
+ "Device specific hacks can be specificied.",
NULL
},
{ 0, 0, 0, 0 }
diff --git a/mmc_cmds.c b/mmc_cmds.c
index 3c7322e..3ea1a61 100644
--- a/mmc_cmds.c
+++ b/mmc_cmds.c
@@ -31,9 +31,10 @@
#include "mmc.h"
#include "mmc_cmds.h"
+#include "ffu.h"
#define EXT_CSD_SIZE 512
-#define FFU_PATH_SIZE (512 - 1)
+#define FFU_DATA_SIZE 512
#define CID_SIZE 16
@@ -1318,22 +1319,66 @@
}
+static const char* const mmc_ffu_hack_names[] = {
+ [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
+};
+
int do_emmc50_ffu (int nargs, char **argv)
{
- int fd, ret;
- char *device;
- char *path;
+ int fd, ret, i, argc=1, ffu_hack=0;
+ char *device, *type, *path;
+ __u64 value;
+ union {
+ __u8 data[FFU_DATA_SIZE];
+ struct mmc_ffu_args ffu_args;
+ } ffu_data;
+ struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
struct mmc_ioc_cmd mmc_ioc_cmd;
- CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n",
- exit(1));
+ while (!strcmp("-k", argv[argc])) {
+ ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
+ if (ret < 1) {
+ fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
+ exit(1);
+ }
+ for (i = 0; i < MMC_HACK_LEN; i++) {
+ if (!strcmp(type, mmc_ffu_hack_names[i])) {
+ ffu_args->hack[ffu_hack].type = i;
+ if (ret == 2) {
+ ffu_args->hack[ffu_hack].value = value;
+ }
+ ffu_hack++;
+ if (ffu_hack * sizeof(struct mmc_ffu_hack) +
+ sizeof(struct mmc_ffu_args) >
+ FFU_DATA_SIZE) {
+ fprintf(stderr, "Too many %d hacks",
+ ffu_hack);
+ exit(1);
+ }
+ break;
+ }
+ }
+ if (i == MMC_HACK_LEN) {
+ fprintf(stderr, "Hack type %s not found\n", type);
+ fprintf(stderr, "Supported types are: ");
+ for (i = 0; i < MMC_HACK_LEN; i++)
+ fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
+ (i == MMC_HACK_LEN-1 ? "\n": ", "));
- path = argv[1];
- if (strlen(path) > FFU_PATH_SIZE) {
+ exit(1);
+ }
+ free(type);
+ argc++;
+ }
+ ffu_args->hack_nb = ffu_hack;
+
+ path = argv[argc++];
+ if (strlen(path) >= FFU_NAME_LEN) {
fprintf(stderr, "Filename \"%.20s\" too long\n", path);
exit(1);
}
- device = argv[2];
+ strcpy(ffu_args->name, path);
+ device = argv[argc++];
fd = open(device, O_RDWR);
if (fd < 0) {
perror("open");
@@ -1343,12 +1388,12 @@
/* prepare and send ioctl */
memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
- mmc_ioc_cmd.blksz = MIN(strlen(path), FFU_PATH_SIZE);
+ mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
mmc_ioc_cmd.blocks = 1;
mmc_ioc_cmd.arg = 0;
mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
mmc_ioc_cmd.write_flag = 1;
- mmc_ioc_cmd_set_data(mmc_ioc_cmd, path);
+ mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
if (ret) {
fprintf(stderr, "FFU install failed : %s\n", strerror(errno));