2024-01-24 08:12:42 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* crash . c - kernel crash support code .
* Copyright ( C ) 2002 - 2004 Eric Biederman < ebiederm @ xmission . com >
*/
# include <linux/buildid.h>
# include <linux/init.h>
# include <linux/utsname.h>
# include <linux/vmalloc.h>
# include <linux/sizes.h>
# include <linux/kexec.h>
# include <linux/memory.h>
# include <linux/cpuhotplug.h>
# include <linux/memblock.h>
# include <linux/kmemleak.h>
# include <asm/page.h>
# include <asm/sections.h>
# include <crypto/sha1.h>
# include "kallsyms_internal.h"
# include "kexec_internal.h"
/* vmcoreinfo stuff */
unsigned char * vmcoreinfo_data ;
size_t vmcoreinfo_size ;
u32 * vmcoreinfo_note ;
/* trusted vmcoreinfo, e.g. we can make a copy in the crash memory */
static unsigned char * vmcoreinfo_data_safecopy ;
Elf_Word * append_elf_note ( Elf_Word * buf , char * name , unsigned int type ,
void * data , size_t data_len )
{
struct elf_note * note = ( struct elf_note * ) buf ;
note - > n_namesz = strlen ( name ) + 1 ;
note - > n_descsz = data_len ;
note - > n_type = type ;
buf + = DIV_ROUND_UP ( sizeof ( * note ) , sizeof ( Elf_Word ) ) ;
memcpy ( buf , name , note - > n_namesz ) ;
buf + = DIV_ROUND_UP ( note - > n_namesz , sizeof ( Elf_Word ) ) ;
memcpy ( buf , data , data_len ) ;
buf + = DIV_ROUND_UP ( data_len , sizeof ( Elf_Word ) ) ;
return buf ;
}
void final_note ( Elf_Word * buf )
{
memset ( buf , 0 , sizeof ( struct elf_note ) ) ;
}
static void update_vmcoreinfo_note ( void )
{
u32 * buf = vmcoreinfo_note ;
if ( ! vmcoreinfo_size )
return ;
buf = append_elf_note ( buf , VMCOREINFO_NOTE_NAME , 0 , vmcoreinfo_data ,
vmcoreinfo_size ) ;
final_note ( buf ) ;
}
void crash_update_vmcoreinfo_safecopy ( void * ptr )
{
if ( ptr )
memcpy ( ptr , vmcoreinfo_data , vmcoreinfo_size ) ;
vmcoreinfo_data_safecopy = ptr ;
}
void crash_save_vmcoreinfo ( void )
{
if ( ! vmcoreinfo_note )
return ;
/* Use the safe copy to generate vmcoreinfo note if have */
if ( vmcoreinfo_data_safecopy )
vmcoreinfo_data = vmcoreinfo_data_safecopy ;
vmcoreinfo_append_str ( " CRASHTIME=%lld \n " , ktime_get_real_seconds ( ) ) ;
update_vmcoreinfo_note ( ) ;
}
void vmcoreinfo_append_str ( const char * fmt , . . . )
{
va_list args ;
char buf [ 0x50 ] ;
size_t r ;
va_start ( args , fmt ) ;
r = vscnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
va_end ( args ) ;
r = min ( r , ( size_t ) VMCOREINFO_BYTES - vmcoreinfo_size ) ;
memcpy ( & vmcoreinfo_data [ vmcoreinfo_size ] , buf , r ) ;
vmcoreinfo_size + = r ;
WARN_ONCE ( vmcoreinfo_size = = VMCOREINFO_BYTES ,
" vmcoreinfo data exceeds allocated size, truncating " ) ;
}
/*
* provide an empty default implementation here - - architecture
* code may override this
*/
void __weak arch_crash_save_vmcoreinfo ( void )
{ }
phys_addr_t __weak paddr_vmcoreinfo_note ( void )
{
return __pa ( vmcoreinfo_note ) ;
}
EXPORT_SYMBOL ( paddr_vmcoreinfo_note ) ;
static int __init crash_save_vmcoreinfo_init ( void )
{
vmcoreinfo_data = ( unsigned char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! vmcoreinfo_data ) {
pr_warn ( " Memory allocation for vmcoreinfo_data failed \n " ) ;
return - ENOMEM ;
}
vmcoreinfo_note = alloc_pages_exact ( VMCOREINFO_NOTE_SIZE ,
GFP_KERNEL | __GFP_ZERO ) ;
if ( ! vmcoreinfo_note ) {
free_page ( ( unsigned long ) vmcoreinfo_data ) ;
vmcoreinfo_data = NULL ;
pr_warn ( " Memory allocation for vmcoreinfo_note failed \n " ) ;
return - ENOMEM ;
}
VMCOREINFO_OSRELEASE ( init_uts_ns . name . release ) ;
VMCOREINFO_BUILD_ID ( ) ;
VMCOREINFO_PAGESIZE ( PAGE_SIZE ) ;
VMCOREINFO_SYMBOL ( init_uts_ns ) ;
VMCOREINFO_OFFSET ( uts_namespace , name ) ;
VMCOREINFO_SYMBOL ( node_online_map ) ;
# ifdef CONFIG_MMU
VMCOREINFO_SYMBOL_ARRAY ( swapper_pg_dir ) ;
# endif
VMCOREINFO_SYMBOL ( _stext ) ;
vmcoreinfo_append_str ( " NUMBER(VMALLOC_START)=0x%lx \n " , ( unsigned long ) VMALLOC_START ) ;
# ifndef CONFIG_NUMA
VMCOREINFO_SYMBOL ( mem_map ) ;
VMCOREINFO_SYMBOL ( contig_page_data ) ;
# endif
2024-02-27 04:49:52 +03:00
# ifdef CONFIG_SPARSEMEM_VMEMMAP
VMCOREINFO_SYMBOL_ARRAY ( vmemmap ) ;
# endif
2024-01-24 08:12:42 +03:00
# ifdef CONFIG_SPARSEMEM
VMCOREINFO_SYMBOL_ARRAY ( mem_section ) ;
VMCOREINFO_LENGTH ( mem_section , NR_SECTION_ROOTS ) ;
VMCOREINFO_STRUCT_SIZE ( mem_section ) ;
VMCOREINFO_OFFSET ( mem_section , section_mem_map ) ;
VMCOREINFO_NUMBER ( SECTION_SIZE_BITS ) ;
VMCOREINFO_NUMBER ( MAX_PHYSMEM_BITS ) ;
# endif
VMCOREINFO_STRUCT_SIZE ( page ) ;
VMCOREINFO_STRUCT_SIZE ( pglist_data ) ;
VMCOREINFO_STRUCT_SIZE ( zone ) ;
VMCOREINFO_STRUCT_SIZE ( free_area ) ;
VMCOREINFO_STRUCT_SIZE ( list_head ) ;
VMCOREINFO_SIZE ( nodemask_t ) ;
VMCOREINFO_OFFSET ( page , flags ) ;
VMCOREINFO_OFFSET ( page , _refcount ) ;
VMCOREINFO_OFFSET ( page , mapping ) ;
VMCOREINFO_OFFSET ( page , lru ) ;
VMCOREINFO_OFFSET ( page , _mapcount ) ;
VMCOREINFO_OFFSET ( page , private ) ;
VMCOREINFO_OFFSET ( page , compound_head ) ;
VMCOREINFO_OFFSET ( pglist_data , node_zones ) ;
VMCOREINFO_OFFSET ( pglist_data , nr_zones ) ;
# ifdef CONFIG_FLATMEM
VMCOREINFO_OFFSET ( pglist_data , node_mem_map ) ;
# endif
VMCOREINFO_OFFSET ( pglist_data , node_start_pfn ) ;
VMCOREINFO_OFFSET ( pglist_data , node_spanned_pages ) ;
VMCOREINFO_OFFSET ( pglist_data , node_id ) ;
VMCOREINFO_OFFSET ( zone , free_area ) ;
VMCOREINFO_OFFSET ( zone , vm_stat ) ;
VMCOREINFO_OFFSET ( zone , spanned_pages ) ;
VMCOREINFO_OFFSET ( free_area , free_list ) ;
VMCOREINFO_OFFSET ( list_head , next ) ;
VMCOREINFO_OFFSET ( list_head , prev ) ;
VMCOREINFO_LENGTH ( zone . free_area , NR_PAGE_ORDERS ) ;
log_buf_vmcoreinfo_setup ( ) ;
VMCOREINFO_LENGTH ( free_area . free_list , MIGRATE_TYPES ) ;
VMCOREINFO_NUMBER ( NR_FREE_PAGES ) ;
VMCOREINFO_NUMBER ( PG_lru ) ;
VMCOREINFO_NUMBER ( PG_private ) ;
VMCOREINFO_NUMBER ( PG_swapcache ) ;
VMCOREINFO_NUMBER ( PG_swapbacked ) ;
2024-03-21 17:24:45 +03:00
# define PAGE_SLAB_MAPCOUNT_VALUE (~PG_slab)
VMCOREINFO_NUMBER ( PAGE_SLAB_MAPCOUNT_VALUE ) ;
2024-01-24 08:12:42 +03:00
# ifdef CONFIG_MEMORY_FAILURE
VMCOREINFO_NUMBER ( PG_hwpoison ) ;
# endif
VMCOREINFO_NUMBER ( PG_head_mask ) ;
# define PAGE_BUDDY_MAPCOUNT_VALUE (~PG_buddy)
VMCOREINFO_NUMBER ( PAGE_BUDDY_MAPCOUNT_VALUE ) ;
2024-03-21 17:24:43 +03:00
# define PAGE_HUGETLB_MAPCOUNT_VALUE (~PG_hugetlb)
VMCOREINFO_NUMBER ( PAGE_HUGETLB_MAPCOUNT_VALUE ) ;
2024-01-24 08:12:42 +03:00
# define PAGE_OFFLINE_MAPCOUNT_VALUE (~PG_offline)
VMCOREINFO_NUMBER ( PAGE_OFFLINE_MAPCOUNT_VALUE ) ;
# ifdef CONFIG_KALLSYMS
VMCOREINFO_SYMBOL ( kallsyms_names ) ;
VMCOREINFO_SYMBOL ( kallsyms_num_syms ) ;
VMCOREINFO_SYMBOL ( kallsyms_token_table ) ;
VMCOREINFO_SYMBOL ( kallsyms_token_index ) ;
# ifdef CONFIG_KALLSYMS_BASE_RELATIVE
VMCOREINFO_SYMBOL ( kallsyms_offsets ) ;
VMCOREINFO_SYMBOL ( kallsyms_relative_base ) ;
# else
VMCOREINFO_SYMBOL ( kallsyms_addresses ) ;
# endif /* CONFIG_KALLSYMS_BASE_RELATIVE */
# endif /* CONFIG_KALLSYMS */
arch_crash_save_vmcoreinfo ( ) ;
update_vmcoreinfo_note ( ) ;
return 0 ;
}
subsys_initcall ( crash_save_vmcoreinfo_init ) ;