sync with latest
[sdk/emulator/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 <jyoung5@us.ibm.com>
8  *          Hollis Blanchard <hollisb@us.ibm.com>
9  *
10  * This work is licensed under the GNU GPL license version 2 or later.
11  *
12  */
13
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20
21 #include "config.h"
22 #include "qemu-common.h"
23 #include "device_tree.h"
24 #include "hw/loader.h"
25 #include "qemu-option.h"
26 #include "qemu-config.h"
27
28 #include <libfdt.h>
29
30 #define FDT_MAX_SIZE  0x10000
31
32 void *create_device_tree(int *sizep)
33 {
34     void *fdt;
35     int ret;
36
37     *sizep = FDT_MAX_SIZE;
38     fdt = g_malloc0(FDT_MAX_SIZE);
39     ret = fdt_create(fdt, FDT_MAX_SIZE);
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         fprintf(stderr, "Unable to copy device tree in memory\n");
58         exit(1);
59     }
60
61     return fdt;
62 fail:
63     fprintf(stderr, "%s Couldn't create dt: %s\n", __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         printf("Unable to get size of device tree file '%s'\n",
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         printf("Unable to open device tree file '%s'\n",
91                filename_path);
92         goto fail;
93     }
94
95     ret = fdt_open_into(fdt, fdt, dt_size);
96     if (ret) {
97         printf("Unable to copy device tree in memory\n");
98         goto fail;
99     }
100
101     /* Check sanity of device tree */
102     if (fdt_check_header(fdt)) {
103         printf ("Device tree file loaded into memory is invalid: %s\n",
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         fprintf(stderr, "%s Couldn't find node %s: %s\n", __func__, node_path,
122                 fdt_strerror(offset));
123         exit(1);
124     }
125
126     return offset;
127 }
128
129 int qemu_devtree_setprop(void *fdt, const char *node_path,
130                          const char *property, const void *val_array, int size)
131 {
132     int r;
133
134     r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val_array, size);
135     if (r < 0) {
136         fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path,
137                 property, fdt_strerror(r));
138         exit(1);
139     }
140
141     return r;
142 }
143
144 int qemu_devtree_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         fprintf(stderr, "%s: Couldn't set %s/%s = %#08x: %s\n", __func__,
152                 node_path, property, val, fdt_strerror(r));
153         exit(1);
154     }
155
156     return r;
157 }
158
159 int qemu_devtree_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_devtree_setprop(fdt, node_path, property, &val, sizeof(val));
164 }
165
166 int qemu_devtree_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         fprintf(stderr, "%s: Couldn't set %s/%s = %s: %s\n", __func__,
174                 node_path, property, string, fdt_strerror(r));
175         exit(1);
176     }
177
178     return r;
179 }
180
181 const void *qemu_devtree_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         fprintf(stderr, "%s: Couldn't get %s/%s: %s\n", __func__,
192                 node_path, property, fdt_strerror(*lenp));
193         exit(1);
194     }
195     return r;
196 }
197
198 uint32_t qemu_devtree_getprop_cell(void *fdt, const char *node_path,
199                                    const char *property)
200 {
201     int len;
202     const uint32_t *p = qemu_devtree_getprop(fdt, node_path, property, &len);
203     if (len != 4) {
204         fprintf(stderr, "%s: %s/%s not 4 bytes long (not a cell?)\n",
205                 __func__, node_path, property);
206         exit(1);
207     }
208     return be32_to_cpu(*p);
209 }
210
211 uint32_t qemu_devtree_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         fprintf(stderr, "%s: Couldn't get phandle for %s: %s\n", __func__,
218                 path, fdt_strerror(r));
219         exit(1);
220     }
221
222     return r;
223 }
224
225 int qemu_devtree_setprop_phandle(void *fdt, const char *node_path,
226                                  const char *property,
227                                  const char *target_node_path)
228 {
229     uint32_t phandle = qemu_devtree_get_phandle(fdt, target_node_path);
230     return qemu_devtree_setprop_cell(fdt, node_path, property, phandle);
231 }
232
233 uint32_t qemu_devtree_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 allocting phandles.
240      */
241     if (!phandle) {
242         QemuOpts *machine_opts;
243         machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
244         if (machine_opts) {
245             const char *phandle_start;
246             phandle_start = qemu_opt_get(machine_opts, "phandle_start");
247             if (phandle_start) {
248                 phandle = strtoul(phandle_start, NULL, 0);
249             }
250         }
251     }
252
253     if (!phandle) {
254         /*
255          * None or invalid phandle given on the command line, so fall back to
256          * default starting point.
257          */
258         phandle = 0x8000;
259     }
260
261     return phandle++;
262 }
263
264 int qemu_devtree_nop_node(void *fdt, const char *node_path)
265 {
266     int r;
267
268     r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
269     if (r < 0) {
270         fprintf(stderr, "%s: Couldn't nop node %s: %s\n", __func__, node_path,
271                 fdt_strerror(r));
272         exit(1);
273     }
274
275     return r;
276 }
277
278 int qemu_devtree_add_subnode(void *fdt, const char *name)
279 {
280     char *dupname = g_strdup(name);
281     char *basename = strrchr(dupname, '/');
282     int retval;
283     int parent = 0;
284
285     if (!basename) {
286         g_free(dupname);
287         return -1;
288     }
289
290     basename[0] = '\0';
291     basename++;
292
293     if (dupname[0]) {
294         parent = findnode_nofail(fdt, dupname);
295     }
296
297     retval = fdt_add_subnode(fdt, parent, basename);
298     if (retval < 0) {
299         fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name,
300                 fdt_strerror(retval));
301         exit(1);
302     }
303
304     g_free(dupname);
305     return retval;
306 }