move gic to new devtree interface
[lunaix-os.git] / lunaix-os / hal / devtree / dt.c
index 72c443c340bc3026691145bdd6147dc6b3572bd4..ee68cf63d8a9405361903809b1cc9a2b5e39c389 100644 (file)
 #include <lunaix/mm/valloc.h>
 #include <lunaix/syslog.h>
-
-#include <klibc/string.h>
+#include <lunaix/owloysius.h>
 
 #include "devtree.h"
 
 LOG_MODULE("dtb")
 
+static morph_t* devtree_obj_root;
 static struct dt_context dtctx;
 
-void 
-fdt_itbegin(struct fdt_iter* fdti, struct fdt_header* fdt_hdr)
+void
+fdt_load(struct fdt_blob* fdt, ptr_t base)
 {
-    unsigned int off_struct, off_str;
-    struct fdt_token* tok;
-    const char* str_blk;
+    struct fdt_header* hdr;
 
-    off_str    = le(fdt_hdr->off_dt_strings);
-    off_struct = le(fdt_hdr->off_dt_struct);
+    fdt->fdt_base = base;
 
-    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
-    };
-}
+    hdr = fdt->header;
+    if (hdr->magic != FDT_MAGIC) {
+        FATAL("invalid dtb, unexpected magic: 0x%x, expect: 0x%x", 
+                hdr->magic, FDT_MAGIC);
+    }
 
-void 
-fdt_itend(struct fdt_iter* fdti)
-{
-    fdti->pos = NULL;
+    fdt->plat_rsvd_base = base + hdr->off_mem_rsvmap;
+    fdt->str_block_base = base + hdr->off_dt_strings;
+    fdt->root.ptr       = base + hdr->off_dt_struct;
 }
 
 bool
