Statistics
| Branch: | Revision:

root / device_tree.c @ 97c38f8c

History | View | Annotate | Download (8.6 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_begin_node(fdt, "");
45
    if (ret < 0) {
46
        goto fail;
47
    }
48
    ret = fdt_end_node(fdt);
49
    if (ret < 0) {
50
        goto fail;
51
    }
52
    ret = fdt_finish(fdt);
53
    if (ret < 0) {
54
        goto fail;
55
    }
56
    ret = fdt_open_into(fdt, fdt, *sizep);
57
    if (ret) {
58
        fprintf(stderr, "Unable to copy device tree in memory\n");
59
        exit(1);
60
    }
61

    
62
    return fdt;
63
fail:
64
    fprintf(stderr, "%s Couldn't create dt: %s\n", __func__, fdt_strerror(ret));
65
    exit(1);
66
}
67

    
68
void *load_device_tree(const char *filename_path, int *sizep)
69
{
70
    int dt_size;
71
    int dt_file_load_size;
72
    int ret;
73
    void *fdt = NULL;
74

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

    
83
    /* Expand to 2x size to give enough room for manipulation.  */
84
    dt_size += 10000;
85
    dt_size *= 2;
86
    /* First allocate space in qemu for device tree */
87
    fdt = g_malloc0(dt_size);
88

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

    
96
    ret = fdt_open_into(fdt, fdt, dt_size);
97
    if (ret) {
98
        printf("Unable to copy device tree in memory\n");
99
        goto fail;
100
    }
101

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

    
111
fail:
112
    g_free(fdt);
113
    return NULL;
114
}
115

    
116
static int findnode_nofail(void *fdt, const char *node_path)
117
{
118
    int offset;
119

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

    
127
    return offset;
128
}
129

    
130
int qemu_devtree_setprop(void *fdt, const char *node_path,
131
                         const char *property, const void *val_array, int size)
132
{
133
    int r;
134

    
135
    r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val_array, size);
136
    if (r < 0) {
137
        fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path,
138
                property, fdt_strerror(r));
139
        exit(1);
140
    }
141

    
142
    return r;
143
}
144

    
145
int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
146
                              const char *property, uint32_t val)
147
{
148
    int r;
149

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

    
157
    return r;
158
}
159

    
160
int qemu_devtree_setprop_u64(void *fdt, const char *node_path,
161
                             const char *property, uint64_t val)
162
{
163
    val = cpu_to_be64(val);
164
    return qemu_devtree_setprop(fdt, node_path, property, &val, sizeof(val));
165
}
166

    
167
int qemu_devtree_setprop_string(void *fdt, const char *node_path,
168
                                const char *property, const char *string)
169
{
170
    int r;
171

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

    
179
    return r;
180
}
181

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

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

    
212
uint32_t qemu_devtree_get_phandle(void *fdt, const char *path)
213
{
214
    uint32_t r;
215

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

    
223
    return r;
224
}
225

    
226
int qemu_devtree_setprop_phandle(void *fdt, const char *node_path,
227
                                 const char *property,
228
                                 const char *target_node_path)
229
{
230
    uint32_t phandle = qemu_devtree_get_phandle(fdt, target_node_path);
231
    return qemu_devtree_setprop_cell(fdt, node_path, property, phandle);
232
}
233

    
234
uint32_t qemu_devtree_alloc_phandle(void *fdt)
235
{
236
    static int phandle = 0x0;
237

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

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

    
255
    return phandle++;
256
}
257

    
258
int qemu_devtree_nop_node(void *fdt, const char *node_path)
259
{
260
    int r;
261

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

    
269
    return r;
270
}
271

    
272
int qemu_devtree_add_subnode(void *fdt, const char *name)
273
{
274
    char *dupname = g_strdup(name);
275
    char *basename = strrchr(dupname, '/');
276
    int retval;
277
    int parent = 0;
278

    
279
    if (!basename) {
280
        g_free(dupname);
281
        return -1;
282
    }
283

    
284
    basename[0] = '\0';
285
    basename++;
286

    
287
    if (dupname[0]) {
288
        parent = findnode_nofail(fdt, dupname);
289
    }
290

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

    
298
    g_free(dupname);
299
    return retval;
300
}
301

    
302
void qemu_devtree_dumpdtb(void *fdt, int size)
303
{
304
    const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
305

    
306
    if (dumpdtb) {
307
        /* Dump the dtb to a file and quit */
308
        exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
309
    }
310
}
311

    
312
int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
313
                                                const char *node_path,
314
                                                const char *property,
315
                                                int numvalues,
316
                                                uint64_t *values)
317
{
318
    uint32_t *propcells;
319
    uint64_t value;
320
    int cellnum, vnum, ncells;
321
    uint32_t hival;
322

    
323
    propcells = g_new0(uint32_t, numvalues * 2);
324

    
325
    cellnum = 0;
326
    for (vnum = 0; vnum < numvalues; vnum++) {
327
        ncells = values[vnum * 2];
328
        if (ncells != 1 && ncells != 2) {
329
            return -1;
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
            return -1;
337
        }
338
        propcells[cellnum++] = cpu_to_be32(value);
339
    }
340

    
341
    return qemu_devtree_setprop(fdt, node_path, property, propcells,
342
                                cellnum * sizeof(uint32_t));
343
}