Statistics
| Branch: | Revision:

root / device_tree.c @ be5907f2

History | View | Annotate | Download (8.5 kB)

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 "sysemu/device_tree.h"
24
#include "sysemu/sysemu.h"
25
#include "hw/loader.h"
26
#include "qemu/option.h"
27
#include "qemu/config-file.h"
28

    
29
#include <libfdt.h>
30

    
31
#define FDT_MAX_SIZE  0x10000
32

    
33
void *create_device_tree(int *sizep)
34
{
35
    void *fdt;
36
    int ret;
37

    
38
    *sizep = FDT_MAX_SIZE;
39
    fdt = g_malloc0(FDT_MAX_SIZE);
40
    ret = fdt_create(fdt, FDT_MAX_SIZE);
41
    if (ret < 0) {
42
        goto fail;
43
    }
44
    ret = fdt_finish_reservemap(fdt);
45
    if (ret < 0) {
46
        goto fail;
47
    }
48
    ret = fdt_begin_node(fdt, "");
49
    if (ret < 0) {
50
        goto fail;
51
    }
52
    ret = fdt_end_node(fdt);
53
    if (ret < 0) {
54
        goto fail;
55
    }
56
    ret = fdt_finish(fdt);
57
    if (ret < 0) {
58
        goto fail;
59
    }
60
    ret = fdt_open_into(fdt, fdt, *sizep);
61
    if (ret) {
62
        fprintf(stderr, "Unable to copy device tree in memory\n");
63
        exit(1);
64
    }
65

    
66
    return fdt;
67
fail:
68
    fprintf(stderr, "%s Couldn't create dt: %s\n", __func__, fdt_strerror(ret));
69
    exit(1);
70
}
71

    
72
void *load_device_tree(const char *filename_path, int *sizep)
73
{
74
    int dt_size;
75
    int dt_file_load_size;
76
    int ret;
77
    void *fdt = NULL;
78

    
79
    *sizep = 0;
80
    dt_size = get_image_size(filename_path);
81
    if (dt_size < 0) {
82
        printf("Unable to get size of device tree file '%s'\n",
83
            filename_path);
84
        goto fail;
85
    }
86

    
87
    /* Expand to 2x size to give enough room for manipulation.  */
88
    dt_size += 10000;
89
    dt_size *= 2;
90
    /* First allocate space in qemu for device tree */
91
    fdt = g_malloc0(dt_size);
92

    
93
    dt_file_load_size = load_image(filename_path, fdt);
94
    if (dt_file_load_size < 0) {
95
        printf("Unable to open device tree file '%s'\n",
96
               filename_path);
97
        goto fail;
98
    }
99

    
100
    ret = fdt_open_into(fdt, fdt, dt_size);
101
    if (ret) {
102
        printf("Unable to copy device tree in memory\n");
103
        goto fail;
104
    }
105

    
106
    /* Check sanity of device tree */
107
    if (fdt_check_header(fdt)) {
108
        printf ("Device tree file loaded into memory is invalid: %s\n",
109
            filename_path);
110
        goto fail;
111
    }
112
    *sizep = dt_size;
113
    return fdt;
114

    
115
fail:
116
    g_free(fdt);
117
    return NULL;
118
}
119

    
120
static int findnode_nofail(void *fdt, const char *node_path)
121
{
122
    int offset;
123

    
124
    offset = fdt_path_offset(fdt, node_path);
125
    if (offset < 0) {
126
        fprintf(stderr, "%s Couldn't find node %s: %s\n", __func__, node_path,
127
                fdt_strerror(offset));
128
        exit(1);
129
    }
130

    
131
    return offset;
132
}
133

    
134
int qemu_fdt_setprop(void *fdt, const char *node_path,
135
                     const char *property, const void *val, int size)
136
{
137
    int r;
138

    
139
    r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
140
    if (r < 0) {
141
        fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path,
142
                property, fdt_strerror(r));
143
        exit(1);
144
    }
145

    
146
    return r;
147
}
148

    
149
int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
150
                          const char *property, uint32_t val)
151
{
152
    int r;
153

    
154
    r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
155
    if (r < 0) {
156
        fprintf(stderr, "%s: Couldn't set %s/%s = %#08x: %s\n", __func__,
157
                node_path, property, val, fdt_strerror(r));
158
        exit(1);
159
    }
160

    
161
    return r;
162
}
163

    
164
int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
165
                         const char *property, uint64_t val)
166
{
167
    val = cpu_to_be64(val);
168
    return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
169
}
170

    
171
int qemu_fdt_setprop_string(void *fdt, const char *node_path,
172
                            const char *property, const char *string)