-fdt_itnext(struct fdt_iter* fdti)
+fdt_next_boot_rsvdmem(struct fdt_blob* fdt, fdt_loc_t* loc, 
+                      struct dt_memory_node* mem)
 {
-    struct fdt_token *current;
-    struct fdt_prop  *prop;
+    fdt_loc_t current;
 
-    current = fdti->pos;
-    if (!current) {
+    current = *loc;
+
+    if (!current.rsvd_ent->addr && !current.rsvd_ent->addr) {
         return false;
     }
 
+    mem->base = current.rsvd_ent->addr;
+    mem->size = current.rsvd_ent->size;
+    mem->type = FDT_MEM_RSVD;
+
+    current.rsvd_ent++;
+    *loc = current;
+
+    return true;
+}
+
+fdt_loc_t
+fdt_next_token(fdt_loc_t loc, int* delta_depth)
+{
+    int d = 0;
+
+    do {
+        if (fdt_node(loc.token)) {
+            d++;
+            loc.ptr += strlen(loc.node->name) + 1;
+            loc.ptr  = ROUNDUP(loc.ptr, sizeof(int));
+        }
+        else if (fdt_node_end(loc.token)) {
+            d--;
+        }
+        else if (fdt_prop(loc.token)) {
+            loc.ptr   += loc.prop->len + 2 * sizeof(int);
+            loc.ptr    = ROUNDUP(loc.ptr, sizeof(int));
+        }
+
+        loc.token++;
+    } while (fdt_nope(loc.token));
+
+    *delta_depth = d;
+    return loc;
+}
+
+bool
+fdt_next_sibling(fdt_loc_t loc, fdt_loc_t* loc_out)
+{
+    int depth = 0, new_depth = 0;
+
+    do {
+        loc = fdt_next_token(loc, &new_depth);
+        depth += new_depth;
+    } while (depth > 0);
+
+    *loc_out = loc;
+    return !fdt_node_end(loc.token);
+}
+
+fdt_loc_t
+fdt_descend_into(fdt_loc_t loc) 
+{
+    fdt_loc_t new_loc;
+    int depth = 0;
+
+    new_loc = fdt_next_token(loc, &depth);
+
+    return depth != 1 ? loc : new_loc;
+}
+
+bool
+fdt_find_prop(const struct fdt_blob* fdt, fdt_loc_t loc, 
+              const char* name, struct dtp_val* val)
+{
+    char* prop_name;
+
+    loc = fdt_descend_into(loc);
+
     do
     {
-        if (fdt_nope(current)) {
+        if (!fdt_prop(loc.token)) {
             continue;
         }
 
-        if (fdt_prop(current)) {
-            prop    = (struct fdt_prop*) current;
-            current = offset(current, prop->len);
-            continue;
-        }
+        prop_name = fdt_prop_key(fdt, loc);
         
-        if (fdt_node_end(current)) {
-            fdti->depth--;
+        if (!streq(prop_name, name)) {
             continue;
         }
         
-        // node begin
-
-        fdti->depth++;
-        if (fdti->depth == 1) {
-            // enter root node
-            break;
-        }
-
-        while (!fdt_prop(current) && !fdt_node_end(current)) {
-            current++;
+        if (likely(val)) {
+            val->encoded = (dt_enc_t)loc.prop->val;
+            val->size    = loc.prop->len;
         }
-
-        if (fdt_prop(current)) {
-            break;
-        }
-        
-        current++;
+        return true;
         
-    } while (fdt_nope(current) && fdti->depth > 0);
+    } while (fdt_next_sibling(loc, &loc));
 
-    return fdti->depth > 0;
+    return false;
 }
 
-bool 
-fdt_itnext_at(struct fdt_iter* fdti, int level)
+bool
+fdt_memscan_begin(struct fdt_memscan* mscan, const struct fdt_blob* fdt)
 {
-    while (fdti->depth != level && fdt_itnext(fdti));
-    
-    return fdti->depth == level;
-}
+    struct dtp_val val;
+    fdt_loc_t loc;
 
-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];
+    loc  = fdt->root;
+    loc  = fdt_descend_into(loc);
+
+    if (fdt_find_prop(fdt, loc, "#address-cells", &val))
+    {
+        mscan->root_addr_c = val.ref->u32_val;
+    }
+
+    if (fdt_find_prop(fdt, loc, "#size-cells", &val))
+    {
+        mscan->root_size_c = val.ref->u32_val;
+    }
+
+    mscan->loc = loc;
+    mscan->node_type = FDT_MEM_FREE;
 }
 
+#define get_size(mscan, val)    \
+    (mscan->root_size_c == 1 ? (val)->ref->u32_val : (val)->ref->u64_val)
+
+#define get_addr(mscan, val)    \
+    (mscan->root_addr_c == 1 ? (val)->ref->u32_val : (val)->ref->u64_val)
+
 bool
-fdt_memrsvd_itnext(struct fdt_memrsvd_iter* rsvdi)
+fdt_memscan_nextnode(struct fdt_memscan* mscan, struct fdt_blob* fdt)
 {
-    struct fdt_memrsvd_ent* ent;
+    char* prop_name;
 
-    ent = rsvdi->block;
-    if (!ent) {
-        return false;
+    struct dtp_val val, reg_val;
+    fdt_loc_t loc, next;
+    struct dtpropi dtpi;
+    bool has_reg = false, found = false;
+
+    next = mscan->loc;
+
+restart:
+    do
+    {
+        loc = next;
+        
+        if (!fdt_node(loc.token))
+            continue;
+
+        if (mscan->node_type != FDT_MEM_FREE) {
+            found = true;
+            continue;
+        }
+
+        if (streq(loc.node->name, "reserved-memory")) {
+            // dived into /reserved-memory, walking for childrens
+            mscan->node_type = FDT_MEM_RSVD;
+            loc = fdt_descend_into(loc);
+            continue;
+        }
+
+        if (!fdt_find_prop(fdt, loc, "device_type", &val))
+            continue;
+
+        if (!streq(val.str_val, "memory"))
+            continue;
+
+        found = true;
+    } while (fdt_next_sibling(loc, &next) && !found);
+
+    if (found) goto _found;
+
+    // emerged from /reserved-memory, resume walking for /memory
+    if (mscan->node_type != FDT_MEM_FREE) {
+        mscan->node_type = FDT_MEM_FREE;
+        goto restart;
     }
 
-    rsvdi->block++;
+    return false;
 
-    return ent->addr || ent->size;
-}
+_found:
 
-void
-fdt_memrsvd_itend(struct fdt_memrsvd_iter* rsvdi)
-{
-    rsvdi->block = NULL;
-}
+    dtpi_init_empty(&mscan->regit);
+    mscan->found = loc;
+    mscan->loc   = next;
 
-static inline bool
-propeq(struct fdt_iter* it, const char* key)
-{
-    return streq(fdtit_prop_key(it), key);
-}
+    has_reg = fdt_find_prop(fdt, loc, "reg", &val);
+    if (mscan->node_type == FDT_MEM_RSVD) {
+        goto do_rsvd_child;
+    }
 
-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);
-}
+    if (!has_reg)
+    {
+        WARN("malformed memory node");
+        goto restart;
+    }
 
