Blame fdtput.c

Packit Service 0ee8e1
// SPDX-License-Identifier: GPL-2.0-or-later
Packit 2ad57b
/*
Packit 2ad57b
 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Packit 2ad57b
 */
Packit 2ad57b
Packit 2ad57b
#include <assert.h>
Packit 2ad57b
#include <ctype.h>
Packit 2ad57b
#include <getopt.h>
Packit 2ad57b
#include <stdio.h>
Packit 2ad57b
#include <stdlib.h>
Packit 2ad57b
#include <string.h>
Packit 2ad57b
Packit 2ad57b
#include <libfdt.h>
Packit 2ad57b
Packit 2ad57b
#include "util.h"
Packit 2ad57b
Packit 2ad57b
/* These are the operations we support */
Packit 2ad57b
enum oper_type {
Packit 2ad57b
	OPER_WRITE_PROP,		/* Write a property in a node */
Packit 2ad57b
	OPER_CREATE_NODE,		/* Create a new node */
Packit 2ad57b
	OPER_REMOVE_NODE,		/* Delete a node */
Packit 2ad57b
	OPER_DELETE_PROP,		/* Delete a property in a node */
Packit 2ad57b
};
Packit 2ad57b
Packit 2ad57b
struct display_info {
Packit 2ad57b
	enum oper_type oper;	/* operation to perform */
Packit 2ad57b
	int type;		/* data type (s/i/u/x or 0 for default) */
Packit 2ad57b
	int size;		/* data size (1/2/4) */
Packit 2ad57b
	int verbose;		/* verbose output */
Packit 2ad57b
	int auto_path;		/* automatically create all path components */
Packit 2ad57b
};
Packit 2ad57b
Packit 2ad57b
Packit 2ad57b
/**
Packit 2ad57b
 * Report an error with a particular node.
Packit 2ad57b
 *
Packit 2ad57b
 * @param name		Node name to report error on
Packit 2ad57b
 * @param namelen	Length of node name, or -1 to use entire string
Packit 2ad57b
 * @param err		Error number to report (-FDT_ERR_...)
Packit 2ad57b
 */
Packit 2ad57b
static void report_error(const char *name, int namelen, int err)
Packit 2ad57b
{
Packit 2ad57b
	if (namelen == -1)
Packit 2ad57b
		namelen = strlen(name);
Packit 2ad57b
	fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
Packit 2ad57b
		fdt_strerror(err));
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
/**
Packit 2ad57b
 * Encode a series of arguments in a property value.
Packit 2ad57b
 *
Packit 2ad57b
 * @param disp		Display information / options
Packit 2ad57b
 * @param arg		List of arguments from command line
Packit 2ad57b
 * @param arg_count	Number of arguments (may be 0)
Packit 2ad57b
 * @param valuep	Returns buffer containing value
Packit 2ad57b
 * @param value_len	Returns length of value encoded
Packit 2ad57b
 */
Packit 2ad57b
static int encode_value(struct display_info *disp, char **arg, int arg_count,
Packit 2ad57b
			char **valuep, int *value_len)
