Rust call cause "vm fault on data at address"

@nspin sure. Happy to do so. After the first VirtIO device was accessed, no further read/ write on other VirtIO devices is possible. Read/write on VirtIO devices means the code section print_device_info e.g., ptr::read_volatile((mapped_address + 0x000) as *const u32);

Root application:

#![no_std]
#![no_main]

extern crate alloc;
//use core::error;
use core::{any::{Any, TypeId}, f32::consts::E, fmt::Debug, ops::Index};
use sel4::{
    cap_type,
    init_thread::{self, Slot},
    CapRights, CapTypeForFrameObjectOfFixedSize,
};
use core::ops::Range;
use sel4_sys::*;
use alloc::{borrow::ToOwned, string::ToString};
use alloc::vec::Vec;
use buddy_system_allocator::{FrameAllocator, LockedHeap};
use flat_device_tree::{node::FdtNode, standard_nodes::Compatible, Fdt};
use virtio_drivers::{
    device::{
        blk::VirtIOBlk,
        console::VirtIOConsole,
        gpu::VirtIOGpu,
        net::VirtIONetRaw,
        socket::{
            VirtIOSocket, VsockAddr, VsockConnectionManager, VsockEventType, VMADDR_CID_HOST,
        },
    },
    transport::{
        mmio::{MmioTransport, VirtIOHeader},
        pci::{
            bus::{BarInfo, Cam, Command, DeviceFunction, MemoryBarType, PciRoot},
            virtio_device_type, PciTransport,
        },
        DeviceType, Transport,
    },
};
use core::{
    mem::size_of,
    alloc::Layout,
    panic::PanicInfo,
    ptr::{self, NonNull},
};

use virtio_drivers::{BufferDirection, Hal, PhysAddr, PAGE_SIZE};

use sel4_sync::{lock_api::Mutex, PanickingRawMutex};
use sel4::*;
use log::{debug, error, info, trace, warn, LevelFilter};

use sel4_immutable_cell::ImmutableCell;
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap<128> = LockedHeap::<128>::new();
use sel4::*;
use sel4_root_task::{debug_println, root_task, Never};

// RUST global allocator 
const HEAP_SIZE : usize = 102400;
static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];



fn traverse_device_tree(bootinfo: &sel4::BootInfoPtr, empty_slots: &mut Vec<sel4::init_thread::Slot>, node: FdtNode) {
    
    // Never access memory as an I/O device 
    if node.name.contains(& "memory"){
        return;
    }

    print_device_info(node, bootinfo, empty_slots);

    for (position, element ) in node.children().enumerate(){
        traverse_device_tree(bootinfo,empty_slots, element);
    }
}

fn load_device_tree( bootinfo: &sel4::BootInfoPtr, empty_slots: &mut Vec<sel4::init_thread::Slot>){
    if bootinfo.extra().count() > 0 {
        for (position, element) in bootinfo.extra().enumerate(){
            if element.id == BootInfoExtraId::Fdt {
                let dt = Fdt::new(element.content()).unwrap();
                for node in dt.all_nodes(){
                    traverse_device_tree(bootinfo,empty_slots,node);
                } 
            }
        }

    }
}

fn map_u8_to_usize(bytes: &[u8]) -> Option<usize> {
    if bytes.len() >= size_of::<usize>() {
        let mut array = [0u8; size_of::<usize>()];
        for (i, byte) in bytes.iter().take(size_of::<usize>()).enumerate() {
            array[i] = *byte;
        }
        Some(usize::from_ne_bytes(array))
    } else {
        None
    }
}

