@ -12,11 +12,13 @@
*/
# include "qemu/osdep.h"
# include "qemu/units.h"
# include "cpu.h"
# include "s390x-internal.h"
# include "elf.h"
# include "sysemu/dump.h"
# include "hw/s390x/pv.h"
# include "kvm/kvm_s390x.h"
struct S390xUserRegsStruct {
uint64_t psw [ 2 ] ;
@ -76,9 +78,16 @@ typedef struct noteStruct {
uint64_t todcmp ;
uint32_t todpreg ;
uint64_t ctrs [ 16 ] ;
uint8_t dynamic [ 1 ] ; /*
* Would be a flexible array member , if
* that was legal inside a union . Real
* size comes from PV info interface .
*/
} contents ;
} QEMU_PACKED Note ;
static bool pv_dump_initialized ;
static void s390x_write_elf64_prstatus ( Note * note , S390CPU * cpu , int id )
{
int i ;
@ -177,28 +186,39 @@ static void s390x_write_elf64_prefix(Note *note, S390CPU *cpu, int id)
note - > contents . prefix = cpu_to_be32 ( ( uint32_t ) ( cpu - > env . psa ) ) ;
}
static void s390x_write_elf64_pv ( Note * note , S390CPU * cpu , int id )
{
note - > hdr . n_type = cpu_to_be32 ( NT_S390_PV_CPU_DATA ) ;
if ( ! pv_dump_initialized ) {
return ;
}
kvm_s390_dump_cpu ( cpu , & note - > contents . dynamic ) ;
}
typedef struct NoteFuncDescStruct {
int contents_size ;
uint64_t ( * note_size_func ) ( void ) ; /* NULL for non-dynamic sized contents */
void ( * note_contents_func ) ( Note * note , S390CPU * cpu , int id ) ;
bool pvonly ;
} NoteFuncDesc ;
static const NoteFuncDesc note_core [ ] = {
{ sizeof_field ( Note , contents . prstatus ) , s390x_write_elf64_prstatus } ,
{ sizeof_field ( Note , contents . fpregset ) , s390x_write_elf64_fpregset } ,
{ 0 , NULL }
{ sizeof_field ( Note , contents . prstatus ) , NULL , s390x_write_elf64_prstatus , false } ,
{ sizeof_field ( Note , contents . fpregset ) , NULL , s390x_write_elf64_fpregset , false } ,
{ 0 , NULL , NULL , false }
} ;
static const NoteFuncDesc note_linux [ ] = {
{ sizeof_field ( Note , contents . prefix ) , s390x_write_elf64_prefix } ,
{ sizeof_field ( Note , contents . ctrs ) , s390x_write_elf64_ctrs } ,
{ sizeof_field ( Note , contents . timer ) , s390x_write_elf64_timer } ,
{ sizeof_field ( Note , contents . todcmp ) , s390x_write_elf64_todcmp } ,
{ sizeof_field ( Note , contents . todpreg ) , s390x_write_elf64_todpreg } ,
{ sizeof_field ( Note , contents . vregslo ) , s390x_write_elf64_vregslo } ,
{ sizeof_field ( Note , contents . vregshi ) , s390x_write_elf64_vregshi } ,
{ sizeof_field ( Note , contents . gscb ) , s390x_write_elf64_gscb } ,
{ 0 , NULL }
{ sizeof_field ( Note , contents . prefix ) , NULL , s390x_write_elf64_prefix , false } ,
{ sizeof_field ( Note , contents . ctrs ) , NULL , s390x_write_elf64_ctrs , false } ,
{ sizeof_field ( Note , contents . timer ) , NULL , s390x_write_elf64_timer , false } ,
{ sizeof_field ( Note , contents . todcmp ) , NULL , s390x_write_elf64_todcmp , false } ,
{ sizeof_field ( Note , contents . todpreg ) , NULL , s390x_write_elf64_todpreg , false } ,
{ sizeof_field ( Note , contents . vregslo ) , NULL , s390x_write_elf64_vregslo , false } ,
{ sizeof_field ( Note , contents . vregshi ) , NULL , s390x_write_elf64_vregshi , false } ,
{ sizeof_field ( Note , contents . gscb ) , NULL , s390x_write_elf64_gscb , false } ,
{ 0 , kvm_s390_pv_dmp_get_size_cpu , s390x_write_elf64_pv , true } ,
{ 0 , NULL , NULL , false }
} ;
static int s390x_write_elf64_notes ( const char * note_name ,
@ -207,22 +227,41 @@ static int s390x_write_elf64_notes(const char *note_name,
DumpState * s ,
const NoteFuncDesc * funcs )
{
Note note ;
Note note , * notep ;
const NoteFuncDesc * nf ;
int note_size ;
int note_size , content_size ;
int ret = - 1 ;
assert ( strlen ( note_name ) < sizeof ( note . name ) ) ;
for ( nf = funcs ; nf - > note_contents_func ; nf + + ) {
memset ( & note , 0 , sizeof ( note ) ) ;
note . hdr . n_namesz = cpu_to_be32 ( strlen ( note_name ) + 1 ) ;
note . hdr . n_descsz = cpu_to_be32 ( nf - > contents_size ) ;
g_strlcpy ( note . name , note_name , sizeof ( note . name ) ) ;
( * nf - > note_contents_func ) ( & note , cpu , id ) ;
notep = & note ;
if ( nf - > pvonly & & ! s390_is_pv ( ) ) {
continue ;
}
content_size = nf - > note_size_func ? nf - > note_size_func ( ) : nf - > contents_size ;
note_size = sizeof ( note ) - sizeof ( notep - > contents ) + content_size ;
/* Notes with dynamic sizes need to allocate a note */
if ( nf - > note_size_func ) {
notep = g_malloc ( note_size ) ;
}
memset ( notep , 0 , sizeof ( note ) ) ;
note_size = sizeof ( note ) - sizeof ( note . contents ) + nf - > contents_size ;
ret = f ( & note , note_size , s ) ;
/* Setup note header data */
notep - > hdr . n_descsz = cpu_to_be32 ( content_size ) ;
notep - > hdr . n_namesz = cpu_to_be32 ( strlen ( note_name ) + 1 ) ;
g_strlcpy ( notep - > name , note_name , sizeof ( notep - > name ) ) ;
/* Get contents and write them out */
( * nf - > note_contents_func ) ( notep , cpu , id ) ;
ret = f ( notep , note_size , s ) ;
if ( nf - > note_size_func ) {
g_free ( notep ) ;
}
if ( ret < 0 ) {
return - 1 ;
@ -247,13 +286,179 @@ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
return s390x_write_elf64_notes ( " LINUX " , f , cpu , cpuid , s , note_linux ) ;
}
/* PV dump section size functions */
static uint64_t get_mem_state_size_from_len ( uint64_t len )
{
return ( len / ( MiB ) ) * kvm_s390_pv_dmp_get_size_mem_state ( ) ;
}
static uint64_t get_size_mem_state ( DumpState * s )
{
return get_mem_state_size_from_len ( s - > total_size ) ;
}
static uint64_t get_size_completion_data ( DumpState * s )
{
return kvm_s390_pv_dmp_get_size_completion_data ( ) ;
}
/* PV dump section data functions*/
static int get_data_completion ( DumpState * s , uint8_t * buff )
{
int rc ;
if ( ! pv_dump_initialized ) {
return 0 ;
}
rc = kvm_s390_dump_completion_data ( buff ) ;
if ( ! rc ) {
pv_dump_initialized = false ;
}
return rc ;
}
static int get_mem_state ( DumpState * s , uint8_t * buff )
{
int64_t memblock_size , memblock_start ;
GuestPhysBlock * block ;
uint64_t off ;
int rc ;
QTAILQ_FOREACH ( block , & s - > guest_phys_blocks . head , next ) {
memblock_start = dump_filtered_memblock_start ( block , s - > filter_area_begin ,
s - > filter_area_length ) ;
if ( memblock_start = = - 1 ) {
continue ;
}
memblock_size = dump_filtered_memblock_size ( block , s - > filter_area_begin ,
s - > filter_area_length ) ;
off = get_mem_state_size_from_len ( block - > target_start ) ;
rc = kvm_s390_dump_mem_state ( block - > target_start ,
get_mem_state_size_from_len ( memblock_size ) ,
buff + off ) ;
if ( rc ) {
return rc ;
}
}
return 0 ;
}
static struct sections {
uint64_t ( * sections_size_func ) ( DumpState * s ) ;
int ( * sections_contents_func ) ( DumpState * s , uint8_t * buff ) ;
char sctn_str [ 12 ] ;
} sections [ ] = {
{ get_size_mem_state , get_mem_state , " pv_mem_meta " } ,
{ get_size_completion_data , get_data_completion , " pv_compl " } ,
{ NULL , NULL , " " }
} ;
static uint64_t arch_sections_write_hdr ( DumpState * s , uint8_t * buff )
{
Elf64_Shdr * shdr = ( void * ) buff ;
struct sections * sctn = sections ;
uint64_t off = s - > section_offset ;
if ( ! pv_dump_initialized ) {
return 0 ;
}
for ( ; sctn - > sections_size_func ; off + = shdr - > sh_size , sctn + + , shdr + + ) {
memset ( shdr , 0 , sizeof ( * shdr ) ) ;
shdr - > sh_type = SHT_PROGBITS ;
shdr - > sh_offset = off ;
shdr - > sh_size = sctn - > sections_size_func ( s ) ;
shdr - > sh_name = s - > string_table_buf - > len ;
g_array_append_vals ( s - > string_table_buf , sctn - > sctn_str , sizeof ( sctn - > sctn_str ) ) ;
}
return ( uintptr_t ) shdr - ( uintptr_t ) buff ;
}
/* Add arch specific number of sections and their respective sizes */
static void arch_sections_add ( DumpState * s )
{
struct sections * sctn = sections ;
/*
* We only do a PV dump if we are running a PV guest , KVM supports
* the dump API and we got valid dump length information .
*/
if ( ! s390_is_pv ( ) | | ! kvm_s390_get_protected_dump ( ) | |
! kvm_s390_pv_info_basic_valid ( ) ) {
return ;
}
/*
* Start the UV dump process by doing the initialize dump call via
* KVM as the proxy .
*/
if ( ! kvm_s390_dump_init ( ) ) {
pv_dump_initialized = true ;
} else {
/*
* Dump init failed , maybe the guest owner disabled dumping .
* We ' ll continue the non - PV dump process since this is no
* reason to crash qemu .
*/
return ;
}
for ( ; sctn - > sections_size_func ; sctn + + ) {
s - > shdr_num + = 1 ;
s - > elf_section_data_size + = sctn - > sections_size_func ( s ) ;
}
}
/*
* After the PV dump has been initialized , the CPU data has been
* fetched and memory has been dumped , we need to grab the tweak data
* and the completion data .
*/
static int arch_sections_write ( DumpState * s , uint8_t * buff )
{
struct sections * sctn = sections ;
int rc ;
if ( ! pv_dump_initialized ) {
return - EINVAL ;
}
for ( ; sctn - > sections_size_func ; sctn + + ) {
rc = sctn - > sections_contents_func ( s , buff ) ;
buff + = sctn - > sections_size_func ( s ) ;
if ( rc ) {
return rc ;
}
}
return 0 ;
}
int cpu_get_dump_info ( ArchDumpInfo * info ,
const struct GuestPhysBlockList * guest_phys_blocks )
{
info - > d_machine = EM_S390 ;
info - > d_endian = ELFDATA2MSB ;
info - > d_class = ELFCLASS64 ;
/*
* This is evaluated for each dump so we can freely switch
* between PV and non - PV .
*/
if ( s390_is_pv ( ) & & kvm_s390_get_protected_dump ( ) & &
kvm_s390_pv_info_basic_valid ( ) ) {
info - > arch_sections_add_fn = * arch_sections_add ;
info - > arch_sections_write_hdr_fn = * arch_sections_write_hdr ;
info - > arch_sections_write_fn = * arch_sections_write ;
} else {
info - > arch_sections_add_fn = NULL ;
info - > arch_sections_write_hdr_fn = NULL ;
info - > arch_sections_write_fn = NULL ;
}
return 0 ;
}
@ -261,7 +466,7 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
{
int name_size = 8 ; /* "LINUX" or "CORE" + pad */
size_t elf_note_size = 0 ;
int note_head_size ;
int note_head_size , content_size ;
const NoteFuncDesc * nf ;
assert ( class = = ELFCLASS64 ) ;
@ -270,12 +475,15 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
note_head_size = sizeof ( Elf64_Nhdr ) ;
for ( nf = note_core ; nf - > note_contents_func ; nf + + ) {
elf_note_size = elf_note_size + note_head_size + name_size +
nf - > contents_size ;
elf_note_size = elf_note_size + note_head_size + name_size + nf - > contents_size ;
}
for ( nf = note_linux ; nf - > note_contents_func ; nf + + ) {
if ( nf - > pvonly & & ! s390_is_pv ( ) ) {
continue ;
}
content_size = nf - > contents_size ? nf - > contents_size : nf - > note_size_func ( ) ;
elf_note_size = elf_note_size + note_head_size + name_size +
nf - > contents_size ;
content_size ;
}
return ( elf_note_size ) * nr_cpus ;