[coreboot-gerrit] Change in coreboot[master]: util/sconfig: Add support for overriding base tree properties/node

Furquan Shaikh (Code Review) gerrit at coreboot.org
Fri Jun 22 18:40:07 CEST 2018


Furquan Shaikh has uploaded this change for review. ( https://review.coreboot.org/27206


Change subject: util/sconfig: Add support for overriding base tree properties/node
......................................................................

util/sconfig: Add support for overriding base tree properties/node

This change adds support to allow variants to override the devices and
properties in base device tree by providing an override device
tree. It works as follows:
1. Both base and override device trees are parsed from provided input
files.
2. Walk through the trees in lockstep fashion using depth-first
traversal checking if a node in override tree has a matching node in
base tree.
 - If matching node is found, then update the properties of base node
 using the override node. Continue walking the children of the nodes.
 - If matching node is not found, then copy the entire override
 subtree of the node under the current base parent. In addition to
 that, chip instance pointers of the nodes in override tree need to be
 updated if they were pointing to the override parents chip instance.

Since chip always expects a device to be present, it leads to a
side-effect that overriding chip registers requires that a device is
always provided for the chip in the override tree as well.

BUG=b:80081934

Change-Id: I6604e4f8abe3fc48240e942fea32da96031e1e46
Signed-off-by: Furquan Shaikh <furquan at google.com>
---
M util/sconfig/main.c
1 file changed, 258 insertions(+), 9 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/06/27206/1

diff --git a/util/sconfig/main.c b/util/sconfig/main.c
index 91b3ec4..168262d 100644
--- a/util/sconfig/main.c
+++ b/util/sconfig/main.c
@@ -334,6 +334,43 @@
 	return instance;
 }
 
+static void delete_chip_instance(struct chip_instance *ins)
+{
+	struct chip *c = ins->chip;
+	struct chip_instance *i = c->instance;
+
+	/*
+	 * If chip instance to be deleted is the first instance, then update
+	 * instance pointer of the chip as well.
+	 */
+	if (i == ins) {
+		c->instance = ins->next;
+		free(ins);
+		return;
+	}
+
+	/*
+	 * Loop through the instances list of the chip to find and remove the
+	 * given instance.
+	 */
+	while (1) {
+		if (i == NULL) {
+			printf("ERROR: chip instance not found!\n");
+			exit(1);
+		}
+
+		if (i->next != ins) {
+			i = i->next;
+			continue;
+		}
+
+		i->next = ins->next;
+		break;
+	}
+
+	free(ins);
+}
+
 /*
  * Allocate a new bus for the provided device.
  *   - If this is the first bus being allocated under this device, then its id
@@ -402,6 +439,23 @@
 	return NULL;
 }
 
+/*
+ * Add given node as child of the provided parent. If this is the first child of
+ * the parent, update parent->children pointer as well.
+ */
+static void set_new_child(struct bus *parent, struct device *child)
+{
+	struct device *c = parent->children;
+	if (c) {
+		while (c->sibling)
+			c = c->sibling;
+		c->sibling = child;
+	} else
+		parent->children = child;
+
+	child->parent = parent;
+}
+
 struct device *new_device(struct bus *parent,
 			  struct chip_instance *chip_instance,
 			  const int bustype, const char *devnum,
@@ -439,13 +493,7 @@
 	new_d->enabled = enabled;
 	new_d->chip_instance = chip_instance;
 
-	struct device *c = parent->children;
-	if (c) {
-		while (c->sibling)
-			c = c->sibling;
-		c->sibling = new_d;
-	} else
-		parent->children = new_d;
+	set_new_child(parent, new_d);
 
 	switch (bustype) {
 	case PCI:
@@ -500,9 +548,8 @@
 	return new_d;
 }
 
-void add_resource(struct bus *bus, int type, int index, int base)
+static void new_resource(struct device *dev, int type, int index, int base)
 {
-	struct device *dev = bus->dev;
 	struct resource *r = S_ALLOC(sizeof(struct resource));
 
 	r->type = type;
@@ -518,6 +565,11 @@
 	}
 }
 
+void add_resource(struct bus *bus, int type, int index, int base)
+{
+	new_resource(bus->dev, type, index, base);
+}
+
 void add_register(struct chip_instance *chip_instance, char *name, char *val)
 {
 	struct reg *r = S_ALLOC(sizeof(struct reg));
@@ -911,6 +963,201 @@
 	fclose(filec);
 }
 
