Browse Source

add device tree parser

1. add driver/of/*
2. add driver/inc/of.h
3. add board.unflatten(addr) to `board' lua object.

Signed-off-by: surenyi <surenyi82@163.com>
master
surenyi 6 years ago
parent
commit
aa1f3d12af
  1. 258
      packages/vsky/libdsp/driver/of/base.c
  2. 415
      packages/vsky/libdsp/driver/of/fdt.c
  3. 19
      packages/vsky/libdsp/driver/of/of_private.h
  4. 23
      packages/vsky/libdsp/driver/utils.c
  5. 46
      packages/vsky/libdsp/inc/list.h
  6. 74
      packages/vsky/libdsp/inc/of.h
  7. 2
      packages/vsky/libdsp/inc/utils.h
  8. 9
      packages/vsky/libdsp/lua/lualib/kit.c
  9. 2
      packages/vsky/libdsp/package.bld

258
packages/vsky/libdsp/driver/of/base.c

@ -0,0 +1,258 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include "utils.h"
#include "libfdt.h"
#include "of.h"
#include "of_private.h"
LIST_HEAD(aliases_lookup);
struct device_node *of_aliases;
struct device_node *of_root;
struct device_node *of_chosen;
struct device_node *of_stdout;
static const char *of_stdout_options;
static struct device_node *__of_get_next_child(const struct device_node *node,
struct device_node *prev)
{
struct device_node *next;
if (!node)
return NULL;
next = prev ? prev->sibling : node->child;
for (; next; next = next->sibling)
if (next)
break;
return next;
}
#define __for_each_child_of_node(parent, child) \
for (child = __of_get_next_child(parent, NULL); child != NULL; \
child = __of_get_next_child(parent, child))
static inline struct device_node *of_find_node_by_path(const char *path)
{
return of_find_node_opts_by_path(path, NULL);
}
static struct device_node *__of_find_node_by_path(struct device_node *parent,
const char *path)
{
struct device_node *child;
int len;
len = strcspn(path, "/:");
if (!len)
return NULL;
__for_each_child_of_node(parent, child) {
const char *name = strrchr(child->full_name, '/');
if (!name) {
printf("malformed device_node %s\n", child->full_name);
continue;
}
name++;
if (strncmp(path, name, len) == 0 && (strlen(name) == len))
return child;
}
return NULL;
}
/**
* of_find_node_opts_by_path - Find a node matching a full OF path
* @path: Either the full path to match, or if the path does not
* start with '/', the name of a property of the /aliases
* node (an alias). In the case of an alias, the node
* matching the alias' value will be returned.
* @opts: Address of a pointer into which to store the start of
* an options string appended to the end of the path with
* a ':' separator.
*
* Valid paths:
* /foo/bar Full path
* foo Valid alias
* foo/bar Valid alias + relative path
*
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
*/
struct device_node *of_find_node_opts_by_path(const char *path, const char **opts)
{
struct device_node *np = NULL;
struct property *pp;
const char *separator = strchr(path, ':');
if (opts)
*opts = separator ? separator + 1 : NULL;
if (strcmp(path, "/") == 0)
return of_root;
/* The path could begin with an alias */
if (*path != '/') {
int len;
const char *p = separator;
if (!p)
p = strchrnul(path, '/');
len = p - path;
/* of_aliases must not be NULL */
if (!of_aliases)
return NULL;
for_each_property_of_node(of_aliases, pp) {
if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {
np = of_find_node_by_path(pp->value);
break;
}
}
if (!np)
return NULL;
path = p;
}
/* Step down the tree matching path components */
if (!np)
np = of_root;
while (np && *path == '/') {
path++; /* Increment past '/' delimiter */
np = __of_find_node_by_path(np, path);
path = strchrnul(path, '/');
if (separator && separator < path)
break;
}
return np;
}
static struct property *__of_find_property(const struct device_node *np,
const char *name, int *lenp)
{
struct property *pp;
if (!np)
return NULL;
for (pp = np->properties; pp; pp = pp->next) {
if (strcmp(pp->name, name) == 0) {
if (lenp)
*lenp = pp->length;
break;
}
}
return pp;
}
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp)
{
struct property *pp;
pp = __of_find_property(np, name, lenp);
return pp;
}
const void *of_get_property(const struct device_node *np, const char *name,
int *lenp)
{
struct property *pp = of_find_property(np, name, lenp);
return pp ? pp->value : NULL;
}
static int kstrtoint(const char *s, unsigned int base, int *res)
{
long long tmp;
tmp = strtoll(s, NULL, base);
if (tmp != (long long)(int)tmp)
return -1;
*res = tmp;
return 0;
}
static void of_alias_add(struct alias_prop *ap, struct device_node *np,
int id, const char *stem, int stem_len)
{
ap->np = np;
ap->id = id;
strncpy(ap->stem, stem, stem_len);
ap->stem[stem_len] = 0;
list_add_tail(&ap->link, &aliases_lookup);
printf("adding DT alias:%s: stem=%s id=%i node=%s\n",
ap->alias, ap->stem, ap->id, of_node_full_name(np));
}
/**
* of_alias_scan - Scan all properties of the 'aliases' node
*
* The function scans all the properties of the 'aliases' node and populates
* the global lookup table with the properties. It returns the
* number of alias properties found, or an error code in case of failure.
*
* @dt_alloc: An allocator that provides a virtual address to memory
* for storing the resulting tree
*/
void of_alias_scan(void * (*dt_alloc)(uint32_t size, uint32_t align))
{
struct property *pp;
of_aliases = of_find_node_by_path("/aliases");
of_chosen = of_find_node_by_path("/chosen");
if (of_chosen == NULL)
of_chosen = of_find_node_by_path("/chosen@0");
if (of_chosen) {
/* linux,stdout-path and /aliases/stdout are for legacy compatibility */
const char *name = of_get_property(of_chosen, "stdout-path", NULL);
if (!name)
name = of_get_property(of_chosen, "linux,stdout-path", NULL);
if (!name)
name = of_get_property(of_aliases, "stdout", NULL);
if (name)
of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
}
if (!of_aliases)
return;
for_each_property_of_node(of_aliases, pp) {
const char *start = pp->name;
const char *end = start + strlen(start);
struct device_node *np;
struct alias_prop *ap;
int id, len;
/* Skip those we do not want to proceed */
if (!strcmp(pp->name, "name") ||
!strcmp(pp->name, "phandle") ||
!strcmp(pp->name, "linux,phandle"))
continue;
np = of_find_node_by_path(pp->value);
if (!np)
continue;
/* walk the alias backwards to extract the id and work out
* the 'stem' string */
while (isdigit(*(end-1)) && end > start)
end--;
len = end - start;
if (kstrtoint(end, 10, &id) < 0)
continue;
/* Allocate an alias_prop with enough space for the stem */
ap = dt_alloc(sizeof(*ap) + len + 1, 4);
if (!ap)
continue;
memset(ap, 0, sizeof(*ap) + len + 1);
ap->alias = start;
of_alias_add(ap, np, id, start, len);
}
}

