Synchronize local copy of DTC with Linux 6.17 . This includes the
following picked and squashed commits from Linux kernel. The squash
was necessary, since the DTC here contains changes which were also
part of DTC in Linux alraedy, and the squash helped resolve those
without going back and forth with the changes.
The following commits from Linux are picked:
8f324cd712df7 # scripts/dtc: consolidate include path options in Makefile
b5b3d9b63b0ee # scripts/dtc: Add yamltree.c to dtc sources
7d97a76f226d6 # scripts/dtc: Update to upstream version v1.4.7-14-gc86da84d30e4
ea6f243be74e5 # scripts/dtc: Update to upstream version v1.4.7-57-gf267e674d145
02d435d4eccd8 # scripts/dtc: Update to upstream version v1.5.0-23-g87963ee20693
6e321b7637396 # scripts/dtc: Update to upstream version v1.5.0-30-g702c1b6c0e73
9f19ec91a7a35 # scripts/dtc: dtx_diff - add color output support
8287d642f38d1 # scripts/dtc: Update to upstream version v1.5.1-22-gc40aeb60b47a
4c52deef9225d # scripts/dtc: Revert "yamltree: Ensure consistent bracketing of properties with phandles"
5d3827e1452ed # scripts/dtc: Remove unused makefile fragments
40dd266887654 # scripts/dtc: Update to upstream version v1.6.0-2-g87a656ae5ff9
8d4cf6b6acb59 # scripts/dtc: use pkg-config to include <yaml.h> in non-standard path
b9bf9ace5ae90 # scripts/dtc: Update to upstream version v1.6.0-11-g9d7888cbf19c
69a883b6f5ac0 # scripts/dtc: dtx_diff - make help text formatting consistent
8f829108b8aed # scripts/dtc: only append to HOST_EXTRACFLAGS instead of overwriting
b39b4342ac495 # scripts/dtc: Update to upstream version v1.6.0-31-gcbca977ea121
93c6424c486b3 # scripts: dtc: Fetch fdtoverlay.c from external DTC project
0dd574a1d75c3 # scripts/dtc: Update to upstream version v1.6.0-51-g183df9e9c2b9
ec38b5df8a231 # scripts: dtc: Build fdtoverlay tool
a0c8c431411f5 # scripts: dtc: Remove the unused fdtdump.c file
e7dc653d4e890 # scripts/dtc: Add missing fdtoverlay to gitignore
d2bf5d2e3f09c # scripts/dtc: Update to upstream version v1.6.1-19-g0a3a9d3449c8
a60878f5532d0 # scripts/dtc: dtx_diff: remove broken example from help text
8b739d8658a9b # scripts/dtc: Call pkg-config POSIXly correct
b6eeafa67df00 # scripts/dtc: Update to upstream version v1.6.1-63-g55778a03df61
f96cc4c787588 # scripts/dtc: Update to upstream version v1.6.1-66-gabbd523bae6e
09ab9c092ef2b # scripts/dtc: Update to upstream version v1.7.0-93-g1df7b047fe43
ded8a5a498f2d # scripts/dtc: Update to upstream version v1.7.0-95-gbcd02b523429
ee6ff6fca7e71 # scripts/dtc: Update to upstream version v1.7.2-35-g52f07dcca47c
This also includes forward port of U-Boot commit
e8c2d25845 ("libfdt: Revert 6dcb8ba4 from upstream libfdt")
to avoid binary size growth.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
386 lines
8.0 KiB
C
386 lines
8.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
|
*/
|
|
|
|
#include "dtc.h"
|
|
#include "srcpos.h"
|
|
|
|
extern FILE *yyin;
|
|
extern int yyparse(void);
|
|
extern YYLTYPE yylloc;
|
|
|
|
struct dt_info *parser_output;
|
|
bool treesource_error;
|
|
|
|
struct dt_info *dt_from_source(const char *fname)
|
|
{
|
|
parser_output = NULL;
|
|
treesource_error = false;
|
|
|
|
srcfile_push(fname);
|
|
yyin = current_srcfile->f;
|
|
yylloc.file = current_srcfile;
|
|
|
|
if (yyparse() != 0)
|
|
die("Unable to parse input tree\n");
|
|
|
|
if (treesource_error)
|
|
die("Syntax error parsing input tree\n");
|
|
|
|
return parser_output;
|
|
}
|
|
|
|
static void write_prefix(FILE *f, int level)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < level; i++)
|
|
fputc('\t', f);
|
|
}
|
|
|
|
static bool isstring(char c)
|
|
{
|
|
return (isprint((unsigned char)c)
|
|
|| (c == '\0')
|
|
|| strchr("\a\b\t\n\v\f\r", c));
|
|
}
|
|
|
|
static void write_propval_string(FILE *f, const char *s, size_t len)
|
|
{
|
|
const char *end = s + len - 1;
|
|
|
|
if (!len)
|
|
return;
|
|
|
|
assert(*end == '\0');
|
|
|
|
fprintf(f, "\"");
|
|
while (s < end) {
|
|
char c = *s++;
|
|
switch (c) {
|
|
case '\a':
|
|
fprintf(f, "\\a");
|
|
break;
|
|
case '\b':
|
|
fprintf(f, "\\b");
|
|
break;
|
|
case '\t':
|
|
fprintf(f, "\\t");
|
|
break;
|
|
case '\n':
|
|
fprintf(f, "\\n");
|
|
break;
|
|
case '\v':
|
|
fprintf(f, "\\v");
|
|
break;
|
|
case '\f':
|
|
fprintf(f, "\\f");
|
|
break;
|
|
case '\r':
|
|
fprintf(f, "\\r");
|
|
break;
|
|
case '\\':
|
|
fprintf(f, "\\\\");
|
|
break;
|
|
case '\"':
|
|
fprintf(f, "\\\"");
|
|
break;
|
|
case '\0':
|
|
fprintf(f, "\\0");
|
|
break;
|
|
default:
|
|
if (isprint((unsigned char)c))
|
|
fprintf(f, "%c", c);
|
|
else
|
|
fprintf(f, "\\x%02"PRIx8, c);
|
|
}
|
|
}
|
|
fprintf(f, "\"");
|
|
}
|
|
|
|
static void write_propval_int(FILE *f, const char *p, size_t len, size_t width)
|
|
{
|
|
const char *end = p + len;
|
|
assert(len % width == 0);
|
|
|
|
for (; p < end; p += width) {
|
|
switch (width) {
|
|
case 1:
|
|
fprintf(f, "%02"PRIx8, *(const uint8_t*)p);
|
|
break;
|
|
case 2:
|
|
fprintf(f, "0x%02"PRIx16, dtb_ld16(p));
|
|
break;
|
|
case 4:
|
|
fprintf(f, "0x%02"PRIx32, dtb_ld32(p));
|
|
break;
|
|
case 8:
|
|
fprintf(f, "0x%02"PRIx64, dtb_ld64(p));
|
|
break;
|
|
}
|
|
if (p + width < end)
|
|
fputc(' ', f);
|
|
}
|
|
}
|
|
|
|
static const char *delim_start[] = {
|
|
[TYPE_UINT8] = "[",
|
|
[TYPE_UINT16] = "/bits/ 16 <",
|
|
[TYPE_UINT32] = "<",
|
|
[TYPE_UINT64] = "/bits/ 64 <",
|
|
[TYPE_STRING] = "",
|
|
};
|
|
static const char *delim_end[] = {
|
|
[TYPE_UINT8] = "]",
|
|
[TYPE_UINT16] = ">",
|
|
[TYPE_UINT32] = ">",
|
|
[TYPE_UINT64] = ">",
|
|
[TYPE_STRING] = "",
|
|
};
|
|
|
|
/*
|
|
* The invariants in the marker list are:
|
|
* - offsets are non-strictly monotonically increasing
|
|
* - for a single offset there is at most one type marker
|
|
* - for a single offset that has both a type marker and non-type markers, the
|
|
* type marker appears before the others.
|
|
*/
|
|
static struct marker **add_marker(struct marker **mi,
|
|
enum markertype type, unsigned int offset, char *ref)
|
|
{
|
|
struct marker *nm;
|
|
|
|
while (*mi && (*mi)->offset < offset)
|
|
mi = &(*mi)->next;
|
|
|
|
if (*mi && (*mi)->offset == offset && is_type_marker((*mi)->type)) {
|
|
if (is_type_marker(type))
|
|
return mi;
|
|
mi = &(*mi)->next;
|
|
}
|
|
|
|
if (*mi && (*mi)->offset == offset && type == (*mi)->type)
|
|
return mi;
|
|
|
|
nm = xmalloc(sizeof(*nm));
|
|
nm->type = type;
|
|
nm->offset = offset;
|
|
nm->ref = ref;
|
|
nm->next = *mi;
|
|
*mi = nm;
|
|
|
|
return &nm->next;
|
|
}
|
|
|
|
static void add_string_markers(struct property *prop)
|
|
{
|
|
int l, len = prop->val.len;
|
|
const char *p = prop->val.val;
|
|
struct marker **mi = &prop->val.markers;
|
|
|
|
for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1)
|
|
mi = add_marker(mi, TYPE_STRING, l, NULL);
|
|
}
|
|
|
|
static enum markertype guess_value_type(struct property *prop)
|
|
{
|
|
int len = prop->val.len;
|
|
const char *p = prop->val.val;
|
|
struct marker *m = prop->val.markers;
|
|
int nnotstring = 0, nnul = 0;
|
|
int nnotstringlbl = 0, nnotcelllbl = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (! isstring(p[i]))
|
|
nnotstring++;
|
|
if (p[i] == '\0')
|
|
nnul++;
|
|
}
|
|
|
|
for_each_marker_of_type(m, LABEL) {
|
|
if ((m->offset > 0) && (prop->val.val[m->offset - 1] != '\0'))
|
|
nnotstringlbl++;
|
|
if ((m->offset % sizeof(cell_t)) != 0)
|
|
nnotcelllbl++;
|
|
}
|
|
|
|
if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul <= (len-nnul))
|
|
&& (nnotstringlbl == 0)) {
|
|
if (nnul > 1)
|
|
add_string_markers(prop);
|
|
return TYPE_STRING;
|
|
} else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) {
|
|
return TYPE_UINT32;
|
|
}
|
|
|
|
return TYPE_UINT8;
|
|
}
|
|
|
|
static void write_propval(FILE *f, struct property *prop)
|
|
{
|
|
size_t len = prop->val.len;
|
|
struct marker *m = prop->val.markers;
|
|
struct marker dummy_marker;
|
|
enum markertype emit_type = TYPE_NONE;
|
|
char *srcstr;
|
|
|
|
if (len == 0) {
|
|
fprintf(f, ";");
|
|
if (annotate) {
|
|
srcstr = srcpos_string_first(prop->srcpos, annotate);
|
|
if (srcstr) {
|
|
fprintf(f, " /* %s */", srcstr);
|
|
free(srcstr);
|
|
}
|
|
}
|
|
fprintf(f, "\n");
|
|
return;
|
|
}
|
|
|
|
fprintf(f, " =");
|
|
|
|
if (!next_type_marker(m)) {
|
|
/* data type information missing, need to guess */
|
|
dummy_marker.type = guess_value_type(prop);
|
|
dummy_marker.next = prop->val.markers;
|
|
dummy_marker.offset = 0;
|
|
dummy_marker.ref = NULL;
|
|
m = &dummy_marker;
|
|
}
|
|
|
|
for_each_marker(m) {
|
|
size_t chunk_len = (m->next ? m->next->offset : len) - m->offset;
|
|
size_t data_len = type_marker_length(m) ? : len - m->offset;
|
|
const char *p = &prop->val.val[m->offset];
|
|
struct marker *m_phandle;
|
|
|
|
if (is_type_marker(m->type)) {
|
|
emit_type = m->type;
|
|
fprintf(f, " %s", delim_start[emit_type]);
|
|
} else if (m->type == LABEL)
|
|
fprintf(f, " %s:", m->ref);
|
|
|
|
if (emit_type == TYPE_NONE || chunk_len == 0)
|
|
continue;
|
|
|
|
switch(emit_type) {
|
|
case TYPE_UINT16:
|
|
write_propval_int(f, p, chunk_len, 2);
|
|
break;
|
|
case TYPE_UINT32:
|
|
m_phandle = prop->val.markers;
|
|
for_each_marker_of_type(m_phandle, REF_PHANDLE)
|
|
if (m->offset == m_phandle->offset)
|
|
break;
|
|
|
|
if (m_phandle) {
|
|
if (m_phandle->ref[0] == '/')
|
|
fprintf(f, "&{%s}", m_phandle->ref);
|
|
else
|
|
fprintf(f, "&%s", m_phandle->ref);
|
|
if (chunk_len > 4) {
|
|
fputc(' ', f);
|
|
write_propval_int(f, p + 4, chunk_len - 4, 4);
|
|
}
|
|
} else {
|
|
write_propval_int(f, p, chunk_len, 4);
|
|
}
|
|
if (data_len > chunk_len)
|
|
fputc(' ', f);
|
|
break;
|
|
case TYPE_UINT64:
|
|
write_propval_int(f, p, chunk_len, 8);
|
|
break;
|
|
case TYPE_STRING:
|
|
write_propval_string(f, p, chunk_len);
|
|
break;
|
|
default:
|
|
write_propval_int(f, p, chunk_len, 1);
|
|
}
|
|
|
|
if (chunk_len == data_len) {
|
|
size_t pos = m->offset + chunk_len;
|
|
fprintf(f, pos == len ? "%s" : "%s,",
|
|
delim_end[emit_type] ? : "");
|
|
emit_type = TYPE_NONE;
|
|
}
|
|
}
|
|
fprintf(f, ";");
|
|
if (annotate) {
|
|
srcstr = srcpos_string_first(prop->srcpos, annotate);
|
|
if (srcstr) {
|
|
fprintf(f, " /* %s */", srcstr);
|
|
free(srcstr);
|
|
}
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
static void write_tree_source_node(FILE *f, struct node *tree, int level)
|
|
{
|
|
struct property *prop;
|
|
struct node *child;
|
|
struct label *l;
|
|
char *srcstr;
|
|
|
|
write_prefix(f, level);
|
|
for_each_label(tree->labels, l)
|
|
fprintf(f, "%s: ", l->label);
|
|
if (tree->name && (*tree->name))
|
|
fprintf(f, "%s {", tree->name);
|
|
else
|
|
fprintf(f, "/ {");
|
|
|
|
if (annotate) {
|
|
srcstr = srcpos_string_first(tree->srcpos, annotate);
|
|
if (srcstr) {
|
|
fprintf(f, " /* %s */", srcstr);
|
|
free(srcstr);
|
|
}
|
|
}
|
|
fprintf(f, "\n");
|
|
|
|
for_each_property(tree, prop) {
|
|
write_prefix(f, level+1);
|
|
for_each_label(prop->labels, l)
|
|
fprintf(f, "%s: ", l->label);
|
|
fprintf(f, "%s", prop->name);
|
|
write_propval(f, prop);
|
|
}
|
|
for_each_child(tree, child) {
|
|
fprintf(f, "\n");
|
|
write_tree_source_node(f, child, level+1);
|
|
}
|
|
write_prefix(f, level);
|
|
fprintf(f, "};");
|
|
if (annotate) {
|
|
srcstr = srcpos_string_last(tree->srcpos, annotate);
|
|
if (srcstr) {
|
|
fprintf(f, " /* %s */", srcstr);
|
|
free(srcstr);
|
|
}
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
void dt_to_source(FILE *f, struct dt_info *dti)
|
|
{
|
|
struct reserve_info *re;
|
|
|
|
fprintf(f, "/dts-v1/;\n\n");
|
|
|
|
for (re = dti->reservelist; re; re = re->next) {
|
|
struct label *l;
|
|
|
|
for_each_label(re->labels, l)
|
|
fprintf(f, "%s: ", l->label);
|
|
fprintf(f, "/memreserve/\t0x%016llx 0x%016llx;\n",
|
|
(unsigned long long)re->address,
|
|
(unsigned long long)re->size);
|
|
}
|
|
|
|
write_tree_source_node(f, dti->dt, 0);
|
|
}
|