linux/drivers/of/overlay.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Functions for working with device tree overlays
 *
 * Copyright (C) 2012 Pantelis Antoniou <[email protected]>
 * Copyright (C) 2012 Texas Instruments Inc.
 */

#define pr_fmt(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 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 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 {};

/* flags are sticky - once set, do not reset */
static int devicetree_state_flags;
#define DTSF_APPLY_FAIL
#define DTSF_REVERT_FAIL

/*
 * 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)
{}

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)
{}

void of_overlay_mutex_unlock(void)
{}

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)
{}
EXPORT_SYMBOL_GPL();

/**
 * of_overlay_notifier_unregister() - Unregister notifier for overlay operations
 * @nb:		Notifier block to unregister
 */
int of_overlay_notifier_unregister(struct notifier_block *nb)
{}
EXPORT_SYMBOL_GPL();

static int overlay_notify(struct overlay_changeset *ovcs,
		enum of_overlay_notify_action action)
{}

/*
 * The values of properties in the "/__symbols__" node are paths in
 * the ovcs->overlay_root.  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 fragment node.
 */
static struct property *dup_and_fixup_symbol_prop(
		struct overlay_changeset *ovcs, const struct property *prop)
{}

/**
 * add_changeset_property() - add @overlay_prop to overlay changeset
 * @ovcs:		overlay changeset
 * @target:		where @overlay_prop will be placed
 * @overlay_prop:	property to add or update, from overlay tree
 * @is_symbols_prop:	1 if @overlay_prop is from node "/__symbols__"
 *
 * If @overlay_prop does not already exist in live devicetree, add changeset
 * entry to add @overlay_prop in @target, else add changeset entry to update
 * value of @overlay_prop.
 *
 * @target may be either in the live devicetree or in a new subtree that
 * is contained in the changeset.
 *
 * Some special properties are not added or updated (no error returned):
 * "name", "phandle", "linux,phandle".
 *
 * Properties "#address-cells" and "#size-cells" are not updated if they
 * are already in the live tree, but if present in the live tree, the values
 * in the overlay must match the values in the live tree.
 *
 * Update of property in symbols node is not allowed.
 *
 * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
 * invalid @overlay.
 */
static int add_changeset_property(struct overlay_changeset *ovcs,
		struct target *target, struct property *overlay_prop,
		bool is_symbols_prop)
{}

/**
 * add_changeset_node() - add @node (and children) to overlay changeset
 * @ovcs:	overlay changeset
 * @target:	where @node will be placed in live tree or changeset
 * @node:	node from within overlay device tree fragment
 *
 * If @node does not already exist in @target, add changeset entry
 * to add @node in @target.
 *
 * If @node already exists in @target, and the existing node has
 * a phandle, the overlay node is not allowed to have a phandle.
 *
 * If @node has child nodes, add the children recursively via
 * build_changeset_next_level().
 *
 * NOTE_1: A live devicetree created from a flattened device tree (FDT) will
 *       not contain the full path in node->full_name.  Thus an overlay
 *       created from an FDT also will not contain the full path in
 *       node->full_name.  However, a live devicetree created from Open
 *       Firmware may have the full path in node->full_name.
 *
 *       add_changeset_node() follows the FDT convention and does not include
 *       the full path in node->full_name.  Even though it expects the overlay
 *       to not contain the full path, it uses kbasename() to remove the
 *       full path should it exist.  It also uses kbasename() in comparisons
 *       to nodes in the live devicetree so that it can apply an overlay to
 *       a live devicetree created from Open Firmware.
 *
 * NOTE_2: Multiple mods of created nodes not supported.
 *
 * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
 * invalid @overlay.
 */
static int add_changeset_node(struct overlay_changeset *ovcs,
		struct target *target, struct device_node *node)
{}

