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