--- /dev/null
+#include <lunaix/mm/valloc.h>
+#include <lunaix/syslog.h>
+
+#include <klibc/string.h>
+
+#include <hal/devtree.h>
+
+LOG_MODULE("dtb")
+
+static struct dt_context dtctx;
+
+void
+fdt_itbegin(struct fdt_iter* fdti, struct fdt_header* fdt_hdr)
+{
+ unsigned int off_struct, off_str;
+ struct fdt_token* tok;
+ const char* str_blk;
+
+ off_str = le(fdt_hdr->off_dt_strings);
+ off_struct = le(fdt_hdr->off_dt_struct);
+
+ tok = offset_t(fdt_hdr, struct fdt_token, off_struct);
+ str_blk = offset_t(fdt_hdr, const char, off_str);
+
+ *fdti = (struct fdt_iter) {
+ .pos = tok,
+ .str_block = str_blk
+ };
+}
+
+void
+fdt_itend(struct fdt_iter* fdti)
+{
+ fdti->pos = NULL;
+}
+
+bool
+fdt_itnext(struct fdt_iter* fdti)
+{
+ struct fdt_token *current;
+ struct fdt_prop *prop;
+
+ current = fdti->pos;
+ if (!current) {
+ return false;
+ }
+
+ do
+ {
+ if (fdt_nope(current)) {
+ continue;
+ }
+
+ if (fdt_prop(current)) {
+ prop = (struct fdt_prop*) current;
+ current = offset(current, prop->len);
+ continue;
+ }
+
+ if (fdt_node_end(current)) {
+ fdti->depth--;
+ continue;
+ }
+
+ // node begin
+
+ fdti->depth++;
+ if (fdti->depth == 1) {
+ // enter root node
+ break;
+ }
+
+ while (!fdt_prop(current) && !fdt_node_end(current)) {
+ current++;
+ }
+
+ if (fdt_prop(current)) {
+ break;
+ }
+
+ current++;
+
+ } while (fdt_nope(current) && fdti->depth > 0);
+
+ return fdti->depth > 0;
+}
+
+bool
+fdt_itnext_at(struct fdt_iter* fdti, int level)
+{
+ while (fdti->depth != level && fdt_itnext(fdti));
+
+ return fdti->depth == level;
+}
+
+void
+fdt_memrsvd_itbegin(struct fdt_memrsvd_iter* rsvdi,
+ struct fdt_header* fdt_hdr)
+{
+ size_t off = le(fdt_hdr->off_mem_rsvmap);
+
+ rsvdi->block =
+ offset_t(fdt_hdr, typeof(*rsvdi->block), off);
+
+ rsvdi->block = &rsvdi->block[-1];
+}
+
+bool
+fdt_memrsvd_itnext(struct fdt_memrsvd_iter* rsvdi)
+{
+ struct fdt_memrsvd_ent* ent;
+
+ ent = rsvdi->block;
+ if (!ent) {
+ return false;
+ }
+
+ rsvdi->block++;
+
+ return ent->addr || ent->size;
+}
+
+void
+fdt_memrsvd_itend(struct fdt_memrsvd_iter* rsvdi)
+{
+ rsvdi->block = NULL;
+}
+
+static inline bool
+propeq(struct fdt_iter* it, const char* key)
+{
+ return streq(fdtit_prop_key(it), key);
+}
+
+static inline void
+__mkprop_val32(struct fdt_iter* it, struct dt_prop_val* val)
+{
+ val->u32_val = le(*(u32_t*)&it->prop[1]);
+ val->size = le(it->prop->len);
+}
+
+static inline void
+__mkprop_val64(struct fdt_iter* it, struct dt_prop_val* val)
+{
+ val->u64_val = le64(*(u64_t*)&it->prop[1]);
+ val->size = le(it->prop->len);
+}
+
+static inline void
+__mkprop_ptr(struct fdt_iter* it, struct dt_prop_val* val)
+{
+ val->ptr_val = __ptr(&it->prop[1]);
+ val->size = le(it->prop->len);
+}
+
+static inline u32_t
+__prop_getu32(struct fdt_iter* it)
+{
+ return le(*(u32_t*)&it->prop[1]);
+}
+
+static bool
+__parse_stdbase_prop(struct fdt_iter* it, struct dt_node_base* node)
+{
+ struct fdt_prop* prop;
+
+ prop = it->prop;
+
+ if (propeq(it, "compatible")) {
+ __mkprop_ptr(it, &node->compat);
+ }
+
+ else if (propeq(it, "model")) {
+ node->model = (const char*)&prop[1];
+ }
+
+ else if (propeq(it, "phandle")) {
+ node->phandle = __prop_getu32(it);
+ hashtable_hash_in(dtctx.phnds_table,
+ &node->phnd_link, node->phandle);
+ }
+
+ else if (propeq(it, "#address-cells")) {
+ node->addr_c = (char)__prop_getu32(it);
+ }
+
+ else if (propeq(it, "#size-cells")) {
+ node->sz_c = (char)__prop_getu32(it);
+ }
+
+ else if (propeq(it, "#interrupt-cells")) {
+ node->intr_c = (char)__prop_getu32(it);
+ }
+
+ else if (propeq(it, "status")) {
+ char peek = *(char*)&it->prop[1];
+ if (peek == 'o') {
+ node->status = STATUS_OK;
+ }
+ else if (peek == 'r') {
+ node->status = STATUS_RSVD;
+ }
+ else if (peek == 'd') {
+ node->status = STATUS_DISABLE;
+ }
+ else if (peek == 'f') {
+ node->status = STATUS_FAIL;
+ }
+ }
+
+ else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+__parse_stdnode_prop(struct fdt_iter* it, struct dt_node* node)
+{
+ if (propeq(it, "reg")) {
+ __mkprop_ptr(it, &node->reg);
+ }
+
+ else if (propeq(it, "virtual-reg")) {
+ __mkprop_ptr(it, &node->vreg);
+ }
+
+ else if (propeq(it, "ranges")) {
+ __mkprop_ptr(it, &node->ranges);
+ }
+
+ else if (propeq(it, "dma-ranges")) {
+ __mkprop_ptr(it, &node->dma_ranges);
+ }
+
+ else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+__parse_stdintr_prop(struct fdt_iter* it, struct dt_intr_node* node)
+{
+ if (propeq(it, "interrupt-map")) {
+ __mkprop_ptr(it, &node->intr_map);
+ }
+
+ else if (propeq(it, "interrupt-map-mask")) {
+ __mkprop_ptr(it, &node->intr_map_mask);
+ }
+
+ else if (propeq(it, "interrupt-parent")) {
+ node->parent_hnd = __prop_getu32(it);
+ }
+
+ else if (propeq(it, "interrupt-extended")) {
+ node->intr.extended = true;
+ __mkprop_ptr(it, &node->intr.arr);
+ }
+
+ else if (!node->intr.extended && propeq(it, "interrupts")) {
+ __mkprop_ptr(it, &node->intr.arr);
+ }
+
+ else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+__parse_stdflags(struct fdt_iter* it, struct dt_node_base* node)
+{
+ if (propeq(it, "dma-coherent")) {
+ node->dma_coherent = true;
+ }
+
+ else if (propeq(it, "dma-noncoherent")) {
+ node->dma_ncoherent = true;
+ }
+
+ else if (propeq(it, "interrupt-controller")) {
+ node->intr_controll = true;
+ }
+
+ else {
+ return false;
+ }
+
+ return true;
+}
+
+static void
+__parse_other_prop(struct fdt_iter* it, struct dt_node_base* node)
+{
+ struct dt_prop* prop;
+ const char* key;
+ unsigned int hash;
+
+ prop = valloc(sizeof(*prop));
+ key = fdtit_prop_key(it);
+
+ prop->key = HSTR(key, strlen(key));
+ __mkprop_ptr(it, &prop->val);
+
+ hstr_rehash(&prop->key, HSTR_FULL_HASH);
+ hash = prop->key.hash;
+
+ hashtable_hash_in(node->_op_bucket, &prop->ht, hash);
+}
+
+static void
+__fill_node(struct fdt_iter* it, struct dt_node* node)
+{
+ if (__parse_stdflags(it, &node->base)) {
+ return;
+ }
+
+ if (__parse_stdbase_prop(it, &node->base)) {
+ return;
+ }
+
+ if (__parse_stdnode_prop(it, node)) {
+ return;
+ }
+
+ if (__parse_stdintr_prop(it, &node->intr)) {
+ return;
+ }
+
+ __parse_other_prop(it, &node->base);
+}
+
+static void
+__fill_root(struct fdt_iter* it, struct dt_root* node)
+{
+ if (__parse_stdflags(it, &node->base)) {
+ return;
+ }
+
+ if (__parse_stdbase_prop(it, &node->base)) {
+ return;
+ }
+
+ struct fdt_prop* prop;
+
+ prop = it->prop;
+ if (propeq(it, "serial-number")) {
+ node->serial = (const char*)&prop[1];
+ }
+
+ else if (propeq(it, "chassis-type")) {
+ node->chassis = (const char*)&prop[1];
+ }
+
+ __parse_other_prop(it, &node->base);
+}
+
+static inline void
+__init_node(struct dt_node_base* node)
+{
+ hashtable_init(node->_op_bucket);
+ llist_init_head(&node->children);
+}
+
+static inline void
+__init_node_regular(struct dt_node* node)
+{
+ __init_node(&node->base);
+ node->intr.parent_hnd = PHND_NULL;
+}
+
+static void
+__expand_extended_intr(struct dt_intr_node* intrupt)
+{
+ struct dt_prop_iter it;
+ struct dt_prop_val arr;
+ struct dt_node *node;
+ struct dt_node *master;
+ struct dt_intr_prop* intr_prop;
+
+ if (!intrupt->intr.extended) {
+ return;
+ }
+
+ arr = intrupt->intr.arr;
+ node = DT_NODE(intrupt);
+
+ llist_init_head(&intrupt->intr.values);
+
+ dt_decode(&it, &node->base, &arr, 1);
+
+ dt_phnd_t phnd;
+ while(dtprop_next(&it)) {
+ phnd = dtprop_to_u32(it.prop_loc);
+ master = dt_resolve_phandle(phnd);
+
+ if (!master) {
+ WARN("dtb: (intr_extended) malformed phandle: %d", phnd);
+ continue;
+ }
+
+ intr_prop = valloc(sizeof(*intr_prop));
+
+ intr_prop->master = &master->intr;
+ intr_prop->val = (struct dt_prop_val) {
+ .encoded = it.prop_loc_next,
+ .size = master->base.intr_c
+ };
+
+ llist_append(&intrupt->intr.values, &intr_prop->props);
+ dtprop_next_n(&it, intr_prop->val.size);
+ }
+}
+
+static void
+__resolve_phnd_references()
+{
+ struct dt_node_base *pos, *n;
+ struct dt_node *node, *parent, *default_parent;
+ struct dt_intr_node* intrupt;
+ dt_phnd_t phnd;
+
+ llist_for_each(pos, n, &dtctx.nodes, nodes)
+ {
+ node = (struct dt_node*)pos;
+ intrupt = &node->intr;
+ if (!node->base.intr_c) {
+ continue;
+ }
+
+ phnd = intrupt->parent_hnd;
+ default_parent = (struct dt_node*)node->base.parent;
+ parent = default_parent;
+
+ if (phnd != PHND_NULL) {
+ parent = dt_resolve_phandle(phnd);
+ }
+
+ if (!parent) {
+ WARN("dtb: (phnd_resolve) malformed phandle: %d", phnd);
+ parent = default_parent;
+ }
+
+ intrupt->parent = &parent->intr;
+
+ __expand_extended_intr(intrupt);
+ }
+}
+
+bool
+dt_load(ptr_t dtb_dropoff)
+{
+ dtctx.reloacted_dtb = dtb_dropoff;
+
+ if (dtctx.fdt->magic != FDT_MAGIC) {
+ ERROR("invalid dtb, unexpected magic: 0x%x", dtctx.fdt->magic);
+ return false;
+ }
+
+ size_t str_off = le(dtctx.fdt->size_dt_strings);
+ dtctx.str_block = offset_t(dtb_dropoff, const char, str_off);
+
+ llist_init_head(&dtctx.nodes);
+ hashtable_init(dtctx.phnds_table);
+
+ struct fdt_iter it;
+ struct fdt_token* tok;
+ struct dt_node_base *node, *prev;
+
+ struct dt_node_base* depth[16];
+ bool is_root_level, filled;
+
+ node = NULL;
+ depth[0] = NULL;
+ fdt_itbegin(&it, dtctx.fdt);
+
+ while (fdt_itnext(&it)) {
+ is_root_level = it.depth == 1;
+
+ if (it.depth >= 16) {
+ // tree too deep
+ ERROR("strange dtb, too deep to dive.");
+ return false;
+ }
+
+ depth[it.depth] = NULL;
+ node = depth[it.depth - 1];
+
+ if (!node) {
+ // need new node
+ if (unlikely(is_root_level)) {
+ node = valloc(sizeof(struct dt_root));
+ __init_node(node);
+ }
+ else {
+ node = valloc(sizeof(struct dt_node));
+ prev = depth[it.depth - 2];
+
+ __init_node_regular((struct dt_node*)node);
+ llist_append(&prev->children, &node->siblings);
+ node->parent = prev;
+
+ llist_append(&dtctx.nodes, &node->nodes);
+ }
+
+ node->name = (const char*)&it.pos[1];
+ }
+
+ if (unlikely(is_root_level)) {
+ __fill_root(&it, (struct dt_root*)node);
+ }
+ else {
+ __fill_node(&it, (struct dt_node*)node);
+ }
+ }
+
+ fdt_itend(&it);
+
+ dtctx.root = (struct dt_root*)depth[0];
+
+ __resolve_phnd_references();
+
+ return true;
+}
+
+static bool
+__name_starts_with(struct dt_node_base* node, const char* name)
+{
+ int i = 0;
+ const char* be_matched = node->name;
+
+ while (be_matched[i] && name[i])
+ {
+ if (be_matched[i] != name[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+struct dt_node*
+dt_resolve_phandle(dt_phnd_t phandle)
+{
+ struct dt_node_base *pos, *n;
+ hashtable_hash_foreach(dtctx.phnds_table, phandle, pos, n, phnd_link)
+ {
+ if (pos->phandle == phandle) {
+ return (struct dt_node*)pos;
+ }
+ }
+
+ return NULL;
+}
+
+void
+dt_begin_find(struct dt_node_iter* iter,
+ struct dt_node* node, const char* name)
+{
+ node = node ? : (struct dt_node*)dtctx.root;
+
+ iter->head = &node->base;
+ iter->matched = NULL;
+ iter->name = name;
+
+ struct dt_node_base *pos, *n;
+ llist_for_each(pos, n, &node->base.children, siblings)
+ {
+ if (__name_starts_with(pos, name)) {
+ iter->matched = pos;
+ break;
+ }
+ }
+}
+
+bool
+dt_find_next(struct dt_node_iter* iter,
+ struct dt_node_base** matched)
+{
+ if (!dt_found_any(iter)) {
+ return false;
+ }
+
+ struct dt_node_base *pos, *head;
+
+ head = iter->head;
+ pos = iter->matched;
+ *matched = pos;
+
+ while (&pos->siblings != &head->children)
+ {
+ pos = list_next(pos, struct dt_node_base, siblings);
+
+ if (!__name_starts_with(pos, iter->name)) {
+ continue;
+ }
+
+ iter->matched = pos;
+ return true;
+ }
+
+ return false;
+}
+
+struct dt_prop_val*
+dt_getprop(struct dt_node* node, const char* name)
+{
+ struct hstr hashed_name;
+ struct dt_prop *pos, *n;
+ unsigned int hash;
+
+ hashed_name = HSTR(name, strlen(name));
+ hstr_rehash(&hashed_name, HSTR_FULL_HASH);
+ hash = hashed_name.hash;
+
+ hashtable_hash_foreach(node->base._op_bucket, hash, pos, n, ht)
+ {
+ if (HSTR_EQ(&pos->key, &hashed_name)) {
+ return &pos->val;
+ }
+ }
+
+ return NULL;
+}
\ No newline at end of file