415
packages/vsky/libdsp/driver/of/fdt.c

@ -0,0 +1,415 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "libfdt.h"
#include "of.h"
#include "of_private.h"
static void * __alloc(uint32_t size, uint32_t align)
{
return malloc(size);
}
static inline void of_node_set_flag(struct device_node *n, unsigned long flag)
{
n->_flags |= (flag << 1);
}
static void reverse_nodes(struct device_node *parent)
{
struct device_node *child, *next;
/* In-depth first */
child = parent->child;
while (child) {
reverse_nodes(child);
child = child->sibling;
}
/* Reverse the nodes in the child list */
child = parent->child;
parent->child = NULL;
while (child) {
next = child->sibling;
child->sibling = parent->child;
parent->child = child;
child = next;
}
}
static void *unflatten_dt_alloc(void **mem, unsigned long size,
unsigned long align)
{
unsigned char *dp;
dp = *mem;
dp += size;
*mem = dp;
//*mem += size;
return dp;
}
static void populate_properties(const void *blob,
int offset,
void **mem,
struct device_node *np,
const char *nodename,
int dryrun)
{
struct property *pp, **pprev = NULL;
int cur;
int has_name = 0;
pprev = &np->properties;
for (cur = fdt_first_property_offset(blob, offset);
cur >= 0;
cur = fdt_next_property_offset(blob, cur)) {
const uint32_t *val;
const char *pname;
int sz;
val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
if (!val) {
printf("Cannot locate property at 0x%x\n", cur);
continue;
}
if (!pname) {
printf("Cannot find property name at 0x%x\n", cur);
continue;
}
if (!strcmp(pname, "name"))
has_name = 1;
pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property));
if (dryrun)
continue;
/* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties. If both
* appear and have different values, things
* will get weird. Don't do that.
*/
if (!strcmp(pname, "phandle") ||
!strcmp(pname, "linux,phandle")) {
if (!np->phandle) {
np->phandle = *val; //be32_to_cpup(val);
}
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff
*/
if (!strcmp(pname, "ibm,phandle")) {
np->phandle = *val; //be32_to_cpup(val);
}
pp->name = (char *)pname;
pp->length = sz;
pp->value = (void *)val;
*pprev = pp;
pprev = &pp->next;
}
/* With version 0x10 we may not have the name property,
* recreate it here from the unit name if absent
*/
if (!has_name) {
const char *p = nodename, *ps = p, *pa = NULL;
int len;
while (*p) {
if ((*p) == '@')
pa = p;
else if ((*p) == '/')
ps = p + 1;
p++;
}
if (pa < ps)
pa = p;
len = (pa - ps) + 1;
pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
__alignof__(struct property));
if (!dryrun) {
pp->name = "name";
pp->length = len;
pp->value = pp + 1;
*pprev = pp;
pprev = &pp->next;
memcpy(pp->value, ps, len - 1);
((char *)pp->value)[len - 1] = 0;
printf("fixed up name for %s -> %s\n",
nodename, (char *)pp->value);
}
}
if (!dryrun)
*pprev = NULL;
}
static unsigned int populate_node(const void *blob,
int offset,
void **mem,
struct device_node *dad,
unsigned int fpsize,
struct device_node **pnp,
int dryrun)
{
struct device_node *np;
const char *pathp;
unsigned int l, allocl;
int new_format = 0;
pathp = fdt_get_name(blob, offset, (int *)&l);
if (!pathp) {
*pnp = NULL;
return 0;
}
allocl = ++l;
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
* it later. We detect this because the first character of the name is
* not '/'.
*/
if ((*pathp) != '/') {
new_format = 1;
if (fpsize == 0) {
/* root node: special case. fpsize accounts for path
* plus terminating zero. root node only has '/', so
* fpsize should be 2, but we want to avoid the first
* level nodes to have two '/' so we use fpsize 1 here
*/
fpsize = 1;
allocl = 2;
l = 1;
pathp = "";
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
*/
fpsize += l;
allocl = fpsize;
}
}
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
/* rebuild full path for new format */
if (dad && dad->parent) {
strcpy(fn, dad->full_name);
#ifdef DEBUG
if ((strlen(fn) + l + 1) != allocl) {
pr_debug("%s: p: %d, l: %d, a: %d\n",
pathp, (int)strlen(fn),
l, allocl);
}
#endif
fn += strlen(fn);
}
*(fn++) = '/';
}
memcpy(fn, pathp, l);
if (dad != NULL) {
np->parent = dad;
np->sibling = dad->child;
dad->child = np;
}
}
populate_properties(blob, offset, mem, np, pathp, dryrun);
if (!dryrun) {
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
if (!np->name)
np->name = "<NULL>";
if (!np->type)
np->type = "<NULL>";
}
*pnp = np;
return fpsize;
}
/**
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
* @blob: The parent device tree blob
* @mem: Memory chunk to use for allocating device nodes and properties
* @dad: Parent struct device_node
* @nodepp: The device_node tree created by the call
*
* It returns the size of unflattened device tree or error code
*/
static int unflatten_dt_nodes(const void *blob,
void *mem,
struct device_node *dad,
struct device_node **nodepp)
{
struct device_node *root;
int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH 64
unsigned int fpsizes[FDT_MAX_DEPTH];
struct device_node *nps[FDT_MAX_DEPTH];
void *base = mem;
int dryrun = !base;
if (nodepp)
*nodepp = NULL;
/*
* We're unflattening device sub-tree if @dad is valid. There are
* possibly multiple nodes in the first level of depth. We need
* set @depth to 1 to make fdt_next_node() happy as it bails
* immediately when negative @depth is found. Otherwise, the device
* nodes except the first one won't be unflattened successfully.
*/
if (dad)
depth = initial_depth = 1;
root = dad;
fpsizes[depth] = dad ? strlen(of_node_full_name(dad)) : 0;
nps[depth] = dad;
for (offset = 0;
offset >= 0 && depth >= initial_depth;
offset = fdt_next_node(blob, offset, &depth)) {
if (depth >= FDT_MAX_DEPTH) {
printf("depth overflow.\n");
continue;
}
fpsizes[depth+1] = populate_node(blob, offset, &mem,
nps[depth],
fpsizes[depth],
&nps[depth+1], dryrun);
if (!fpsizes[depth+1])
return (unsigned char *)mem - (unsigned char *)base;
if (!dryrun && nodepp && !*nodepp)
*nodepp = nps[depth+1];
if (!dryrun && !root)
root = nps[depth+1];
}
if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
printf("Error %d processing FDT\n", offset);
return -22;
}
/*
* Reverse the child list. Some drivers assumes node order matches .dts
* node order
*/
if (!dryrun)
reverse_nodes(root);
return (unsigned char *)mem - (unsigned char *)base;
}
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device-tree, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
* @blob: The blob to expand
* @dad: Parent device node
* @mynodes: The device_node tree created by the call
* @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree
*
* Returns NULL on failure or the memory chunk containing the unflattened
* device tree on success.
*/
static void *__unflatten_device_tree(const void *blob,
struct device_node *dad,
struct device_node **mynodes,
void *(*dt_alloc)(uint32_t size, uint32_t align),
int detached)
{
int size;
void *mem;
printf(" -> unflatten_device_tree()\n");
if (!blob) {
printf("No device tree pointer\n");
return NULL;
}
printf("Unflattening device tree:\n");
printf("magic: %08x\n", fdt_magic(blob));
printf("size: %08x\n", fdt_totalsize(blob));
printf("version: %08x\n", fdt_version(blob));
if (fdt_check_header(blob)) {
printf("Invalid device tree blob header\n");
return NULL;
}
/* First pass, scan for size */
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
if (size < 0)
return NULL;
size = ((size + 3) & (~3)); //(ALIGN(size, 4);
printf(" size is %d, allocating...\n", size);
/* Allocate memory for the expanded device tree */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
memset(mem, 0, size);
*(uint32_t *)((unsigned char *)mem + size) = 0xefbeadde;
printf(" unflattening %p...\n", mem);
/* Second pass, do actual unflattening */
unflatten_dt_nodes(blob, mem, dad, mynodes);
if (*(uint32_t *)((unsigned char *)mem + size) != 0xefbeadde)
printf("End of tree marker overwritten: %08x\n",
*(uint32_t *)((unsigned char *)mem + size));
if (detached && mynodes) {
of_node_set_flag(*mynodes, OF_DETACHED);
printf("unflattened tree is detached\n");
}
printf(" <- unflatten_device_tree()\n");
return mem;
}
/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
*/
int unflatten_device_tree(void *mempos)
{
__unflatten_device_tree(mempos, NULL, &of_root,
__alloc, 0);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(__alloc);
return of_root ? 0 : -1;
}

