dtoverlay: Add overlay_map functionality

The overlay_map DTB file provides a way to load different .dtbo
files on different platforms from the same overlay name, e.g.
translating "vc4-kms-v3d" to "vc4-kms-v3d-pi4" on bcm2711.

See: https://github.com/raspberrypi/linux/issues/3520
This commit is contained in:
Phil Elwell
2020-04-01 13:52:36 +01:00
committed by popcornmix
parent 279c93f8ec
commit a246147c21
4 changed files with 288 additions and 27 deletions

View File

@@ -70,6 +70,9 @@ static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type,
static DTOVERLAY_LOGGING_FUNC *dtoverlay_logging_func = dtoverlay_stdio_logging;
static int dtoverlay_debug_enabled = 0;
static DTBLOB_T *overlay_map;
static const char *platform_name;
static int platform_name_len;
static int strmemcmp(const char *mem, int mem_len, const char *str)
{
@@ -541,8 +544,8 @@ static int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off,
if (dtoverlay_debug_enabled)
{
char base_path[256];
char overlay_path[256];
char base_path[DTOVERLAY_MAX_PATH];
char overlay_path[DTOVERLAY_MAX_PATH];
fdt_get_path(base_dtb->fdt, target_off, base_path, sizeof(base_path));
fdt_get_path(overlay_dtb->fdt, overlay_off, overlay_path,
sizeof(overlay_path));
@@ -866,7 +869,7 @@ static int dtoverlay_resolve_fixups(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
if (symbols_off < 0)
{
dtoverlay_error("No symbols found");
dtoverlay_error("no symbols found");
return -FDT_ERR_NOTFOUND;
}
}
@@ -1120,7 +1123,7 @@ int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
fdt_for_each_property_offset(sym_off, overlay_dtb->fdt, frag_off)
{
char target_path[256];
char target_path[DTOVERLAY_MAX_PATH];
const char *sym_name = NULL;
const char *sym_path;
const char *p;
@@ -1180,7 +1183,7 @@ int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
new_path_len = target_path_len + (sym_path + sym_len - p);
if (new_path_len >= sizeof(target_path))
{
dtoverlay_error("Exported symbol path too long for %s", sym_path);
dtoverlay_error("exported symbol path too long for %s", sym_path);
err = -FDT_ERR_NOSPACE;
break;
}
@@ -1189,7 +1192,7 @@ int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb)
base_symbols = fdt_path_offset(base_dtb->fdt, "/__symbols__");
fdt_setprop(base_dtb->fdt, base_symbols,
sym_name, target_path, new_path_len);
dtoverlay_debug("Set label '%s' path to '%s'",
dtoverlay_debug("set label '%s' path to '%s'",
sym_name, target_path);
}
continue;
@@ -1310,7 +1313,6 @@ int dtoverlay_filter_symbols(DTBLOB_T *dtb)
int symbols_off;
int exports_off;
struct str_item *exports = NULL;
const fdt32_t *prop_val;
int prop_off;
struct str_item
@@ -1368,7 +1370,7 @@ int dtoverlay_filter_symbols(DTBLOB_T *dtb)
const char *name = NULL;
struct str_item *str;
prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, &name, NULL);
(void)fdt_getprop_by_offset(dtb->fdt, prop_off, &name, NULL);
if (!name)
break;
@@ -1419,7 +1421,7 @@ const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name,
data = fdt_getprop(dtb->fdt, overrides_off, override_name, &len);
*data_len = len;
if (data)
dtoverlay_debug("Found override %s", override_name);
dtoverlay_debug("found override %s", override_name);
else
dtoverlay_debug("/__overrides__ has no %s property", override_name);
@@ -2357,10 +2359,151 @@ DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size)
FILE *fp = fopen(filename, "rb");
if (fp)
return dtoverlay_load_dtb_from_fp(fp, max_size);
dtoverlay_error("Failed to open '%s'", filename);
dtoverlay_error("failed to open '%s'", filename);
return NULL;
}
void dtoverlay_init_map_from_fp(FILE *fp, const char *compatible,
int compatible_len)
{
if (!compatible)
return;
while (compatible_len > 0)
{
const char *p;
int len;
// Look for a string containing a comma
p = memchr(compatible, ',', compatible_len);
if (p)
{
p++;
len = compatible + compatible_len - p;
}
else
{
// Otherwise treat it as a simple string
p = compatible;
len = compatible_len;
}
/* Group the members of the BCM2835 family */
if (strncmp(p, "bcm2708", len) == 0 ||
strncmp(p, "bcm2709", len) == 0 ||
strncmp(p, "bcm2710", len) == 0 ||
strncmp(p, "bcm2835", len) == 0 ||
strncmp(p, "bcm2836", len) == 0 ||
strncmp(p, "bcm2837", len) == 0)
{
platform_name = "bcm2835";
break;
}
else if (strncmp(p, "bcm2711", len) == 0)
{
platform_name = "bcm2711";
break;
}
compatible_len -= (p - compatible);
compatible = p;
len = strnlen(compatible, compatible_len) + 1;
compatible += len;
compatible_len -= len;
}
if (platform_name)
{
dtoverlay_debug("using platform '%s'", platform_name);
platform_name_len = strlen(platform_name);
if (fp)
overlay_map = dtoverlay_load_dtb_from_fp(fp, 0);
}
else
{
dtoverlay_warn("no matching platform found");
}
dtoverlay_debug("overlay map %sloaded", overlay_map ? "" : "not ");
}
void dtoverlay_init_map(const char *overlay_dir, const char *compatible,
int compatible_len)
{
char map_file[DTOVERLAY_MAX_PATH];
int dir_len = strlen(overlay_dir);
FILE *fp;
static int tried;
if (tried)
return;
tried = 1;
if (!compatible)
return;
/* Handle the possibility that the supplied directory may or may not end
with a slash */
sprintf(map_file, "%s%soverlay_map.dtb", overlay_dir,
(!dir_len || overlay_dir[dir_len - 1] != '/') ? "/" : "");
fp = fopen(map_file, "rb");
dtoverlay_init_map_from_fp(fp, compatible, compatible_len);
}
const char *dtoverlay_remap_overlay(const char *overlay)
{
while (overlay_map)
{
const char *deprecated_msg;
const char *new_name;
int root_off;
int overlay_off;
int prop_len;
root_off = fdt_path_offset(overlay_map->fdt, "/");
overlay_off = fdt_subnode_offset(overlay_map->fdt, root_off, overlay);
if (overlay_off < 0)
break;
new_name = fdt_getprop_namelen(overlay_map->fdt, overlay_off,
platform_name, platform_name_len,
&prop_len);
if (new_name)
{
if (new_name[0])
overlay = new_name;
break;
}
// Has it been renamed or deprecated?
new_name = fdt_getprop_namelen(overlay_map->fdt, overlay_off,
"renamed", 7, &prop_len);
if (new_name)
{
dtoverlay_warn("overlay '%s' has been renamed '%s'",
overlay, new_name);
overlay = new_name;
continue;
}
deprecated_msg = fdt_getprop_namelen(overlay_map->fdt, overlay_off,
"deprecated", 10, &prop_len);
if (deprecated_msg)
dtoverlay_error("overlay '%s' is deprecated: %s",
overlay, deprecated_msg);
else
dtoverlay_error("overlay '%s' is not supported on the '%s' platform", overlay, platform_name);
return NULL;
}
return overlay;
}
DTBLOB_T *dtoverlay_import_fdt(void *fdt, int buf_size)
{
DTBLOB_T *dtb = NULL;
@@ -2435,12 +2578,12 @@ int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename)
goto error_exit;
}
dtoverlay_debug("Wrote %ld bytes to '%s'", len, filename);
dtoverlay_debug("wrote %ld bytes to '%s'", len, filename);
fclose(fp);
}
else
{
dtoverlay_debug("Failed to create '%s'", filename);
dtoverlay_debug("failed to create '%s'", filename);
err = -1;
}
@@ -2529,7 +2672,7 @@ int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name)
if (symbols_off < 0)
{
dtoverlay_error("No symbols found");
dtoverlay_error("no symbols found");
return -FDT_ERR_NOTFOUND;
}
@@ -2595,7 +2738,7 @@ int dtoverlay_set_property(DTBLOB_T *dtb, int pos,
{
int err = fdt_setprop(dtb->fdt, pos, prop_name, prop, prop_len);
if (err < 0)
dtoverlay_error("Failed to set property '%s'", prop_name);
dtoverlay_error("failed to set property '%s'", prop_name);
return err;
}
@@ -2642,6 +2785,14 @@ void dtoverlay_error(const char *fmt, ...)
va_end(args);
}
void dtoverlay_warn(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
(*dtoverlay_logging_func)(DTOVERLAY_WARN, fmt, args);
va_end(args);
}
void dtoverlay_debug(const char *fmt, ...)
{
va_list args;
@@ -2664,6 +2815,10 @@ static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type,
type_str = "error";
break;
case DTOVERLAY_WARN:
type_str = "warn";
break;
case DTOVERLAY_DEBUG:
type_str = "debug";
break;

View File

@@ -45,11 +45,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define ONLY_FATAL(err) (IS_FATAL(err) ? (err) : 0)
#define DTOVERLAY_PADDING(size) (-(size))
#define DTOVERLAY_MAX_PATH 256
typedef enum
{
DTOVERLAY_ERROR,
DTOVERLAY_DEBUG
DTOVERLAY_DEBUG,
DTOVERLAY_WARN, // Append to preserve backwards compatibility
} dtoverlay_logging_type_t;
typedef struct dtoverlay_struct
@@ -160,6 +162,13 @@ DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size);
DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size);
void dtoverlay_init_map_from_fp(FILE *fp, const char *compatible,
int compatible_len);
void dtoverlay_init_map(const char *overlay_dir, const char *compatible,
int compatible_len);
const char *dtoverlay_remap_overlay(const char *overlay);
DTBLOB_T *dtoverlay_import_fdt(void *fdt, int max_size);
int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename);
@@ -221,6 +230,8 @@ void dtoverlay_enable_debug(int enable);
void dtoverlay_error(const char *fmt, ...);
void dtoverlay_warn(const char *fmt, ...);
void dtoverlay_debug(const char *fmt, ...);
#endif