fn print_device_info(node: FdtNode,bootinfo: &sel4::BootInfoPtr, empty_slots: &mut Vec<sel4::init_thread::Slot>) -> usize {
    
        // CNode stores capabilities and represents a logical component.
        let cnode = sel4::init_thread::slot::CNODE.cap();
        
        sel4::debug_println!("Name {} ", node.name);
        // VirtIO in 2 ways
        // 1. Via MMIO

        if let (Some(compatible), Some(region)) = (node.compatible(), node.reg().next()) {
            if compatible.all().any(|s| s.contains("virtio,mmio") )
                && region.size.unwrap_or(0) > size_of::<VirtIOHeader>()
            {
                sel4::debug_println!("Region size {}", region.size.unwrap_or(0) );
                let (mapped_address, dev_ut_idx) = init_device(bootinfo,empty_slots, region.starting_address as usize, region.size.unwrap_or(0) );
                
                if mapped_address == 0 {
                    if dev_ut_idx != 0{
                        sel4::debug_println!("Invalid device ");
                        //unmap_slot(dev_ut_idx);
                    }
                    return 0;
                }
                unsafe { 
                    let magic_value = ptr::read_volatile((mapped_address + 0x000) as *const u32);
                    let version = ptr::read_volatile((mapped_address + 0x004) as *const u32);
                    let device_id = ptr::read_volatile((mapped_address + 0x008) as *const u32);

                    if magic_value != 0x74726976 {
                        sel4::debug_println!("Invalid Virtio device {} {} {}",magic_value, version, device_id);
                        //unmap_slot(dev_ut_idx);
                        return 0;
                    }
                    if device_id == 0 {
                        sel4::debug_println!("Invalid Virtio device {} {} {}",magic_value, version, device_id);
                        //unmap_slot(dev_ut_idx);
                        return 0; 
                    }

                    let mut device_status = ptr::read_volatile((mapped_address + 0x070) as *const u32);
                    sel4::debug_println!("Device status {}",device_status);
                    ptr::write_volatile((mapped_address + 0x070) as *mut u32, device_status | 1 << 0);
                    ptr::write_volatile((mapped_address + 0x070) as *mut u32, device_status | 1 << 1);

                    
                    let mut device_features = ptr::read_volatile((mapped_address + 0x010) as *const u32);
                    ptr::write_volatile((mapped_address + 0x020) as *mut u32, 0 | device_features);


                    ptr::write_volatile((mapped_address + 0x030) as *mut u32, 0);
                    ptr::write_volatile((mapped_address + 0x034) as *mut u32, 2);
                    ptr::write_volatile((mapped_address + 0x030) as *mut u32, 1);
                    ptr::write_volatile((mapped_address + 0x034) as *mut u32, 2);

                    ptr::write_volatile((mapped_address + 0x044) as *mut u32, 1);
                    ptr::write_volatile((mapped_address + 0x050) as *mut u32, 1);


                    ptr::write_volatile((mapped_address + 0x070) as *mut u32, device_status | 1 << 3);

                    device_status = ptr::read_volatile((mapped_address + 0x070) as *const u32);
                    sel4::debug_println!("Device status {}",device_status);

                    sel4::debug_println!("Init device of virtio type no. {} ", device_id);
                }

          
            }
        }
        // 2. Via PCI(e)
        // 3. Special devices
        return 0;
}


fn unmap_slot(dev_ut_idx: usize) {
    let slot: Slot<cap_type::Granule> = init_thread::Slot::<cap_type::Granule>::from_index(dev_ut_idx);
    let cap = slot.cap();
    cap.frame_unmap();
}


fn device_by_address(bootinfo: &BootInfoPtr, address: usize, size : usize) -> Vec<(usize, &UntypedDesc,usize)> {
    let mut memory_range: Vec<(usize, &UntypedDesc, usize)>  = Vec::new();
    let mut allocated_bits: usize = 0;
    let mut allocated_address = address;
    while allocated_bits < size{
        // It is not ensured that the address spaces are sequentially given
        // in the untyped list of the bootinfo 
        let _result = find_untyped_memory(bootinfo, allocated_address);
        if _result.is_ok() {
            let mut _tmp = _result.unwrap();
            allocated_bits = allocated_bits + ( 1 << _tmp.1.size_bits() );
            allocated_address = address + allocated_bits +1;
            memory_range.push( _tmp );
        }
        else {
            sel4::debug_println!("Failure ");
        }
    }
    sel4::debug_println!("Required pages {}", memory_range.len() );
    return memory_range;
}