19
packages/vsky/libdsp/driver/of/of_private.h

@ -0,0 +1,19 @@
#ifndef __OF_PRIVATE_H____
#define __OF_PRIVATE_H____
#include "list.h"
struct alias_prop {
struct list_head link;
const char *alias;
struct device_node *np;
int id;
char stem[0];
};
extern struct device_node *of_root;
extern struct device_node *of_aliases;
extern struct device_node *of_chosen;
extern struct device_node *of_stdout;
#endif

23
packages/vsky/libdsp/driver/utils.c

@ -95,3 +95,26 @@ int strncasecmp(const char *s1, const char *s2, size_t n)
return (0);
}
#undef u_char
char *strchrnul(const char *s, int c)
{
while (*s && *s != (char)c)
s++;
return (char *)s;
}
size_t strcspn(const char *s, const char *reject)
{
const char *p;
const char *r;
size_t count = 0;
for (p = s; *p != '\0'; ++p) {
for (r = reject; *r != '\0'; ++r) {
if (*p == *r)
return count;
}
++count;
}
return count;
}

46
packages/vsky/libdsp/inc/list.h

@ -0,0 +1,46 @@
#ifndef __LIST_H____
#define __LIST_H____
struct list_head {
struct list_head *next, *prev;
};
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *_new,
struct list_head *prev,
struct list_head *next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
#endif

