use super::reader::GuestMem;
use super::symbols::kva_to_pa;
pub(crate) const XA_CHUNK_SIZE: u64 = 64;
pub(crate) fn translate_any_kva(
mem: &GuestMem,
cr3_pa: u64,
page_offset: u64,
kva: u64,
l5: bool,
) -> Option<u64> {
let direct_pa = kva_to_pa(kva, page_offset);
if direct_pa < mem.size() {
return Some(direct_pa);
}
mem.translate_kva(cr3_pa, kva, l5)
}
pub(crate) fn xa_load(
mem: &GuestMem,
page_offset: u64,
xa_head: u64,
index: u64,
slots_off: usize,
shift_off: usize,
) -> Option<u64> {
if xa_head == 0 {
return Some(0);
}
if xa_head & 2 == 0 {
return if index == 0 { Some(xa_head) } else { Some(0) };
}
let mut node_kva = xa_head & !3u64;
let mut shift = xa_node_shift(mem, page_offset, node_kva, shift_off);
loop {
let slot_idx = (index >> shift) & (XA_CHUNK_SIZE - 1);
let slot_pa = kva_to_pa(node_kva + slots_off as u64 + slot_idx * 8, page_offset);
let entry = mem.read_u64(slot_pa, 0);
if entry == 0 {
return Some(0);
}
if entry & 2 == 0 {
return Some(entry);
}
node_kva = entry & !3u64;
if shift < 6 {
return Some(0);
}
shift -= 6; }
}
pub(crate) fn xa_node_shift(
mem: &GuestMem,
page_offset: u64,
node_kva: u64,
shift_off: usize,
) -> u64 {
let pa = kva_to_pa(node_kva, page_offset);
mem.read_u8(pa, shift_off) as u64
}
#[cfg(test)]
mod tests {
use super::*;
use crate::monitor::symbols::DEFAULT_PAGE_OFFSET;
#[test]
fn xa_node_shift_reads_shift_byte() {
let mut buf = [0u8; 0x200];
let shift_off = 2;
buf[0x100 + shift_off] = 12;
let mem = GuestMem::new(buf.as_mut_ptr(), buf.len() as u64);
let node_kva = DEFAULT_PAGE_OFFSET + 0x100;
assert_eq!(
xa_node_shift(&mem, DEFAULT_PAGE_OFFSET, node_kva, shift_off),
12
);
}
#[test]
fn xa_node_shift_zero() {
let mut buf = [0u8; 0x200];
let mem = GuestMem::new(buf.as_mut_ptr(), buf.len() as u64);
let node_kva = DEFAULT_PAGE_OFFSET;
assert_eq!(xa_node_shift(&mem, DEFAULT_PAGE_OFFSET, node_kva, 0), 0);
}
#[test]
fn xa_node_shift_max_u8() {
let mut buf = [0u8; 0x100];
buf[0x10] = 255;
let mem = GuestMem::new(buf.as_mut_ptr(), buf.len() as u64);
let node_kva = DEFAULT_PAGE_OFFSET;
assert_eq!(
xa_node_shift(&mem, DEFAULT_PAGE_OFFSET, node_kva, 0x10),
255
);
}
}