+// See LICENSE for license details.
+
+#ifndef _RISCV_DEVICETREE_H
+#define _RISCV_DEVICETREE_H
+
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <map>
+#include <vector>
+#include <arpa/inet.h>
+
+#define FDT_MAGIC 0xd00dfeedU
+#define FDT_VERSION 17
+#define FDT_COMP_VERSION 16
+#define FDT_BEGIN_NODE 1
+#define FDT_END_NODE 2
+#define FDT_PROP 3
+#define FDT_END 9
+
+struct fdt_header {
+ uint32_t magic;
+ uint32_t totalsize;
+ uint32_t off_dt_struct;
+ uint32_t off_dt_strings;
+ uint32_t off_rsvmap;
+ uint32_t version;
+ uint32_t last_comp_version;
+ uint32_t boot_cpuid_phys;
+ uint32_t size_dt_strings;
+ uint32_t size_dt_struct;
+};
+
+struct fdt_reserve_entry {
+ uint64_t address;
+ uint64_t size;
+};
+
+struct string_table {
+ std::map<std::string, size_t> strings;
+ std::vector<char> data;
+
+ size_t add(std::string s) {
+ if (!strings.count(s)) {
+ strings[s] = data.size();
+ data.insert(data.end(), s.begin(), s.end());
+ data.push_back(0);
+ }
+ return strings[s];
+ }
+};
+
+struct device_tree {
+ device_tree() {
+ memset(rsvmap, 0, sizeof(rsvmap));
+ }
+
+ void begin_node(std::string s) {
+ std::vector<uint32_t> name = s2v(s);
+ sblock.push_back(FDT_BEGIN_NODE);
+ sblock.insert(sblock.end(), name.begin(), name.end());
+ }
+
+ void end_node() {
+ sblock.push_back(FDT_END_NODE);
+ }
+
+ std::vector<char> finalize() {
+ sblock.push_back(FDT_END);
+
+ struct fdt_header h;
+ h.size_dt_struct = sblock.size() * sizeof(sblock[0]);
+ h.size_dt_strings = strings.data.size();
+ h.magic = FDT_MAGIC;
+ h.off_rsvmap = sizeof(h);
+ h.off_dt_struct = h.off_rsvmap + sizeof(rsvmap);
+ h.off_dt_strings = h.off_dt_struct + h.size_dt_struct;
+ h.totalsize = h.off_dt_strings + h.size_dt_strings;
+ h.version = FDT_VERSION;
+ h.last_comp_version = FDT_COMP_VERSION;
+ h.boot_cpuid_phys = 0;
+
+ for (uint32_t* p = &h.magic; p < &h.magic + sizeof(h)/sizeof(uint32_t); p++)
+ *p = htonl(*p);
+ for (uint32_t& p : sblock)
+ p = htonl(p);
+
+ std::vector<char> res;
+ res.insert(res.end(), (char*)&h, (char*)&h + sizeof(h));
+ res.insert(res.end(), (char*)&rsvmap, (char*)&rsvmap + sizeof(rsvmap));
+ res.insert(res.end(), (char*)&sblock[0],
+ (char*)&sblock[0] + sblock.size() * sizeof(sblock[0]));
+ res.insert(res.end(), strings.data.begin(), strings.data.end());
+ return res;
+ }
+
+ void add_prop(std::string name, uint32_t data)
+ {
+ add_prop(name, std::vector<uint32_t>(1, data), sizeof(data));
+ }
+
+ void add_reg(std::vector<uint64_t> values)
+ {
+ std::vector<uint32_t> v;
+ for (auto x : values) {
+ v.push_back(x >> 32);
+ v.push_back(x);
+ }
+ add_prop("reg", v, v.size() * sizeof(v[0]));
+ }
+
+ void add_prop(std::string name, std::string data)
+ {
+ add_prop(name, s2v(data), data.size()+1);
+ }
+
+ private:
+ struct string_table strings;
+ std::vector<uint32_t> sblock;
+ struct fdt_reserve_entry rsvmap[1];
+
+ std::vector<uint32_t> s2v(std::string data) {
+ std::vector<char> v(data.begin(), data.end());
+ do {
+ v.push_back(0);
+ } while (v.size() % 4);
+
+ std::vector<uint32_t> words;
+ for (size_t i = 0; i < v.size(); i += 4)
+ words.push_back((v[i] << 24) | (v[i+1] << 16) | (v[i+2] << 8) | v[i+3]);
+ return words;
+ }
+
+ void add_prop(std::string name, std::vector<uint32_t> data, size_t len) {
+ sblock.push_back(FDT_PROP);
+ sblock.push_back(len);
+ sblock.push_back(strings.add(name));
+ sblock.insert(sblock.end(), data.begin(), data.end());
+ }
+};
+
+#endif