173
{
174
    int r;
175

    
176
    r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
177
    if (r < 0) {
178
        fprintf(stderr, "%s: Couldn't set %s/%s = %s: %s\n", __func__,
179
                node_path, property, string, fdt_strerror(r));
180
        exit(1);
181
    }
182

    
183
    return r;
184
}
185

    
186
const void *qemu_fdt_getprop(void *fdt, const char *node_path,
187
                             const char *property, int *lenp)
188
{
189
    int len;
190
    const void *r;
191
    if (!lenp) {
192
        lenp = &len;
193
    }
194
    r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
195
    if (!r) {
196
        fprintf(stderr, "%s: Couldn't get %s/%s: %s\n", __func__,
197
                node_path, property, fdt_strerror(*lenp));
198
        exit(1);
199
    }
200
    return r;
201
}
202

    
203
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
204
                               const char *property)
205
{
206
    int len;
207
    const uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len);
208
    if (len != 4) {
209
        fprintf(stderr, "%s: %s/%s not 4 bytes long (not a cell?)\n",
210
                __func__, node_path, property);
211
        exit(1);
212
    }
213
    return be32_to_cpu(*p);
214
}
215

    
216
uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
217
{
218
    uint32_t r;
219

    
220
    r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
221
    if (r == 0) {
222
        fprintf(stderr, "%s: Couldn't get phandle for %s: %s\n", __func__,
223
                path, fdt_strerror(r));
224
        exit(1);
225
    }
226

    
227
    return r;
228
}
229

    
230
int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
231
                             const char *property,
232
                             const char *target_node_path)
233
{
234
    uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
235
    return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
236
}
237

    
238
uint32_t qemu_fdt_alloc_phandle(void *fdt)
239
{
240
    static int phandle = 0x0;
241

    
242
    /*
243
     * We need to find out if the user gave us special instruction at
244
     * which phandle id to start allocting phandles.
245
     */
246
    if (!phandle) {
247
        phandle = qemu_opt_get_number(qemu_get_machine_opts(),
248
                                      "phandle_start", 0);
249
    }
250

    
251
    if (!phandle) {
252
        /*
253
         * None or invalid phandle given on the command line, so fall back to
254
         * default starting point.
255
         */
256
        phandle = 0x8000;
257
    }
258

    
259
    return phandle++;
260
}
261

    
262
int qemu_fdt_nop_node(void *fdt, const char *node_path)
263
{
264
    int r;
265

    
266
    r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
267
    if (r < 0) {
268
        fprintf(stderr, "%s: Couldn't nop node %s: %s\n", __func__, node_path,
269
                fdt_strerror(r));
270
        exit(1);
271
    }
272

    
273
    return r;
274
}
275

    
276
int qemu_fdt_add_subnode(void *fdt, const char *name)
277
{
278
    char *dupname = g_strdup(name);
279
    char *basename = strrchr(dupname, '/');
280
    int retval;
281
    int parent = 0;
282

    
283
    if (!basename) {
284
        g_free(dupname);
285
        return -1;
286
    }
287

    
288
    basename[0] = '\0';
289
    basename++;
290

    
291
    if (dupname[0]) {
292
        parent = findnode_nofail(fdt, dupname);
293
    }
294

    
295
    retval = fdt_add_subnode(fdt, parent, basename);
296
    if (retval < 0) {
297
        fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name,
298
                fdt_strerror(retval));
299
        exit(1);
300
    }
301

    
302
    g_free(dupname);
303
    return retval;
304
}
305

    
306
void qemu_fdt_dumpdtb(void *fdt, int size)
307
{
308
    const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
309

    
310
    if (dumpdtb) {
311
        /* Dump the dtb to a file and quit */
312
        exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
313
    }
314
}
315

    
316
int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
317
                                            const char *node_path,
318
                                            const char *property,
319
                                            int numvalues,
320
                                            uint64_t *values)
321
{
322
    uint32_t *propcells;
323
    uint64_t value;
324
    int cellnum, vnum, ncells;
325
    uint32_t hival;
326

    
327
    propcells = g_new0(uint32_t, numvalues * 2);
328

    
329
    cellnum = 0;
330
    for (vnum = 0; vnum < numvalues; vnum++) {
331
        ncells = values[vnum * 2];
332
        if (ncells != 1 && ncells != 2) {
333
            return -1;
334
        }
335
        value = values[vnum * 2 + 1];
336
        hival = cpu_to_be32(value >> 32);
337
        if (ncells > 1) {
338
            propcells[cellnum++] = hival;
339
        } else if (hival != 0) {
340
            return -1;
341
        }
342
        propcells[cellnum++] = cpu_to_be32(value);
343
    }
344

    
345
    return qemu_fdt_setprop(fdt, node_path, property, propcells,
346
                            cellnum * sizeof(uint32_t));
347
}