View File

@@ -49,11 +49,15 @@ int main(int argc, char **argv)
const char *base_file;
const char *merged_file;
const char *overlay_file;
const char *compatible;
char *overlay_dir;
char *p;
DTBLOB_T *base_dtb;
DTBLOB_T *overlay_dtb;
int err;
int argn = 1;
int max_dtb_size = 100000;
int compatible_len;
while ((argn < argc) && (argv[argn][0] == '-'))
{
@@ -87,6 +91,24 @@ int main(int argc, char **argv)
return -1;
}
if (strnlen(overlay_file, DTOVERLAY_MAX_PATH) == DTOVERLAY_MAX_PATH)
{
printf("* overlay filename too long\n");
return -1;
}
overlay_dir = strdup(overlay_file);
p = strrchr(overlay_dir, '/');
if (p)
*p = 0;
else
overlay_dir = ".";
compatible = dtoverlay_get_property(base_dtb,
dtoverlay_find_node(base_dtb, "/", 1),
"compatible", &compatible_len);
dtoverlay_init_map(overlay_dir, compatible, compatible_len);
err = dtoverlay_set_synonym(base_dtb, "i2c", "i2c0");
err = dtoverlay_set_synonym(base_dtb, "i2c_arm", "i2c0");
err = dtoverlay_set_synonym(base_dtb, "i2c_vc", "i2c1");
@@ -100,11 +122,42 @@ int main(int argc, char **argv)
}
else
{
overlay_dtb = dtoverlay_load_dtb(overlay_file, max_dtb_size);
if (overlay_dtb)
err = dtoverlay_fixup_overlay(base_dtb, overlay_dtb);
char new_file[DTOVERLAY_MAX_PATH];
char *overlay_name;
const char *new_name;
char *p;
int len;
strcpy(new_file, overlay_file);
overlay_name = strrchr(new_file, '/');
if (overlay_name)
overlay_name++;
else
err = -1;
overlay_name = new_file;
p = strrchr(overlay_name, '.');
if (p)
*p = 0;
new_name = dtoverlay_remap_overlay(overlay_name);
if (new_name)
{
if (strcmp(overlay_name, new_name))
dtoverlay_debug("mapped overlay '%s' to '%s'", overlay_name, new_name);
len = strlen(new_name);
memmove(overlay_name, new_name, len);
strcpy(overlay_name + len, ".dtbo");
overlay_dtb = dtoverlay_load_dtb(new_file, max_dtb_size);
if (overlay_dtb)
err = dtoverlay_fixup_overlay(base_dtb, overlay_dtb);
else
err = -1;
}
else
{
overlay_dtb = NULL;
err = -2;
}
}
while (!err && (argn < argc))
@@ -165,8 +218,5 @@ int main(int argc, char **argv)
dtoverlay_free_dtb(base_dtb);
if (err != 0)
printf("* Exiting with error code %d\n", err);
return err;
}

