mirror of
https://github.com/rcore-os/rCore.git
synced 2025-01-31 10:04:04 +04:00
Merge branch 'master' into pull-30-merge
This commit is contained in:
commit
103a2da33b
12
kernel/Cargo.lock
generated
12
kernel/Cargo.lock
generated
@ -374,6 +374,7 @@ dependencies = [
|
||||
"smoltcp 0.5.0 (git+https://github.com/rcore-os/smoltcp?rev=5bd87c7c)",
|
||||
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uart_16550 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"virtio-drivers 0.1.0 (git+https://github.com/rcore-os/virtio-drivers?rev=dfa70e14)",
|
||||
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"x86_64 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xmas-elf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -608,6 +609,16 @@ name = "ux"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-drivers"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rcore-os/virtio-drivers?rev=dfa70e14#dfa70e1425c9eac1b30e671be61b557a3ce56e7e"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "volatile"
|
||||
version = "0.2.6"
|
||||
@ -755,6 +766,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5"
|
||||
"checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
|
||||
"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f"
|
||||
"checksum virtio-drivers 0.1.0 (git+https://github.com/rcore-os/virtio-drivers?rev=dfa70e14)" = "<none>"
|
||||
"checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29"
|
||||
"checksum vte 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
|
@ -59,6 +59,7 @@ buddy_system_allocator = "0.3"
|
||||
pci = { git = "https://github.com/rcore-os/pci-rs" }
|
||||
device_tree = { git = "https://github.com/rcore-os/device_tree-rs" }
|
||||
isomorphic_drivers = { git = "https://github.com/rcore-os/isomorphic_drivers", features = ["log"] }
|
||||
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "dfa70e14" }
|
||||
lazy_static = { version = "1.4", features = ["spin_no_std"] }
|
||||
smoltcp = { git = "https://github.com/rcore-os/smoltcp", rev = "5bd87c7c", default-features = false, features = ["alloc", "log", "ethernet", "proto-ipv4", "proto-igmp", "socket-icmp", "socket-udp", "socket-tcp", "socket-raw"] }
|
||||
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator" }
|
||||
|
@ -1,116 +1,18 @@
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use core::cmp::min;
|
||||
use core::mem::{size_of, zeroed};
|
||||
use core::slice;
|
||||
|
||||
use bitflags::*;
|
||||
use device_tree::util::SliceRead;
|
||||
use device_tree::Node;
|
||||
use log::*;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
use volatile::Volatile;
|
||||
use virtio_drivers::{VirtIOBlk, VirtIOHeader};
|
||||
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
|
||||
use super::super::bus::virtio_mmio::*;
|
||||
use super::super::{DeviceType, Driver, BLK_DRIVERS, DRIVERS, IRQ_MANAGER};
|
||||
use crate::memory::phys_to_virt;
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
|
||||
pub struct VirtIOBlk {
|
||||
interrupt_parent: u32,
|
||||
interrupt: u32,
|
||||
header: usize,
|
||||
queue: VirtIOVirtqueue,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
pub struct VirtIOBlkDriver(Mutex<VirtIOBlk>);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOBlkConfig {
|
||||
capacity: Volatile<u64>, // number of 512 sectors
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
struct VirtIOBlkReadReq {
|
||||
req_type: u32,
|
||||
reserved: u32,
|
||||
sector: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct VirtIOBlkWriteReq {
|
||||
req_type: u32,
|
||||
reserved: u32,
|
||||
sector: u64,
|
||||
data: [u8; VIRTIO_BLK_BLK_SIZE],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct VirtIOBlkReadResp {
|
||||
data: [u8; VIRTIO_BLK_BLK_SIZE],
|
||||
status: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
struct VirtIOBlkWriteResp {
|
||||
status: u8,
|
||||
}
|
||||
|
||||
const VIRTIO_BLK_T_IN: u32 = 0;
|
||||
const VIRTIO_BLK_T_OUT: u32 = 1;
|
||||
|
||||
const VIRTIO_BLK_S_OK: u8 = 0;
|
||||
const VIRTIO_BLK_S_IOERR: u8 = 1;
|
||||
const VIRTIO_BLK_S_UNSUPP: u8 = 2;
|
||||
|
||||
const VIRTIO_BLK_BLK_SIZE: usize = 512;
|
||||
|
||||
bitflags! {
|
||||
struct VirtIOBlkFeature : u64 {
|
||||
const BARRIER = 1 << 0;
|
||||
const SIZE_MAX = 1 << 1;
|
||||
const SEG_MAX = 1 << 2;
|
||||
const GEOMETRY = 1 << 4;
|
||||
const RO = 1 << 5;
|
||||
const BLK_SIZE = 1 << 6;
|
||||
const SCSI = 1 << 7;
|
||||
const FLUSH = 1 << 9;
|
||||
const TOPOLOGY = 1 << 10;
|
||||
const CONFIG_WCE = 1 << 11;
|
||||
const DISCARD = 1 << 13;
|
||||
const WRITE_ZEROES = 1 << 14;
|
||||
// device independent
|
||||
const NOTIFY_ON_EMPTY = 1 << 24; // legacy
|
||||
const ANY_LAYOUT = 1 << 27; // legacy
|
||||
const RING_INDIRECT_DESC = 1 << 28;
|
||||
const RING_EVENT_IDX = 1 << 29;
|
||||
const UNUSED = 1 << 30; // legacy
|
||||
const VERSION_1 = 1 << 32; // detect legacy
|
||||
const ACCESS_PLATFORM = 1 << 33; // since virtio v1.1
|
||||
const RING_PACKED = 1 << 34; // since virtio v1.1
|
||||
const IN_ORDER = 1 << 35; // since virtio v1.1
|
||||
const ORDER_PLATFORM = 1 << 36; // since virtio v1.1
|
||||
const SR_IOV = 1 << 37; // since virtio v1.1
|
||||
const NOTIFICATION_DATA = 1 << 38; // since virtio v1.1
|
||||
}
|
||||
}
|
||||
struct VirtIOBlkDriver(Mutex<VirtIOBlk<'static>>);
|
||||
|
||||
impl Driver for VirtIOBlkDriver {
|
||||
fn try_handle_interrupt(&self, _irq: Option<u32>) -> bool {
|
||||
let driver = self.0.lock();
|
||||
|
||||
let header = unsafe { &mut *(driver.header as *mut VirtIOHeader) };
|
||||
let interrupt = header.interrupt_status.read();
|
||||
if interrupt != 0 {
|
||||
header.interrupt_ack.write(interrupt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
self.0.lock().ack_interrupt()
|
||||
}
|
||||
|
||||
fn device_type(&self) -> DeviceType {
|
||||
@ -122,96 +24,17 @@ impl Driver for VirtIOBlkDriver {
|
||||
}
|
||||
|
||||
fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
|
||||
let mut driver = self.0.lock();
|
||||
let mut req = VirtIOBlkReadReq::default();
|
||||
req.req_type = VIRTIO_BLK_T_IN;
|
||||
req.reserved = 0;
|
||||
req.sector = block_id as u64;
|
||||
let mut input = [0; size_of::<VirtIOBlkReadResp>()];
|
||||
let output = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
&mut req as *mut VirtIOBlkReadReq as *mut u8,
|
||||
size_of::<VirtIOBlkReadReq>(),
|
||||
)
|
||||
};
|
||||
driver.queue.add_and_notify(&[&mut input], &[output], 0);
|
||||
driver.queue.get_block();
|
||||
let resp = unsafe { &*(&input as *const u8 as *const VirtIOBlkReadResp) };
|
||||
if resp.status == VIRTIO_BLK_S_OK {
|
||||
let len = min(buf.len(), VIRTIO_BLK_BLK_SIZE);
|
||||
buf[..len].clone_from_slice(&resp.data[..len]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
self.0.lock().read_block(block_id, buf).is_ok()
|
||||
}
|
||||
|
||||
fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
|
||||
let mut driver = self.0.lock();
|
||||
let mut req: VirtIOBlkWriteReq = unsafe { zeroed() };
|
||||
req.req_type = VIRTIO_BLK_T_OUT;
|
||||
req.reserved = 0;
|
||||
req.sector = block_id as u64;
|
||||
let len = min(buf.len(), VIRTIO_BLK_BLK_SIZE);
|
||||
req.data[..len].clone_from_slice(&buf[..len]);
|
||||
let mut input = [0; size_of::<VirtIOBlkWriteResp>()];
|
||||
let output = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
&mut req as *mut VirtIOBlkWriteReq as *mut u8,
|
||||
size_of::<VirtIOBlkWriteReq>(),
|
||||
)
|
||||
};
|
||||
driver.queue.add_and_notify(&[&mut input], &[output], 0);
|
||||
driver.queue.get_block();
|
||||
let resp = unsafe { &*(&input as *const u8 as *const VirtIOBlkWriteResp) };
|
||||
if resp.status == VIRTIO_BLK_S_OK {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
self.0.lock().write_block(block_id, buf).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn virtio_blk_init(node: &Node) {
|
||||
let reg = node.prop_raw("reg").unwrap();
|
||||
let paddr = reg.as_slice().read_be_u64(0).unwrap();
|
||||
let vaddr = phys_to_virt(paddr as usize);
|
||||
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
|
||||
|
||||
header.status.write(VirtIODeviceStatus::DRIVER.bits());
|
||||
|
||||
let device_features_bits = header.read_device_features();
|
||||
let device_features = VirtIOBlkFeature::from_bits_truncate(device_features_bits);
|
||||
info!("Device features {:?}", device_features);
|
||||
|
||||
// negotiate these flags only
|
||||
let supported_features = VirtIOBlkFeature::empty();
|
||||
let driver_features = (device_features & supported_features).bits();
|
||||
header.write_driver_features(driver_features);
|
||||
|
||||
// read configuration space
|
||||
let config = unsafe { &mut *((vaddr + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIOBlkConfig) };
|
||||
info!("Config: {:?}", config);
|
||||
info!(
|
||||
"Found a block device of size {}KB",
|
||||
config.capacity.read() / 2
|
||||
);
|
||||
|
||||
// virtio 4.2.4 Legacy interface
|
||||
// configure two virtqueues: ingress and egress
|
||||
header.guest_page_size.write(PAGE_SIZE as u32); // one page
|
||||
|
||||
let driver = VirtIOBlkDriver(Mutex::new(VirtIOBlk {
|
||||
interrupt: node.prop_u32("interrupts").unwrap(),
|
||||
interrupt_parent: node.prop_u32("interrupt-parent").unwrap(),
|
||||
header: vaddr as usize,
|
||||
queue: VirtIOVirtqueue::new(header, 0, 16),
|
||||
capacity: config.capacity.read() as usize,
|
||||
}));
|
||||
|
||||
header.status.write(VirtIODeviceStatus::DRIVER_OK.bits());
|
||||
|
||||
let driver = Arc::new(driver);
|
||||
pub fn init(header: &'static mut VirtIOHeader) {
|
||||
let blk = VirtIOBlk::new(header).expect("failed to init blk driver");
|
||||
let driver = Arc::new(VirtIOBlkDriver(Mutex::new(blk)));
|
||||
DRIVERS.write().push(driver.clone());
|
||||
IRQ_MANAGER.write().register_all(driver.clone());
|
||||
BLK_DRIVERS.write().push(driver);
|
||||
|
@ -1,17 +1,8 @@
|
||||
use alloc::alloc::{GlobalAlloc, Layout};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::mem::size_of;
|
||||
use core::slice;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use bitflags::*;
|
||||
use device_tree::util::SliceRead;
|
||||
use device_tree::Node;
|
||||
use log::*;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
use volatile::{ReadOnly, Volatile, WriteOnly};
|
||||
|
||||
use crate::HEAP_ALLOCATOR;
|
||||
use virtio_drivers::{DeviceType, VirtIOHeader};
|
||||
|
||||
use super::super::block::virtio_blk;
|
||||
use super::super::gpu::virtio_gpu;
|
||||
@ -19,373 +10,31 @@ use super::super::input::virtio_input;
|
||||
use super::super::net::virtio_net;
|
||||
use crate::memory::{phys_to_virt, virt_to_phys};
|
||||
|
||||
// virtio 4.2.4 Legacy interface
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VirtIOHeader {
|
||||
magic: ReadOnly<u32>, // 0x000
|
||||
version: ReadOnly<u32>, // 0x004
|
||||
device_id: ReadOnly<u32>, // 0x008
|
||||
vendor_id: ReadOnly<u32>, // 0x00c
|
||||
pub device_features: ReadOnly<u32>, // 0x010
|
||||
pub device_features_sel: WriteOnly<u32>, // 0x014
|
||||
__r1: [ReadOnly<u32>; 2],
|
||||
pub driver_features: WriteOnly<u32>, // 0x020
|
||||
pub driver_features_sel: WriteOnly<u32>, // 0x024
|
||||
pub guest_page_size: WriteOnly<u32>, // 0x028
|
||||
__r2: ReadOnly<u32>,
|
||||
pub queue_sel: WriteOnly<u32>, // 0x030
|
||||
pub queue_num_max: ReadOnly<u32>, // 0x034
|
||||
pub queue_num: WriteOnly<u32>, // 0x038
|
||||
pub queue_align: WriteOnly<u32>, // 0x03c
|
||||
pub queue_pfn: Volatile<u32>, // 0x040
|
||||
queue_ready: Volatile<u32>, // new interface only
|
||||
__r3: [ReadOnly<u32>; 2],
|
||||
pub queue_notify: WriteOnly<u32>, // 0x050
|
||||
__r4: [ReadOnly<u32>; 3],
|
||||
pub interrupt_status: ReadOnly<u32>, // 0x060
|
||||
pub interrupt_ack: WriteOnly<u32>, // 0x064
|
||||
__r5: [ReadOnly<u32>; 2],
|
||||
pub status: Volatile<u32>, // 0x070
|
||||
__r6: [ReadOnly<u32>; 3],
|
||||
queue_desc_low: WriteOnly<u32>, // new interface only since here
|
||||
queue_desc_high: WriteOnly<u32>,
|
||||
__r7: [ReadOnly<u32>; 2],
|
||||
queue_avail_low: WriteOnly<u32>,
|
||||
queue_avail_high: WriteOnly<u32>,
|
||||
__r8: [ReadOnly<u32>; 2],
|
||||
queue_used_low: WriteOnly<u32>,
|
||||
queue_used_high: WriteOnly<u32>,
|
||||
__r9: [ReadOnly<u32>; 21],
|
||||
config_generation: ReadOnly<u32>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct VirtIOVirtqueue {
|
||||
header: usize,
|
||||
queue_address: usize,
|
||||
queue_num: usize,
|
||||
queue: usize,
|
||||
desc: usize, // *mut VirtIOVirtqueueDesc,
|
||||
avail: usize, // *mut VirtIOVirtqueueAvailableRing,
|
||||
used: usize, // *mut VirtIOVirtqueueUsedRing,
|
||||
desc_state: Vec<usize>,
|
||||
num_used: usize,
|
||||
free_head: usize,
|
||||
avail_idx: u16,
|
||||
last_used_idx: u16,
|
||||
}
|
||||
|
||||
impl VirtIOVirtqueue {
|
||||
// Initialize a virtqueue
|
||||
pub fn new(header: &mut VirtIOHeader, queue: usize, queue_num: usize) -> VirtIOVirtqueue {
|
||||
header.queue_sel.write(queue as u32);
|
||||
assert_eq!(header.queue_pfn.read(), 0); // not in use
|
||||
let queue_num_max = header.queue_num_max.read();
|
||||
assert!(queue_num_max >= queue_num as u32); // queue available
|
||||
assert_eq!(queue_num & (queue_num - 1), 0); // power of two
|
||||
let align = PAGE_SIZE;
|
||||
let size = virtqueue_size(queue_num, align);
|
||||
assert_eq!(size % align, 0);
|
||||
// alloc continuous pages
|
||||
let address =
|
||||
unsafe { HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(size, align).unwrap()) }
|
||||
as usize;
|
||||
|
||||
header.queue_num.write(queue_num as u32);
|
||||
header.queue_align.write(align as u32);
|
||||
header.queue_pfn.write((virt_to_phys(address) as u32) >> 12);
|
||||
|
||||
// link desc together
|
||||
let desc =
|
||||
unsafe { slice::from_raw_parts_mut(address as *mut VirtIOVirtqueueDesc, queue_num) };
|
||||
for i in 0..(queue_num - 1) {
|
||||
desc[i].next.write((i + 1) as u16);
|
||||
}
|
||||
|
||||
VirtIOVirtqueue {
|
||||
header: header as *mut VirtIOHeader as usize,
|
||||
queue_address: address,
|
||||
queue_num,
|
||||
queue,
|
||||
desc: address,
|
||||
avail: address + size_of::<VirtIOVirtqueueDesc>() * queue_num,
|
||||
used: address + virtqueue_used_elem_offset(queue_num, align),
|
||||
desc_state: vec![0; queue_num],
|
||||
num_used: 0,
|
||||
free_head: 0,
|
||||
avail_idx: 0,
|
||||
last_used_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_add(&self, input_len: usize, output_len: usize) -> bool {
|
||||
return input_len + output_len + self.num_used <= self.queue_num;
|
||||
}
|
||||
|
||||
// Add buffers to the virtqueue
|
||||
// Return true on success, false otherwise
|
||||
// ref. linux virtio_ring.c virtqueue_add
|
||||
pub fn add(&mut self, input: &[&mut [u8]], output: &[&mut [u8]], user_data: usize) -> bool {
|
||||
assert!(input.len() + output.len() > 0);
|
||||
if !self.can_add(input.len(), output.len()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let desc = unsafe {
|
||||
slice::from_raw_parts_mut(self.desc as *mut VirtIOVirtqueueDesc, self.queue_num)
|
||||
};
|
||||
let head = self.free_head;
|
||||
let mut prev = 0;
|
||||
let mut cur = self.free_head;
|
||||
for i in 0..output.len() {
|
||||
desc[cur].flags.write(VirtIOVirtqueueFlag::NEXT.bits());
|
||||
desc[cur]
|
||||
.addr
|
||||
.write(virt_to_phys(output[i].as_ptr() as usize) as u64);
|
||||
desc[cur].len.write(output[i].len() as u32);
|
||||
prev = cur;
|
||||
cur = desc[cur].next.read() as usize;
|
||||
}
|
||||
for i in 0..input.len() {
|
||||
desc[cur]
|
||||
.flags
|
||||
.write((VirtIOVirtqueueFlag::NEXT | VirtIOVirtqueueFlag::WRITE).bits());
|
||||
desc[cur]
|
||||
.addr
|
||||
.write(virt_to_phys(input[i].as_ptr() as usize) as u64);
|
||||
desc[cur].len.write(input[i].len() as u32);
|
||||
prev = cur;
|
||||
cur = desc[cur].next.read() as usize;
|
||||
}
|
||||
desc[prev]
|
||||
.flags
|
||||
.write(desc[prev].flags.read() & !(VirtIOVirtqueueFlag::NEXT.bits()));
|
||||
|
||||
self.num_used += input.len() + output.len();
|
||||
self.free_head = cur;
|
||||
|
||||
let avail = unsafe { &mut *(self.avail as *mut VirtIOVirtqueueAvailableRing) };
|
||||
|
||||
let avail_slot = self.avail_idx as usize & (self.queue_num - 1);
|
||||
avail.ring[avail_slot].write(head as u16);
|
||||
|
||||
// write barrier
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
self.avail_idx = self.avail_idx.wrapping_add(1);
|
||||
avail.idx.write(self.avail_idx);
|
||||
self.desc_state[head] = user_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add buffers to the virtqueue and notify device about it
|
||||
pub fn add_and_notify(
|
||||
&mut self,
|
||||
input: &[&mut [u8]],
|
||||
output: &[&mut [u8]],
|
||||
user_data: usize,
|
||||
) -> bool {
|
||||
let res = self.add(input, output, user_data);
|
||||
if res {
|
||||
self.notify();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
pub fn can_get(&self) -> bool {
|
||||
let used = unsafe { &mut *(self.used as *mut VirtIOVirtqueueUsedRing) };
|
||||
return self.last_used_idx != used.idx.read();
|
||||
}
|
||||
|
||||
// Get device used buffers (input, output, length, user_data)
|
||||
// ref. linux virtio_ring.c virtqueue_get_buf_ctx
|
||||
pub fn get(
|
||||
&mut self,
|
||||
) -> Option<(Vec<&'static mut [u8]>, Vec<&'static mut [u8]>, usize, usize)> {
|
||||
let used = unsafe { &mut *(self.used as *mut VirtIOVirtqueueUsedRing) };
|
||||
if self.last_used_idx == used.idx.read() {
|
||||
return None;
|
||||
}
|
||||
// read barrier
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let last_used_slot = self.last_used_idx as usize & (self.queue_num - 1);
|
||||
let index = used.ring[last_used_slot].id.read() as usize;
|
||||
let len = used.ring[last_used_slot].len.read();
|
||||
|
||||
let user_data = self.desc_state[last_used_slot];
|
||||
self.desc_state[last_used_slot] = 0;
|
||||
|
||||
let mut cur = index;
|
||||
let desc = unsafe {
|
||||
slice::from_raw_parts_mut(self.desc as *mut VirtIOVirtqueueDesc, self.queue_num)
|
||||
};
|
||||
let mut input = Vec::new();
|
||||
let mut output = Vec::new();
|
||||
loop {
|
||||
let flags = VirtIOVirtqueueFlag::from_bits_truncate(desc[cur].flags.read());
|
||||
let addr = phys_to_virt(desc[cur].addr.read() as usize);
|
||||
let buffer = unsafe {
|
||||
slice::from_raw_parts_mut(addr as *mut u8, desc[cur].len.read() as usize)
|
||||
};
|
||||
if flags.contains(VirtIOVirtqueueFlag::WRITE) {
|
||||
input.push(buffer);
|
||||
} else {
|
||||
output.push(buffer);
|
||||
}
|
||||
|
||||
if flags.contains(VirtIOVirtqueueFlag::NEXT) {
|
||||
cur = desc[cur].next.read() as usize;
|
||||
self.num_used -= 1;
|
||||
} else {
|
||||
desc[cur].next.write(self.free_head as u16);
|
||||
self.num_used -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.free_head = index;
|
||||
self.last_used_idx = self.last_used_idx.wrapping_add(1);
|
||||
|
||||
Some((input, output, len as usize, user_data))
|
||||
}
|
||||
|
||||
// Get device used buffers until succeed
|
||||
// See get() above
|
||||
pub fn get_block(&mut self) -> (Vec<&'static mut [u8]>, Vec<&'static mut [u8]>, usize, usize) {
|
||||
loop {
|
||||
let res = self.get();
|
||||
if res.is_some() {
|
||||
return res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notify device about new buffers
|
||||
pub fn notify(&mut self) {
|
||||
let header = unsafe { &mut *(self.header as *mut VirtIOHeader) };
|
||||
header.queue_notify.write(self.queue as u32);
|
||||
}
|
||||
}
|
||||
|
||||
pub const VIRTIO_CONFIG_SPACE_OFFSET: usize = 0x100;
|
||||
|
||||
impl VirtIOHeader {
|
||||
pub fn read_device_features(&mut self) -> u64 {
|
||||
let mut device_features_bits: u64;
|
||||
self.device_features_sel.write(0); // device features [0, 32)
|
||||
device_features_bits = self.device_features.read().into();
|
||||
self.device_features_sel.write(1); // device features [32, 64)
|
||||
device_features_bits = device_features_bits + ((self.device_features.read() as u64) << 32);
|
||||
device_features_bits
|
||||
}
|
||||
|
||||
pub fn write_driver_features(&mut self, driver_features: u64) {
|
||||
self.driver_features_sel.write(0); // driver features [0, 32)
|
||||
self.driver_features
|
||||
.write((driver_features & 0xFFFFFFFF) as u32);
|
||||
self.driver_features_sel.write(1); // driver features [32, 64)
|
||||
self.driver_features
|
||||
.write(((driver_features & 0xFFFFFFFF00000000) >> 32) as u32);
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct VirtIODeviceStatus : u32 {
|
||||
const ACKNOWLEDGE = 1;
|
||||
const DRIVER = 2;
|
||||
const FAILED = 128;
|
||||
const FEATURES_OK = 8;
|
||||
const DRIVER_OK = 4;
|
||||
const DEVICE_NEEDS_RESET = 64;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VirtIOVirtqueueDesc {
|
||||
pub addr: Volatile<u64>,
|
||||
pub len: Volatile<u32>,
|
||||
pub flags: Volatile<u16>,
|
||||
pub next: Volatile<u16>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct VirtIOVirtqueueFlag : u16 {
|
||||
const NEXT = 1;
|
||||
const WRITE = 2;
|
||||
const INDIRECT = 4;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VirtIOVirtqueueAvailableRing {
|
||||
pub flags: Volatile<u16>,
|
||||
pub idx: Volatile<u16>,
|
||||
pub ring: [Volatile<u16>; 32], // actual size: queue_size
|
||||
used_event: Volatile<u16>, // unused
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VirtIOVirtqueueUsedElem {
|
||||
id: Volatile<u32>,
|
||||
len: Volatile<u32>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct VirtIOVirtqueueUsedRing {
|
||||
pub flags: Volatile<u16>,
|
||||
pub idx: Volatile<u16>,
|
||||
pub ring: [VirtIOVirtqueueUsedElem; 32], // actual size: queue_size
|
||||
avail_event: Volatile<u16>, // unused
|
||||
}
|
||||
|
||||
// virtio 2.4.2 Legacy Interfaces: A Note on Virtqueue Layout
|
||||
pub fn virtqueue_size(num: usize, align: usize) -> usize {
|
||||
(((size_of::<VirtIOVirtqueueDesc>() * num + size_of::<u16>() * (3 + num)) + align)
|
||||
& !(align - 1))
|
||||
+ (((size_of::<u16>() * 3 + size_of::<VirtIOVirtqueueUsedElem>() * num) + align)
|
||||
& !(align - 1))
|
||||
}
|
||||
|
||||
pub fn virtqueue_used_elem_offset(num: usize, align: usize) -> usize {
|
||||
((size_of::<VirtIOVirtqueueDesc>() * num + size_of::<u16>() * (3 + num)) + align) & !(align - 1)
|
||||
}
|
||||
|
||||
pub fn virtio_probe(node: &Node) {
|
||||
if let Some(reg) = node.prop_raw("reg") {
|
||||
let paddr = reg.as_slice().read_be_u64(0).unwrap();
|
||||
let vaddr = phys_to_virt(paddr as usize);
|
||||
debug!("walk dt {:x} {:x}", paddr, vaddr);
|
||||
let size = reg.as_slice().read_be_u64(8).unwrap();
|
||||
// assuming one page
|
||||
assert_eq!(size as usize, PAGE_SIZE);
|
||||
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
|
||||
let magic = header.magic.read();
|
||||
let version = header.version.read();
|
||||
let device_id = header.device_id.read();
|
||||
let reg = match node.prop_raw("reg") {
|
||||
Some(reg) => reg,
|
||||
_ => return,
|
||||
};
|
||||
let paddr = reg.as_slice().read_be_u64(0).unwrap();
|
||||
let vaddr = phys_to_virt(paddr as usize);
|
||||
let size = reg.as_slice().read_be_u64(8).unwrap();
|
||||
// assuming one page
|
||||
assert_eq!(size as usize, PAGE_SIZE);
|
||||
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
|
||||
if !header.verify() {
|
||||
// only support legacy device
|
||||
if magic == 0x74726976 && version == 1 && device_id != 0 {
|
||||
// "virt" magic
|
||||
info!(
|
||||
"Detected virtio device with vendor id {:#X}",
|
||||
header.vendor_id.read()
|
||||
);
|
||||
info!("Device tree node {:?}", node);
|
||||
// virtio 3.1.1 Device Initialization
|
||||
header.status.write(0);
|
||||
header.status.write(VirtIODeviceStatus::ACKNOWLEDGE.bits());
|
||||
match device_id {
|
||||
1 => virtio_net::virtio_net_init(node),
|
||||
2 => virtio_blk::virtio_blk_init(node),
|
||||
16 => virtio_gpu::virtio_gpu_init(node),
|
||||
18 => virtio_input::virtio_input_init(node),
|
||||
_ => warn!("Unrecognized virtio device {}", device_id),
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
info!(
|
||||
"Detected virtio device with vendor id: {:#X}",
|
||||
header.vendor_id()
|
||||
);
|
||||
info!("Device tree node {:?}", node);
|
||||
match header.device_type() {
|
||||
DeviceType::Network => virtio_net::init(header),
|
||||
DeviceType::Block => virtio_blk::init(header),
|
||||
DeviceType::GPU => virtio_gpu::init(header),
|
||||
DeviceType::Input => virtio_input::init(header),
|
||||
t => warn!("Unrecognized virtio device: {:?}", t),
|
||||
}
|
||||
}
|
||||
|
@ -1,209 +1,18 @@
|
||||
use alloc::alloc::{GlobalAlloc, Layout};
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use core::slice;
|
||||
|
||||
use bitflags::*;
|
||||
use device_tree::util::SliceRead;
|
||||
use device_tree::Node;
|
||||
use log::*;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
use volatile::{ReadOnly, Volatile, WriteOnly};
|
||||
use virtio_drivers::{VirtIOGpu, VirtIOHeader};
|
||||
|
||||
use crate::arch::cpu;
|
||||
use super::super::{DeviceType, Driver, DRIVERS, IRQ_MANAGER};
|
||||
use crate::memory::virt_to_phys;
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
use crate::HEAP_ALLOCATOR;
|
||||
|
||||
use super::super::bus::virtio_mmio::*;
|
||||
use super::super::{DeviceType, Driver, DRIVERS, IRQ_MANAGER};
|
||||
use super::test::mandelbrot;
|
||||
use crate::memory::phys_to_virt;
|
||||
|
||||
const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
|
||||
|
||||
struct VirtIOGpu {
|
||||
interrupt_parent: u32,
|
||||
interrupt: u32,
|
||||
header: &'static mut VirtIOHeader,
|
||||
queue_buffer: [usize; 2],
|
||||
frame_buffer: usize,
|
||||
rect: VirtIOGpuRect,
|
||||
queues: [VirtIOVirtqueue; 2],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuConfig {
|
||||
events_read: ReadOnly<u32>,
|
||||
events_clear: WriteOnly<u32>,
|
||||
num_scanouts: Volatile<u32>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct VirtIOGpuFeature : u64 {
|
||||
const VIRGL = 1 << 0;
|
||||
const EDID = 1 << 1;
|
||||
// device independent
|
||||
const NOTIFY_ON_EMPTY = 1 << 24; // legacy
|
||||
const ANY_LAYOUT = 1 << 27; // legacy
|
||||
const RING_INDIRECT_DESC = 1 << 28;
|
||||
const RING_EVENT_IDX = 1 << 29;
|
||||
const UNUSED = 1 << 30; // legacy
|
||||
const VERSION_1 = 1 << 32; // detect legacy
|
||||
const ACCESS_PLATFORM = 1 << 33; // since virtio v1.1
|
||||
const RING_PACKED = 1 << 34; // since virtio v1.1
|
||||
const IN_ORDER = 1 << 35; // since virtio v1.1
|
||||
const ORDER_PLATFORM = 1 << 36; // since virtio v1.1
|
||||
const SR_IOV = 1 << 37; // since virtio v1.1
|
||||
const NOTIFICATION_DATA = 1 << 38; // since virtio v1.1
|
||||
}
|
||||
}
|
||||
|
||||
const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x100;
|
||||
const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x101;
|
||||
const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x102;
|
||||
const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x103;
|
||||
const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x104;
|
||||
const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x105;
|
||||
const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x106;
|
||||
const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x107;
|
||||
const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x108;
|
||||
const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x109;
|
||||
const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x10a;
|
||||
|
||||
const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x300;
|
||||
const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x301;
|
||||
|
||||
const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
|
||||
const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
|
||||
const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
|
||||
const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
|
||||
const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104;
|
||||
|
||||
const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
|
||||
const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
|
||||
const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
|
||||
|
||||
const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuCtrlHdr {
|
||||
hdr_type: u32,
|
||||
flags: u32,
|
||||
fence_id: u64,
|
||||
ctx_id: u32,
|
||||
padding: u32,
|
||||
}
|
||||
|
||||
impl VirtIOGpuCtrlHdr {
|
||||
fn with_type(hdr_type: u32) -> VirtIOGpuCtrlHdr {
|
||||
VirtIOGpuCtrlHdr {
|
||||
hdr_type,
|
||||
flags: 0,
|
||||
fence_id: 0,
|
||||
ctx_id: 0,
|
||||
padding: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
struct VirtIOGpuRect {
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuRespDisplayInfo {
|
||||
header: VirtIOGpuCtrlHdr,
|
||||
rect: VirtIOGpuRect,
|
||||
enabled: u32,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuResourceCreate2D {
|
||||
header: VirtIOGpuCtrlHdr,
|
||||
resource_id: u32,
|
||||
format: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuResourceAttachBacking {
|
||||
header: VirtIOGpuCtrlHdr,
|
||||
resource_id: u32,
|
||||
nr_entries: u32, // always 1
|
||||
addr: u64,
|
||||
length: u32,
|
||||
padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuSetScanout {
|
||||
header: VirtIOGpuCtrlHdr,
|
||||
rect: VirtIOGpuRect,
|
||||
scanout_id: u32,
|
||||
resource_id: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuTransferToHost2D {
|
||||
header: VirtIOGpuCtrlHdr,
|
||||
rect: VirtIOGpuRect,
|
||||
offset: u64,
|
||||
resource_id: u32,
|
||||
padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOGpuResourceFlush {
|
||||
header: VirtIOGpuCtrlHdr,
|
||||
rect: VirtIOGpuRect,
|
||||
resource_id: u32,
|
||||
padding: u32,
|
||||
}
|
||||
|
||||
const VIRTIO_QUEUE_TRANSMIT: usize = 0;
|
||||
const VIRTIO_QUEUE_CURSOR: usize = 1;
|
||||
|
||||
const VIRTIO_BUFFER_TRANSMIT: usize = 0;
|
||||
const VIRTIO_BUFFER_RECEIVE: usize = 1;
|
||||
|
||||
const VIRTIO_GPU_RESOURCE_ID: u32 = 0xbabe;
|
||||
|
||||
pub struct VirtIOGpuDriver(Mutex<VirtIOGpu>);
|
||||
struct VirtIOGpuDriver(Mutex<VirtIOGpu<'static>>);
|
||||
|
||||
impl Driver for VirtIOGpuDriver {
|
||||
fn try_handle_interrupt(&self, _irq: Option<u32>) -> bool {
|
||||
// for simplicity
|
||||
if cpu::id() > 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut driver = self.0.lock();
|
||||
|
||||
let interrupt = driver.header.interrupt_status.read();
|
||||
if interrupt != 0 {
|
||||
driver.header.interrupt_ack.write(interrupt);
|
||||
debug!("Got interrupt {:?}", interrupt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
self.0.lock().ack_interrupt()
|
||||
}
|
||||
|
||||
fn device_type(&self) -> DeviceType {
|
||||
@ -215,206 +24,33 @@ impl Driver for VirtIOGpuDriver {
|
||||
}
|
||||
}
|
||||
|
||||
fn request(driver: &mut VirtIOGpu) {
|
||||
let input = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut u8,
|
||||
PAGE_SIZE,
|
||||
)
|
||||
};
|
||||
let output = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut u8,
|
||||
PAGE_SIZE,
|
||||
)
|
||||
};
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].add_and_notify(&[input], &[output], 0);
|
||||
}
|
||||
pub fn init(header: &'static mut VirtIOHeader) {
|
||||
let mut gpu = VirtIOGpu::new(header).expect("failed to create gpu driver");
|
||||
let framebuffer = gpu.setup_framebuffer().expect("failed to get fb");
|
||||
let fb_vaddr = framebuffer.as_ptr() as usize;
|
||||
let fb_size = framebuffer.len();
|
||||
let (width, height) = gpu.resolution();
|
||||
|
||||
fn setup_framebuffer(driver: &mut VirtIOGpu) {
|
||||
// get display info
|
||||
let request_get_display_info =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuCtrlHdr) };
|
||||
*request_get_display_info = VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_GET_DISPLAY_INFO);
|
||||
request(driver);
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block();
|
||||
let response_get_display_info = unsafe {
|
||||
&mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuRespDisplayInfo)
|
||||
};
|
||||
info!("response: {:?}", response_get_display_info);
|
||||
driver.rect = response_get_display_info.rect;
|
||||
|
||||
// create resource 2d
|
||||
let request_resource_create_2d = unsafe {
|
||||
&mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuResourceCreate2D)
|
||||
};
|
||||
*request_resource_create_2d = VirtIOGpuResourceCreate2D {
|
||||
header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D),
|
||||
resource_id: VIRTIO_GPU_RESOURCE_ID,
|
||||
format: VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM,
|
||||
width: response_get_display_info.rect.width,
|
||||
height: response_get_display_info.rect.height,
|
||||
};
|
||||
request(driver);
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block();
|
||||
let response_resource_create_2d =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) };
|
||||
info!("response: {:?}", response_resource_create_2d);
|
||||
|
||||
// alloc continuous pages for the frame buffer
|
||||
let size = response_get_display_info.rect.width * response_get_display_info.rect.height * 4;
|
||||
let frame_buffer = unsafe {
|
||||
HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(size as usize, PAGE_SIZE).unwrap())
|
||||
} as usize;
|
||||
// test framebuffer
|
||||
// mandelbrot(
|
||||
// driver.rect.width,
|
||||
// driver.rect.height,
|
||||
// frame_buffer as *mut u32,
|
||||
// );
|
||||
driver.frame_buffer = frame_buffer;
|
||||
let request_resource_attach_backing = unsafe {
|
||||
&mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuResourceAttachBacking)
|
||||
};
|
||||
*request_resource_attach_backing = VirtIOGpuResourceAttachBacking {
|
||||
header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING),
|
||||
resource_id: VIRTIO_GPU_RESOURCE_ID,
|
||||
nr_entries: 1,
|
||||
addr: virt_to_phys(frame_buffer) as u64,
|
||||
length: size,
|
||||
padding: 0,
|
||||
};
|
||||
request(driver);
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block();
|
||||
let response_resource_attach_backing =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) };
|
||||
info!("response: {:?}", response_resource_attach_backing);
|
||||
|
||||
// map frame buffer to screen
|
||||
let request_set_scanout =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuSetScanout) };
|
||||
*request_set_scanout = VirtIOGpuSetScanout {
|
||||
header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_SET_SCANOUT),
|
||||
rect: response_get_display_info.rect,
|
||||
scanout_id: 0,
|
||||
resource_id: VIRTIO_GPU_RESOURCE_ID,
|
||||
};
|
||||
request(driver);
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block();
|
||||
let response_set_scanout =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) };
|
||||
info!("response: {:?}", response_set_scanout);
|
||||
|
||||
flush_frame_buffer_to_screen(driver);
|
||||
}
|
||||
|
||||
fn flush_frame_buffer_to_screen(driver: &mut VirtIOGpu) {
|
||||
// copy data from guest to host
|
||||
let request_transfer_to_host_2d = unsafe {
|
||||
&mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuTransferToHost2D)
|
||||
};
|
||||
*request_transfer_to_host_2d = VirtIOGpuTransferToHost2D {
|
||||
header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D),
|
||||
rect: driver.rect,
|
||||
offset: 0,
|
||||
resource_id: VIRTIO_GPU_RESOURCE_ID,
|
||||
padding: 0,
|
||||
};
|
||||
request(driver);
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block();
|
||||
let response_transfer_to_host_2d =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) };
|
||||
info!("response: {:?}", response_transfer_to_host_2d);
|
||||
|
||||
// flush data to screen
|
||||
let request_resource_flush = unsafe {
|
||||
&mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuResourceFlush)
|
||||
};
|
||||
*request_resource_flush = VirtIOGpuResourceFlush {
|
||||
header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_RESOURCE_FLUSH),
|
||||
rect: driver.rect,
|
||||
resource_id: VIRTIO_GPU_RESOURCE_ID,
|
||||
padding: 0,
|
||||
};
|
||||
request(driver);
|
||||
driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block();
|
||||
let response_resource_flush =
|
||||
unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) };
|
||||
info!("response: {:?}", response_resource_flush);
|
||||
}
|
||||
|
||||
pub fn virtio_gpu_init(node: &Node) {
|
||||
let reg = node.prop_raw("reg").unwrap();
|
||||
let paddr = reg.as_slice().read_be_u64(0).unwrap();
|
||||
let vaddr = phys_to_virt(paddr as usize);
|
||||
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
|
||||
|
||||
header.status.write(VirtIODeviceStatus::DRIVER.bits());
|
||||
|
||||
let device_features_bits = header.read_device_features();
|
||||
let device_features = VirtIOGpuFeature::from_bits_truncate(device_features_bits);
|
||||
info!("Device features {:?}", device_features);
|
||||
|
||||
// negotiate these flags only
|
||||
let supported_features = VirtIOGpuFeature::empty();
|
||||
let driver_features = (device_features & supported_features).bits();
|
||||
header.write_driver_features(driver_features);
|
||||
|
||||
// read configuration space
|
||||
let config = unsafe { &mut *((vaddr + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIOGpuConfig) };
|
||||
info!("Config: {:?}", config);
|
||||
|
||||
// virtio 4.2.4 Legacy interface
|
||||
// configure two virtqueues: ingress and egress
|
||||
header.guest_page_size.write(PAGE_SIZE as u32); // one page
|
||||
|
||||
let queue_num = 2;
|
||||
let queues = [
|
||||
VirtIOVirtqueue::new(header, VIRTIO_QUEUE_TRANSMIT, queue_num),
|
||||
VirtIOVirtqueue::new(header, VIRTIO_QUEUE_CURSOR, queue_num),
|
||||
];
|
||||
let mut driver = VirtIOGpu {
|
||||
interrupt: node.prop_u32("interrupts").unwrap(),
|
||||
interrupt_parent: node.prop_u32("interrupt-parent").unwrap(),
|
||||
header,
|
||||
queue_buffer: [0, 0],
|
||||
frame_buffer: 0,
|
||||
rect: VirtIOGpuRect::default(),
|
||||
queues,
|
||||
};
|
||||
|
||||
for buffer in 0..2 {
|
||||
// allocate a page for each buffer
|
||||
let page = unsafe {
|
||||
HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap())
|
||||
} as usize;
|
||||
driver.queue_buffer[buffer as usize] = page;
|
||||
debug!("buffer {} using page address {:#X}", buffer, page as usize);
|
||||
}
|
||||
|
||||
driver
|
||||
.header
|
||||
.status
|
||||
.write(VirtIODeviceStatus::DRIVER_OK.bits());
|
||||
|
||||
setup_framebuffer(&mut driver);
|
||||
// test
|
||||
// super::test::mandelbrot(width, height, fb_vaddr as _);
|
||||
// gpu.flush().expect("failed to flush");
|
||||
|
||||
use super::fb;
|
||||
fb::init(fb::FramebufferInfo {
|
||||
xres: driver.rect.width,
|
||||
yres: driver.rect.height,
|
||||
xres_virtual: driver.rect.width,
|
||||
yres_virtual: driver.rect.height,
|
||||
xres: width,
|
||||
yres: height,
|
||||
xres_virtual: width,
|
||||
yres_virtual: height,
|
||||
xoffset: 0,
|
||||
yoffset: 0,
|
||||
depth: fb::ColorDepth::ColorDepth32,
|
||||
format: fb::ColorFormat::RGBA8888,
|
||||
paddr: virt_to_phys(driver.frame_buffer),
|
||||
vaddr: driver.frame_buffer,
|
||||
screen_size: (driver.rect.width * driver.rect.height * 4) as usize,
|
||||
paddr: virt_to_phys(fb_vaddr),
|
||||
vaddr: fb_vaddr,
|
||||
screen_size: fb_size,
|
||||
});
|
||||
|
||||
let driver = Arc::new(VirtIOGpuDriver(Mutex::new(driver)));
|
||||
let driver = Arc::new(VirtIOGpuDriver(Mutex::new(gpu)));
|
||||
IRQ_MANAGER.write().register_all(driver.clone());
|
||||
DRIVERS.write().push(driver);
|
||||
}
|
||||
|
@ -1,159 +1,23 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec;
|
||||
use core::fmt;
|
||||
use core::mem::size_of;
|
||||
use core::mem::transmute_copy;
|
||||
use core::slice;
|
||||
|
||||
use bitflags::*;
|
||||
use device_tree::util::SliceRead;
|
||||
use device_tree::Node;
|
||||
use log::*;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
use volatile::Volatile;
|
||||
use virtio_drivers::{VirtIOHeader, VirtIOInput};
|
||||
|
||||
use crate::arch::cpu;
|
||||
use super::super::{DeviceType, Driver, DRIVERS, IRQ_MANAGER};
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
|
||||
use super::super::bus::virtio_mmio::*;
|
||||
use super::super::{DeviceType, Driver, DRIVERS, IRQ_MANAGER};
|
||||
use crate::memory::phys_to_virt;
|
||||
|
||||
struct VirtIOInput {
|
||||
interrupt_parent: u32,
|
||||
interrupt: u32,
|
||||
header: &'static mut VirtIOHeader,
|
||||
// 0 for event, 1 for status
|
||||
queues: [VirtIOVirtqueue; 2],
|
||||
x: isize,
|
||||
y: isize,
|
||||
}
|
||||
|
||||
const VIRTIO_INPUT_CFG_UNSET: u8 = 0x00;
|
||||
const VIRTIO_INPUT_CFG_ID_NAME: u8 = 0x01;
|
||||
const VIRTIO_INPUT_CFG_ID_SERIAL: u8 = 0x02;
|
||||
const VIRTIO_INPUT_CFG_ID_DEVIDS: u8 = 0x03;
|
||||
const VIRTIO_INPUT_CFG_PROP_BITS: u8 = 0x10;
|
||||
const VIRTIO_INPUT_CFG_EV_BITS: u8 = 0x11;
|
||||
const VIRTIO_INPUT_CFG_ABS_INFO: u8 = 0x12;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOInputConfig {
|
||||
select: Volatile<u8>,
|
||||
subsel: Volatile<u8>,
|
||||
size: u8,
|
||||
reversed: [u8; 5],
|
||||
data: [u8; 32],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOInputAbsInfo {
|
||||
min: u32,
|
||||
max: u32,
|
||||
fuzz: u32,
|
||||
flat: u32,
|
||||
res: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIOInputDevIDs {
|
||||
bustype: u16,
|
||||
vendor: u16,
|
||||
product: u16,
|
||||
version: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Default)]
|
||||
struct VirtIOInputEvent {
|
||||
event_type: u16,
|
||||
code: u16,
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl fmt::Display for VirtIOInputEvent {
|
||||
// linux event codes
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.event_type {
|
||||
0 => match self.code {
|
||||
0 => write!(f, "SYN_REPORT"),
|
||||
_ => write!(f, "Unknown SYN code {}", self.code),
|
||||
},
|
||||
2 => match self.code {
|
||||
0 => write!(f, "REL_X {}", self.value),
|
||||
1 => write!(f, "REL_Y {}", self.value),
|
||||
_ => write!(f, "Unknown REL code {}", self.code),
|
||||
},
|
||||
_ => write!(f, "Unknown event type {}", self.event_type),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct VirtIOInputFeature : u64 {
|
||||
// device independent
|
||||
const NOTIFY_ON_EMPTY = 1 << 24; // legacy
|
||||
const ANY_LAYOUT = 1 << 27; // legacy
|
||||
const RING_INDIRECT_DESC = 1 << 28;
|
||||
const RING_EVENT_IDX = 1 << 29;
|
||||
const UNUSED = 1 << 30; // legacy
|
||||
const VERSION_1 = 1 << 32; // detect legacy
|
||||
const ACCESS_PLATFORM = 1 << 33; // since virtio v1.1
|
||||
const RING_PACKED = 1 << 34; // since virtio v1.1
|
||||
const IN_ORDER = 1 << 35; // since virtio v1.1
|
||||
const ORDER_PLATFORM = 1 << 36; // since virtio v1.1
|
||||
const SR_IOV = 1 << 37; // since virtio v1.1
|
||||
const NOTIFICATION_DATA = 1 << 38; // since virtio v1.1
|
||||
}
|
||||
}
|
||||
|
||||
const VIRTIO_QUEUE_EVENT: usize = 0;
|
||||
const VIRTIO_QUEUE_STATUS: usize = 1;
|
||||
|
||||
pub struct VirtIOInputDriver(Mutex<VirtIOInput>);
|
||||
|
||||
impl VirtIOInput {
|
||||
fn try_handle_interrupt(&mut self, _irq: Option<u32>) -> bool {
|
||||
// for simplicity
|
||||
if cpu::id() > 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let interrupt = self.header.interrupt_status.read();
|
||||
if interrupt != 0 {
|
||||
self.header.interrupt_ack.write(interrupt);
|
||||
debug!("Got interrupt {:?}", interrupt);
|
||||
loop {
|
||||
if let Some((input, output, _, _)) = self.queues[VIRTIO_QUEUE_EVENT].get() {
|
||||
let event: VirtIOInputEvent = unsafe { transmute_copy(&input[0][0]) };
|
||||
if event.event_type == 2 && event.code == 0 {
|
||||
// X
|
||||
self.x += event.value as isize;
|
||||
} else if event.event_type == 2 && event.code == 1 {
|
||||
// X
|
||||
self.y += event.value as isize;
|
||||
}
|
||||
trace!("got {}", event);
|
||||
self.queues[VIRTIO_QUEUE_EVENT].add(&input, &output, 0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("mouse is at x {} y {}", self.x, self.y);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
struct VirtIOInputDriver(Mutex<VirtIOInput<'static>>);
|
||||
|
||||
impl Driver for VirtIOInputDriver {
|
||||
fn try_handle_interrupt(&self, irq: Option<u32>) -> bool {
|
||||
self.0.lock().try_handle_interrupt(irq)
|
||||
fn try_handle_interrupt(&self, _irq: Option<u32>) -> bool {
|
||||
let mut input = self.0.lock();
|
||||
let ack = input.ack_interrupt().expect("failed to ack interrupt");
|
||||
if ack {
|
||||
info!("mouse: {:?}", input.mouse_xy());
|
||||
}
|
||||
ack
|
||||
}
|
||||
|
||||
fn device_type(&self) -> DeviceType {
|
||||
@ -165,63 +29,11 @@ impl Driver for VirtIOInputDriver {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn virtio_input_init(node: &Node) {
|
||||
let reg = node.prop_raw("reg").unwrap();
|
||||
let paddr = reg.as_slice().read_be_u64(0).unwrap();
|
||||
let vaddr = phys_to_virt(paddr as usize);
|
||||
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
|
||||
pub fn init(header: &'static mut VirtIOHeader) {
|
||||
let event_buf = Box::leak(Box::new([0u64; 32]));
|
||||
let mut input = VirtIOInput::new(header, event_buf).expect("failed to create input driver");
|
||||
|
||||
header.status.write(VirtIODeviceStatus::DRIVER.bits());
|
||||
|
||||
let device_features_bits = header.read_device_features();
|
||||
let device_features = VirtIOInputFeature::from_bits_truncate(device_features_bits);
|
||||
println!("Device features {:?}", device_features);
|
||||
|
||||
// negotiate these flags only
|
||||
let supported_features = VirtIOInputFeature::empty();
|
||||
let driver_features = (device_features & supported_features).bits();
|
||||
header.write_driver_features(driver_features);
|
||||
|
||||
// read configuration space
|
||||
let config = unsafe { &mut *((vaddr + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIOInputConfig) };
|
||||
info!("Config: {:?}", config);
|
||||
|
||||
// virtio 4.2.4 Legacy interface
|
||||
// configure two virtqueues: ingress and egress
|
||||
header.guest_page_size.write(PAGE_SIZE as u32); // one page
|
||||
|
||||
let queue_num = 32;
|
||||
let queues = [
|
||||
VirtIOVirtqueue::new(header, VIRTIO_QUEUE_EVENT, queue_num),
|
||||
VirtIOVirtqueue::new(header, VIRTIO_QUEUE_STATUS, queue_num),
|
||||
];
|
||||
let mut driver = VirtIOInput {
|
||||
interrupt: node.prop_u32("interrupts").unwrap(),
|
||||
interrupt_parent: node.prop_u32("interrupt-parent").unwrap(),
|
||||
header,
|
||||
queues,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
let buffer = vec![VirtIOInputEvent::default(); queue_num];
|
||||
let input_buffers: &mut [VirtIOInputEvent] = Box::leak(buffer.into_boxed_slice());
|
||||
for i in 0..queue_num {
|
||||
let buffer = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
(&mut input_buffers[i]) as *mut VirtIOInputEvent as *mut u8,
|
||||
size_of::<VirtIOInputEvent>(),
|
||||
)
|
||||
};
|
||||
driver.queues[VIRTIO_QUEUE_EVENT].add(&[buffer], &[], 0);
|
||||
}
|
||||
|
||||
driver
|
||||
.header
|
||||
.status
|
||||
.write(VirtIODeviceStatus::DRIVER_OK.bits());
|
||||
|
||||
let driver = Arc::new(VirtIOInputDriver(Mutex::new(driver)));
|
||||
let driver = Arc::new(VirtIOInputDriver(Mutex::new(input)));
|
||||
IRQ_MANAGER.write().register_all(driver.clone());
|
||||
DRIVERS.write().push(driver);
|
||||
}
|
||||
|
@ -1,58 +1,24 @@
|
||||
use alloc::alloc::{GlobalAlloc, Layout};
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use core::mem::size_of;
|
||||
use core::slice;
|
||||
|
||||
use bitflags::*;
|
||||
use device_tree::util::SliceRead;
|
||||
use device_tree::Node;
|
||||
use log::*;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
use smoltcp::phy::{self, DeviceCapabilities};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{EthernetAddress, Ipv4Address};
|
||||
use smoltcp::Result;
|
||||
use volatile::{ReadOnly, Volatile};
|
||||
use virtio_drivers::{VirtIOHeader, VirtIONet};
|
||||
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
use crate::HEAP_ALLOCATOR;
|
||||
|
||||
use super::super::bus::virtio_mmio::*;
|
||||
use super::super::{DeviceType, Driver, DRIVERS, IRQ_MANAGER, NET_DRIVERS};
|
||||
use crate::memory::phys_to_virt;
|
||||
|
||||
pub struct VirtIONet {
|
||||
interrupt_parent: u32,
|
||||
interrupt: u32,
|
||||
header: usize,
|
||||
mac: EthernetAddress,
|
||||
// 0 for receive, 1 for transmit
|
||||
queues: [VirtIOVirtqueue; 2],
|
||||
}
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VirtIONetDriver(Arc<Mutex<VirtIONet>>);
|
||||
|
||||
const VIRTIO_QUEUE_RECEIVE: usize = 0;
|
||||
const VIRTIO_QUEUE_TRANSMIT: usize = 1;
|
||||
pub struct VirtIONetDriver(Arc<Mutex<VirtIONet<'static>>>);
|
||||
|
||||
impl Driver for VirtIONetDriver {
|
||||
fn try_handle_interrupt(&self, _irq: Option<u32>) -> bool {
|
||||
let driver = self.0.lock();
|
||||
|
||||
let header = unsafe { &mut *(driver.header as *mut VirtIOHeader) };
|
||||
let interrupt = header.interrupt_status.read();
|
||||
if interrupt != 0 {
|
||||
header.interrupt_ack.write(interrupt);
|
||||
let interrupt_status = VirtIONetworkInterruptStatus::from_bits_truncate(interrupt);
|
||||
debug!("Got interrupt {:?}", interrupt_status);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
self.0.lock().ack_interrupt()
|
||||
}
|
||||
|
||||
fn device_type(&self) -> DeviceType {
|
||||
@ -64,11 +30,11 @@ impl Driver for VirtIONetDriver {
|
||||
}
|
||||
|
||||
fn get_mac(&self) -> EthernetAddress {
|
||||
self.0.lock().mac
|
||||
EthernetAddress(self.0.lock().mac())
|
||||
}
|
||||
|
||||
fn get_ifname(&self) -> String {
|
||||
format!("virtio{}", self.0.lock().interrupt)
|
||||
format!("virtio{:?}", self.0.lock().mac())
|
||||
}
|
||||
|
||||
fn ipv4_address(&self) -> Option<Ipv4Address> {
|
||||
@ -80,40 +46,23 @@ impl Driver for VirtIONetDriver {
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtIONet {
|
||||
fn transmit_available(&self) -> bool {
|
||||
self.queues[VIRTIO_QUEUE_TRANSMIT].can_add(1, 0)
|
||||
}
|
||||
impl phy::Device<'_> for VirtIONetDriver {
|
||||
type RxToken = VirtIONetDriver;
|
||||
type TxToken = VirtIONetDriver;
|
||||
|
||||
fn receive_available(&self) -> bool {
|
||||
self.queues[VIRTIO_QUEUE_RECEIVE].can_get()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtIONetRxToken(VirtIONetDriver);
|
||||
pub struct VirtIONetTxToken(VirtIONetDriver);
|
||||
|
||||
impl<'a> phy::Device<'a> for VirtIONetDriver {
|
||||
type RxToken = VirtIONetRxToken;
|
||||
type TxToken = VirtIONetTxToken;
|
||||
|
||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
let driver = self.0.lock();
|
||||
if driver.transmit_available() && driver.receive_available() {
|
||||
// potential racing
|
||||
Some((
|
||||
VirtIONetRxToken(self.clone()),
|
||||
VirtIONetTxToken(self.clone()),
|
||||
))
|
||||
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
let mut net = self.0.lock();
|
||||
if net.can_recv() {
|
||||
Some((self.clone(), self.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||
let driver = self.0.lock();
|
||||
if driver.transmit_available() {
|
||||
Some(VirtIONetTxToken(self.clone()))
|
||||
fn transmit(&mut self) -> Option<Self::TxToken> {
|
||||
let mut net = self.0.lock();
|
||||
if net.can_send() {
|
||||
Some(self.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -127,169 +76,36 @@ impl<'a> phy::Device<'a> for VirtIONetDriver {
|
||||
}
|
||||
}
|
||||
|
||||
impl phy::RxToken for VirtIONetRxToken {
|
||||
impl phy::RxToken for VirtIONetDriver {
|
||||
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let (mut input, output, _, user_data) = {
|
||||
let mut driver = (self.0).0.lock();
|
||||
driver.queues[VIRTIO_QUEUE_RECEIVE].get().unwrap()
|
||||
};
|
||||
let result = f(&mut input[0][size_of::<VirtIONetHeader>()..]);
|
||||
|
||||
let mut driver = (self.0).0.lock();
|
||||
driver.queues[VIRTIO_QUEUE_RECEIVE].add_and_notify(&input, &output, user_data);
|
||||
result
|
||||
let mut buffer = [0u8; 2000];
|
||||
let mut driver = self.0.lock();
|
||||
let len = driver.recv(&mut buffer).expect("failed to recv packet");
|
||||
f(&mut buffer[..len])
|
||||
}
|
||||
}
|
||||
|
||||
impl phy::TxToken for VirtIONetTxToken {
|
||||
impl phy::TxToken for VirtIONetDriver {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let output = {
|
||||
let mut driver = (self.0).0.lock();
|
||||
if let Some((_, output, _, _)) = driver.queues[VIRTIO_QUEUE_TRANSMIT].get() {
|
||||
unsafe { slice::from_raw_parts_mut(output[0].as_ptr() as *mut u8, output[0].len()) }
|
||||
} else {
|
||||
// allocate a page for buffer
|
||||
let page = unsafe {
|
||||
HEAP_ALLOCATOR
|
||||
.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap())
|
||||
} as usize;
|
||||
unsafe { slice::from_raw_parts_mut(page as *mut u8, PAGE_SIZE) }
|
||||
}
|
||||
};
|
||||
let output_buffer =
|
||||
&mut output[size_of::<VirtIONetHeader>()..(size_of::<VirtIONetHeader>() + len)];
|
||||
let result = f(output_buffer);
|
||||
|
||||
let mut driver = (self.0).0.lock();
|
||||
assert!(driver.queues[VIRTIO_QUEUE_TRANSMIT].add_and_notify(&[], &[output], 0));
|
||||
let mut buffer = [0u8; 2000];
|
||||
let result = f(&mut buffer[..len]);
|
||||
let mut driver = self.0.lock();
|
||||
driver.send(&buffer).expect("failed to send packet");
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct VirtIONetFeature : u64 {
|
||||
const CSUM = 1 << 0;
|
||||
const GUEST_CSUM = 1 << 1;
|
||||
const CTRL_GUEST_OFFLOADS = 1 << 2;
|
||||
const MTU = 1 << 3;
|
||||
const MAC = 1 << 5;
|
||||
const GSO = 1 << 6;
|
||||
const GUEST_TSO4 = 1 << 7;
|
||||
const GUEST_TSO6 = 1 << 8;
|
||||
const GUEST_ECN = 1 << 9;
|
||||
const GUEST_UFO = 1 << 10;
|
||||
const HOST_TSO4 = 1 << 11;
|
||||
const HOST_TSO6 = 1 << 12;
|
||||
const HOST_ECN = 1 << 13;
|
||||
const HOST_UFO = 1 << 14;
|
||||
const MRG_RXBUF = 1 << 15;
|
||||
const STATUS = 1 << 16;
|
||||
const CTRL_VQ = 1 << 17;
|
||||
const CTRL_RX = 1 << 18;
|
||||
const CTRL_VLAN = 1 << 19;
|
||||
const CTRL_RX_EXTRA = 1 << 20;
|
||||
const GUEST_ANNOUNCE = 1 << 21;
|
||||
const MQ = 1 << 22;
|
||||
const CTL_MAC_ADDR = 1 << 23;
|
||||
// device independent
|
||||
const RING_INDIRECT_DESC = 1 << 28;
|
||||
const RING_EVENT_IDX = 1 << 29;
|
||||
const VERSION_1 = 1 << 32; // legacy
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct VirtIONetworkStatus : u16 {
|
||||
const LINK_UP = 1;
|
||||
const ANNOUNCE = 2;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct VirtIONetworkInterruptStatus : u32 {
|
||||
const USED_RING_UPDATE = 1 << 0;
|
||||
const CONFIGURATION_CHANGE = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIONetworkConfig {
|
||||
mac: [u8; 6],
|
||||
status: ReadOnly<u16>,
|
||||
}
|
||||
|
||||
// virtio 5.1.6 Device Operation
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct VirtIONetHeader {
|
||||
flags: Volatile<u8>,
|
||||
gso_type: Volatile<u8>,
|
||||
hdr_len: Volatile<u16>, // cannot rely on this
|
||||
gso_size: Volatile<u16>,
|
||||
csum_start: Volatile<u16>,
|
||||
csum_offset: Volatile<u16>,
|
||||
// payload starts from here
|
||||
}
|
||||
|
||||
pub fn virtio_net_init(node: &Node) {
|
||||
let reg = node.prop_raw("reg").unwrap();
|
||||
let paddr = reg.as_slice().read_be_u64(0).unwrap();
|
||||
let vaddr = phys_to_virt(paddr as usize);
|
||||
let header = unsafe { &mut *(vaddr as *mut VirtIOHeader) };
|
||||
|
||||
header.status.write(VirtIODeviceStatus::DRIVER.bits());
|
||||
|
||||
let device_features_bits = header.read_device_features();
|
||||
let device_features = VirtIONetFeature::from_bits_truncate(device_features_bits);
|
||||
debug!("Device features {:?}", device_features);
|
||||
|
||||
// negotiate these flags only
|
||||
let supported_features = VirtIONetFeature::MAC | VirtIONetFeature::STATUS;
|
||||
let driver_features = (device_features & supported_features).bits();
|
||||
header.write_driver_features(driver_features);
|
||||
|
||||
// read configuration space
|
||||
let config =
|
||||
unsafe { &mut *((vaddr + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIONetworkConfig) };
|
||||
let mac = config.mac;
|
||||
let status = VirtIONetworkStatus::from_bits_truncate(config.status.read());
|
||||
debug!("Got MAC address {:?} and status {:?}", mac, status);
|
||||
|
||||
// virtio 4.2.4 Legacy interface
|
||||
// configure two virtqueues: ingress and egress
|
||||
header.guest_page_size.write(PAGE_SIZE as u32); // one page
|
||||
|
||||
let queue_num = 2; // for simplicity
|
||||
let mut driver = VirtIONet {
|
||||
interrupt: node.prop_u32("interrupts").unwrap(),
|
||||
interrupt_parent: node.prop_u32("interrupt-parent").unwrap(),
|
||||
header: vaddr as usize,
|
||||
mac: EthernetAddress(mac),
|
||||
queues: [
|
||||
VirtIOVirtqueue::new(header, VIRTIO_QUEUE_RECEIVE, queue_num),
|
||||
VirtIOVirtqueue::new(header, VIRTIO_QUEUE_TRANSMIT, queue_num),
|
||||
],
|
||||
};
|
||||
|
||||
// allocate a page for buffer
|
||||
let buf_ptr = unsafe {
|
||||
HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap())
|
||||
};
|
||||
let input = unsafe { slice::from_raw_parts_mut(buf_ptr, PAGE_SIZE) };
|
||||
driver.queues[VIRTIO_QUEUE_RECEIVE].add_and_notify(&[input], &[], 0);
|
||||
|
||||
header.status.write(VirtIODeviceStatus::DRIVER_OK.bits());
|
||||
|
||||
let net_driver = Arc::new(VirtIONetDriver(Arc::new(Mutex::new(driver))));
|
||||
|
||||
DRIVERS.write().push(net_driver.clone());
|
||||
IRQ_MANAGER.write().register_all(net_driver.clone());
|
||||
NET_DRIVERS.write().push(net_driver);
|
||||
pub fn init(header: &'static mut VirtIOHeader) {
|
||||
let net = VirtIONet::new(header).expect("failed to create net driver");
|
||||
let driver = Arc::new(VirtIONetDriver(Arc::new(Mutex::new(net))));
|
||||
|
||||
DRIVERS.write().push(driver.clone());
|
||||
IRQ_MANAGER.write().register_all(driver.clone());
|
||||
NET_DRIVERS.write().push(driver);
|
||||
}
|
||||
|
@ -9,19 +9,50 @@ impl provider::Provider for Provider {
|
||||
const PAGE_SIZE: usize = PAGE_SIZE;
|
||||
|
||||
fn alloc_dma(size: usize) -> (usize, usize) {
|
||||
// TODO: allocate continuous pages
|
||||
let mut paddr = alloc_frame().unwrap();
|
||||
for i in 1..(size / PAGE_SIZE) {
|
||||
let paddr_new = alloc_frame().unwrap();
|
||||
assert_eq!(paddr - PAGE_SIZE, paddr_new);
|
||||
paddr = paddr_new;
|
||||
}
|
||||
let paddr = virtio_dma_alloc(size / PAGE_SIZE);
|
||||
let vaddr = phys_to_virt(paddr);
|
||||
(vaddr, paddr)
|
||||
}
|
||||
|
||||
fn dealloc_dma(vaddr: usize, size: usize) {
|
||||
let paddr = virt_to_phys(vaddr);
|
||||
dealloc_frame(paddr);
|
||||
for i in 0..size / PAGE_SIZE {
|
||||
dealloc_frame(paddr + i * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
|
||||
// TODO: allocate continuous pages
|
||||
let mut paddr = alloc_frame().unwrap();
|
||||
for _ in 1..pages {
|
||||
let paddr_new = alloc_frame().unwrap();
|
||||
assert_eq!(paddr - PAGE_SIZE, paddr_new);
|
||||
paddr = paddr_new;
|
||||
}
|
||||
trace!("alloc DMA: paddr={:#x}, pages={}", paddr, pages);
|
||||
paddr
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn virtio_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
|
||||
for i in 0..pages {
|
||||
dealloc_frame(paddr + i * PAGE_SIZE);
|
||||
}
|
||||
trace!("dealloc DMA: paddr={:#x}, pages={}", paddr, pages);
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr {
|
||||
phys_to_virt(paddr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
|
||||
virt_to_phys(vaddr)
|
||||
}
|
||||
|
||||
type VirtAddr = usize;
|
||||
type PhysAddr = usize;
|
||||
|
Loading…
x
Reference in New Issue
Block a user