2014-10-28 23:35:58 +03:00
/*
* Functions for working with device tree overlays
*
* Copyright ( C ) 2012 Pantelis Antoniou < panto @ antoniou - consulting . com >
* Copyright ( C ) 2012 Texas Instruments Inc .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*/
2016-06-15 16:32:18 +03:00
# define pr_fmt(fmt) "OF: overlay: " fmt
2014-10-28 23:35:58 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/string.h>
# include <linux/ctype.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/err.h>
2015-02-17 05:36:58 +03:00
# include <linux/idr.h>
2014-10-28 23:35:58 +03:00
# include "of_private.h"
/**
2017-10-18 02:36:23 +03:00
* struct fragment - info about fragment nodes in overlay expanded device tree
2014-10-28 23:35:58 +03:00
* @ target : target of the overlay operation
2017-10-18 02:36:23 +03:00
* @ overlay : pointer to the __overlay__ node
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:23 +03:00
struct fragment {
2014-10-28 23:35:58 +03:00
struct device_node * target ;
struct device_node * overlay ;
} ;
/**
2017-10-18 02:36:23 +03:00
* struct overlay_changeset
2017-10-18 02:36:30 +03:00
* @ ovcs_list : list on which we are located
2017-10-18 02:36:31 +03:00
* @ overlay_tree : expanded device tree that contains the fragment nodes
2017-10-18 02:36:30 +03:00
* @ 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
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:23 +03:00
struct overlay_changeset {
2014-10-28 23:35:58 +03:00
int id ;
2017-10-18 02:36:23 +03:00
struct list_head ovcs_list ;
2017-10-18 02:36:31 +03:00
struct device_node * overlay_tree ;
2014-10-28 23:35:58 +03:00
int count ;
2017-10-18 02:36:23 +03:00
struct fragment * fragments ;
2017-10-18 02:36:30 +03:00
bool symbols_fragment ;
2014-10-28 23:35:58 +03:00
struct of_changeset cset ;
} ;
2017-10-18 02:36:26 +03:00
/* 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 ) ;
}
2017-10-18 02:36:23 +03:00
static int build_changeset_next_level ( struct overlay_changeset * ovcs ,
struct device_node * target_node ,
2017-10-18 02:36:30 +03:00
const struct device_node * overlay_node ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:29 +03:00
/*
* 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 ) ;
}
2017-10-18 02:36:25 +03:00
static LIST_HEAD ( ovcs_list ) ;
static DEFINE_IDR ( ovcs_idr ) ;
2017-10-18 02:36:23 +03:00
static BLOCKING_NOTIFIER_HEAD ( overlay_notify_chain ) ;
2016-11-01 22:14:22 +03:00
int of_overlay_notifier_register ( struct notifier_block * nb )
{
2017-10-18 02:36:23 +03:00
return blocking_notifier_chain_register ( & overlay_notify_chain , nb ) ;
2016-11-01 22:14:22 +03:00
}
EXPORT_SYMBOL_GPL ( of_overlay_notifier_register ) ;
int of_overlay_notifier_unregister ( struct notifier_block * nb )
{
2017-10-18 02:36:23 +03:00
return blocking_notifier_chain_unregister ( & overlay_notify_chain , nb ) ;
2016-11-01 22:14:22 +03:00
}
EXPORT_SYMBOL_GPL ( of_overlay_notifier_unregister ) ;
2017-10-18 02:36:26 +03:00
static char * of_overlay_action_name [ ] = {
" pre-apply " ,
" post-apply " ,
" pre-remove " ,
" post-remove " ,
} ;
2017-10-18 02:36:23 +03:00
static int overlay_notify ( struct overlay_changeset * ovcs ,
enum of_overlay_notify_action action )
2016-11-01 22:14:22 +03:00
{
struct of_overlay_notify_data nd ;
int i , ret ;
2017-10-18 02:36:23 +03:00
for ( i = 0 ; i < ovcs - > count ; i + + ) {
struct fragment * fragment = & ovcs - > fragments [ i ] ;
2016-11-01 22:14:22 +03:00
2017-10-18 02:36:23 +03:00
nd . target = fragment - > target ;
nd . overlay = fragment - > overlay ;
2016-11-01 22:14:22 +03:00
2017-10-18 02:36:23 +03:00
ret = blocking_notifier_call_chain ( & overlay_notify_chain ,
2016-11-01 22:14:22 +03:00
action , & nd ) ;
2017-10-20 00:18:27 +03:00
if ( ret = = NOTIFY_OK | | ret = = NOTIFY_STOP )
2017-10-18 02:36:26 +03:00
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 ;
}
2016-11-01 22:14:22 +03:00
}
return 0 ;
}
2017-10-18 02:36:24 +03:00
/*
2017-10-18 02:36:31 +03:00
* 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 .
2017-10-18 02:36:24 +03:00
*
2017-10-18 02:36:31 +03:00
* 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 .
2017-10-18 02:36:24 +03:00
*
* 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 .
*/
2017-10-18 02:36:23 +03:00
static struct property * dup_and_fixup_symbol_prop (
struct overlay_changeset * ovcs , const struct property * prop )
2017-07-19 19:25:22 +03:00
{
2017-10-18 02:36:23 +03:00
struct fragment * fragment ;
2017-10-18 02:36:31 +03:00
struct property * new_prop ;
struct device_node * fragment_node ;
struct device_node * overlay_node ;
const char * path ;
const char * path_tail ;
2017-07-19 19:25:22 +03:00
const char * target_path ;
int k ;
int overlay_name_len ;
2017-10-18 02:36:31 +03:00
int path_len ;
int path_tail_len ;
2017-07-19 19:25:22 +03:00
int target_path_len ;
if ( ! prop - > value )
return NULL ;
2017-10-18 02:36:31 +03:00
if ( strnlen ( prop - > value , prop - > length ) > = prop - > length )
return NULL ;
path = prop - > value ;
path_len = strlen ( path ) ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
if ( path_len < 1 )
2017-07-19 19:25:22 +03:00
return NULL ;
2017-10-18 02:36:31 +03:00
fragment_node = __of_find_node_by_path ( ovcs - > overlay_tree , path + 1 ) ;
overlay_node = __of_find_node_by_path ( fragment_node , " __overlay__/ " ) ;
of_node_put ( fragment_node ) ;
of_node_put ( overlay_node ) ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:23 +03:00
for ( k = 0 ; k < ovcs - > count ; k + + ) {
fragment = & ovcs - > fragments [ k ] ;
2017-10-18 02:36:31 +03:00
if ( fragment - > overlay = = overlay_node )
2017-07-19 19:25:22 +03:00
break ;
}
2017-10-18 02:36:23 +03:00
if ( k > = ovcs - > count )
2017-10-18 02:36:31 +03:00
return NULL ;
overlay_name_len = snprintf ( NULL , 0 , " %pOF " , fragment - > overlay ) ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
if ( overlay_name_len > path_len )
return NULL ;
path_tail = path + overlay_name_len ;
path_tail_len = strlen ( path_tail ) ;
target_path = kasprintf ( GFP_KERNEL , " %pOF " , fragment - > target ) ;
if ( ! target_path )
return NULL ;
2017-07-19 19:25:22 +03:00
target_path_len = strlen ( target_path ) ;
2017-10-18 02:36:31 +03:00
new_prop = kzalloc ( sizeof ( * new_prop ) , GFP_KERNEL ) ;
if ( ! new_prop )
goto err_free_target_path ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
new_prop - > name = kstrdup ( prop - > name , GFP_KERNEL ) ;
new_prop - > length = target_path_len + path_tail_len + 1 ;
new_prop - > value = kzalloc ( new_prop - > length , GFP_KERNEL ) ;
if ( ! new_prop - > name | | ! new_prop - > value )
goto err_free_new_prop ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
strcpy ( new_prop - > value , target_path ) ;
strcpy ( new_prop - > value + target_path_len , path_tail ) ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
of_property_set_flag ( new_prop , OF_DYNAMIC ) ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
return new_prop ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:31 +03:00
err_free_new_prop :
kfree ( new_prop - > name ) ;
kfree ( new_prop - > value ) ;
kfree ( new_prop ) ;
err_free_target_path :
kfree ( target_path ) ;
2017-07-19 19:25:22 +03:00
return NULL ;
}
2017-10-18 02:36:23 +03:00
/**
* add_changeset_property ( ) - add @ overlay_prop to overlay changeset
* @ ovcs : overlay changeset
* @ target_node : where to place @ overlay_prop in live tree
* @ overlay_prop : property to add or update , from overlay tree
2017-10-18 02:36:30 +03:00
* @ is_symbols_prop : 1 if @ overlay_prop is from node " /__symbols__ "
2017-10-18 02:36:23 +03:00
*
* If @ overlay_prop does not already exist in @ target_node , add changeset entry
* to add @ overlay_prop in @ target_node , else add changeset entry to update
* value of @ overlay_prop .
*
2017-10-18 02:36:21 +03:00
* Some special properties are not updated ( no error returned ) .
2017-10-18 02:36:23 +03:00
*
2017-10-18 02:36:21 +03:00
* Update of property in symbols node is not allowed .
2017-10-18 02:36:23 +03:00
*
* Returns 0 on success , - ENOMEM if memory allocation failure , or - EINVAL if
* invalid @ overlay .
2017-10-18 02:36:21 +03:00
*/
2017-10-18 02:36:23 +03:00
static int add_changeset_property ( struct overlay_changeset * ovcs ,
struct device_node * target_node ,
struct property * overlay_prop ,
2017-10-18 02:36:30 +03:00
bool is_symbols_prop )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
struct property * new_prop = NULL , * prop ;
2017-10-16 12:54:32 +03:00
int ret = 0 ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
prop = of_find_property ( target_node , overlay_prop - > name , NULL ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
if ( ! of_prop_cmp ( overlay_prop - > name , " name " ) | |
! of_prop_cmp ( overlay_prop - > name , " phandle " ) | |
! of_prop_cmp ( overlay_prop - > name , " linux,phandle " ) )
2014-10-28 23:35:58 +03:00
return 0 ;
2017-10-18 02:36:30 +03:00
if ( is_symbols_prop ) {
2017-10-18 02:36:23 +03:00
if ( prop )
2017-07-19 19:25:22 +03:00
return - EINVAL ;
2017-10-18 02:36:23 +03:00
new_prop = dup_and_fixup_symbol_prop ( ovcs , overlay_prop ) ;
2017-07-19 19:25:22 +03:00
} else {
2017-10-18 02:36:23 +03:00
new_prop = __of_prop_dup ( overlay_prop , GFP_KERNEL ) ;
2017-07-19 19:25:22 +03:00
}
2017-10-18 02:36:23 +03:00
if ( ! new_prop )
2014-10-28 23:35:58 +03:00
return - ENOMEM ;
2017-10-18 02:36:23 +03:00
if ( ! prop )
ret = of_changeset_add_property ( & ovcs - > cset , target_node ,
new_prop ) ;
2017-10-18 02:36:21 +03:00
else
2017-10-18 02:36:23 +03:00
ret = of_changeset_update_property ( & ovcs - > cset , target_node ,
new_prop ) ;
2017-10-16 12:54:32 +03:00
if ( ret ) {
2017-10-18 02:36:23 +03:00
kfree ( new_prop - > name ) ;
kfree ( new_prop - > value ) ;
kfree ( new_prop ) ;
2017-10-16 12:54:32 +03:00
}
return ret ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:23 +03:00
/**
* add_changeset_node ( ) - add @ node ( and children ) to overlay changeset
* @ ovcs : overlay changeset
* @ target_node : where to place @ node in live tree
* @ node : node from within overlay device tree fragment
*
* If @ node does not already exist in @ target_node , add changeset entry
* to add @ node in @ target_node .
*
* If @ node already exists in @ target_node , 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 : Multiple mods of created nodes not supported .
2017-10-18 02:36:26 +03:00
* If more than one fragment contains a node that does not already exist
* in the live tree , then for each fragment of_changeset_attach_node ( )
* will add a changeset entry to add the node . When the changeset is
* applied , __of_attach_node ( ) will attach the node twice ( once for
* each fragment ) . At this point the device tree will be corrupted .
*
* TODO : add integrity check to ensure that multiple fragments do not
* create the same node .
2017-10-18 02:36:23 +03:00
*
* Returns 0 on success , - ENOMEM if memory allocation failure , or - EINVAL if
* invalid @ overlay .
*/
static int add_changeset_node ( struct overlay_changeset * ovcs ,
struct device_node * target_node , struct device_node * node )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
const char * node_kbasename ;
2015-03-03 16:04:45 +03:00
struct device_node * tchild ;
2014-10-28 23:35:58 +03:00
int ret = 0 ;
2017-10-18 02:36:23 +03:00
node_kbasename = kbasename ( node - > full_name ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
for_each_child_of_node ( target_node , tchild )
if ( ! of_node_cmp ( node_kbasename , kbasename ( tchild - > full_name ) ) )
2017-07-19 19:25:21 +03:00
break ;
2017-10-18 02:36:25 +03:00
if ( ! tchild ) {
2017-10-18 02:36:23 +03:00
tchild = __of_node_dup ( node , " %pOF/%s " ,
target_node , node_kbasename ) ;
2014-10-28 23:35:58 +03:00
if ( ! tchild )
return - ENOMEM ;
2017-10-18 02:36:23 +03:00
tchild - > parent = target_node ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
ret = of_changeset_attach_node ( & ovcs - > cset , tchild ) ;
2014-10-28 23:35:58 +03:00
if ( ret )
return ret ;
2017-10-18 02:36:30 +03:00
return build_changeset_next_level ( ovcs , tchild , node ) ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:28 +03:00
if ( node - > phandle & & tchild - > phandle )
ret = - EINVAL ;
else
2017-10-18 02:36:30 +03:00
ret = build_changeset_next_level ( ovcs , tchild , node ) ;
2017-10-18 02:36:25 +03:00
of_node_put ( tchild ) ;
2014-10-28 23:35:58 +03:00
return ret ;
}
2017-10-18 02:36:23 +03:00
/**
* build_changeset_next_level ( ) - add level of overlay changeset
* @ ovcs : overlay changeset
* @ target_node : where to place @ overlay_node in live tree
* @ overlay_node : node from within an overlay device tree fragment
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:23 +03:00
* 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 .
2017-10-18 02:36:21 +03:00
*
* Do not allow symbols node to have any children .
2017-10-18 02:36:23 +03:00
*
* Returns 0 on success , - ENOMEM if memory allocation failure , or - EINVAL if
* invalid @ overlay_node .
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:23 +03:00
static int build_changeset_next_level ( struct overlay_changeset * ovcs ,
struct device_node * target_node ,
2017-10-18 02:36:30 +03:00
const struct device_node * overlay_node )
2014-10-28 23:35:58 +03:00
{
struct device_node * child ;
struct property * prop ;
int ret ;
2017-10-18 02:36:23 +03:00
for_each_property_of_node ( overlay_node , prop ) {
2017-10-18 02:36:30 +03:00
ret = add_changeset_property ( ovcs , target_node , prop , 0 ) ;
2014-10-28 23:35:58 +03:00
if ( ret ) {
2017-10-18 02:36:26 +03:00
pr_debug ( " Failed to apply prop @%pOF/%s, err=%d \n " ,
target_node , prop - > name , ret ) ;
2014-10-28 23:35:58 +03:00
return ret ;
}
}
2017-10-18 02:36:23 +03:00
for_each_child_of_node ( overlay_node , child ) {
ret = add_changeset_node ( ovcs , target_node , child ) ;
2017-10-18 02:36:22 +03:00
if ( ret ) {
2017-10-18 02:36:26 +03:00
pr_debug ( " Failed to apply node @%pOF/%s, err=%d \n " ,
target_node , child - > name , ret ) ;
2015-10-22 12:02:48 +03:00
of_node_put ( child ) ;
2014-10-28 23:35:58 +03:00
return ret ;
}
}
return 0 ;
}
2017-10-18 02:36:30 +03:00
/*
* Add the properties from __overlay__ node to the @ ovcs - > cset changeset .
*/
static int build_changeset_symbols_node ( struct overlay_changeset * ovcs ,
struct device_node * target_node ,
const struct device_node * overlay_symbols_node )
{
struct property * prop ;
int ret ;
for_each_property_of_node ( overlay_symbols_node , prop ) {
ret = add_changeset_property ( ovcs , target_node , prop , 1 ) ;
if ( ret ) {
pr_debug ( " Failed to apply prop @%pOF/%s, err=%d \n " ,
target_node , prop - > name , ret ) ;
return ret ;
}
}
return 0 ;
}
2014-10-28 23:35:58 +03:00
/**
2017-10-18 02:36:23 +03:00
* build_changeset ( ) - populate overlay changeset in @ ovcs from @ ovcs - > fragments
* @ ovcs : Overlay changeset
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:23 +03:00
* 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 .
*
* Returns 0 on success , - ENOMEM if memory allocation failure , or - EINVAL if
* invalid overlay in @ ovcs - > fragments [ ] .
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:23 +03:00
static int build_changeset ( struct overlay_changeset * ovcs )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:30 +03:00
struct fragment * fragment ;
int fragments_count , i , ret ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:30 +03:00
/*
* if there is a symbols fragment in ovcs - > fragments [ i ] it is
* the final element in the array
*/
if ( ovcs - > symbols_fragment )
fragments_count = ovcs - > count - 1 ;
else
fragments_count = ovcs - > count ;
for ( i = 0 ; i < fragments_count ; i + + ) {
fragment = & ovcs - > fragments [ i ] ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
ret = build_changeset_next_level ( ovcs , fragment - > target ,
2017-10-18 02:36:30 +03:00
fragment - > overlay ) ;
if ( ret ) {
pr_debug ( " apply failed '%pOF' \n " , fragment - > target ) ;
return ret ;
}
}
if ( ovcs - > symbols_fragment ) {
fragment = & ovcs - > fragments [ ovcs - > count - 1 ] ;
ret = build_changeset_symbols_node ( ovcs , fragment - > target ,
fragment - > overlay ) ;
2017-10-18 02:36:23 +03:00
if ( ret ) {
2017-10-18 02:36:26 +03:00
pr_debug ( " apply failed '%pOF' \n " , fragment - > target ) ;
2017-10-18 02:36:23 +03:00
return ret ;
2014-10-28 23:35:58 +03:00
}
}
return 0 ;
}
/*
* Find the target node using a number of different strategies
2017-10-18 02:36:21 +03:00
* in order of preference :
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:21 +03:00
* 1 ) " target " property containing the phandle of the target
* 2 ) " target-path " property containing the path of the target
2014-10-28 23:35:58 +03:00
*/
static struct device_node * find_target_node ( struct device_node * info_node )
{
const char * path ;
u32 val ;
int ret ;
ret = of_property_read_u32 ( info_node , " target " , & val ) ;
2017-10-18 02:36:22 +03:00
if ( ! ret )
2014-10-28 23:35:58 +03:00
return of_find_node_by_phandle ( val ) ;
ret = of_property_read_string ( info_node , " target-path " , & path ) ;
2017-10-18 02:36:22 +03:00
if ( ! ret )
2014-10-28 23:35:58 +03:00
return of_find_node_by_path ( path ) ;
2016-06-15 16:32:18 +03:00
pr_err ( " Failed to find target for node %p (%s) \n " ,
2014-10-28 23:35:58 +03:00
info_node , info_node - > name ) ;
return NULL ;
}
/**
2017-10-18 02:36:23 +03:00
* init_overlay_changeset ( ) - initialize overlay changeset from overlay tree
* @ ovcs Overlay changeset to build
* @ tree : Contains all the overlay fragments and overlay fixup nodes
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:23 +03:00
* Initialize @ ovcs . Populate @ ovcs - > fragments with node information from
* the top level of @ tree . The relevant top level nodes are the fragment
* nodes and the __symbols__ node . Any other top level node will be ignored .
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:23 +03:00
* Returns 0 on success , - ENOMEM if memory allocation failure , - EINVAL if error
2017-10-18 02:36:25 +03:00
* detected in @ tree , or - ENOSPC if idr_alloc ( ) error .
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:23 +03:00
static int init_overlay_changeset ( struct overlay_changeset * ovcs ,
2014-10-28 23:35:58 +03:00
struct device_node * tree )
{
2017-10-18 02:36:25 +03:00
struct device_node * node , * overlay_node ;
2017-10-18 02:36:23 +03:00
struct fragment * fragment ;
struct fragment * fragments ;
int cnt , ret ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:26 +03:00
/*
* Warn for some issues . Can not return - EINVAL for these until
* of_unittest_apply_overlay ( ) is fixed to pass these checks .
*/
if ( ! of_node_check_flag ( tree , OF_DYNAMIC ) )
pr_debug ( " %s() tree is not dynamic \n " , __func__ ) ;
if ( ! of_node_check_flag ( tree , OF_DETACHED ) )
pr_debug ( " %s() tree is not detached \n " , __func__ ) ;
if ( ! of_node_is_root ( tree ) )
pr_debug ( " %s() tree is not root \n " , __func__ ) ;
2017-10-18 02:36:31 +03:00
ovcs - > overlay_tree = tree ;
2017-10-18 02:36:25 +03:00
INIT_LIST_HEAD ( & ovcs - > ovcs_list ) ;
of_changeset_init ( & ovcs - > cset ) ;
ovcs - > id = idr_alloc ( & ovcs_idr , ovcs , 1 , 0 , GFP_KERNEL ) ;
if ( ovcs - > id < = 0 )
return ovcs - > id ;
2014-10-28 23:35:58 +03:00
cnt = 0 ;
2017-10-18 02:36:25 +03:00
/* fragment nodes */
for_each_child_of_node ( tree , node ) {
overlay_node = of_get_child_by_name ( node , " __overlay__ " ) ;
if ( overlay_node ) {
cnt + + ;
of_node_put ( overlay_node ) ;
}
}
node = of_get_child_by_name ( tree , " __symbols__ " ) ;
if ( node ) {
2017-07-19 19:25:22 +03:00
cnt + + ;
2017-10-18 02:36:25 +03:00
of_node_put ( node ) ;
}
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:23 +03:00
fragments = kcalloc ( cnt , sizeof ( * fragments ) , GFP_KERNEL ) ;
2017-10-18 02:36:25 +03:00
if ( ! fragments ) {
ret = - ENOMEM ;
goto err_free_idr ;
}
2014-10-28 23:35:58 +03:00
cnt = 0 ;
for_each_child_of_node ( tree , node ) {
2017-10-18 02:36:25 +03:00
fragment = & fragments [ cnt ] ;
fragment - > overlay = of_get_child_by_name ( node , " __overlay__ " ) ;
if ( fragment - > overlay ) {
fragment - > target = find_target_node ( node ) ;
if ( ! fragment - > target ) {
of_node_put ( fragment - > overlay ) ;
ret = - EINVAL ;
goto err_free_fragments ;
} else {
cnt + + ;
}
}
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:30 +03:00
/*
* if there is a symbols fragment in ovcs - > fragments [ i ] it is
* the final element in the array
*/
2017-07-19 19:25:22 +03:00
node = of_get_child_by_name ( tree , " __symbols__ " ) ;
if ( node ) {
2017-10-18 02:36:30 +03:00
ovcs - > symbols_fragment = 1 ;
2017-10-18 02:36:23 +03:00
fragment = & fragments [ cnt ] ;
fragment - > overlay = node ;
fragment - > target = of_find_node_by_path ( " /__symbols__ " ) ;
2017-07-19 19:25:22 +03:00
2017-10-18 02:36:23 +03:00
if ( ! fragment - > target ) {
2017-10-20 00:38:11 +03:00
pr_err ( " symbols in overlay, but not in live tree \n " ) ;
2017-10-18 02:36:25 +03:00
ret = - EINVAL ;
goto err_free_fragments ;
2017-07-19 19:25:22 +03:00
}
cnt + + ;
}
2017-10-18 02:36:22 +03:00
if ( ! cnt ) {
2017-10-18 02:36:25 +03:00
ret = - EINVAL ;
goto err_free_fragments ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:23 +03:00
ovcs - > count = cnt ;
ovcs - > fragments = fragments ;
2014-10-28 23:35:58 +03:00
return 0 ;
2017-10-18 02:36:25 +03:00
err_free_fragments :
kfree ( fragments ) ;
err_free_idr :
idr_remove ( & ovcs_idr , ovcs - > id ) ;
2017-10-18 02:36:26 +03:00
pr_err ( " %s() failed, ret = %d \n " , __func__ , ret ) ;
2017-10-18 02:36:25 +03:00
return ret ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:25 +03:00
static void free_overlay_changeset ( struct overlay_changeset * ovcs )
2014-10-28 23:35:58 +03:00
{
int i ;
2017-10-18 02:36:25 +03:00
if ( ! ovcs - > cset . entries . next )
return ;
of_changeset_destroy ( & ovcs - > cset ) ;
if ( ovcs - > id )
idr_remove ( & ovcs_idr , ovcs - > id ) ;
for ( i = 0 ; i < ovcs - > count ; i + + ) {
2017-10-18 02:36:23 +03:00
of_node_put ( ovcs - > fragments [ i ] . target ) ;
of_node_put ( ovcs - > fragments [ i ] . overlay ) ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:23 +03:00
kfree ( ovcs - > fragments ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:25 +03:00
kfree ( ovcs ) ;
}
2014-10-28 23:35:58 +03:00
/**
2017-10-18 02:36:23 +03:00
* of_overlay_apply ( ) - Create and apply an overlay changeset
* @ tree : Expanded overlay device tree
2017-10-18 02:36:26 +03:00
* @ ovcs_id : Pointer to overlay changeset id
*
* Creates and applies an overlay changeset .
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:26 +03:00
* If an error occurs in a pre - apply notifier , then no changes are made
* to the device tree .
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:26 +03:00
* A non - zero return value will not have created the changeset if error is from :
* - parameter checks
* - building the changeset
2017-11-28 11:25:23 +03:00
* - overlay changeset pre - apply notifier
2017-10-18 02:36:26 +03:00
*
* If an error is returned by an overlay changeset pre - apply notifier
* then no further overlay changeset pre - apply notifier will be called .
*
* A non - zero return value will have created the changeset if error is from :
* - overlay changeset entry notifier
2017-11-28 11:25:23 +03:00
* - overlay changeset post - apply notifier
2017-10-18 02:36:26 +03:00
*
* 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 . Overlay changeset
* id is returned to * ovcs_id .
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:26 +03:00
int of_overlay_apply ( struct device_node * tree , int * ovcs_id )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
struct overlay_changeset * ovcs ;
2017-10-18 02:36:26 +03:00
int ret = 0 , ret_revert , ret_tmp ;
* ovcs_id = 0 ;
if ( devicetree_corrupt ( ) ) {
pr_err ( " devicetree state suspect, refuse to apply overlay \n " ) ;
ret = - EBUSY ;
goto out ;
}
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
ovcs = kzalloc ( sizeof ( * ovcs ) , GFP_KERNEL ) ;
2017-10-18 02:36:26 +03:00
if ( ! ovcs ) {
ret = - ENOMEM ;
goto out ;
}
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:29 +03:00
of_overlay_mutex_lock ( ) ;
ret = of_resolve_phandles ( tree ) ;
if ( ret )
goto err_overlay_unlock ;
2014-10-28 23:35:58 +03:00
mutex_lock ( & of_mutex ) ;
2017-10-18 02:36:23 +03:00
ret = init_overlay_changeset ( ovcs , tree ) ;
2017-10-18 02:36:26 +03:00
if ( ret )
2017-10-18 02:36:25 +03:00
goto err_free_overlay_changeset ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
ret = overlay_notify ( ovcs , OF_OVERLAY_PRE_APPLY ) ;
2017-10-18 02:36:26 +03:00
if ( ret ) {
pr_err ( " overlay changeset pre-apply notify error %d \n " , ret ) ;
2017-10-18 02:36:25 +03:00
goto err_free_overlay_changeset ;
2016-11-01 22:14:22 +03:00
}
2017-10-18 02:36:23 +03:00
ret = build_changeset ( ovcs ) ;
if ( ret )
2017-10-18 02:36:25 +03:00
goto err_free_overlay_changeset ;
2016-06-15 16:32:18 +03:00
2017-10-18 02:36:26 +03:00
ret_revert = 0 ;
ret = __of_changeset_apply_entries ( & ovcs - > cset , & ret_revert ) ;
if ( ret ) {
if ( ret_revert ) {
pr_debug ( " overlay changeset revert error %d \n " ,
ret_revert ) ;
devicetree_state_flags | = DTSF_APPLY_FAIL ;
}
2017-10-18 02:36:25 +03:00
goto err_free_overlay_changeset ;
2017-10-18 02:36:26 +03:00
} else {
ret = __of_changeset_apply_notify ( & ovcs - > cset ) ;
if ( ret )
pr_err ( " overlay changeset entry notify error %d \n " ,
ret ) ;
/* fall through */
}
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
list_add_tail ( & ovcs - > ovcs_list , & ovcs_list ) ;
2017-10-18 02:36:26 +03:00
* ovcs_id = ovcs - > id ;
ret_tmp = overlay_notify ( ovcs , OF_OVERLAY_POST_APPLY ) ;
if ( ret_tmp ) {
pr_err ( " overlay changeset post-apply notify error %d \n " ,
ret_tmp ) ;
if ( ! ret )
ret = ret_tmp ;
}
2016-11-01 22:14:22 +03:00
2014-10-28 23:35:58 +03:00
mutex_unlock ( & of_mutex ) ;
2017-10-18 02:36:29 +03:00
of_overlay_mutex_unlock ( ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:26 +03:00
goto out ;
2017-10-18 02:36:25 +03:00
2017-10-18 02:36:29 +03:00
err_overlay_unlock :
of_overlay_mutex_unlock ( ) ;
2017-10-18 02:36:25 +03:00
err_free_overlay_changeset :
free_overlay_changeset ( ovcs ) ;
2014-10-28 23:35:58 +03:00
mutex_unlock ( & of_mutex ) ;
2017-10-18 02:36:26 +03:00
out :
pr_debug ( " %s() err=%d \n " , __func__ , ret ) ;
2017-10-18 02:36:23 +03:00
return ret ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:23 +03:00
EXPORT_SYMBOL_GPL ( of_overlay_apply ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:21 +03:00
/*
2017-10-18 02:36:23 +03:00
* Find @ np in @ tree .
*
* Returns 1 if @ np is @ tree or is contained in @ tree , else 0
2017-10-18 02:36:21 +03:00
*/
2017-10-18 02:36:23 +03:00
static int find_node ( struct device_node * tree , struct device_node * np )
2014-10-28 23:35:58 +03:00
{
struct device_node * child ;
2017-10-18 02:36:23 +03:00
if ( tree = = np )
2014-10-28 23:35:58 +03:00
return 1 ;
for_each_child_of_node ( tree , child ) {
2017-10-18 02:36:23 +03:00
if ( find_node ( child , np ) ) {
2015-10-22 12:02:48 +03:00
of_node_put ( child ) ;
2014-10-28 23:35:58 +03:00
return 1 ;
2015-10-22 12:02:48 +03:00
}
2014-10-28 23:35:58 +03:00
}
return 0 ;
}
2017-10-18 02:36:21 +03:00
/*
2017-10-18 02:36:27 +03:00
* Is @ remove_ce_node a child of , a parent of , or the same as any
2017-10-18 02:36:23 +03:00
* node in an overlay changeset more topmost than @ remove_ovcs ?
*
* Returns 1 if found , else 0
2017-10-18 02:36:21 +03:00
*/
2017-10-18 02:36:27 +03:00
static int node_overlaps_later_cs ( struct overlay_changeset * remove_ovcs ,
struct device_node * remove_ce_node )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
struct overlay_changeset * ovcs ;
2014-10-28 23:35:58 +03:00
struct of_changeset_entry * ce ;
2017-10-18 02:36:23 +03:00
list_for_each_entry_reverse ( ovcs , & ovcs_list , ovcs_list ) {
if ( ovcs = = remove_ovcs )
2014-10-28 23:35:58 +03:00
break ;
2017-10-18 02:36:23 +03:00
list_for_each_entry ( ce , & ovcs - > cset . entries , node ) {
2017-10-18 02:36:27 +03:00
if ( find_node ( ce - > np , remove_ce_node ) ) {
pr_err ( " %s: #%d overlaps with #%d @%pOF \n " ,
2017-10-18 02:36:23 +03:00
__func__ , remove_ovcs - > id , ovcs - > id ,
2017-10-18 02:36:27 +03:00
remove_ce_node ) ;
return 1 ;
}
if ( find_node ( remove_ce_node , ce - > np ) ) {
pr_err ( " %s: #%d overlaps with #%d @%pOF \n " ,
__func__ , remove_ovcs - > id , ovcs - > id ,
remove_ce_node ) ;
2017-10-18 02:36:23 +03:00
return 1 ;
2014-10-28 23:35:58 +03:00
}
}
}
2017-10-18 02:36:23 +03:00
return 0 ;
2014-10-28 23:35:58 +03:00
}
/*
* 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 permited .
*/
2017-10-18 02:36:23 +03:00
static int overlay_removal_is_ok ( struct overlay_changeset * remove_ovcs )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
struct of_changeset_entry * remove_ce ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:23 +03:00
list_for_each_entry ( remove_ce , & remove_ovcs - > cset . entries , node ) {
2017-10-18 02:36:27 +03:00
if ( node_overlaps_later_cs ( remove_ovcs , remove_ce - > np ) ) {
2017-10-18 02:36:23 +03:00
pr_err ( " overlay #%d is not topmost \n " , remove_ovcs - > id ) ;
2014-10-28 23:35:58 +03:00
return 0 ;
}
}
return 1 ;
}
/**
2017-10-18 02:36:23 +03:00
* of_overlay_remove ( ) - Revert and free an overlay changeset
2017-10-18 02:36:26 +03:00
* @ ovcs_id : Pointer to overlay changeset id
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:26 +03:00
* Removes an overlay if it is permissible . @ ovcs_id was previously returned
2017-10-18 02:36:23 +03:00
* by of_overlay_apply ( ) .
2014-10-28 23:35:58 +03:00
*
2017-10-18 02:36:26 +03:00
* 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
2017-11-28 11:25:23 +03:00
* - overlay changeset pre - remove notifier
2017-10-18 02:36:26 +03:00
* - 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
2017-11-28 11:25:23 +03:00
* - overlay changeset post - remove notifier
2017-10-18 02:36:26 +03:00
*
* If an error is returned by an overlay changeset post - remove notifier
* then no further overlay changeset post - remove notifier will be called .
*
* Returns 0 on success , or a negative error number . * ovcs_id is set to
* zero after reverting the changeset , even if a subsequent error occurs .
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:26 +03:00
int of_overlay_remove ( int * ovcs_id )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
struct overlay_changeset * ovcs ;
2017-10-18 02:36:26 +03:00
int ret , ret_apply , ret_tmp ;
ret = 0 ;
if ( devicetree_corrupt ( ) ) {
pr_err ( " suspect devicetree state, refuse to remove overlay \n " ) ;
ret = - EBUSY ;
goto out ;
}
2014-10-28 23:35:58 +03:00
mutex_lock ( & of_mutex ) ;
2017-10-18 02:36:26 +03:00
ovcs = idr_find ( & ovcs_idr , * ovcs_id ) ;
2017-10-18 02:36:23 +03:00
if ( ! ovcs ) {
ret = - ENODEV ;
2017-10-18 02:36:26 +03:00
pr_err ( " remove: Could not find overlay #%d \n " , * ovcs_id ) ;
goto out_unlock ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:23 +03:00
if ( ! overlay_removal_is_ok ( ovcs ) ) {
ret = - EBUSY ;
2017-10-18 02:36:26 +03:00
goto out_unlock ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:26 +03:00
ret = overlay_notify ( ovcs , OF_OVERLAY_PRE_REMOVE ) ;
if ( ret ) {
pr_err ( " overlay changeset pre-remove notify error %d \n " , ret ) ;
goto out_unlock ;
}
2017-10-18 02:36:25 +03:00
2017-10-18 02:36:23 +03:00
list_del ( & ovcs - > ovcs_list ) ;
2017-10-18 02:36:25 +03:00
2017-10-18 02:36:26 +03:00
ret_apply = 0 ;
ret = __of_changeset_revert_entries ( & ovcs - > cset , & ret_apply ) ;
if ( ret ) {
if ( ret_apply )
devicetree_state_flags | = DTSF_REVERT_FAIL ;
goto out_unlock ;
} else {
ret = __of_changeset_revert_notify ( & ovcs - > cset ) ;
if ( ret ) {
pr_err ( " overlay changeset entry notify error %d \n " ,
ret ) ;
/* fall through - changeset was reverted */
}
}
2017-10-18 02:36:25 +03:00
2017-10-18 02:36:26 +03:00
* ovcs_id = 0 ;
ret_tmp = overlay_notify ( ovcs , OF_OVERLAY_POST_REMOVE ) ;
if ( ret_tmp ) {
pr_err ( " overlay changeset post-remove notify error %d \n " ,
ret_tmp ) ;
if ( ! ret )
ret = ret_tmp ;
}
2017-10-18 02:36:25 +03:00
free_overlay_changeset ( ovcs ) ;
2014-10-28 23:35:58 +03:00
2017-10-18 02:36:26 +03:00
out_unlock :
2014-10-28 23:35:58 +03:00
mutex_unlock ( & of_mutex ) ;
2017-10-18 02:36:26 +03:00
out :
pr_debug ( " %s() err=%d \n " , __func__ , ret ) ;
2017-10-18 02:36:23 +03:00
return ret ;
2014-10-28 23:35:58 +03:00
}
2017-10-18 02:36:23 +03:00
EXPORT_SYMBOL_GPL ( of_overlay_remove ) ;
2014-10-28 23:35:58 +03:00
/**
2017-10-18 02:36:23 +03:00
* of_overlay_remove_all ( ) - Reverts and frees all overlay changesets
2014-10-28 23:35:58 +03:00
*
* Removes all overlays from the system in the correct order .
*
2015-05-21 15:10:26 +03:00
* Returns 0 on success , or a negative error number
2014-10-28 23:35:58 +03:00
*/
2017-10-18 02:36:23 +03:00
int of_overlay_remove_all ( void )
2014-10-28 23:35:58 +03:00
{
2017-10-18 02:36:23 +03:00
struct overlay_changeset * ovcs , * ovcs_n ;
2017-10-18 02:36:25 +03:00
int ret ;
2014-10-28 23:35:58 +03:00
/* the tail of list is guaranteed to be safe to remove */
2017-10-18 02:36:23 +03:00
list_for_each_entry_safe_reverse ( ovcs , ovcs_n , & ovcs_list , ovcs_list ) {
2017-10-18 02:36:26 +03:00
ret = of_overlay_remove ( & ovcs - > id ) ;
2017-10-18 02:36:25 +03:00
if ( ret )
return ret ;
2014-10-28 23:35:58 +03:00
}
return 0 ;
}
2017-10-18 02:36:23 +03:00
EXPORT_SYMBOL_GPL ( of_overlay_remove_all ) ;