summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile33
-rw-r--r--cgpt/cgpt_find.c50
-rw-r--r--cgpt/cgpt_nor.c249
-rw-r--r--cgpt/cgpt_nor.h34
-rw-r--r--cgpt/cgpt_wrapper.c147
5 files changed, 508 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index e595d2c7..058b47a0 100644
--- a/Makefile
+++ b/Makefile
@@ -476,6 +476,7 @@ CGPT_SRCS = \
cgpt/cgpt_create.c \
cgpt/cgpt_find.c \
cgpt/cgpt_legacy.c \
+ cgpt/cgpt_nor.c \
cgpt/cgpt_prioritize.c \
cgpt/cgpt_repair.c \
cgpt/cgpt_show.c \
@@ -489,8 +490,19 @@ CGPT_SRCS = \
cgpt/cmd_show.c
CGPT_OBJS = ${CGPT_SRCS:%.c=${BUILD}/%.o}
+
ALL_OBJS += ${CGPT_OBJS}
+CGPT_WRAPPER = ${BUILD}/cgpt/cgpt_wrapper
+
+CGPT_WRAPPER_SRCS = \
+ cgpt/cgpt_nor.c \
+ cgpt/cgpt_wrapper.c
+
+CGPT_WRAPPER_OBJS = ${CGPT_WRAPPER_SRCS:%.c=${BUILD}/%.o}
+
+ALL_OBJS += ${CGPT_WRAPPER_OBJS}
+
# Utility defaults
UTIL_DEFAULTS = ${BUILD}/default/vboot_reference
@@ -747,6 +759,9 @@ clean:
.PHONY: install
install: cgpt_install utils_install signing_install futil_install
+.PHONY: install_mtd
+install_mtd: install cgpt_wrapper_install
+
.PHONY: install_for_test
install_for_test: override DESTDIR = ${TEST_INSTALL_DIR}
install_for_test: install
@@ -923,8 +938,15 @@ ${TINYHOSTLIB}: ${TINYHOSTLIB_OBJS}
# ----------------------------------------------------------------------------
# CGPT library and utility
+.PHONY: cgpt_wrapper
+cgpt_wrapper: ${CGPT_WRAPPER}
+
+${CGPT_WRAPPER}: ${CGPT_WRAPPER_OBJS} ${UTILLIB}
+ @$(PRINTF) " LD $(subst ${BUILD}/,,$@)\n"
+ ${Q}${LD} -o ${CGPT_WRAPPER} ${CFLAGS} $^
+
.PHONY: cgpt
-cgpt: ${CGPT}
+cgpt: ${CGPT} ${CGPT_WRAPPER}
${CGPT}: LDFLAGS += -static
${CGPT}: LDLIBS += -luuid
@@ -939,6 +961,15 @@ cgpt_install: ${CGPT}
${Q}mkdir -p ${UB_DIR}
${Q}${INSTALL} -t ${UB_DIR} $^
+.PHONY: cgpt_wrapper_install
+cgpt_wrapper_install: cgpt_install ${CGPT_WRAPPER}
+ @$(PRINTF) " INSTALL cgpt_wrapper\n"
+ ${Q}${INSTALL} -t ${UB_DIR} ${CGPT_WRAPPER}
+ ${Q}mv ${UB_DIR}/$(notdir ${CGPT}) \
+ ${UB_DIR}/$(notdir ${CGPT}).bin
+ ${Q}mv ${UB_DIR}/$(notdir ${CGPT_WRAPPER}) \
+ ${UB_DIR}/$(notdir ${CGPT})
+
# ----------------------------------------------------------------------------
# Utilities
diff --git a/cgpt/cgpt_find.c b/cgpt/cgpt_find.c
index c3193040..1a516d8c 100644
--- a/cgpt/cgpt_find.c
+++ b/cgpt/cgpt_find.c
@@ -8,6 +8,7 @@
#include <unistd.h>
#include "cgpt.h"
+#include "cgpt_nor.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
@@ -145,6 +146,7 @@ static int do_search(CgptFindParams *params, char *fileName) {
}
+#define PROC_MTD "/proc/mtd"
#define PROC_PARTITIONS "/proc/partitions"
#define DEV_DIR "/dev"
#define SYS_BLOCK_DIR "/sys/block"
@@ -186,23 +188,23 @@ static char *is_wholedev(const char *basename) {
return 0;
}
-
// This scans all the physical devices it can find, looking for a match. It
// returns true if any matches were found, false otherwise.
static int scan_real_devs(CgptFindParams *params) {
int found = 0;
- char line[BUFSIZE];
char partname[128]; // max size for /proc/partition lines?
FILE *fp;
char *pathname;
- fp = fopen(PROC_PARTITIONS, "r");
+ fp = fopen(PROC_PARTITIONS, "re");
if (!fp) {
perror("can't read " PROC_PARTITIONS);
return found;
}
- while (fgets(line, sizeof(line), fp)) {
+ size_t line_length = 0;
+ char *line = NULL;
+ while (getline(&line, &line_length, fp) != -1) {
int ma, mi;
long long unsigned int sz;
@@ -217,6 +219,46 @@ static int scan_real_devs(CgptFindParams *params) {
}
fclose(fp);
+
+ fp = fopen(PROC_MTD, "re");
+ if (!fp) {
+ free(line);
+ return found;
+ }
+
+ while (getline(&line, &line_length, fp) != -1) {
+ uint64_t sz;
+ uint32_t erasesz;
+ char name[128];
+ // dev: size erasesize name
+ if (sscanf(line, "%64[^:]: %" PRIx64 " %x \"%127[^\"]\"",
+ partname, &sz, &erasesz, name) != 4)
+ continue;
+ if (strcmp(partname, "mtd0") == 0) {
+ char temp_dir[] = "/tmp/cgpt_find.XXXXXX";
+ if (params->drive_size == 0) {
+ if (GetMtdSize("/dev/mtd0", &params->drive_size) != 0) {
+ perror("GetMtdSize");
+ goto cleanup;
+ }
+ }
+ if (ReadNorFlash(temp_dir) != 0) {
+ perror("ReadNorFlash");
+ goto cleanup;
+ }
+ char nor_file[64];
+ if (snprintf(nor_file, sizeof(nor_file), "%s/rw_gpt", temp_dir) > 0) {
+ if (do_search(params, nor_file)) {
+ found++;
+ }
+ }
+ RemoveDir(temp_dir);
+ break;
+ }
+ }
+cleanup:
+ fclose(fp);
+ free(line);
return found;
}
diff --git a/cgpt/cgpt_nor.c b/cgpt/cgpt_nor.c
new file mode 100644
index 00000000..17502a73
--- /dev/null
+++ b/cgpt/cgpt_nor.c
@@ -0,0 +1,249 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <inttypes.h>
+#include <linux/major.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "cgpt.h"
+#include "cgpt_nor.h"
+
+static const char FLASHROM_PATH[] = "/usr/sbin/flashrom";
+
+// Obtain the MTD size from its sysfs node.
+int GetMtdSize(const char *mtd_device, uint64_t *size) {
+ mtd_device = strrchr(mtd_device, '/');
+ if (mtd_device == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ char *sysfs_name;
+ if (asprintf(&sysfs_name, "/sys/class/mtd%s/size", mtd_device) == -1) {
+ return 1;
+ }
+ FILE *fp = fopen(sysfs_name, "r");
+ free(sysfs_name);
+ if (fp == NULL) {
+ return 1;
+ }
+ int ret = (fscanf(fp, "%" PRIu64 "\n", size) != 1);
+ fclose(fp);
+ return ret;
+}
+
+int ForkExecV(const char *cwd, const char *const argv[]) {
+ pid_t pid = fork();
+ if (pid == -1) {
+ return -1;
+ }
+ int status = -1;
+ if (pid == 0) {
+ if (cwd && chdir(cwd) != 0) {
+ return -1;
+ }
+ execv(argv[0], (char *const *)argv);
+ // If this is reached, execv fails.
+ err(-1, "Cannot exec %s in %s.", argv[0], cwd);
+ } else {
+ if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status))
+ return WEXITSTATUS(status);
+ }
+ return status;
+}
+
+int ForkExecL(const char *cwd, const char *cmd, ...) {
+ int argc;
+ va_list ap;
+ va_start(ap, cmd);
+ for (argc = 1; va_arg(ap, char *) != NULL; ++argc);
+ va_end(ap);
+
+ va_start(ap, cmd);
+ const char **argv = calloc(argc + 1, sizeof(char *));
+ if (argv == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ argv[0] = cmd;
+ int i;
+ for (i = 1; i < argc; ++i) {
+ argv[i] = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ int ret = ForkExecV(cwd, argv);
+ free(argv);
+ return ret;
+}
+
+static int read_write(int source_fd,
+ uint64_t size,
+ const char *src_name,
+ int idx) {
+ int ret = 1;
+ const int bufsize = 4096;
+ char *buf = malloc(bufsize);
+ if (buf == NULL) {
+ goto clean_exit;
+ }
+
+ ret++;
+ char *dest;
+ if (asprintf(&dest, "%s_%d", src_name, idx) == -1) {
+ goto free_buf;
+ }
+
+ ret++;
+ int dest_fd = open(dest, O_WRONLY | O_CLOEXEC | O_CREAT, 0600);
+ if (dest_fd < 0) {
+ goto free_dest;
+ }
+
+ ret++;
+ uint64_t copied = 0;
+ ssize_t nr_read;
+ ssize_t nr_write;
+ while (copied < size) {
+ size_t to_read = size - copied;
+ if (to_read > bufsize) {
+ to_read = bufsize;
+ }
+ nr_read = read(source_fd, buf, to_read);
+ if (nr_read < 0) {
+ goto close_dest_fd;
+ }
+ nr_write = 0;
+ while (nr_write < nr_read) {
+ ssize_t s = write(dest_fd, buf + nr_write, nr_read - nr_write);
+ if (s < 0) {
+ goto close_dest_fd;
+ }
+ nr_write += s;
+ }
+ copied += nr_read;
+ }
+
+ ret = 0;
+
+close_dest_fd:
+ close(dest_fd);
+free_dest:
+ free(dest);
+free_buf:
+ free(buf);
+clean_exit:
+ return ret;
+}
+
+static int split_gpt(const char *dir_name, const char *file_name) {
+ int ret = 1;
+ char *source;
+ if (asprintf(&source, "%s/%s", dir_name, file_name) == -1) {
+ goto clean_exit;
+ }
+
+ ret++;
+ int fd = open(source, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ goto free_source;
+ }
+
+ ret++;
+ struct stat stat;
+ if (fstat(fd, &stat) != 0 || (stat.st_size & 1) != 0) {
+ goto close_fd;
+ }
+ uint64_t half_size = stat.st_size / 2;
+
+ ret++;
+ if (read_write(fd, half_size, source, 1) != 0 ||
+ read_write(fd, half_size, source, 2) != 0) {
+ goto close_fd;
+ }
+
+ ret = 0;
+close_fd:
+ close(fd);
+free_source:
+ free(source);
+clean_exit:
+ return ret;
+}
+
+static int remove_file_or_dir(const char *fpath, const struct stat *sb,
+ int typeflag, struct FTW *ftwbuf) {
+ return remove(fpath);
+}
+
+int RemoveDir(const char *dir) {
+ return nftw(dir, remove_file_or_dir, 20, FTW_DEPTH | FTW_PHYS);
+}
+
+// Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
+// |temp_dir_template| is passed to mkdtemp() so it must satisfy all
+// requirements by mkdtemp.
+int ReadNorFlash(char *temp_dir_template) {
+ int ret = 0;
+
+ // Create a temp dir to work in.
+ ret++;
+ if (mkdtemp(temp_dir_template) == NULL) {
+ Error("Cannot create a temporary directory.\n");
+ return ret;
+ }
+
+ // Read RW_GPT section from NOR flash to "rw_gpt".
+ ret++;
+ if (ForkExecL(temp_dir_template, FLASHROM_PATH, "-i", "RW_GPT:rw_gpt", "-r",
+ NULL) != 0) {
+ Error("Cannot exec flashrom to read from RW_GPT section.\n");
+ RemoveDir(temp_dir_template);
+ return ret;
+ }
+
+ ret = 0;
+ return ret;
+}
+
+// Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
+int WriteNorFlash(const char *dir) {
+ int ret = 0;
+ ret++;
+ if (split_gpt(dir, "rw_gpt") != 0) {
+ Error("Cannot split rw_gpt in two.\n");
+ return ret;
+ }
+ ret++;
+ int nr_fails = 0;
+ if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_PRIMARY:rw_gpt_1",
+ "-w", NULL) != 0) {
+ Warning("Cannot write the 1st half of rw_gpt back with flashrom.\n");
+ nr_fails++;
+ }
+ if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_SECONDARY:rw_gpt_2",
+ "-w", NULL) != 0) {
+ Warning("Cannot write the 2nd half of rw_gpt back with flashrom.\n");
+ nr_fails++;
+ }
+ switch (nr_fails) {
+ case 0: ret = 0; break;
+ case 1: Warning("It might still be okay.\n"); break;
+ case 2: Error("Cannot write both parts back with flashrom.\n"); break;
+ }
+ return ret;
+}
diff --git a/cgpt/cgpt_nor.h b/cgpt/cgpt_nor.h
new file mode 100644
index 00000000..b01f88d8
--- /dev/null
+++ b/cgpt/cgpt_nor.h
@@ -0,0 +1,34 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This module provides some utility functions to use "flashrom" to read from
+ * and write to NOR flash.
+ */
+
+#ifndef VBOOT_REFERCENCE_CGPT_CGPT_NOR_H_
+#define VBOOT_REFERCENCE_CGPT_CGPT_NOR_H_
+
+// Obtain the MTD size from its sysfs node. |mtd_device| should point to
+// a dev node such as /dev/mtd0. This function returns 0 on success.
+int GetMtdSize(const char *mtd_device, uint64_t *size);
+
+// Exec |argv| in |cwd|. Return -1 on error, or exit code on success. |argv|
+// must be terminated with a NULL element as is required by execv().
+int ForkExecV(const char *cwd, const char *const argv[]);
+
+// Similar to ForkExecV but with a vararg instead of an array of pointers.
+int ForkExecL(const char *cwd, const char *cmd, ...);
+
+// Exec "rm" to remove |dir|.
+int RemoveDir(const char *dir);
+
+// Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
+// |temp_dir_template| is passed to mkdtemp() so it must satisfy all
+// requirements by mkdtemp().
+int ReadNorFlash(char *temp_dir_template);
+
+// Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
+int WriteNorFlash(const char *dir);
+
+#endif // VBOOT_REFERCENCE_CGPT_CGPT_NOR_H_
diff --git a/cgpt/cgpt_wrapper.c b/cgpt/cgpt_wrapper.c
new file mode 100644
index 00000000..6750d8d8
--- /dev/null
+++ b/cgpt/cgpt_wrapper.c
@@ -0,0 +1,147 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This utility wraps around "cgpt" execution to work with NAND. If the target
+ * device is an MTD device, this utility will read the GPT structures from
+ * FMAP, invokes "cgpt" on that, and writes the result back to NOR flash. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <linux/major.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "cgpt.h"
+#include "cgpt_nor.h"
+
+// Check if cmdline |argv| has "-D". "-D" signifies that GPT structs are stored
+// off device, and hence we should not wrap around cgpt.
+static bool has_dash_D(int argc, const char *const argv[]) {
+ int i;
+ // We go from 2, because the second arg is a cgpt command such as "create".
+ for (i = 2; i < argc; ++i) {
+ if (strcmp("-D", argv[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Check if |device_path| is an MTD device based on its major number being 90.
+static bool is_mtd(const char *device_path) {
+ struct stat stat;
+ if (lstat(device_path, &stat) != 0) {
+ return false;
+ }
+
+ if (major(stat.st_rdev) != MTD_CHAR_MAJOR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Return the element in |argv| that is an MTD device.
+static const char *find_mtd_device(int argc, const char *const argv[]) {
+ int i;
+ for (i = 2; i < argc; ++i) {
+ if (is_mtd(argv[i])) {
+ return argv[i];
+ }
+ }
+ return NULL;
+}
+
+static int wrap_cgpt(int argc,
+ const char *const argv[],
+ const char *mtd_device) {
+ int ret = 0;
+
+ // Create a temp dir to work in.
+ ret++;
+ char temp_dir[] = "/tmp/cgpt_wrapper.XXXXXX";
+ if (ReadNorFlash(temp_dir) != 0) {
+ return ret;
+ }
+ char rw_gpt_path[PATH_MAX];
+ if (snprintf(rw_gpt_path, sizeof(rw_gpt_path), "%s/rw_gpt", temp_dir) < 0) {
+ goto cleanup;
+ }
+
+ // Obtain the MTD size.
+ ret++;
+ uint64_t drive_size = 0;
+ if (GetMtdSize(mtd_device, &drive_size) != 0) {
+ Error("Cannot get the size of %s.\n", mtd_device);
+ goto cleanup;
+ }
+
+ // Launch cgpt on "rw_gpt" with -D size.
+ ret++;
+ const char** my_argv = calloc(argc + 2 + 1, sizeof(char *));
+ if (my_argv == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(my_argv, argv, sizeof(char *) * argc);
+ char *real_cgpt;
+ if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) {
+ free(my_argv);
+ goto cleanup;
+ }
+ my_argv[0] = real_cgpt;
+
+ int i;
+ for (i = 2; i < argc; ++i) {
+ if (strcmp(my_argv[i], mtd_device) == 0) {
+ my_argv[i] = rw_gpt_path;
+ }
+ }
+ my_argv[argc] = "-D";
+ char size[32];
+ snprintf(size, sizeof(size), "%" PRIu64, drive_size);
+ my_argv[argc + 1] = size;
+ i = ForkExecV(NULL, my_argv);
+ free(real_cgpt);
+ free(my_argv);
+ if (i != 0) {
+ Error("Cannot exec cgpt to modify rw_gpt.\n");
+ goto cleanup;
+ }
+
+ // Write back "rw_gpt" to NOR flash in two chunks.
+ ret++;
+ if (WriteNorFlash(temp_dir) == 0) {
+ ret = 0;
+ }
+
+cleanup:
+ RemoveDir(temp_dir);
+ return ret;
+}
+
+int main(int argc, const char *argv[]) {
+ if (argc > 2 && !has_dash_D(argc, argv)) {
+ const char *mtd_device = find_mtd_device(argc, argv);
+ if (mtd_device) {
+ return wrap_cgpt(argc, argv, mtd_device);
+ }
+ }
+
+ // Forward to cgpt as-is. Real cgpt has been renamed cgpt.bin.
+ char *real_cgpt;
+ if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) {
+ return -1;
+ }
+ argv[0] = real_cgpt;
+ return execv(argv[0], (char * const *)argv);
+}