page-table-generic 0.2.2

A library for general page table operations
Documentation
extern crate std;
use std::{
    alloc::{self, Layout},
    mem,
    ptr::NonNull,
};

use log::trace;
use page_table_generic::*;

use tock_registers::{interfaces::Readable, register_bitfields, registers::ReadWrite};

register_bitfields! [
    u64,
    PTE [
        PA OFFSET(0) NUMBITS(48) [
        ],
        READ OFFSET(48) NUMBITS(1) [
        ],
        WRITE OFFSET(49) NUMBITS(1) [
        ],
        USER_EXECUTE OFFSET(50) NUMBITS(1) [
        ],
        USER_ACCESS OFFSET(51) NUMBITS(1) [
        ],
        PRIVILEGE_EXECUTE OFFSET(52) NUMBITS(1) [
        ],
        BLOCK OFFSET(53) NUMBITS(1) [
        ],
        DEVICE OFFSET(54) NUMBITS(1) [
        ],
    ],
];

#[derive(Clone, Copy)]
struct PteImpl;

impl PTEArch for PteImpl {
    fn page_size() -> usize {
        4096
    }

    fn new_pte(config: PTEGeneric) -> usize {
        let paddr = config.paddr >> 12;
        let mut v = PTE::PA.val(paddr as _);

        if config.setting.privilege_access.readable() {
            v += PTE::READ::SET;
        }

        if config.setting.privilege_access.writable() {
            v += PTE::WRITE::SET;
        }

        if config.setting.user_access.readable() | config.setting.user_access.writable() {
            v += PTE::USER_ACCESS::SET;
        }
        if config.setting.privilege_access.executable() {
            v += PTE::PRIVILEGE_EXECUTE::SET;
        }

        match config.setting.cache_setting {
            CacheSetting::Normal => {}
            CacheSetting::Device => {
                v += PTE::DEVICE::SET;
            }
            CacheSetting::NonCache => {}
        }

        if config.is_block {
            v += PTE::BLOCK::SET;
        }

        v.value as _
    }

    fn read_pte(pte: usize) -> PTEGeneric {
        let v: ReadWrite<u64, PTE::Register> = unsafe { mem::transmute(pte) };
        let paddr: u64 = v.read(PTE::PA) << 12;

        let mut privilege_access = AccessSetting::empty();
        let mut user_access = AccessSetting::empty();
        let mut is_valid = false;

        if v.is_set(PTE::READ) {
            privilege_access |= AccessSetting::Read;
            is_valid = true;
        }
        if v.is_set(PTE::WRITE) {
            privilege_access |= AccessSetting::Write;
        }
        if v.is_set(PTE::USER_ACCESS) {
            user_access |= AccessSetting::Read;

            if v.is_set(PTE::WRITE) {
                user_access |= AccessSetting::Write;
            }
        }
        if v.is_set(PTE::USER_EXECUTE) {
            user_access |= AccessSetting::Execute;
        }
        if v.is_set(PTE::PRIVILEGE_EXECUTE) {
            privilege_access |= AccessSetting::Execute;
        }
        let cache_setting = if v.is_set(PTE::DEVICE) {
            CacheSetting::Device
        } else {
            CacheSetting::Normal
        };

        PTEGeneric {
            paddr: paddr as usize,
            is_block: v.is_set(PTE::BLOCK),
            setting: PTESetting {
                cache_setting,
                privilege_access,
                user_access,
                is_global: true,
            },
            is_valid,
        }
    }

    fn level() -> usize {
        4
    }
}

struct AccessImpl;

impl Access for AccessImpl {
    fn va_offset(&self) -> usize {
        0
    }

    unsafe fn alloc(&mut self, layout: Layout) -> Option<NonNull<u8>> {
        let ptr = alloc::alloc(layout);
        trace!("alloc: {:?}", ptr);
        NonNull::new(ptr)
    }

