1
0
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:
Chen 2020-03-25 08:24:18 +08:00 committed by GitHub
commit 103a2da33b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 155 additions and 1375 deletions

12
kernel/Cargo.lock generated
View File

@ -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"

View File

@ -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" }

View File

@ -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);

View File

@ -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),
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;