diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/.gitignore | 1 | ||||
| -rw-r--r-- | tools/Makefile | 25 | ||||
| -rwxr-xr-x | tools/checkpatch.pl | 1 | ||||
| -rw-r--r-- | tools/fit_image.c | 44 | ||||
| -rw-r--r-- | tools/image-host.c | 527 | ||||
| -rw-r--r-- | tools/mkimage.c | 36 | ||||
| -rw-r--r-- | tools/mkimage.h | 16 | ||||
| -rw-r--r-- | tools/pblimage.c | 32 | ||||
| -rw-r--r-- | tools/proftool.c | 611 | 
9 files changed, 1261 insertions, 32 deletions
diff --git a/tools/.gitignore b/tools/.gitignore index 9bce71947..a7fee26cd 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -9,6 +9,7 @@  /mxsboot  /ncb  /ncp +/proftool  /ubsha1  /xway-swap-bytes  /*.exe diff --git a/tools/Makefile b/tools/Makefile index 4630f03dc..46159b23d 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -74,11 +74,13 @@ BIN_FILES-$(CONFIG_MX28) += mxsboot$(SFX)  BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX)  BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX)  BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX) +BIN_FILES-y += proftool(SFX)  # Source files which exist outside the tools directory  EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o  EXT_OBJ_FILES-y += common/image.o  EXT_OBJ_FILES-$(CONFIG_FIT) += common/image-fit.o +EXT_OBJ_FILES-y += common/image-sig.o  EXT_OBJ_FILES-y += lib/crc32.o  EXT_OBJ_FILES-y += lib/md5.o  EXT_OBJ_FILES-y += lib/sha1.o @@ -87,6 +89,7 @@ EXT_OBJ_FILES-y += lib/sha1.o  OBJ_FILES-$(CONFIG_LCD_LOGO) += bmp_logo.o  OBJ_FILES-$(CONFIG_VIDEO_LOGO) += bmp_logo.o  NOPED_OBJ_FILES-y += default_image.o +NOPED_OBJ_FILES-y += proftool.o  OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc.o  NOPED_OBJ_FILES-y += fit_image.o  OBJ_FILES-$(CONFIG_CMD_NET) += gen_eth_addr.o @@ -122,6 +125,9 @@ LIBFDT_OBJ_FILES-y += fdt_rw.o  LIBFDT_OBJ_FILES-y += fdt_strerror.o  LIBFDT_OBJ_FILES-y += fdt_wip.o +# RSA objects +RSA_OBJ_FILES-$(CONFIG_FIT_SIGNATURE) += rsa-sign.o +  # Generated LCD/video logo  LOGO_H = $(OBJTREE)/include/bmp_logo.h  LOGO_DATA_H = $(OBJTREE)/include/bmp_logo_data.h @@ -149,8 +155,14 @@ endif # !LOGO_BMP  HOSTSRCS += $(addprefix $(SRCTREE)/,$(EXT_OBJ_FILES-y:.o=.c))  HOSTSRCS += $(addprefix $(SRCTREE)/tools/,$(OBJ_FILES-y:.o=.c))  HOSTSRCS += $(addprefix $(SRCTREE)/lib/libfdt/,$(LIBFDT_OBJ_FILES-y:.o=.c)) +HOSTSRCS += $(addprefix $(SRCTREE)/lib/rsa/,$(RSA_OBJ_FILES-y:.o=.c))  BINS	:= $(addprefix $(obj),$(sort $(BIN_FILES-y)))  LIBFDT_OBJS	:= $(addprefix $(obj),$(LIBFDT_OBJ_FILES-y)) +RSA_OBJS	:= $(addprefix $(obj),$(RSA_OBJ_FILES-y)) + +# We cannot check CONFIG_FIT_SIGNATURE here since it is not set on the host +FIT_SIG_OBJ_FILES	:= image-sig.o +FIT_SIG_OBJS		:= $(addprefix $(obj),$(FIT_SIG_OBJ_FILES))  HOSTOBJS := $(addprefix $(obj),$(OBJ_FILES-y))  NOPEDOBJS := $(addprefix $(obj),$(NOPED_OBJ_FILES-y)) @@ -180,6 +192,10 @@ $(obj)bmp_logo$(SFX):	$(obj)bmp_logo.o  	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^  	$(HOSTSTRIP) $@ +$(obj)proftool(SFX):	$(obj)proftool.o +	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ +	$(HOSTSTRIP) $@ +  $(obj)envcrc$(SFX):	$(obj)crc32.o $(obj)env_embedded.o $(obj)envcrc.o $(obj)sha1.o  	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ @@ -207,6 +223,7 @@ $(obj)mkimage$(SFX):	$(obj)aisimage.o \  			$(obj)image-fit.o \  			$(obj)image.o \  			$(obj)image-host.o \ +			$(FIT_SIG_OBJS) \  			$(obj)imximage.o \  			$(obj)kwbimage.o \  			$(obj)pblimage.o \ @@ -216,8 +233,9 @@ $(obj)mkimage$(SFX):	$(obj)aisimage.o \  			$(obj)omapimage.o \  			$(obj)sha1.o \  			$(obj)ublimage.o \ -			$(LIBFDT_OBJS) -	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ +			$(LIBFDT_OBJS) \ +			$(RSA_OBJS) +	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTLIBS)  	$(HOSTSTRIP) $@  $(obj)mk$(BOARD)spl$(SFX):	$(obj)mkexynosspl.o @@ -253,6 +271,9 @@ $(obj)%.o: $(SRCTREE)/lib/%.c  $(obj)%.o: $(SRCTREE)/lib/libfdt/%.c  	$(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< +$(obj)%.o: $(SRCTREE)/lib/rsa/%.c +	$(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< +  subdirs:  ifeq ($(TOOLSUBDIRS),)  	@: diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index 9f2390187..896e2bc98 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -273,6 +273,7 @@ our $logFunctions = qr{(?x:  	WARN(?:_RATELIMIT|_ONCE|)|  	panic|  	debug| +	printf|  	MODULE_[A-Z_]+  )}; diff --git a/tools/fit_image.c b/tools/fit_image.c index cc123dd37..281c2bda1 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -105,9 +105,11 @@ static int fit_handle_file (struct mkimage_params *params)  {  	char tmpfile[MKIMAGE_MAX_TMPFILE_LEN];  	char cmd[MKIMAGE_MAX_DTC_CMDLINE_LEN]; -	int tfd; +	int tfd, destfd = 0; +	void *dest_blob = NULL;  	struct stat sbuf;  	void *ptr; +	off_t destfd_size = 0;  	/* Flattened Image Tree (FIT) format  handling */  	debug ("FIT format handling\n"); @@ -122,29 +124,44 @@ static int fit_handle_file (struct mkimage_params *params)  	}  	sprintf (tmpfile, "%s%s", params->imagefile, MKIMAGE_TMPFILE_SUFFIX); -	/* dtc -I dts -O dtb -p 500 datafile > tmpfile */ -	sprintf (cmd, "%s %s %s > %s", -		MKIMAGE_DTC, params->dtc, params->datafile, tmpfile); -	debug ("Trying to execute \"%s\"\n", cmd); +	/* We either compile the source file, or use the existing FIT image */ +	if (params->datafile) { +		/* dtc -I dts -O dtb -p 500 datafile > tmpfile */ +		snprintf(cmd, sizeof(cmd), "%s %s %s > %s", +			 MKIMAGE_DTC, params->dtc, params->datafile, tmpfile); +		debug("Trying to execute \"%s\"\n", cmd); +	} else { +		snprintf(cmd, sizeof(cmd), "cp %s %s", +			 params->imagefile, tmpfile); +	}  	if (system (cmd) == -1) {  		fprintf (stderr, "%s: system(%s) failed: %s\n",  				params->cmdname, cmd, strerror(errno));  		goto err_system;  	} +	if (params->keydest) { +		destfd = mmap_fdt(params, params->keydest, &dest_blob, &sbuf); +		if (destfd < 0) +			goto err_keydest; +		destfd_size = sbuf.st_size; +	} +  	tfd = mmap_fdt(params, tmpfile, &ptr, &sbuf);  	if (tfd < 0)  		goto err_mmap;  	/* set hashes for images in the blob */ -	if (fit_add_verification_data(ptr)) { -		fprintf (stderr, "%s Can't add hashes to FIT blob", -				params->cmdname); +	if (fit_add_verification_data(params->keydir, +				      dest_blob, ptr, params->comment, +				      params->require_keys)) { +		fprintf(stderr, "%s Can't add hashes to FIT blob\n", +			params->cmdname);  		goto err_add_hashes;  	} -	/* add a timestamp at offset 0 i.e., root  */ -	if (fit_set_timestamp (ptr, 0, sbuf.st_mtime)) { +	/* for first image creation, add a timestamp at offset 0 i.e., root  */ +	if (params->datafile && fit_set_timestamp(ptr, 0, sbuf.st_mtime)) {  		fprintf (stderr, "%s: Can't add image timestamp\n",  				params->cmdname);  		goto err_add_timestamp; @@ -153,6 +170,10 @@ static int fit_handle_file (struct mkimage_params *params)  	munmap ((void *)ptr, sbuf.st_size);  	close (tfd); +	if (dest_blob) { +		munmap(dest_blob, destfd_size); +		close(destfd); +	}  	if (rename (tmpfile, params->imagefile) == -1) {  		fprintf (stderr, "%s: Can't rename %s to %s: %s\n", @@ -168,6 +189,9 @@ err_add_timestamp:  err_add_hashes:  	munmap(ptr, sbuf.st_size);  err_mmap: +	if (dest_blob) +		munmap(dest_blob, destfd_size); +err_keydest:  err_system:  	unlink(tmpfile);  	return -1; diff --git a/tools/image-host.c b/tools/image-host.c index d944d0ff4..932384bea 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -26,12 +26,8 @@   */  #include "mkimage.h" -#include <bootstage.h>  #include <image.h> -#include <sha1.h> -#include <time.h> -#include <u-boot/crc.h> -#include <u-boot/md5.h> +#include <version.h>  /**   * fit_set_hash_value - set hash value in requested has node @@ -108,9 +104,165 @@ static int fit_image_process_hash(void *fit, const char *image_name,  }  /** - * fit_image_add_verification_data() - calculate/set hash data for image node + * fit_image_write_sig() - write the signature to a FIT   * - * This adds hash values for a component image node. + * This writes the signature and signer data to the FIT. + * + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: signature value to be set + * @value_len: signature value length + * @comment: Text comment to write (NULL for none) + * + * returns + *     0, on success + *     -FDT_ERR_..., on failure + */ +static int fit_image_write_sig(void *fit, int noffset, uint8_t *value, +		int value_len, const char *comment, const char *region_prop, +		int region_proplen) +{ +	int string_size; +	int ret; + +	/* +	 * Get the current string size, before we update the FIT and add +	 * more +	 */ +	string_size = fdt_size_dt_strings(fit); + +	ret = fdt_setprop(fit, noffset, FIT_VALUE_PROP, value, value_len); +	if (!ret) { +		ret = fdt_setprop_string(fit, noffset, "signer-name", +					 "mkimage"); +	} +	if (!ret) { +		ret = fdt_setprop_string(fit, noffset, "signer-version", +				  PLAIN_VERSION); +	} +	if (comment && !ret) +		ret = fdt_setprop_string(fit, noffset, "comment", comment); +	if (!ret) +		ret = fit_set_timestamp(fit, noffset, time(NULL)); +	if (region_prop && !ret) { +		uint32_t strdata[2]; + +		ret = fdt_setprop(fit, noffset, "hashed-nodes", +				   region_prop, region_proplen); +		strdata[0] = 0; +		strdata[1] = cpu_to_fdt32(string_size); +		if (!ret) { +			ret = fdt_setprop(fit, noffset, "hashed-strings", +					  strdata, sizeof(strdata)); +		} +	} + +	return ret; +} + +static int fit_image_setup_sig(struct image_sign_info *info, +		const char *keydir, void *fit, const char *image_name, +		int noffset, const char *require_keys) +{ +	const char *node_name; +	char *algo_name; + +	node_name = fit_get_name(fit, noffset, NULL); +	if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { +		printf("Can't get algo property for '%s' signature node in '%s' image node\n", +		       node_name, image_name); +		return -1; +	} + +	memset(info, '\0', sizeof(*info)); +	info->keydir = keydir; +	info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); +	info->fit = fit; +	info->node_offset = noffset; +	info->algo = image_get_sig_algo(algo_name); +	info->require_keys = require_keys; +	if (!info->algo) { +		printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n", +		       algo_name, node_name, image_name); +		return -1; +	} + +	return 0; +} + +/** + * fit_image_process_sig- Process a single subnode of the images/ node + * + * Check each subnode and process accordingly. For signature nodes we + * generate a signed hash of the supplised data and store it in the node. + * + * @keydir:	Directory containing keys to use for signing + * @keydest:	Destination FDT blob to write public keys into + * @fit:	pointer to the FIT format image header + * @image_name:	name of image being processes (used to display errors) + * @noffset:	subnode offset + * @data:	data to process + * @size:	size of data in bytes + * @comment:	Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' + * @return 0 if ok, -1 on error + */ +static int fit_image_process_sig(const char *keydir, void *keydest, +		void *fit, const char *image_name, +		int noffset, const void *data, size_t size, +		const char *comment, int require_keys) +{ +	struct image_sign_info info; +	struct image_region region; +	const char *node_name; +	uint8_t *value; +	uint value_len; +	int ret; + +	if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset, +				require_keys ? "image" : NULL)) +		return -1; + +	node_name = fit_get_name(fit, noffset, NULL); +	region.data = data; +	region.size = size; +	ret = info.algo->sign(&info, ®ion, 1, &value, &value_len); +	if (ret) { +		printf("Failed to sign '%s' signature node in '%s' image node: %d\n", +		       node_name, image_name, ret); + +		/* We allow keys to be missing */ +		if (ret == -ENOENT) +			return 0; +		return -1; +	} + +	ret = fit_image_write_sig(fit, noffset, value, value_len, comment, +			NULL, 0); +	if (ret) { +		printf("Can't write signature for '%s' signature node in '%s' image node: %s\n", +		       node_name, image_name, fdt_strerror(ret)); +		return -1; +	} +	free(value); + +	/* Get keyname again, as FDT has changed and invalidated our pointer */ +	info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + +	/* Write the public key into the supplied FDT file */ +	if (keydest && info.algo->add_verify_data(&info, keydest)) { +		printf("Failed to add verification data for '%s' signature node in '%s' image node\n", +		       node_name, image_name); +		return -1; +	} + +	return 0; +} + +/** + * fit_image_add_verification_data() - calculate/set verig. data for image node + * + * This adds hash and signature values for an component image node.   *   * All existing hash subnodes are checked, if algorithm property is set to   * one of the supported hash algorithms, hash value is computed and @@ -133,11 +285,17 @@ static int fit_image_process_hash(void *fit, const char *image_name,   *   * For signature details, please see doc/uImage.FIT/signature.txt   * + * @keydir	Directory containing *.key and *.crt files (or NULL) + * @keydest	FDT Blob to write public keys into (NULL if none)   * @fit:	Pointer to the FIT format image header   * @image_noffset: Requested component image node + * @comment:	Comment to add to signature nodes + * @require_keys: Mark all keys as 'required'   * @return: 0 on success, <0 on failure   */ -int fit_image_add_verification_data(void *fit, int image_noffset) +int fit_image_add_verification_data(const char *keydir, void *keydest, +		void *fit, int image_noffset, const char *comment, +		int require_keys)  {  	const char *image_name;  	const void *data; @@ -169,6 +327,12 @@ int fit_image_add_verification_data(void *fit, int image_noffset)  			     strlen(FIT_HASH_NODENAME))) {  			ret = fit_image_process_hash(fit, image_name, noffset,  						data, size); +		} else if (IMAGE_ENABLE_SIGN && keydir && +			   !strncmp(node_name, FIT_SIG_NODENAME, +				strlen(FIT_SIG_NODENAME))) { +			ret = fit_image_process_sig(keydir, keydest, +				fit, image_name, noffset, data, size, +				comment, require_keys);  		}  		if (ret)  			return -1; @@ -177,9 +341,326 @@ int fit_image_add_verification_data(void *fit, int image_noffset)  	return 0;  } -int fit_add_verification_data(void *fit) +struct strlist { +	int count; +	char **strings; +}; + +static void strlist_init(struct strlist *list) +{ +	memset(list, '\0', sizeof(*list)); +} + +static void strlist_free(struct strlist *list) +{ +	int i; + +	for (i = 0; i < list->count; i++) +		free(list->strings[i]); +	free(list->strings); +} + +static int strlist_add(struct strlist *list, const char *str) +{ +	char *dup; + +	dup = strdup(str); +	list->strings = realloc(list->strings, +				(list->count + 1) * sizeof(char *)); +	if (!list || !str) +		return -1; +	list->strings[list->count++] = dup; + +	return 0; +} + +static const char *fit_config_get_image_list(void *fit, int noffset, +		int *lenp, int *allow_missingp) +{ +	static const char default_list[] = FIT_KERNEL_PROP "\0" +			FIT_FDT_PROP; +	const char *prop; + +	/* If there is an "image" property, use that */ +	prop = fdt_getprop(fit, noffset, "sign-images", lenp); +	if (prop) { +		*allow_missingp = 0; +		return *lenp ? prop : NULL; +	} + +	/* Default image list */ +	*allow_missingp = 1; +	*lenp = sizeof(default_list); + +	return default_list; +} + +static int fit_config_get_hash_list(void *fit, int conf_noffset, +				    int sig_offset, struct strlist *node_inc) +{ +	int allow_missing; +	const char *prop, *iname, *end; +	const char *conf_name, *sig_name; +	char name[200], path[200]; +	int image_count; +	int ret, len; + +	conf_name = fit_get_name(fit, conf_noffset, NULL); +	sig_name = fit_get_name(fit, sig_offset, NULL); + +	/* +	 * Build a list of nodes we need to hash. We always need the root +	 * node and the configuration. +	 */ +	strlist_init(node_inc); +	snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, conf_name); +	if (strlist_add(node_inc, "/") || +	    strlist_add(node_inc, name)) +		goto err_mem; + +	/* Get a list of images that we intend to sign */ +	prop = fit_config_get_image_list(fit, conf_noffset, &len, +					&allow_missing); +	if (!prop) +		return 0; + +	/* Locate the images */ +	end = prop + len; +	image_count = 0; +	for (iname = prop; iname < end; iname += strlen(iname) + 1) { +		int noffset; +		int image_noffset; +		int hash_count; + +		image_noffset = fit_conf_get_prop_node(fit, conf_noffset, +						       iname); +		if (image_noffset < 0) { +			printf("Failed to find image '%s' in  configuration '%s/%s'\n", +			       iname, conf_name, sig_name); +			if (allow_missing) +				continue; + +			return -ENOENT; +		} + +		ret = fdt_get_path(fit, image_noffset, path, sizeof(path)); +		if (ret < 0) +			goto err_path; +		if (strlist_add(node_inc, path)) +			goto err_mem; + +		snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, +			 conf_name); + +		/* Add all this image's hashes */ +		hash_count = 0; +		for (noffset = fdt_first_subnode(fit, image_noffset); +		     noffset >= 0; +		     noffset = fdt_next_subnode(fit, noffset)) { +			const char *name = fit_get_name(fit, noffset, NULL); + +			if (strncmp(name, FIT_HASH_NODENAME, +				    strlen(FIT_HASH_NODENAME))) +				continue; +			ret = fdt_get_path(fit, noffset, path, sizeof(path)); +			if (ret < 0) +				goto err_path; +			if (strlist_add(node_inc, path)) +				goto err_mem; +			hash_count++; +		} + +		if (!hash_count) { +			printf("Failed to find any hash nodes in configuration '%s/%s' image '%s' - without these it is not possible to verify this image\n", +			       conf_name, sig_name, iname); +			return -ENOMSG; +		} + +		image_count++; +	} + +	if (!image_count) { +		printf("Failed to find any images for configuration '%s/%s'\n", +		       conf_name, sig_name); +		return -ENOMSG; +	} + +	return 0; + +err_mem: +	printf("Out of memory processing configuration '%s/%s'\n", conf_name, +	       sig_name); +	return -ENOMEM; + +err_path: +	printf("Failed to get path for image '%s' in configuration '%s/%s': %s\n", +	       iname, conf_name, sig_name, fdt_strerror(ret)); +	return -ENOENT; +} + +static int fit_config_get_data(void *fit, int conf_noffset, int noffset, +		struct image_region **regionp, int *region_countp, +		char **region_propp, int *region_proplen)  { -	int images_noffset; +	char * const exc_prop[] = {"data"}; +	struct strlist node_inc; +	struct image_region *region; +	struct fdt_region fdt_regions[100]; +	const char *conf_name, *sig_name; +	char path[200]; +	int count, i; +	char *region_prop; +	int ret, len; + +	conf_name = fit_get_name(fit, conf_noffset, NULL); +	sig_name = fit_get_name(fit, conf_noffset, NULL); +	debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name); + +	/* Get a list of nodes we want to hash */ +	ret = fit_config_get_hash_list(fit, conf_noffset, noffset, &node_inc); +	if (ret) +		return ret; + +	/* Get a list of regions to hash */ +	count = fdt_find_regions(fit, node_inc.strings, node_inc.count, +			exc_prop, ARRAY_SIZE(exc_prop), +			fdt_regions, ARRAY_SIZE(fdt_regions), +			path, sizeof(path), 1); +	if (count < 0) { +		printf("Failed to hash configuration '%s/%s': %s\n", conf_name, +		       sig_name, fdt_strerror(ret)); +		return -EIO; +	} +	if (count == 0) { +		printf("No data to hash for configuration '%s/%s': %s\n", +		       conf_name, sig_name, fdt_strerror(ret)); +		return -EINVAL; +	} + +	/* Build our list of data blocks */ +	region = fit_region_make_list(fit, fdt_regions, count, NULL); +	if (!region) { +		printf("Out of memory hashing configuration '%s/%s'\n", +		       conf_name, sig_name); +		return -ENOMEM; +	} + +	/* Create a list of all hashed properties */ +	debug("Hash nodes:\n"); +	for (i = len = 0; i < node_inc.count; i++) { +		debug("   %s\n", node_inc.strings[i]); +		len += strlen(node_inc.strings[i]) + 1; +	} +	region_prop = malloc(len); +	if (!region_prop) { +		printf("Out of memory setting up regions for configuration '%s/%s'\n", +		       conf_name, sig_name); +		return -ENOMEM; +	} +	for (i = len = 0; i < node_inc.count; +	     len += strlen(node_inc.strings[i]) + 1, i++) +		strcpy(region_prop + len, node_inc.strings[i]); +	strlist_free(&node_inc); + +	*region_countp = count; +	*regionp = region; +	*region_propp = region_prop; +	*region_proplen = len; + +	return 0; +} + +static int fit_config_process_sig(const char *keydir, void *keydest, +		void *fit, const char *conf_name, int conf_noffset, +		int noffset, const char *comment, int require_keys) +{ +	struct image_sign_info info; +	const char *node_name; +	struct image_region *region; +	char *region_prop; +	int region_proplen; +	int region_count; +	uint8_t *value; +	uint value_len; +	int ret; + +	node_name = fit_get_name(fit, noffset, NULL); +	if (fit_config_get_data(fit, conf_noffset, noffset, ®ion, +				®ion_count, ®ion_prop, ®ion_proplen)) +		return -1; + +	if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset, +				require_keys ? "conf" : NULL)) +		return -1; + +	ret = info.algo->sign(&info, region, region_count, &value, &value_len); +	free(region); +	if (ret) { +		printf("Failed to sign '%s' signature node in '%s' conf node\n", +		       node_name, conf_name); + +		/* We allow keys to be missing */ +		if (ret == -ENOENT) +			return 0; +		return -1; +	} + +	if (fit_image_write_sig(fit, noffset, value, value_len, comment, +				region_prop, region_proplen)) { +		printf("Can't write signature for '%s' signature node in '%s' conf node\n", +		       node_name, conf_name); +		return -1; +	} +	free(value); +	free(region_prop); + +	/* Get keyname again, as FDT has changed and invalidated our pointer */ +	info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + +	/* Write the public key into the supplied FDT file */ +	if (keydest && info.algo->add_verify_data(&info, keydest)) { +		printf("Failed to add verification data for '%s' signature node in '%s' image node\n", +		       node_name, conf_name); +		return -1; +	} + +	return 0; +} + +static int fit_config_add_verification_data(const char *keydir, void *keydest, +		void *fit, int conf_noffset, const char *comment, +		int require_keys) +{ +	const char *conf_name; +	int noffset; + +	conf_name = fit_get_name(fit, conf_noffset, NULL); + +	/* Process all hash subnodes of the configuration node */ +	for (noffset = fdt_first_subnode(fit, conf_noffset); +	     noffset >= 0; +	     noffset = fdt_next_subnode(fit, noffset)) { +		const char *node_name; +		int ret = 0; + +		node_name = fit_get_name(fit, noffset, NULL); +		if (!strncmp(node_name, FIT_SIG_NODENAME, +			     strlen(FIT_SIG_NODENAME))) { +			ret = fit_config_process_sig(keydir, keydest, +				fit, conf_name, conf_noffset, noffset, comment, +				require_keys); +		} +		if (ret) +			return ret; +	} + +	return 0; +} + +int fit_add_verification_data(const char *keydir, void *keydest, void *fit, +			      const char *comment, int require_keys) +{ +	int images_noffset, confs_noffset;  	int noffset;  	int ret; @@ -199,7 +680,31 @@ int fit_add_verification_data(void *fit)  		 * Direct child node of the images parent node,  		 * i.e. component image node.  		 */ -		ret = fit_image_add_verification_data(fit, noffset); +		ret = fit_image_add_verification_data(keydir, keydest, +				fit, noffset, comment, require_keys); +		if (ret) +			return ret; +	} + +	/* If there are no keys, we can't sign configurations */ +	if (!IMAGE_ENABLE_SIGN || !keydir) +		return 0; + +	/* Find configurations parent node offset */ +	confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); +	if (confs_noffset < 0) { +		printf("Can't find images parent node '%s' (%s)\n", +		       FIT_IMAGES_PATH, fdt_strerror(confs_noffset)); +		return -ENOENT; +	} + +	/* Process its subnodes, print out component images details */ +	for (noffset = fdt_first_subnode(fit, confs_noffset); +	     noffset >= 0; +	     noffset = fdt_next_subnode(fit, noffset)) { +		ret = fit_config_add_verification_data(keydir, keydest, +						       fit, noffset, comment, +						       require_keys);  		if (ret)  			return ret;  	} diff --git a/tools/mkimage.c b/tools/mkimage.c index e43b09f76..d312844e9 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -183,6 +183,11 @@ main (int argc, char **argv)  					genimg_get_arch_id (*++argv)) < 0)  					usage ();  				goto NXTARG; +			case 'c': +				if (--argc <= 0) +					usage(); +				params.comment = *++argv; +				goto NXTARG;  			case 'C':  				if ((--argc <= 0) ||  					(params.comp = @@ -240,19 +245,34 @@ main (int argc, char **argv)  			case 'f':  				if (--argc <= 0)  					usage (); +				params.datafile = *++argv; +				/* no break */ +			case 'F':  				/*  				 * The flattened image tree (FIT) format  				 * requires a flattened device tree image type  				 */  				params.type = IH_TYPE_FLATDT; -				params.datafile = *++argv;  				params.fflag = 1;  				goto NXTARG; +			case 'k': +				if (--argc <= 0) +					usage(); +				params.keydir = *++argv; +				goto NXTARG; +			case 'K': +				if (--argc <= 0) +					usage(); +				params.keydest = *++argv; +				goto NXTARG;  			case 'n':  				if (--argc <= 0)  					usage ();  				params.imagename = *++argv;  				goto NXTARG; +			case 'r': +				params.require_keys = 1; +				break;  			case 'R':  				if (--argc <= 0)  					usage(); @@ -623,8 +643,20 @@ usage ()  			 "          -d ==> use image data from 'datafile'\n"  			 "          -x ==> set XIP (execute in place)\n",  		params.cmdname); -	fprintf (stderr, "       %s [-D dtc_options] -f fit-image.its fit-image\n", +	fprintf(stderr, "       %s [-D dtc_options] [-f fit-image.its|-F] fit-image\n",  		params.cmdname); +	fprintf(stderr, "          -D => set options for device tree compiler\n" +			"          -f => input filename for FIT source\n"); +#ifdef CONFIG_FIT_SIGNATURE +	fprintf(stderr, "Signing / verified boot options: [-k keydir] [-K dtb] [ -c <comment>] [-r]\n" +			"          -k => set directory containing private keys\n" +			"          -K => write public keys to this .dtb file\n" +			"          -c => add comment in signature node\n" +			"          -F => re-sign existing FIT image\n" +			"          -r => mark keys used as 'required' in dtb\n"); +#else +	fprintf(stderr, "Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined)\n"); +#endif  	fprintf (stderr, "       %s -V ==> print version information and exit\n",  		params.cmdname); diff --git a/tools/mkimage.h b/tools/mkimage.h index e07a6157e..1d9984e1a 100644 --- a/tools/mkimage.h +++ b/tools/mkimage.h @@ -44,12 +44,24 @@  #define ARRAY_SIZE(x)		(sizeof(x) / sizeof((x)[0])) +static inline void *map_sysmem(ulong paddr, unsigned long len) +{ +	return (void *)(uintptr_t)paddr; +} + +static inline ulong map_to_sysmem(void *ptr) +{ +	return (ulong)(uintptr_t)ptr; +} +  #define MKIMAGE_TMPFILE_SUFFIX		".tmp"  #define MKIMAGE_MAX_TMPFILE_LEN		256  #define MKIMAGE_DEFAULT_DTC_OPTIONS	"-I dts -O dtb -p 500"  #define MKIMAGE_MAX_DTC_CMDLINE_LEN	512  #define MKIMAGE_DTC			"dtc"   /* assume dtc is in $PATH */ +#define IH_ARCH_DEFAULT		IH_ARCH_INVALID +  /*   * This structure defines all such variables those are initialized by   * mkimage main core and need to be referred by image type specific @@ -75,6 +87,10 @@ struct mkimage_params {  	char *datafile;  	char *imagefile;  	char *cmdname; +	const char *keydir;	/* Directory holding private keys */ +	const char *keydest;	/* Destination .dtb for public key */ +	const char *comment;	/* Comment to add to signature node */ +	int require_keys;	/* 1 to mark signing keys as 'required' */  };  /* diff --git a/tools/pblimage.c b/tools/pblimage.c index 508a747a3..5f39dc5bb 100644 --- a/tools/pblimage.c +++ b/tools/pblimage.c @@ -26,18 +26,14 @@  #include "pblimage.h"  /* - * The PBL can load up to 64 bytes at a time, so we split the U-Boot - * image into 64 byte chunks. PBL needs a command for each piece, of - * the form "81xxxxxx", where "xxxxxx" is the offset. SYS_TEXT_BASE - * is 0xFFF80000 for PBL boot, and PBL only cares about low 24-bit, - * so it starts from 0x81F80000. + * Initialize to an invalid value.   */ -static uint32_t next_pbl_cmd = 0x81F80000; +static uint32_t next_pbl_cmd = 0x82000000;  /*   * need to store all bytes in memory for calculating crc32, then write the   * bytes to image file for PBL boot.   */ -static unsigned char mem_buf[600000]; +static unsigned char mem_buf[1000000];  static unsigned char *pmem_buf = mem_buf;  static int pbl_size;  static char *fname = "Unknown"; @@ -52,6 +48,27 @@ static union  #define ENDIANNESS ((char)endian_test.l) +/* + * The PBL can load up to 64 bytes at a time, so we split the U-Boot + * image into 64 byte chunks. PBL needs a command for each piece, of + * the form "81xxxxxx", where "xxxxxx" is the offset. Calculate the + * start offset by subtracting the size of the u-boot image from the + * top of the allowable 24-bit range. + */ +static void init_next_pbl_cmd(FILE *fp_uboot) +{ +	struct stat st; +	int fd = fileno(fp_uboot); + +	if (fstat(fd, &st) == -1) { +		printf("Error: Could not determine u-boot image size. %s\n", +			strerror(errno)); +		exit(EXIT_FAILURE); +	} + +	next_pbl_cmd = 0x82000000 - st.st_size; +} +  static void generate_pbl_cmd(void)  {  	uint32_t val = next_pbl_cmd; @@ -80,6 +97,7 @@ static void pbl_fget(size_t size, FILE *stream)  /* load split u-boot with PBI command 81xxxxxx. */  static void load_uboot(FILE *fp_uboot)  { +	init_next_pbl_cmd(fp_uboot);  	while (next_pbl_cmd < 0x82000000) {  		generate_pbl_cmd();  		pbl_fget(64, fp_uboot); diff --git a/tools/proftool.c b/tools/proftool.c new file mode 100644 index 000000000..aa05e77ec --- /dev/null +++ b/tools/proftool.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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 02111-1307 USA + */ + +/* Decode and dump U-Boot profiling information */ + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <regex.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> + +#include <compiler.h> +#include <trace.h> + +#define MAX_LINE_LEN 500 + +enum { +	FUNCF_TRACE	= 1 << 0,	/* Include this function in trace */ +}; + +struct func_info { +	unsigned long offset; +	const char *name; +	unsigned long code_size; +	unsigned long call_count; +	unsigned flags; +	/* the section this function is in */ +	struct objsection_info *objsection; +}; + +enum trace_line_type { +	TRACE_LINE_INCLUDE, +	TRACE_LINE_EXCLUDE, +}; + +struct trace_configline_info { +	struct trace_configline_info *next; +	enum trace_line_type type; +	const char *name;	/* identifier name / wildcard */ +	regex_t regex;		/* Regex to use if name starts with / */ +}; + +/* The contents of the trace config file */ +struct trace_configline_info *trace_config_head; + +struct func_info *func_list; +int func_count; +struct trace_call *call_list; +int call_count; +int verbose;	/* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */ +unsigned long text_offset;		/* text address of first function */ + +static void outf(int level, const char *fmt, ...) +		__attribute__ ((format (__printf__, 2, 3))); +#define error(fmt, b...) outf(0, fmt, ##b) +#define warn(fmt, b...) outf(1, fmt, ##b) +#define notice(fmt, b...) outf(2, fmt, ##b) +#define info(fmt, b...) outf(3, fmt, ##b) +#define debug(fmt, b...) outf(4, fmt, ##b) + + +static void outf(int level, const char *fmt, ...) +{ +	if (verbose >= level) { +		va_list args; + +		va_start(args, fmt); +		vfprintf(stderr, fmt, args); +		va_end(args); +	} +} + +static void usage(void) +{ +	fprintf(stderr, +		"Usage: proftool -cds -v3 <cmd> <profdata>\n" +		"\n" +		"Commands\n" +		"   dump-ftrace\t\tDump out textual data in ftrace format\n" +		"\n" +		"Options:\n" +		"   -m <map>\tSpecify Systen.map file\n" +		"   -t <trace>\tSpecific trace data file (from U-Boot)\n" +		"   -v <0-4>\tSpecify verbosity\n"); +	exit(EXIT_FAILURE); +} + +static int h_cmp_offset(const void *v1, const void *v2) +{ +	const struct func_info *f1 = v1, *f2 = v2; + +	return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE); +} + +static int read_system_map(FILE *fin) +{ +	unsigned long offset, start = 0; +	struct func_info *func; +	char buff[MAX_LINE_LEN]; +	char symtype; +	char symname[MAX_LINE_LEN + 1]; +	int linenum; +	int alloced; + +	for (linenum = 1, alloced = func_count = 0;; linenum++) { +		int fields = 0; + +		if (fgets(buff, sizeof(buff), fin)) +			fields = sscanf(buff, "%lx %c %100s\n", &offset, +				&symtype, symname); +		if (fields == 2) { +			continue; +		} else if (feof(fin)) { +			break; +		} else if (fields < 2) { +			error("Map file line %d: invalid format\n", linenum); +			return 1; +		} + +		/* Must be a text symbol */ +		symtype = tolower(symtype); +		if (symtype != 't' && symtype != 'w') +			continue; + +		if (func_count == alloced) { +			alloced += 256; +			func_list = realloc(func_list, +					sizeof(struct func_info) * alloced); +			assert(func_list); +		} +		if (!func_count) +			start = offset; + +		func = &func_list[func_count++]; +		memset(func, '\0', sizeof(*func)); +		func->offset = offset - start; +		func->name = strdup(symname); +		func->flags = FUNCF_TRACE;	/* trace by default */ + +		/* Update previous function's code size */ +		if (func_count > 1) +			func[-1].code_size = func->offset - func[-1].offset; +	} +	notice("%d functions found in map file\n", func_count); +	text_offset = start; +	return 0; +} + +static int read_data(FILE *fin, void *buff, int size) +{ +	int err; + +	err = fread(buff, 1, size, fin); +	if (!err) +		return 1; +	if (err != size) { +		error("Cannot read profile file at pos %ld\n", ftell(fin)); +		return -1; +	} +	return 0; +} + +static struct func_info *find_func_by_offset(uint32_t offset) +{ +	struct func_info key, *found; + +	key.offset = offset; +	found = bsearch(&key, func_list, func_count, sizeof(struct func_info), +			h_cmp_offset); + +	return found; +} + +/* This finds the function which contains the given offset */ +static struct func_info *find_caller_by_offset(uint32_t offset) +{ +	int low;	/* least function that could be a match */ +	int high;	/* greated function that could be a match */ +	struct func_info key; + +	low = 0; +	high = func_count - 1; +	key.offset = offset; +	while (high > low + 1) { +		int mid = (low + high) / 2; +		int result; + +		result = h_cmp_offset(&key, &func_list[mid]); +		if (result > 0) +			low = mid; +		else if (result < 0) +			high = mid; +		else +			return &func_list[mid]; +	} + +	return low >= 0 ? &func_list[low] : NULL; +} + +static int read_calls(FILE *fin, int count) +{ +	struct trace_call *call_data; +	int i; + +	notice("call count: %d\n", count); +	call_list = (struct trace_call *)calloc(count, sizeof(*call_data)); +	if (!call_list) { +		error("Cannot allocate call_list\n"); +		return -1; +	} +	call_count = count; + +	call_data = call_list; +	for (i = 0; i < count; i++, call_data++) { +		if (read_data(fin, call_data, sizeof(*call_data))) +			return 1; +	} +	return 0; +} + +static int read_profile(FILE *fin, int *not_found) +{ +	struct trace_output_hdr hdr; + +	*not_found = 0; +	while (!feof(fin)) { +		int err; + +		err = read_data(fin, &hdr, sizeof(hdr)); +		if (err == 1) +			break; /* EOF */ +		else if (err) +			return 1; + +		switch (hdr.type) { +		case TRACE_CHUNK_FUNCS: +			/* Ignored at present */ +			break; + +		case TRACE_CHUNK_CALLS: +			if (read_calls(fin, hdr.rec_count)) +				return 1; +			break; +		} +	} +	return 0; +} + +static int read_map_file(const char *fname) +{ +	FILE *fmap; +	int err = 0; + +	fmap = fopen(fname, "r"); +	if (!fmap) { +		error("Cannot open map file '%s'\n", fname); +		return 1; +	} +	if (fmap) { +		err = read_system_map(fmap); +		fclose(fmap); +	} +	return err; +} + +static int read_profile_file(const char *fname) +{ +	int not_found = INT_MAX; +	FILE *fprof; +	int err; + +	fprof = fopen(fname, "rb"); +	if (!fprof) { +		error("Cannot open profile data file '%s'\n", +		      fname); +		return 1; +	} else { +		err = read_profile(fprof, ¬_found); +		fclose(fprof); +		if (err) +			return err; + +		if (not_found) { +			warn("%d profile functions could not be found in the map file - are you sure that your profile data and map file correspond?\n", +			     not_found); +			return 1; +		} +	} +	return 0; +} + +static int regex_report_error(regex_t *regex, int err, const char *op, +			      const char *name) +{ +	char buf[200]; + +	regerror(err, regex, buf, sizeof(buf)); +	error("Regex error '%s' in %s '%s'\n", buf, op, name); +	return -1; +} + +static void check_trace_config_line(struct trace_configline_info *item) +{ +	struct func_info *func, *end; +	int err; + +	debug("Checking trace config line '%s'\n", item->name); +	for (func = func_list, end = func + func_count; func < end; func++) { +		err = regexec(&item->regex, func->name, 0, NULL, 0); +		debug("   - regex '%s', string '%s': %d\n", item->name, +		      func->name, err); +		if (err == REG_NOMATCH) +			continue; + +		if (err) { +			regex_report_error(&item->regex, err, "match", +					   item->name); +			break; +		} + +		/* It matches, so perform the action */ +		switch (item->type) { +		case TRACE_LINE_INCLUDE: +			info("      include %s at %lx\n", func->name, +			     text_offset + func->offset); +			func->flags |= FUNCF_TRACE; +			break; + +		case TRACE_LINE_EXCLUDE: +			info("      exclude %s at %lx\n", func->name, +			     text_offset + func->offset); +			func->flags &= ~FUNCF_TRACE; +			break; +		} +	} +} + +static void check_trace_config(void) +{ +	struct trace_configline_info *line; + +	for (line = trace_config_head; line; line = line->next) +		check_trace_config_line(line); +} + +/** + * Check the functions to see if they each have an objsection. If not, then + * the linker must have eliminated them. + */ +static void check_functions(void) +{ +	struct func_info *func, *end; +	unsigned long removed_code_size = 0; +	int not_found = 0; + +	/* Look for missing functions */ +	for (func = func_list, end = func + func_count; func < end; func++) { +		if (!func->objsection) { +			removed_code_size += func->code_size; +			not_found++; +		} +	} + +	/* Figure out what functions we want to trace */ +	check_trace_config(); + +	warn("%d functions removed by linker, %ld code size\n", +	     not_found, removed_code_size); +} + +static int read_trace_config(FILE *fin) +{ +	char buff[200]; +	int linenum = 0; +	struct trace_configline_info **tailp = &trace_config_head; + +	while (fgets(buff, sizeof(buff), fin)) { +		int len = strlen(buff); +		struct trace_configline_info *line; +		char *saveptr; +		char *s, *tok; +		int err; + +		linenum++; +		if (len && buff[len - 1] == '\n') +			buff[len - 1] = '\0'; + +		/* skip blank lines and comments */ +		for (s = buff; *s == ' ' || *s == '\t'; s++) +			; +		if (!*s || *s == '#') +			continue; + +		line = (struct trace_configline_info *)calloc(1, +							      sizeof(*line)); +		if (!line) { +			error("Cannot allocate config line\n"); +			return -1; +		} + +		tok = strtok_r(s, " \t", &saveptr); +		if (!tok) { +			error("Invalid trace config data on line %d\n", +			      linenum); +			return -1; +		} +		if (0 == strcmp(tok, "include-func")) { +			line->type = TRACE_LINE_INCLUDE; +		} else if (0 == strcmp(tok, "exclude-func")) { +			line->type = TRACE_LINE_EXCLUDE; +		} else { +			error("Unknown command in trace config data line %d\n", +			      linenum); +			return -1; +		} + +		tok = strtok_r(NULL, " \t", &saveptr); +		if (!tok) { +			error("Missing pattern in trace config data line %d\n", +			      linenum); +			return -1; +		} + +		err = regcomp(&line->regex, tok, REG_NOSUB); +		if (err) { +			free(line); +			return regex_report_error(&line->regex, err, "compile", +						  tok); +		} + +		/* link this new one to the end of the list */ +		line->name = strdup(tok); +		line->next = NULL; +		*tailp = line; +		tailp = &line->next; +	} + +	if (!feof(fin)) { +		error("Cannot read from trace config file at position %ld\n", +		      ftell(fin)); +		return -1; +	} +	return 0; +} + +static int read_trace_config_file(const char *fname) +{ +	FILE *fin; +	int err; + +	fin = fopen(fname, "r"); +	if (!fin) { +		error("Cannot open trace_config file '%s'\n", fname); +		return -1; +	} +	err = read_trace_config(fin); +	fclose(fin); +	return err; +} + +static void out_func(ulong func_offset, int is_caller, const char *suffix) +{ +	struct func_info *func; + +	func = (is_caller ? find_caller_by_offset : find_func_by_offset) +		(func_offset); + +	if (func) +		printf("%s%s", func->name, suffix); +	else +		printf("%lx%s", func_offset, suffix); +} + +/* + * # tracer: function + * # + * #           TASK-PID   CPU#    TIMESTAMP  FUNCTION + * #              | |      |          |         | + * #           bash-4251  [01] 10152.583854: path_put <-path_walk + * #           bash-4251  [01] 10152.583855: dput <-path_put + * #           bash-4251  [01] 10152.583855: _atomic_dec_and_lock <-dput + */ +static int make_ftrace(void) +{ +	struct trace_call *call; +	int missing_count = 0, skip_count = 0; +	int i; + +	printf("# tracer: ftrace\n" +		"#\n" +		"#           TASK-PID   CPU#    TIMESTAMP  FUNCTION\n" +		"#              | |      |          |         |\n"); +	for (i = 0, call = call_list; i < call_count; i++, call++) { +		struct func_info *func = find_func_by_offset(call->func); +		ulong time = call->flags & FUNCF_TIMESTAMP_MASK; + +		if (TRACE_CALL_TYPE(call) != FUNCF_ENTRY && +		    TRACE_CALL_TYPE(call) != FUNCF_EXIT) +			continue; +		if (!func) { +			warn("Cannot find function at %lx\n", +			     text_offset + call->func); +			missing_count++; +			continue; +		} + +		if (!(func->flags & FUNCF_TRACE)) { +			debug("Funcion '%s' is excluded from trace\n", +			      func->name); +			skip_count++; +			continue; +		} + +		printf("%16s-%-5d [01] %lu.%06lu: ", "uboot", 1, +		       time / 1000000, time % 1000000); + +		out_func(call->func, 0, " <- "); +		out_func(call->caller, 1, "\n"); +	} +	info("ftrace: %d functions not found, %d excluded\n", missing_count, +	     skip_count); + +	return 0; +} + +static int prof_tool(int argc, char * const argv[], +		     const char *prof_fname, const char *map_fname, +		     const char *trace_config_fname) +{ +	int err = 0; + +	if (read_map_file(map_fname)) +		return -1; +	if (prof_fname && read_profile_file(prof_fname)) +		return -1; +	if (trace_config_fname && read_trace_config_file(trace_config_fname)) +		return -1; + +	check_functions(); + +	for (; argc; argc--, argv++) { +		const char *cmd = *argv; + +		if (0 == strcmp(cmd, "dump-ftrace")) +			err = make_ftrace(); +		else +			warn("Unknown command '%s'\n", cmd); +	} + +	return err; +} + +int main(int argc, char *argv[]) +{ +	const char *map_fname = "System.map"; +	const char *prof_fname = NULL; +	const char *trace_config_fname = NULL; +	int opt; + +	verbose = 2; +	while ((opt = getopt(argc, argv, "m:p:t:v:")) != -1) { +		switch (opt) { +		case 'm': +			map_fname = optarg; +			break; + +		case 'p': +			prof_fname = optarg; +			break; + +		case 't': +			trace_config_fname = optarg; +			break; + +		case 'v': +			verbose = atoi(optarg); +			break; + +		default: +			usage(); +		} +	} +	argc -= optind; argv += optind; +	if (argc < 1) +		usage(); + +	debug("Debug enabled\n"); +	return prof_tool(argc, argv, prof_fname, map_fname, +			 trace_config_fname); +}  |