// SPDX-License-Identifier: GPL-2.0
/*
* Functions for working with device tree overlays
*
* Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
* Copyright (C) 2012 Texas Instruments Inc.
*/
#define pr_fmt(fmt) "OF: overlay: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_fdt.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/libfdt.h>
#include <linux/err.h>
#include <linux/idr.h>
#include "of_private.h"
/**
* struct fragment - info about fragment nodes in overlay expanded device tree
* @target: target of the overlay operation
* @overlay: pointer to the __overlay__ node
*/
struct fragment {
struct device_node *target;
struct device_node *overlay;
};
/**
* struct overlay_changeset
* @id: changeset identifier
* @ovcs_list: list on which we are located
* @fdt: FDT that was unflattened to create @overlay_tree
* @overlay_tree: expanded device tree that contains the fragment nodes
* @count: count of fragment structures
* @fragments: fragment nodes in the overlay expanded device tree
* @symbols_fragment: last element of @fragments[] is the __symbols__ node
* @cset: changeset to apply fragments to live device tree
*/
struct overlay_changeset {
int id;
struct list_head ovcs_list;
const void *fdt;
struct device_node *overlay_tree;
int count;
struct fragment *fragments;
bool symbols_fragment;
struct of_changeset cset;
};
/* flags are sticky - once set, do not reset */
static int devicetree_state_flags;
#define DTSF_APPLY_FAIL 0x01
#define DTSF_REVERT_FAIL 0x02
/*
* If a changeset apply or revert encounters an error, an attempt will
* be made to undo partial changes, but may fail. If the undo fails
* we do not know the state of the devicetree.
*/
static int devicetree_corrupt(void)
{
return devicetree_state_flags &
(DTSF_APPLY_FAIL | DTSF_REVERT_FAIL);
}
static int build_changeset_next_level(struct overlay_changeset *ovcs,
struct device_node *target_node,
const struct device_node *overlay_node);
/*
* of_resolve_phandles() finds the largest phandle in the live tree.
* of_overlay_apply() may add a larger phandle to the live tree.
* Do not allow race between two overlays being applied simultaneously:
* mutex_lock(&of_overlay_phandle_mutex)
* of_resolve_phandles()
* of_overlay_apply()
* mutex_unlock(&of_overlay_phandle_mutex)
*/
static DEFINE_MUTEX(of_overlay_phandle_mutex);
void of_overlay_mutex_lock(void)
{
mutex_lock(&of_overlay_phandle_mutex);
}
void of_overlay_mutex_unlock(void)
{
mutex_unlock(&of_overlay_phandle_mutex);
}
static LIST_HEAD(ovcs_list);
static DEFINE_IDR(ovcs_idr);
static BLOCKING_NOTIFIER_HEAD(overlay_notify_chain);
/**
* of_overlay_notifier_register() - Register notifier for overlay operations
* @nb: Notifier block to register
*
* Register for notification on overlay operations on device tree nodes. The
* reported actions definied by @of_reconfig_change. The notifier callback
* furthermore receives a pointer to the affected device tree node.
*
* Note that a notifier callback is not supposed to store pointers to a device
* tree node or its content beyond @OF_OVERLAY_POST_REMOVE corresponding to the
* respective node it received.
*/
int of_overlay_notifier_register(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&overlay_notify_chain, nb);
}
EXPORT_SYMBOL_GPL(of_overlay_notifier_register);
/**
* of_overlay_notifier_register() - Unregister notifier for overlay operations
* @nb: Notifier block to unregister
*/
int of_overlay_notifier_unregister(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&overlay_notify_chain, nb);
}
EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister);
static char *of_overlay_action_name[] = {
"pre-apply",
"post-apply",
"pre-remove",
"post-remove",
};
static int overlay_notify(struct overlay_changeset *ovcs,
enum of_overlay_notify_action action)
{
struct of_overlay_notify_data nd;
int i, ret;
for (i = 0; i < ovcs->count; i++) {
struct fragment *fragment = &ovcs->fragments[i];
nd.target = fragment->target;
nd.overlay = fragment->overlay;
ret = blocking_notifier_call_chain(&overlay_notify_chain,
action, &nd);
if (ret == NOTIFY_OK || ret == NOTIFY_STOP)
return 0;
if (ret) {
ret = notifier_to_errno(ret);
pr_err("overlay changeset %s notifier error %d, target: %pOF\n",
of_overlay_action_name[action], ret, nd.target);
return ret;
}
}
return 0;
}
/*
* The values of properties in the "/__symbols__" node are paths in
* the ovcs->overlay_tree. When duplicating the properties, the paths
* need to be adjusted to be the correct path for the live device tree.
*
* The paths refer to a node in the subtree of a fragment node's "__overlay__"
* node, for example "/fragment@0/__overlay__/symbol_path_tail",
* where symbol_path_tail can be a single node or it may be a multi-node path.
*
* The duplicated property value will be modified by replacing the
* "/fragment_name/__overlay/" portion of the value with the target
* path from the fragm