back into virtio work

master
Able 2022-04-25 13:39:39 -05:00
parent c966d04af0
commit 2b431f3c0a
Signed by untrusted user: able
GPG Key ID: D164AF5F5700BE51
14 changed files with 619 additions and 27 deletions

View File

@ -18,17 +18,13 @@ run-args = [
"-smp",
"cores=2",
"-device",
"cirrus-vga",
# "-device",
# "virtio-blk-pci,drive=drive0,id=virtblk0,num-queues=4",
# A simple example of a boot image
# "-drive",
# "file=disk.qcow2,if=none,id=drive0",
"-device",
"virtio-rng",
"-soundhw", "pcspk",
"-device", "virtio-gpu-pci",
# "-machine", "pcspk-audiodev=0",
"-qmp",
"unix:../qmp-sock,server,nowait"

View File

@ -43,8 +43,7 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
// This gives fast interrupts
set_pit_frequency(1000);
reset_pit_for_cpu();
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
@ -151,13 +150,29 @@ pub fn init_idt() {
IDT.load();
}
fn set_pit_frequency(freq: u32) {
pub fn set_pit_frequency(pit: u16, freq: u32) {
let divisor: u16 = (1193180 / freq).try_into().unwrap();
unsafe {
outb(0x36, 0x43);
outb((divisor & 0xFF) as u8, 0x40);
outb((divisor >> 8) as u8, 0x40);
outb((divisor & 0xFF) as u8, 0x39 + pit);
outb((divisor >> 8) as u8, 0x40 + pit);
}
}
pub fn set_pit_1(freq: u32) {
set_pit_frequency(1, freq);
}
pub fn set_pit_2(freq: u32) {
set_pit_frequency(2, freq);
}
pub fn set_pit_3(freq: u32) {
set_pit_frequency(3, freq);
}
pub fn reset_pit_for_cpu() {
set_pit_1(1000);
set_pit_2(1000);
set_pit_3(1000);
}

View File

@ -1,9 +1,10 @@
pub mod character_devs;
pub mod id;
pub mod pci_inner;
mod dev_vterm;
pub mod id;
pub mod pci;
pub mod pci_inner;
pub use self::Device::*;
use crate::devices::dev_vterm::VTerm;

View File

@ -0,0 +1,178 @@
/// The major class specification for a PCI device.
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub enum PciClass {
Unclassified = 0x00,
MassStorage = 0x01,
Network = 0x02,
Display = 0x03,
Multimedia = 0x04,
Memory = 0x05,
Bridge = 0x06,
Other = 0xFF,
}
impl PciClass {
/// Convert a u8 into the corresponding PciClass
pub fn from_u8(n: u8) -> PciClass {
match n {
0x00 => PciClass::Unclassified,
0x01 => PciClass::MassStorage,
0x02 => PciClass::Network,
0x03 => PciClass::Display,
0x04 => PciClass::Multimedia,
0x05 => PciClass::Memory,
0x06 => PciClass::Bridge,
_ => PciClass::Other,
}
}
/// Convert a PciClass to its u8 representation
pub fn as_u8(&self) -> u8 {
*self as u8
}
}
impl From<u8> for PciClass {
/// Convert a u8 into the corresponding PciClass
fn from(n: u8) -> Self {
Self::from_u8(n)
}
}
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
/// Full class specification (type and subtype) for a PCI device.
///
/// Uses non-camel-case types for readability.
pub enum PciFullClass {
Unclassified_NonVgaCompatible = 0x0000,
Unclassified_VgaCompatible = 0x0001,
MassStorage_ScsiBus = 0x0100,
MassStorage_IDE = 0x0101,
MassStorage_Floppy = 0x0102,
MassStorage_IpiBus = 0x0103,
MassStorage_RAID = 0x0104,
MassStorage_ATA = 0x0105,
MassStorage_SATA = 0x0106,
MassStorage_SerialSCSI = 0x0107,
MassStorage_NVM = 0x0108,
MassStorage_Other = 0x0180,
Network_Ethernet = 0x0200,
Network_TokenRing = 0x0201,
Network_FDDI = 0x0202,
Network_ATM = 0x0203,
Network_ISDN = 0x0204,
Network_WorldFlip = 0x0205,
Network_PICMG = 0x0206,
Network_Infiniband = 0x0207,
Network_Fabric = 0x0208,
Network_Other = 0x0280,
Display_VGA = 0x0300,
Display_XGA = 0x0301,
Display_3D = 0x0302,
Display_Other = 0x0380,
Multimedia_Video = 0x0400,
Multimedia_AudioController = 0x0401,
Multimedia_Telephony = 0x0402,
Multimedia_AudioDevice = 0x0403,
Multimedia_Other = 0x0480,
Memory_RAM = 0x0500,
Memory_Flash = 0x0501,
Memory_Other = 0x0580,
Bridge_Host = 0x0600,
Bridge_ISA = 0x0601,
Bridge_EISA = 0x0602,
Bridge_MCA = 0x0603,
Bridge_PciToPci = 0x0604,
Bridge_PCMCIA = 0x0605,
Bridge_NuBus = 0x0606,
Bridge_CardBus = 0x0607,
Bridge_RACEway = 0x0608,
Bridge_PciToPciSemiTransparent = 0x0609,
Bridge_InfinibandToPci = 0x060A,
Bridge_Other = 0x0680,
Unknown = 0xFFFF,
}
impl PciFullClass {
// listen, i know this sucks, but i didn't want to include
// `num`, `num-traits` and `num-derive` as dependencies for
// this crate just for a convenience function
/// Convert a u16 into the corresponding PciFullClass
pub fn from_u16(n: u16) -> PciFullClass {
match n {
0x0000 => PciFullClass::Unclassified_NonVgaCompatible,
0x0001 => PciFullClass::Unclassified_VgaCompatible,
0x0100 => PciFullClass::MassStorage_ScsiBus,
0x0101 => PciFullClass::MassStorage_IDE,
0x0102 => PciFullClass::MassStorage_Floppy,
0x0103 => PciFullClass::MassStorage_IpiBus,
0x0104 => PciFullClass::MassStorage_RAID,
0x0105 => PciFullClass::MassStorage_ATA,
0x0106 => PciFullClass::MassStorage_SATA,
0x0107 => PciFullClass::MassStorage_SerialSCSI,
0x0108 => PciFullClass::MassStorage_NVM,
0x0180 => PciFullClass::MassStorage_Other,
0x0200 => PciFullClass::Network_Ethernet,
0x0201 => PciFullClass::Network_TokenRing,
0x0202 => PciFullClass::Network_FDDI,
0x0203 => PciFullClass::Network_ATM,
0x0204 => PciFullClass::Network_ISDN,
0x0205 => PciFullClass::Network_WorldFlip,
0x0206 => PciFullClass::Network_PICMG,
0x0207 => PciFullClass::Network_Infiniband,
0x0208 => PciFullClass::Network_Fabric,
0x0280 => PciFullClass::Network_Other,
0x0300 => PciFullClass::Display_VGA,
0x0301 => PciFullClass::Display_XGA,
0x0302 => PciFullClass::Display_3D,
0x0380 => PciFullClass::Display_Other,
0x0400 => PciFullClass::Multimedia_Video,
0x0401 => PciFullClass::Multimedia_AudioController,
0x0402 => PciFullClass::Multimedia_Telephony,
0x0403 => PciFullClass::Multimedia_AudioDevice,
0x0480 => PciFullClass::Multimedia_Other,
0x0500 => PciFullClass::Memory_RAM,
0x0501 => PciFullClass::Memory_Flash,
0x0580 => PciFullClass::Memory_Other,
0x0600 => PciFullClass::Bridge_Host,
0x0601 => PciFullClass::Bridge_ISA,
0x0602 => PciFullClass::Bridge_EISA,
0x0603 => PciFullClass::Bridge_MCA,
0x0604 => PciFullClass::Bridge_PciToPci,
0x0605 => PciFullClass::Bridge_PCMCIA,
0x0606 => PciFullClass::Bridge_NuBus,
0x0607 => PciFullClass::Bridge_CardBus,
0x0608 => PciFullClass::Bridge_RACEway,
0x0609 => PciFullClass::Bridge_PciToPciSemiTransparent,
0x060A => PciFullClass::Bridge_InfinibandToPci,
0x0680 => PciFullClass::Bridge_Other,
_ => PciFullClass::Unknown,
}
}
/// Convert a PciFullClass to its u16 representation
pub fn as_u16(&self) -> u16 {
*self as u16
}
}
impl From<u16> for PciFullClass {
/// Convert a u16 into the corresponding PciFullClass
fn from(n: u16) -> Self {
Self::from_u16(n)
}
}

View File

@ -0,0 +1,227 @@
// The MIT License (MIT)
// Copyright (c) 2021 trashbyte
// See LICENSE.txt for full license
#![feature(asm)]
extern crate alloc;
use alloc::{format, string::String, vec::Vec};
use core::fmt::{Display, Error, Formatter};
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
mod enums;
pub use enums::*;
pub mod port;
use port::*;
// PciDeviceInfo ///////////////////////////////////////////////////////////////
#[allow(dead_code)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
/// A struct containing info about a PCI device.
pub struct PciDeviceInfo {
pub device: u8,
pub bus: u8,
pub device_id: u16,
pub vendor_id: u16,
pub full_class: PciFullClass,
pub header_type: u8,
pub bars: [u32; 6],
pub supported_fns: [bool; 8],
pub interrupt_line: u8,
pub interrupt_pin: u8,
}
impl PciDeviceInfo {
/// Get the class of the PCI device as a PciClass
pub fn class(&self) -> PciClass {
PciClass::from_u8(((self.full_class.as_u16() >> 8) & 0xFF) as u8)
}
/// Get the full class of the PCI device as a PciFullClass
pub fn subclass(&self) -> PciClass {
PciClass::from_u8((self.full_class.as_u16() & 0xFF) as u8)
}
}
impl Display for PciDeviceInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let vendor_name = name_for_vendor_id(self.vendor_id);
writeln!(
f,
"Device {:X} | Bus {:X} | Vendor: {}",
self.device, self.bus, vendor_name
)?;
writeln!(
f,
" Class: {:?} ({:#06X})",
self.full_class,
self.full_class.as_u16()
)?;
writeln!(f, " Header type: {:X}", self.header_type)?;
write!(f, " Supported functions: 0")?;
for (i, b) in self.supported_fns.iter().enumerate().skip(1) {
if *b {
write!(f, ", {}", i)?;
}
}
writeln!(f)?;
write!(f, " BARs: [ ")?;
for i in self.bars.iter() {
if *i == 0 {
write!(f, "0x0 ")?;
} else {
write!(f, "{:#010X} ", i)?;
}
}
writeln!(f, "]")?;
writeln!(
f,
" Interrupt line / pin: {} / {}",
self.interrupt_line, self.interrupt_pin
)?;
Ok(())
}
}
// Public functions ////////////////////////////////////////////////////////////
/// Converts a u16 vendor id into a human-readable name.
pub fn name_for_vendor_id(vendor_id: u16) -> String {
match vendor_id {
0x8086 => "Intel Corp. (0x8086)".into(),
0x1234 => "QEMU (0x1234)".into(),
_ => format!("Unknown({:#06X})", vendor_id),
}
}
/// Brute force scans for devices 0-31 on buses 0-255.
pub fn brute_force_scan() -> Vec<PciDeviceInfo> {
let mut infos = Vec::new();
for bus in 0u8..=255 {
for device in 0u8..32 {
if let Some(info) = check_device(bus, device) {
infos.push(info);
}
}
}
infos
}
// Internal functions //////////////////////////////////////////////////////////
fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
assert!(device < 32);
let function = 0u8;
let (device_id, vendor_id) = get_ids(bus, device, function);
if vendor_id == 0xFFFF {
// Device doesn't exist
return None;
}
let class = unsafe { pci_config_read(bus, device, 0, 0x8) };
let class = (class >> 16) & 0x0000FFFF;
let pci_class = PciFullClass::from_u16(class as u16);
let header_type = get_header_type(bus, device, function);
let mut supported_fns = [true, false, false, false, false, false, false, false];
if (header_type & 0x80) != 0 {
// It is a multi-function device, so check remaining functions
for function in 0u8..8 {
if get_ids(bus, device, function).1 != 0xFFFF {
if check_function(bus, device, function) {
supported_fns[function as usize] = true;
}
}
}
}
let mut bars = [0, 0, 0, 0, 0, 0];
unsafe {
bars[0] = pci_config_read(bus, device, 0, 0x10);
bars[1] = pci_config_read(bus, device, 0, 0x14);
bars[2] = pci_config_read(bus, device, 0, 0x18);
bars[3] = pci_config_read(bus, device, 0, 0x1C);
bars[4] = pci_config_read(bus, device, 0, 0x20);
bars[5] = pci_config_read(bus, device, 0, 0x24);
}
let last_row = unsafe { pci_config_read(bus, device, 0, 0x3C) };
Some(PciDeviceInfo {
device,
bus,
device_id,
vendor_id,
full_class: pci_class,
header_type,
bars,
supported_fns,
interrupt_line: (last_row & 0xFF) as u8,
interrupt_pin: ((last_row >> 8) & 0xFF) as u8,
})
}
unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
let bus = bus as u32;
let device = device as u32;
let func = func as u32;
let offset = offset as u32;
// construct address param
let address =
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
// write address
unsafe {
Port::<u32>::new(0xCF8).write(address);
}
// read data
unsafe { Port::<u32>::new(0xCFC).read() }
}
#[allow(dead_code)]
unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) {
let bus = bus as u32;
let device = device as u32;
let func = func as u32;
let offset = offset as u32;
// construct address param
let address =
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
// write address
unsafe {
Port::<u32>::new(0xCF8).write(address);
}
// write data
unsafe {
Port::<u32>::new(0xCFC).write(value);
}
}
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
assert!(device < 32);
assert!(function < 8);
let res = unsafe { pci_config_read(bus, device, function, 0x0C) };
((res >> 16) & 0xFF) as u8
}
fn check_function(bus: u8, device: u8, function: u8) -> bool {
assert!(device < 32);
assert!(function < 8);
get_ids(bus, device, function).1 != 0xFFFF
}
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
assert!(device < 32);
assert!(function < 8);
let res = unsafe { pci_config_read(bus, device, function, 0) };
let dev_id = ((res >> 16) & 0xFFFF) as u16;
let vnd_id = (res & 0xFFFF) as u16;
(dev_id, vnd_id)
}

