aboutsummaryrefslogtreecommitdiff
path: root/pk/devicetree.c
blob: 9a7f77233351d41edec4a23ecf360ebef8211414 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "devicetree.h"
#include "encoding.h"
#include "pk.h"
#include "mtrap.h"
#include <arpa/inet.h>
#include <stdbool.h>

static uintptr_t max_hart_id;

static uint64_t fdt_read_uint64(uint32_t* addr) {
  return ((uint64_t)ntohl(addr[0]) << 32) | ntohl(addr[1]);
}

static void fdt_handle_cpu(const char* isa, uint32_t* reg_addr, uint32_t reg_len)
{
  int xlen = sizeof(long) * 8;
  kassert(reg_len == 8);
  kassert(isa && isa[0]=='r' && isa[1]=='v' && isa[2]=='0'+(xlen/10));

  uintptr_t* base_addr = (uintptr_t*)(uintptr_t)fdt_read_uint64(reg_addr);
  debug_printk("at %p, ", base_addr);
  uintptr_t hart_id = *(uintptr_t*)(base_addr + CSR_MHARTID);
  debug_printk("found hart %ld\n", hart_id);
  hls_init(hart_id, base_addr);
  num_harts++;
  max_hart_id = MAX(max_hart_id, hart_id);
}

static void fdt_handle_mem(uint32_t* reg_addr, uint32_t reg_len)
{
  kassert(reg_len == 16);
  uint64_t base = fdt_read_uint64(reg_addr);
  uint64_t size = fdt_read_uint64(reg_addr+2);
  debug_printk("at %p, found %d MiB of memory\n", base, (int)(size >> 20));
  kassert(base == 0);
  mem_size = size;
}

// This code makes the following assumptions about FDTs:
// - They are trusted and don't need to be sanitized
// - All addresses and sizes are 64 bits (we don't parse #address-cells etc)

static uint32_t* parse_node(uint32_t* token, char* strings)
{
  const char* nodename = (const char*)token, *s, *dev_type = 0, *isa = 0;
  uint32_t reg_len = 0, *reg_addr = 0;
  token = (uint32_t*)nodename + strlen(nodename)/4+1;

  while (1) switch (ntohl(*token)) {
    case FDT_PROP: {
      token++;
      uint32_t len = ntohl(*token++);
      const char* name = strings + ntohl(*token++);
      if (strcmp(name, "device_type") == 0) {
        dev_type = (char*)token;
      } else if (strcmp(name, "isa") == 0) {
        isa = (char*)token;
      } else if (strcmp(name, "reg") == 0) {
        reg_len = len;
        reg_addr = token;
      }
      token += (len+3)/4;
      continue;
    }
    case FDT_BEGIN_NODE:
      token = parse_node(token+1, strings);
      continue;
    case FDT_END_NODE:
      goto out;
    case FDT_NOP:
      continue;
    default:
      kassert(0);
  }

out:
  if (dev_type && strcmp(dev_type, "cpu") == 0)
    fdt_handle_cpu(isa, reg_addr, reg_len);
  else if (dev_type && strcmp(dev_type, "memory") == 0)
    fdt_handle_mem(reg_addr, reg_len);

  return token+1;
}

void parse_device_tree()
{
  struct fdt_header* hdr = (struct fdt_header*)read_csr(miobase);
  debug_printk("reading device tree at %p\n", hdr);
  kassert(ntohl(hdr->magic) == FDT_MAGIC);
  char* strings = (char*)hdr + ntohl(hdr->off_dt_strings);
  uint32_t* root = (uint32_t*)((char*)hdr + ntohl(hdr->off_dt_struct));
  while (ntohl(*root++) != FDT_BEGIN_NODE);
  parse_node(root, strings);
  kassert(max_hart_id == num_harts-1);
}