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@google.com>
Reviewed-on: https://review.coreboot.org/27206
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Furquan Shaikh 2018-06-22 09:19:15 -07:00
parent f241998352
commit 27efc501d1
2 changed files with 394 additions and 10 deletions

View File

@ -20,7 +20,11 @@
extern int linenum; extern int linenum;
/* Maintains list of all the unique chip structures for the board. */ /*
* Maintains list of all the unique chip structures for the board.
* This is shared across base and override device trees since we need to
* generate headers for all chips added by both the trees.
*/
static struct chip chip_header; static struct chip chip_header;
/* /*
@ -131,6 +135,7 @@ static struct chip mainboard_chip = {
static struct chip_instance mainboard_instance = { static struct chip_instance mainboard_instance = {
.id = 0, .id = 0,
.chip = &mainboard_chip, .chip = &mainboard_chip,
.ref_count = 2,
}; };
/* This is the parent of all devices added by parsing the devicetree file. */ /* This is the parent of all devices added by parsing the devicetree file. */
@ -340,6 +345,54 @@ struct chip_instance *new_chip_instance(char *path)
return instance; return instance;
} }
static void delete_chip_instance(struct chip_instance *ins)
{
if (ins->ref_count == 0) {
printf("ERROR: ref count for chip instance is zero!!\n");
exit(1);
}
if (--ins->ref_count)
return;
struct chip *c = ins->chip;
/* Get pointer to first instance of the 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. * Allocate a new bus for the provided device.
* - If this is the first bus being allocated under this device, then its id * - If this is the first bus being allocated under this device, then its id
@ -408,6 +461,24 @@ static struct device *get_dev(struct bus *parent, int path_a, int path_b,
return NULL; 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->sibling = NULL;
child->parent = parent;
}
struct device *new_device(struct bus *parent, struct device *new_device(struct bus *parent,
struct chip_instance *chip_instance, struct chip_instance *chip_instance,
const int bustype, const char *devnum, const int bustype, const char *devnum,
@ -444,14 +515,9 @@ struct device *new_device(struct bus *parent,
new_d->enabled = enabled; new_d->enabled = enabled;
new_d->chip_instance = chip_instance; new_d->chip_instance = chip_instance;
chip_instance->ref_count++;
struct device *c = parent->children; set_new_child(parent, new_d);
if (c) {
while (c->sibling)
c = c->sibling;
c->sibling = new_d;
} else
parent->children = new_d;
switch (bustype) { switch (bustype) {
case PCI: case PCI:
@ -506,9 +572,8 @@ struct device *new_device(struct bus *parent,
return new_d; 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)); struct resource *r = S_ALLOC(sizeof(struct resource));
r->type = type; r->type = type;
@ -524,6 +589,11 @@ void add_resource(struct bus *bus, int type, int index, int base)
} }
} }
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) void add_register(struct chip_instance *chip_instance, char *name, char *val)
{ {
struct reg *r = S_ALLOC(sizeof(struct reg)); struct reg *r = S_ALLOC(sizeof(struct reg));
@ -917,6 +987,312 @@ static void parse_devicetree(const char *file, struct bus *parent)
fclose(filec); 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);
/*
* Update the base device properties using the properties of override device. In
* addition to that, call override_devicetree for all the buses under the
* override device.
*
* Override Rules:
* +--------------------+--------------------------------------------+
* | | |
* |struct device member| Rule |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | id | Unchanged. This is used to generate device |
* | | structure name in static.c. So, no need to |
* | | override. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | enabled | Copy enabled state from override device. |
* | | This allows variants to override device |
* | | state. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | subsystem_vendor | Copy from override device only if any one |
* | subsystem_device | of the ids is non-zero. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | inherit_subsystem | Copy from override device only if it is |
* | | non-zero. This allows variant to only |
* | | enable inherit flag for a device. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | path | Unchanged since these are same for both |
* | path_a | base and override device (Used for |
* | path_b | matching devices). |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | bustype | Unchanged since this is same for both base |
* | | and override device (User for matching |
* | | devices). |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | pci_irq_info | Unchanged. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | parent | Unchanged. This is meaningful only within |
* | sibling | the parse tree, hence not being copied. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | res | Each resource that is present in override |
* | | device is copied over to base device: |
* | | 1. If resource of same type is present in |
* | | base device, then index and base of the |
* | | resource is copied. |
* | | 2. If not, then a new resource is allocated|
* | | under the base device using type, index |
* | | and base from override res. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | chip_instance | Each register of chip_instance is copied |
* | | over from override device to base device: |
* | | 1. If register with same key is present in |
* | | base device, then value of the register |
* | | is copied. |
* | | 2. If not, then a new register is allocated|
* | | under the base chip_instance using key |
* | | and value from override register. |
* | | |
* +-----------------------------------------------------------------+
* | | |
* | bus | Recursively call override_devicetree on |
* | last_bus | each bus of override device. It is assumed |
* | | that bus with id X under base device |
* | | to bus with id X under override device. If |
* | | override device has more buses than base |
* | | device, then new buses are allocated under |
* | | base device. |
* | | |
* +-----------------------------------------------------------------+
*/
static void update_device(struct device *base_dev, struct device *override_dev)
{
/*
* Copy the enabled state of override device to base device. This allows
* override tree to enable or disable a particular device.
*/
base_dev->enabled = override_dev->enabled;
/*
* Copy subsystem vendor and device ids from override device to base
* device only if the ids are non-zero in override device. Else, honor
* the values in base device.
*/
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;
}
/*
* Copy value of inherity_subsystem from override device to base device
* only if it is non-zero in override device. This allows override
* tree to only enable inhert flag for a device.
*/
if (override_dev->inherit_subsystem)
base_dev->inherit_subsystem = override_dev->inherit_subsystem;
/*
* Copy resources of override device to base device.
* 1. If resource is already present in base device, then index and base
* of the resource will be copied over.
* 2. If resource is not already present in base device, a new resource
* will be allocated.
*/
struct resource *res = override_dev->res;
while (res) {
update_resource(base_dev, res);
res = res->next;
}
/*
* Copy registers of override chip instance to base chip instance.
* 1. If register key is already present in base chip instance, then
* value for the register is copied over.
* 2. If register key is not already present in base chip instance, then
* a new register will be allocated.
*/
struct reg *reg = override_dev->chip_instance->reg;
while (reg) {
update_register(base_dev->chip_instance, reg);
reg = reg->next;
}
/*
* Now that the device properties are all copied over, look at each bus
* of the override device and run override_devicetree in a recursive
* manner. The assumption here is that first bus of override device
* corresponds to first bus of base device and so on. If base device has
* lesser buses than override tree, then new buses are allocated for it.
*/
struct bus *override_bus = override_dev->bus;
struct bus *base_bus = base_dev->bus;
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_bus;
base_bus = base_bus->next_bus;
}
delete_chip_instance(override_dev->chip_instance);
override_dev->chip_instance = NULL;
}
/*
* 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) int main(int argc, char **argv)
{ {
if ((argc < MANDATORY_ARG_COUNT) || (argc > TOTAL_ARG_COUNT)) if ((argc < MANDATORY_ARG_COUNT) || (argc > TOTAL_ARG_COUNT))
@ -931,6 +1307,8 @@ int main(int argc, char **argv)
if (argc == TOTAL_ARG_COUNT) { if (argc == TOTAL_ARG_COUNT) {
override_devtree = argv[OVERRIDE_DEVICEFILE_ARG]; override_devtree = argv[OVERRIDE_DEVICEFILE_ARG];
parse_devicetree(override_devtree, &override_root_bus); parse_devicetree(override_devtree, &override_root_bus);
override_devicetree(&base_root_bus, &override_root_bus);
} }
FILE *autogen = fopen(outputc, "w"); FILE *autogen = fopen(outputc, "w");

View File

@ -58,6 +58,12 @@ struct chip_instance {
/* Pointer to next instance of the same chip. */ /* Pointer to next instance of the same chip. */
struct chip_instance *next; struct chip_instance *next;
/*
* Reference count - Indicates how many devices hold pointer to this
* chip instance.
*/
int ref_count;
}; };
struct chip { struct chip {