    unsafe fn dealloc(&mut self, mut ptr: NonNull<u8>, layout: Layout) {
        trace!("dealloc: {:?}", ptr);
        alloc::dealloc(ptr.as_mut(), layout);
    }
}

#[test]
fn test_pte() {
    let want = PTEGeneric {
        paddr: 0xfff123456780000,
        is_block: false,
        setting: PTESetting {
            cache_setting: CacheSetting::Normal,
            privilege_access: AccessSetting::empty(),
            user_access: AccessSetting::empty(),
            is_global: true,
        },
        is_valid: true,
    };

    let v = PteImpl::new_pte(want.clone());
    let pte = PteImpl::read_pte(v);

    assert_eq!(want.paddr, pte.paddr);
}

#[test]
fn test_new() {
    let _ = env_logger::builder()
        .is_test(true)
        .filter_level(log::LevelFilter::Trace)
        .try_init();

    let mut access = AccessImpl;

    let mut pg = PageTableRef::<PteImpl>::create_empty(&mut access).unwrap();
    unsafe {
        pg.map_region(
            MapConfig::new(
                0xffff000000000000usize as _,
                0x0000,
                AccessSetting::Read | AccessSetting::Write,
                CacheSetting::Device,
            ),
            0x2000,
            false,
            &mut access,
        )
        .unwrap();
    }
    let msg = pg
        .as_slice(&access)
        .iter()
        .filter_map(|o| {
            let v = o.as_usize();
            if v > 0 {
                Some(format!("{:#x}", v))
            } else {
                None
            }
        })
        .collect::<Vec<_>>()
        .join(", ");

    println!("vec: {}", msg);

    let list = pg.iter_all(&access).collect::<Vec<_>>();

    for i in &list {
        println!("l: {:x}, va: {:#p} c: {:?}", i.level, i.vaddr, i.pte);
    }

    assert_eq!(list.len(), 5);
}

#[test]
fn test_block() {
    let _ = env_logger::builder()
        .is_test(true)
        .filter_level(log::LevelFilter::Trace)
        .try_init();

    let mut access = AccessImpl;

    let mut pg = PageTableRef::<PteImpl>::create_empty(&mut access).unwrap();
    unsafe {
        pg.map_region(
            MapConfig::new(
                0xffff000000000000usize as _,
                0x0000,
                AccessSetting::Read | AccessSetting::Write,
                CacheSetting::Device,
            ),
            1024 * 1024 * 1024,
            true,
            &mut access,
        )
        .unwrap();
    }
    let msg = pg
        .as_slice(&access)
        .iter()
        .filter_map(|o| {
            let v = o.as_usize();
            if v > 0 {
                Some(format!("{:#x}", v))
            } else {
                None
            }
        })
        .collect::<Vec<_>>()
        .join(", ");

    println!("vec: {}", msg);

    let list = pg.iter_all(&access).collect::<Vec<_>>();

    for i in &list {
        println!("l: {:x}, va: {:#p} c: {:?}", i.level, i.vaddr, i.pte);
    }

    assert_eq!(list.len(), 2);
    assert!(list.last().unwrap().pte.is_block);
}

#[test]
fn test_release() {
    let _ = env_logger::builder()
        .is_test(true)
        .filter_level(log::LevelFilter::Trace)
        .try_init();

    let mut access = AccessImpl;

    let mut pg = PageTableRef::<PteImpl>::create_empty(&mut access).unwrap();
    unsafe {
        pg.map_region(
            MapConfig::new(
                0xffff000000000000usize as _,
                0x0000,
                AccessSetting::Read | AccessSetting::Write,
                CacheSetting::Device,
            )
            .set_user_access(AccessSetting::Read),
            0x2000,
            false,
            &mut access,
        )
        .unwrap();
    }
    for i in pg.iter_all(&access) {
        println!("l: {:x}, va: {:#p} c: {:?}", i.level, i.vaddr, i.pte);
    }
    pg.release(&mut access);
}