dtc/treesource.c
Rob Herring 95c74d71f0 treesource: Restore string list output when no type markers
When the DTS output has no type markers, we have to guess the type. Prior
to commit 32b9c61307 ("Preserve datatype markers when emitting dts
format"), instances of string lists would be delimited. Since then, a
single string with embedded "\0"s are emitted. An embedded "\0" is valid
for DTS files, but that's a rare exception and lists of strings are the
overwhelming majority. Restore the prior behavior.

stringlist.dts is reused for testing this, but needs a couple of tweaks
in order to match the dts output.

Reported-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Rob Herring <robh@kernel.org>
Message-ID: <20231027142901.2536622-1-robh@kernel.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2023-11-04 12:43:38 +11:00

363 lines
7.3 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] = "",
};
static void add_string_markers(struct property *prop)
{
int l, len = prop->val.len;
const char *p = prop->val.val;
for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1) {
struct marker *m, **nextp;
m = xmalloc(sizeof(*m));
m->offset = l;
m->type = TYPE_STRING;
m->ref = NULL;
m->next = NULL;
/* Find the end of the markerlist */
nextp = &prop->val.markers;
while (*nextp)
nextp = &((*nextp)->next);
*nextp = m;
}
}
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);
}