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