2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-12-20 03:22:35 +03:00
/*
* Copyright ( C ) 2016 IBM Corporation
*
* Authors :
* Thiago Jung Bauermann < bauerman @ linux . vnet . ibm . com >
* Mimi Zohar < zohar @ linux . vnet . ibm . com >
*/
2018-04-24 17:30:38 +03:00
2016-12-20 03:22:48 +03:00
# include <linux/seq_file.h>
# include <linux/vmalloc.h>
# include <linux/kexec.h>
2021-02-21 20:49:27 +03:00
# include <linux/of.h>
2021-06-10 20:15:52 +03:00
# include <linux/ima.h>
2016-12-20 03:22:35 +03:00
# include "ima.h"
2016-12-20 03:22:48 +03:00
# ifdef CONFIG_IMA_KEXEC
static int ima_dump_measurement_list ( unsigned long * buffer_size , void * * buffer ,
unsigned long segment_size )
{
struct ima_queue_entry * qe ;
struct seq_file file ;
2016-12-20 03:22:57 +03:00
struct ima_kexec_hdr khdr ;
2016-12-20 03:22:48 +03:00
int ret = 0 ;
/* segment size can't change between kexec load and execute */
file . buf = vmalloc ( segment_size ) ;
if ( ! file . buf ) {
ret = - ENOMEM ;
goto out ;
}
file . size = segment_size ;
file . read_pos = 0 ;
file . count = sizeof ( khdr ) ; /* reserved space */
2016-12-20 03:22:57 +03:00
memset ( & khdr , 0 , sizeof ( khdr ) ) ;
khdr . version = 1 ;
2016-12-20 03:22:48 +03:00
list_for_each_entry_rcu ( qe , & ima_measurements , later ) {
if ( file . count < file . size ) {
khdr . count + + ;
ima_measurements_show ( & file , qe ) ;
} else {
ret = - EINVAL ;
break ;
}
}
if ( ret < 0 )
goto out ;
/*
* fill in reserved space with some buffer details
* ( eg . version , buffer size , number of measurements )
*/
khdr . buffer_size = file . count ;
2016-12-20 03:22:57 +03:00
if ( ima_canonical_fmt ) {
khdr . version = cpu_to_le16 ( khdr . version ) ;
khdr . count = cpu_to_le64 ( khdr . count ) ;
khdr . buffer_size = cpu_to_le64 ( khdr . buffer_size ) ;
}
2016-12-20 03:22:48 +03:00
memcpy ( file . buf , & khdr , sizeof ( khdr ) ) ;
2016-12-20 03:22:57 +03:00
2021-12-29 05:03:03 +03:00
print_hex_dump_debug ( " ima dump: " , DUMP_PREFIX_NONE , 16 , 1 ,
file . buf , file . count < 100 ? file . count : 100 ,
true ) ;
2016-12-20 03:22:48 +03:00
* buffer_size = file . count ;
* buffer = file . buf ;
out :
if ( ret = = - EINVAL )
vfree ( file . buf ) ;
return ret ;
}
/*
* Called during kexec_file_load so that IMA can add a segment to the kexec
* image for the measurement list for the next kernel .
*
* This function assumes that kexec_mutex is held .
*/
void ima_add_kexec_buffer ( struct kimage * image )
{
struct kexec_buf kbuf = { . image = image , . buf_align = PAGE_SIZE ,
. buf_min = 0 , . buf_max = ULONG_MAX ,
. top_down = true } ;
unsigned long binary_runtime_size ;
/* use more understandable variable names than defined in kbuf */
void * kexec_buffer = NULL ;
size_t kexec_buffer_size ;
size_t kexec_segment_size ;
int ret ;
/*
* Reserve an extra half page of memory for additional measurements
* added during the kexec load .
*/
binary_runtime_size = ima_get_binary_runtime_size ( ) ;
if ( binary_runtime_size > = ULONG_MAX - PAGE_SIZE )
kexec_segment_size = ULONG_MAX ;
else
kexec_segment_size = ALIGN ( ima_get_binary_runtime_size ( ) +
PAGE_SIZE / 2 , PAGE_SIZE ) ;
if ( ( kexec_segment_size = = ULONG_MAX ) | |
2018-12-28 11:34:29 +03:00
( ( kexec_segment_size > > PAGE_SHIFT ) > totalram_pages ( ) / 2 ) ) {
2016-12-20 03:22:48 +03:00
pr_err ( " Binary measurement list too large. \n " ) ;
return ;
}
ima_dump_measurement_list ( & kexec_buffer_size , & kexec_buffer ,
kexec_segment_size ) ;
if ( ! kexec_buffer ) {
pr_err ( " Not enough memory for the kexec measurement buffer. \n " ) ;
return ;
}
kbuf . buffer = kexec_buffer ;
kbuf . bufsz = kexec_buffer_size ;
kbuf . memsz = kexec_segment_size ;
ret = kexec_add_buffer ( & kbuf ) ;
if ( ret ) {
pr_err ( " Error passing over kexec measurement buffer. \n " ) ;
2021-02-04 20:49:50 +03:00
vfree ( kexec_buffer ) ;
2016-12-20 03:22:48 +03:00
return ;
}
2021-02-21 20:49:25 +03:00
image - > ima_buffer_addr = kbuf . mem ;
image - > ima_buffer_size = kexec_segment_size ;
2021-02-04 20:49:51 +03:00
image - > ima_buffer = kexec_buffer ;
2016-12-20 03:22:48 +03:00
pr_debug ( " kexec measurement buffer for the loaded kernel at 0x%lx. \n " ,
kbuf . mem ) ;
}
# endif /* IMA_KEXEC */
2016-12-20 03:22:35 +03:00
/*
* Restore the measurement list from the previous kernel .
*/
2022-06-30 11:36:12 +03:00
void __init ima_load_kexec_buffer ( void )
2016-12-20 03:22:35 +03:00
{
void * kexec_buffer = NULL ;
size_t kexec_buffer_size = 0 ;
int rc ;
rc = ima_get_kexec_buffer ( & kexec_buffer , & kexec_buffer_size ) ;
switch ( rc ) {
case 0 :
rc = ima_restore_measurement_list ( kexec_buffer_size ,
kexec_buffer ) ;
if ( rc ! = 0 )
pr_err ( " Failed to restore the measurement list: %d \n " ,
rc ) ;
ima_free_kexec_buffer ( ) ;
break ;
case - ENOTSUPP :
pr_debug ( " Restoring the measurement list not supported \n " ) ;
break ;
case - ENOENT :
pr_debug ( " No measurement list to restore \n " ) ;
break ;
default :
pr_debug ( " Error restoring the measurement list: %d \n " , rc ) ;
}
}