vmm: add cpu segment note for coredump

The crash tool use a special note segment which named 'QEMU' to
analyze kaslr info and so on. If we don't add the 'QEMU' note
segment, crash tool can't find linux version to move on.

For now, the most convenient way is to add 'QEMU' note segment to
make crash tool happy.

Signed-off-by: Yi Wang <wang.yi59@zte.com.cn>
This commit is contained in:
Yi Wang
2022-05-24 10:39:55 +08:00
committed by Sebastien Boeuf
parent 0e65ca4a6c
commit ccb604e1e1
3 changed files with 364 additions and 5 deletions

View File

@@ -3,6 +3,10 @@
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "x86_64")]
use hypervisor::kvm::kvm_bindings::kvm_dtable as DTableRegister;
#[cfg(target_arch = "x86_64")]
use hypervisor::x86_64::SegmentRegister;
use linux_loader::elf;
use std::fs::File;
use std::io::Write;
@@ -72,6 +76,8 @@ pub struct X86_64UserRegs {
pub gs: u64,
}
unsafe impl ByteValued for X86_64UserRegs {}
#[repr(C)]
#[allow(dead_code)]
pub struct X86_64ElfPrStatus {
@@ -82,12 +88,101 @@ pub struct X86_64ElfPrStatus {
pub pad3: [u8; 8],
}
#[repr(C)]
#[derive(Default, Copy, Clone)]
pub struct CpuSegment {
pub selector: u32,
pub limit: u32,
pub flags: u32,
pub pad: u32,
pub base: u64,
}
const DESC_TYPE_SHIFT: u32 = 8;
const DESC_S_SHIFT: u32 = 12;
const DESC_DPL_SHIFT: u32 = 13;
const DESC_P_SHIFT: u32 = 15;
const DESC_P_MASK: u32 = 1 << DESC_P_SHIFT;
const DESC_AVL_SHIFT: u32 = 20;
const DESC_AVL_MASK: u32 = 1 << DESC_AVL_SHIFT;
const DESC_L_SHIFT: u32 = 21;
const DESC_B_SHIFT: u32 = 22;
const DESC_S_MASK: u32 = 1 << DESC_S_SHIFT;
const DESC_G_SHIFT: u32 = 23;
const DESC_G_MASK: u32 = 1 << DESC_G_SHIFT;
impl CpuSegment {
pub fn new(reg: SegmentRegister) -> Self {
let p_mask = if (reg.present > 0) && (reg.unusable == 0) {
DESC_P_MASK
} else {
0
};
let flags = ((reg.type_ as u32) << DESC_TYPE_SHIFT)
| p_mask
| ((reg.dpl as u32) << DESC_DPL_SHIFT)
| ((reg.db as u32) << DESC_B_SHIFT)
| ((reg.s as u32) * DESC_S_MASK)
| ((reg.l as u32) << DESC_L_SHIFT)
| ((reg.g as u32) * DESC_G_MASK)
| ((reg.avl as u32) * DESC_AVL_MASK);
CpuSegment {
selector: reg.selector as u32,
limit: reg.limit as u32,
flags,
pad: 0,
base: reg.base,
}
}
pub fn new_from_table(reg: DTableRegister) -> Self {
CpuSegment {
selector: 0,
limit: reg.limit as u32,
flags: 0,
pad: 0,
base: reg.base,
}
}
}
#[repr(C)]
#[derive(Default, Copy, Clone)]
pub struct CpuState {
pub version: u32,
pub size: u32,
/// rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp
pub regs1: [u64; 8],
/// r8, r9, r10, r11, r12, r13, r14, r15
pub regs2: [u64; 8],
pub rip: u64,
pub rflags: u64,
pub cs: CpuSegment,
pub ds: CpuSegment,
pub es: CpuSegment,
pub fs: CpuSegment,
pub gs: CpuSegment,
pub ss: CpuSegment,
pub ldt: CpuSegment,
pub tr: CpuSegment,
pub gdt: CpuSegment,
pub idt: CpuSegment,
pub cr: [u64; 5],
pub kernel_gs_base: u64,
}
unsafe impl ByteValued for CpuState {}
pub enum NoteDescType {
ElfDesc = 0,
VmmDesc = 1,
ElfAndVmmDesc = 2,
}
// "CORE" or "QEMU"
pub const COREDUMP_NAME_SIZE: u32 = 5;
pub const NT_PRSTATUS: u32 = 1;
/// Core file.
const ET_CORE: u16 = 4;
@@ -223,11 +318,32 @@ pub trait Elf64Writable {
fn get_note_size(&self, desc_type: NoteDescType, nr_cpus: u32) -> u32 {
let note_head_size = std::mem::size_of::<elf::Elf64_Nhdr>() as u32;
let elf_desc_size = std::mem::size_of::<X86_64ElfPrStatus>() as u32;
let cpu_state_desc_size = std::mem::size_of::<CpuState>() as u32;
let elf_note_size = self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, elf_desc_size);
let vmm_note_size =
self.elf_note_size(note_head_size, COREDUMP_NAME_SIZE, cpu_state_desc_size);
match desc_type {
NoteDescType::ElfDesc => elf_note_size * nr_cpus,
NoteDescType::VmmDesc => vmm_note_size * nr_cpus,
NoteDescType::ElfAndVmmDesc => (elf_note_size + vmm_note_size) * nr_cpus,
}
}
}
pub trait CpuElf64Writable {
fn cpu_write_elf64_note(
&mut self,
_dump_state: &DumpState,
) -> std::result::Result<(), GuestDebuggableError> {
Ok(())
}
fn cpu_write_vmm_note(
&mut self,
_dump_state: &DumpState,
) -> std::result::Result<(), GuestDebuggableError> {
Ok(())
}
}