+/*
+ * Match device nodes from base and override tree to see if they are the same
+ * node.
+ */
+static int device_match(struct device *a, struct device *b)
+{
+	return ((a->path_a == b->path_a) &&
+		(a->path_b == b->path_b) &&
+		(a->bustype == b->bustype) &&
+		(a->chip_instance->chip ==
+		 b->chip_instance->chip));
+}
+
+/*
+ * Walk through the override subtree in breadth-first manner starting at node to
+ * see if chip_instance pointer of the node is same as chip_instance pointer of
+ * override parent that is passed into the function. If yes, then update the
+ * chip_instance pointer of the node to chip_instance pointer of the base
+ * parent.
+ */
+static void update_chip_pointers(struct device *node,
+				 struct chip_instance *base_parent_ci,
+				 struct chip_instance *override_parent_ci)
+{
+	struct queue_entry *q_head = NULL;
+
+	enqueue_tail(&q_head, node);
+
+	while ((node = dequeue_head(&q_head))) {
+		if (node->chip_instance != override_parent_ci)
+			continue;
+		node->chip_instance = base_parent_ci;
+		add_children_to_queue(&q_head, node);
+	}
+}
+
+/*
+ * Add resource to device. If resource is already present, then update its base
+ * and index. If not, then add a new resource to the device.
+ */
+static void update_resource(struct device *dev, struct resource *res)
+{
+	struct resource *base_res = dev->res;
+
+	while (base_res) {
+		if (base_res->type == res->type) {
+			base_res->index = res->index;
+			base_res->base = res->base;
+			return;
+		}
+		base_res = base_res->next;
+	}
+
+	new_resource(dev, res->type, res->index, res->base);
+}
+
+/*
+ * Add register to chip instance. If register is already present, then update
+ * its value. If not, then add a new register to the chip instance.
+ */
+static void update_register(struct chip_instance *c, struct reg *reg)
+{
+	struct reg *base_reg = c->reg;
+
+	while (base_reg) {
+		if (!strcmp(base_reg->key, reg->key)) {
+			base_reg->value = reg->value;
+			return;
+		}
+		base_reg = base_reg->next;
+	}
+
+	add_register(c, reg->key, reg->value);
+}
+
+static void override_devicetree(struct bus *base_parent,
+				struct bus *override_parent);
+
+/*
+ * If a matching device is found in base tree for the override tree device, then
+ * update the base tree device with the properties from override tree
+ * device. Also, call override_devicetree for all the buses under the override
+ * tree device.
+ */
+static void update_device(struct device *base_dev, struct device *override_dev)
+{
+	base_dev->enabled = override_dev->enabled;
+
+	if (override_dev->subsystem_vendor ||
+	    override_dev->subsystem_device) {
+		base_dev->subsystem_vendor = override_dev->subsystem_vendor;
+		base_dev->subsystem_device = override_dev->subsystem_device;
+	}
+
+	if (override_dev->inherit_subsystem)
+		base_dev->inherit_subsystem = override_dev->inherit_subsystem;
+
+	struct resource *res = override_dev->res;
+	while (res) {
+		update_resource(base_dev, res);
+		res = res->next;
+	}
+
+	struct reg *reg = override_dev->chip_instance->reg;
+	while (reg) {
+		update_register(base_dev->chip_instance, reg);
+		reg = reg->next;
+	}
+
+	struct bus *override_bus = override_dev->bus;
+	struct bus *base_bus = base_dev->bude;
+
+	while (override_bus) {
+
+		/*
+		 * If we have more buses in override tree device, then allocate
+		 * a new bus for the base tree device as well.
+		 */
+		if (!base_bus) {
+			alloc_bus(base_dev);
+			base_bus = base_dev->last_bus;
+		}
+
+		override_devicetree(base_dev->bus, override_dev->bus);
+
+		override_bus = override_bus->next;
+		base_bus = base_bus->next;
+	}
+
+	/*
+	 * If override tree device isn't using the same chip instance as its
+	 * parent dev, then delete the chip instance of the override tree
+	 * device since we would have already copied the registers above.
+	 */
+	if (override_dev->chip_instance !=
+	    override_dev->parent->dev->chip_instance)
+		delete_chip_instance(override_dev->chip_instance);
+}
+
+/*
+ * Perform copy of device and properties from override parent to base parent.
+ * This function walks through the override tree in a depth-first manner
+ * performing following actions:
+ * 1. If matching device is found in base tree, then copy the properties of
+ * override device to base tree device. Call override_devicetree recursively on
+ * the bus of override device.
+ * 2. If matching device is not found in base tree, then set override tree
+ * device as new child of base_parent and update the chip pointers in override
+ * device subtree to ensure the nodes do not point to override tree chip
+ * instance.
+ */
+static void override_devicetree(struct bus *base_parent,
+				struct bus *override_parent)
+{
+	struct device *base_child;
+	struct device *override_child = override_parent->children;
+	struct device *next_child;
+
+	while (override_child) {
+
+		/* Look for a matching device in base tree. */
+		for (base_child = base_parent->children;
+		     base_child; base_child = base_child->sibling) {
+			if (device_match(base_child, override_child))
+				break;
+		}
+
+		next_child = override_child->sibling;
+
+		/*
+		 * If matching device is found, copy properties of
+		 * override_child to base_child.
+		 */
+		if (base_child)
+			update_device(base_child, override_child);
+		else {
+			/*
+			 * If matching device is not found, set override_child
+			 * as a new child of base_parent.
+			 */
+			set_new_child(base_parent, override_child);
+			/*
+			 * Ensure all nodes in override tree pointing to
+			 * override parent chip_instance now point to base
+			 * parent chip_instance.
+			 */
+			update_chip_pointers(override_child,
+					base_parent->dev->chip_instance,
+					override_parent->dev->chip_instance);
+		}
+
+		override_child = next_child;
+	}
+}
+
 int main(int argc, char **argv)
 {
 	if ((argc < MANDATORY_ARG_COUNT) || (argc > TOTAL_ARG_COUNT))
@@ -925,6 +1172,8 @@
 	if (argc == TOTAL_ARG_COUNT) {
 		override_devtree = argv[OVERRIDE_DEVICEFILE_ARG];
 		parse_devicetree(override_devtree, &override_root_bus);
+
+		override_devicetree(&base_root_bus, &override_root_bus);
 	}
 
 	FILE *autogen = fopen(outputc, "w");

-- 
To view, visit https://review.coreboot.org/27206
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I6604e4f8abe3fc48240e942fea32da96031e1e46
Gerrit-Change-Number: 27206
Gerrit-PatchSet: 1
Gerrit-Owner: Furquan Shaikh <furquan at google.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180622/f4d244cb/attachment-0001.html>


More information about the coreboot-gerrit mailing list