fn find_untyped_memory(bootinfo: &BootInfoPtr, address: usize) -> Result<(usize, &UntypedDesc, usize)> {
    for ( position, element) in bootinfo.untyped_list().iter().enumerate() {
        let _from = element.paddr() as usize;
        let _to = _from + ( 1 << element.size_bits() );
        let _tmp_range = ( _from ..  _to );
        if ( _tmp_range.contains(&address)) {
            let _offset = &address - _tmp_range.min().unwrap();
            
            sel4::debug_println!("Memory index found, with free space of {}", _to -&address );
            return Result::Ok( (position, element, _offset) );
        }
    }
    return Result::Err(Error::AlignmentError);
}


#[root_task]
fn main(bootinfo: &sel4::BootInfoPtr) -> sel4::Result<Never> {

    unsafe {
        // Safe because `HEAP` is only used here and `entry` is only called once.
        HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len());
    }
    sel4::debug_println!("Simplify");


    /*  # Manage untyped memory.
        # 
    */  
    let mut empty_capability_slots: Vec<sel4::init_thread::Slot> = Vec::new();
    for (position, element) in bootinfo.empty().range().enumerate() {
        empty_capability_slots.push(sel4::init_thread::Slot::from_index(element));
    }
    
    sel4::debug_println!("Amount of capability slots: {}",empty_capability_slots.len() );

    /*  # Init root CNode
        #
    */
    let cnode = sel4::init_thread::slot::CNODE;
    let cnode_capability = cnode.cap();
    
    /*  # Object allocatpr
        #
     */

    load_device_tree(bootinfo,&mut empty_capability_slots);

    sel4::debug_println!("Simply initialized ...");
    loop {}
    

}

fn init_device(bootinfo: &BootInfoPtr, empty_slots: &mut Vec<sel4::init_thread::Slot>, address: usize, size : usize) -> (usize,usize)  {
    let _untyped_vector: Vec<(usize, &UntypedDesc, usize)>  = device_by_address(bootinfo,address as usize, size); 
    if _untyped_vector.len() > 0{
        let (device_ut_ix, device_ut_desc, device_addr_offset ) = _untyped_vector.index(0);
        
        sel4::debug_println!("Index {} ", &device_ut_ix );
        if device_ut_desc.is_device() {
            unmap_slot(*device_ut_ix);
            return map_addressspace_into_thread(bootinfo, device_ut_ix, device_ut_desc, device_addr_offset, address, size, empty_slots);
        }
    }
    return (0,0);
}

fn map_addressspace_into_thread(bootinfo: &BootInfoPtr, device_ut_ix: &usize,device_ut_desc: &UntypedDesc, device_addr_offset:&usize, address: usize, size : usize, empty_slots: &mut Vec<Slot>) -> (usize, usize) {

    sel4::debug_println!("Kernel mapping ");
    let device_ut_cap = bootinfo.untyped().index(*device_ut_ix).cap();
    sel4::debug_println!("Untyped size {}",device_addr_offset );
    

    // Take a CSpace slot 
    let device_frame_slot = empty_slots
        .pop()
        .unwrap()
        .downcast::<sel4::cap_type::SmallPage>();

    // Retype the untyped memory and put it into the CSpace slot
    let cap_status = device_ut_cap
    .untyped_retype(
        &sel4::cap_type::SmallPage::object_blueprint(),
        &sel4::init_thread::slot::CNODE.cap().relative_self(),
        device_frame_slot.index(),
        1,
    );

    if cap_status.is_err() {
        sel4::debug_println!("Faile 1 ");
        return (0,*device_ut_ix);
    }
    cap_status.unwrap();
    
    // Take the capability out of the slot 
    let device_frame_cap = device_frame_slot.cap();
    
    // Map the physical address to a page table entry
    let mut device_page_addr_ptr: usize = init_free_page_addr(bootinfo) as usize;
    
    let cap_status = device_frame_cap
        .frame_map(
            sel4::init_thread::slot::VSPACE.cap(),
            device_page_addr_ptr,
            sel4::CapRights::read_write(),
            sel4::VmAttributes::default(),
        );
    if cap_status.is_err() {
        sel4::debug_println!("Faile 2 ");
        return (0,*device_ut_ix);
    }
    cap_status.unwrap();
    //return ( device_frame_cap.frame_get_address().unwrap() + device_addr_offset, *device_ut_ix);   
    return ( device_page_addr_ptr as usize + device_addr_offset, *device_ut_ix);   
}



