source: trunk/packages/xen-3.1/xen-3.1/tools/libxc/powerpc64/flatdevtree.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 15.6 KB
Line 
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
15 *
16 * Copyright Pantelis Antoniou 2006
17 * Copyright IBM Corporation 2006, 2007
18 * 2006 (c) MontaVista, Software, Inc.
19 *
20 * Authors: Pantelis Antoniou <pantelis@embeddedalley.com>
21 *          Hollis Blanchard <hollisb@us.ibm.com>
22 *          Mark A. Greer <mgreer@mvista.com>
23 */
24
25#include "flatdevtree.h"
26
27/* Set ptrs to current one's info; return addr of next one */
28static u32 *ft_next(u32 *p, const u32 *p_strings, const u32 version,
29                u32 **tagpp, char **namepp, char **datapp, u32 **sizepp)
30{
31        u32 sz;
32
33        *namepp = NULL;
34        *datapp = NULL;
35        *sizepp = NULL;
36        *tagpp = p;
37
38        switch (be32_to_cpu(*p++)) { /* Tag */
39        case OF_DT_BEGIN_NODE:
40                *namepp = (char *)p;
41                p = (u32 *)_ALIGN((unsigned long)p + strlen((char *)p) + 1, 4);
42                break;
43        case OF_DT_PROP:
44                sz = be32_to_cpu(*p);
45                *sizepp = p++;
46                *namepp = (char *)p_strings + be32_to_cpu(*p++);
47                if ((version < 0x10) && (sz >= 8))
48                        p = (u32 *)_ALIGN((unsigned long)p, 8);
49                *datapp = (char *)p;
50                p = (u32 *)_ALIGN((unsigned long)p + sz, 4);
51                break;
52        case OF_DT_END_NODE:
53        case OF_DT_NOP:
54                break;
55        case OF_DT_END:
56        default:
57                p = NULL;
58                break;
59        }
60
61        return p;
62}
63
64static void ft_put_word(struct ft_cxt *cxt, u32 v)
65{
66        if (cxt->overflow)      /* do nothing */
67                return;
68
69        /* check for overflow */
70        if (cxt->p + 4 > cxt->pstr) {
71                cxt->overflow = 1;
72                return;
73        }
74
75        *(u32 *) cxt->p = cpu_to_be32(v);
76        cxt->p += 4;
77}
78
79static inline void ft_put_bin(struct ft_cxt *cxt, const void *data, int sz)
80{
81        char *p;
82
83        if (cxt->overflow)      /* do nothing */
84                return;
85
86        /* next pointer pos */
87        p = (char *) _ALIGN((unsigned long)cxt->p + sz, 4);
88
89        /* check for overflow */
90        if (p > cxt->pstr) {
91                cxt->overflow = 1;
92                return;
93        }
94
95        memcpy(cxt->p, data, sz);
96        if ((sz & 3) != 0)
97                memset(cxt->p + sz, 0, 4 - (sz & 3));
98        cxt->p = p;
99}
100
101void ft_begin_node(struct ft_cxt *cxt, const char *name)
102{
103        ft_put_word(cxt, OF_DT_BEGIN_NODE);
104        ft_put_bin(cxt, name, strlen(name) + 1);
105}
106
107void ft_end_node(struct ft_cxt *cxt)
108{
109        ft_put_word(cxt, OF_DT_END_NODE);
110}
111
112void ft_nop(struct ft_cxt *cxt)
113{
114        ft_put_word(cxt, OF_DT_NOP);
115}
116
117static int lookup_string(struct ft_cxt *cxt, const char *name)
118{
119        char *p;
120
121        p = cxt->pstr;
122        while (p < cxt->pstr_begin) {
123                if (strcmp(p, (char *)name) == 0)
124                        return p - cxt->p_begin;
125                p += strlen(p) + 1;
126        }
127
128        return -1;
129}
130
131void ft_prop(struct ft_cxt *cxt, const char *name,
132                const void *data, unsigned int sz)
133{
134        int len, off;
135
136        if (cxt->overflow)
137                return;
138
139        len = strlen(name) + 1;
140
141        off = lookup_string(cxt, name);
142        if (off == -1) {
143                /* check if we have space */
144                if (cxt->p + 12 + sz + len > cxt->pstr) {
145                        cxt->overflow = 1;
146                        return;
147                }
148
149                cxt->pstr -= len;
150                memcpy(cxt->pstr, name, len);
151                off = cxt->pstr - cxt->p_begin;
152        }
153
154        /* now put offset from beginning of *STRUCTURE* */
155        /* will be fixed up at the end */
156        ft_put_word(cxt, OF_DT_PROP);
157        ft_put_word(cxt, sz);
158        ft_put_word(cxt, off);
159        ft_put_bin(cxt, data, sz);
160}
161
162void ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str)
163{
164        ft_prop(cxt, name, str, strlen(str) + 1);
165}
166
167void ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val)
168{
169        u32 v = cpu_to_be32((u32) val);
170
171        ft_prop(cxt, name, &v, 4);
172}
173
174/* start construction of the flat OF tree */
175void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size)
176{
177        struct boot_param_header *bph = blob;
178        u32 off;
179
180        /* clear the cxt */
181        memset(cxt, 0, sizeof(*cxt));
182
183        cxt->bph = bph;
184        cxt->max_size = max_size;
185
186        /* zero everything in the header area */
187        memset(bph, 0, sizeof(*bph));
188
189        bph->magic = cpu_to_be32(OF_DT_HEADER);
190        bph->version = cpu_to_be32(0x10);
191        bph->last_comp_version = cpu_to_be32(0x10);
192
193        /* start pointers */
194        cxt->pres_begin = (char *) _ALIGN((unsigned long)(bph + 1), 8);
195        cxt->pres = cxt->pres_begin;
196
197        off = (unsigned long)cxt->pres_begin - (unsigned long)bph;
198        bph->off_mem_rsvmap = cpu_to_be32(off);
199
200        ((u64 *) cxt->pres)[0] = 0;     /* phys = 0, size = 0, terminate */
201        ((u64 *) cxt->pres)[1] = 0;
202
203        cxt->p_anchor = cxt->pres + 16; /* over the terminator */
204}
205
206/* add a reserver physical area to the rsvmap */
207void ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size)
208{
209        ((u64 *) cxt->pres)[0] = cpu_to_be64(physaddr); /* phys = 0, size = 0, terminate */
210        ((u64 *) cxt->pres)[1] = cpu_to_be64(size);
211
212        cxt->pres += 16;        /* advance two u64s worth */
213
214        ((u64 *) cxt->pres)[0] = 0;     /* phys = 0, size = 0, terminate */
215        ((u64 *) cxt->pres)[1] = 0;
216
217        /* keep track of size */
218        cxt->res_size = cxt->pres + 16 - cxt->pres_begin;
219
220        cxt->p_anchor = cxt->pres + 16; /* over the terminator */
221}
222
223int ft_set_rsvmap(void *bphp, int m, u64 physaddr, u64 size)
224{
225        const struct boot_param_header *bph = bphp;
226        u64 *p_rsvmap = (u64 *)
227                ((char *)bph + be32_to_cpu(bph->off_mem_rsvmap));
228        u32 i;
229
230        for (i = 0;; i++) {
231                u64 addr, sz;
232
233                addr = be64_to_cpu(p_rsvmap[i * 2]);
234                sz = be64_to_cpu(p_rsvmap[i * 2 + 1]);
235                if (addr == 0 && size == 0)
236                        break;
237                if (m == i) {
238                        p_rsvmap[i * 2] = cpu_to_be64(physaddr);
239                        p_rsvmap[i * 2 + 1] = cpu_to_be64(size);
240                        return 0;
241                }
242        }
243        return -1;
244}
245
246void ft_begin_tree(struct ft_cxt *cxt)
247{
248        cxt->p_begin = cxt->p_anchor;
249        cxt->pstr_begin = (char *)cxt->bph + cxt->max_size;     /* point at the end */
250
251        cxt->p = cxt->p_begin;
252        cxt->pstr = cxt->pstr_begin;
253}
254
255int ft_end_tree(struct ft_cxt *cxt)
256{
257        struct boot_param_header *bph = cxt->bph;
258        int off, sz, sz1;
259        u32 tag, v;
260        char *p;
261
262        ft_put_word(cxt, OF_DT_END);
263
264        if (cxt->overflow)
265                return -ENOMEM;
266
267        /* size of the areas */
268        cxt->struct_size = cxt->p - cxt->p_begin;
269        cxt->strings_size = cxt->pstr_begin - cxt->pstr;
270
271        /* the offset we must move */
272        off = (cxt->pstr_begin - cxt->p_begin) - cxt->strings_size;
273
274        /* the new strings start */
275        cxt->pstr_begin = cxt->p_begin + cxt->struct_size;
276
277        /* move the whole string area */
278        memmove(cxt->pstr_begin, cxt->pstr, cxt->strings_size);
279
280        /* now perform the fixup of the strings */
281        p = cxt->p_begin;
282        while ((tag = be32_to_cpu(*(u32 *) p)) != OF_DT_END) {
283                p += 4;
284
285                if (tag == OF_DT_BEGIN_NODE) {
286                        p = (char *) _ALIGN((unsigned long)p + strlen(p) + 1, 4);
287                        continue;
288                }
289
290                if (tag == OF_DT_END_NODE || tag == OF_DT_NOP)
291                        continue;
292
293                if (tag != OF_DT_PROP)
294                        return -EINVAL;
295
296                sz = be32_to_cpu(*(u32 *) p);
297                p += 4;
298
299                v = be32_to_cpu(*(u32 *) p);
300                v -= off;
301                *(u32 *) p = cpu_to_be32(v);    /* move down */
302                p += 4;
303
304                p = (char *) _ALIGN((unsigned long)p + sz, 4);
305        }
306
307        /* fix sizes */
308        p = (char *)cxt->bph;
309        sz = (cxt->pstr_begin + cxt->strings_size) - p;
310        sz1 = _ALIGN(sz, 16);   /* align at 16 bytes */
311        if (sz != sz1)
312                memset(p + sz, 0, sz1 - sz);
313        bph->totalsize = cpu_to_be32(sz1);
314        bph->off_dt_struct = cpu_to_be32(cxt->p_begin - p);
315        bph->off_dt_strings = cpu_to_be32(cxt->pstr_begin - p);
316
317        /* the new strings start */
318        cxt->pstr_begin = cxt->p_begin + cxt->struct_size;
319        cxt->pstr = cxt->pstr_begin + cxt->strings_size;
320
321        /* mark the size of string structure in bph */
322        bph->size_dt_strings = cxt->strings_size;
323
324        return 0;
325}
326
327/**********************************************************************/
328
329static inline int isprint(int c)
330{
331        return c >= 0x20 && c <= 0x7e;
332}
333
334static int is_printable_string(const void *data, int len)
335{
336        const char *s = data;
337        const char *ss;
338
339        /* zero length is not */
340        if (len == 0)
341                return 0;
342
343        /* must terminate with zero */
344        if (s[len - 1] != '\0')
345                return 0;
346
347        ss = s;
348        while (*s && isprint(*s))
349                s++;
350
351        /* not zero, or not done yet */
352        if (*s != '\0' || (s + 1 - ss) < len)
353                return 0;
354
355        return 1;
356}
357
358static void print_data(const void *data, int len)
359{
360        int i;
361        const char *s;
362
363        /* no data, don't print */
364        if (len == 0)
365                return;
366
367        if (is_printable_string(data, len)) {
368                printf(" = \"%s\"", (char *)data);
369                return;
370        }
371
372        switch (len) {
373        case 1:         /* byte */
374                printf(" = <0x%02x>", (*(char *) data) & 0xff);
375                break;
376        case 2:         /* half-word */
377                printf(" = <0x%04x>", be16_to_cpu(*(u16 *) data) & 0xffff);
378                break;
379        case 4:         /* word */
380                printf(" = <0x%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU);
381                break;
382        case 8:         /* double-word */
383                printf(" = <0x%16llx>", be64_to_cpu(*(u64 *) data));
384                break;
385        default:                /* anything else... hexdump */
386                printf(" = [");
387                for (i = 0, s = data; i < len; i++)
388                        printf("%02x%s", s[i], i < len - 1 ? " " : "");
389                printf("]");
390
391                break;
392        }
393}
394
395void ft_dump_blob(const void *bphp)
396{
397        const struct boot_param_header *bph = bphp;
398        const u64 *p_rsvmap = (const u64 *)
399                ((const char *)bph + be32_to_cpu(bph->off_mem_rsvmap));
400        const u32 *p_struct = (const u32 *)
401                ((const char *)bph + be32_to_cpu(bph->off_dt_struct));
402        const u32 *p_strings = (const u32 *)
403                ((const char *)bph + be32_to_cpu(bph->off_dt_strings));
404        const u32 version = be32_to_cpu(bph->version);
405        u32 i, *p, *tagp, *sizep;
406        char *namep, *datap;
407        int depth, shift;
408        u64 addr, size;
409
410
411        if (be32_to_cpu(bph->magic) != OF_DT_HEADER) {
412                /* not valid tree */
413                return;
414        }
415
416        depth = 0;
417        shift = 4;
418
419        for (i = 0;; i++) {
420                addr = be64_to_cpu(p_rsvmap[i * 2]);
421                size = be64_to_cpu(p_rsvmap[i * 2 + 1]);
422                if (addr == 0 && size == 0)
423                        break;
424
425                printf("/memreserve/ 0x%llx 0x%llx;\n", addr, size);
426        }
427
428        p = (u32 *)p_struct;
429        while ((p = ft_next(p, p_strings, version, &tagp, &namep, &datap,
430                                        &sizep)) != NULL)
431                switch (be32_to_cpu(*tagp)) {
432                case OF_DT_BEGIN_NODE:
433                        printf("%*s%s {\n", depth * shift, "", namep);
434                        depth++;
435                        break;
436                case OF_DT_END_NODE:
437                        depth--;
438                        printf("%*s};\n", depth * shift, "");
439                        break;
440                case OF_DT_NOP:
441                        printf("%*s[NOP]\n", depth * shift, "");
442                        break;
443                case OF_DT_END:
444                        break;
445                case OF_DT_PROP:
446                        printf("%*s%s", depth * shift, "", namep);
447                        print_data(datap, *sizep);
448                        printf(";\n");
449                        break;
450                default:
451                        fprintf(stderr, "%*s ** Unknown tag 0x%08x\n",
452                                depth * shift, "", *tagp);
453                        return;
454                }
455}
456
457void ft_backtrack_node(struct ft_cxt *cxt)
458{
459        if (be32_to_cpu(*(u32 *) (cxt->p - 4)) != OF_DT_END_NODE)
460                return;         /* XXX only for node */
461
462        cxt->p -= 4;
463}
464
465/* note that the root node of the blob is "peeled" off */
466void ft_merge_blob(struct ft_cxt *cxt, void *blob)
467{
468        struct boot_param_header *bph = (struct boot_param_header *)blob;
469        u32 *p_struct = (u32 *) ((char *)bph + be32_to_cpu(bph->off_dt_struct));
470        u32 *p_strings =
471                (u32 *) ((char *)bph + be32_to_cpu(bph->off_dt_strings));
472        const u32 version = be32_to_cpu(bph->version);
473        u32 *p, *tagp, *sizep;
474        char *namep, *datap;
475        int depth;
476
477        if (be32_to_cpu(*(u32 *) (cxt->p - 4)) != OF_DT_END_NODE)
478                return;         /* XXX only for node */
479
480        cxt->p -= 4;
481
482        depth = 0;
483        p = p_struct;
484        while ((p = ft_next(p, p_strings, version, &tagp, &namep, &datap,
485                                        &sizep)) != NULL)
486                switch (be32_to_cpu(*tagp)) {
487                case OF_DT_BEGIN_NODE:
488                        if (depth++ > 0)
489                                ft_begin_node(cxt, namep);
490                        break;
491                case OF_DT_END_NODE:
492                        ft_end_node(cxt);
493                        if (--depth == 0)
494                                return;
495                        break;
496                case OF_DT_PROP:
497                        ft_prop(cxt, namep, datap, *sizep);
498                        break;
499                }
500}
501
502/**********************************************************************/
503
504void *ft_find_node(const void *bphp, const char *srch_path)
505{
506        const struct boot_param_header *bph = bphp;
507        u32 *p_struct = (u32 *)((char *)bph + be32_to_cpu(bph->off_dt_struct));
508        u32 *p_strings= (u32 *)((char *)bph + be32_to_cpu(bph->off_dt_strings));
509        u32 version = be32_to_cpu(bph->version);
510        u32 *p, *tagp, *sizep;
511        char *namep, *datap;
512        static char path[MAX_PATH_LEN];
513
514        path[0] = '\0';
515        p = p_struct;
516
517        while ((p = ft_next(p, p_strings, version, &tagp, &namep, &datap,
518                                        &sizep)) != NULL)
519                switch (be32_to_cpu(*tagp)) {
520                case OF_DT_BEGIN_NODE:
521                        strcat(path, namep);
522                        if (!strcmp(path, srch_path))
523                                return tagp;
524                        strcat(path, "/");
525                        break;
526                case OF_DT_END_NODE:
527                        ft_parentize(path, 1);
528                        break;
529                }
530        return NULL;
531}
532
533int ft_get_prop(const void *bphp, const void *node, const char *propname,
534                void *buf, const unsigned int buflen)
535{
536        const struct boot_param_header *bph = bphp;
537        u32 *p_strings= (u32 *)((char *)bph + be32_to_cpu(bph->off_dt_strings));
538        u32 version = be32_to_cpu(bph->version);
539        u32 *p, *tagp, *sizep, size;
540        char *namep, *datap;
541        int depth;
542
543        depth = 0;
544        p = (u32 *)node;
545
546        while ((p = ft_next(p, p_strings, version, &tagp, &namep, &datap,
547                                        &sizep)) != NULL)
548                switch (be32_to_cpu(*tagp)) {
549                case OF_DT_BEGIN_NODE:
550                        depth++;
551                        break;
552                case OF_DT_PROP:
553                        if ((depth == 1) && !strcmp(namep, propname)) {
554                                size = min(be32_to_cpu(*sizep), (u32)buflen);
555                                memcpy(buf, datap, size);
556                                return size;
557                        }
558                        break;
559                case OF_DT_END_NODE:
560                        if (--depth <= 0)
561                                return -1;
562                        break;
563                }
564        return -1;
565}
566
567static void ft_modify_prop(void **bphpp, char *datap, u32 *old_prop_sizep,
568                const char *buf, const unsigned int buflen)
569{
570        u32 old_prop_data_len, new_prop_data_len;
571
572        old_prop_data_len = _ALIGN(be32_to_cpu(*old_prop_sizep), 4);
573        new_prop_data_len = _ALIGN(buflen, 4);
574
575        /* Check if new prop data fits in old prop data area */
576        if (new_prop_data_len == old_prop_data_len) {
577                memcpy(datap, buf, buflen);
578                *old_prop_sizep = cpu_to_be32(buflen);
579        } else {
580                /* Need to alloc new area to put larger or smaller ft */
581                struct boot_param_header *old_bph = *bphpp, *new_bph;
582                u32 *old_tailp, *new_tailp, *new_datap;
583                u32 old_total_size, new_total_size, head_len, tail_len, diff, v;
584
585                old_total_size = be32_to_cpu(old_bph->totalsize);
586                head_len = (u32)(datap - (char *)old_bph);
587                tail_len = old_total_size - (head_len + old_prop_data_len);
588                old_tailp = (u32 *)(datap + old_prop_data_len);
589                new_total_size = head_len + new_prop_data_len + tail_len;
590
591                if (!(new_bph = malloc(new_total_size))) {
592                        printf("Can't alloc space for new ft\n");
593                        ft_exit(-ENOSPC);
594                }
595
596                new_datap = (u32 *)((char *)new_bph + head_len);
597                new_tailp = (u32 *)((char *)new_datap + new_prop_data_len);
598
599                memcpy(new_bph, *bphpp, head_len);
600                memcpy(new_datap, buf, buflen);
601                memcpy(new_tailp, old_tailp, tail_len);
602
603                *(new_datap - 2) = cpu_to_be32(buflen); /* Set prop size */
604
605                new_bph->totalsize = cpu_to_be32(new_total_size);
606                diff = new_prop_data_len - old_prop_data_len;
607
608                if (be32_to_cpu(old_bph->off_dt_strings)
609                                > be32_to_cpu(old_bph->off_dt_struct)) {
610                        v = be32_to_cpu(new_bph->off_dt_strings);
611                        new_bph->off_dt_strings = cpu_to_be32(v + diff);
612                }
613
614                if (be32_to_cpu(old_bph->off_mem_rsvmap)
615                                > be32_to_cpu(old_bph->off_dt_struct)) {
616                        v = be32_to_cpu(new_bph->off_mem_rsvmap);
617                        new_bph->off_mem_rsvmap = cpu_to_be32(v + diff);
618                }
619
620                ft_free(*bphpp, old_total_size);
621                *bphpp = new_bph;
622        }
623}
624
625/*
626 * - Only modifies existing properties.
627 * - The dev tree passed in may be freed and a new one allocated
628 *   (and *bphpp set to location of new dev tree).
629 */
630int ft_set_prop(void **bphpp, const void *node, const char *propname,
631                const void *buf, const unsigned int buflen)
632{
633        struct boot_param_header *bph = *bphpp;
634        u32 *p_strings= (u32 *)((char *)bph + be32_to_cpu(bph->off_dt_strings));
635        u32 version = be32_to_cpu(bph->version);
636        u32 *p, *tagp, *sizep;
637        char *namep, *datap;
638        int depth;
639
640        depth = 0;
641        p = (u32 *)node;
642
643        while ((p = ft_next(p, p_strings, version, &tagp, &namep, &datap,
644                                        &sizep)) != NULL)
645                switch (be32_to_cpu(*tagp)) {
646                case OF_DT_BEGIN_NODE:
647                        depth++;
648                        break;
649                case OF_DT_PROP:
650                        if ((depth == 1) && !strcmp(namep, propname)) {
651                                ft_modify_prop(bphpp, datap, sizep, buf,
652                                                buflen);
653                                return be32_to_cpu(*sizep);
654                        }
655                        break;
656                case OF_DT_END_NODE:
657                        if (--depth <= 0)
658                                return -1;
659                        break;
660                }
661        return -1;
662}
Note: See TracBrowser for help on using the repository browser.