Statistics
| Branch: | Revision:

root / device_tree.c @ feature-archipelago

History | View | Annotate | Download (8.5 kB)

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