#[derive(Copy)]
#[derive(Clone)]
#[repr(C, align(4096))]
struct SmallPage(#[allow(dead_code)] [u8; SMALL_PAGE_PLACEHOLDER_SIZE]);

static SMALL_PAGE_PLACEHOLDERS: [SmallPage; SMALL_PAGE_NUMBER] = [SmallPage([0; SMALL_PAGE_PLACEHOLDER_SIZE]); SMALL_PAGE_NUMBER];

static mut OFFSET: usize = 0;

fn init_free_page_addr(bootinfo: &sel4::BootInfoPtr) -> usize {
    let addr = unsafe { ptr::addr_of!( SMALL_PAGE_PLACEHOLDERS[OFFSET] ) as usize };
    sel4::debug_println!("Bytes {} ", cap_type::Granule::FRAME_OBJECT_TYPE.bytes() );
    sel4::debug_println!("ALLOC Page {} {} ", unsafe {OFFSET}, addr );
    unsafe { OFFSET +=1 };

    addr
}

extern "C" {
    static __executable_start: usize;
    static _end: usize;
}

fn user_image_bounds() -> Range<usize> {
    unsafe { addr_of_ref(&__executable_start)..addr_of_ref(&_end) }
}


fn addr_of_ref<T>(x: &T) -> usize {
    x as *const T as usize
}

const SMALL_PAGE_NUMBER: usize = 256;
const SMALL_PAGE_PLACEHOLDER_SIZE: usize = 4096;


QEMU startup:

	qemu-system-aarch64 \
		-machine virt,virtualization=on  \
		-cpu cortex-a57 \
		-smp 2 \
		-m 2048 \
		-nographic \
		-append "console=ttyAMA0" \
		-drive file=./disk.img,if=none,format=raw,id=blk-device-0 \
		-device virtio-blk-device,drive=blk-device-0 \
		-netdev user,id=net0 \
		-device virtio-net-device,netdev=net0 \
		-device virtio-rng-device \
		-device virtio-balloon-device \
		-device virtio-gpu-device \
		-device virtio-serial-device \
		-serial mon:stdio \
		-dtb ./devicetree.dtb \
		-kernel $(image)

Example output:

qemu-system-aarch64 -machine virt,virtualization=on -cpu cortex-a57 -smp 2 -m 2048 -nographic -append "console=ttyAMA0" -drive file=./disk.img,if=none,format=raw,id=blk-device-0 -device virtio-blk-device,drive=blk-device-0 -netdev user,id=net0 -device virtio-net-device,netdev=net0 -device virtio-rng-device -device virtio-balloon-device -device virtio-gpu-device -device virtio-serial-device -serial mon:stdio -dtb ./devicetree.dtb -gdb tcp::3333 -S -kernel build/image.elf
seL4 kernel loader | INFO   Starting loader
seL4 kernel loader | DEBUG  Platform info: PlatformInfo {
    memory: [
        0x60000000..0x80000000,
    ],
    devices: [
        0x0..0x8000000,
        0x8001000..0x8010000,
        0x8011000..0x8030000,
        0x8031000..0x60000000,
        0x80000000..0x100000000000,
    ],
}
seL4 kernel loader | DEBUG  Loader footprint: 0x6027f000..0x60477b17
seL4 kernel loader | DEBUG  Payload info: PayloadInfo {
    kernel_image: ImageInfo {
        phys_addr_range: 0x60000000..0x6023f000,
        phys_to_virt_offset: 0x8000000000,
        virt_entry: 0x8060000000,
    },
    user_image: ImageInfo {
        phys_addr_range: 0x7fe5e000..0x80000000,
        phys_to_virt_offset: 0xffffffff803a2000,
        virt_entry: 0x3345f8,
    },
    fdt_phys_addr_range: Some(
        0x7fe5c000..0x7fe5dddc,
    ),
}
seL4 kernel loader | DEBUG  Payload regions:
seL4 kernel loader | DEBUG      0x60000000..60031da0 true
seL4 kernel loader | DEBUG      0x60031da0..6023f000 false
seL4 kernel loader | DEBUG      0x7fe5e000..7ff877ac true
seL4 kernel loader | DEBUG      0x7ff887b0..7ffd4d74 true
seL4 kernel loader | DEBUG      0x7ffd5d78..7ffd5d90 true
seL4 kernel loader | DEBUG      0x7ffd5d90..7ffff208 false
seL4 kernel loader | DEBUG      0x7fe5c000..7fe5dddc true
seL4 kernel loader | DEBUG  Copying payload data
seL4 kernel loader | INFO   Entering kernel
Bootstrapping kernel
available phys memory regions: 1
  [60000000..80000000]
reserved virt address space regions: 3
  [8060000000..806023f000]
  [807fe5c000..807fe5dddc]
  [807fe5e000..8080000000]
Booting all finished, dropped to user space
Simplify
Amount of capability slots: 3592
Name / 
Name psci 
Name platform@c000000 
Name fw-cfg@9020000 
Name virtio_mmio@a000000 
Region size 512
Memory index found, with free space of 33554432
Required pages 1
Index 21 
Kernel mapping 
Untyped size 0
Bytes 4096 
ALLOC Page 0 2109440 
Invalid Virtio device 1953655158 1 0
Name virtio_mmio@a000200 
Region size 512
Memory index found, with free space of 33553920
Required pages 1
Index 21 
Kernel mapping 
Untyped size 512
Bytes 4096 
ALLOC Page 1 2113536 
Invalid Virtio device 1953655158 1 0
Name virtio_mmio@a000400 
Region size 512
Memory index found, with free space of 33553408
Required pages 1
Index 21 
Kernel mapping 
Untyped size 1024
Bytes 4096 
ALLOC Page 2 2117632 
Invalid Virtio device 1953655158 1 0
Name virtio_mmio@a000600 
Region size 512
Memory index found, with free space of 33552896
Required pages 1
Index 21 
Kernel mapping 
Untyped size 1536
Bytes 4096 
ALLOC Page 3 2121728 
Device status 0
Device status 8
Init device of virtio type no. 16 
Name virtio_mmio@a000800 
Region size 512
Memory index found, with free space of 33552384
Required pages 1
Index 21 
Kernel mapping 
Untyped size 2048
Bytes 4096 
ALLOC Page 4 2125824 
Caught cap fault in send phase at address 0
while trying to handle:
vm fault on data at address 0x207800 with status 0x92000010
in thread 0x807fe49400 "rootserver" at address 0x35048c
With stack:
0x39ec20: 0x39ebd0
0x39ec28: 0x207800
0x39ec30: 0x207800
0x39ec38: 0x32b4d0
0x39ec40: 0x3315c0
0x39ec48: 0x33152c
0x39ec50: 0x0
0x39ec58: 0x202480
0x39ec60: 0x202486
0x39ec68: 0x202480
0x39ec70: 0x202486
0x39ec78: 0x202480
0x39ec80: 0x3749d0
0x39ec88: 0x6
0x39ec90: 0x202480
0x39ec98: 0x100000006