Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /*
3 : : * Produce and consume flattened device trees
4 : : *
5 : : * Copyright 2013-2019 IBM Corp.
6 : : */
7 : :
8 : : #include <skiboot.h>
9 : : #include <stdarg.h>
10 : : #include <libfdt.h>
11 : : #include <device.h>
12 : : #include <chip.h>
13 : : #include <cpu.h>
14 : : #include <opal.h>
15 : : #include <interrupts.h>
16 : : #include <fsp.h>
17 : : #include <cec.h>
18 : : #include <vpd.h>
19 : : #include <ccan/str/str.h>
20 : :
21 : : static int fdt_error;
22 : :
23 : : #undef DEBUG_FDT
24 : : #ifdef DEBUG_FDT
25 : : #define FDT_DBG(fmt, a...) prlog(PR_DEBUG, "FDT: " fmt, ##a)
26 : : #else
27 : : #define FDT_DBG(fmt, a...)
28 : : #endif
29 : :
30 : 1012 : static void __save_err(int err, const char *str)
31 : : {
32 : : FDT_DBG("rc: %d from \"%s\"\n", err, str);
33 : 1012 : if (err && !fdt_error) {
34 : 0 : prerror("FDT: Error %d from \"%s\"\n", err, str);
35 : 0 : fdt_error = err;
36 : : }
37 : 1012 : }
38 : :
39 : : #define save_err(...) __save_err(__VA_ARGS__, #__VA_ARGS__)
40 : :
41 : 80 : static void dt_property_cell(void *fdt, const char *name, u32 cell)
42 : : {
43 : 80 : save_err(fdt_property_cell(fdt, name, cell));
44 : 80 : }
45 : :
46 : 80 : static void dt_begin_node(void *fdt, const struct dt_node *dn)
47 : : {
48 : 80 : save_err(fdt_begin_node(fdt, dn->name));
49 : :
50 : 80 : dt_property_cell(fdt, "phandle", dn->phandle);
51 : 80 : }
52 : :
53 : 766 : static void dt_property(void *fdt, const struct dt_property *p)
54 : : {
55 : 766 : save_err(fdt_property(fdt, p->name, p->prop, p->len));
56 : 766 : }
57 : :
58 : 80 : static void dt_end_node(void *fdt)
59 : : {
60 : 80 : save_err(fdt_end_node(fdt));
61 : 80 : }
62 : :
63 : : #ifdef DEBUG_FDT
64 : : static void dump_fdt(void *fdt)
65 : : {
66 : : int i, off, depth, err;
67 : :
68 : : prlog(PR_INFO, "Device tree %u@%p\n", fdt_totalsize(fdt), fdt);
69 : : err = fdt_check_header(fdt);
70 : : if (err) {
71 : : prerror("fdt_check_header: %s\n", fdt_strerror(err));
72 : : return;
73 : : }
74 : : prlog(PR_INFO, "fdt_check_header passed\n");
75 : :
76 : : prlog(PR_INFO, "fdt_num_mem_rsv = %u\n", fdt_num_mem_rsv(fdt));
77 : : for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
78 : : u64 addr, size;
79 : :
80 : : err = fdt_get_mem_rsv(fdt, i, &addr, &size);
81 : : if (err) {
82 : : prlog(PR_INFO, " ERR %s\n", fdt_strerror(err));
83 : : return;
84 : : }
85 : : prlog(PR_INFO, " mem_rsv[%i] = %lu@%#lx\n",
86 : : i, (long)addr, (long)size);
87 : : }
88 : :
89 : : for (off = fdt_next_node(fdt, 0, &depth);
90 : : off > 0;
91 : : off = fdt_next_node(fdt, off, &depth)) {
92 : : int len;
93 : : const char *name;
94 : :
95 : : name = fdt_get_name(fdt, off, &len);
96 : : if (!name) {
97 : : prerror("fdt: offset %i no name!\n", off);
98 : : return;
99 : : }
100 : : prlog(PR_INFO, "name: %s [%u]\n", name, off);
101 : : }
102 : : }
103 : : #endif
104 : :
105 : 80 : static void flatten_dt_properties(void *fdt, const struct dt_node *dn)
106 : : {
107 : : const struct dt_property *p;
108 : :
109 : 849 : list_for_each(&dn->properties, p, list) {
110 : 769 : if (strstarts(p->name, DT_PRIVATE))
111 : 3 : continue;
112 : :
113 : : FDT_DBG(" prop: %s size: %ld\n", p->name, p->len);
114 : 766 : dt_property(fdt, p);
115 : : }
116 : 80 : }
117 : :
118 : 80 : static void flatten_dt_node(void *fdt, const struct dt_node *root,
119 : : bool exclusive)
120 : : {
121 : : const struct dt_node *i;
122 : :
123 : 80 : if (!exclusive) {
124 : : FDT_DBG("node: %s\n", root->name);
125 : 80 : dt_begin_node(fdt, root);
126 : 80 : flatten_dt_properties(fdt, root);
127 : : }
128 : :
129 : 159 : list_for_each(&root->children, i, list)
130 : 79 : flatten_dt_node(fdt, i, false);
131 : :
132 : 80 : if (!exclusive)
133 : 80 : dt_end_node(fdt);
134 : 80 : }
135 : :
136 : 1 : static void create_dtb_reservemap(void *fdt, const struct dt_node *root)
137 : : {
138 : : uint64_t base, size;
139 : : const __be64 *ranges;
140 : : const struct dt_property *prop;
141 : : int i;
142 : :
143 : : /* Duplicate the reserved-ranges property into the fdt reservemap */
144 : 1 : prop = dt_find_property(root, "reserved-ranges");
145 : 1 : if (prop) {
146 : 1 : ranges = (const void *)prop->prop;
147 : :
148 : 4 : for (i = 0; i < prop->len / (sizeof(uint64_t) * 2); i++) {
149 : 3 : base = be64_to_cpu(*(ranges++));
150 : 3 : size = be64_to_cpu(*(ranges++));
151 : 3 : save_err(fdt_add_reservemap_entry(fdt, base, size));
152 : : }
153 : : }
154 : :
155 : 1 : save_err(fdt_finish_reservemap(fdt));
156 : 1 : }
157 : :
158 : 1 : static int __create_dtb(void *fdt, size_t len,
159 : : const struct dt_node *root,
160 : : bool exclusive)
161 : : {
162 : 1 : if (chip_quirk(QUIRK_SLOW_SIM))
163 : 0 : save_err(fdt_create_with_flags(fdt, len, FDT_CREATE_FLAG_NO_NAME_DEDUP));
164 : : else
165 : 1 : save_err(fdt_create_with_flags(fdt, len, 0));
166 : 1 : if (fdt_error)
167 : 0 : goto err;
168 : :
169 : 1 : if (root == dt_root && !exclusive)
170 : 1 : create_dtb_reservemap(fdt, root);
171 : : else
172 : 0 : save_err(fdt_finish_reservemap(fdt));
173 : :
174 : 1 : flatten_dt_node(fdt, root, exclusive);
175 : :
176 : 1 : save_err(fdt_finish(fdt));
177 : 1 : if (fdt_error) {
178 : 0 : err:
179 : 0 : prerror("dtb: error %s\n", fdt_strerror(fdt_error));
180 : 0 : return fdt_error;
181 : : }
182 : :
183 : : #ifdef DEBUG_FDT
184 : : dump_fdt(fdt);
185 : : #endif
186 : 1 : return 0;
187 : : }
188 : :
189 : 1 : void *create_dtb(const struct dt_node *root, bool exclusive)
190 : : {
191 : 1 : void *fdt = NULL;
192 : 1 : size_t len = DEVICE_TREE_MAX_SIZE;
193 : 1 : uint32_t old_last_phandle = get_last_phandle();
194 : : int ret;
195 : :
196 : : do {
197 : 1 : set_last_phandle(old_last_phandle);
198 : 1 : fdt_error = 0;
199 : 1 : fdt = malloc(len);
200 : 1 : if (!fdt) {
201 : 0 : prerror("dtb: could not malloc %lu\n", (long)len);
202 : 0 : return NULL;
203 : : }
204 : :
205 : 1 : ret = __create_dtb(fdt, len, root, exclusive);
206 : 1 : if (ret) {
207 : 0 : free(fdt);
208 : 0 : fdt = NULL;
209 : : }
210 : :
211 : 1 : len *= 2;
212 : 1 : } while (ret == -FDT_ERR_NOSPACE);
213 : :
214 : 1 : return fdt;
215 : : }
216 : :
217 : 0 : static int64_t opal_get_device_tree(uint32_t phandle,
218 : : uint64_t buf, uint64_t len)
219 : : {
220 : : struct dt_node *root;
221 : 0 : void *fdt = (void *)buf;
222 : : uint32_t old_last_phandle;
223 : : int64_t totalsize;
224 : : int ret;
225 : :
226 : 0 : if (!opal_addr_valid(fdt))
227 : 0 : return OPAL_PARAMETER;
228 : :
229 : 0 : root = dt_find_by_phandle(dt_root, phandle);
230 : 0 : if (!root)
231 : 0 : return OPAL_PARAMETER;
232 : :
233 : 0 : if (!fdt) {
234 : 0 : fdt = create_dtb(root, true);
235 : 0 : if (!fdt)
236 : 0 : return OPAL_INTERNAL_ERROR;
237 : 0 : totalsize = fdt_totalsize(fdt);
238 : 0 : free(fdt);
239 : 0 : return totalsize;
240 : : }
241 : :
242 : 0 : if (!len)
243 : 0 : return OPAL_PARAMETER;
244 : :
245 : 0 : fdt_error = 0;
246 : 0 : old_last_phandle = get_last_phandle();
247 : 0 : ret = __create_dtb(fdt, len, root, true);
248 : 0 : if (ret) {
249 : 0 : set_last_phandle(old_last_phandle);
250 : 0 : if (ret == -FDT_ERR_NOSPACE)
251 : 0 : return OPAL_NO_MEM;
252 : :
253 : 0 : return OPAL_EMPTY;
254 : : }
255 : :
256 : 0 : return OPAL_SUCCESS;
257 : : }
258 : : opal_call(OPAL_GET_DEVICE_TREE, opal_get_device_tree, 3);
|