-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);
-}
+    dtpi_init(&mscan->regit, &val);
 
-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);
-}
+    return true;
 
-static inline u32_t
-__prop_getu32(struct fdt_iter* it)
-{
-    return le(*(u32_t*)&it->prop[1]);
+do_rsvd_child:
+
+    mscan->node_attr.nomap = fdt_find_prop(fdt, loc, "no-map", NULL);
+    mscan->node_attr.reusable = fdt_find_prop(fdt, loc, "reusable", NULL);
+
+    if (has_reg)
+    {
+        dtpi_init(&mscan->regit, &val);
+        mscan->node_type = FDT_MEM_RSVD;
+        return true;
+    }
+    
+    if (!fdt_find_prop(fdt, loc, "size", &val)) 
+    {
+        WARN("malformed reserved memory child node");
+        goto restart;
+    }
+    
+    mscan->node_type = FDT_MEM_RSVD_DYNAMIC;
+    mscan->node_attr.total_size = get_size(mscan, &val);
+
+    if (fdt_find_prop(fdt, loc, "alignment", &val)) {
+        mscan->node_attr.alignment = get_size(mscan, &val);
+    }
+
+    if (fdt_find_prop(fdt, loc, "alloc-ranges", &val)) {
+        dtpi_init(&mscan->regit, &val);
+    }
+
+    return true;
 }
 
