Skip to content

Commit 1f8e149

Browse files
committed
dtoverlay: intra fragments, phandles in overrides
Intra fragments (those that target other fragments in the overlay) require special handling when applying overlays at runtime because they have the potential to copy phandles, breaking the fixups. Similarly for overrides that write phandle values. Add logic to detect those cases and patch the fixups accordingly. See: raspberrypi/linux#5652 Signed-off-by: Phil Elwell <[email protected]>
1 parent c3e9cfc commit 1f8e149

File tree

3 files changed

+318
-17
lines changed

3 files changed

+318
-17
lines changed

dtmerge/dtoverlay.c

Lines changed: 189 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2016-2019 Raspberry Pi (Trading) Ltd.
2+
Copyright (c) 2016-2023 Raspberry Pi Ltd.
33
All rights reserved.
44
55
Redistribution and use in source and binary forms, with or without
@@ -75,6 +75,12 @@ static DTBLOB_T *overlay_map;
7575
static const char *platform_name;
7676
static int platform_name_len;
7777

78+
static void (*cell_changed_callback)(DTBLOB_T *, int, const char *, int, int);
79+
static void (*intra_fragment_merged_callback)(DTBLOB_T *, int, int);
80+
81+
static const void *override_data_start;
82+
static const void *cell_source;
83+
7884
static int strmemcmp(const char *mem, int mem_len, const char *str)
7985
{
8086
int ret = strncmp(mem, str, mem_len);
@@ -602,7 +608,9 @@ static int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off,
602608
err = fdt_appendprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len);
603609
}
604610
else
611+
{
605612
err = fdt_setprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len);
613+
}
606614
}
607615

608616
// Merge each subnode of the node
@@ -1167,7 +1175,6 @@ int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
11671175
}
11681176

11691177
target_off = dtoverlay_get_target_offset(NULL, overlay_dtb, frag_off);
1170-
11711178
if (target_off < 0)
11721179
continue;
11731180

@@ -1176,6 +1183,9 @@ int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
11761183
// as source and destination because the source is not expected to
11771184
// change. Instead, clone the overlay, apply the fragment, then switch.
11781185

1186+
if (intra_fragment_merged_callback)
1187+
(*intra_fragment_merged_callback)(overlay_dtb, overlay_off, target_off);
1188+
11791189
if (!overlay_copy)
11801190
{
11811191
overlay_copy = malloc(overlay_size);
@@ -1192,6 +1202,7 @@ int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
11921202
overlay_off, 0);
11931203
if (err)
11941204
break;
1205+
11951206
// Swap the buffers
11961207
{
11971208
void *temp = overlay_dtb->fdt;
@@ -1441,6 +1452,170 @@ int dtoverlay_filter_symbols(DTBLOB_T *dtb)
14411452
return 0;
14421453
}
14431454