View File

@ -0,0 +1,72 @@
use core::marker::PhantomData;
/// Trait for limiting [Port] to only being able to read/write u8/16/32.
pub(crate) trait PortRW {
/// Read a value (self) from the port
unsafe fn read_port(port: u16) -> Self;
/// Write a value (self) to the port
unsafe fn write_port(port: u16, value: Self);
}
// PortRW Implementations //////////////////////////////////////////////////////////////////////////
impl PortRW for u8 {
unsafe fn read_port(port: u16) -> Self {
let value: u8;
asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags));
value
}
unsafe fn write_port(port: u16, value: Self) {
asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack, preserves_flags));
}
}
impl PortRW for u16 {
unsafe fn read_port(port: u16) -> Self {
let value: u16;
asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack, preserves_flags));
value
}
unsafe fn write_port(port: u16, value: Self) {
asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack, preserves_flags));
}
}
impl PortRW for u32 {
unsafe fn read_port(port: u16) -> Self {
let value: u32;
asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack, preserves_flags));
value
}
unsafe fn write_port(port: u16, value: Self) {
asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack, preserves_flags));
}
}
/// A simple wrapper around the asm instructions needed to read/write I/O ports.
pub(crate) struct Port<T: PortRW> {
addr: u16,
_phantom: PhantomData<T>,
}
impl<T: PortRW> Port<T> {
/// Create a new `Port` with the given address and data size
pub(crate) fn new(addr: u16) -> Self {
Self {
addr,
_phantom: PhantomData,
}
}
/// Read a value from the port
pub(crate) unsafe fn read(&self) -> T {
T::read_port(self.addr)
}
/// Write a value to the port
pub(crate) unsafe fn write(&self, value: T) {
T::write_port(self.addr, value);
}
}

