Statistics
| Branch: | Revision:

root / device_tree.c @ 909a196d

History | View | Annotate | Download (7.9 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 "hw/loader.h"
25
#include "qemu/option.h"
26
#include "qemu/config-file.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
}
307

    
308
void qemu_devtree_dumpdtb(void *fdt, int size)
309
{
310
    QemuOpts *machine_opts;
311

    
312
    machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
313
    if (machine_opts) {
314
        const char *dumpdtb = qemu_opt_get(machine_opts, "dumpdtb");
315
        if (dumpdtb) {
316
            /* Dump the dtb to a file and quit */
317
            exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
318
        }
319
    }
320

    
321
}