55
host_applications/linux/apps/dtoverlay/dtoverlay_main.c Executable file → Normal file
View File

@@ -102,6 +102,8 @@ const char *work_dir = WORK_DIR;
const char *overlay_src_dir;
const char *dt_overlays_dir;
const char *error_file = NULL;
const char *platform_string;
int platform_string_len;
int dry_run = 0;
int main(int argc, const char **argv)
@@ -163,6 +165,13 @@ int main(int argc, const char **argv)
usage();
overlay_src_dir = argv[argn++];
}
else if (strcmp(arg, "-p") == 0)
{
if (argn == argc)
usage();
platform_string = argv[argn++];
platform_string_len = strlen(platform_string) + 1;
}
else if (strcmp(arg, "-v") == 0)
{
opt_verbose = 1;
@@ -257,6 +266,34 @@ int main(int argc, const char **argv)
fatal_error("Failed to mount configfs - %d", errno);
}
if (!platform_string)
{
FILE *fp = fopen("/proc/device-tree/compatible", "r");
if (fp)
{
long len;
long bytes_read;
char *prop_buf;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fseek(fp, 0, SEEK_SET);
prop_buf = malloc(len);
bytes_read = fread(prop_buf, 1, len, fp);
if (bytes_read == len)
{
platform_string = prop_buf;
platform_string_len = len;
}
else
fatal_error("Failed to read 'compatible' property");
fclose(fp);
}
}
if (platform_string)
dtoverlay_init_map(overlay_src_dir, platform_string, platform_string_len);
if (!dry_run)
{
dt_overlays_dir = sprintf_dup("%s/%s", cfg_dir, DT_OVERLAYS_SUBDIR);
@@ -425,6 +462,12 @@ static int dtoverlay_add(STATE_T *state, const char *overlay,
}
else
{
const char *remapped = dtoverlay_remap_overlay(overlay);
if (!remapped)
return error("Failed to load '%s'", overlay);
if (strcmp(overlay, remapped))
dtoverlay_debug("mapped overlay '%s' to '%s'", overlay, remapped);
overlay = remapped;
overlay_file = sprintf_dup("%s/%s.dtbo", overlay_src_dir, overlay);
}
@@ -432,6 +475,7 @@ static int dtoverlay_add(STATE_T *state, const char *overlay,
overlay_name = "dry_run";
else
overlay_name = sprintf_dup("%d_%s", state->count, overlay);
dtoverlay_debug("loading file '%s'", overlay_file);
overlay_dtb = dtoverlay_load_dtb(overlay_file, DTOVERLAY_PADDING(4096));
if (!overlay_dtb)
return error("Failed to read '%s'", overlay_file);
@@ -851,7 +895,7 @@ static void usage(void)
printf(" %s Display help on all parameters\n", cmd_name);
printf(" %s <param>=<val>...\n", cmd_name);
printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), "");
printf(" %s -D [<idx>] Dry-run (prepare overlay, but don't apply -\n",
printf(" %s -D Dry-run (prepare overlay, but don't apply -\n",
cmd_name);
printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), "");
printf(" %s -r [<idx>] Remove an overlay (by index, or the last)\n", cmd_name);
@@ -866,7 +910,7 @@ static void usage(void)
{
printf(" %s <overlay> [<param>=<val>...]\n", cmd_name);
printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), "");
printf(" %s -D [<idx>] Dry-run (prepare overlay, but don't apply -\n",
printf(" %s -D Dry-run (prepare overlay, but don't apply -\n",
cmd_name);
printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), "");
printf(" %s -r [<overlay>] Remove an overlay (by name, index or the last)\n", cmd_name);
@@ -880,9 +924,10 @@ static void usage(void)
printf(" where <overlay> is the name of an overlay or 'dtparam' for dtparams\n");
}
printf("Options applicable to most variants:\n");
printf(" -d <dir> Specify an alternate location for the overlays\n");
printf(" (defaults to /boot/overlays or /flash/overlays)\n");
printf(" -v Verbose operation\n");
printf(" -d <dir> Specify an alternate location for the overlays\n");
printf(" (defaults to /boot/overlays or /flash/overlays)\n");
printf(" -p <string> Force a compatible string for the platform\n");
printf(" -v Verbose operation\n");
printf("\n");
printf("Adding or removing overlays and parameters requires root privileges.\n");