// 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 target - info about current target node as recursing through overlay
* @np: node where current level of overlay will be applied
* @in_livetree: @np is a node in the live devicetree
*
* Used in the algorithm to create the portion of a changeset that describes
* an overlay fragment, which is a devicetree subtree. Initially @np is a node
* in the live devicetree where the overlay subtree is targeted to be grafted
* into. When recursing to the next level of the overlay subtree, the target
* also recurses to the next level of the live devicetree, as long as overlay
* subtree node also exists in the live devicetree. When a node in the overlay
* subtree does not exist at the same level in the live devicetree, target->np
* points to a newly allocated node, and all subsequent targets in the subtree
* will be newly allocated nodes.
*/
struct target {
struct device_node *np;
bool in_livetree;
};
/**
* struct fragment - info about fragment nodes in overlay expanded device tree
* @overlay: pointer to the __overlay__ node
* @target: target of the overlay operation
*/
struct fragment {
struct device_node *overlay;
struct device_node *target;
};
/**
* struct overlay_changeset
* @id: changeset identifier
* @ovcs_list: list on which we are located
* @new_fdt: Memory allocated to hold unflattened aligned FDT
* @overlay_mem: the memory chunk that contains @overlay_root
* @overlay_root: expanded device tree that contains the fragment nodes
* @notify_state: most recent notify action used on overlay
* @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 *new_fdt;
const void *overlay_mem;
struct device_node *overlay_root;
enum of_overlay_notify_action notify_state;
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 target *target, 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_unregister() - 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);
}