-static bool
-__parse_stdbase_prop(struct fdt_iter* it, struct dt_node_base* node)
+bool
+fdt_memscan_nextrange(struct fdt_memscan* mscan, struct dt_memory_node* mem)
 {
-    struct fdt_prop* prop;
+    struct dtp_val val;
 
-    prop = it->prop;
+    if (dtpi_is_empty(&mscan->regit)) {
+        return false;
+    }
 
-    if (propeq(it, "compatible")) {
-        __mkprop_ptr(it, &node->compat);
-    } 
+    if (!dtpi_has_next(&mscan->regit)) {
+        return false;
+    }
+
+    if (dtpi_next_val(&mscan->regit, &val, mscan->root_addr_c)) {
+        mem->base = get_addr(mscan, &val);
+    }
+
+    if (dtpi_next_val(&mscan->regit, &val, mscan->root_size_c)) {
+        mem->size = get_size(mscan, &val);
+    }
+
+    mem->type = mscan->node_type;
     
-    else if (propeq(it, "model")) {
-        node->model = (const char*)&prop[1];
+    if (mem->type == FDT_MEM_RSVD_DYNAMIC) {
+        mem->dyn_alloc_attr = mscan->node_attr;
+    }
+
+    return true;
+}
+
+static bool
+__parse_stdbase_prop(struct fdt_blob* fdt, fdt_loc_t loc, 
+                     struct dtn_base* node)
+{
+    if (propeq(fdt, loc, "compatible")) {
+        __mkprop_ptr(loc, &node->compat);
     } 
 
-    else if (propeq(it, "phandle")) {
-        node->phandle = __prop_getu32(it);
-        hashtable_hash_in(dtctx.phnds_table, 
-                          &node->phnd_link, node->phandle);
+    else if (propeq(fdt, loc, "phandle")) {
+        node->phandle = __prop_getu32(loc);
     }
     
-    else if (propeq(it, "#address-cells")) {
-        node->addr_c = (char)__prop_getu32(it);
+    else if (propeq(fdt, loc, "#address-cells")) {
+        node->addr_c = (char)__prop_getu32(loc);
     } 
     
-    else if (propeq(it, "#size-cells")) {
-        node->sz_c = (char)__prop_getu32(it);
+    else if (propeq(fdt, loc, "#size-cells")) {
+        node->sz_c = (char)__prop_getu32(loc);
     } 
     
-    else if (propeq(it, "#interrupt-cells")) {
-        node->intr_c = (char)__prop_getu32(it);
+    else if (propeq(fdt, loc, "#interrupt-cells")) {
+        node->intr_c = (char)__prop_getu32(loc);
     } 
     
-    else if (propeq(it, "status")) {
-        char peek = *(char*)&it->prop[1];
+    else if (propeq(fdt, loc, "status")) {
+        char peek = loc.prop->val_str[0];
         if (peek == 'o') {
             node->status = STATUS_OK;
         }
@@ -216,22 +342,18 @@ __parse_stdbase_prop(struct fdt_iter* it, struct dt_node_base* node)
 }
 
 static bool
-__parse_stdnode_prop(struct fdt_iter* it, struct dt_node* node)
+__parse_stdnode_prop(struct fdt_blob* fdt, fdt_loc_t loc, struct dtn* node)
 {
-    if (propeq(it, "reg")) {
-        __mkprop_ptr(it, &node->reg);
+    if (propeq(fdt, loc, "reg")) {
+        __mkprop_ptr(loc, &node->reg);
     }
 
-    else if (propeq(it, "virtual-reg")) {
-        __mkprop_ptr(it, &node->vreg);
+    else if (propeq(fdt, loc, "ranges")) {
+        __mkprop_ptr(loc, &node->ranges);
     }
 
-    else if (propeq(it, "ranges")) {
-        __mkprop_ptr(it, &node->ranges);
-    }
-
-    else if (propeq(it, "dma-ranges")) {
-        __mkprop_ptr(it, &node->dma_ranges);
+    else if (propeq(fdt, loc, "dma-ranges")) {
+        __mkprop_ptr(loc, &node->dma_ranges);
     }
 
     else {
@@ -242,17 +364,17 @@ __parse_stdnode_prop(struct fdt_iter* it, struct dt_node* node)
 }
 
 static bool
-__parse_stdflags(struct fdt_iter* it, struct dt_node_base* node)
+__parse_stdflags(struct fdt_blob* fdt, fdt_loc_t loc, struct dtn_base* node)
 {
-    if (propeq(it, "dma-coherent")) {
+    if (propeq(fdt, loc, "dma-coherent")) {
         node->dma_coherent = true;
     }
 
-    else if (propeq(it, "dma-noncoherent")) {
+    else if (propeq(fdt, loc, "dma-noncoherent")) {
         node->dma_ncoherent = true;
     }
 
-    else if (propeq(it, "interrupt-controller")) {
+    else if (propeq(fdt, loc, "interrupt-controller")) {
         node->intr_controll = true;
     }
 
@@ -263,152 +385,156 @@ __parse_stdflags(struct fdt_iter* it, struct dt_node_base* node)
     return true;
 }
 
+static inline void
+__dt_node_set_name(struct dtn* node, const char* name)
+{
+    changeling_setname(&node->mobj, name);
+}
+
+static inline void
+__init_prop_table(struct dtn_base* node)
+{
+    struct dtp_table* propt;
+
+    propt = valloc(sizeof(*propt));
+    hashtable_init(propt->_op_bucket);
+
+    node->props = propt;
+}
+
+#define prop_table_add(node, prop)                                             \
+            hashtable_hash_in( (node)->props->_op_bucket,                      \
+                              &(prop)->ht, (prop)->key.hash);
+
 static void
-__parse_other_prop(struct fdt_iter* it, struct dt_node_base* node)
+__parse_other_prop(struct fdt_blob* fdt, fdt_loc_t loc, struct dtn_base* node)
 {
-    struct dt_prop* prop;
+    struct dtp* prop;
     const char* key;
     unsigned int hash;
 
     prop = valloc(sizeof(*prop));
-    key  = fdtit_prop_key(it);
+    key  = fdt_prop_key(fdt, loc);
 
     prop->key = HSTR(key, strlen(key));
-    __mkprop_ptr(it, &prop->val);
+    __mkprop_ptr(loc, &prop->val);
 
     hstr_rehash(&prop->key, HSTR_FULL_HASH);
-    hash = prop->key.hash;
 
-    hashtable_hash_in(node->_op_bucket, &prop->ht, hash);
+    prop_table_add(node, prop);
 }
 
 static void
-__fill_node(struct fdt_iter* it, struct dt_node* node)
+__fill_node(struct fdt_blob* fdt, fdt_loc_t loc, struct dtn* node)
 {
-    if (__parse_stdflags(it, &node->base)) {
+    if (__parse_stdflags(fdt, loc, &node->base)) {
         return;
     }
 
-    if (__parse_stdbase_prop(it, &node->base)) {
+    if (__parse_stdbase_prop(fdt, loc, &node->base)) {
         return;
     }
 
-    if (__parse_stdnode_prop(it, node)) {
+    if (__parse_stdnode_prop(fdt, loc, node)) {
         return;
     }
 
-    if (__parse_stdintr_prop(it, &node->intr)) {
+    if (parse_stdintr_prop(fdt, loc, &node->intr)) {
         return;
     }
 
-    __parse_other_prop(it, &node->base);
+    __parse_other_prop(fdt, loc, &node->base);
 }
 
-static void
-__fill_root(struct fdt_iter* it, struct dt_root* node)
+static inline void
+__set_parent(struct dtn_base* parent, struct dtn_base* node)
 {
-    if (__parse_stdflags(it, &node->base)) {
-        return;
-    }
+    morph_t* parent_obj;
     
-    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];
+    parent_obj   = devtree_obj_root;
+    node->parent = parent;
+    
+    if (parent) {
+        node->addr_c = parent->addr_c;
+        node->sz_c = parent->sz_c;
+        node->intr_c = parent->intr_c;
+        parent_obj = dt_mobj(parent);
     }
 
-    __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);
-
-    if (node->parent)
-        node->_std = node->parent->_std;
+    changeling_attach(parent_obj, dt_mobj(node));
 }
 
 static inline void
-__init_node_regular(struct dt_node* node)
+__init_node_regular(struct dtn* node)
 {
-    __init_node(&node->base);
+    __init_prop_table(&node->base);
+    changeling_morph_anon(NULL, node->mobj, dt_morpher);
     node->intr.parent_hnd = PHND_NULL;
 }
 
 static void
-__expand_extended_intr(struct dt_intr_node* intrupt)
+__expand_extended_intr(struct dtn_intr* 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;
+    struct dtpropi it;
+    struct dtp_val  arr;
+    struct dtn *domain;
+    struct dtspec_intr* ispec;
+    int nr_intrs = 0;
 
-    if (!intrupt->intr.extended) {
-        return;
+    if (!intrupt->extended) {
+        nr_intrs  = intrupt->raw_ispecs.size / sizeof(u32_t);
+        nr_intrs /= intrupt->parent->base.intr_c; 
+        goto done;
     }
 
-    arr = intrupt->intr.arr;
-    node = INTR_TO_DTNODE(intrupt);
+    arr = intrupt->raw_ispecs;
 
-    llist_init_head(&intrupt->intr.values);
+    llist_init_head(&intrupt->ext_ispecs);
     
-    dt_decode(&it, &node->base, &arr, 1);
-    
-    dt_phnd_t phnd;
-    do {
-        phnd   = dtprop_to_u32(it.prop_loc);
-        master = dt_resolve_phandle(phnd);
+    dtpi_init(&it, &arr);
+
+    while(dtpi_has_next(&it)) 
+    {
+        domain = dtpi_next_hnd(&it);
 
-        if (!master) {
-            WARN("dtb: (intr_extended) malformed phandle: %d", phnd);
+        if (!domain) {
+            WARN("(intr_extended) malformed phandle");
             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);
+        ispec = valloc(sizeof(*ispec));
         
-    } while(dtprop_next(&it));
+        ispec->domain = domain;
+        dtpi_next_val(&it, &ispec->val, domain->base.intr_c);
+
+        llist_append(&intrupt->ext_ispecs, &ispec->ispecs);
+        nr_intrs++;
+    };
+
+done:
+    intrupt->nr_intrs = nr_intrs;
 }
 
 static void
 __resolve_phnd_references()
 {
-    struct dt_node_base *pos, *n;
-    struct dt_node *node, *parent, *default_parent;
-    struct dt_intr_node* intrupt;
+    struct dtn_base *pos, *n;
+    struct dtn *node, *parent, *default_parent;
+    struct dtn_intr* intrupt;
     dt_phnd_t phnd;
     
     llist_for_each(pos, n, &dtctx.nodes, nodes)
     {
-        node = BASE_TO_DTNODE(pos);
+        node = dtn_from(pos);
         intrupt = &node->intr;
 
-        if (!node->base.intr_c) {
+        if (intrupt->parent_hnd == PHND_NULL) {
             continue;
         }
 
         phnd = intrupt->parent_hnd;
-        default_parent = (struct dt_node*)node->base.parent;
+        default_parent = (struct dtn*)node->base.parent;
         parent = default_parent;
 
         if (phnd != PHND_NULL) {
@@ -420,7 +546,7 @@ __resolve_phnd_references()
             parent = default_parent;
         }
 
-        intrupt->parent = &parent->intr;
+        intrupt->parent = parent;
 
         __expand_extended_intr(intrupt);
     }
@@ -429,101 +555,98 @@ __resolve_phnd_references()
 static void
 __resolve_inter_map()
 {
-    struct dt_node_base *pos, *n;
+    struct dtn_base *pos, *n;
 
     llist_for_each(pos, n, &dtctx.nodes, nodes)
     {
-        dt_resolve_interrupt_map(BASE_TO_DTNODE(pos));
+        dt_resolve_interrupt_map(dtn_from(pos));
     }
 }
 
 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 fdt_blob *fdt;
+    struct dtn      *node,
+                    *stack[16] = { NULL };
     
-    struct dt_node_base* depth[16];
-    bool is_root_level, filled;
+    int depth = 0, delta = 0, nr_nodes = 0;
+    fdt_loc_t  loc, next_loc;
+
+    fdt = &dtctx.fdt;
+    fdt_load(&dtctx.fdt, dtb_dropoff);
 
-    node = NULL;
-    depth[0] = NULL;
-    fdt_itbegin(&it, dtctx.fdt);
+    loc = fdt->root;
     
-    while (fdt_itnext(&it)) {
-        is_root_level = it.depth == 1;
+    while (!fdt_eof(loc.token)) 
+    {
+        next_loc = fdt_next_token(loc, &delta);
 
-        if (it.depth >= 16) {
+        if (depth >= 16) {
             // tree too deep
             ERROR("strange dtb, too deep to dive.");
             return false;
         }
 
-        depth[it.depth] = NULL;
-        node = depth[it.depth - 1];
+        assert(depth >= 0);
+        node = stack[depth];
 
-        if (!node) {
-            // need new node
-            if (unlikely(is_root_level)) {
-                node = vzalloc(sizeof(struct dt_root));
-                __init_node(node);
-            }
-            else {
-                node = vzalloc(sizeof(struct dt_node));
-                prev = depth[it.depth - 2];
-                node->parent = prev;
+        if (fdt_node(loc.token))
+        {
+            assert(!node);
+
+            node = vzalloc(sizeof(struct dtn));
+            __init_node_regular(node);
+            llist_append(&dtctx.nodes, &node->base.nodes);
 
-                __init_node_regular((struct dt_node*)node);
-                llist_append(&prev->children, &node->siblings);
+            __dt_node_set_name(node, loc.node->name);
 
-                llist_append(&dtctx.nodes, &node->nodes);
+            if (depth) {
+                __set_parent(&stack[depth - 1]->base, &node->base);
             }
 
-            node->name = (const char*)&it.pos[1];
+            nr_nodes++;
+            stack[depth] = node;
         }
 
-        if (unlikely(is_root_level)) {
-            __fill_root(&it, (struct dt_root*)node);
+        else if (depth > 1 && fdt_node_end(loc.token))
+        {
+            stack[depth - 1] = NULL;
         }
-        else {
-            __fill_node(&it, (struct dt_node*)node);
+
+        else if (fdt_prop(loc.token))
+        {
+            node = stack[depth - 1];
+
+            assert(depth && node);
+            __fill_node(fdt, loc, node);
         }
-    }
 
-    fdt_itend(&it);
+        depth += delta;
+        loc = next_loc;
+    }
 
-    dtctx.root = (struct dt_root*)depth[0];
+    dtctx.root = stack[0];
 
     __resolve_phnd_references();
     __resolve_inter_map();
 
-    INFO("device tree loaded");
+    INFO("%d nodes loaded.", nr_nodes);
 
     return true;
 }
 
-struct dt_node*
+struct dtn*
 dt_resolve_phandle(dt_phnd_t phandle)
 {
-    struct dt_node_base *pos, *n;
-    hashtable_hash_foreach(dtctx.phnds_table, phandle, pos, n, phnd_link)
+    struct dtn_base *pos, *n;
+    llist_for_each(pos, n, &dtctx.nodes, nodes)
     {
         if (pos->phandle == phandle) {
-            return (struct dt_node*)pos;
+            return (struct dtn*)pos;
         }
     }
 
@@ -531,10 +654,10 @@ dt_resolve_phandle(dt_phnd_t phandle)
 }
 
 static bool
-__byname_predicate(struct dt_node_iter* iter, struct dt_node_base* node)
+__byname_predicate(struct dtn_iter* iter, struct dtn_base* node)
 {
     int i = 0;
-    const char* be_matched = node->name;
+    const char* be_matched = HSTR_VAL(node->mobj.name);
     const char* name = (const char*)iter->closure;
 
     while (be_matched[i] && name[i])
@@ -550,74 +673,78 @@ __byname_predicate(struct dt_node_iter* iter, struct dt_node_base* node)
 }
 
 void
-dt_begin_find_byname(struct dt_node_iter* iter, 
-              struct dt_node* node, const char* name)
+dt_begin_find_byname(struct dtn_iter* iter, 
+              struct dtn* node, const char* name)
 {
     dt_begin_find(iter, node, __byname_predicate, name);
 }
 
 void
-dt_begin_find(struct dt_node_iter* iter, struct dt_node* node, 
+dt_begin_find(struct dtn_iter* iter, struct dtn* node, 
               node_predicate_t pred, void* closure)
 {
-    node = node ? : (struct dt_node*)dtctx.root;
+    node = node ? : (struct dtn*)dtctx.root;
 
     iter->head = &node->base;
     iter->matched = NULL;
     iter->closure = closure;
     iter->pred = pred;
 
-    struct dt_node_base *pos, *n;
-    llist_for_each(pos, n, &node->base.children, siblings)
+    morph_t *pos, *n;
+    struct dtn_base* base;
+    changeling_for_each(pos, n, &node->mobj)
     {
-        if (pred(iter, pos)) {
-            iter->matched = pos;
+        base = &changeling_reveal(pos, dt_morpher)->base;
+        if (pred(iter, base)) {
+            iter->matched = base;
             break;
         }
     }
 }
 
 bool
-dt_find_next(struct dt_node_iter* iter,
-             struct dt_node_base** matched)
+dt_find_next(struct dtn_iter* iter,
+             struct dtn_base** matched)
 {
     if (!dt_found_any(iter)) {
         return false;
     }
 
-    struct dt_node_base *pos, *head;
+    struct dtn *node;
+    morph_t *pos, *head;
 
-    head = iter->head;
-    pos = iter->matched;
-    *matched = pos;
+    head = dt_mobj(iter->head);
+    pos  = dt_mobj(iter->matched);
+    *matched = iter->matched;
 
-    while (&pos->siblings != &head->children)
+    while (&pos->sibs != &head->subs)
     {
-        pos = list_next(pos, struct dt_node_base, siblings);
+        pos = list_next(pos, morph_t, sibs);
+        node = changeling_reveal(pos, dt_morpher);
 
-        if (!iter->pred(iter, pos)) {
+        if (!iter->pred(iter, &node->base)) {
             continue;
         }
 
-        iter->matched = pos;
+        iter->matched = &node->base;
         return true;
     }
 
     return false;
 }
 
-struct dt_prop_val*
-dt_getprop(struct dt_node_base* base, const char* name)
+struct dtp_val*
+dt_getprop(struct dtn_base* base, const char* name)
 {
     struct hstr hashed_name;
-    struct dt_prop *pos, *n;
+    struct dtp *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(base->_op_bucket, hash, pos, n, ht)
+    hashtable_hash_foreach(base->props->_op_bucket, hash, pos, n, ht)
     {
         if (HSTR_EQ(&pos->key, &hashed_name)) {
             return &pos->val;
@@ -625,4 +752,171 @@ dt_getprop(struct dt_node_base* base, const char* name)
     }
 
     return NULL;
-}
\ No newline at end of file
+}
+
+void
+dtpx_compile_proplet(struct dtprop_def* proplet)
+{
+    int i;
+    unsigned int acc = 0;
+    struct dtprop_def* pl;
+    
+    for (i = 0; proplet[i].type && i < 10; ++i)
+    {
+        pl = &proplet[i];
+
+        if (pl->type == DTP_COMPX) {
+            if (pl->cell == 1) 
+                pl->type = DTP_U32;
+            else if (pl->cell == 2)
+                pl->type = DTP_U64;
+        }
+
+        pl->acc_sz = acc;
+        acc += pl->cell;
+    }
+
+    if (proplet[i - 1].type && i == 10) {
+        FATAL("invalid proplet: no terminator detected");
+    }
+
+    proplet[i].acc_sz = acc;
+}
+
+void
+dtpx_prepare_with(struct dtpropx* propx, struct dtp_val* prop,
+                  struct dtprop_def* proplet)
+{
+    int i;
+    bool has_str = false;
+    
+    for (i = 0; proplet[i].type; ++i);
+
+    propx->proplet = proplet;
+    propx->proplet_len = i;
+    propx->proplet_sz = proplet[i].acc_sz;
+    propx->raw = prop;
+    propx->row_loc = 0;
+}
+
+bool
+dtpx_goto_row(struct dtpropx* propx, int row)
+{
+    off_t loc;
+
+    loc  = propx->proplet_sz;
+    loc *= row;
+
+    if (loc * sizeof(u32_t) >= propx->raw->size) {
+        return false;
+    }
+
+    propx->row_loc = loc;
+    return true;
+}
+
+bool
+dtpx_next_row(struct dtpropx* propx)
+{
+    off_t loc;
+
+    loc  = propx->row_loc;
+    loc += propx->proplet_sz;
+
+    if (loc * sizeof(u32_t) >= propx->raw->size) {
+        return false;
+    }
+
+    propx->row_loc = loc;
+    return true;
+}
+
+bool
+dtpx_extract_at(struct dtpropx* propx, 
+                struct dtprop_xval* val, int col)
+{
+    struct dtprop_def* def;
+    union dtp_baseval* raw;
+    dt_enc_t enc;
+
+    if (unlikely(col >= propx->proplet_len)) {
+        return false;
+    }
+
+    def = &propx->proplet[col];
+    enc = &propx->raw->encoded[propx->row_loc + def->acc_sz];
+    raw = (union dtp_baseval*)enc;
+
+    val->archetype = def;
+
+    switch (def->type)
+    {
+        case DTP_U32:
+            val->u32 = raw->u32_val;
+            break;
+
+        case DTP_U64:
+            val->u64 = raw->u64_val;
+            break;
+
+        case DTP_PHANDLE:
+        {
+            ptr_t hnd = raw->phandle;
+            val->phandle = dt_resolve_phandle(hnd);
+        } break;
+
+        case DTP_COMPX:
+            {
+                val->composite = enc;
+            }
+            break;
+        
+        default:
+            break;
+    }
+
+    return true;
+}
+
+bool
+dtpx_extract_loc(struct dtpropx* propx, 
+                 struct dtprop_xval* val, int row, int col)
+{
+    ptr_t loc = propx->row_loc;
+
+    if (!dtpx_goto_row(propx, row))
+        return false;
+
+    
+    bool r = dtpx_extract_at(propx, val, col);
+    propx->row_loc = loc;
+    return r;
+}
+
+bool
+dtpx_extract_row(struct dtpropx* propx, struct dtprop_xval* vals, int len)
+{
+    assert(len == propx->proplet_len);
+
+    for (int i = 0; i < len; i++)
+    {
+        if (!dtpx_extract_at(propx, &vals[i], i)) {
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+struct dt_context*
+dt_main_context()
+{
+    return &dtctx;
+}
+
+static void
+__init_devtree()
+{
+    devtree_obj_root = changeling_spawn(NULL, NULL);
+}
+owloysius_fetch_init(__init_devtree, on_sysconf);
\ No newline at end of file