1455+
const char *dtoverlay_find_fixup(DTBLOB_T *dtb, const char *fixup_loc)
1456+
{
1457+
int fixups_off;
1458+
1459+
fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__");
1460+
1461+
if (fixups_off > 0)
1462+
{
1463+
int fixup_off;
1464+
1465+
for (fixup_off = fdt_first_property_offset(dtb->fdt, fixups_off);
1466+
fixup_off >= 0;
1467+
fixup_off = fdt_next_property_offset(dtb->fdt, fixup_off))
1468+
{
1469+
const char *fixups_stringlist;
1470+
const char *symbol_name;
1471+
int list_len;
1472+
1473+
fixups_stringlist = fdt_getprop_by_offset(dtb->fdt, fixup_off,
1474+
&symbol_name, &list_len);
1475+
if (fdt_stringlist_contains(fixups_stringlist, list_len, fixup_loc) > 0)
1476+
return symbol_name;
1477+
}
1478+
}
1479+
1480+
return NULL;
1481+
}
1482+
1483+
int dtoverlay_add_fixup(DTBLOB_T *dtb, const char *symbol, const char *fixup_loc)
1484+
{
1485+
int loc_len = strlen(fixup_loc);
1486+
int fixups_off;
1487+
1488+
fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__");
1489+
assert(fixups_off > 0);
1490+
1491+
if (fixups_off < 0)
1492+
return fixups_off;
1493+
1494+
return fdt_appendprop(dtb->fdt, fixups_off, symbol, fixup_loc, loc_len + 1);
1495+
}
1496+
1497+
static const char *stringlist_find(const char *strlist, int listlen, const char *str, int len)
1498+
{
1499+
const char *p;
1500+
1501+
while (listlen >= len) {
1502+
if (memcmp(str, strlist, len + 1) == 0)
1503+
return strlist;
1504+
p = memchr(strlist, '\0', listlen);
1505+
if (!p)
1506+
return NULL; /* malformed strlist.. */
1507+
listlen -= (p-strlist) + 1;
1508+
strlist = p + 1;
1509+
}
1510+
1511+
return NULL;
1512+
}
1513+
1514+
int dtoverlay_delete_fixup(DTBLOB_T *dtb, const char *fixup_loc)
1515+
{
1516+
int loc_len = strlen(fixup_loc);
1517+
int fixups_off;
1518+
1519+
fixups_off = fdt_path_offset(dtb->fdt, "/__fixups__");
1520+
1521+
if (fixups_off > 0)
1522+
{
1523+
int fixup_off;
1524+
1525+
for (fixup_off = fdt_first_property_offset(dtb->fdt, fixups_off);
1526+
fixup_off >= 0;
1527+
fixup_off = fdt_next_property_offset(dtb->fdt, fixup_off))
1528+
{
1529+
const char *fixups_stringlist;
1530+
const char *symbol_name;
1531+
const char *match;
1532+
int list_len;
1533+
1534+
fixups_stringlist = fdt_getprop_by_offset(dtb->fdt, fixup_off,
1535+
&symbol_name, &list_len);
1536+
1537+
match = stringlist_find(fixups_stringlist, list_len, fixup_loc, loc_len);
1538+
if (match)
1539+
{
1540+
int match_len = loc_len + 1;
1541+
if (match_len == list_len)
1542+
{
1543+
/* This was the only fixup - the symbol is no longer referenced */
1544+
return fdt_delprop(dtb->fdt, fixups_off, symbol_name);
1545+
}
1546+
else
1547+
{
1548+
char *buf = malloc(list_len - match_len);
1549+
int match_off = match - fixups_stringlist;
1550+
int after_match = list_len - (match_off + match_len);
1551+
int err;
1552+
if (match_off)
1553+
memcpy(buf, fixups_stringlist, match_off);
1554+
if (after_match)
1555+
memcpy(buf + match_off, match + match_len, after_match);
1556+
err = fdt_setprop(dtb->fdt, fixups_off, symbol_name, buf, list_len - match_len);
1557+
free(buf);
1558+
return err;
1559+
}
1560+
}
1561+
}
1562+
}
1563+
1564+
return -FDT_ERR_NOTFOUND;
1565+
}
1566+
1567+
int dtoverlay_stringlist_replace(const char *src, int src_len,
1568+
const char *src_prefix, int src_prefix_len,
1569+
const char *dst_prefix, int dst_prefix_len,
1570+
char *dst)
1571+
{
1572+
/* With a NULL dst, only returns the new length */
1573+
char *dst_p = dst;
1574+
int replaced = 0;
1575+
1576+
while (src_len)
1577+
{
1578+
const char *p;
1579+
int copy_bytes;
1580+
1581+
p = memchr(src, '\0', src_len);
1582+
if (!p)
1583+
return -1; /* malformed strlist.. */
1584+
1585+
if (src_prefix_len < src_len && memcmp(src, src_prefix, src_prefix_len) == 0)
1586+
{
1587+
if (dst)
1588+
memcpy(dst_p, dst_prefix, dst_prefix_len);
1589+
src_len -= src_prefix_len;
1590+
src += src_prefix_len;
1591+
dst_p += dst_prefix_len;
1592+
replaced = 1;
1593+
}
1594+
1595+
copy_bytes = (p - src) + 1;
1596+
if (dst)
1597+
memcpy(dst_p, src, copy_bytes);
1598+
dst_p += copy_bytes;
1599+
src_len -= copy_bytes;
1600+
src = p + 1;
1601+
}
1602+
1603+
if (!replaced)
1604+
return -1;
1605+
1606+
return dst_p - dst;
1607+
}
1608+
1609+
void dtoverlay_set_intra_fragment_merged_callback(void (*callback)(DTBLOB_T *, int, int))
1610+
{
1611+
intra_fragment_merged_callback = callback;
1612+
}
1613+
1614+
void dtoverlay_set_cell_changed_callback(void (*callback)(DTBLOB_T *, int, const char *, int, int))
1615+
{
1616+
cell_changed_callback = callback;
1617+
}
1618+
14441619
/* Returns a pointer to the override data and (through data_len) its length.
14451620
On error, sets *data_len to be the error code. */
14461621
const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name,
@@ -1790,6 +1965,10 @@ int dtoverlay_override_one_target(int override_type,
17901965
}
17911966
}
17921967

