use crate::addr_width::AddrWidth;
use crate::cli::VirtualAddress;
pub fn one_bitmask_of_length(mut val: u64) -> u64 {
assert!(val <= 64);
let mut bitmask = 0;
while val > 0 {
bitmask <<= 1;
bitmask |= 1;
val -= 1;
}
bitmask
}
#[derive(Debug)]
pub struct PageTableLookupMetaInfo {
#[allow(unused)]
pub v_addr: VirtualAddress,
pub level: u64,
pub index: u64,
#[allow(unused)]
pub shift: u64,
#[allow(unused)]
pub relevant_part_of_addr: u64,
}
pub fn calculate_page_table_index(
index_bits: u64,
page_offset_bits: u64,
v_addr: impl Into<VirtualAddress>,
level: u64,
addr_width: AddrWidth,
) -> PageTableLookupMetaInfo {
assert!(index_bits > 0);
assert!(page_offset_bits > 0);
assert!(level > 0);
let v_addr = v_addr.into();
let addr = u64::from(v_addr);
let addr = if addr_width == AddrWidth::Bits32 {
addr & u32::MAX as u64
} else {
addr
};
let shift = index_bits * (level - 1) + page_offset_bits;
let shifted_addr = addr >> shift;
let bitmask = one_bitmask_of_length(index_bits);
let index = shifted_addr & bitmask;
let relevant_part_of_addr = addr & (bitmask << shift);
PageTableLookupMetaInfo {
v_addr,
level,
index,
shift,
relevant_part_of_addr,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_one_bitmask_of_length() {
assert_eq!(one_bitmask_of_length(0), 0);
assert_eq!(one_bitmask_of_length(1), 1);
assert_eq!(one_bitmask_of_length(2), 0b11);
assert_eq!(one_bitmask_of_length(4), 0xf);
assert_eq!(one_bitmask_of_length(64), !0);
}
#[test]
fn test_calculate_page_table_index_x86() {
#[allow(clippy::unusual_byte_groupings)]
let addr = 0b1111111111_1010101010_001111000011;
{
let PageTableLookupMetaInfo {
index: l2_index,
relevant_part_of_addr: l2_bits,
..
} = calculate_page_table_index(10, 12, addr, 2, AddrWidth::Bits32);
assert_eq!(
l2_index, 0b1111111111,
"Should be 0b1111111111 but is {l2_index:#b}",
);
let expected_bits: u64 = 0b1111111111 << (10 + 12);
assert_eq!(
l2_bits, expected_bits,
"Should be {l2_bits:#b} but is {expected_bits:#b}"
);
}
{
let PageTableLookupMetaInfo {
index: l1_index,
relevant_part_of_addr: l1_bits,
..
} = calculate_page_table_index(10, 12, addr, 1, AddrWidth::Bits32);
assert_eq!(
l1_index, 0b1010101010,
"Should be 0b1010101010 but is {l1_index:#b}",
);
let expected_bits: u64 = 0b1010101010 << 12;
assert_eq!(
l1_bits, expected_bits,
"Should be {l1_bits:#b} but is {expected_bits:#b}"
);
}
}
#[test]
fn test_calculate_page_table_index_x86_pae() {
#[allow(clippy::unusual_byte_groupings)]
let addr = 0b10_111111111_010101010_001111000011;
{
let PageTableLookupMetaInfo {
index: l3_index,
relevant_part_of_addr: l3_bits,
..
} = calculate_page_table_index(9, 12, addr, 3, AddrWidth::Bits32);
assert_eq!(l3_index, 0b10, "Should be 0b10 but is {l3_index:#b}",);
let expected_bits: u64 = 0b10 << (9 * 2 + 12);
assert_eq!(
l3_bits, expected_bits,
"Should be {l3_bits:#b} but is {expected_bits:#b}"
);
}
{
let PageTableLookupMetaInfo {
index: l2_index,
relevant_part_of_addr: l2_bits,
..
} = calculate_page_table_index(9, 12, addr, 2, AddrWidth::Bits32);
assert_eq!(
l2_index, 0b111111111,
"Should be 0b111111111 but is {l2_index:#b}",
);
let expected_bits: u64 = 0b111111111 << (9 + 12);
assert_eq!(
l2_bits, expected_bits,
"Should be {l2_bits:#b} but is {expected_bits:#b}"
);
}
{
let PageTableLookupMetaInfo {
index: l1_index,
relevant_part_of_addr: l1_bits,
..
} = calculate_page_table_index(9, 12, addr, 1, AddrWidth::Bits32);
assert_eq!(
l1_index, 0b010101010,
"Should be 0b010101010 but is {l1_index:#b}",
);
let expected_bits: u64 = 0b010101010 << 12;
assert_eq!(
l1_bits, expected_bits,
"Should be {l1_bits:#b} but is {expected_bits:#b}"
);
}
}
#[test]
#[allow(clippy::identity_op)]
fn test_calculate_page_table_index_64() {
#[allow(clippy::unusual_byte_groupings)]
let addr = 0b000100000_000011111_111111111_010101010_001111000011;
{
let PageTableLookupMetaInfo {
index: l4_index,
relevant_part_of_addr: l4_bits,
..
} = calculate_page_table_index(9, 12, addr, 4, AddrWidth::Bits64);
assert_eq!(
l4_index, 0b000100000,
"Should be 0b000100000 but is {l4_index:#b}"
);
let expected_bits: u64 = 0b000100000 << (3 * 9 + 12);
assert_eq!(
l4_bits, expected_bits,
"Should be {l4_bits:#b} but is {expected_bits:#b}"
);
}
{
let PageTableLookupMetaInfo {
index: l3_index,
relevant_part_of_addr: l3_bits,
..
} = calculate_page_table_index(9, 12, addr, 3, AddrWidth::Bits64);
assert_eq!(
l3_index, 0b000011111,
"Should be 0b000011111 but is {l3_index:#b}"
);
let expected_bits: u64 = 0b000011111 << (2 * 9 + 12);
assert_eq!(
l3_bits, expected_bits,
"Should be {l3_bits:#b} but is {expected_bits:#b}"
);
}
{
let PageTableLookupMetaInfo {
index: l2_index,
relevant_part_of_addr: l2_bits,
..
} = calculate_page_table_index(9, 12, addr, 2, AddrWidth::Bits64);
assert_eq!(
l2_index, 0b111111111,
"Should be 0b111111111 but is {l2_index:#b}"
);
let expected_bits: u64 = 0b111111111 << (9 + 12);
assert_eq!(
l2_bits, expected_bits,
"Should be {l2_bits:#b} but is {expected_bits:#b}"
);
}
{
let PageTableLookupMetaInfo {
index: l1_index,
relevant_part_of_addr: l1_bits,
..
} = calculate_page_table_index(9, 12, addr, 1, AddrWidth::Bits64);
assert_eq!(
l1_index, 0b010101010,
"Should be 0b010101010 but is {l1_index:#b}"
);
let expected_bits: u64 = 0b010101010 << 12;
assert_eq!(
l1_bits, expected_bits,
"Should be {l1_bits:#b} but is {expected_bits:#b}"
);
}
}
}