xref: /cloud-hypervisor/vmm/src/coredump.rs (revision d7edd9d51fbe16067f06ccb3014a012861a0c52b)
190034fd6SYi Wang // Copyright © 2022 ZTE Corporation
290034fd6SYi Wang //
390034fd6SYi Wang // SPDX-License-Identifier: Apache-2.0
490034fd6SYi Wang //
590034fd6SYi Wang 
688a9f799SRob Bradford use std::fs::File;
788a9f799SRob Bradford use std::io::Write;
888a9f799SRob Bradford 
9ccb604e1SYi Wang #[cfg(target_arch = "x86_64")]
10f1ab86feSWei Liu use hypervisor::arch::x86::{DescriptorTable, SegmentRegister};
117e280b6fSYi Wang use linux_loader::elf;
12fff62d93SPhilipp Schuster use thiserror::Error;
137e280b6fSYi Wang use vm_memory::ByteValued;
1490034fd6SYi Wang 
1590034fd6SYi Wang #[derive(Clone)]
1690034fd6SYi Wang pub struct CoredumpMemoryRegion {
1790034fd6SYi Wang     pub mem_offset_in_elf: u64,
1890034fd6SYi Wang     pub mem_size: u64,
1990034fd6SYi Wang }
2090034fd6SYi Wang 
2190034fd6SYi Wang #[derive(Clone)]
2290034fd6SYi Wang pub struct CoredumpMemoryRegions {
2390034fd6SYi Wang     pub ram_maps: std::collections::BTreeMap<u64, CoredumpMemoryRegion>,
2490034fd6SYi Wang }
2590034fd6SYi Wang 
2690034fd6SYi Wang /// Platform information
2790034fd6SYi Wang #[derive(Default)]
2890034fd6SYi Wang pub struct DumpState {
2990034fd6SYi Wang     pub elf_note_size: isize,
3090034fd6SYi Wang     pub elf_phdr_num: u16,
3190034fd6SYi Wang     pub elf_sh_info: u32,
3290034fd6SYi Wang     pub mem_offset: u64,
3390034fd6SYi Wang     pub mem_info: Option<CoredumpMemoryRegions>,
3490034fd6SYi Wang     pub file: Option<File>,
3590034fd6SYi Wang }
3690034fd6SYi Wang 
37fff62d93SPhilipp Schuster #[derive(Error, Debug)]
3890034fd6SYi Wang pub enum GuestDebuggableError {
39*d7edd9d5SPhilipp Schuster     #[error("coredump")]
40fff62d93SPhilipp Schuster     Coredump(#[source] anyhow::Error),
41*d7edd9d5SPhilipp Schuster     #[error("coredump file")]
42fff62d93SPhilipp Schuster     CoredumpFile(#[source] std::io::Error),
43*d7edd9d5SPhilipp Schuster     #[error("Failed to pause")]
44fff62d93SPhilipp Schuster     Pause(#[source] vm_migration::MigratableError),
45*d7edd9d5SPhilipp Schuster     #[error("Failed to resume")]
46fff62d93SPhilipp Schuster     Resume(#[source] vm_migration::MigratableError),
4790034fd6SYi Wang }
4890034fd6SYi Wang 
4990034fd6SYi Wang pub trait GuestDebuggable: vm_migration::Pausable {
coredump( &mut self, _destination_url: &str, ) -> std::result::Result<(), GuestDebuggableError>5090034fd6SYi Wang     fn coredump(
5190034fd6SYi Wang         &mut self,
5290034fd6SYi Wang         _destination_url: &str,
5390034fd6SYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
5490034fd6SYi Wang         Ok(())
5590034fd6SYi Wang     }
5690034fd6SYi Wang }
577e280b6fSYi Wang 
587e280b6fSYi Wang #[repr(C)]
597e280b6fSYi Wang #[derive(Default, Copy, Clone)]
607e280b6fSYi Wang pub struct X86_64UserRegs {
617e280b6fSYi Wang     /// r15, r14, r13, r12, rbp, rbx, r11, r10;
627e280b6fSYi Wang     pub regs1: [u64; 8],
637e280b6fSYi Wang     /// r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax;
647e280b6fSYi Wang     pub regs2: [u64; 8],
657e280b6fSYi Wang     pub rip: u64,
667e280b6fSYi Wang     pub cs: u64,
677e280b6fSYi Wang     pub eflags: u64,
687e280b6fSYi Wang     pub rsp: u64,
697e280b6fSYi Wang     pub ss: u64,
707e280b6fSYi Wang     pub fs_base: u64,
717e280b6fSYi Wang     pub gs_base: u64,
727e280b6fSYi Wang     pub ds: u64,
737e280b6fSYi Wang     pub es: u64,
747e280b6fSYi Wang     pub fs: u64,
757e280b6fSYi Wang     pub gs: u64,
767e280b6fSYi Wang }
777e280b6fSYi Wang 
78d05586f5SWei Liu // SAFETY: This is just a series of bytes
79ccb604e1SYi Wang unsafe impl ByteValued for X86_64UserRegs {}
80ccb604e1SYi Wang 
817e280b6fSYi Wang #[repr(C)]
827e280b6fSYi Wang pub struct X86_64ElfPrStatus {
837e280b6fSYi Wang     pub pad1: [u8; 32],
847e280b6fSYi Wang     pub pid: u32,
857e280b6fSYi Wang     pub pads2: [u8; 76],
867e280b6fSYi Wang     pub regs: X86_64UserRegs,
877e280b6fSYi Wang     pub pad3: [u8; 8],
887e280b6fSYi Wang }
897e280b6fSYi Wang 
90ccb604e1SYi Wang #[repr(C)]
91ccb604e1SYi Wang #[derive(Default, Copy, Clone)]
92ccb604e1SYi Wang pub struct CpuSegment {
93ccb604e1SYi Wang     pub selector: u32,
94ccb604e1SYi Wang     pub limit: u32,
95ccb604e1SYi Wang     pub flags: u32,
96ccb604e1SYi Wang     pub pad: u32,
97ccb604e1SYi Wang     pub base: u64,
98ccb604e1SYi Wang }
99ccb604e1SYi Wang 
100ccb604e1SYi Wang const DESC_TYPE_SHIFT: u32 = 8;
101ccb604e1SYi Wang const DESC_S_SHIFT: u32 = 12;
102ccb604e1SYi Wang const DESC_DPL_SHIFT: u32 = 13;
103ccb604e1SYi Wang const DESC_P_SHIFT: u32 = 15;
104ccb604e1SYi Wang const DESC_P_MASK: u32 = 1 << DESC_P_SHIFT;
105ccb604e1SYi Wang const DESC_AVL_SHIFT: u32 = 20;
106ccb604e1SYi Wang const DESC_AVL_MASK: u32 = 1 << DESC_AVL_SHIFT;
107ccb604e1SYi Wang const DESC_L_SHIFT: u32 = 21;
108ccb604e1SYi Wang const DESC_B_SHIFT: u32 = 22;
109ccb604e1SYi Wang const DESC_S_MASK: u32 = 1 << DESC_S_SHIFT;
110ccb604e1SYi Wang const DESC_G_SHIFT: u32 = 23;
111ccb604e1SYi Wang const DESC_G_MASK: u32 = 1 << DESC_G_SHIFT;
112ccb604e1SYi Wang 
113ccb604e1SYi Wang impl CpuSegment {
new(reg: SegmentRegister) -> Self114ccb604e1SYi Wang     pub fn new(reg: SegmentRegister) -> Self {
115ccb604e1SYi Wang         let p_mask = if (reg.present > 0) && (reg.unusable == 0) {
116ccb604e1SYi Wang             DESC_P_MASK
117ccb604e1SYi Wang         } else {
118ccb604e1SYi Wang             0
119ccb604e1SYi Wang         };
120ccb604e1SYi Wang         let flags = ((reg.type_ as u32) << DESC_TYPE_SHIFT)
121ccb604e1SYi Wang             | p_mask
122ccb604e1SYi Wang             | ((reg.dpl as u32) << DESC_DPL_SHIFT)
123ccb604e1SYi Wang             | ((reg.db as u32) << DESC_B_SHIFT)
124ccb604e1SYi Wang             | ((reg.s as u32) * DESC_S_MASK)
125ccb604e1SYi Wang             | ((reg.l as u32) << DESC_L_SHIFT)
126ccb604e1SYi Wang             | ((reg.g as u32) * DESC_G_MASK)
127ccb604e1SYi Wang             | ((reg.avl as u32) * DESC_AVL_MASK);
128ccb604e1SYi Wang 
129ccb604e1SYi Wang         CpuSegment {
130ccb604e1SYi Wang             selector: reg.selector as u32,
131a9ec0f33SBo Chen             limit: reg.limit,
132ccb604e1SYi Wang             flags,
133ccb604e1SYi Wang             pad: 0,
134ccb604e1SYi Wang             base: reg.base,
135ccb604e1SYi Wang         }
136ccb604e1SYi Wang     }
137ccb604e1SYi Wang 
new_from_table(reg: DescriptorTable) -> Self138f1ab86feSWei Liu     pub fn new_from_table(reg: DescriptorTable) -> Self {
139ccb604e1SYi Wang         CpuSegment {
140ccb604e1SYi Wang             selector: 0,
141ccb604e1SYi Wang             limit: reg.limit as u32,
142ccb604e1SYi Wang             flags: 0,
143ccb604e1SYi Wang             pad: 0,
144ccb604e1SYi Wang             base: reg.base,
145ccb604e1SYi Wang         }
146ccb604e1SYi Wang     }
147ccb604e1SYi Wang }
148ccb604e1SYi Wang 
149ccb604e1SYi Wang #[repr(C)]
150ccb604e1SYi Wang #[derive(Default, Copy, Clone)]
151ccb604e1SYi Wang pub struct CpuState {
152ccb604e1SYi Wang     pub version: u32,
153ccb604e1SYi Wang     pub size: u32,
154ccb604e1SYi Wang     /// rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp
155ccb604e1SYi Wang     pub regs1: [u64; 8],
156ccb604e1SYi Wang     /// r8, r9, r10, r11, r12, r13, r14, r15
157ccb604e1SYi Wang     pub regs2: [u64; 8],
158ccb604e1SYi Wang     pub rip: u64,
159ccb604e1SYi Wang     pub rflags: u64,
160ccb604e1SYi Wang     pub cs: CpuSegment,
161ccb604e1SYi Wang     pub ds: CpuSegment,
162ccb604e1SYi Wang     pub es: CpuSegment,
163ccb604e1SYi Wang     pub fs: CpuSegment,
164ccb604e1SYi Wang     pub gs: CpuSegment,
165ccb604e1SYi Wang     pub ss: CpuSegment,
166ccb604e1SYi Wang     pub ldt: CpuSegment,
167ccb604e1SYi Wang     pub tr: CpuSegment,
168ccb604e1SYi Wang     pub gdt: CpuSegment,
169ccb604e1SYi Wang     pub idt: CpuSegment,
170ccb604e1SYi Wang     pub cr: [u64; 5],
171ccb604e1SYi Wang     pub kernel_gs_base: u64,
172ccb604e1SYi Wang }
173ccb604e1SYi Wang 
174d05586f5SWei Liu // SAFETY: This is just a series of bytes
175ccb604e1SYi Wang unsafe impl ByteValued for CpuState {}
176ccb604e1SYi Wang 
1777e280b6fSYi Wang pub enum NoteDescType {
17894fb9f81SRob Bradford     Elf = 0,
17994fb9f81SRob Bradford     Vmm = 1,
18094fb9f81SRob Bradford     ElfAndVmm = 2,
1817e280b6fSYi Wang }
1827e280b6fSYi Wang 
1837e280b6fSYi Wang // "CORE" or "QEMU"
1847e280b6fSYi Wang pub const COREDUMP_NAME_SIZE: u32 = 5;
185ccb604e1SYi Wang pub const NT_PRSTATUS: u32 = 1;
1867e280b6fSYi Wang 
1877e280b6fSYi Wang /// Core file.
1887e280b6fSYi Wang const ET_CORE: u16 = 4;
1897e280b6fSYi Wang /// 64-bit object.
1907e280b6fSYi Wang const ELFCLASS64: u8 = 2;
1917e280b6fSYi Wang /// Current ELF version.
1927e280b6fSYi Wang const EV_CURRENT: u8 = 1;
1937e280b6fSYi Wang /// AMD x86-64 architecture
1947e280b6fSYi Wang const EM_X86_64: u16 = 62;
1957e280b6fSYi Wang 
1967e280b6fSYi Wang pub trait Elf64Writable {
write_header( &mut self, dump_state: &DumpState, ) -> std::result::Result<(), GuestDebuggableError>1977e280b6fSYi Wang     fn write_header(
1987e280b6fSYi Wang         &mut self,
1997e280b6fSYi Wang         dump_state: &DumpState,
2007e280b6fSYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
2017e280b6fSYi Wang         let e_ident = [
2027e280b6fSYi Wang             elf::ELFMAG0 as u8, // magic
2037e280b6fSYi Wang             elf::ELFMAG1,
2047e280b6fSYi Wang             elf::ELFMAG2,
2057e280b6fSYi Wang             elf::ELFMAG3,
2067e280b6fSYi Wang             ELFCLASS64,             // class
2077e280b6fSYi Wang             elf::ELFDATA2LSB as u8, //data
2087e280b6fSYi Wang             EV_CURRENT,             // version
2097e280b6fSYi Wang             0,                      // os_abi
2107e280b6fSYi Wang             0,                      // abi_version
2117e280b6fSYi Wang             0,                      // padding
2127e280b6fSYi Wang             0,
2137e280b6fSYi Wang             0,
2147e280b6fSYi Wang             0,
2157e280b6fSYi Wang             0,
2167e280b6fSYi Wang             0,
2177e280b6fSYi Wang             0,
2187e280b6fSYi Wang         ];
2197e280b6fSYi Wang         let elf64_ehdr_size = std::mem::size_of::<elf::Elf64_Ehdr>();
2207e280b6fSYi Wang         let elf64_phdr_size = std::mem::size_of::<elf::Elf64_Phdr>();
2217e280b6fSYi Wang         let mut elf64_ehdr = elf::Elf64_Ehdr {
2227e280b6fSYi Wang             e_ident,
2237e280b6fSYi Wang             e_type: ET_CORE,
2247e280b6fSYi Wang             e_machine: EM_X86_64,
2257e280b6fSYi Wang             e_version: EV_CURRENT as u32,
2267e280b6fSYi Wang             e_entry: 0,
2277e280b6fSYi Wang             e_phoff: elf64_ehdr_size as u64,
2287e280b6fSYi Wang             e_shoff: 0,
2297e280b6fSYi Wang             e_flags: 0,
2307e280b6fSYi Wang             e_ehsize: 0,
2317e280b6fSYi Wang             e_phentsize: elf64_phdr_size as u16,
2327e280b6fSYi Wang             e_phnum: dump_state.elf_phdr_num,
2337e280b6fSYi Wang             e_shentsize: 0,
2347e280b6fSYi Wang             e_shnum: 0,
2357e280b6fSYi Wang             e_shstrndx: 0,
2367e280b6fSYi Wang         };
2377e280b6fSYi Wang         elf64_ehdr.e_ehsize = std::mem::size_of_val(&elf64_ehdr) as u16;
2387e280b6fSYi Wang 
2397e280b6fSYi Wang         let mut coredump_file = dump_state.file.as_ref().unwrap();
2407e280b6fSYi Wang         let bytes: &[u8] = elf64_ehdr.as_slice();
2417e280b6fSYi Wang         coredump_file
2427e280b6fSYi Wang             .write(bytes)
24394fb9f81SRob Bradford             .map_err(GuestDebuggableError::CoredumpFile)?;
2447e280b6fSYi Wang 
2457e280b6fSYi Wang         Ok(())
2467e280b6fSYi Wang     }
2477e280b6fSYi Wang 
write_note( &mut self, dump_state: &DumpState, ) -> std::result::Result<(), GuestDebuggableError>2487e280b6fSYi Wang     fn write_note(
2497e280b6fSYi Wang         &mut self,
2507e280b6fSYi Wang         dump_state: &DumpState,
2517e280b6fSYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
2527e280b6fSYi Wang         let begin = dump_state.mem_offset - dump_state.elf_note_size as u64;
2537e280b6fSYi Wang         let elf64_phdr = elf::Elf64_Phdr {
2547e280b6fSYi Wang             p_type: elf::PT_NOTE,
2557e280b6fSYi Wang             p_flags: 0,
2567e280b6fSYi Wang             p_offset: begin,
2577e280b6fSYi Wang             p_vaddr: 0,
2587e280b6fSYi Wang             p_paddr: 0,
2597e280b6fSYi Wang             p_filesz: dump_state.elf_note_size as u64,
2607e280b6fSYi Wang             p_memsz: dump_state.elf_note_size as u64,
2617e280b6fSYi Wang             p_align: 0,
2627e280b6fSYi Wang         };
2637e280b6fSYi Wang 
2647e280b6fSYi Wang         let mut coredump_file = dump_state.file.as_ref().unwrap();
2657e280b6fSYi Wang         let bytes: &[u8] = elf64_phdr.as_slice();
2667e280b6fSYi Wang         coredump_file
2677e280b6fSYi Wang             .write(bytes)
26894fb9f81SRob Bradford             .map_err(GuestDebuggableError::CoredumpFile)?;
2697e280b6fSYi Wang 
2707e280b6fSYi Wang         Ok(())
2717e280b6fSYi Wang     }
2727e280b6fSYi Wang 
write_load( &mut self, offset: u64, phys_addr: u64, length: u64, virt_addr: u64, dump_state: &DumpState, ) -> std::result::Result<(), GuestDebuggableError>2737e280b6fSYi Wang     fn write_load(
2747e280b6fSYi Wang         &mut self,
2757e280b6fSYi Wang         offset: u64,
2767e280b6fSYi Wang         phys_addr: u64,
2777e280b6fSYi Wang         length: u64,
2787e280b6fSYi Wang         virt_addr: u64,
2797e280b6fSYi Wang         dump_state: &DumpState,
2807e280b6fSYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
2817e280b6fSYi Wang         let elf64_load = elf::Elf64_Phdr {
2827e280b6fSYi Wang             p_type: elf::PT_LOAD,
2837e280b6fSYi Wang             p_flags: 0,
2847e280b6fSYi Wang             p_offset: offset,
2857e280b6fSYi Wang             p_vaddr: virt_addr,
2867e280b6fSYi Wang             p_paddr: phys_addr,
2877e280b6fSYi Wang             p_filesz: length,
2887e280b6fSYi Wang             p_memsz: length,
2897e280b6fSYi Wang             p_align: 0,
2907e280b6fSYi Wang         };
2917e280b6fSYi Wang 
2927e280b6fSYi Wang         let mut coredump_file = dump_state.file.as_ref().unwrap();
2937e280b6fSYi Wang         let bytes: &[u8] = elf64_load.as_slice();
2947e280b6fSYi Wang         coredump_file
2957e280b6fSYi Wang             .write(bytes)
29694fb9f81SRob Bradford             .map_err(GuestDebuggableError::CoredumpFile)?;
2977e280b6fSYi Wang 
2987e280b6fSYi Wang         Ok(())
2997e280b6fSYi Wang     }
3007e280b6fSYi Wang 
write_loads( &mut self, dump_state: &DumpState, ) -> std::result::Result<(), GuestDebuggableError>3017e280b6fSYi Wang     fn write_loads(
3027e280b6fSYi Wang         &mut self,
3037e280b6fSYi Wang         dump_state: &DumpState,
3047e280b6fSYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
3057e280b6fSYi Wang         let mem_info = dump_state.mem_info.as_ref().unwrap();
3067e280b6fSYi Wang 
3077e280b6fSYi Wang         for (gpa, load) in &mem_info.ram_maps {
3087e280b6fSYi Wang             self.write_load(load.mem_offset_in_elf, *gpa, load.mem_size, 0, dump_state)?;
3097e280b6fSYi Wang         }
3107e280b6fSYi Wang 
3117e280b6fSYi Wang         Ok(())
3127e280b6fSYi Wang     }
3137e280b6fSYi Wang 
elf_note_size(&self, hdr_size: u32, name_size: u32, desc_size: u32) -> u323147e280b6fSYi Wang     fn elf_note_size(&self, hdr_size: u32, name_size: u32, desc_size: u32) -> u32 {
3156164aa08SRuoqing He         (hdr_size.div_ceil(4) + name_size.div_ceil(4) + desc_size.div_ceil(4)) * 4
3167e280b6fSYi Wang     }
3177e280b6fSYi Wang 
get_note_size(&self, desc_type: NoteDescType, nr_cpus: u32) -> u323187e280b6fSYi Wang     fn get_note_size(&self, desc_type: NoteDescType, nr_cpus: u32) -> u32 {
3197e280b6fSYi Wang         let note_head_size = std::mem::size_of::<elf::Elf64_Nhdr>() as u32;
3207e280b6fSYi Wang         let elf_desc_size = std::mem::size_of::<X86_64ElfPrStatus>() as u32;
321ccb604e1SYi Wang         let cpu_state_desc_size = std::mem::size_of::<CpuState>() as u32;
3227e280b6fSYi Wang 
3237e280b6fSYi Wang         let elf_note_size = self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, elf_desc_size);
324ccb604e1SYi Wang         let vmm_note_size =
325ccb604e1SYi Wang             self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, cpu_state_desc_size);
3267e280b6fSYi Wang 
3277e280b6fSYi Wang         match desc_type {
32894fb9f81SRob Bradford             NoteDescType::Elf => elf_note_size * nr_cpus,
32994fb9f81SRob Bradford             NoteDescType::Vmm => vmm_note_size * nr_cpus,
33094fb9f81SRob Bradford             NoteDescType::ElfAndVmm => (elf_note_size + vmm_note_size) * nr_cpus,
3317e280b6fSYi Wang         }
3327e280b6fSYi Wang     }
3337e280b6fSYi Wang }
334ccb604e1SYi Wang 
335ccb604e1SYi Wang pub trait CpuElf64Writable {
cpu_write_elf64_note( &mut self, _dump_state: &DumpState, ) -> std::result::Result<(), GuestDebuggableError>336ccb604e1SYi Wang     fn cpu_write_elf64_note(
337ccb604e1SYi Wang         &mut self,
338ccb604e1SYi Wang         _dump_state: &DumpState,
339ccb604e1SYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
340ccb604e1SYi Wang         Ok(())
341ccb604e1SYi Wang     }
342ccb604e1SYi Wang 
cpu_write_vmm_note( &mut self, _dump_state: &DumpState, ) -> std::result::Result<(), GuestDebuggableError>343ccb604e1SYi Wang     fn cpu_write_vmm_note(
344ccb604e1SYi Wang         &mut self,
345ccb604e1SYi Wang         _dump_state: &DumpState,
346ccb604e1SYi Wang     ) -> std::result::Result<(), GuestDebuggableError> {
347ccb604e1SYi Wang         Ok(())
348ccb604e1SYi Wang     }
349ccb604e1SYi Wang }
350