2019-02-02 10:41:15 +01:00
// SPDX-License-Identifier: GPL-2.0
2015-09-30 23:01:56 +09:00
/*
* fake_mem . c
*
* Copyright ( C ) 2015 FUJITSU LIMITED
* Author : Taku Izumi < izumi . taku @ jp . fujitsu . com >
*
* This code introduces new boot option named " efi_fake_mem "
* By specifying this parameter , you can add arbitrary attribute to
* specific memory range by updating original ( firmware provided ) EFI
* memmap .
*/
# include <linux/kernel.h>
# include <linux/efi.h>
# include <linux/init.h>
# include <linux/memblock.h>
# include <linux/types.h>
# include <linux/sort.h>
# include <asm/efi.h>
# define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
2016-02-29 20:30:39 +00:00
static struct efi_mem_range fake_mems [ EFI_MAX_FAKEMEM ] ;
2015-09-30 23:01:56 +09:00
static int nr_fake_mem ;
static int __init cmp_fake_mem ( const void * x1 , const void * x2 )
{
2016-02-29 20:30:39 +00:00
const struct efi_mem_range * m1 = x1 ;
const struct efi_mem_range * m2 = x2 ;
2015-09-30 23:01:56 +09:00
if ( m1 - > range . start < m2 - > range . start )
return - 1 ;
if ( m1 - > range . start > m2 - > range . start )
return 1 ;
return 0 ;
}
void __init efi_fake_memmap ( void )
{
2016-04-25 21:06:39 +01:00
int new_nr_map = efi . memmap . nr_map ;
2015-09-30 23:01:56 +09:00
efi_memory_desc_t * md ;
2015-10-23 11:48:17 +02:00
phys_addr_t new_memmap_phy ;
2015-09-30 23:01:56 +09:00
void * new_memmap ;
int i ;
2016-06-21 23:11:38 +01:00
if ( ! nr_fake_mem )
2015-09-30 23:01:56 +09:00
return ;
/* count up the number of EFI memory descriptor */
2016-02-29 16:58:18 +00:00
for ( i = 0 ; i < nr_fake_mem ; i + + ) {
for_each_efi_memory_desc ( md ) {
struct range * r = & fake_mems [ i ] . range ;
2016-02-29 20:30:39 +00:00
new_nr_map + = efi_memmap_split_count ( md , r ) ;
2015-09-30 23:01:56 +09:00
}
}
/* allocate memory for new EFI memmap */
2017-01-05 13:51:29 +01:00
new_memmap_phy = efi_memmap_alloc ( new_nr_map ) ;
2015-09-30 23:01:56 +09:00
if ( ! new_memmap_phy )
return ;
/* create new EFI memmap */
new_memmap = early_memremap ( new_memmap_phy ,
2016-04-25 21:06:39 +01:00
efi . memmap . desc_size * new_nr_map ) ;
2015-09-30 23:01:56 +09:00
if ( ! new_memmap ) {
2016-04-25 21:06:39 +01:00
memblock_free ( new_memmap_phy , efi . memmap . desc_size * new_nr_map ) ;
2015-09-30 23:01:56 +09:00
return ;
}
2016-02-29 16:58:18 +00:00
for ( i = 0 ; i < nr_fake_mem ; i + + )
2016-02-29 20:30:39 +00:00
efi_memmap_insert ( & efi . memmap , new_memmap , & fake_mems [ i ] ) ;
2015-09-30 23:01:56 +09:00
/* swap into new EFI memmap */
2016-02-26 21:22:05 +00:00
early_memunmap ( new_memmap , efi . memmap . desc_size * new_nr_map ) ;
2016-06-22 16:54:00 +01:00
efi_memmap_install ( new_memmap_phy , new_nr_map ) ;
2015-09-30 23:01:56 +09:00
/* print new EFI memmap */
efi_print_memmap ( ) ;
}
static int __init setup_fake_mem ( char * p )
{
u64 start = 0 , mem_size = 0 , attribute = 0 ;
int i ;
if ( ! p )
return - EINVAL ;
while ( * p ! = ' \0 ' ) {
mem_size = memparse ( p , & p ) ;
if ( * p = = ' @ ' )
start = memparse ( p + 1 , & p ) ;
else
break ;
if ( * p = = ' : ' )
attribute = simple_strtoull ( p + 1 , & p , 0 ) ;
else
break ;
if ( nr_fake_mem > = EFI_MAX_FAKEMEM )
break ;
fake_mems [ nr_fake_mem ] . range . start = start ;
fake_mems [ nr_fake_mem ] . range . end = start + mem_size - 1 ;
fake_mems [ nr_fake_mem ] . attribute = attribute ;
nr_fake_mem + + ;
if ( * p = = ' , ' )
p + + ;
}
2016-02-29 20:30:39 +00:00
sort ( fake_mems , nr_fake_mem , sizeof ( struct efi_mem_range ) ,
2015-09-30 23:01:56 +09:00
cmp_fake_mem , NULL ) ;
for ( i = 0 ; i < nr_fake_mem ; i + + )
pr_info ( " efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx] " ,
fake_mems [ i ] . attribute , fake_mems [ i ] . range . start ,
fake_mems [ i ] . range . end ) ;
return * p = = ' \0 ' ? 0 : - EINVAL ;
}
early_param ( " efi_fake_mem " , setup_fake_mem ) ;