xref: /cloud-hypervisor/vmm/src/coredump.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
1 // Copyright © 2022 ZTE Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use std::fs::File;
7 use std::io::Write;
8 
9 #[cfg(target_arch = "x86_64")]
10 use hypervisor::arch::x86::{DescriptorTable, SegmentRegister};
11 use linux_loader::elf;
12 use vm_memory::ByteValued;
13 
14 #[derive(Clone)]
15 pub struct CoredumpMemoryRegion {
16     pub mem_offset_in_elf: u64,
17     pub mem_size: u64,
18 }
19 
20 #[derive(Clone)]
21 pub struct CoredumpMemoryRegions {
22     pub ram_maps: std::collections::BTreeMap<u64, CoredumpMemoryRegion>,
23 }
24 
25 /// Platform information
26 #[derive(Default)]
27 pub struct DumpState {
28     pub elf_note_size: isize,
29     pub elf_phdr_num: u16,
30     pub elf_sh_info: u32,
31     pub mem_offset: u64,
32     pub mem_info: Option<CoredumpMemoryRegions>,
33     pub file: Option<File>,
34 }
35 
36 #[derive(Debug)]
37 pub enum GuestDebuggableError {
38     Coredump(anyhow::Error),
39     CoredumpFile(std::io::Error),
40     Pause(vm_migration::MigratableError),
41     Resume(vm_migration::MigratableError),
42 }
43 
44 pub trait GuestDebuggable: vm_migration::Pausable {
45     fn coredump(
46         &mut self,
47         _destination_url: &str,
48     ) -> std::result::Result<(), GuestDebuggableError> {
49         Ok(())
50     }
51 }
52 
53 #[repr(C)]
54 #[derive(Default, Copy, Clone)]
55 pub struct X86_64UserRegs {
56     /// r15, r14, r13, r12, rbp, rbx, r11, r10;
57     pub regs1: [u64; 8],
58     /// r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax;
59     pub regs2: [u64; 8],
60     pub rip: u64,
61     pub cs: u64,
62     pub eflags: u64,
63     pub rsp: u64,
64     pub ss: u64,
65     pub fs_base: u64,
66     pub gs_base: u64,
67     pub ds: u64,
68     pub es: u64,
69     pub fs: u64,
70     pub gs: u64,
71 }
72 
73 // SAFETY: This is just a series of bytes
74 unsafe impl ByteValued for X86_64UserRegs {}
75 
76 #[repr(C)]
77 pub struct X86_64ElfPrStatus {
78     pub pad1: [u8; 32],
79     pub pid: u32,
80     pub pads2: [u8; 76],
81     pub regs: X86_64UserRegs,
82     pub pad3: [u8; 8],
83 }
84 
85 #[repr(C)]
86 #[derive(Default, Copy, Clone)]
87 pub struct CpuSegment {
88     pub selector: u32,
89     pub limit: u32,
90     pub flags: u32,
91     pub pad: u32,
92     pub base: u64,
93 }
94 
95 const DESC_TYPE_SHIFT: u32 = 8;
96 const DESC_S_SHIFT: u32 = 12;
97 const DESC_DPL_SHIFT: u32 = 13;
98 const DESC_P_SHIFT: u32 = 15;
99 const DESC_P_MASK: u32 = 1 << DESC_P_SHIFT;
100 const DESC_AVL_SHIFT: u32 = 20;
101 const DESC_AVL_MASK: u32 = 1 << DESC_AVL_SHIFT;
102 const DESC_L_SHIFT: u32 = 21;
103 const DESC_B_SHIFT: u32 = 22;
104 const DESC_S_MASK: u32 = 1 << DESC_S_SHIFT;
105 const DESC_G_SHIFT: u32 = 23;
106 const DESC_G_MASK: u32 = 1 << DESC_G_SHIFT;
107 
108 impl CpuSegment {
109     pub fn new(reg: SegmentRegister) -> Self {
110         let p_mask = if (reg.present > 0) && (reg.unusable == 0) {
111             DESC_P_MASK
112         } else {
113             0
114         };
115         let flags = ((reg.type_ as u32) << DESC_TYPE_SHIFT)
116             | p_mask
117             | ((reg.dpl as u32) << DESC_DPL_SHIFT)
118             | ((reg.db as u32) << DESC_B_SHIFT)
119             | ((reg.s as u32) * DESC_S_MASK)
120             | ((reg.l as u32) << DESC_L_SHIFT)
121             | ((reg.g as u32) * DESC_G_MASK)
122             | ((reg.avl as u32) * DESC_AVL_MASK);
123 
124         CpuSegment {
125             selector: reg.selector as u32,
126             limit: reg.limit,
127             flags,
128             pad: 0,
129             base: reg.base,
130         }
131     }
132 
133     pub fn new_from_table(reg: DescriptorTable) -> Self {
134         CpuSegment {
135             selector: 0,
136             limit: reg.limit as u32,
137             flags: 0,
138             pad: 0,
139             base: reg.base,
140         }
141     }
142 }
143 
144 #[repr(C)]
145 #[derive(Default, Copy, Clone)]
146 pub struct CpuState {
147     pub version: u32,
148     pub size: u32,
149     /// rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp
150     pub regs1: [u64; 8],
151     /// r8, r9, r10, r11, r12, r13, r14, r15
152     pub regs2: [u64; 8],
153     pub rip: u64,
154     pub rflags: u64,
155     pub cs: CpuSegment,
156     pub ds: CpuSegment,
157     pub es: CpuSegment,
158     pub fs: CpuSegment,
159     pub gs: CpuSegment,
160     pub ss: CpuSegment,
161     pub ldt: CpuSegment,
162     pub tr: CpuSegment,
163     pub gdt: CpuSegment,
164     pub idt: CpuSegment,
165     pub cr: [u64; 5],
166     pub kernel_gs_base: u64,
167 }
168 
169 // SAFETY: This is just a series of bytes
170 unsafe impl ByteValued for CpuState {}
171 
172 pub enum NoteDescType {
173     Elf = 0,
174     Vmm = 1,
175     ElfAndVmm = 2,
176 }
177 
178 // "CORE" or "QEMU"
179 pub const COREDUMP_NAME_SIZE: u32 = 5;
180 pub const NT_PRSTATUS: u32 = 1;
181 
182 /// Core file.
183 const ET_CORE: u16 = 4;
184 /// 64-bit object.
185 const ELFCLASS64: u8 = 2;
186 /// Current ELF version.
187 const EV_CURRENT: u8 = 1;
188 /// AMD x86-64 architecture
189 const EM_X86_64: u16 = 62;
190 
191 pub trait Elf64Writable {
192     fn write_header(
193         &mut self,
194         dump_state: &DumpState,
195     ) -> std::result::Result<(), GuestDebuggableError> {
196         let e_ident = [
197             elf::ELFMAG0 as u8, // magic
198             elf::ELFMAG1,
199             elf::ELFMAG2,
200             elf::ELFMAG3,
201             ELFCLASS64,             // class
202             elf::ELFDATA2LSB as u8, //data
203             EV_CURRENT,             // version
204             0,                      // os_abi
205             0,                      // abi_version
206             0,                      // padding
207             0,
208             0,
209             0,
210             0,
211             0,
212             0,
213         ];
214         let elf64_ehdr_size = std::mem::size_of::<elf::Elf64_Ehdr>();
215         let elf64_phdr_size = std::mem::size_of::<elf::Elf64_Phdr>();
216         let mut elf64_ehdr = elf::Elf64_Ehdr {
217             e_ident,
218             e_type: ET_CORE,
219             e_machine: EM_X86_64,
220             e_version: EV_CURRENT as u32,
221             e_entry: 0,
222             e_phoff: elf64_ehdr_size as u64,
223             e_shoff: 0,
224             e_flags: 0,
225             e_ehsize: 0,
226             e_phentsize: elf64_phdr_size as u16,
227             e_phnum: dump_state.elf_phdr_num,
228             e_shentsize: 0,
229             e_shnum: 0,
230             e_shstrndx: 0,
231         };
232         elf64_ehdr.e_ehsize = std::mem::size_of_val(&elf64_ehdr) as u16;
233 
234         let mut coredump_file = dump_state.file.as_ref().unwrap();
235         let bytes: &[u8] = elf64_ehdr.as_slice();
236         coredump_file
237             .write(bytes)
238             .map_err(GuestDebuggableError::CoredumpFile)?;
239 
240         Ok(())
241     }
242 
243     fn write_note(
244         &mut self,
245         dump_state: &DumpState,
246     ) -> std::result::Result<(), GuestDebuggableError> {
247         let begin = dump_state.mem_offset - dump_state.elf_note_size as u64;
248         let elf64_phdr = elf::Elf64_Phdr {
249             p_type: elf::PT_NOTE,
250             p_flags: 0,
251             p_offset: begin,
252             p_vaddr: 0,
253             p_paddr: 0,
254             p_filesz: dump_state.elf_note_size as u64,
255             p_memsz: dump_state.elf_note_size as u64,
256             p_align: 0,
257         };
258 
259         let mut coredump_file = dump_state.file.as_ref().unwrap();
260         let bytes: &[u8] = elf64_phdr.as_slice();
261         coredump_file
262             .write(bytes)
263             .map_err(GuestDebuggableError::CoredumpFile)?;
264 
265         Ok(())
266     }
267 
268     fn write_load(
269         &mut self,
270         offset: u64,
271         phys_addr: u64,
272         length: u64,
273         virt_addr: u64,
274         dump_state: &DumpState,
275     ) -> std::result::Result<(), GuestDebuggableError> {
276         let elf64_load = elf::Elf64_Phdr {
277             p_type: elf::PT_LOAD,
278             p_flags: 0,
279             p_offset: offset,
280             p_vaddr: virt_addr,
281             p_paddr: phys_addr,
282             p_filesz: length,
283             p_memsz: length,
284             p_align: 0,
285         };
286 
287         let mut coredump_file = dump_state.file.as_ref().unwrap();
288         let bytes: &[u8] = elf64_load.as_slice();
289         coredump_file
290             .write(bytes)
291             .map_err(GuestDebuggableError::CoredumpFile)?;
292 
293         Ok(())
294     }
295 
296     fn write_loads(
297         &mut self,
298         dump_state: &DumpState,
299     ) -> std::result::Result<(), GuestDebuggableError> {
300         let mem_info = dump_state.mem_info.as_ref().unwrap();
301 
302         for (gpa, load) in &mem_info.ram_maps {
303             self.write_load(load.mem_offset_in_elf, *gpa, load.mem_size, 0, dump_state)?;
304         }
305 
306         Ok(())
307     }
308 
309     fn elf_note_size(&self, hdr_size: u32, name_size: u32, desc_size: u32) -> u32 {
310         (hdr_size.div_ceil(4) + name_size.div_ceil(4) + desc_size.div_ceil(4)) * 4
311     }
312 
313     fn get_note_size(&self, desc_type: NoteDescType, nr_cpus: u32) -> u32 {
314         let note_head_size = std::mem::size_of::<elf::Elf64_Nhdr>() as u32;
315         let elf_desc_size = std::mem::size_of::<X86_64ElfPrStatus>() as u32;
316         let cpu_state_desc_size = std::mem::size_of::<CpuState>() as u32;
317 
318         let elf_note_size = self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, elf_desc_size);
319         let vmm_note_size =
320             self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, cpu_state_desc_size);
321 
322         match desc_type {
323             NoteDescType::Elf => elf_note_size * nr_cpus,
324             NoteDescType::Vmm => vmm_note_size * nr_cpus,
325             NoteDescType::ElfAndVmm => (elf_note_size + vmm_note_size) * nr_cpus,
326         }
327     }
328 }
329 
330 pub trait CpuElf64Writable {
331     fn cpu_write_elf64_note(
332         &mut self,
333         _dump_state: &DumpState,
334     ) -> std::result::Result<(), GuestDebuggableError> {
335         Ok(())
336     }
337 
338     fn cpu_write_vmm_note(
339         &mut self,
340         _dump_state: &DumpState,
341     ) -> std::result::Result<(), GuestDebuggableError> {
342         Ok(())
343     }
344 }
345