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