dtoverlay: Add support for string escape sequences

Add the ability to embed arbitrary byte data in strings. The change was
motivated by the desire to allow parameters to supply multiple compatible
strings, but there may be other uses.

The acceptable escape sequences are most of the range supported by C, but
with the hexadecimal ('\x??`) escape limited to precisely two digits.

See: https://forums.raspberrypi.com/viewtopic.php?t=330792

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
This commit is contained in:
Phil Elwell
2022-03-14 10:49:51 +00:00
committed by Dom Cobley
parent b1ee39e50e
commit d1e92d79e5

View File

@@ -1453,7 +1453,7 @@ const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name,
return data; return data;
} }
int hex_digit(char c) static int hex_digit(char c)
{ {
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
return c - '0'; return c - '0';
@@ -1465,6 +1465,19 @@ int hex_digit(char c)
return -1; return -1;
} }
static int hex_byte(const char *p)
{
int nib1, nib2;
nib1 = hex_digit(p[0]);
if (nib1 < 0)
return -1;
nib2 = hex_digit(p[1]);
if (nib2 < 0)
return -1;
return (nib1 << 4) | nib2;
}
int dtoverlay_override_one_target(int override_type, int dtoverlay_override_one_target(int override_type,
const char *override_value, const char *override_value,
DTBLOB_T *dtb, int node_off, DTBLOB_T *dtb, int node_off,
@@ -1476,24 +1489,75 @@ int dtoverlay_override_one_target(int override_type,
if (override_type == DTOVERRIDE_STRING) if (override_type == DTOVERRIDE_STRING)
{ {
char *prop_val; char unescaped_value[256];
char *prop_val, *q;
const char *p;
int prop_len; int prop_len;
/* Replace the whole property with the string */ p = override_value;
q = unescaped_value;
while (*p)
{
int c = *(p++);
if (c == '\\')
{
c = *(p++);
if (c >= '0' && c <= '7')
{
c -= '0';
if (*p >= '0' && *p <= '7')
{
c = (c << 3) + *(p++) - '0';
if (*p >= '0' && *p <= '7')
{
c = (c << 3) + *(p++) - '0';
if (c > 255)
c = -1;
}
}
}
else if (c == 'a')
c = '\x07';
else if (c == 'b')
c = '\b';
else if (c == 'f')
c = '\f';
else if (c == 'n')
c = '\n';
else if (c == 'r')
c = '\r';
else if (c == 't')
c = '\t';
else if (c == 'v')
c = '\v';
else if (c == 'x')
c = hex_byte(p), p += 2;
else if (c != '\\' && c != '\'' && c != '"')
c = -1;
if (c < 0)
{
dtoverlay_error("invalid escape in '%s'", override_value);
return NON_FATAL(FDT_ERR_BADVALUE);
}
}
*(q++) = (char)c;
}
*(q++) = '\0';
/* Append to or replace the property string */
if ((strcmp(prop_name, "bootargs") == 0) && if ((strcmp(prop_name, "bootargs") == 0) &&
((prop_val = fdt_getprop_w(dtb->fdt, node_off, prop_name, ((prop_val = fdt_getprop_w(dtb->fdt, node_off, prop_name,
&prop_len)) != NULL) && &prop_len)) != NULL) &&
(prop_len > 0) && prop_val[0]) (prop_len > 0) && prop_val[0])
{ {
prop_val[prop_len - 1] = ' '; prop_val[prop_len - 1] = ' ';
err = fdt_appendprop_string(dtb->fdt, node_off, prop_name, override_value); err = fdt_appendprop(dtb->fdt, node_off, prop_name, unescaped_value, q - unescaped_value);
} }
else if (strcmp(prop_name, "name") == 0) // "name" is a pseudo-property else if (strcmp(prop_name, "name") == 0) // "name" is a pseudo-property
{ {
err = dtoverlay_set_node_name(dtb, node_off, override_value); err = dtoverlay_set_node_name(dtb, node_off, unescaped_value);
} }
else else
err = fdt_setprop_string(dtb->fdt, node_off, prop_name, override_value); err = fdt_setprop(dtb->fdt, node_off, prop_name, unescaped_value, q - unescaped_value);
} }
else if (override_type == DTOVERRIDE_BYTE_STRING) else if (override_type == DTOVERRIDE_BYTE_STRING)
{ {
@@ -1504,18 +1568,17 @@ int dtoverlay_override_one_target(int override_type,
while (*p) while (*p)
{ {
int nib1, nib2; int byteval;
// whitespace and colons are legal separators // whitespace and colons are legal separators
if (*p == ':' || *p == ' ' || *p == '\t') if (*p == ':' || *p == ' ' || *p == '\t')
{ {
p++; p++;
continue; continue;
} }
nib1 = hex_digit(*p++); byteval = hex_byte(p);
nib2 = hex_digit(*p++); if (byteval < 0)
if (nib1 < 0 || nib2 < 0)
{ {
dtoverlay_error("invalid bytestring '%s'", override_value); dtoverlay_error("invalid bytestring at '%s'", p);
return NON_FATAL(FDT_ERR_BADVALUE); return NON_FATAL(FDT_ERR_BADVALUE);
} }
if (byte_count == sizeof(bytes_buf)) if (byte_count == sizeof(bytes_buf))
@@ -1523,7 +1586,8 @@ int dtoverlay_override_one_target(int override_type,
dtoverlay_error("bytestring '%s' too long", override_value); dtoverlay_error("bytestring '%s' too long", override_value);
return NON_FATAL(FDT_ERR_BADVALUE); return NON_FATAL(FDT_ERR_BADVALUE);
} }
bytes_buf[byte_count++] = (nib1 << 4) | nib2; bytes_buf[byte_count++] = byteval;
p += 2;
} }
err = fdt_setprop(dtb->fdt, node_off, prop_name, bytes_buf, byte_count); err = fdt_setprop(dtb->fdt, node_off, prop_name, bytes_buf, byte_count);