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