]> Git Repo - qemu.git/blob - device_tree.c
cuda: port POWERDOWN command to new framework
[qemu.git] / device_tree.c
1 /*
2  * Functions to help device tree manipulation using libfdt.
3  * It also provides functions to read entries from device tree proc
4  * interface.
5  *
6  * Copyright 2008 IBM Corporation.
7  * Authors: Jerone Young <[email protected]>
8  *          Hollis Blanchard <[email protected]>
9  *
10  * This work is licensed under the GNU GPL license version 2 or later.
11  *
12  */
13
14 #include "qemu/osdep.h"
15
16 #include "qemu-common.h"
17 #include "qemu/error-report.h"
18 #include "sysemu/device_tree.h"
19 #include "sysemu/sysemu.h"
20 #include "hw/loader.h"
21 #include "hw/boards.h"
22 #include "qemu/config-file.h"
23
24 #include <libfdt.h>
25
26 #define FDT_MAX_SIZE  0x10000
27
28 void *create_device_tree(int *sizep)
29 {
30     void *fdt;
31     int ret;
32
33     *sizep = FDT_MAX_SIZE;
34     fdt = g_malloc0(FDT_MAX_SIZE);
35     ret = fdt_create(fdt, FDT_MAX_SIZE);
36     if (ret < 0) {
37         goto fail;
38     }
39     ret = fdt_finish_reservemap(fdt);
40     if (ret < 0) {
41         goto fail;
42     }
43     ret = fdt_begin_node(fdt, "");
44     if (ret < 0) {
45         goto fail;
46     }
47     ret = fdt_end_node(fdt);
48     if (ret < 0) {
49         goto fail;
50     }
51     ret = fdt_finish(fdt);
52     if (ret < 0) {
53         goto fail;
54     }
55     ret = fdt_open_into(fdt, fdt, *sizep);
56     if (ret) {
57         error_report("Unable to copy device tree in memory");
58         exit(1);
59     }
60
61     return fdt;
62 fail:
63     error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
64     exit(1);
65 }
66
67 void *load_device_tree(const char *filename_path, int *sizep)
68 {
69     int dt_size;
70     int dt_file_load_size;
71     int ret;
72     void *fdt = NULL;
73
74     *sizep = 0;
75     dt_size = get_image_size(filename_path);
76     if (dt_size < 0) {
77         error_report("Unable to get size of device tree file '%s'",
78                      filename_path);
79         goto fail;
80     }
81
82     /* Expand to 2x size to give enough room for manipulation.  */
83     dt_size += 10000;
84     dt_size *= 2;
85     /* First allocate space in qemu for device tree */
86     fdt = g_malloc0(dt_size);
87
88     dt_file_load_size = load_image(filename_path, fdt);
89     if (dt_file_load_size < 0) {
90         error_report("Unable to open device tree file '%s'",
91                      filename_path);
92         goto fail;
93     }
94
95     ret = fdt_open_into(fdt, fdt, dt_size);
96     if (ret) {
97         error_report("Unable to copy device tree in memory");
98         goto fail;
99     }
100
101     /* Check sanity of device tree */
102     if (fdt_check_header(fdt)) {
103         error_report("Device tree file loaded into memory is invalid: %s",
104                      filename_path);
105         goto fail;
106     }
107     *sizep = dt_size;
108     return fdt;
109
110 fail:
111     g_free(fdt);
112     return NULL;
113 }
114
115 static int findnode_nofail(void *fdt, const char *node_path)
116 {
117     int offset;
118
119     offset = fdt_path_offset(fdt, node_path);
120     if (offset < 0) {
121         error_report("%s Couldn't find node %s: %s", __func__, node_path,
122                      fdt_strerror(offset));
123         exit(1);
124     }
125
126     return offset;
127 }
128
129 int qemu_fdt_setprop(void *fdt, const char *node_path,
130                      const char *property, const void *val, int size)
131 {
132     int r;
133
134     r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
135     if (r < 0) {
136         error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
137                      property, fdt_strerror(r));
138         exit(1);
139     }
140
141     return r;
142 }
143
144 int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
145                           const char *property, uint32_t val)
146 {
147     int r;
148
149     r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
150     if (r < 0) {
151         error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
152                      node_path, property, val, fdt_strerror(r));
153         exit(1);
154     }
155
156     return r;
157 }
158
159 int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
160                          const char *property, uint64_t val)
161 {
162     val = cpu_to_be64(val);
163     return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
164 }
165
166 int qemu_fdt_setprop_string(void *fdt, const char *node_path,
167                             const char *property, const char *string)
168 {
169     int r;
170
171     r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
172     if (r < 0) {
173         error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
174                      node_path, property, string, fdt_strerror(r));
175         exit(1);
176     }
177
178     return r;
179 }
180
181 const void *qemu_fdt_getprop(void *fdt, const char *node_path,
182                              const char *property, int *lenp)
183 {
184     int len;
185     const void *r;
186     if (!lenp) {
187         lenp = &len;
188     }
189     r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
190     if (!r) {
191         error_report("%s: Couldn't get %s/%s: %s", __func__,
192                      node_path, property, fdt_strerror(*lenp));
193         exit(1);
194     }
195     return r;
196 }
197
198 uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
199                                const char *property)
200 {
201     int len;
202     const uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len);
203     if (len != 4) {
204         error_report("%s: %s/%s not 4 bytes long (not a cell?)",
205                      __func__, node_path, property);
206         exit(1);
207     }
208     return be32_to_cpu(*p);
209 }
210
211 uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
212 {
213     uint32_t r;
214
215     r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
216     if (r == 0) {
217         error_report("%s: Couldn't get phandle for %s: %s", __func__,
218                      path, fdt_strerror(r));
219         exit(1);
220     }
221
222     return r;
223 }
224
225 int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
226                              const char *property,
227                              const char *target_node_path)
228 {
229     uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
230     return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
231 }
232
233 uint32_t qemu_fdt_alloc_phandle(void *fdt)
234 {
235     static int phandle = 0x0;
236
237     /*
238      * We need to find out if the user gave us special instruction at
239      * which phandle id to start allocating phandles.
240      */
241     if (!phandle) {
242         phandle = machine_phandle_start(current_machine);
243     }
244
245     if (!phandle) {
246         /*
247          * None or invalid phandle given on the command line, so fall back to
248          * default starting point.
249          */
250         phandle = 0x8000;
251     }
252
253     return phandle++;
254 }
255
256 int qemu_fdt_nop_node(void *fdt, const char *node_path)
257 {
258     int r;
259
260     r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
261     if (r < 0) {
262         error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
263                      fdt_strerror(r));
264         exit(1);
265     }
266
267     return r;
268 }
269
270 int qemu_fdt_add_subnode(void *fdt, const char *name)
271 {
272     char *dupname = g_strdup(name);
273     char *basename = strrchr(dupname, '/');
274     int retval;
275     int parent = 0;
276
277     if (!basename) {
278         g_free(dupname);
279         return -1;
280     }
281
282     basename[0] = '\0';
283     basename++;
284
285     if (dupname[0]) {
286         parent = findnode_nofail(fdt, dupname);
287     }
288
289     retval = fdt_add_subnode(fdt, parent, basename);
290     if (retval < 0) {
291         error_report("FDT: Failed to create subnode %s: %s", name,
292                      fdt_strerror(retval));
293         exit(1);
294     }
295
296     g_free(dupname);
297     return retval;
298 }
299
300 void qemu_fdt_dumpdtb(void *fdt, int size)
301 {
302     const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
303
304     if (dumpdtb) {
305         /* Dump the dtb to a file and quit */
306         exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
307     }
308 }
309
310 int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
311                                             const char *node_path,
312                                             const char *property,
313                                             int numvalues,
314                                             uint64_t *values)
315 {
316     uint32_t *propcells;
317     uint64_t value;
318     int cellnum, vnum, ncells;
319     uint32_t hival;
320     int ret;
321
322     propcells = g_new0(uint32_t, numvalues * 2);
323
324     cellnum = 0;
325     for (vnum = 0; vnum < numvalues; vnum++) {
326         ncells = values[vnum * 2];
327         if (ncells != 1 && ncells != 2) {
328             ret = -1;
329             goto out;
330         }
331         value = values[vnum * 2 + 1];
332         hival = cpu_to_be32(value >> 32);
333         if (ncells > 1) {
334             propcells[cellnum++] = hival;
335         } else if (hival != 0) {
336             ret = -1;
337             goto out;
338         }
339         propcells[cellnum++] = cpu_to_be32(value);
340     }
341
342     ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
343                            cellnum * sizeof(uint32_t));
344 out:
345     g_free(propcells);
346     return ret;
347 }
This page took 0.042843 seconds and 4 git commands to generate.