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