74
packages/vsky/libdsp/inc/of.h

@ -0,0 +1,74 @@
#ifndef __DRIVER_OF_H__
#define __DRIVER_OF_H__
struct property {
char *name;
int length;
void *value;
struct property *next;
unsigned long _flags;
unsigned int unique_id;
};
enum fwnode_type {
FWNODE_INVALID = 0,
FWNODE_OF,
FWNODE_ACPI,
FWNODE_ACPI_DATA,
FWNODE_PDATA,
FWNODE_IRQCHIP,
};
struct fwnode_handle {
enum fwnode_type type;
struct fwnode_handle *secondary;
};
/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED 2 /* node has been detached from the device tree */
#define OF_POPULATED 3 /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */
#define OF_BAD_ADDR ((u64)-1)
struct device_node {
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
unsigned int phandle;
struct fwnode_handle fwnode;
const char *name;
const char *type;
const char *full_name;
unsigned long _flags;
struct property *properties;
struct property *deadprops; /* removed properties */
};
static inline void of_node_init(struct device_node *node)
{
node->fwnode.type = FWNODE_OF;
}
#define for_each_property_of_node(dn, pp) \
for (pp = dn->properties; pp != NULL; pp = pp->next)
static inline const char *of_node_full_name(const struct device_node *np)
{
return np ? np->full_name : "<no-node>";
}
const void *of_get_property(const struct device_node *np, const char *name, int *lenp);
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);
struct device_node *of_find_node_opts_by_path(const char *path, const char **opts);
void of_alias_scan(void * (*dt_alloc)(uint32_t size, uint32_t align));
int unflatten_device_tree(void *mempos);
#endif

