ahrightos-memory 0.1.0

Portable memory primitives for the AhRightOS teaching kernel.
Documentation
use ahrightos_memory::{
    MemoryManager, Page, PageTable, PageTableEntry, PageTableFlags, PhysAddr, SimpleFrameAllocator,
    VirtAddr, VmPermissions, VmRegion, VmRegionKind, VmRegionTable, PAGE_SIZE,
};

#[test]
fn memory_crate_exposes_kernel_ready_contract() {
    let mut manager = MemoryManager::new(0x8020_0123, 0x8030_0000);
    let first = manager.frame_allocator.allocate().expect("first frame");
    assert_eq!(first.start_address().as_usize() % PAGE_SIZE, 0);

    let page = Page::containing_address(VirtAddr::new(0x4000_0123));
    assert_eq!(page.start_address(), VirtAddr::new(0x4000_0000));

    let mut table = PageTable::new();
    let entry = table.get_entry_mut(1);
    entry.set(
        PhysAddr::new(0x8020_0000).as_usize() >> 12,
        PageTableFlags::Valid as usize | PageTableFlags::Read as usize,
    );
    assert_ne!(*table.get_entry(1), PageTableEntry::new());
    table.zero();
    assert_eq!(*table.get_entry(1), PageTableEntry::new());
}

#[test]
fn frame_allocator_ignores_double_free_and_kernel_range_free() {
    let mut allocator = SimpleFrameAllocator::new(0x8020_0000, 0x8020_5000);
    let first = allocator.allocate().expect("first frame");
    let first_addr = first.start_address();

    allocator.deallocate(first);
    allocator.deallocate(first);

    let reused = allocator.allocate().expect("reused frame");
    let next = allocator.allocate().expect("next frame");
    assert_eq!(reused.start_address(), first_addr);
    assert_ne!(next.start_address(), first_addr);

    let before = allocator.next_frame();
    let kernel_frame = ahrightos_memory::PhysFrame::containing_address(PhysAddr::new(0x8010_0000));
    allocator.deallocate(kernel_frame);
    assert_eq!(allocator.next_frame(), before);
}

#[test]
fn vm_region_table_exposes_mmap_stack_and_swap_contracts() {
    let user_rw = VmPermissions::READ
        .union(VmPermissions::WRITE)
        .union(VmPermissions::USER);
    let mut table = VmRegionTable::new();

    table
        .insert(
            VmRegion::new(
                VirtAddr::new(0x4000_0000),
                PAGE_SIZE * 2,
                VmRegionKind::FileBacked,
                user_rw,
            )
            .unwrap(),
        )
        .unwrap();
    table
        .insert(
            VmRegion::new(
                VirtAddr::new(0x5000_0000),
                PAGE_SIZE,
                VmRegionKind::SwapBacked,
                user_rw,
            )
            .unwrap(),
        )
        .unwrap();

    assert_eq!(table.len(), 2);
    assert_eq!(
        table.find(VirtAddr::new(0x4000_0100)).unwrap().kind(),
        VmRegionKind::FileBacked
    );
    assert!(table.commit_page(VirtAddr::new(0x5000_0000)).unwrap());
    assert_eq!(
        table
            .find(VirtAddr::new(0x5000_0000))
            .unwrap()
            .committed_page_count(),
        1
    );
}