1968+
if (!err && cell_changed_callback && cell_source && override_type == DTOVERRIDE_INTEGER && target_size == 4)
1969+
(*cell_changed_callback)(dtb, node_off, prop_name, target_off,
1970+
(int)(cell_source - override_data_start));
1971+
17931972
return err;
17941973
}
17951974

@@ -1844,6 +2023,7 @@ int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name,
18442023
memcpy(data_buf, override_data, data_len);
18452024
data = data_buf;
18462025
data_end = data + data_len;
2026+
override_data_start = data_buf;
18472027

18482028
while (err == 0)
18492029
{
@@ -1869,7 +2049,6 @@ int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name,
18692049
err = override_type;
18702050
break;
18712051
}
1872-
/* Pass DTOVERRIDE_END to the callback, in case it is interested */
18732052

18742053
if (target_phandle != 0)
18752054
{
@@ -1889,8 +2068,10 @@ int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name,
18892068
prop_name[name_len] = '\0';
18902069
}
18912070

2071+
/* Pass DTOVERRIDE_END to the callback, in case it is interested */
18922072
err = callback(override_type, target_value, dtb, node_off, prop_name,
1893-
target_phandle, target_off, target_size, callback_state);
2073+
target_phandle, target_off, target_size,
2074+
callback_state);
18942075

18952076
if (override_type == DTOVERRIDE_END)
18962077
break;
@@ -1930,6 +2111,8 @@ static int dtoverlay_extract_override(const char *override_name,
19302111
char literal_type = '?';
19312112
int type;
19322113

2114+
cell_source = NULL;
2115+
19332116
data = *datap;
19342117
len = data_end - data;
19352118
if (len <= 0)
@@ -2104,6 +2287,7 @@ static int dtoverlay_extract_override(const char *override_name,
21042287
{
21052288
/* Cell */
21062289
sprintf(override_value, "%d", dtoverlay_read_u32(data, 0));
2290+
cell_source = data;
21072291
*datap = data + 4;
21082292
}
21092293
}
@@ -2159,6 +2343,7 @@ static const char *dtoverlay_extract_immediate(const char *data, const char *dat
21592343
return NULL;
21602344
}
21612345
val = dtoverlay_read_u32(data, 0);
2346+
cell_source = data;
21622347
if (buf)
21632348
snprintf(buf, buf_len, "%d", val);
21642349
data += 4;

dtmerge/dtoverlay.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2016 Raspberry Pi (Trading) Ltd.
2+
Copyright (c) 2016-2023 Raspberry Pi Ltd.
33
All rights reserved.
44
55
Redistribution and use in source and binary forms, with or without
@@ -135,6 +135,17 @@ int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params,
135135

136136
int dtoverlay_filter_symbols(DTBLOB_T *dtb);
137137

138+
const char *dtoverlay_find_fixup(DTBLOB_T *dtb, const char *fixup_loc);
139+
int dtoverlay_add_fixup(DTBLOB_T *dtb, const char *symbol, const char *fixup_loc);
140+
int dtoverlay_delete_fixup(DTBLOB_T *dtb, const char *fixup_loc);
141+
142+
int dtoverlay_stringlist_replace(const char *src, int src_len,
143+
const char *src_prefix, int src_prefix_len,
144+
const char *dst_prefix, int dst_prefix_len,
145+
char *dst);
146+
void dtoverlay_set_intra_fragment_merged_callback(void (*callback)(DTBLOB_T *, int, int));
147+
void dtoverlay_set_cell_changed_callback(void (*callback)(DTBLOB_T *, int, const char *, int, int));
148+
138149
const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name,
139150
int *data_len);
140151

0 commit comments

Comments
 (0)