2
packages/vsky/libdsp/inc/utils.h

@ -12,6 +12,8 @@ unsigned int get_global_l2_address(unsigned int addr);
int strncasecmp(const char *s1, const char *s2, size_t n);
int strcasecmp(const char *s1, const char *s2);
char *strchrnul(const char *s, int c);
size_t strcspn(const char *s, const char *reject);
#ifdef __cplusplus
}
#endif

9
packages/vsky/libdsp/lua/lualib/kit.c

@ -11,6 +11,7 @@
#include "uart.h"
#include "power_ctrl.h"
#include "elfloader.h"
#include "of.h"
/* lua */
#include "script.h"
#include "auxiliar.h"
@ -493,6 +494,13 @@ static int __strtoul(lua_State *L)
return 1;
}
static int __unflatten(lua_State *L)
{
unsigned int addr = luaL_checkinteger(L, 1);
lua_pushboolean(L, unflatten_device_tree((void *)addr) == 0);
return 1;
}
static struct luaL_Reg utils[] = {
{"coreId", __get_core_id},
{"l2Address",__to_l2_address},
@ -521,6 +529,7 @@ static struct luaL_Reg board[] = {
{"flash", __flash_open},
{"uart", __uart_open},
{"power", __powerup},
{"unflatten", __unflatten},
{NULL, NULL}
};

2
packages/vsky/libdsp/package.bld

@ -39,6 +39,8 @@ var drvFiles = [
"driver/elf.c",
"driver/elfloader.c",
"driver/hyplink.c",
"driver/of/base.c",
"driver/of/fdt.c",
];
var emacFiles = [

Loading…
Cancel
Save