2017-12-27 21:55:14 +03:00
// SPDX-License-Identifier: GPL-2.0
2009-11-24 00:53:09 +03:00
/*
* Functions for working with the Flattened Device Tree data format
*
* Copyright 2009 Benjamin Herrenschmidt , IBM Corp
* benh @ kernel . crashing . org
*/
2016-10-17 22:21:23 +03:00
# define pr_fmt(fmt) "OF: fdt: " fmt
2016-06-15 16:32:18 +03:00
2021-08-11 11:51:01 +03:00
# include <linux/crash_dump.h>
2014-11-14 20:05:35 +03:00
# include <linux/crc32.h>
2009-11-24 06:07:01 +03:00
# include <linux/kernel.h>
2009-11-24 13:26:58 +03:00
# include <linux/initrd.h>
2013-08-29 00:18:32 +04:00
# include <linux/memblock.h>
2015-12-06 03:13:53 +03:00
# include <linux/mutex.h>
2009-11-24 00:53:09 +03:00
# include <linux/of.h>
# include <linux/of_fdt.h>
2014-02-28 17:42:48 +04:00
# include <linux/of_reserved_mem.h>
2014-02-28 17:42:47 +04:00
# include <linux/sizes.h>
2010-02-14 17:13:47 +03:00
# include <linux/string.h>
# include <linux/errno.h>
2010-11-19 02:55:02 +03:00
# include <linux/slab.h>
2014-04-03 00:10:14 +04:00
# include <linux/libfdt.h>
2014-04-03 01:56:48 +04:00
# include <linux/debugfs.h>
2014-03-27 17:07:01 +04:00
# include <linux/serial_core.h>
2014-11-14 20:05:35 +03:00
# include <linux/sysfs.h>
2019-08-23 09:24:51 +03:00
# include <linux/random.h>
2022-01-15 01:04:08 +03:00
# include <linux/kmemleak.h>
2010-02-02 07:34:14 +03:00
ARM: prom.h: Fix build error by removing unneeded header file
Fix the following build error:
CC [M] fs/udf/balloc.o
In file included from /home/fabio/next/linux-next/arch/arm/include/asm/prom.h:16,
from include/linux/of.h:140,
from include/asm-generic/gpio.h:7,
from arch/arm/plat-mxc/include/mach/irqs.h:14,
from /home/fabio/next/linux-next/arch/arm/include/asm/irq.h:4,
from /home/fabio/next/linux-next/arch/arm/include/asm/hardirq.h:6,
from include/linux/hardirq.h:7,
from include/linux/highmem.h:8,
from include/linux/pagemap.h:10,
from include/linux/buffer_head.h:13,
from fs/udf/udfdecl.h:11,
from fs/udf/balloc.c:22:
/home/fabio/next/linux-next/arch/arm/include/asm/setup.h:146: error: redefinition of 'struct tag'
Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
[grant.likely: fix build failure on drivers/of/fdt.c]
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
2012-01-02 20:19:03 +04:00
# include <asm/setup.h> /* for COMMAND_LINE_SIZE */
2010-02-14 17:13:47 +03:00
# include <asm/page.h>
2017-04-26 03:09:54 +03:00
# include "of_private.h"
2014-07-15 21:03:35 +04:00
/*
* of_fdt_limit_memory - limit the number of regions in the / memory node
* @ limit : maximum entries
*
* Adjust the flattened device tree to have at most ' limit ' number of
* memory entries in the / memory node . This function may be called
* any time after initial_boot_param is set .
*/
2019-05-14 23:40:52 +03:00
void __init of_fdt_limit_memory ( int limit )
2014-07-15 21:03:35 +04:00
{
int memory ;
int len ;
const void * val ;
int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT ;
int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT ;
2017-05-04 20:56:12 +03:00
const __be32 * addr_prop ;
const __be32 * size_prop ;
2014-07-15 21:03:35 +04:00
int root_offset ;
int cell_size ;
root_offset = fdt_path_offset ( initial_boot_params , " / " ) ;
if ( root_offset < 0 )
return ;
addr_prop = fdt_getprop ( initial_boot_params , root_offset ,
" #address-cells " , NULL ) ;
if ( addr_prop )
nr_address_cells = fdt32_to_cpu ( * addr_prop ) ;
size_prop = fdt_getprop ( initial_boot_params , root_offset ,
" #size-cells " , NULL ) ;
if ( size_prop )
nr_size_cells = fdt32_to_cpu ( * size_prop ) ;
cell_size = sizeof ( uint32_t ) * ( nr_address_cells + nr_size_cells ) ;
memory = fdt_path_offset ( initial_boot_params , " /memory " ) ;
if ( memory > 0 ) {
val = fdt_getprop ( initial_boot_params , memory , " reg " , & len ) ;
if ( len > limit * cell_size ) {
len = limit * cell_size ;
pr_debug ( " Limiting number of entries to %d \n " , limit ) ;
fdt_setprop ( initial_boot_params , memory , " reg " , val ,
len ) ;
}
}
}
2017-09-29 03:20:32 +03:00
static bool of_fdt_device_is_available ( const void * blob , unsigned long node )
{
const char * status = fdt_getprop ( blob , node , " status " , NULL ) ;
if ( ! status )
return true ;
if ( ! strcmp ( status , " ok " ) | | ! strcmp ( status , " okay " ) )
return true ;
return false ;
}
2013-08-29 16:30:35 +04:00
static void * unflatten_dt_alloc ( void * * mem , unsigned long size ,
2009-11-24 06:07:00 +03:00
unsigned long align )
{
void * res ;
2013-08-29 16:30:35 +04:00
* mem = PTR_ALIGN ( * mem , align ) ;
res = * mem ;
2009-11-24 06:07:00 +03:00
* mem + = size ;
return res ;
}
2016-05-03 16:22:47 +03:00
static void populate_properties ( const void * blob ,
int offset ,
void * * mem ,
struct device_node * np ,
const char * nodename ,
2014-10-03 19:28:27 +04:00
bool dryrun )
2009-11-24 06:07:00 +03:00
{
2016-05-03 16:22:47 +03:00
struct property * pp , * * pprev = NULL ;
int cur ;
bool has_name = false ;
pprev = & np - > properties ;
for ( cur = fdt_first_property_offset ( blob , offset ) ;
cur > = 0 ;
cur = fdt_next_property_offset ( blob , cur ) ) {
const __be32 * val ;
const char * pname ;
u32 sz ;
val = fdt_getprop_by_offset ( blob , cur , & pname , & sz ) ;
if ( ! val ) {
2016-06-15 16:32:18 +03:00
pr_warn ( " Cannot locate property at 0x%x \n " , cur ) ;
2016-05-03 16:22:47 +03:00
continue ;
}
if ( ! pname ) {
2016-06-15 16:32:18 +03:00
pr_warn ( " Cannot find property name at 0x%x \n " , cur ) ;
2016-05-03 16:22:47 +03:00
continue ;
}
if ( ! strcmp ( pname , " name " ) )
has_name = true ;
pp = unflatten_dt_alloc ( mem , sizeof ( struct property ) ,
__alignof__ ( struct property ) ) ;
if ( dryrun )
continue ;
/* We accept flattened tree phandles either in
* ePAPR - style " phandle " properties , or the
* legacy " linux,phandle " properties . If both
* appear and have different values , things
* will get weird . Don ' t do that .
*/
if ( ! strcmp ( pname , " phandle " ) | |
! strcmp ( pname , " linux,phandle " ) ) {
if ( ! np - > phandle )
np - > phandle = be32_to_cpup ( val ) ;
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff
*/
if ( ! strcmp ( pname , " ibm,phandle " ) )
np - > phandle = be32_to_cpup ( val ) ;
pp - > name = ( char * ) pname ;
pp - > length = sz ;
pp - > value = ( __be32 * ) val ;
* pprev = pp ;
pprev = & pp - > next ;
}
/* With version 0x10 we may not have the name property,
* recreate it here from the unit name if absent
*/
if ( ! has_name ) {
const char * p = nodename , * ps = p , * pa = NULL ;
int len ;
while ( * p ) {
if ( ( * p ) = = ' @ ' )
pa = p ;
else if ( ( * p ) = = ' / ' )
ps = p + 1 ;
p + + ;
}
if ( pa < ps )
pa = p ;
len = ( pa - ps ) + 1 ;
pp = unflatten_dt_alloc ( mem , sizeof ( struct property ) + len ,
__alignof__ ( struct property ) ) ;
if ( ! dryrun ) {
pp - > name = " name " ;
pp - > length = len ;
pp - > value = pp + 1 ;
* pprev = pp ;
memcpy ( pp - > value , ps , len - 1 ) ;
( ( char * ) pp - > value ) [ len - 1 ] = 0 ;
pr_debug ( " fixed up name for %s -> %s \n " ,
nodename , ( char * ) pp - > value ) ;
}
}
}
2021-04-05 06:28:45 +03:00
static int populate_node ( const void * blob ,
2017-06-02 02:01:47 +03:00
int offset ,
void * * mem ,
struct device_node * dad ,
struct device_node * * pnp ,
bool dryrun )
2016-05-03 16:22:47 +03:00
{
2009-11-24 06:07:00 +03:00
struct device_node * np ;
2014-04-03 00:10:14 +04:00
const char * pathp ;
2021-04-05 06:28:45 +03:00
int len ;
2009-11-24 06:07:00 +03:00
2021-04-05 06:28:45 +03:00
pathp = fdt_get_name ( blob , offset , & len ) ;
2016-05-03 16:22:47 +03:00
if ( ! pathp ) {
* pnp = NULL ;
2021-04-05 06:28:45 +03:00
return len ;
2016-05-03 16:22:47 +03:00
}
2014-04-03 00:10:14 +04:00
2021-04-05 06:28:45 +03:00
len + + ;
2009-11-24 06:07:00 +03:00
2021-04-05 06:28:45 +03:00
np = unflatten_dt_alloc ( mem , sizeof ( struct device_node ) + len ,
2009-11-24 06:07:00 +03:00
__alignof__ ( struct device_node ) ) ;
2014-10-03 19:28:27 +04:00
if ( ! dryrun ) {
2012-11-15 02:37:12 +04:00
char * fn ;
2013-12-13 22:08:59 +04:00
of_node_init ( np ) ;
2012-11-15 02:37:12 +04:00
np - > full_name = fn = ( ( char * ) np ) + sizeof ( * np ) ;
2017-06-02 02:01:47 +03:00
2021-04-05 06:28:45 +03:00
memcpy ( fn , pathp , len ) ;
2012-11-15 02:37:12 +04:00
2009-11-24 06:07:00 +03:00
if ( dad ! = NULL ) {
np - > parent = dad ;
2014-11-28 19:03:33 +03:00
np - > sibling = dad - > child ;
dad - > child = np ;
2009-11-24 06:07:00 +03:00
}
}
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:47 +03:00
populate_properties ( blob , offset , mem , np , pathp , dryrun ) ;
2014-10-03 19:28:27 +04:00
if ( ! dryrun ) {
2009-11-24 06:07:00 +03:00
np - > name = of_get_property ( np , " name " , NULL ) ;
if ( ! np - > name )
np - > name = " <NULL> " ;
}
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:47 +03:00
* pnp = np ;
2017-06-02 02:01:47 +03:00
return true ;
2016-05-03 16:22:47 +03:00
}
2016-05-03 16:22:48 +03:00
static void reverse_nodes ( struct device_node * parent )
{
struct device_node * child , * next ;
/* In-depth first */
child = parent - > child ;
while ( child ) {
reverse_nodes ( child ) ;
child = child - > sibling ;
}
/* Reverse the nodes in the child list */
child = parent - > child ;
parent - > child = NULL ;
while ( child ) {
next = child - > sibling ;
child - > sibling = parent - > child ;
parent - > child = child ;
child = next ;
}
}
2016-05-03 16:22:47 +03:00
/**
2016-05-03 16:22:49 +03:00
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
2016-05-03 16:22:47 +03:00
* @ blob : The parent device tree blob
* @ mem : Memory chunk to use for allocating device nodes and properties
* @ dad : Parent struct device_node
* @ nodepp : The device_node tree created by the call
2016-05-03 16:22:48 +03:00
*
2021-03-25 19:47:12 +03:00
* Return : The size of unflattened device tree or error code
2016-05-03 16:22:47 +03:00
*/
2016-05-03 16:22:49 +03:00
static int unflatten_dt_nodes ( const void * blob ,
void * mem ,
struct device_node * dad ,
struct device_node * * nodepp )
2016-05-03 16:22:47 +03:00
{
2016-05-03 16:22:48 +03:00
struct device_node * root ;
2016-06-09 08:50:49 +03:00
int offset = 0 , depth = 0 , initial_depth = 0 ;
2016-05-03 16:22:48 +03:00
# define FDT_MAX_DEPTH 64
struct device_node * nps [ FDT_MAX_DEPTH ] ;
void * base = mem ;
bool dryrun = ! base ;
2021-04-05 06:28:45 +03:00
int ret ;
2016-05-03 16:22:47 +03:00
2016-05-03 16:22:48 +03:00
if ( nodepp )
* nodepp = NULL ;
2016-06-09 08:50:49 +03:00
/*
* We ' re unflattening device sub - tree if @ dad is valid . There are
* possibly multiple nodes in the first level of depth . We need
* set @ depth to 1 to make fdt_next_node ( ) happy as it bails
* immediately when negative @ depth is found . Otherwise , the device
* nodes except the first one won ' t be unflattened successfully .
*/
if ( dad )
depth = initial_depth = 1 ;
2016-05-03 16:22:48 +03:00
root = dad ;
2016-05-11 20:36:57 +03:00
nps [ depth ] = dad ;
2016-06-09 08:50:49 +03:00
2016-05-03 16:22:48 +03:00
for ( offset = 0 ;
2016-06-09 08:50:49 +03:00
offset > = 0 & & depth > = initial_depth ;
2016-05-03 16:22:48 +03:00
offset = fdt_next_node ( blob , offset , & depth ) ) {
if ( WARN_ON_ONCE ( depth > = FDT_MAX_DEPTH ) )
continue ;
2016-05-03 16:22:47 +03:00
2017-10-03 19:07:55 +03:00
if ( ! IS_ENABLED ( CONFIG_OF_KOBJ ) & &
! of_fdt_device_is_available ( blob , offset ) )
continue ;
2021-04-05 06:28:45 +03:00
ret = populate_node ( blob , offset , & mem , nps [ depth ] ,
& nps [ depth + 1 ] , dryrun ) ;
if ( ret < 0 )
return ret ;
2016-05-03 16:22:48 +03:00
if ( ! dryrun & & nodepp & & ! * nodepp )
2016-05-11 20:36:57 +03:00
* nodepp = nps [ depth + 1 ] ;
2016-05-03 16:22:48 +03:00
if ( ! dryrun & & ! root )
2016-05-11 20:36:57 +03:00
root = nps [ depth + 1 ] ;
2016-05-03 16:22:48 +03:00
}
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:48 +03:00
if ( offset < 0 & & offset ! = - FDT_ERR_NOTFOUND ) {
2016-06-15 16:32:18 +03:00
pr_err ( " Error %d processing FDT \n " , offset ) ;
2016-05-03 16:22:48 +03:00
return - EINVAL ;
}
2014-04-03 00:10:14 +04:00
2014-11-28 19:03:33 +03:00
/*
* Reverse the child list . Some drivers assumes node order matches . dts
* node order
*/
2016-05-03 16:22:48 +03:00
if ( ! dryrun )
reverse_nodes ( root ) ;
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:48 +03:00
return mem - base ;
2009-11-24 06:07:00 +03:00
}
2009-11-24 06:07:01 +03:00
2010-11-19 02:55:02 +03:00
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
* @ blob : The blob to expand
2016-05-03 16:22:50 +03:00
* @ dad : Parent device node
2010-11-19 02:55:02 +03:00
* @ mynodes : The device_node tree created by the call
* @ dt_alloc : An allocator that provides a virtual address to memory
* for the resulting tree
2017-10-13 10:41:29 +03:00
* @ detached : if true set OF_DETACHED on @ mynodes
2016-05-03 16:22:51 +03:00
*
2021-03-26 22:26:06 +03:00
* unflattens a device - tree , creating the tree of struct device_node . It also
* fills the " name " and " type " pointers of the nodes so the normal device - tree
* walking functions can be used .
*
2021-03-25 19:47:12 +03:00
* Return : NULL on failure or the memory chunk containing the unflattened
2016-05-03 16:22:51 +03:00
* device tree on success .
2010-11-19 02:55:02 +03:00
*/
2017-04-26 03:09:54 +03:00
void * __unflatten_device_tree ( const void * blob ,
struct device_node * dad ,
struct device_node * * mynodes ,
void * ( * dt_alloc ) ( u64 size , u64 align ) ,
bool detached )
2010-11-19 02:55:02 +03:00
{
2016-05-03 16:22:48 +03:00
int size ;
2014-04-03 00:10:14 +04:00
void * mem ;
2021-04-05 06:28:45 +03:00
int ret ;
if ( mynodes )
* mynodes = NULL ;
2010-11-19 02:55:02 +03:00
pr_debug ( " -> unflatten_device_tree() \n " ) ;
if ( ! blob ) {
pr_debug ( " No device tree pointer \n " ) ;
2016-05-03 16:22:51 +03:00
return NULL ;
2010-11-19 02:55:02 +03:00
}
pr_debug ( " Unflattening device tree: \n " ) ;
2014-04-02 07:48:01 +04:00
pr_debug ( " magic: %08x \n " , fdt_magic ( blob ) ) ;
pr_debug ( " size: %08x \n " , fdt_totalsize ( blob ) ) ;
pr_debug ( " version: %08x \n " , fdt_version ( blob ) ) ;
2010-11-19 02:55:02 +03:00
2014-04-02 07:48:01 +04:00
if ( fdt_check_header ( blob ) ) {
2010-11-19 02:55:02 +03:00
pr_err ( " Invalid device tree blob header \n " ) ;
2016-05-03 16:22:51 +03:00
return NULL ;
2010-11-19 02:55:02 +03:00
}
/* First pass, scan for size */
2016-05-03 16:22:50 +03:00
size = unflatten_dt_nodes ( blob , NULL , dad , NULL ) ;
2021-04-05 06:28:45 +03:00
if ( size < = 0 )
2016-05-03 16:22:51 +03:00
return NULL ;
2010-11-19 02:55:02 +03:00
2016-05-03 16:22:48 +03:00
size = ALIGN ( size , 4 ) ;
pr_debug ( " size is %d, allocating... \n " , size ) ;
2010-11-19 02:55:02 +03:00
/* Allocate memory for the expanded device tree */
2013-08-29 16:30:35 +04:00
mem = dt_alloc ( size + 4 , __alignof__ ( struct device_node ) ) ;
2017-05-17 18:29:09 +03:00
if ( ! mem )
return NULL ;
2013-08-29 16:30:35 +04:00
memset ( mem , 0 , size ) ;
2010-11-19 02:55:02 +03:00
2013-08-29 16:30:35 +04:00
* ( __be32 * ) ( mem + size ) = cpu_to_be32 ( 0xdeadbeef ) ;
2013-08-12 15:06:53 +04:00
2013-08-29 16:30:35 +04:00
pr_debug ( " unflattening %p... \n " , mem ) ;
2010-11-19 02:55:02 +03:00
/* Second pass, do actual unflattening */
2021-04-05 06:28:45 +03:00
ret = unflatten_dt_nodes ( blob , mem , dad , mynodes ) ;
2013-08-29 16:30:35 +04:00
if ( be32_to_cpup ( mem + size ) ! = 0xdeadbeef )
2019-10-18 06:18:33 +03:00
pr_warn ( " End of tree marker overwritten: %08x \n " ,
be32_to_cpup ( mem + size ) ) ;
2010-11-19 02:55:02 +03:00
2021-04-05 06:28:45 +03:00
if ( ret < = 0 )
return NULL ;
if ( detached & & mynodes & & * mynodes ) {
2016-07-19 01:01:12 +03:00
of_node_set_flag ( * mynodes , OF_DETACHED ) ;
pr_debug ( " unflattened tree is detached \n " ) ;
}
2010-11-19 02:55:02 +03:00
pr_debug ( " <- unflatten_device_tree() \n " ) ;
2016-05-03 16:22:51 +03:00
return mem ;
2010-11-19 02:55:02 +03:00
}
static void * kernel_tree_alloc ( u64 size , u64 align )
{
return kzalloc ( size , GFP_KERNEL ) ;
}
2015-12-06 03:13:53 +03:00
static DEFINE_MUTEX ( of_fdt_unflatten_mutex ) ;
2010-11-19 02:55:02 +03:00
/**
* of_fdt_unflatten_tree - create tree of device_nodes from flat blob
2016-05-03 16:22:50 +03:00
* @ blob : Flat device tree blob
* @ dad : Parent device node
* @ mynodes : The device tree created by the call
2010-11-19 02:55:02 +03:00
*
* unflattens the device - tree passed by the firmware , creating the
* tree of struct device_node . It also fills the " name " and " type "
* pointers of the nodes so the normal device - tree walking functions
* can be used .
2016-05-03 16:22:51 +03:00
*
2021-03-25 19:47:12 +03:00
* Return : NULL on failure or the memory chunk containing the unflattened
2016-05-03 16:22:51 +03:00
* device tree on success .
2010-11-19 02:55:02 +03:00
*/
2016-05-03 16:22:51 +03:00
void * of_fdt_unflatten_tree ( const unsigned long * blob ,
struct device_node * dad ,
struct device_node * * mynodes )
2010-11-19 02:55:02 +03:00
{
2016-05-03 16:22:51 +03:00
void * mem ;
2015-12-06 03:13:53 +03:00
mutex_lock ( & of_fdt_unflatten_mutex ) ;
2016-07-19 01:01:12 +03:00
mem = __unflatten_device_tree ( blob , dad , mynodes , & kernel_tree_alloc ,
true ) ;
2015-12-06 03:13:53 +03:00
mutex_unlock ( & of_fdt_unflatten_mutex ) ;
2016-05-03 16:22:51 +03:00
return mem ;
2010-11-19 02:55:02 +03:00
}
EXPORT_SYMBOL_GPL ( of_fdt_unflatten_tree ) ;
2010-11-19 02:55:01 +03:00
/* Everything below here references initial_boot_params directly. */
int __initdata dt_root_addr_cells ;
int __initdata dt_root_size_cells ;
2019-05-14 23:40:53 +03:00
void * initial_boot_params __ro_after_init ;
2010-11-19 02:55:01 +03:00
# ifdef CONFIG_OF_EARLY_FLATTREE
2014-11-14 20:05:35 +03:00
static u32 of_fdt_crc32 ;
2021-08-11 11:52:28 +03:00
static int __init early_init_dt_reserve_memory_arch ( phys_addr_t base ,
phys_addr_t size , bool nomap )
{
if ( nomap ) {
/*
* If the memory is already reserved ( by another region ) , we
2022-01-07 22:42:32 +03:00
* should not allow it to be marked nomap , but don ' t worry
* if the region isn ' t memory as it won ' t be mapped .
2021-08-11 11:52:28 +03:00
*/
2022-01-07 22:42:32 +03:00
if ( memblock_overlaps_region ( & memblock . memory , base , size ) & &
memblock_is_region_reserved ( base , size ) )
2021-08-11 11:52:28 +03:00
return - EBUSY ;
return memblock_mark_nomap ( base , size ) ;
}
return memblock_reserve ( base , size ) ;
}
2021-03-18 13:40:33 +03:00
/*
2020-05-11 18:04:57 +03:00
* __reserved_mem_reserve_reg ( ) - reserve all memory described in ' reg ' property
2014-02-28 17:42:47 +04:00
*/
static int __init __reserved_mem_reserve_reg ( unsigned long node ,
const char * uname )
{
int t_len = ( dt_root_addr_cells + dt_root_size_cells ) * sizeof ( __be32 ) ;
phys_addr_t base , size ;
2014-04-02 08:49:03 +04:00
int len ;
const __be32 * prop ;
2019-05-30 13:39:27 +03:00
int first = 1 ;
bool nomap ;
2014-02-28 17:42:47 +04:00
prop = of_get_flat_dt_prop ( node , " reg " , & len ) ;
if ( ! prop )
return - ENOENT ;
if ( len & & len % t_len ! = 0 ) {
pr_err ( " Reserved memory: invalid reg property in '%s', skipping node. \n " ,
uname ) ;
return - EINVAL ;
}
nomap = of_get_flat_dt_prop ( node , " no-map " , NULL ) ! = NULL ;
while ( len > = t_len ) {
base = dt_mem_next_cell ( dt_root_addr_cells , & prop ) ;
size = dt_mem_next_cell ( dt_root_size_cells , & prop ) ;
2014-08-07 00:30:04 +04:00
if ( size & &
2022-01-15 01:04:08 +03:00
early_init_dt_reserve_memory_arch ( base , size , nomap ) = = 0 ) {
2021-06-16 12:27:44 +03:00
pr_debug ( " Reserved memory: reserved region for node '%s': base %pa, size %lu MiB \n " ,
uname , & base , ( unsigned long ) ( size / SZ_1M ) ) ;
2022-01-15 01:04:08 +03:00
if ( ! nomap )
kmemleak_alloc_phys ( base , size , 0 , 0 ) ;
}
2014-02-28 17:42:47 +04:00
else
2021-06-16 12:27:44 +03:00
pr_info ( " Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB \n " ,
uname , & base , ( unsigned long ) ( size / SZ_1M ) ) ;
2014-02-28 17:42:47 +04:00
len - = t_len ;
2014-02-28 17:42:48 +04:00
if ( first ) {
fdt_reserved_mem_save_node ( node , uname , base , size ) ;
first = 0 ;
}
2014-02-28 17:42:47 +04:00
}
return 0 ;
}
2021-03-18 13:40:33 +03:00
/*
2014-02-28 17:42:47 +04:00
* __reserved_mem_check_root ( ) - check if # size - cells , # address - cells provided
* in / reserved - memory matches the values supported by the current implementation ,
* also check if ranges property has been provided
*/
2014-04-08 09:48:07 +04:00
static int __init __reserved_mem_check_root ( unsigned long node )
2014-02-28 17:42:47 +04:00
{
2014-04-02 08:49:03 +04:00
const __be32 * prop ;
2014-02-28 17:42:47 +04:00
prop = of_get_flat_dt_prop ( node , " #size-cells " , NULL ) ;
if ( ! prop | | be32_to_cpup ( prop ) ! = dt_root_size_cells )
return - EINVAL ;
prop = of_get_flat_dt_prop ( node , " #address-cells " , NULL ) ;
if ( ! prop | | be32_to_cpup ( prop ) ! = dt_root_addr_cells )
return - EINVAL ;
prop = of_get_flat_dt_prop ( node , " ranges " , NULL ) ;
if ( ! prop )
return - EINVAL ;
return 0 ;
}
2021-03-18 13:40:33 +03:00
/*
2021-10-20 23:44:36 +03:00
* fdt_scan_reserved_mem ( ) - scan a single FDT node for reserved memory
2014-02-28 17:42:47 +04:00
*/
2021-10-20 23:44:36 +03:00
static int __init fdt_scan_reserved_mem ( void )
2014-02-28 17:42:47 +04:00
{
2021-10-20 23:44:36 +03:00
int node , child ;
const void * fdt = initial_boot_params ;
node = fdt_path_offset ( fdt , " /reserved-memory " ) ;
if ( node < 0 )
return - ENODEV ;
if ( __reserved_mem_check_root ( node ) ! = 0 ) {
pr_err ( " Reserved memory: unsupported node format, ignoring \n " ) ;
return - EINVAL ;
2014-02-28 17:42:47 +04:00
}
2021-10-20 23:44:36 +03:00
fdt_for_each_subnode ( child , fdt , node ) {
const char * uname ;
int err ;
2014-02-28 17:42:47 +04:00
2021-10-20 23:44:36 +03:00
if ( ! of_fdt_device_is_available ( fdt , child ) )
continue ;
uname = fdt_get_name ( fdt , child , NULL ) ;
2014-02-28 17:42:47 +04:00
2021-10-20 23:44:36 +03:00
err = __reserved_mem_reserve_reg ( child , uname ) ;
if ( err = = - ENOENT & & of_get_flat_dt_prop ( child , " size " , NULL ) )
fdt_reserved_mem_save_node ( child , uname , 0 , 0 ) ;
}
2014-02-28 17:42:47 +04:00
return 0 ;
}
2021-08-11 11:51:01 +03:00
/*
2021-08-25 12:40:40 +03:00
* fdt_reserve_elfcorehdr ( ) - reserves memory for elf core header
2021-08-11 11:51:01 +03:00
*
* This function reserves the memory occupied by an elf core header
* described in the device tree . This region contains all the
* information about primary kernel ' s core image and is used by a dump
* capture kernel to access the system memory on primary kernel .
*/
2021-08-25 12:40:40 +03:00
static void __init fdt_reserve_elfcorehdr ( void )
2021-08-11 11:51:01 +03:00
{
if ( ! IS_ENABLED ( CONFIG_CRASH_DUMP ) | | ! elfcorehdr_size )
return ;
if ( memblock_is_region_reserved ( elfcorehdr_addr , elfcorehdr_size ) ) {
pr_warn ( " elfcorehdr is overlapped \n " ) ;
return ;
}
memblock_reserve ( elfcorehdr_addr , elfcorehdr_size ) ;
pr_info ( " Reserving %llu KiB of memory at 0x%llx for elfcorehdr \n " ,
elfcorehdr_size > > 10 , elfcorehdr_addr ) ;
}
2014-02-28 17:42:47 +04:00
/**
* early_init_fdt_scan_reserved_mem ( ) - create reserved memory regions
*
* This function grabs memory from early allocator for device exclusive use
* defined in device tree structures . It should be called by arch specific code
* once the early allocator ( i . e . memblock ) has been fully activated .
*/
void __init early_init_fdt_scan_reserved_mem ( void )
{
2014-04-02 07:46:48 +04:00
int n ;
u64 base , size ;
2014-03-14 01:36:36 +04:00
if ( ! initial_boot_params )
return ;
2014-04-02 07:46:48 +04:00
/* Process header /memreserve/ fields */
for ( n = 0 ; ; n + + ) {
fdt_get_mem_rsv ( initial_boot_params , n , & base , & size ) ;
if ( ! size )
break ;
2019-05-30 13:39:27 +03:00
early_init_dt_reserve_memory_arch ( base , size , false ) ;
2014-04-02 07:46:48 +04:00
}
2021-10-20 23:44:36 +03:00
fdt_scan_reserved_mem ( ) ;
2021-08-25 12:40:40 +03:00
fdt_reserve_elfcorehdr ( ) ;
2022-01-28 07:23:21 +03:00
fdt_init_reserved_mem ( ) ;
2014-02-28 17:42:47 +04:00
}
2015-06-01 14:40:31 +03:00
/**
* early_init_fdt_reserve_self ( ) - reserve the memory used by the FDT blob
*/
void __init early_init_fdt_reserve_self ( void )
{
if ( ! initial_boot_params )
return ;
/* Reserve the dtb region */
early_init_dt_reserve_memory_arch ( __pa ( initial_boot_params ) ,
fdt_totalsize ( initial_boot_params ) ,
2019-05-30 13:39:27 +03:00
false ) ;
2015-06-01 14:40:31 +03:00
}
2010-11-19 02:55:01 +03:00
/**
* of_scan_flat_dt - scan flattened tree blob and call callback on each .
* @ it : callback function
* @ data : context data pointer
*
* This function is used to scan the flattened device - tree , it is
* used to extract the memory information at boot before we can
* unflatten the tree
*/
int __init of_scan_flat_dt ( int ( * it ) ( unsigned long node ,
const char * uname , int depth ,
void * data ) ,
void * data )
{
2014-04-03 00:10:14 +04:00
const void * blob = initial_boot_params ;
const char * pathp ;
int offset , rc = 0 , depth = - 1 ;
2016-11-23 12:40:07 +03:00
if ( ! blob )
return 0 ;
for ( offset = fdt_next_node ( blob , - 1 , & depth ) ;
offset > = 0 & & depth > = 0 & & ! rc ;
offset = fdt_next_node ( blob , offset , & depth ) ) {
2014-04-03 00:10:14 +04:00
pathp = fdt_get_name ( blob , offset , NULL ) ;
rc = it ( offset , pathp , depth , data ) ;
}
2010-11-19 02:55:01 +03:00
return rc ;
}
2017-04-18 22:12:18 +03:00
/**
* of_scan_flat_dt_subnodes - scan sub - nodes of a node call callback on each .
2021-03-18 13:40:33 +03:00
* @ parent : parent node
2017-04-18 22:12:18 +03:00
* @ it : callback function
* @ data : context data pointer
*
* This function is used to scan sub - nodes of a node .
*/
int __init of_scan_flat_dt_subnodes ( unsigned long parent ,
int ( * it ) ( unsigned long node ,
const char * uname ,
void * data ) ,
void * data )
{
const void * blob = initial_boot_params ;
int node ;
fdt_for_each_subnode ( node , blob , parent ) {
const char * pathp ;
int rc ;
pathp = fdt_get_name ( blob , node , NULL ) ;
rc = it ( node , pathp , data ) ;
if ( rc )
return rc ;
}
return 0 ;
}
2016-04-07 15:03:33 +03:00
/**
* of_get_flat_dt_subnode_by_name - get the subnode by given name
*
* @ node : the parent node
* @ uname : the name of subnode
* @ return offset of the subnode , or - FDT_ERR_NOTFOUND if there is none
*/
2019-05-14 23:40:52 +03:00
int __init of_get_flat_dt_subnode_by_name ( unsigned long node , const char * uname )
2016-04-07 15:03:33 +03:00
{
return fdt_subnode_offset ( initial_boot_params , node , uname ) ;
}
2021-03-18 13:40:33 +03:00
/*
2010-11-19 02:55:01 +03:00
* of_get_flat_dt_root - find the root node in the flat blob
*/
unsigned long __init of_get_flat_dt_root ( void )
{
2014-04-03 00:10:14 +04:00
return 0 ;
2010-11-19 02:55:01 +03:00
}
2021-03-18 13:40:33 +03:00
/*
2010-11-19 02:55:01 +03:00
* of_get_flat_dt_prop - Given a node in the flat blob , return the property ptr
*
* This function can be used within scan_flattened_dt callback to get
* access to properties
*/
2014-04-02 08:49:03 +04:00
const void * __init of_get_flat_dt_prop ( unsigned long node , const char * name ,
int * size )
2010-11-19 02:55:01 +03:00
{
2014-04-03 00:10:14 +04:00
return fdt_getprop ( initial_boot_params , node , name , size ) ;
2010-11-19 02:55:01 +03:00
}
2019-06-15 06:03:43 +03:00
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
* @ blob : A device tree blob
* @ node : node to test
* @ compat : compatible string to compare with compatible list .
*
2021-03-25 19:47:12 +03:00
* Return : a non - zero value on match with smaller values returned for more
2019-06-15 06:03:43 +03:00
* specific compatible values .
*/
static int of_fdt_is_compatible ( const void * blob ,
unsigned long node , const char * compat )
{
const char * cp ;
int cplen ;
unsigned long l , score = 0 ;
cp = fdt_getprop ( blob , node , " compatible " , & cplen ) ;
if ( cp = = NULL )
return 0 ;
while ( cplen > 0 ) {
score + + ;
if ( of_compat_cmp ( cp , compat , strlen ( compat ) ) = = 0 )
return score ;
l = strlen ( cp ) + 1 ;
cp + = l ;
cplen - = l ;
}
return 0 ;
}
2010-11-19 02:55:01 +03:00
/**
* of_flat_dt_is_compatible - Return true if given node has compat in compatible list
* @ node : node to test
* @ compat : compatible string to compare with compatible list .
*/
int __init of_flat_dt_is_compatible ( unsigned long node , const char * compat )
{
return of_fdt_is_compatible ( initial_boot_params , node , compat ) ;
}
2021-03-18 13:40:33 +03:00
/*
2010-10-30 19:49:09 +04:00
* of_flat_dt_match - Return true if node matches a list of compatible values
*/
2019-05-14 23:40:52 +03:00
static int __init of_flat_dt_match ( unsigned long node , const char * const * compat )
2010-10-30 19:49:09 +04:00
{
2019-06-15 06:03:43 +03:00
unsigned int tmp , score = 0 ;
if ( ! compat )
return 0 ;
while ( * compat ) {
tmp = of_fdt_is_compatible ( initial_boot_params , node , * compat ) ;
if ( tmp & & ( score = = 0 | | ( tmp < score ) ) )
score = tmp ;
compat + + ;
}
return score ;
2010-10-30 19:49:09 +04:00
}
2021-03-18 13:40:33 +03:00
/*
* of_get_flat_dt_phandle - Given a node in the flat blob , return the phandle
2017-04-18 22:12:18 +03:00
*/
uint32_t __init of_get_flat_dt_phandle ( unsigned long node )
{
return fdt_get_phandle ( initial_boot_params , node ) ;
}
2013-08-26 16:41:56 +04:00
struct fdt_scan_status {
const char * name ;
int namelen ;
int depth ;
int found ;
int ( * iterator ) ( unsigned long node , const char * uname , int depth , void * data ) ;
void * data ;
} ;
2013-08-28 06:41:56 +04:00
const char * __init of_flat_dt_get_machine_name ( void )
{
const char * name ;
unsigned long dt_root = of_get_flat_dt_root ( ) ;
name = of_get_flat_dt_prop ( dt_root , " model " , NULL ) ;
if ( ! name )
name = of_get_flat_dt_prop ( dt_root , " compatible " , NULL ) ;
return name ;
}
/**
* of_flat_dt_match_machine - Iterate match tables to find matching machine .
*
* @ default_match : A machine specific ptr to return in case of no match .
* @ get_next_compat : callback function to return next compatible match table .
*
* Iterate through machine match tables to find the best match for the machine
* compatible string in the FDT .
*/
const void * __init of_flat_dt_match_machine ( const void * default_match ,
const void * ( * get_next_compat ) ( const char * const * * ) )
{
const void * data = NULL ;
const void * best_data = default_match ;
const char * const * compat ;
unsigned long dt_root ;
unsigned int best_score = ~ 1 , score = 0 ;
dt_root = of_get_flat_dt_root ( ) ;
while ( ( data = get_next_compat ( & compat ) ) ) {
score = of_flat_dt_match ( dt_root , compat ) ;
if ( score > 0 & & score < best_score ) {
best_data = data ;
best_score = score ;
}
}
if ( ! best_data ) {
const char * prop ;
2014-04-02 08:49:03 +04:00
int size ;
2013-08-28 06:41:56 +04:00
pr_err ( " \n unrecognized device tree list: \n [ " ) ;
prop = of_get_flat_dt_prop ( dt_root , " compatible " , & size ) ;
if ( prop ) {
while ( size > 0 ) {
printk ( " '%s' " , prop ) ;
size - = strlen ( prop ) + 1 ;
prop + = strlen ( prop ) + 1 ;
}
}
printk ( " ] \n \n " ) ;
return NULL ;
}
pr_info ( " Machine model: %s \n " , of_flat_dt_get_machine_name ( ) ) ;
return best_data ;
}
2016-02-16 15:52:33 +03:00
static void __early_init_dt_declare_initrd ( unsigned long start ,
unsigned long end )
{
2018-11-06 01:54:30 +03:00
/* ARM64 would cause a BUG to occur here when CONFIG_DEBUG_VM is
* enabled since __va ( ) is called too early . ARM64 does make use
* of phys_initrd_start / phys_initrd_size so we can skip this
* conversion .
*/
if ( ! IS_ENABLED ( CONFIG_ARM64 ) ) {
initrd_start = ( unsigned long ) __va ( start ) ;
initrd_end = ( unsigned long ) __va ( end ) ;
initrd_below_start_ok = 1 ;
}
2016-02-16 15:52:33 +03:00
}
2009-11-24 13:26:58 +03:00
/**
* early_init_dt_check_for_initrd - Decode initrd location from flat tree
* @ node : reference to node containing initrd location ( ' chosen ' )
*/
2013-08-31 02:06:53 +04:00
static void __init early_init_dt_check_for_initrd ( unsigned long node )
2009-11-24 13:26:58 +03:00
{
2013-07-01 22:20:35 +04:00
u64 start , end ;
2014-04-02 08:49:03 +04:00
int len ;
const __be32 * prop ;
2009-11-24 13:26:58 +03:00
2021-08-11 11:51:03 +03:00
if ( ! IS_ENABLED ( CONFIG_BLK_DEV_INITRD ) )
return ;
2009-11-24 13:26:58 +03:00
pr_debug ( " Looking for initrd properties... " ) ;
prop = of_get_flat_dt_prop ( node , " linux,initrd-start " , & len ) ;
2010-01-30 11:31:21 +03:00
if ( ! prop )
return ;
2013-07-01 22:20:35 +04:00
start = of_read_number ( prop , len / 4 ) ;
2010-01-30 11:31:21 +03:00
prop = of_get_flat_dt_prop ( node , " linux,initrd-end " , & len ) ;
if ( ! prop )
return ;
2013-07-01 22:20:35 +04:00
end = of_read_number ( prop , len / 4 ) ;
2009-11-24 13:26:58 +03:00
2016-02-16 15:52:33 +03:00
__early_init_dt_declare_initrd ( start , end ) ;
2018-11-06 01:54:28 +03:00
phys_initrd_start = start ;
phys_initrd_size = end - start ;
2013-08-31 02:06:53 +04:00
2021-06-16 12:27:45 +03:00
pr_debug ( " initrd_start=0x%llx initrd_end=0x%llx \n " , start , end ) ;
2009-11-24 13:26:58 +03:00
}
2021-08-11 11:51:01 +03:00
/**
* early_init_dt_check_for_elfcorehdr - Decode elfcorehdr location from flat
* tree
* @ node : reference to node containing elfcorehdr location ( ' chosen ' )
*/
static void __init early_init_dt_check_for_elfcorehdr ( unsigned long node )
{
const __be32 * prop ;
int len ;
if ( ! IS_ENABLED ( CONFIG_CRASH_DUMP ) )
return ;
pr_debug ( " Looking for elfcorehdr property... " ) ;
prop = of_get_flat_dt_prop ( node , " linux,elfcorehdr " , & len ) ;
if ( ! prop | | ( len < ( dt_root_addr_cells + dt_root_size_cells ) ) )
return ;
elfcorehdr_addr = dt_mem_next_cell ( dt_root_addr_cells , & prop ) ;
elfcorehdr_size = dt_mem_next_cell ( dt_root_size_cells , & prop ) ;
pr_debug ( " elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx \n " ,
elfcorehdr_addr , elfcorehdr_size ) ;
}
2021-12-14 07:01:56 +03:00
static unsigned long chosen_node_offset = - FDT_ERR_NOTFOUND ;
2021-08-11 11:51:02 +03:00
/**
* early_init_dt_check_for_usable_mem_range - Decode usable memory range
* location from flat tree
*/
efi: apply memblock cap after memblock_add()
On arm64, during kdump kernel saves vmcore, it runs into the following bug:
...
[ 15.148919] usercopy: Kernel memory exposure attempt detected from SLUB object 'kmem_cache_node' (offset 0, size 4096)!
[ 15.159707] ------------[ cut here ]------------
[ 15.164311] kernel BUG at mm/usercopy.c:99!
[ 15.168482] Internal error: Oops - BUG: 0 [#1] SMP
[ 15.173261] Modules linked in: xfs libcrc32c crct10dif_ce ghash_ce sha2_ce sha256_arm64 sha1_ce sbsa_gwdt ast i2c_algo_bit drm_vram_helper drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops cec drm_ttm_helper ttm drm nvme nvme_core xgene_hwmon i2c_designware_platform i2c_designware_core dm_mirror dm_region_hash dm_log dm_mod overlay squashfs zstd_decompress loop
[ 15.206186] CPU: 0 PID: 542 Comm: cp Not tainted 5.16.0-rc4 #1
[ 15.212006] Hardware name: GIGABYTE R272-P30-JG/MP32-AR0-JG, BIOS F12 (SCP: 1.5.20210426) 05/13/2021
[ 15.221125] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 15.228073] pc : usercopy_abort+0x9c/0xa0
[ 15.232074] lr : usercopy_abort+0x9c/0xa0
[ 15.236070] sp : ffff8000121abba0
[ 15.239371] x29: ffff8000121abbb0 x28: 0000000000003000 x27: 0000000000000000
[ 15.246494] x26: 0000000080000400 x25: 0000ffff885c7000 x24: 0000000000000000
[ 15.253617] x23: 000007ff80400000 x22: ffff07ff80401000 x21: 0000000000000001
[ 15.260739] x20: 0000000000001000 x19: ffff07ff80400000 x18: ffffffffffffffff
[ 15.267861] x17: 656a626f2042554c x16: 53206d6f72662064 x15: 6574636574656420
[ 15.274983] x14: 74706d6574746120 x13: 2129363930342065 x12: 7a6973202c302074
[ 15.282105] x11: ffffc8b041d1b148 x10: 00000000ffff8000 x9 : ffffc8b04012812c
[ 15.289228] x8 : 00000000ffff7fff x7 : ffffc8b041d1b148 x6 : 0000000000000000
[ 15.296349] x5 : 0000000000000000 x4 : 0000000000007fff x3 : 0000000000000000
[ 15.303471] x2 : 0000000000000000 x1 : ffff07ff8c064800 x0 : 000000000000006b
[ 15.310593] Call trace:
[ 15.313027] usercopy_abort+0x9c/0xa0
[ 15.316677] __check_heap_object+0xd4/0xf0
[ 15.320762] __check_object_size.part.0+0x160/0x1e0
[ 15.325628] __check_object_size+0x2c/0x40
[ 15.329711] copy_oldmem_page+0x7c/0x140
[ 15.333623] read_from_oldmem.part.0+0xfc/0x1c0
[ 15.338142] __read_vmcore.constprop.0+0x23c/0x350
[ 15.342920] read_vmcore+0x28/0x34
[ 15.346309] proc_reg_read+0xb4/0xf0
[ 15.349871] vfs_read+0xb8/0x1f0
[ 15.353088] ksys_read+0x74/0x100
[ 15.356390] __arm64_sys_read+0x28/0x34
...
This bug introduced by commit b261dba2fdb2 ("arm64: kdump: Remove custom
linux,usable-memory-range handling"), which moves
memblock_cap_memory_range() to fdt, but it breaches the rules that
memblock_cap_memory_range() should come after memblock_add() etc as said
in commit e888fa7bb882 ("memblock: Check memory add/cap ordering").
As a consequence, the virtual address set up by copy_oldmem_page() does
not bail out from the test of virt_addr_valid() in check_heap_object(),
and finally hits the BUG_ON().
Since memblock allocator has no idea about when the memblock is fully
populated, while efi_init() is aware, so tackling this issue by calling the
interface early_init_dt_check_for_usable_mem_range() exposed by of/fdt.
Fixes: b261dba2fdb2 ("arm64: kdump: Remove custom linux,usable-memory-range handling")
Signed-off-by: Pingfan Liu <kernelfans@gmail.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Zhen Lei <thunder.leizhen@huawei.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Frank Rowand <frowand.list@gmail.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Nick Terrell <terrelln@fb.com>
Cc: linux-arm-kernel@lists.infradead.org
To: devicetree@vger.kernel.org
To: linux-efi@vger.kernel.org
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20211215021348.8766-1-kernelfans@gmail.com
2021-12-15 05:13:48 +03:00
void __init early_init_dt_check_for_usable_mem_range ( void )
2021-08-11 11:51:02 +03:00
{
const __be32 * prop ;
int len ;
2021-12-14 07:01:56 +03:00
phys_addr_t cap_mem_addr ;
phys_addr_t cap_mem_size ;
unsigned long node = chosen_node_offset ;
if ( ( long ) node < 0 )
return ;
2021-08-11 11:51:02 +03:00
pr_debug ( " Looking for usable-memory-range property... " ) ;
prop = of_get_flat_dt_prop ( node , " linux,usable-memory-range " , & len ) ;
if ( ! prop | | ( len < ( dt_root_addr_cells + dt_root_size_cells ) ) )
return ;
cap_mem_addr = dt_mem_next_cell ( dt_root_addr_cells , & prop ) ;
cap_mem_size = dt_mem_next_cell ( dt_root_size_cells , & prop ) ;
pr_debug ( " cap_mem_start=%pa cap_mem_size=%pa \n " , & cap_mem_addr ,
& cap_mem_size ) ;
2021-12-14 07:01:56 +03:00
memblock_cap_memory_range ( cap_mem_addr , cap_mem_size ) ;
2021-08-11 11:51:02 +03:00
}
2014-03-27 17:07:01 +04:00
# ifdef CONFIG_SERIAL_EARLYCON
2016-09-27 23:54:12 +03:00
int __init early_init_dt_scan_chosen_stdout ( void )
2014-03-27 17:07:01 +04:00
{
int offset ;
2016-01-17 02:23:42 +03:00
const char * p , * q , * options = NULL ;
2014-03-27 17:07:01 +04:00
int l ;
2020-11-23 13:23:13 +03:00
const struct earlycon_id * match ;
2014-03-27 17:07:01 +04:00
const void * fdt = initial_boot_params ;
offset = fdt_path_offset ( fdt , " /chosen " ) ;
if ( offset < 0 )
offset = fdt_path_offset ( fdt , " /chosen@0 " ) ;
if ( offset < 0 )
return - ENOENT ;
p = fdt_getprop ( fdt , offset , " stdout-path " , & l ) ;
if ( ! p )
p = fdt_getprop ( fdt , offset , " linux,stdout-path " , & l ) ;
if ( ! p | | ! l )
return - ENOENT ;
2016-01-17 02:23:42 +03:00
q = strchrnul ( p , ' : ' ) ;
if ( * q ! = ' \0 ' )
options = q + 1 ;
2016-01-17 02:23:48 +03:00
l = q - p ;
2015-10-10 11:29:30 +03:00
2014-03-27 17:07:01 +04:00
/* Get the node specified by stdout-path */
2016-01-17 02:23:48 +03:00
offset = fdt_path_offset_namelen ( fdt , p , l ) ;
if ( offset < 0 ) {
pr_warn ( " earlycon: stdout-path %.*s not found \n " , l , p ) ;
return 0 ;
}
2014-03-27 17:07:01 +04:00
2020-11-23 13:23:13 +03:00
for ( match = __earlycon_table ; match < __earlycon_table_end ; match + + ) {
2016-01-17 02:23:39 +03:00
if ( ! match - > compatible [ 0 ] )
continue ;
if ( fdt_node_check_compatible ( fdt , offset , match - > compatible ) )
2014-03-27 17:07:01 +04:00
continue ;
2019-09-10 08:58:33 +03:00
if ( of_setup_earlycon ( match , offset , options ) = = 0 )
return 0 ;
2014-03-27 17:07:01 +04:00
}
return - ENODEV ;
}
# endif
2021-03-18 13:40:33 +03:00
/*
2009-11-24 13:27:10 +03:00
* early_init_dt_scan_root - fetch the top level address and size cells
*/
2021-11-18 21:12:11 +03:00
int __init early_init_dt_scan_root ( void )
2009-11-24 13:27:10 +03:00
{
2014-04-02 08:49:03 +04:00
const __be32 * prop ;
2021-11-18 21:12:11 +03:00
const void * fdt = initial_boot_params ;
int node = fdt_path_offset ( fdt , " / " ) ;
2009-11-24 13:27:10 +03:00
2021-11-18 21:12:11 +03:00
if ( node < 0 )
return - ENODEV ;
2009-11-24 13:27:10 +03:00
2010-01-30 11:45:26 +03:00
dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT ;
dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT ;
2009-11-24 13:27:10 +03:00
prop = of_get_flat_dt_prop ( node , " #size-cells " , NULL ) ;
2010-01-30 11:45:26 +03:00
if ( prop )
dt_root_size_cells = be32_to_cpup ( prop ) ;
2009-11-24 13:27:10 +03:00
pr_debug ( " dt_root_size_cells = %x \n " , dt_root_size_cells ) ;
prop = of_get_flat_dt_prop ( node , " #address-cells " , NULL ) ;
2010-01-30 11:45:26 +03:00
if ( prop )
dt_root_addr_cells = be32_to_cpup ( prop ) ;
2009-11-24 13:27:10 +03:00
pr_debug ( " dt_root_addr_cells = %x \n " , dt_root_addr_cells ) ;
2021-11-18 21:12:11 +03:00
return 0 ;
2009-11-24 13:27:10 +03:00
}
2014-04-02 08:49:03 +04:00
u64 __init dt_mem_next_cell ( int s , const __be32 * * cellp )
2009-11-24 13:37:56 +03:00
{
2014-04-02 08:49:03 +04:00
const __be32 * p = * cellp ;
2009-11-24 13:37:56 +03:00
* cellp = p + s ;
return of_read_number ( p , s ) ;
}
2021-03-18 13:40:33 +03:00
/*
2017-06-21 02:46:28 +03:00
* early_init_dt_scan_memory - Look for and parse memory nodes
2010-02-02 07:34:14 +03:00
*/
2021-12-15 18:01:02 +03:00
int __init early_init_dt_scan_memory ( void )
2010-02-02 07:34:14 +03:00
{
2021-12-15 18:01:02 +03:00
int node ;
const void * fdt = initial_boot_params ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
fdt_for_each_subnode ( node , fdt , 0 ) {
const char * type = of_get_flat_dt_prop ( node , " device_type " , NULL ) ;
const __be32 * reg , * endp ;
int l ;
bool hotpluggable ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
/* We are scanning "memory" nodes only */
if ( type = = NULL | | strcmp ( type , " memory " ) ! = 0 )
continue ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
reg = of_get_flat_dt_prop ( node , " linux,usable-memory " , & l ) ;
if ( reg = = NULL )
reg = of_get_flat_dt_prop ( node , " reg " , & l ) ;
if ( reg = = NULL )
continue ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
endp = reg + ( l / sizeof ( __be32 ) ) ;
hotpluggable = of_get_flat_dt_prop ( node , " hotpluggable " , NULL ) ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
pr_debug ( " memory scan node %s, reg size %d, \n " ,
fdt_get_name ( fdt , node , NULL ) , l ) ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
while ( ( endp - reg ) > = ( dt_root_addr_cells + dt_root_size_cells ) ) {
u64 base , size ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
base = dt_mem_next_cell ( dt_root_addr_cells , & reg ) ;
size = dt_mem_next_cell ( dt_root_size_cells , & reg ) ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
if ( size = = 0 )
continue ;
pr_debug ( " - %llx, %llx \n " , base , size ) ;
2016-12-13 03:43:02 +03:00
2021-12-15 18:01:02 +03:00
early_init_dt_add_memory_arch ( base , size ) ;
2016-12-13 03:43:02 +03:00
2021-12-15 18:01:02 +03:00
if ( ! hotpluggable )
continue ;
2010-02-02 07:34:14 +03:00
2021-12-15 18:01:02 +03:00
if ( memblock_mark_hotplug ( base , size ) )
pr_warn ( " failed to mark hotplug range 0x%llx - 0x%llx \n " ,
base , base + size ) ;
}
}
2010-02-02 07:34:14 +03:00
return 0 ;
}
2021-11-18 21:12:10 +03:00
int __init early_init_dt_scan_chosen ( char * cmdline )
2009-12-11 09:42:21 +03:00
{
2021-11-18 21:12:10 +03:00
int l , node ;
2014-04-02 08:49:03 +04:00
const char * p ;
2019-08-23 09:24:51 +03:00
const void * rng_seed ;
2021-11-18 21:12:10 +03:00
const void * fdt = initial_boot_params ;
2009-12-11 09:42:21 +03:00
2021-11-18 21:12:10 +03:00
node = fdt_path_offset ( fdt , " /chosen " ) ;
if ( node < 0 )
node = fdt_path_offset ( fdt , " /chosen@0 " ) ;
if ( node < 0 )
return - ENOENT ;
2009-12-11 09:42:21 +03:00
2021-12-14 07:01:56 +03:00
chosen_node_offset = node ;
2009-12-11 09:42:21 +03:00
early_init_dt_check_for_initrd ( node ) ;
2021-08-11 11:51:01 +03:00
early_init_dt_check_for_elfcorehdr ( node ) ;
2009-12-11 09:42:21 +03:00
2011-03-31 05:57:33 +04:00
/* Retrieve command line */
2009-12-11 09:42:21 +03:00
p = of_get_flat_dt_prop ( node , " bootargs " , & l ) ;
if ( p ! = NULL & & l > 0 )
2021-11-18 21:12:10 +03:00
strlcpy ( cmdline , p , min ( l , COMMAND_LINE_SIZE ) ) ;
2009-12-11 09:42:21 +03:00
2011-09-19 22:50:15 +04:00
/*
* CONFIG_CMDLINE is meant to be a default in case nothing else
* managed to set the command line , unless CONFIG_CMDLINE_FORCE
* is set in which case we override whatever was found earlier .
*/
2009-12-11 09:42:21 +03:00
# ifdef CONFIG_CMDLINE
2016-04-13 12:52:16 +03:00
# if defined(CONFIG_CMDLINE_EXTEND)
2021-11-18 21:12:10 +03:00
strlcat ( cmdline , " " , COMMAND_LINE_SIZE ) ;
strlcat ( cmdline , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
2016-04-13 12:52:16 +03:00
# elif defined(CONFIG_CMDLINE_FORCE)
2021-11-18 21:12:10 +03:00
strlcpy ( cmdline , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
2016-04-13 12:52:16 +03:00
# else
/* No arguments from boot loader, use kernel's cmdl*/
2021-11-18 21:12:10 +03:00
if ( ! ( ( char * ) cmdline ) [ 0 ] )
strlcpy ( cmdline , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
2016-04-13 12:52:16 +03:00
# endif
2009-12-11 09:42:21 +03:00
# endif /* CONFIG_CMDLINE */
2021-11-18 21:12:10 +03:00
pr_debug ( " Command line is: %s \n " , ( char * ) cmdline ) ;
2009-12-11 09:42:21 +03:00
2019-08-23 09:24:51 +03:00
rng_seed = of_get_flat_dt_prop ( node , " rng-seed " , & l ) ;
if ( rng_seed & & l > 0 ) {
add_bootloader_randomness ( rng_seed , l ) ;
/* try to clear seed so it won't be found. */
fdt_nop_property ( initial_boot_params , node , " rng-seed " ) ;
2019-08-27 13:33:53 +03:00
/* update CRC check value */
of_fdt_crc32 = crc32_be ( ~ 0 , initial_boot_params ,
fdt_totalsize ( initial_boot_params ) ) ;
2019-08-23 09:24:51 +03:00
}
2021-11-18 21:12:10 +03:00
return 0 ;
2009-12-11 09:42:21 +03:00
}
2016-02-16 15:52:32 +03:00
# ifndef MIN_MEMBLOCK_ADDR
# define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET)
# endif
2015-08-18 12:34:41 +03:00
# ifndef MAX_MEMBLOCK_ADDR
# define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0)
# endif
2014-07-08 04:45:43 +04:00
2013-09-25 07:20:01 +04:00
void __init __weak early_init_dt_add_memory_arch ( u64 base , u64 size )
{
2016-02-16 15:52:32 +03:00
const u64 phys_offset = MIN_MEMBLOCK_ADDR ;
2014-08-20 19:10:31 +04:00
2018-10-27 01:04:48 +03:00
if ( size < PAGE_SIZE - ( base & ~ PAGE_MASK ) ) {
pr_warn ( " Ignoring memory block 0x%llx - 0x%llx \n " ,
base , base + size ) ;
return ;
}
2014-08-20 19:10:31 +04:00
if ( ! PAGE_ALIGNED ( base ) ) {
size - = PAGE_SIZE - ( base & ~ PAGE_MASK ) ;
base = PAGE_ALIGN ( base ) ;
}
2013-09-25 07:20:01 +04:00
size & = PAGE_MASK ;
2014-06-20 07:13:38 +04:00
2015-08-18 12:34:41 +03:00
if ( base > MAX_MEMBLOCK_ADDR ) {
2019-10-18 06:18:33 +03:00
pr_warn ( " Ignoring memory block 0x%llx - 0x%llx \n " ,
base , base + size ) ;
2014-07-08 04:45:43 +04:00
return ;
}
2014-06-20 07:13:38 +04:00
2015-08-18 12:34:41 +03:00
if ( base + size - 1 > MAX_MEMBLOCK_ADDR ) {
2019-10-18 06:18:33 +03:00
pr_warn ( " Ignoring memory range 0x%llx - 0x%llx \n " ,
( ( u64 ) MAX_MEMBLOCK_ADDR ) + 1 , base + size ) ;
2015-08-18 12:34:41 +03:00
size = MAX_MEMBLOCK_ADDR - base + 1 ;
2014-06-20 07:13:38 +04:00
}
2013-09-25 07:20:01 +04:00
if ( base + size < phys_offset ) {
2019-10-18 06:18:33 +03:00
pr_warn ( " Ignoring memory block 0x%llx - 0x%llx \n " ,
base , base + size ) ;
2013-09-25 07:20:01 +04:00
return ;
}
if ( base < phys_offset ) {
2019-10-18 06:18:33 +03:00
pr_warn ( " Ignoring memory range 0x%llx - 0x%llx \n " ,
base , phys_offset ) ;
2013-09-25 07:20:01 +04:00
size - = phys_offset - base ;
base = phys_offset ;
}
memblock_add ( base , size ) ;
}
2018-01-06 00:32:33 +03:00
static void * __init early_init_dt_alloc_memory_arch ( u64 size , u64 align )
2015-06-19 04:35:46 +03:00
{
2019-03-12 09:30:31 +03:00
void * ptr = memblock_alloc ( size , align ) ;
if ( ! ptr )
panic ( " %s: Failed to allocate %llu bytes align=0x%llx \n " ,
__func__ , size , align ) ;
return ptr ;
2015-06-19 04:35:46 +03:00
}
2013-08-29 00:18:32 +04:00
2014-07-15 21:03:34 +04:00
bool __init early_init_dt_verify ( void * params )
2013-08-26 18:47:40 +04:00
{
if ( ! params )
return false ;
/* check device tree validity */
2014-10-29 21:15:00 +03:00
if ( fdt_check_header ( params ) )
2013-08-26 18:47:40 +04:00
return false ;
2014-10-29 21:15:00 +03:00
/* Setup flat device-tree pointer */
initial_boot_params = params ;
2019-08-27 13:33:53 +03:00
of_fdt_crc32 = crc32_be ( ~ 0 , initial_boot_params ,
fdt_totalsize ( initial_boot_params ) ) ;
2014-07-15 21:03:34 +04:00
return true ;
}
void __init early_init_dt_scan_nodes ( void )
{
2021-11-18 21:12:10 +03:00
int rc ;
2018-11-10 03:53:17 +03:00
2021-08-11 11:51:01 +03:00
/* Initialize {size,address}-cells info */
2021-11-18 21:12:11 +03:00
early_init_dt_scan_root ( ) ;
2021-08-11 11:51:01 +03:00
2013-08-26 18:47:40 +04:00
/* Retrieve various information from the /chosen node */
2021-11-18 21:12:10 +03:00
rc = early_init_dt_scan_chosen ( boot_command_line ) ;
if ( rc )
2018-11-10 03:53:17 +03:00
pr_warn ( " No chosen node found, continuing without \n " ) ;
2013-08-26 18:47:40 +04:00
/* Setup memory, calling early_init_dt_add_memory_arch */
2021-12-15 18:01:02 +03:00
early_init_dt_scan_memory ( ) ;
2021-08-11 11:51:02 +03:00
/* Handle linux,usable-memory-range property */
2021-12-14 07:01:56 +03:00
early_init_dt_check_for_usable_mem_range ( ) ;
2014-07-15 21:03:34 +04:00
}
bool __init early_init_dt_scan ( void * params )
{
bool status ;
status = early_init_dt_verify ( params ) ;
if ( ! status )
return false ;
2013-08-26 18:47:40 +04:00
2014-07-15 21:03:34 +04:00
early_init_dt_scan_nodes ( ) ;
2013-08-26 18:47:40 +04:00
return true ;
}
2009-11-24 06:07:01 +03:00
/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens the device - tree passed by the firmware , creating the
* tree of struct device_node . It also fills the " name " and " type "
* pointers of the nodes so the normal device - tree walking functions
* can be used .
*/
void __init unflatten_device_tree ( void )
{
2016-05-03 16:22:50 +03:00
__unflatten_device_tree ( initial_boot_params , NULL , & of_root ,
2016-07-19 01:01:12 +03:00
early_init_dt_alloc_memory_arch , false ) ;
2009-11-24 06:07:01 +03:00
2013-05-30 13:38:08 +04:00
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
2011-08-15 11:28:14 +04:00
of_alias_scan ( early_init_dt_alloc_memory_arch ) ;
2017-04-26 03:09:54 +03:00
unittest_unflatten_overlay_base ( ) ;
2009-11-24 06:07:01 +03:00
}
2010-11-19 02:54:56 +03:00
2013-08-26 20:22:45 +04:00
/**
* unflatten_and_copy_device_tree - copy and create tree of device_nodes from flat blob
*
* Copies and unflattens the device - tree passed by the firmware , creating the
* tree of struct device_node . It also fills the " name " and " type "
* pointers of the nodes so the normal device - tree walking functions
* can be used . This should only be used when the FDT memory has not been
* reserved such is the case when the FDT is built - in to the kernel init
* section . If the FDT memory is reserved already then unflatten_device_tree
* should be used instead .
*/
void __init unflatten_and_copy_device_tree ( void )
{
2013-11-21 17:44:14 +04:00
int size ;
void * dt ;
if ( ! initial_boot_params ) {
pr_warn ( " No valid device tree found, continuing without \n " ) ;
return ;
}
2014-04-02 07:48:01 +04:00
size = fdt_totalsize ( initial_boot_params ) ;
2013-11-21 17:44:14 +04:00
dt = early_init_dt_alloc_memory_arch ( size ,
2014-04-02 07:48:01 +04:00
roundup_pow_of_two ( FDT_V17_SIZE ) ) ;
2013-08-26 20:22:45 +04:00
if ( dt ) {
memcpy ( dt , initial_boot_params , size ) ;
initial_boot_params = dt ;
}
unflatten_device_tree ( ) ;
}
2014-11-14 20:05:35 +03:00
# ifdef CONFIG_SYSFS
static ssize_t of_fdt_raw_read ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
2014-04-03 01:56:48 +04:00
{
2014-11-14 20:05:35 +03:00
memcpy ( buf , initial_boot_params + off , count ) ;
return count ;
}
2014-04-03 01:56:48 +04:00
2014-11-14 20:05:35 +03:00
static int __init of_fdt_raw_init ( void )
{
static struct bin_attribute of_fdt_raw_attr =
__BIN_ATTR ( fdt , S_IRUSR , of_fdt_raw_read , NULL , 0 ) ;
2014-04-03 01:56:48 +04:00
2014-11-14 20:05:35 +03:00
if ( ! initial_boot_params )
return 0 ;
2014-04-03 01:56:48 +04:00
2014-11-14 20:05:35 +03:00
if ( of_fdt_crc32 ! = crc32_be ( ~ 0 , initial_boot_params ,
fdt_totalsize ( initial_boot_params ) ) ) {
2016-06-15 16:32:18 +03:00
pr_warn ( " not creating '/sys/firmware/fdt': CRC check failed \n " ) ;
2014-11-14 20:05:35 +03:00
return 0 ;
}
of_fdt_raw_attr . size = fdt_totalsize ( initial_boot_params ) ;
return sysfs_create_bin_file ( firmware_kobj , & of_fdt_raw_attr ) ;
2014-04-03 01:56:48 +04:00
}
2014-11-14 20:05:35 +03:00
late_initcall ( of_fdt_raw_init ) ;
2014-04-03 01:56:48 +04:00
# endif
2010-11-19 02:54:56 +03:00
# endif /* CONFIG_OF_EARLY_FLATTREE */