rust-riscv-os c to rust virtio-drivers lib callback

https://learningos.github.io/rust-based-os-comp2022/chapter8/index.html
https://zhuanlan.zhihu.com/p/344819263
https://avacariu.me/writing/2014/calling-rust-from-c
http://rcore-os.cn/rCore-Tutorial-deploy/docs/lab-4/guide/part-6.html
https://docs.oasis-open.org/virtio/virtio/v1.1/cs01/virtio-v1.1-cs01.pdf
https://cloud.tencent.com/developer/article/1770529
https://youtu.be/vkvZ7ohx-84
https://mindchasers.com/dev/rv-getting-started
https://github.com/riscv-collab/riscv-gnu-toolchain
https://ithelp.ithome.com.tw/articles/10257612
https://ppfocus.com/0/au628a911.html
https://cloud.tencent.com/developer/article/1770529
https://magiclen.org/rust-no-std/

看了一些文章,原本想從qemu vga動手,後來越看越多可能要整合opensbi,驅動還要是virtio還是找不到有關於最新的opensbi virtio 的驅動範例,不過有找到rust 版本的
https://github.com/rcore-os/virtio-drivers
主要嘗試在
https://github.com/plctlab/riscv-operating-system-mooc
是否有可能在link 階段靜態編譯,讓實驗用的os用c語言開機去call rust函數在返回

參考一下原版的virtio-drivers

可以看到一些步驟我就省略,virtio透過加載opensbi 可以抓到驅動,算是一層interface
這個專案已經寫了一些test driver.

| Device | Status |

| Queue | ✅ |
| Block | ✅ |
| Net | ✅ |
| GPU | ✅ |
| Input | ✅ |
| Console | ✅ |
| | ❌ Not implemented |

我覺得驅動寫到這邊已經蠻充足的了,今天我想要完成一個gui的介面也只要透過

即可以對qemu的記憶體區段寫入資料,

稍微修改一下

問題來了能不能把它當成強制改成靜態連結進行編譯,用c call rust 的fucntion,來嘗試一下

大概會拆成兩個步驟,但是共同的第一步都要是降成相同位元的compiler
riscv-operating-system-mooc
virtio-drivers
virtio-drivers我們委屈一下,riscv64 降成riscv32

但是riscv64 預設qemu的bios 有支援 opensbi所以又要重編一下 opensbi

git clone https://github.com/riscv/opensbi.git

FW_JUMP_ADDR是我們開機完第一個要跳轉的address我們這邊寫死
,因為要順應LD的起始位置0x80200000

這兩個有預設opensbi寫好的加載範例

/home/x213212/opensbi/firmware/

/home/x213212/opensbi/firmware/payloads

我們在把os riscv-operating-system-mooc第三章的部分的printf 改入這個範例

進行編譯

export CROSS_COMPILE=riscv64-unknown-elf-
make PLATFORM=generic clean
make PLATFORM=generic FW_JUMP_ADDR=0x80200000

位於課程的riscv-operating-system-mooc
/riscv-operating-system-mooc/code/common.mk
編譯和qemu都要降為32位元

CROSS_COMPILE = riscv32-unknown-elf-
CFLAGS = -nostdlib -fno-builtin -march=rv32imafdc -mabi=ilp32 -g -Wall
# qemu-system-riscv64 \
#   --machine virt \
#   --nographic \
#   --bios default
#QEMU = qemu-system-riscv32 
#QFLAGS = --nographic -smp 1 -machine  virt -bios none
QEMU = qemu-system-riscv32
QFLAGS = --nographic -smp 1 -machine  virt -bios fw_jump.elf  -device loader,file=test.bin,addr=0x80200000  -device virtio-gpu-device
GDB = gdb-multiarch
CC = ${CROSS_COMPILE}gcc
OBJCOPY = ${CROSS_COMPILE}objcopy
OBJDUMP = ${CROSS_COMPILE}objdump
~                                    

opensbi編出來的是sivefive的韌體
make run 進行啟動qemu

-device loader,file=test.bin,addr=0x80200000

跳轉位置記得設定好
把剛剛編譯出來的test.elf ,test.bin,fw_jump.elf 都copy到其中一個riscv-operating-system-mooc 的章節我們選選01-helloRVOS 章節

到這邊我們就可以讓課程riscv-operating-system-mooc 加載 opensbi進行啟動。