/**
 * build_changeset_next_level() - add level of overlay changeset
 * @ovcs:		overlay changeset
 * @target:		where to place @overlay_node in live tree
 * @overlay_node:	node from within an overlay device tree fragment
 *
 * Add the properties (if any) and nodes (if any) from @overlay_node to the
 * @ovcs->cset changeset.  If an added node has child nodes, they will
 * be added recursively.
 *
 * Do not allow symbols node to have any children.
 *
 * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
 * invalid @overlay_node.
 */
static int build_changeset_next_level(struct overlay_changeset *ovcs,
		struct target *target, const struct device_node *overlay_node)
{}

/*
 * Add the properties from __overlay__ node to the @ovcs->cset changeset.
 */
static int build_changeset_symbols_node(struct overlay_changeset *ovcs,
		struct target *target,
		const struct device_node *overlay_symbols_node)
{}

static int find_dup_cset_node_entry(struct overlay_changeset *ovcs,
		struct of_changeset_entry *ce_1)
{}

static int find_dup_cset_prop(struct overlay_changeset *ovcs,
		struct of_changeset_entry *ce_1)
{}

/**
 * changeset_dup_entry_check() - check for duplicate entries
 * @ovcs:	Overlay changeset
 *
 * Check changeset @ovcs->cset for multiple {add or delete} node entries for
 * the same node or duplicate {add, delete, or update} properties entries
 * for the same property.
 *
 * Return: 0 on success, or -EINVAL if duplicate changeset entry found.
 */
static int changeset_dup_entry_check(struct overlay_changeset *ovcs)
{}

/**
 * build_changeset() - populate overlay changeset in @ovcs from @ovcs->fragments
 * @ovcs:	Overlay changeset
 *
 * Create changeset @ovcs->cset to contain the nodes and properties of the
 * overlay device tree fragments in @ovcs->fragments[].  If an error occurs,
 * any portions of the changeset that were successfully created will remain
 * in @ovcs->cset.
 *
 * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
 * invalid overlay in @ovcs->fragments[].
 */
static int build_changeset(struct overlay_changeset *ovcs)
{}

/*
 * Find the target node using a number of different strategies
 * in order of preference:
 *
 * 1) "target" property containing the phandle of the target
 * 2) "target-path" property containing the path of the target
 */
static struct device_node *find_target(struct device_node *info_node,
				       struct device_node *target_base)
{}

/**
 * init_overlay_changeset() - initialize overlay changeset from overlay tree
 * @ovcs:		Overlay changeset to build
 * @target_base:	Point to the target node to apply overlay
 *
 * Initialize @ovcs.  Populate @ovcs->fragments with node information from
 * the top level of @overlay_root.  The relevant top level nodes are the
 * fragment nodes and the __symbols__ node.  Any other top level node will
 * be ignored.  Populate other @ovcs fields.
 *
 * Return: 0 on success, -ENOMEM if memory allocation failure, -EINVAL if error
 * detected in @overlay_root.  On error return, the caller of
 * init_overlay_changeset() must call free_overlay_changeset().
 */
static int init_overlay_changeset(struct overlay_changeset *ovcs,
				  struct device_node *target_base)
{}

static void free_overlay_changeset(struct overlay_changeset *ovcs)
{}

/*
 * internal documentation
 *
 * of_overlay_apply() - Create and apply an overlay changeset
 * @ovcs:	overlay changeset
 * @base:	point to the target node to apply overlay
 *
 * Creates and applies an overlay changeset.
 *
 * If an error is returned by an overlay changeset pre-apply notifier
 * then no further overlay changeset pre-apply notifier will be called.
 *
 * If an error is returned by an overlay changeset post-apply notifier
 * then no further overlay changeset post-apply notifier will be called.
 *
 * If more than one notifier returns an error, then the last notifier
 * error to occur is returned.
 *
 * If an error occurred while applying the overlay changeset, then an
 * attempt is made to revert any changes that were made to the
 * device tree.  If there were any errors during the revert attempt
 * then the state of the device tree can not be determined, and any
 * following attempt to apply or remove an overlay changeset will be
 * refused.
 *
 * Returns 0 on success, or a negative error number.  On error return,
 * the caller of of_overlay_apply() must call free_overlay_changeset().
 */