View File

@ -2,6 +2,7 @@
//!
//!
#[repr(C)]
pub enum Vendors {
ThreeDfxInteractiveInc = 0x121a,
ThreeDLabs = 0x3d3d,

View File

@ -34,10 +34,7 @@ impl log::Log for SimpleLogger {
record.args()
);
// kprint!("{}", msg);
// NOTE: This needs to be fixed before merge
if KERNEL_CONF.logging.log_to_serial {
// #[track_caller]
serial_println!(
"[{}{}{}][{}{}{}] {}",
color.0,

View File

@ -1,8 +1,11 @@
use cpuio::{inb, inl, outb, outl};
use cpuio::{inb, inl, inw, outb, outl, outw};
pub fn read32(reg: u16) -> u32 {
unsafe { inl(reg) }
}
pub fn read16(reg: u16) -> u16 {
unsafe { inw(reg) as u16 }
}
pub fn read8(reg: u16) -> u8 {
unsafe { inb(reg) }
@ -12,6 +15,10 @@ pub fn write32(reg: u16, val: u32) {
unsafe { outl(val, reg) }
}
pub fn write16(reg: u16, val: u16) {
unsafe { outw(val, reg) }
}
pub fn write8(reg: u16, val: u8) {
unsafe { outb(val, reg) }
}

View File

@ -1 +1,5 @@
pub mod rust_2021;
pub use core::assert;
pub use core::option::Option::Some;
pub use core::option::*;

View File

@ -2,6 +2,7 @@ pub use crate::print::*;
pub use crate::serial_print::*;
pub use alloc::{boxed::Box, format, string::*, vec, vec::*};
pub use core::arch::asm;
pub use core::prelude::rust_2021::*;
pub use core::prelude::v1::*;
pub use log::{debug, info, trace, warn};

View File

@ -1,9 +1,13 @@
use core::alloc::Layout;
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
use crate::devices::{pci, pci_inner};
use crate::encoding::bin;
use crate::port_io::{read32, read8, write8};
use crate::rhai_shell::shell;
use crate::wasm_jumploader::run_program;
use acpi::{AcpiTables, PlatformInfo};
use cpuio::outb;
use genfs::Fs;
/// Experimental scratchpad for testing.
@ -16,14 +20,33 @@ pub fn scratchpad() {
foo: (None) -> (Num);
}";
let axel = axel::parse(axel_raw.to_string());
for node in axel {
info!("{:?}", node);
}
use crate::devices::pci::brute_force_scan;
let infos = brute_force_scan();
for device in infos {
match device.vendor_id {
0x1af4 => {
info!("Found virtio device");
use crate::virtio::device_handler;
device_handler(device);
}
_ => {
info!("Found unknown device");
}
}
}
// sound(1000);
// sound_off();
// acpi();
real_shell();
}
pub fn pci_fun() {}
use crate::port_io::write16;
pub fn acpi() {
let acpi_handler = AcpiStruct {};
@ -176,3 +199,31 @@ pub fn command_parser(user: String, command: String) {
use crate::filesystem::FILE_SYSTEM;
use genfs::OpenOptions;
pub fn sound(n_frequency: u32) {
let div: u32;
let tmp: u8;
div = 1193180 / n_frequency;
unsafe {
outb(0xb6, 0x43);
set_pit_2(div);
//And play the sound using the PC speaker
tmp = inb(0x61);
if tmp != (tmp | 3) {
outb(tmp | 3, 0x61);
}
}
}
pub fn sound_off() {
unsafe {
let tmp = inb(0x61) & 0xFC;
outb(tmp, 0x61)
};
reset_pit_for_cpu();
}
use cpuio::inb;

View File

@ -9,6 +9,26 @@ pub struct VirtioDevice {
status: VirtioDeviceStatus,
}
use crate::devices::pci::PciDeviceInfo;
pub fn device_handler(device: PciDeviceInfo) {
use crate::devices::pci::PciClass::*;
match device.class() {
// Unclassified => todo!(),
// MassStorage => todo!(),
// Network => todo!(),
Display => {
trace!("Display found");
println!("{}", device.interrupt_pin);
}
// Multimedia => todo!(),
// Memory => todo!(),
// Bridge => todo!(),
// Other => todo!(),
_ => {}
}
}
#[repr(C)]
pub enum VirtioDeviceStatus {
// Indicates that the guest OS has found the device and recognized it as a valid virtio device.
@ -59,3 +79,16 @@ pub struct VirtioDeviceNotification {
pub index: u32,
pub value: u32,
}
pub struct Vring {
pub addr: u64,
pub len: u32,
pub flags: u16,
pub next: u16,
}
pub struct VringAvail {
pub flags: u16,
pub idx: u16,
pub ring: [u16; 0xFFFF],
}

View File

@ -2,7 +2,9 @@ pub mod host_functions;
use crate::{filesystem::FILE_SYSTEM, wasm_jumploader::host_functions::HostExternals};
use genfs::{Fs, OpenOptions};
use wasmi::{ImportsBuilder, ModuleInstance};
use wasmi::{
ImportsBuilder, MemoryDescriptor, ModuleImportResolver, ModuleInstance, StackRecycler,
};
pub fn interp() {
trace!("Interpreting...");
@ -101,17 +103,24 @@ pub fn interp() {
pub fn run_program(program: Vec<u8>) {
// Load wasm binary and prepare it for instantiation.
let module = wasmi::Module::from_buffer(&program).expect("failed to load wasm");
trace!("Loaded wasm binary");
let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {});
trace!("Created imports");
// Instantiate a module with empty imports and
// assert that there is no `start` function.
let instance = ModuleInstance::new(&module, &imports); // .expect("failed to instantiate wasm module")
let instance = ModuleInstance::new(&module, &imports);
// .expect("failed to instantiate wasm module")
use wasmi::GlobalRef;
match instance {
Ok(inst) => {
let instance = inst.assert_no_start();
let mut instance = inst.assert_no_start();
let abc = instance.globals().capacity();
let mut is_driver = false;
let _is_program = false;
let mut has_driver_entry = false;