第二個部分就是c在沒有std環境到底能不能去呼叫rust的東西,這邊我直接把
https://github.com/rcore-os/virtio-drivers
改成靜態連結,目前還在嘗試可以編譯的過,唯一要注意的是很多文章都沒有明確指出到底編譯成std lib 會報錯的問題有那些

Cargo.toml

[package]
name = "riscv"
version = "0.1.0"
authors = ["Runji Wang <wangrunji0408@163.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "double_input"
crate-type = ["staticlib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"


[dependencies]

log = "0.4"
riscv = "0.6"
opensbi-rt = { git = "https://github.com/rcore-os/opensbi-rt.git", rev = "1308cc5" }
device_tree = { git = "https://github.com/rcore-os/device_tree-rs", rev = "2fa8411" }
virtio-drivers = { path = "../.." }
lazy_static = { version = "1.4", features = ["spin_no_std"] }
spin = "0.9.2"
linked_list_allocator = "0.10.1"
spinning_top = "0.2.4"

buddy_system_allocator =  "0.6"

lib.rs

#![no_std] #![no_main] #![crate_type = "staticlib"] #![feature(llvm_asm)] #![feature(default_alloc_error_handler)] extern crate alloc; use alloc::vec; use device_tree::util::SliceRead; use device_tree::{DeviceTree, Node}; use log::{info, warn}; use virtio_drivers::*; use virtio_impl::HalImpl; mod virtio_impl; use buddy_system_allocator::LockedHeap; #[global_allocator] static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); #[no_mangle] pub extern "C" fn double_input() { info!("device tree "); let hi = "hi"; let mut count = 0; // let _ret: usize; // let arg0: usize = b'O' as usize; // let arg1: usize = 0; // let arg2: usize = 0; // let which: usize = 1; // unsafe { // llvm_asm!("ecall" // : "={x10}" (_ret) // : "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which) // : "memory" // : "volatile" // ); // } while count < 100000 { // println!("{}, count: {}", hi, count); count += 1; } // loop {} } use core::panic::PanicInfo; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { loop {} } fn init_dt(dtb: usize) { info!("device tree @ {:#x}", dtb); #[repr(C)] struct DtbHeader { be_magic: u32, be_size: u32, } let header = unsafe { &*(dtb as *const DtbHeader) }; let magic = u32::from_be(header.be_magic); const DEVICE_TREE_MAGIC: u32 = 0xd00dfeed; assert_eq!(magic, DEVICE_TREE_MAGIC); let size = u32::from_be(header.be_size); let dtb_data = unsafe { core::slice::from_raw_parts(dtb as *const u8, size as usize) }; let dt = DeviceTree::load(dtb_data).expect("failed to parse device tree"); walk_dt_node(&dt.root); } fn walk_dt_node(dt: &Node) { if let Ok(compatible) = dt.prop_str("compatible") { if compatible == "virtio,mmio" { virtio_probe(dt); } } for child in dt.children.iter() { walk_dt_node(child); } } fn virtio_probe(node: &Node) { if let Some(reg) = node.prop_raw("reg") { let paddr = reg.as_slice().read_be_u64(0).unwrap(); let size = reg.as_slice().read_be_u64(8).unwrap(); let vaddr = paddr; info!("walk dt addr={:#x}, size={:#x}", paddr, size); let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) }; info!( "Detected virtio device with vendor id {:#X}, device type {:?}", header.vendor_id(), header.device_type(), ); info!("Device tree node {:?}", node); match header.device_type() { DeviceType::Block => virtio_blk(header), DeviceType::GPU => virtio_gpu(header), DeviceType::Input => virtio_input(header), DeviceType::Network => virtio_net(header), t => warn!("Unrecognized virtio device: {:?}", t), } } } fn virtio_blk(header: &'static mut VirtIOHeader) { let mut blk = VirtIOBlk::<HalImpl>::new(header).expect("failed to create blk driver"); let mut input = vec![0xffu8; 512]; let mut output = vec![0; 512]; for i in 0..32 { for x in input.iter_mut() { *x = i as u8; } blk.write_block(i, &input).expect("failed to write"); blk.read_block(i, &mut output).expect("failed to read"); assert_eq!(input, output); } info!("virtio-blk test finished"); } fn virtio_gpu(header: &'static mut VirtIOHeader) { let mut gpu = VirtIOGpu::<HalImpl>::new(header).expect("failed to create gpu driver"); let fb = gpu.setup_framebuffer().expect("failed to get fb"); for y in 0..768 { for x in 0..1024 { let idx = (y * 1024 + x) * 4; fb[idx] = x as u8; fb[idx + 1] = y as u8; fb[idx + 2] = (x + y) as u8; } } gpu.flush().expect("failed to flush"); info!("virtio-gpu test finished"); } fn virtio_input(header: &'static mut VirtIOHeader) { //let mut event_buf = [0u64; 32]; let mut _input = VirtIOInput::<HalImpl>::new(header).expect("failed to create input driver"); // loop { // input.ack_interrupt().expect("failed to ack"); // info!("mouse: {:?}", input.mouse_xy()); // } // TODO: handle external interrupt } fn virtio_net(header: &'static mut VirtIOHeader) { let mut net = VirtIONet::<HalImpl>::new(header).expect("failed to create net driver"); let mut buf = [0u8; 0x100]; let len = net.recv(&mut buf).expect("failed to recv"); info!("recv: {:?}", &buf[..len]); net.send(&buf[..len]).expect("failed to send"); info!("virtio-net test finished"); }

移植會遇到的雷

#![feature(default_alloc_error_handler)]
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();

記得cargo.toml

buddy_system_allocator =  "0.6"

這邊主要嘗試把 virtio-drivers編譯成靜態連結,還不理功能是否正常。

最後我們下cargo build
我們會在相對應的平台找到生成的靜態連結檔案

一樣我們選01-helloRVOS 章節

libdouble_input.a -Wl,gc-sections
記得在link階段進行連結

riscv32-unknown-elf-gcc -nostdlib -fno-builtin -march=rv32imafdc -mabi=ilp32 -g -Wall  -c -o start.o start.S
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -march=rv32imafdc -mabi=ilp32 -g -Wall  -c -o kernel.o kernel.c
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -march=rv32imafdc -mabi=ilp32 -g -Wall  -c -o uart.o uart.c
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -march=rv32imafdc -mabi=ilp32 -g -Wall  -Ttext=0x80000000 -o os.elf start.o kernel.o uart.o  libdouble_input.a -Wl,--gc-sections 
riscv32-unknown-elf-objcopy -O binary os.elf os.bin

這邊比較注意的是我目前吧extern crate opensbi_rt;
移除,不然會出現重複 _start,這是位於這個opensbi_rt應該是寫驅動的作者應該也是開機加載跟程式綁再一起,目前前面階段是ok的,那麼我們在rust應該可以直接呼叫相對應的lib應該Ok(吧
,這個問題以後再探討,當前主要任務是觸發用kernel.c再去呼叫rust寫的fucntion

這邊要注意又再改回然後預設起始位置是0x80000000
/riscv-operating-system-mooc/code/common.mk

CROSS_COMPILE = riscv32-unknown-elf-
CFLAGS = -nostdlib -fno-builtin -march=rv32imafdc -mabi=ilp32 -g -Wall 
# qemu-system-riscv64 \
#   --machine virt \
#   --nographic \
#   --bios default
QEMU = qemu-system-riscv32 
QFLAGS = --nographic -smp 1 -machine  virt -bios none
#QEMU = qemu-system-riscv32 
#QFLAGS = --nographic -smp 1 -machine  virt -bios fw_jump.elf  -device loader,file=test.bin,addr=0x80200000  -device #virtio-gpu-device 
GDB = gdb-multiarch
CC = ${CROSS_COMPILE}gcc
OBJCOPY = ${CROSS_COMPILE}objcopy
OBJDUMP = ${CROSS_COMPILE}objdump

double_input fucntion

https://github.com/alexcrichton/rust-ffi-examples
相關靜態連結的範例,可以跟著改修改返回值


可以看到會稍微延遲在觸發第二個print

到這邊我們的c就可以call rust的fucntion,主要還是因為找不到可以有人專門在寫這部分的driver想說能不能移花接木一下進行呼叫,那麼我只要驅動的部分呼叫rust的就好了,這樣改可能可以成功,後面第三部可能要
riscv-operating-system-mooc移植到剛剛的opensbi的範例啟動,再來要考慮的是這樣方式的link是否有可能會產生衝突,因為rust的lib移除掉opensbi_rt不知正常啟用的話lib功能會不會有問題.

Select a repo