summaryrefslogtreecommitdiffhomepage
path: root/libfdt/libfdt_address.c
blob: 7e78ea6491174cf3fdbc337f250126f3ef964957 (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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2024-2025, Amlal El Mahrouss (amlal@nekernel.org)
// Distributed under the Apache Software License, Version 2.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.apache.org/licenses/LICENSE-2.0)
// Official repository: https://github.com/nekernel-org/neboot

#include <libfdt/libfdt_env.h>

extern fdt32_t* fdt_getprop(const void* fdt, int nodeoffset, const char* name, int* len);
extern fdt32_t* fdt_append_prop(const void* fdt, int nodeoffset, const char* name, uint8_t* data,
                                int32_t len);

#ifndef FDT_MAX_NCELLS
#define FDT_MAX_NCELLS (4)
#endif

#ifndef FDT_BAD_NCELLS
#define FDT_BAD_NCELLS (14)
#endif

#ifndef FDT_NOT_FOUND
#define FDT_NOT_FOUND (1)
#endif

#ifndef FDT_BAD_VALUE
#define FDT_BAD_VALUE (15)
#endif

static int fdt_cells(const void* fdt, int nodeoffset, const char* name) {
  const fdt32_t* c   = null;
  uint32_t       val = 0;
  int            len = 0;

  c = fdt_getprop(fdt, nodeoffset, name, &len);
  if (c == null) return len;

  if (len != sizeof(*c)) return len;

  val = fdt32_to_cpu(*c);
  if (val > FDT_MAX_NCELLS) return -FDT_BAD_NCELLS;

  return (int) val;
}

int fdt_address_cells(const void* fdt, int nodeoffset) {
  int val = 0;
  val     = fdt_cells(fdt, nodeoffset, "#address-cells");
  if (val == 0) return -FDT_BAD_NCELLS;

  if (val == -FDT_NOT_FOUND) return 2;

  return (int) val;
}

int fdt_size_cells(const void* fdt, int nodeoffset) {
  int val = 0;
  val     = fdt_cells(fdt, nodeoffset, "#size-cells");

  if (val == -FDT_NOT_FOUND) return 2;

  return (int) val;
}

int fdt_append_prop_addr_range(void* fdt, int parent, int nodeoffset, const char* name,
                               uint64_t addr, uint64_t size) {
  int     addr_cells, size_cells, ret;
  uint8_t data[sizeof(fdt64_t) * 2], *prop;

  ret = fdt_address_cells(fdt, nodeoffset);
  if (ret < 0) return ret;

  addr_cells = ret;

  ret = fdt_size_cells(fdt, nodeoffset);
  if (ret < 0) return ret;

  size_cells = ret;

  // check address validity
  prop = data;
  if (addr_cells == 1) {
    // seems to do a check according to two parameters.
    // addr > __UINT32_MAX__ detects any out of range addresses?
    // ((__UINT32_MAX__ + 1 - addr) < size check if it's lower than it's size

    if ((addr > __UINT32_MAX__) || ((__UINT32_MAX__ + 1 - addr) < size)) return -FDT_BAD_VALUE;

    // finally set the flat device tree.
    fdt32_st(prop, (uint32_t) addr);
  } else if (addr_cells == 2) {
    // no need to check, apparently.
    fdt64_st(prop, addr);
  } else {
    return -FDT_BAD_NCELLS;
  }

  // access size
  prop += addr_cells * sizeof(fdt32_t);

  if (size_cells == 1) {
    if (size > __UINT32_MAX__) return -FDT_BAD_VALUE;

    fdt32_st(prop, (uint32_t) size);
  } else if (size_cells == 2) {
    fdt64_st(prop, size);
  } else {
    return -FDT_BAD_NCELLS;
  }

  return fdt_append_prop(fdt, nodeoffset, name, data, (addr_cells + size_cells) * sizeof(fdt32_t));
}