Packit 2ad57b
{
Packit 2ad57b
	char *value = NULL;	/* holding area for value */
Packit 2ad57b
	int value_size = 0;	/* size of holding area */
Packit 2ad57b
	char *ptr;		/* pointer to current value position */
Packit 2ad57b
	int len;		/* length of this cell/string/byte */
Packit 2ad57b
	int ival;
Packit 2ad57b
	int upto;	/* the number of bytes we have written to buf */
Packit 2ad57b
	char fmt[3];
Packit 2ad57b
Packit 2ad57b
	upto = 0;
Packit 2ad57b
Packit 2ad57b
	if (disp->verbose)
Packit 2ad57b
		fprintf(stderr, "Decoding value:\n");
Packit 2ad57b
Packit 2ad57b
	fmt[0] = '%';
Packit 2ad57b
	fmt[1] = disp->type ? disp->type : 'd';
Packit 2ad57b
	fmt[2] = '\0';
Packit 2ad57b
	for (; arg_count > 0; arg++, arg_count--, upto += len) {
Packit 2ad57b
		/* assume integer unless told otherwise */
Packit 2ad57b
		if (disp->type == 's')
Packit 2ad57b
			len = strlen(*arg) + 1;
Packit 2ad57b
		else
Packit 2ad57b
			len = disp->size == -1 ? 4 : disp->size;
Packit 2ad57b
Packit 2ad57b
		/* enlarge our value buffer by a suitable margin if needed */
Packit 2ad57b
		if (upto + len > value_size) {
Packit 2ad57b
			value_size = (upto + len) + 500;
Packit 2ad57b
			value = xrealloc(value, value_size);
Packit 2ad57b
		}
Packit 2ad57b
Packit 2ad57b
		ptr = value + upto;
Packit 2ad57b
		if (disp->type == 's') {
Packit 2ad57b
			memcpy(ptr, *arg, len);
Packit 2ad57b
			if (disp->verbose)
Packit 2ad57b
				fprintf(stderr, "\tstring: '%s'\n", ptr);
Packit 2ad57b
		} else {
Packit 2ad57b
			fdt32_t *iptr = (fdt32_t *)ptr;
Packit 2ad57b
			sscanf(*arg, fmt, &ival);
Packit 2ad57b
			if (len == 4)
Packit 2ad57b
				*iptr = cpu_to_fdt32(ival);
Packit 2ad57b
			else
Packit 2ad57b
				*ptr = (uint8_t)ival;
Packit 2ad57b
			if (disp->verbose) {
Packit 2ad57b
				fprintf(stderr, "\t%s: %d\n",
Packit 2ad57b
					disp->size == 1 ? "byte" :
Packit 2ad57b
					disp->size == 2 ? "short" : "int",
Packit 2ad57b
					ival);
Packit 2ad57b
			}
Packit 2ad57b
		}
Packit 2ad57b
	}
Packit 2ad57b
	*value_len = upto;
Packit 2ad57b
	*valuep = value;
Packit 2ad57b
	if (disp->verbose)
Packit 2ad57b
		fprintf(stderr, "Value size %d\n", upto);
Packit 2ad57b
	return 0;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
#define ALIGN(x)		(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
Packit 2ad57b
Packit 2ad57b
static char *realloc_fdt(char *fdt, int delta)
Packit 2ad57b
{
Packit 2ad57b
	int new_sz = fdt_totalsize(fdt) + delta;
Packit 2ad57b
	fdt = xrealloc(fdt, new_sz);
Packit 2ad57b
	fdt_open_into(fdt, fdt, new_sz);
Packit 2ad57b
	return fdt;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
static char *realloc_node(char *fdt, const char *name)
Packit 2ad57b
{
Packit 2ad57b
	int delta;
Packit 2ad57b
	/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
Packit 2ad57b
	delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
Packit 2ad57b
			+ FDT_TAGSIZE;
Packit 2ad57b
	return realloc_fdt(fdt, delta);
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
static char *realloc_property(char *fdt, int nodeoffset,
Packit 2ad57b
		const char *name, int newlen)
Packit 2ad57b
{
Packit 2ad57b
	int delta = 0;
Packit 2ad57b
	int oldlen = 0;
Packit 2ad57b
Packit 2ad57b
	if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
Packit 2ad57b
		/* strings + property header */
Packit 2ad57b
		delta = sizeof(struct fdt_property) + strlen(name) + 1;
Packit 2ad57b
Packit 2ad57b
	if (newlen > oldlen)
Packit 2ad57b
		/* actual value in off_struct */
Packit 2ad57b
		delta += ALIGN(newlen) - ALIGN(oldlen);
Packit 2ad57b
Packit 2ad57b
	return realloc_fdt(fdt, delta);
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
static int store_key_value(char **blob, const char *node_name,
Packit 2ad57b
		const char *property, const char *buf, int len)
Packit 2ad57b
{
Packit 2ad57b
	int node;
Packit 2ad57b
	int err;
Packit 2ad57b
Packit 2ad57b
	node = fdt_path_offset(*blob, node_name);
Packit 2ad57b
	if (node < 0) {
Packit 2ad57b
		report_error(node_name, -1, node);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	err = fdt_setprop(*blob, node, property, buf, len);
Packit 2ad57b
	if (err == -FDT_ERR_NOSPACE) {
Packit 2ad57b
		*blob = realloc_property(*blob, node, property, len);
Packit 2ad57b
		err = fdt_setprop(*blob, node, property, buf, len);
Packit 2ad57b
	}
Packit 2ad57b
	if (err) {
Packit 2ad57b
		report_error(property, -1, err);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
	return 0;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
/**
Packit 2ad57b
 * Create paths as needed for all components of a path
Packit 2ad57b
 *
Packit 2ad57b
 * Any components of the path that do not exist are created. Errors are
Packit 2ad57b
 * reported.
Packit 2ad57b
 *
Packit 2ad57b
 * @param blob		FDT blob to write into
Packit 2ad57b
 * @param in_path	Path to process
Packit 2ad57b
 * @return 0 if ok, -1 on error
Packit 2ad57b
 */
Packit 2ad57b
static int create_paths(char **blob, const char *in_path)
Packit 2ad57b
{
Packit 2ad57b
	const char *path = in_path;
Packit 2ad57b
	const char *sep;
Packit 2ad57b
	int node, offset = 0;
Packit 2ad57b
Packit 2ad57b
	/* skip leading '/' */
Packit 2ad57b
	while (*path == '/')
Packit 2ad57b
		path++;
Packit 2ad57b
Packit 2ad57b
	for (sep = path; *sep; path = sep + 1, offset = node) {
Packit 2ad57b
		/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
Packit 2ad57b
		sep = strchr(path, '/');
Packit 2ad57b
		if (!sep)
Packit 2ad57b
			sep = path + strlen(path);
Packit 2ad57b
Packit 2ad57b
		node = fdt_subnode_offset_namelen(*blob, offset, path,
Packit 2ad57b
				sep - path);
Packit 2ad57b
		if (node == -FDT_ERR_NOTFOUND) {
Packit 2ad57b
			*blob = realloc_node(*blob, path);
Packit 2ad57b
			node = fdt_add_subnode_namelen(*blob, offset, path,
Packit 2ad57b
						       sep - path);
Packit 2ad57b
		}
Packit 2ad57b
		if (node < 0) {
Packit 2ad57b
			report_error(path, sep - path, node);
Packit 2ad57b
			return -1;
Packit 2ad57b
		}
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	return 0;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
/**
Packit 2ad57b
 * Create a new node in the fdt.
Packit 2ad57b
 *
Packit 2ad57b
 * This will overwrite the node_name string. Any error is reported.
Packit 2ad57b
 *
Packit 2ad57b
 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
Packit 2ad57b
 *
Packit 2ad57b
 * @param blob		FDT blob to write into
Packit 2ad57b
 * @param node_name	Name of node to create
Packit 2ad57b
 * @return new node offset if found, or -1 on failure
Packit 2ad57b
 */
Packit 2ad57b
static int create_node(char **blob, const char *node_name)
Packit 2ad57b
{
Packit 2ad57b
	int node = 0;
Packit 2ad57b
	char *p;
Packit 2ad57b
Packit 2ad57b
	p = strrchr(node_name, '/');
Packit 2ad57b
	if (!p) {
Packit 2ad57b
		report_error(node_name, -1, -FDT_ERR_BADPATH);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
	*p = '\0';
Packit 2ad57b
Packit 2ad57b
	*blob = realloc_node(*blob, p + 1);
Packit 2ad57b
Packit 2ad57b
	if (p > node_name) {
Packit 2ad57b
		node = fdt_path_offset(*blob, node_name);
Packit 2ad57b
		if (node < 0) {
Packit 2ad57b
			report_error(node_name, -1, node);
Packit 2ad57b
			return -1;
Packit 2ad57b
		}
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	node = fdt_add_subnode(*blob, node, p + 1);
Packit 2ad57b
	if (node < 0) {
Packit 2ad57b
		report_error(p + 1, -1, node);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	return 0;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
/**
Packit 2ad57b
 * Delete a property of a node in the fdt.
Packit 2ad57b
 *
Packit 2ad57b
 * @param blob		FDT blob to write into
Packit 2ad57b
 * @param node_name	Path to node containing the property to delete
Packit 2ad57b
 * @param prop_name	Name of property to delete
Packit 2ad57b
 * @return 0 on success, or -1 on failure
Packit 2ad57b
 */
Packit 2ad57b
static int delete_prop(char *blob, const char *node_name, const char *prop_name)
Packit 2ad57b
{
Packit 2ad57b
	int node = 0;
Packit 2ad57b
Packit 2ad57b
	node = fdt_path_offset(blob, node_name);
Packit 2ad57b
	if (node < 0) {
Packit 2ad57b
		report_error(node_name, -1, node);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	node = fdt_delprop(blob, node, prop_name);
Packit 2ad57b
	if (node < 0) {
Packit 2ad57b
		report_error(node_name, -1, node);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	return 0;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
/**
Packit 2ad57b
 * Delete a node in the fdt.
Packit 2ad57b
 *
Packit 2ad57b
 * @param blob		FDT blob to write into
Packit 2ad57b
 * @param node_name	Name of node to delete
Packit 2ad57b
 * @return 0 on success, or -1 on failure
Packit 2ad57b
 */
Packit 2ad57b
static int delete_node(char *blob, const char *node_name)
Packit 2ad57b
{
Packit 2ad57b
	int node = 0;
Packit 2ad57b
Packit 2ad57b
	node = fdt_path_offset(blob, node_name);
Packit 2ad57b
	if (node < 0) {
Packit 2ad57b
		report_error(node_name, -1, node);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	node = fdt_del_node(blob, node);
Packit 2ad57b
	if (node < 0) {
Packit 2ad57b
		report_error(node_name, -1, node);
Packit 2ad57b
		return -1;
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	return 0;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
static int do_fdtput(struct display_info *disp, const char *filename,
Packit 2ad57b
		    char **arg, int arg_count)
Packit 2ad57b
{
Packit 2ad57b
	char *value = NULL;
Packit 2ad57b
	char *blob;
Packit 2ad57b
	char *node;
Packit 2ad57b
	int len, ret = 0;
Packit 2ad57b
Packit Service 0ee8e1
	blob = utilfdt_read(filename, NULL);
Packit 2ad57b
	if (!blob)
Packit 2ad57b
		return -1;
Packit 2ad57b
Packit 2ad57b
	switch (disp->oper) {
Packit 2ad57b
	case OPER_WRITE_PROP:
Packit 2ad57b
		/*
Packit 2ad57b
		 * Convert the arguments into a single binary value, then
Packit 2ad57b
		 * store them into the property.
Packit 2ad57b
		 */
Packit 2ad57b
		assert(arg_count >= 2);
Packit 2ad57b
		if (disp->auto_path && create_paths(&blob, *arg))
Packit 2ad57b
			return -1;
Packit 2ad57b
		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
Packit 2ad57b
			store_key_value(&blob, *arg, arg[1], value, len))
Packit 2ad57b
			ret = -1;
Packit 2ad57b
		break;
Packit 2ad57b
	case OPER_CREATE_NODE:
Packit 2ad57b
		for (; ret >= 0 && arg_count--; arg++) {
Packit 2ad57b
			if (disp->auto_path)
Packit 2ad57b
				ret = create_paths(&blob, *arg);
Packit 2ad57b
			else
Packit 2ad57b
				ret = create_node(&blob, *arg);
Packit 2ad57b
		}
Packit 2ad57b
		break;
Packit 2ad57b
	case OPER_REMOVE_NODE:
Packit 2ad57b
		for (; ret >= 0 && arg_count--; arg++)
Packit 2ad57b
			ret = delete_node(blob, *arg);
Packit 2ad57b
		break;
Packit 2ad57b
	case OPER_DELETE_PROP:
Packit 2ad57b
		node = *arg;
Packit 2ad57b
		for (arg++; ret >= 0 && arg_count-- > 1; arg++)
Packit 2ad57b
			ret = delete_prop(blob, node, *arg);
Packit 2ad57b
		break;
Packit 2ad57b
	}
Packit 2ad57b
	if (ret >= 0) {
Packit 2ad57b
		fdt_pack(blob);
Packit 2ad57b
		ret = utilfdt_write(filename, blob);
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	free(blob);
Packit 2ad57b
Packit 2ad57b
	if (value) {
Packit 2ad57b
		free(value);
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	return ret;
Packit 2ad57b
}
Packit 2ad57b
Packit 2ad57b
/* Usage related data. */
Packit 2ad57b
static const char usage_synopsis[] =
Packit 2ad57b
	"write a property value to a device tree\n"
Packit 2ad57b
	"	fdtput <options> 
<node> <property> [<value>...]\n"
Packit 2ad57b
	"	fdtput -c <options> 
[<node>...]\n"
Packit 2ad57b
	"	fdtput -r <options> 
[<node>...]\n"
Packit 2ad57b
	"	fdtput -d <options> 
<node> [<property>...]\n"
Packit 2ad57b
	"\n"
Packit 2ad57b
	"The command line arguments are joined together into a single value.\n"
Packit 2ad57b
	USAGE_TYPE_MSG;
Packit 2ad57b
static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
Packit 2ad57b
static struct option const usage_long_opts[] = {
Packit 2ad57b
	{"create",           no_argument, NULL, 'c'},
Packit 2ad57b
	{"remove",	     no_argument, NULL, 'r'},
Packit 2ad57b
	{"delete",	     no_argument, NULL, 'd'},
Packit 2ad57b
	{"auto-path",        no_argument, NULL, 'p'},
Packit 2ad57b
	{"type",              a_argument, NULL, 't'},
Packit 2ad57b
	{"verbose",          no_argument, NULL, 'v'},
Packit 2ad57b
	USAGE_COMMON_LONG_OPTS,
Packit 2ad57b
};
Packit 2ad57b
static const char * const usage_opts_help[] = {
Packit 2ad57b
	"Create nodes if they don't already exist",
Packit 2ad57b
	"Delete nodes (and any subnodes) if they already exist",
Packit 2ad57b
	"Delete properties if they already exist",
Packit 2ad57b
	"Automatically create nodes as needed for the node path",
Packit 2ad57b
	"Type of data",
Packit 2ad57b
	"Display each value decoded from command line",
Packit 2ad57b
	USAGE_COMMON_OPTS_HELP
Packit 2ad57b
};
Packit 2ad57b
Packit 2ad57b
int main(int argc, char *argv[])
Packit 2ad57b
{
Packit 2ad57b
	int opt;
Packit 2ad57b
	struct display_info disp;
Packit 2ad57b
	char *filename = NULL;
Packit 2ad57b
Packit 2ad57b
	memset(&disp, '\0', sizeof(disp));
Packit 2ad57b
	disp.size = -1;
Packit 2ad57b
	disp.oper = OPER_WRITE_PROP;
Packit 2ad57b
	while ((opt = util_getopt_long()) != EOF) {
Packit 2ad57b
		/*
Packit 2ad57b
		 * TODO: add options to:
Packit 2ad57b
		 * - rename node
Packit 2ad57b
		 * - pack fdt before writing
Packit 2ad57b
		 * - set amount of free space when writing
Packit 2ad57b
		 */
Packit 2ad57b
		switch (opt) {
Packit 2ad57b
		case_USAGE_COMMON_FLAGS
Packit 2ad57b
Packit 2ad57b
		case 'c':
Packit 2ad57b
			disp.oper = OPER_CREATE_NODE;
Packit 2ad57b
			break;
Packit 2ad57b
		case 'r':
Packit 2ad57b
			disp.oper = OPER_REMOVE_NODE;
Packit 2ad57b
			break;
Packit 2ad57b
		case 'd':
Packit 2ad57b
			disp.oper = OPER_DELETE_PROP;
Packit 2ad57b
			break;
Packit 2ad57b
		case 'p':
Packit 2ad57b
			disp.auto_path = 1;
Packit 2ad57b
			break;
Packit 2ad57b
		case 't':
Packit 2ad57b
			if (utilfdt_decode_type(optarg, &disp.type,
Packit 2ad57b
					&disp.size))
Packit 2ad57b
				usage("Invalid type string");
Packit 2ad57b
			break;
Packit 2ad57b
Packit 2ad57b
		case 'v':
Packit 2ad57b
			disp.verbose = 1;
Packit 2ad57b
			break;
Packit 2ad57b
		}
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	if (optind < argc)
Packit 2ad57b
		filename = argv[optind++];
Packit 2ad57b
	if (!filename)
Packit 2ad57b
		usage("missing filename");
Packit 2ad57b
Packit 2ad57b
	argv += optind;
Packit 2ad57b
	argc -= optind;
Packit 2ad57b
Packit 2ad57b
	if (disp.oper == OPER_WRITE_PROP) {
Packit 2ad57b
		if (argc < 1)
Packit 2ad57b
			usage("missing node");
Packit 2ad57b
		if (argc < 2)
Packit 2ad57b
			usage("missing property");
Packit 2ad57b
	}
Packit 2ad57b
Packit 2ad57b
	if (disp.oper == OPER_DELETE_PROP)
Packit 2ad57b
		if (argc < 1)
Packit 2ad57b
			usage("missing node");
Packit 2ad57b
Packit 2ad57b
	if (do_fdtput(&disp, filename, argv, argc))
Packit 2ad57b
		return 1;
Packit 2ad57b
	return 0;
Packit 2ad57b
}