static int of_overlay_apply(struct overlay_changeset *ovcs,
			    struct device_node *base)
{}

/**
 * of_overlay_fdt_apply() - Create and apply an overlay changeset
 * @overlay_fdt:	pointer to overlay FDT
 * @overlay_fdt_size:	number of bytes in @overlay_fdt
 * @ret_ovcs_id:	pointer for returning created changeset id
 * @base:		pointer for the target node to apply overlay
 *
 * Creates and applies an overlay changeset.
 *
 * See of_overlay_apply() for important behavior information.
 *
 * Return: 0 on success, or a negative error number.  *@ret_ovcs_id is set to
 * the value of overlay changeset id, which can be passed to of_overlay_remove()
 * to remove the overlay.
 *
 * On error return, the changeset may be partially applied.  This is especially
 * likely if an OF_OVERLAY_POST_APPLY notifier returns an error.  In this case
 * the caller should call of_overlay_remove() with the value in *@ret_ovcs_id.
 */

int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
			 int *ret_ovcs_id, struct device_node *base)
{}
EXPORT_SYMBOL_GPL();

/*
 * Find @np in @tree.
 *
 * Returns 1 if @np is @tree or is contained in @tree, else 0
 */
static int find_node(struct device_node *tree, struct device_node *np)
{}

/*
 * Is @remove_ce_node a child of, a parent of, or the same as any
 * node in an overlay changeset more topmost than @remove_ovcs?
 *
 * Returns 1 if found, else 0
 */
static int node_overlaps_later_cs(struct overlay_changeset *remove_ovcs,
		struct device_node *remove_ce_node)
{}

/*
 * We can safely remove the overlay only if it's the top-most one.
 * Newly applied overlays are inserted at the tail of the overlay list,
 * so a top most overlay is the one that is closest to the tail.
 *
 * The topmost check is done by exploiting this property. For each
 * affected device node in the log list we check if this overlay is
 * the one closest to the tail. If another overlay has affected this
 * device node and is closest to the tail, then removal is not permitted.
 */
static int overlay_removal_is_ok(struct overlay_changeset *remove_ovcs)
{}

/**
 * of_overlay_remove() - Revert and free an overlay changeset
 * @ovcs_id:	Pointer to overlay changeset id
 *
 * Removes an overlay if it is permissible.  @ovcs_id was previously returned
 * by of_overlay_fdt_apply().
 *
 * If an error occurred while attempting to revert the overlay changeset,
 * then an attempt is made to re-apply any changeset entry that was
 * reverted.  If an error occurs on re-apply then the state of the device
 * tree can not be determined, and any following attempt to apply or remove
 * an overlay changeset will be refused.
 *
 * A non-zero return value will not revert the changeset if error is from:
 *   - parameter checks
 *   - overlay changeset pre-remove notifier
 *   - overlay changeset entry revert
 *
 * If an error is returned by an overlay changeset pre-remove notifier
 * then no further overlay changeset pre-remove notifier will be called.
 *
 * If more than one notifier returns an error, then the last notifier
 * error to occur is returned.
 *
 * A non-zero return value will revert the changeset if error is from:
 *   - overlay changeset entry notifier
 *   - overlay changeset post-remove notifier
 *
 * If an error is returned by an overlay changeset post-remove notifier
 * then no further overlay changeset post-remove notifier will be called.
 *
 * Return: 0 on success, or a negative error number.  *@ovcs_id is set to
 * zero after reverting the changeset, even if a subsequent error occurs.
 */
int of_overlay_remove(int *ovcs_id)
{}
EXPORT_SYMBOL_GPL();

/**
 * of_overlay_remove_all() - Reverts and frees all overlay changesets
 *
 * Removes all overlays from the system in the correct order.
 *
 * Return: 0 on success, or a negative error number
 */
int of_overlay_remove_all(void)
{}
EXPORT_SYMBOL_GPL();