refactor virtio code; APIC , console interrupt support

This commit is contained in:
Yuke Peng 2022-12-26 04:33:27 -08:00
parent 1d401fd8fc
commit 46497554d2
48 changed files with 2365 additions and 548 deletions

43
src/Cargo.lock generated
View File

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "acpi"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "654f48ab3178632ea535be1765073b990895cb62f70a7e5671975d7150c26d15"
dependencies = [
"bit_field",
"log",
"rsdp",
]
[[package]]
name = "anyhow"
version = "1.0.32"
@ -56,6 +67,12 @@ dependencies = [
"spin 0.7.1",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "either"
version = "1.8.0"
@ -100,6 +117,7 @@ dependencies = [
name = "jinux-frame"
version = "0.1.0"
dependencies = [
"acpi",
"bitflags",
"bootloader",
"buddy_system_allocator",
@ -153,6 +171,7 @@ dependencies = [
"spin 0.9.4",
"typeflags",
"typeflags-util",
"virtio-input-decoder",
"vte",
"xmas-elf",
]
@ -228,6 +247,15 @@ dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "pod"
version = "0.1.0"
@ -265,6 +293,15 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310a2514cb46bb500a2f2ad70c79c51c5a475cc23faa245d2c675faabe889370"
[[package]]
name = "rsdp"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d3add2fc55ef37511bcf81a08ee7a09eff07b23aae38b06a29024a38c604b1"
dependencies = [
"log",
]
[[package]]
name = "runner-utils"
version = "0.0.2"
@ -385,6 +422,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]]
name = "virtio-input-decoder"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7292fc7f4de1afc0ded2e9b6e6c557d9cd594fca27a2217eb97a89d34cf5c6f5"
[[package]]
name = "volatile"
version = "0.4.5"

View File

@ -17,6 +17,7 @@ font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"]}
uart_16550 = "0.2.0"
pod = {path = "../pod"}
pod-derive = {path = "../pod-derive"}
acpi= "4.1.1"
[dependencies.lazy_static]
version = "1.0"

View File

@ -2,8 +2,8 @@
use crate::log::LogLevel;
pub const USER_STACK_SIZE: usize = PAGE_SIZE * 2;
pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 16;
pub const USER_STACK_SIZE: usize = PAGE_SIZE * 4;
pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 64;
pub const KERNEL_HEAP_SIZE: usize = 0x1_000_000;
pub const KERNEL_OFFSET: usize = 0xffffff00_00000000;
@ -16,3 +16,5 @@ pub const PAGE_SIZE_BITS: usize = 0xc;
pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS;
pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Close;
/// This value represent the base timer frequency in Hz
pub const TIMER_FREQ: u64 = 100;

View File

@ -0,0 +1,111 @@
use lazy_static::lazy_static;
use crate::{cell::Cell, driver::pic, x86_64_util::*, IrqAllocateHandle, TrapFrame};
use core::fmt::{self, Write};
bitflags::bitflags! {
struct LineSts: u8 {
const INPUT_FULL = 1;
const OUTPUT_EMPTY = 1 << 5;
}
}
/// A port-mapped UART. Copied from uart_16550.
const SERIAL_DATA: u16 = 0x3F8;
const SERIAL_INT_EN: u16 = SERIAL_DATA + 1;
const SERIAL_FIFO_CTRL: u16 = SERIAL_DATA + 2;
const SERIAL_LINE_CTRL: u16 = SERIAL_DATA + 3;
const SERIAL_MODEM_CTRL: u16 = SERIAL_DATA + 4;
const SERIAL_LINE_STS: u16 = SERIAL_DATA + 5;
lazy_static! {
static ref CONSOLE_IRQ_CALLBACK: Cell<IrqAllocateHandle> =
Cell::new(pic::allocate_irq(4).unwrap());
}
/// Initializes the serial port.
pub(crate) fn init() {
// Disable interrupts
out8(SERIAL_INT_EN, 0x00);
// Enable DLAB
out8(SERIAL_LINE_CTRL, 0x80);
// Set maximum speed to 38400 bps by configuring DLL and DLM
out8(SERIAL_DATA, 0x03);
out8(SERIAL_INT_EN, 0x00);
// Disable DLAB and set data word length to 8 bits
out8(SERIAL_LINE_CTRL, 0x03);
// Enable FIFO, clear TX/RX queues and
// set interrupt watermark at 14 bytes
out8(SERIAL_FIFO_CTRL, 0xC7);
// Mark data terminal ready, signal request to send
// and enable auxilliary output #2 (used as interrupt line for CPU)
out8(SERIAL_MODEM_CTRL, 0x0B);
// Enable interrupts
out8(SERIAL_INT_EN, 0x01);
}
pub fn register_console_input_callback<F>(callback: F)
where
F: Fn(&TrapFrame) + Sync + Send + 'static,
{
CONSOLE_IRQ_CALLBACK.get().on_active(callback);
}
fn line_sts() -> LineSts {
LineSts::from_bits_truncate(in8(SERIAL_LINE_STS))
}
/// Sends a byte on the serial port.
pub fn send(data: u8) {
match data {
8 | 0x7F => {
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
out8(SERIAL_DATA, 8);
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
out8(SERIAL_DATA, b' ');
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
out8(SERIAL_DATA, 8)
}
_ => {
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
out8(SERIAL_DATA, data);
}
}
}
/// Receives a byte on the serial port. non-blocking
pub fn receive_char() -> Option<u8> {
if line_sts().contains(LineSts::INPUT_FULL) {
Some(in8(SERIAL_DATA))
} else {
None
}
}
struct Stdout;
impl Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result {
for &c in s.as_bytes() {
send(c);
}
Ok(())
}
}
pub fn print(args: fmt::Arguments) {
Stdout.write_fmt(args).unwrap();
}
#[macro_export]
macro_rules! console_print {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::device::console::print(format_args!($fmt $(, $($arg)+)?))
}
}
#[macro_export]
macro_rules! console_println {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::device::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
}
}

View File

@ -1,16 +1,19 @@
//! Device-related APIs.
pub mod framebuffer;
pub mod console;
mod io_port;
pub mod pci;
mod pic;
pub mod serial;
pub use self::io_port::IoPort;
pub(crate) use pic::{add_timeout_list, TICK};
pub use pic::{TimerCallback, TIMER_FREQ};
pub(crate) fn init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) {
/// first step to init device, call before the memory allocator init
pub(crate) fn first_init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) {
framebuffer::init(framebuffer);
pic::init();
console::init();
}
/// second step to init device, call after the memory allocator init
pub(crate) fn second_init() {
console::register_console_input_callback(|trap| {});
}

View File

@ -1,47 +0,0 @@
use lazy_static::lazy_static;
use spin::Mutex;
use uart_16550::SerialPort;
lazy_static! {
pub static ref SERIAL: Mutex<SerialPort> = {
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
serial_port.init();
Mutex::new(serial_port)
};
}
/// read a char from the keyboard input.
/// FIXME: this function should **NOT** block. If no char receives, this function should return None immediately.
/// However, the receive function on SERIAL will block until a char is received, which will block the whole kernel.
/// A more correct implementation should be added once interrupt is ready. We should register the kerboard interrupt
/// handler to wake up foreground processes which wait on IOEVENTS.
pub fn receive_char() -> Option<u8> {
let byte = SERIAL.lock().receive();
Some(byte)
}
#[doc(hidden)]
pub fn _print(args: ::core::fmt::Arguments) {
use core::fmt::Write;
SERIAL
.lock()
.write_fmt(args)
.expect("Printing to serial failed");
}
/// Prints to the host through the serial interface.
#[macro_export]
macro_rules! serial_print {
($($arg:tt)*) => {
$crate::device::serial::_print(format_args!($($arg)*));
};
}
/// Prints to the host through the serial interface, appending a newline.
#[macro_export]
macro_rules! serial_println {
() => ($crate::serial_print!("\n"));
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
concat!($fmt, "\n"), $($arg)*));
}

View File

@ -0,0 +1,45 @@
use core::ptr::NonNull;
use crate::{info, mm::address::phys_to_virt};
use acpi::{AcpiHandler, AcpiTables};
use lazy_static::lazy_static;
use spin::Mutex;
lazy_static! {
/// RSDP information, key is the signature, value is the virtual address of the signature
pub(crate) static ref ACPI_TABLES : Mutex<AcpiTables<AcpiMemoryHandler>> = unsafe{
Mutex::new(core::mem::MaybeUninit::zeroed().assume_init())
};
}
#[derive(Debug, Clone)]
pub struct AcpiMemoryHandler {}
impl AcpiHandler for AcpiMemoryHandler {
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> acpi::PhysicalMapping<Self, T> {
acpi::PhysicalMapping::new(
physical_address,
NonNull::new(phys_to_virt(physical_address) as *mut T).unwrap(),
size,
size,
self.clone(),
)
}
fn unmap_physical_region<T>(region: &acpi::PhysicalMapping<Self, T>) {}
}
pub fn init(rsdp: u64) {
let a = unsafe { AcpiTables::from_rsdp(AcpiMemoryHandler {}, rsdp as usize).unwrap() };
*ACPI_TABLES.lock() = a;
let c = ACPI_TABLES.lock();
for (signature, sdt) in c.sdts.iter() {
info!("ACPI found signature:{:?}", signature);
}
info!("acpi init complete");
}

View File

@ -0,0 +1,210 @@
use crate::{cell::Cell, debug, x86_64_util};
use lazy_static::lazy_static;
use volatile::{
access::{ReadOnly, ReadWrite, WriteOnly},
Volatile,
};
pub(crate) const IA32_APIC_BASE_MSR: u32 = 0x1B;
pub(crate) const IA32_APIC_BASE_MSR_BSP: u32 = 0x100; // Processor is a BSP
pub(crate) const IA32_APIC_BASE_MSR_ENABLE: u32 = 0x800;
const APIC_LVT_MASK_BITS: u32 = 1 << 16;
lazy_static! {
pub static ref APIC_INSTANCE: Cell<APIC> = Cell::new(APIC::new());
}
#[derive(Debug)]
pub struct APIC {
local_apic_id_register: Volatile<&'static mut u32, ReadWrite>,
local_apic_version_register: Volatile<&'static u32, ReadOnly>,
task_priority_register: Volatile<&'static mut u32, ReadWrite>,
arbitration_priority_register: Volatile<&'static u32, ReadOnly>,
processor_priority_register: Volatile<&'static u32, ReadOnly>,
pub eoi_register: Volatile<&'static mut u32, WriteOnly>,
remote_read_register: Volatile<&'static u32, ReadOnly>,
logical_destination_register: Volatile<&'static mut u32, ReadWrite>,
destination_format_register: Volatile<&'static mut u32, ReadWrite>,
spurious_interrupt_vector_register: Volatile<&'static mut u32, ReadWrite>,
/// total 256 bits, 32 bits per element
isr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8],
/// total 256 bits, 32 bits per element
tmr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8],
/// total 256 bits, 32 bits per element
irr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8],
pub error_status_register: Volatile<&'static u32, ReadOnly>,
lvt_cmci_register: Volatile<&'static mut u32, ReadWrite>,
icr_bits_31_0: Volatile<&'static mut u32, ReadWrite>,
icr_bits_63_32: Volatile<&'static mut u32, ReadWrite>,
pub lvt_timer_register: Volatile<&'static mut u32, ReadWrite>,
lvt_thermal_sensor_register: Volatile<&'static mut u32, ReadWrite>,
lvt_performance_monitoring_counters_register: Volatile<&'static mut u32, ReadWrite>,
lvt_lint0_register: Volatile<&'static mut u32, ReadWrite>,
lvt_lint1_register: Volatile<&'static mut u32, ReadWrite>,
lvt_error_register: Volatile<&'static mut u32, ReadWrite>,
pub initial_count_register: Volatile<&'static mut u32, ReadWrite>,
pub current_count_register: Volatile<&'static u32, ReadOnly>,
pub divide_configuration_register: Volatile<&'static mut u32, ReadWrite>,
}
impl APIC {
pub fn new() -> Self {
let base_address = get_apic_base_address();
let local_apic_id_register = Self::new_read_write_volatile(base_address + 0x0020);
let local_apic_version_register = Self::new_read_only_volatile(base_address + 0x0030);
let task_priority_register = Self::new_read_write_volatile(base_address + 0x0080);
let arbitration_priority_register = Self::new_read_only_volatile(base_address + 0x0090);
let processor_priority_register = Self::new_read_only_volatile(base_address + 0x00A0);
let eoi_register = Self::new_write_only_volatile(base_address + 0x00B0);
let remote_read_register = Self::new_read_only_volatile(base_address + 0x00C0);
let logical_destination_register = Self::new_read_write_volatile(base_address + 0x00D0);
let destination_format_register = Self::new_read_write_volatile(base_address + 0x00E0);
let spurious_interrupt_vector_register =
Self::new_read_write_volatile(base_address + 0x00F0);
let mut isr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8] =
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
for i in 0..8 {
isr_per_32_bits[i] = Self::new_read_only_volatile(base_address + 0x0100 + i * 0x0010);
}
let mut tmr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8] =
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
for i in 0..8 {
tmr_per_32_bits[i] = Self::new_read_only_volatile(base_address + 0x0180 + i * 0x0010);
}
let mut irr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8] =
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
for i in 0..8 {
irr_per_32_bits[i] = Self::new_read_only_volatile(base_address + 0x0200 + i * 0x0010);
}
let error_status_register = Self::new_read_only_volatile(base_address + 0x0280);
let lvt_cmci_register = Self::new_read_write_volatile(base_address + 0x02F0);
let icr_bits_31_0 = Self::new_read_write_volatile(base_address + 0x0300);
let icr_bits_63_32 = Self::new_read_write_volatile(base_address + 0x0310);
let lvt_timer_register = Self::new_read_write_volatile(base_address + 0x0320);
let lvt_thermal_sensor_register = Self::new_read_write_volatile(base_address + 0x0330);
let lvt_performance_monitoring_counters_register =
Self::new_read_write_volatile(base_address + 0x0340);
let lvt_lint0_register = Self::new_read_write_volatile(base_address + 0x0350);
let lvt_lint1_register = Self::new_read_write_volatile(base_address + 0x0360);
let lvt_error_register = Self::new_read_write_volatile(base_address + 0x0370);
let initial_count_register = Self::new_read_write_volatile(base_address + 0x0380);
let current_count_register = Self::new_read_only_volatile(base_address + 0x0390);
let divide_configuration_register = Self::new_read_write_volatile(base_address + 0x03E0);
Self {
local_apic_id_register,
local_apic_version_register,
task_priority_register,
arbitration_priority_register,
processor_priority_register,
eoi_register,
remote_read_register,
logical_destination_register,
destination_format_register,
spurious_interrupt_vector_register,
isr_per_32_bits,
tmr_per_32_bits,
irr_per_32_bits,
error_status_register,
lvt_cmci_register,
icr_bits_31_0,
icr_bits_63_32,
lvt_timer_register,
lvt_thermal_sensor_register,
lvt_performance_monitoring_counters_register,
lvt_lint0_register,
lvt_lint1_register,
lvt_error_register,
initial_count_register,
current_count_register,
divide_configuration_register,
}
}
#[inline(always)]
fn new_read_only_volatile(pa: usize) -> Volatile<&'static u32, ReadOnly> {
Volatile::new_read_only(Self::convert_pa_to_u32_ref(pa))
}
#[inline(always)]
fn new_read_write_volatile(pa: usize) -> Volatile<&'static mut u32, ReadWrite> {
Volatile::new(Self::convert_pa_to_u32_ref(pa))
}
#[inline(always)]
fn new_write_only_volatile(pa: usize) -> Volatile<&'static mut u32, WriteOnly> {
Volatile::new_write_only(Self::convert_pa_to_u32_ref(pa))
}
#[inline(always)]
fn convert_pa_to_u32_ref(pa: usize) -> &'static mut u32 {
unsafe { &mut *(crate::mm::address::phys_to_virt(pa) as *mut usize as *mut u32) }
}
}
pub(crate) fn has_apic() -> bool {
let value = unsafe { x86_64_util::cpuid(1) };
value.edx & 0x100 != 0
}
pub(crate) fn init() {
super::pic::disable_temp();
let apic_lock = APIC_INSTANCE.get();
// enable apic
set_apic_base_address(get_apic_base_address());
let spurious = apic_lock.spurious_interrupt_vector_register.read();
apic_lock
.spurious_interrupt_vector_register
.write(spurious | (0x100));
let apic_id = apic_lock.local_apic_id_register.read() >> 24;
let apic_ver = apic_lock.local_apic_version_register.read();
debug!(
"APIC ID:{:x}, Version:{:x}, Max LVT:{:x}",
apic_id,
apic_ver & 0xff,
(apic_ver >> 16) & 0xff
);
debug!(
"LDR:{:x}, DFR:{:x}",
apic_lock.logical_destination_register.read(),
apic_lock.destination_format_register.read()
);
debug!("spurious:{:x}", spurious);
drop(apic_lock);
}
#[inline(always)]
pub fn ack() {
let lock = APIC_INSTANCE.get();
lock.eoi_register.write(0);
}
/// set APIC base address and enable it
fn set_apic_base_address(address: usize) {
x86_64_util::set_msr(
IA32_APIC_BASE_MSR,
address | IA32_APIC_BASE_MSR_ENABLE as usize,
)
}
/// get APIC base address
fn get_apic_base_address() -> usize {
x86_64_util::get_msr(IA32_APIC_BASE_MSR) & 0xf_ffff_f000
}

View File

@ -0,0 +1,153 @@
use acpi::PlatformInfo;
use super::acpi::ACPI_TABLES;
use crate::cell::Cell;
use crate::debug;
use crate::mm::address::phys_to_virt;
use crate::util::recycle_allocator::RecycleAllocator;
use lazy_static::lazy_static;
lazy_static! {
pub static ref IO_APIC: Cell<IoApic> =
unsafe { Cell::new(core::mem::MaybeUninit::zeroed().assume_init()) };
}
const IOAPICID: u32 = 0x00;
const IOAPICVER: u32 = 0x01;
const IOAPICARB: u32 = 0x02;
const fn IoApicRedtbl(index: u8) -> u32 {
0x10 + 2 * index as u32
}
#[derive(Debug)]
#[repr(C)]
struct IoApicRegister {
address: u32,
reserved: [u8; 0x10 - 0x04],
data: u32,
}
impl IoApicRegister {
pub fn read(self: &mut Self, reg: u32) -> u32 {
self.address = reg & 0xff;
self.data
}
pub fn write(self: &mut Self, reg: u32, value: u32) {
self.address = reg & 0xff;
self.data = value;
}
}
#[derive(Debug)]
pub struct IoApicEntryHandle {
index: u8,
}
impl IoApicEntryHandle {
pub fn read(&mut self) -> u64 {
let io_apic = IO_APIC.get();
io_apic.read_irq(self.index)
}
pub fn write(&mut self, value: u64) {
let io_apic = IO_APIC.get();
io_apic.write_irq(self.index, value);
}
pub fn get_index(&self) -> u8 {
self.index
}
}
impl Drop for IoApicEntryHandle {
fn drop(&mut self) {
let io_apic = IO_APIC.get();
// mask
io_apic.write_irq(self.index, 1 << 16);
io_apic.entry_allocator.dealloc(self.index as usize);
}
}
#[derive(Debug)]
pub struct IoApic {
id: u8,
version: u32,
max_redirection_entry: u32,
io_apic_register: &'static mut IoApicRegister,
entry_allocator: RecycleAllocator,
}
impl IoApic {
fn read_irq(&mut self, irq_index: u8) -> u64 {
let low = self.io_apic_register.read(IoApicRedtbl(irq_index)) as u64;
let high = self.io_apic_register.read(IoApicRedtbl(irq_index) + 1) as u64;
high << 32 | low
}
fn write_irq(&mut self, irq_index: u8, value: u64) {
let low = value as u32;
let high = (value >> 32) as u32;
self.io_apic_register.write(IoApicRedtbl(irq_index), low);
self.io_apic_register
.write(IoApicRedtbl(irq_index) + 1, high);
}
pub fn allocate_entry(&mut self) -> Option<IoApicEntryHandle> {
let id = self.entry_allocator.alloc();
if id == usize::MAX {
return None;
}
Some(IoApicEntryHandle { index: id as u8 })
}
}
pub fn init() {
let c = ACPI_TABLES.lock();
let platform_info = PlatformInfo::new(&*c).unwrap();
let mut ioapic_address = 0;
match platform_info.interrupt_model {
acpi::InterruptModel::Unknown => panic!("not found APIC in ACPI Table"),
acpi::InterruptModel::Apic(apic) => {
for io_apic in apic.io_apics.iter() {
ioapic_address = io_apic.address;
}
}
_ => todo!(),
}
if ioapic_address == 0 {
return;
}
let io_apic_register =
unsafe { &mut *(phys_to_virt(ioapic_address as usize) as *mut IoApicRegister) };
let id = (read_io_apic(io_apic_register, IOAPICID) & (0xF00_0000) >> 24) as u8;
let raw_version = read_io_apic(io_apic_register, IOAPICVER);
let version = raw_version & 0x1ff;
let max_redirection_entry = ((raw_version & (0xFF_0000)) >> 16) + 1;
debug!(
"IOAPIC id: {}, version:{}, max_redirection_entry:{}",
id, version, max_redirection_entry
);
let io_apic = IoApic {
id,
version,
max_redirection_entry,
io_apic_register,
entry_allocator: RecycleAllocator::with_start_max(0, max_redirection_entry as usize),
};
*IO_APIC.get() = io_apic;
}
fn read_io_apic(io_apic_register: &mut IoApicRegister, reg: u32) -> u32 {
io_apic_register.address = reg & 0xff;
io_apic_register.data
}
fn write_io_apic(io_apic_register: &mut IoApicRegister, reg: u32, value: u32) {
io_apic_register.address = reg & 0xff;
io_apic_register.data = value;
}

View File

@ -0,0 +1,30 @@
//! Driver for APIC, PIC, PIT etc.
//! This module should inaccessible by other crate such as std, virtio etc.
//!
pub mod acpi;
pub mod apic;
pub mod ioapic;
pub mod pic;
pub mod timer;
pub use apic::ack;
pub use timer::TimerCallback;
pub(crate) use timer::{add_timeout_list, TICK};
use crate::info;
pub(crate) fn init(rsdp: Option<u64>) {
acpi::init(rsdp.unwrap());
timer::init();
if apic::has_apic() {
ioapic::init();
apic::init();
} else {
info!("No apic exists, using pic instead");
unsafe {
pic::enable();
}
}
pic::init();
}

View File

@ -0,0 +1,123 @@
use crate::{info, trap::allocate_target_irq, x86_64_util::out8, IrqAllocateHandle};
use core::sync::atomic::Ordering::Relaxed;
use core::sync::atomic::{AtomicBool, AtomicU8};
const MASTER_CMD: u16 = 0x20;
const MASTER_DATA: u16 = MASTER_CMD + 1;
const SLAVE_CMD: u16 = 0xA0;
const SLAVE_DATA: u16 = SLAVE_CMD + 1;
const IRQ_OFFSET: u8 = 0x20;
const TIMER_IRQ_NUM: u8 = 32;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;
lazy_static! {
/// store the irq, although we have APIC for manage interrupts
/// but something like serial still need pic for register interrupts
static ref IRQ_LOCK : Mutex<Vec<IrqAllocateHandle>> = Mutex::new(Vec::new());
}
static MASK_MASTER: AtomicU8 = AtomicU8::new(0x00);
static MASK_SLAVE: AtomicU8 = AtomicU8::new(0x00);
static CHANGE_LOCK: AtomicBool = AtomicBool::new(false);
/// init the PIC device
pub(crate) fn init() {
if CHANGE_LOCK.load(Relaxed) {
return;
}
let master_mask = !(MASK_MASTER.load(Relaxed));
let slave_mask = !(MASK_SLAVE.load(Relaxed));
info!(
"PIC init, master mask:{:x} slave_mask:{:x}",
master_mask, slave_mask
);
unsafe {
set_mask(master_mask, slave_mask);
}
}
/// allocate irq, for example, if timer need IRQ0, it will return IrqAllocateHandle with irq num: IRQ_OFFSET+0
pub(crate) fn allocate_irq(index: u8) -> Option<IrqAllocateHandle> {
if index >= 16 {
return None;
}
if let Ok(irq) = allocate_target_irq(IRQ_OFFSET + index) {
if index >= 8 {
MASK_SLAVE.fetch_or(1 << (index - 8), Relaxed);
} else {
MASK_MASTER.fetch_or(1 << (index), Relaxed);
}
Some(irq)
} else {
None
}
}
/// enable the PIC device, this function will permanent enable all the interrupts
#[inline]
pub(crate) unsafe fn enable() {
CHANGE_LOCK.store(true, Relaxed);
set_mask(0, 0);
}
/// disable the PIC device, this function will permanent disable all the interrupts
/// the interrupts mask may not exists after calling init function
#[inline]
pub(crate) unsafe fn disable() {
CHANGE_LOCK.store(true, Relaxed);
set_mask(0xFF, 0xFF);
}
/// enable the PIC device, this function will allow all the interrupts
/// the interrupts mask may not exists after calling init function
#[inline]
pub(crate) fn enable_temp() {
unsafe {
set_mask(0, 0);
}
}
/// disable the PIC device, this function will disable all the interrupts
/// the interrupts mask may not exists after calling init function
#[inline]
pub(crate) fn disable_temp() {
unsafe {
set_mask(0xFF, 0xFF);
}
}
#[inline(always)]
pub(crate) unsafe fn set_mask(master_mask: u8, slave_mask: u8) {
// Start initialization
out8(MASTER_CMD, 0x11);
out8(SLAVE_CMD, 0x11);
// Set offsets
// map master PIC vector 0x00~0x07 to 0x20~0x27 IRQ number
out8(MASTER_DATA, IRQ_OFFSET);
// map slave PIC vector 0x00~0x07 to 0x28~0x2f IRQ number
out8(SLAVE_DATA, IRQ_OFFSET + 0x08);
// Set up cascade, there is slave at IRQ2
out8(MASTER_DATA, 4);
out8(SLAVE_DATA, 2);
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
out8(MASTER_DATA, 1);
out8(SLAVE_DATA, 1);
// mask interrupts
out8(MASTER_DATA, master_mask);
out8(SLAVE_DATA, slave_mask);
}
#[inline(always)]
pub(crate) fn ack() {
out8(MASTER_CMD, 0x20);
}

View File

@ -0,0 +1,70 @@
use crate::{
config,
driver::{apic::APIC_INSTANCE, pic, timer},
info, x86_64_util, TrapFrame,
};
pub fn init() {
let apic_lock = APIC_INSTANCE.get();
let handle = unsafe { crate::trap::IrqLine::acquire(timer::TIMER_IRQ_NUM) };
let a = handle.on_active(init_function);
// divide by 64
apic_lock.divide_configuration_register.write(0b1001);
apic_lock.initial_count_register.write(0xFFFF_FFFF);
// apic_lock.lvt_timer_register.write(timer::TIMER_IRQ_NUM as u32);
drop(apic_lock);
// init pic for now, disable it after the APIC Timer init is done
pic::enable_temp();
timer::pit::init();
static mut IS_FINISH: bool = false;
// wait until it is finish
x86_64_util::enable_interrupts();
unsafe {
while !IS_FINISH {
x86_64_util::hlt();
}
}
x86_64_util::disable_interrupts();
drop(a);
drop(handle);
fn init_function(trap_frame: &TrapFrame) {
static mut IN_TIME: u8 = 0;
static mut FIRST_TIME_COUNT: u32 = 0;
unsafe {
if IS_FINISH || IN_TIME == 0 {
// drop the first entry, since it may not be the time we want
IN_TIME += 1;
let apic_lock = APIC_INSTANCE.get();
let remain_ticks = apic_lock.current_count_register.read();
FIRST_TIME_COUNT = 0xFFFF_FFFF - remain_ticks;
pic::ack();
return;
}
}
pic::disable_temp();
// stop APIC Timer, get the number of tick we need
let apic_lock = APIC_INSTANCE.get();
let remain_ticks = apic_lock.current_count_register.read();
apic_lock.initial_count_register.write(0);
let ticks = unsafe { 0xFFFF_FFFF - remain_ticks - FIRST_TIME_COUNT };
// periodic mode, divide 64, freq: TIMER_FREQ Hz
apic_lock.initial_count_register.write(ticks as u32);
apic_lock
.lvt_timer_register
.write(timer::TIMER_IRQ_NUM as u32 | (1 << 17));
apic_lock.divide_configuration_register.write(0b1001);
info!(
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
ticks,
remain_ticks,
config::TIMER_FREQ
);
unsafe {
IS_FINISH = true;
}
}
}

View File

@ -0,0 +1,126 @@
use acpi::{AcpiError, HpetInfo};
use alloc::vec::Vec;
use volatile::{
access::{ReadOnly, ReadWrite},
Volatile,
};
use crate::{
cell::Cell,
driver::{
acpi::ACPI_TABLES,
ioapic::{self, IoApicEntryHandle},
},
};
use lazy_static::lazy_static;
lazy_static! {
static ref HPET_INSTANCE: Cell<HPET> =
unsafe { Cell::new(core::mem::MaybeUninit::zeroed().assume_init()) };
}
const OFFSET_ID_REGISTER: usize = 0x000;
const OFFSET_CONFIGURATION_REGISTER: usize = 0x010;
const OFFSET_INTERRUPT_STATUS_REGISTER: usize = 0x020;
const OFFSET_MAIN_COUNTER_VALUE_REGISTER: usize = 0x0F0;
const HPET_FREQ: usize = 1_000_000_000_000_000;
#[derive(Debug)]
#[repr(C)]
struct HPETTimerRegister {
configuration_and_capabilities_register: u32,
timer_compartor_value_register: u32,
fsb_interrupt_route_register: u32,
}
struct HPET {
io_apic_entry: IoApicEntryHandle,
information_register: Volatile<&'static u32, ReadOnly>,
general_configuration_register: Volatile<&'static mut u32, ReadWrite>,
general_interrupt_status_register: Volatile<&'static mut u32, ReadWrite>,
timer_registers: Vec<Volatile<&'static mut HPETTimerRegister, ReadWrite>>,
}
impl HPET {
fn new(base_address: usize) -> HPET {
let information_register_ref = unsafe {
&*(crate::mm::address::phys_to_virt(base_address + OFFSET_ID_REGISTER) as *mut usize
as *mut u32)
};
let general_configuration_register_ref = unsafe {
&mut *(crate::mm::address::phys_to_virt(base_address + OFFSET_CONFIGURATION_REGISTER)
as *mut usize as *mut u32)
};
let general_interrupt_status_register_ref = unsafe {
&mut *(crate::mm::address::phys_to_virt(base_address + OFFSET_INTERRUPT_STATUS_REGISTER)
as *mut usize as *mut u32)
};
let information_register = Volatile::new_read_only(information_register_ref);
let general_configuration_register = Volatile::new(general_configuration_register_ref);
let general_interrupt_status_register =
Volatile::new(general_interrupt_status_register_ref);
let num_comparator = ((information_register.read() & 0x1F00) >> 8) as u8 + 1;
let mut comparators = Vec::with_capacity(num_comparator as usize);
for i in 0..num_comparator {
let comp = Volatile::new(unsafe {
&mut *(crate::mm::address::phys_to_virt(base_address + 0x100 + i as usize * 0x20)
as *mut usize as *mut HPETTimerRegister)
});
comparators.push(comp);
}
let mut io_apic_entry = ioapic::IO_APIC.get().allocate_entry().unwrap();
let vector = super::TIMER_IRQ_NUM;
// 0 for now
let destination_apic_id: u8 = 0;
let write_value = (destination_apic_id as u64) << 56 | vector as u64;
io_apic_entry.write(write_value);
HPET {
io_apic_entry,
information_register,
general_configuration_register,
general_interrupt_status_register,
timer_registers: comparators,
}
}
pub fn hardware_rev(&self) -> u8 {
(self.information_register.read() & 0xFF) as u8
}
pub fn num_comparators(&self) -> u8 {
((self.information_register.read() & 0x1F00) >> 8) as u8 + 1
}
pub fn main_counter_is_64bits(&self) -> bool {
(self.information_register.read() & 0x2000) != 0
}
pub fn legacy_irq_capable(&self) -> bool {
(self.information_register.read() & 0x8000) != 0
}
pub fn pci_vendor_id(&self) -> u16 {
((self.information_register.read() & 0xFFFF_0000) >> 16) as u16
}
}
/// HPET init, need to init IOAPIC before init this function
pub fn init() -> Result<(), AcpiError> {
let c = ACPI_TABLES.lock();
let hpet_info = HpetInfo::new(&*c)?;
// config IO APIC entry
let hpet = HPET::new(hpet_info.base_address);
*HPET_INSTANCE.get() = hpet;
Ok(())
}

View File

@ -1,27 +1,16 @@
use crate::cell::Cell;
use crate::x86_64_util::out8;
use crate::{IrqAllocateHandle, TrapFrame};
use alloc::sync::Arc;
use alloc::vec::Vec;
use alloc::{boxed::Box, collections::BinaryHeap};
pub mod apic;
pub mod hpet;
pub mod pit;
use core::any::Any;
use alloc::{boxed::Box, collections::BinaryHeap, sync::Arc, vec::Vec};
use lazy_static::lazy_static;
use spin::Mutex;
const MASTER_CMD: u16 = 0x20;
const MASTER_DATA: u16 = MASTER_CMD + 1;
const SLAVE_CMD: u16 = 0xA0;
const SLAVE_DATA: u16 = SLAVE_CMD + 1;
const TIMER_RATE: u32 = 1193182;
/// This value represent the base timer frequency in Hz
pub const TIMER_FREQ: u64 = 100;
const TIMER_PERIOD_IO_PORT: u16 = 0x40;
const TIMER_MODE_IO_PORT: u16 = 0x43;
const TIMER_SQUARE_WAVE: u8 = 0x36;
const TIMER_IRQ_NUM: u8 = 32;
use crate::{cell::Cell, IrqAllocateHandle, TrapFrame};
pub(crate) const TIMER_IRQ_NUM: u8 = 32;
pub static mut TICK: u64 = 0;
lazy_static! {
@ -31,49 +20,16 @@ lazy_static! {
}
pub fn init() {
// Start initialization
out8(MASTER_CMD, 0x11);
out8(SLAVE_CMD, 0x11);
if super::apic::has_apic() {
apic::init();
} else {
pit::init();
}
// Set offsets
// map master PIC vector 0x00~0x07 to 0x20~0x27 IRQ number
out8(MASTER_DATA, 0x20);
// map slave PIC vector 0x00~0x07 to 0x28~0x2f IRQ number
out8(SLAVE_DATA, 0x28);
// Set up cascade, there is slave at IRQ2
out8(MASTER_DATA, 4);
out8(SLAVE_DATA, 2);
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
out8(MASTER_DATA, 1);
out8(SLAVE_DATA, 1);
// Unmask timer interrupt
out8(MASTER_DATA, 0xFE);
out8(SLAVE_DATA, 0xFF);
// Ack remaining interrupts
out8(MASTER_CMD, 0x20);
out8(SLAVE_CMD, 0x20);
// Initialize timer.
let cycle = TIMER_RATE / TIMER_FREQ as u32; // 1ms per interrupt.
out8(TIMER_MODE_IO_PORT, TIMER_SQUARE_WAVE);
out8(TIMER_PERIOD_IO_PORT, (cycle & 0xFF) as _);
out8(TIMER_PERIOD_IO_PORT, (cycle >> 8) as _);
TIMER_IRQ.lock().on_active(timer_callback);
}
#[inline(always)]
fn ack() {
out8(MASTER_CMD, 0x20);
}
fn timer_callback(trap_frame: &TrapFrame) {
// FIXME: disable and enable interupt will cause infinity loop
// x86_64_util::disable_interrupts();
ack();
let current_ms;
unsafe {
current_ms = TICK;
@ -82,18 +38,16 @@ fn timer_callback(trap_frame: &TrapFrame) {
let timeout_list = TIMEOUT_LIST.get();
let mut callbacks: Vec<Arc<TimerCallback>> = Vec::new();
while let Some(t) = timeout_list.peek() {
if t.expire_ms <= current_ms {
if t.expire_ms <= current_ms && t.is_enable() {
callbacks.push(timeout_list.pop().unwrap());
} else {
break;
}
}
for callback in callbacks {
if callback.is_enable() {
callback.callback.call((&callback,));
}
callback.callback.call((&callback,));
}
// x86_64_util::enable_interrupts();
// crate::interrupt_ack();
}
lazy_static! {

View File

@ -0,0 +1,17 @@
//! used for PIT Timer
use crate::{config::TIMER_FREQ, x86_64_util::out8};
const TIMER_RATE: u32 = 1193182;
const TIMER_PERIOD_IO_PORT: u16 = 0x40;
const TIMER_MODE_IO_PORT: u16 = 0x43;
const TIMER_SQUARE_WAVE: u8 = 0x36;
pub(crate) fn init() {
// Initialize timer.
let cycle = TIMER_RATE / TIMER_FREQ as u32;
out8(TIMER_MODE_IO_PORT, TIMER_SQUARE_WAVE);
out8(TIMER_PERIOD_IO_PORT, (cycle & 0xFF) as _);
out8(TIMER_PERIOD_IO_PORT, (cycle >> 8) as _);
}

View File

@ -16,6 +16,7 @@ pub(crate) mod cell;
pub mod config;
pub mod cpu;
pub mod device;
mod driver;
mod error;
pub mod log;
pub(crate) mod mm;
@ -30,39 +31,48 @@ pub mod vm;
pub(crate) mod x86_64_util;
use core::{mem, panic::PanicInfo};
pub use driver::ack as apic_ack;
pub use self::error::Error;
pub use self::prelude::Result;
pub(crate) use self::sync::up::UPSafeCell;
pub use trap::interrupt_ack;
pub use x86_64_util::{disable_interrupts, enable_interrupts, hlt};
use alloc::vec::Vec;
use bootloader::{
boot_info::{FrameBuffer, MemoryRegionKind},
BootInfo,
};
pub use device::serial::receive_char;
pub use device::console::receive_char;
pub use mm::address::{align_down, align_up, is_aligned, virt_to_phys};
pub use mm::page_table::translate_not_offset_virtual_address;
pub use trap::{allocate_irq, IrqAllocateHandle, TrapFrame};
use trap::{IrqCallbackHandle, IrqLine};
pub use util::AlignExt;
pub use x86_64::registers::rflags::read as get_rflags;
pub use x86_64::registers::rflags::RFlags;
use x86_64_util::enable_common_cpu_features;
static mut IRQ_CALLBACK_LIST: Vec<IrqCallbackHandle> = Vec::new();
// TODO: serial中断
// 讨论syscall的中断启用
#[cfg(not(feature = "serial_print"))]
pub use crate::screen_print as print;
#[cfg(not(feature = "serial_print"))]
pub use crate::screen_println as println;
#[cfg(feature = "serial_print")]
pub use crate::serial_print as print;
pub use crate::console_print as print;
#[cfg(feature = "serial_print")]
pub use crate::serial_println as println;
pub use crate::console_println as println;
pub fn init(boot_info: &'static mut BootInfo) {
let siz = boot_info.framebuffer.as_ref().unwrap() as *const FrameBuffer as usize;
let mut memory_init = false;
// memory
device::first_init(boot_info.framebuffer.as_mut().unwrap());
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
for region in boot_info.memory_regions.iter() {
if region.kind == MemoryRegionKind::Usable {
let start: u64 = region.start;
@ -79,25 +89,25 @@ pub fn init(boot_info: &'static mut BootInfo) {
if !memory_init {
panic!("memory init failed");
}
device::init(boot_info.framebuffer.as_mut().unwrap());
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
trap::init();
device::second_init();
driver::init(boot_info.rsdp_addr.into_option());
enable_common_cpu_features();
unsafe {
for i in 0..256 {
IRQ_CALLBACK_LIST.push(IrqLine::acquire(i as u8).on_active(general_handler))
}
let value = x86_64_util::cpuid(1);
}
// uncomment below code to enable timer interrupt
// x86_64_util::enable_interrupts_and_hlt();
}
fn general_handler(trap_frame: &TrapFrame) {
println!("{:#x?}", trap_frame);
println!("rip = 0x{:x}", trap_frame.rip);
println!("rsp = 0x{:x}", trap_frame.rsp);
println!("cr2 = 0x{:x}", trap_frame.cr2);
// println!("rbx = 0x{:x}", trap_frame.)
panic!("couldn't handler trap right now");
// info!("general handler");
// println!("{:#x?}", trap_frame);
// println!("rip = 0x{:x}", trap_frame.rip);
// println!("rsp = 0x{:x}", trap_frame.rsp);
// println!("cr2 = 0x{:x}", trap_frame.cr2);
// // println!("rbx = 0x{:x}", trap_frame.)
// panic!("couldn't handler trap right now");
}
#[inline(always)]
@ -114,14 +124,14 @@ where
T: Fn(),
{
fn run(&self) {
serial_print!("{}...\n", core::any::type_name::<T>());
console_print!("{}...\n", core::any::type_name::<T>());
self();
serial_println!("[ok]");
console_println!("[ok]");
}
}
pub fn test_runner(tests: &[&dyn Testable]) {
serial_println!("Running {} tests", tests.len());
console_println!("Running {} tests", tests.len());
for test in tests {
test.run();
}
@ -129,11 +139,33 @@ pub fn test_runner(tests: &[&dyn Testable]) {
}
pub fn test_panic_handler(info: &PanicInfo) -> ! {
serial_println!("[failed]");
serial_println!("Error: {}", info);
console_println!("[failed]");
console_println!("Error: {}", info);
exit_qemu(QemuExitCode::Failed);
}
pub fn panic_handler() {
println!("[panic]: cr3:{:x}", x86_64_util::get_cr3());
// let mut fp: usize;
// let stop = unsafe{
// Task::current().kstack.get_top()
// };
// info!("stop:{:x}",stop);
// unsafe{
// asm!("mov rbp, {}", out(reg) fp);
// info!("fp:{:x}",fp);
// println!("---START BACKTRACE---");
// for i in 0..10 {
// if fp == stop {
// break;
// }
// println!("#{}:ra={:#x}", i, *((fp - 8) as *const usize));
// info!("fp target:{:x}",*((fp ) as *const usize));
// fp = *((fp - 16) as *const usize);
// }
// println!("---END BACKTRACE---");
// }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {

View File

@ -23,15 +23,7 @@ pub fn log_print(args: Arguments) {
#[cfg(feature = "serial_print")]
#[doc(hidden)]
pub fn log_print(args: Arguments) {
use crate::device::serial::SERIAL;
use core::fmt::Write;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
SERIAL
.lock()
.write_fmt(args)
.expect("Printing to serial failed");
});
crate::device::console::print(args);
}
/// This macro should not be directly called.

View File

@ -4,13 +4,19 @@ pub mod address;
mod frame_allocator;
mod heap_allocator;
mod memory_set;
mod page_table;
pub(crate) mod page_table;
use core::sync::atomic::AtomicUsize;
use address::PhysAddr;
use address::VirtAddr;
use crate::x86_64_util;
pub use self::{frame_allocator::*, memory_set::*, page_table::*};
pub(crate) static ORIGINAL_CR3: AtomicUsize = AtomicUsize::new(0);
bitflags::bitflags! {
/// Possible flags for a page table entry.
pub struct PTFlags: usize {
@ -35,4 +41,8 @@ pub(crate) fn init(start: u64, size: u64) {
heap_allocator::init();
frame_allocator::init(start as usize, size as usize);
page_table::init();
ORIGINAL_CR3.store(
x86_64_util::get_cr3_raw(),
core::sync::atomic::Ordering::Relaxed,
)
}

View File

@ -192,6 +192,56 @@ fn next_table_or_create<'a>(
}
}
/// translate a virtual address to physical address which cannot use offset to get physical address
/// Note: this may not useful for accessing usermode data, use offset first
pub fn translate_not_offset_virtual_address(va: usize) -> usize {
let cr3 = x86_64_util::get_cr3();
let p4 = table_of(PhysAddr(cr3));
let a = VirtAddr(va);
let pte = p4[p4_index(a)];
let p3 = table_of(pte.pa());
let pte = p3[p3_index(a)];
let p2 = table_of(pte.pa());
let pte = p2[p2_index(a)];
let p1 = table_of(pte.pa());
let pte = p1[p1_index(a)];
(pte.pa().0 & ((1 << 48) - 1)) + (va & ((1 << 12) - 1))
}
pub fn print_virtual_address_translate_information(va: usize) {
let cr3 = x86_64_util::get_cr3();
let p4 = table_of(PhysAddr(cr3));
let a = VirtAddr(va);
info!("p4 index:{:x}", p4_index(a));
let pte = p4[p4_index(a)];
info!("p4 pte:{:x}", pte.0);
let p3 = table_of(pte.pa());
info!("p3 index:{:x}", p3_index(a));
let pte = p3[p3_index(a)];
info!("p3 pte:{:x}", pte.0);
let p2 = table_of(pte.pa());
info!("p2 index:{:x}", p2_index(a));
let pte = p2[p2_index(a)];
info!("p2 pte:{:x}", pte.0);
let p1 = table_of(pte.pa());
info!("p1 index:{:x}", p1_index(a));
let pte = p1[p1_index(a)];
info!("p1 pte:{:x}", pte.0);
}
pub(crate) fn init() {
let cr3 = x86_64_util::get_cr3();
@ -201,7 +251,7 @@ pub(crate) fn init() {
// there is mapping where index is 1,2,3, so user may not use these value
let mut map_pte = ALL_MAPPED_PTE.exclusive_access();
for i in 0..512 {
if !p4[i].flags().is_empty() {
if p4[i].flags().contains(PTFlags::PRESENT) {
map_pte.insert(i, p4[i]);
}
}

View File

@ -1,7 +1,8 @@
//! Timer.
use crate::{
device::{TimerCallback, TICK, TIMER_FREQ},
config::TIMER_FREQ,
driver::{TimerCallback, TICK},
prelude::*,
};
use core::time::Duration;
@ -62,17 +63,13 @@ impl Timer {
}
None => {}
}
let tick_count = timeout.as_secs() * TIMER_FREQ
+ if timeout.subsec_nanos() != 0 {
(timeout.subsec_nanos() as u64 - 1) / NANOS_DIVIDE + 1
} else {
0
};
let tick_count =
timeout.as_secs() * TIMER_FREQ + timeout.subsec_nanos() as u64 / NANOS_DIVIDE;
unsafe {
lock.start_tick = TICK;
lock.timeout_tick = TICK + tick_count;
}
lock.timer_callback = Some(crate::device::add_timeout_list(
lock.timer_callback = Some(crate::driver::add_timeout_list(
tick_count,
self.clone(),
timer_callback,

View File

@ -33,23 +33,21 @@ pub(crate) extern "C" fn trap_handler(f: &mut TrapFrame) {
&Task::current().inner_ctx() as *const TaskContext,
)
}
} else {
let irq_line = IRQ_LIST.get(f.id as usize).unwrap();
let callback_functions = irq_line.callback_list();
for callback_function in callback_functions.iter() {
callback_function.call(f);
}
}
} else {
if is_cpu_fault(f) {
panic!("cannot handle kernel cpu fault now");
}
let irq_line = IRQ_LIST.get(f.id as usize).unwrap();
let callback_functions = irq_line.callback_list();
for callback_function in callback_functions.iter() {
callback_function.call(f);
panic!("cannot handle kernel cpu fault now, information:{:#x?}", f);
}
}
let irq_line = IRQ_LIST.get(f.id as usize).unwrap();
let callback_functions = irq_line.callback_list();
for callback_function in callback_functions.iter() {
callback_function.call(f);
}
if f.id >= 0x20 {
crate::driver::apic::ack();
crate::driver::pic::ack();
}
}
fn is_from_kernel(cs: u64) -> bool {

View File

@ -120,6 +120,15 @@ struct IDT {
entries: [[usize; 2]; 256],
}
#[inline]
pub fn interrupt_ack() {
if crate::driver::apic::has_apic() {
crate::driver::apic::ack();
} else {
crate::driver::pic::ack();
}
}
impl IDT {
const fn default() -> Self {
Self {

View File

@ -2,6 +2,7 @@
use crate::debug;
use crate::x86_64_util::{rdfsbase, wrfsbase};
use x86_64::registers::rflags::RFlags;
use crate::cpu::CpuContext;
use crate::prelude::*;
@ -116,6 +117,10 @@ impl<'a> UserMode<'a> {
}
if !self.executed {
*self.current.syscall_frame() = self.user_space.cpu_ctx.into();
if self.context.gp_regs.rflag == 0 {
self.context.gp_regs.rflag = (RFlags::INTERRUPT_FLAG | RFlags::ID).bits();
}
self.current.syscall_frame().caller.r11 = self.context.gp_regs.rflag;
self.current.syscall_frame().caller.rcx = self.user_space.cpu_ctx.gp_regs.rip;
// write fsbase
wrfsbase(self.user_space.cpu_ctx.fs_base);
@ -129,6 +134,8 @@ impl<'a> UserMode<'a> {
*self.current.trap_frame() = self.context.into();
} else {
*self.current.syscall_frame() = self.context.into();
self.context.gp_regs.rflag |= RFlags::INTERRUPT_FLAG.bits();
self.current.syscall_frame().caller.r11 = self.context.gp_regs.rflag;
self.current.syscall_frame().caller.rcx = self.context.gp_regs.rip;
}

View File

@ -1,5 +1,6 @@
use alloc::vec::Vec;
#[derive(Debug)]
pub struct RecycleAllocator {
current: usize,
recycled: Vec<usize>,

View File

@ -1,5 +1,5 @@
//! util for x86_64, it will rename to x86_64 when depend x86_64 isn't necessary
use core::arch::asm;
use core::arch::{asm, x86_64::CpuidResult};
use x86_64::registers::{control::Cr4Flags, segmentation::Segment64, xcontrol::XCr0Flags};
@ -61,6 +61,18 @@ pub fn out32(port: u16, val: u32) {
}
}
#[inline(always)]
pub unsafe fn cpuid(leaf: u32) -> CpuidResult {
core::arch::x86_64::__cpuid(leaf)
}
#[inline(always)]
pub fn hlt() {
unsafe {
asm!("hlt", options(nomem, nostack));
}
}
#[inline(always)]
pub fn disable_interrupts() {
unsafe {

View File

@ -13,6 +13,8 @@ const COMMON_ARGS: &[&str] = &[
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-device",
"virtio-blk-pci,bus=pci.0,addr=0x6,drive=x0",
"-device",
"virtio-keyboard-pci",
"-serial",
"stdio",
"-display",

View File

@ -7,7 +7,6 @@ pub mod msix;
pub mod util;
extern crate alloc;
use jinux_frame::info;
#[macro_use]
extern crate pod_derive;
use alloc::{sync::Arc, vec::Vec};
@ -39,7 +38,7 @@ pub fn init() {
let mut devices = PCI_DEVICES.lock();
for dev in util::scan_bus(CSpaceAccessMethod::IO) {
info!(
"pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) irq: {}:{:?}",
"pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) command: {:?} status: {:?} irq: {}:{:?}",
dev.loc.bus,
dev.loc.device,
dev.loc.function,
@ -47,6 +46,8 @@ pub fn init() {
dev.id.device_id,
dev.id.class,
dev.id.subclass,
dev.command,
dev.status,
dev.pic_interrupt_line,
dev.interrupt_pin
);

View File

@ -1,10 +1,11 @@
use alloc::vec::Vec;
use pod_derive::Pod;
use crate::util::{CSpaceAccessMethod, Location, BAR};
use super::capability::msix::CapabilityMSIXData;
use jinux_frame::{offset_of, IrqAllocateHandle};
use jinux_frame::{debug, offset_of, IrqAllocateHandle};
use jinux_util::frame_ptr::InFramePtr;
#[derive(Debug, Default)]
@ -40,7 +41,7 @@ impl MSIX {
) -> Self {
let table_info = cap.table_info;
let pba_info = cap.pba_info;
let table_size = table_info & (0b11_1111_1111);
let table_size = (table_info & (0b11_1111_1111)) + 1;
let table_bar_address;
let pba_bar_address;
match bars[(pba_info & 0b111) as usize].expect("MSIX cfg:table bar is none") {
@ -59,17 +60,35 @@ impl MSIX {
panic!("MSIX cfg:table bar is IO type")
}
}
// let pba_base_address = (pba_info >> 3 ) as u64 + pba_bar_address;
// let table_base_address = (table_info >>3 ) as u64 + table_bar_address;
let pba_base_address = (pba_info & (!(0b111 as u32))) as u64 + pba_bar_address;
let table_base_address = (table_info & (!(0b111 as u32))) as u64 + table_bar_address;
debug!("MSIX table size:{}, pba_info:{:x}, table_info:{:x}, pba_address:{:x}, table_address:{:x}",
table_size,pba_info,table_info,pba_base_address,table_base_address);
let mut cap = Self {
table_size: table_size as u16,
table: Vec::new(),
pba_paddr: (pba_info / 8) as u64 + pba_bar_address,
pba_paddr: pba_base_address,
};
let mut table_paddr = (table_info / 8) as u64 + table_bar_address;
for _ in 0..table_size {
// enable MSI-X disable INTx
let am = CSpaceAccessMethod::IO;
debug!("command before:{:x}", am.read16(loc, crate::PCI_COMMAND));
am.write16(
loc,
crate::PCI_COMMAND,
am.read16(loc, crate::PCI_COMMAND) | 0x40f,
);
debug!("command after:{:x}", am.read16(loc, crate::PCI_COMMAND));
let message_control = am.read16(loc, cap_ptr + 2) | 0x8000;
am.write16(loc, cap_ptr + 2, message_control);
for i in 0..table_size {
let value: InFramePtr<MSIXTableEntry> =
InFramePtr::new(table_paddr as usize).expect("can not get in frame ptr for msix");
// let mut value = MSIXTableEntry::default();
InFramePtr::new(table_base_address as usize + 16 * i as usize)
.expect("can not get in frame ptr for msix");
// local APIC address: 0xFEE0_0000
value.write_at(offset_of!(MSIXTableEntry, msg_addr), 0xFEE0_0000 as u32);
value.write_at(offset_of!(MSIXTableEntry, msg_upper_addr), 0 as u32);
// allocate irq number
let handle = jinux_frame::allocate_irq().expect("not enough irq");
value.write_at(offset_of!(MSIXTableEntry, msg_data), handle.num() as u32);
@ -78,11 +97,7 @@ impl MSIX {
table_entry: value,
irq_handle: handle,
});
table_paddr += 16;
}
// enable MSI-X
let am = CSpaceAccessMethod::IO;
am.write8(loc, cap_ptr, 0b1000_0000);
cap
}
}

View File

@ -3,6 +3,34 @@ use crate::capability::Capability;
use alloc::vec::Vec;
use bitflags::bitflags;
pub enum PCIDeviceCommonCfgOffset {
VendorId = 0x00,
DeviceId = 0x02,
Command = 0x04,
Status = 0x06,
RevisionId = 0x08,
ClassCode = 0x09,
CacheLineSize = 0x0C,
LatencyTimer = 0x0D,
HeaderType = 0x0E,
BIST = 0x0F,
BAR0 = 0x10,
BAR1 = 0x14,
BAR2 = 0x18,
BAR3 = 0x1C,
BAR4 = 0x20,
BAR5 = 0x24,
CardbusCisPtr = 0x28,
SubsystemVendorId = 0x2C,
SubsystemId = 0x2E,
XROMBAR = 0x30,
CapabilitiesPointer = 0x34,
InterruptLine = 0x3C,
InterruptPin = 0x3D,
MinGrant = 0x3E,
MaxLatency = 0x3F,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CSpaceAccessMethod {
// The legacy, deprecated (as of PCI 2.0) IO-range method.
@ -48,18 +76,26 @@ impl CSpaceAccessMethod {
}
pub fn write8(self, loc: Location, offset: u16, val: u8) {
let old = self.read32(loc, offset);
let old = self.read32(loc, offset & (0xFFFC));
let dest = offset as usize & 0b11 << 3;
let mask = (0xFF << dest) as u32;
self.write32(loc, offset, ((val as u32) << dest | (old & !mask)).to_le());
self.write32(
loc,
offset & (0xFFFC),
((val as u32) << dest | (old & !mask)).to_le(),
);
}
/// Converts val to little endian before writing.
pub fn write16(self, loc: Location, offset: u16, val: u16) {
let old = self.read32(loc, offset);
let old = self.read32(loc, offset & (0xFFFC));
let dest = offset as usize & 0b10 << 3;
let mask = (0xFFFF << dest) as u32;
self.write32(loc, offset, ((val as u32) << dest | (old & !mask)).to_le());
self.write32(
loc,
offset & (0xFFFC),
((val as u32) << dest | (old & !mask)).to_le(),
);
}
/// Takes a value in native endian, converts it to little-endian, and writes it to the PCI
@ -73,7 +109,7 @@ impl CSpaceAccessMethod {
CSpaceAccessMethod::IO => {
jinux_frame::device::pci::PCI_ADDRESS_PORT
.write_u32(loc.encode() | (offset as u32 & 0b11111100));
jinux_frame::device::pci::PCI_ADDRESS_PORT.write_u32(val.to_le())
jinux_frame::device::pci::PCI_DATA_PORT.write_u32(val.to_le())
} //MemoryMapped(ptr) => {
// // FIXME: Clarify whether the rules for GEP/GEPi forbid using regular .offset() here.
// ::core::intrinsics::volatile_load(::core::intrinsics::arith_offset(ptr, offset as usize))
@ -112,64 +148,64 @@ pub struct Identifier {
bitflags! {
pub struct Command: u16 {
const IO_SPACE = 0x0001;
const MEMORY_SPACE = 0x0002;
const BUS_MASTER = 0x0004;
const SPECIAL_CYCLES = 0x0008;
const MWI_ENABLE = 0x0010;
const VGA_PALETTE_SNOOP = 0x0020;
const PARITY_ERROR_RESPONSE = 0x0040;
const STEPPING_CONTROL = 0x0080;
const SERR_ENABLE = 0x0100;
const FAST_BACK_TO_BACK_ENABLE = 0x0200;
const INTERRUPT_DISABLE = 0x0400;
const RESERVED_11 = 0x0800;
const RESERVED_12 = 0x1000;
const RESERVED_13 = 0x2000;
const RESERVED_14 = 0x4000;
const RESERVED_15 = 0x8000;
const IO_SPACE = 1 << 0;
const MEMORY_SPACE = 1 << 1;
const BUS_MASTER = 1 << 2;
const SPECIAL_CYCLES = 1 << 3;
const MWI_ENABLE = 1 << 4;
const VGA_PALETTE_SNOOP = 1 << 5;
const PARITY_ERROR_RESPONSE = 1 << 6;
const STEPPING_CONTROL = 1 << 7;
const SERR_ENABLE = 1 << 8;
const FAST_BACK_TO_BACK_ENABLE = 1 << 9;
const INTERRUPT_DISABLE = 1 << 10;
const RESERVED_11 = 1 << 11;
const RESERVED_12 = 1 << 12;
const RESERVED_13 = 1 << 13;
const RESERVED_14 = 1 << 14;
const RESERVED_15 = 1 << 15;
}
}
bitflags! {
pub struct Status: u16 {
const RESERVED_0 = 0x0001;
const RESERVED_1 = 0x0002;
const RESERVED_2 = 0x0004;
const INTERRUPT_STATUS = 0x0008;
const CAPABILITIES_LIST = 0x0010;
const MHZ66_CAPABLE = 0x0020;
const RESERVED_6 = 0x0040;
const FAST_BACK_TO_BACK_CAPABLE = 0x0080;
const MASTER_DATA_PARITY_ERROR = 0x0100;
const DEVSEL_MEDIUM_TIMING = 0x0200;
const DEVSEL_SLOW_TIMING = 0x0400;
const SIGNALED_TARGET_ABORT = 0x0800;
const RECEIVED_TARGET_ABORT = 0x1000;
const RECEIVED_MASTER_ABORT = 0x2000;
const SIGNALED_SYSTEM_ERROR = 0x4000;
const DETECTED_PARITY_ERROR = 0x8000;
const RESERVED_0 = 1 << 0;
const RESERVED_1 = 1 << 1;
const RESERVED_2 = 1 << 2;
const INTERRUPT_STATUS = 1 << 3;
const CAPABILITIES_LIST = 1 << 4;
const MHZ66_CAPABLE = 1 << 5;
const RESERVED_6 = 1 << 6;
const FAST_BACK_TO_BACK_CAPABLE = 1 << 7;
const MASTER_DATA_PARITY_ERROR = 1 << 8;
const DEVSEL_MEDIUM_TIMING = 1 << 9;
const DEVSEL_SLOW_TIMING = 1 << 10;
const SIGNALED_TARGET_ABORT = 1 << 11;
const RECEIVED_TARGET_ABORT = 1 << 12;
const RECEIVED_MASTER_ABORT = 1 << 13;
const SIGNALED_SYSTEM_ERROR = 1 << 14;
const DETECTED_PARITY_ERROR = 1 << 15;
}
}
bitflags! {
pub struct BridgeControl: u16 {
const PARITY_ERROR_RESPONSE_ENABLE = 0x0001;
const SERR_ENABLE = 0x0002;
const ISA_ENABLE = 0x0004;
const VGA_ENABLE = 0x0008;
const RESERVED_4 = 0x0010;
const MASTER_ABORT_MODE = 0x0020;
const SECONDARY_BUS_RESET = 0x0040;
const FAST_BACK_TO_BACK_ENABLE = 0x0080;
const PRIMARY_DISCARD_TIMER = 0x0100;
const SECONDARY_DISCARD_TIMER = 0x0200;
const DISCARD_TIMER_STATUS = 0x0400;
const DISCARD_TIMER_SERR_ENABLED = 0x0800;
const RESERVED_12 = 0x1000;
const RESERVED_13 = 0x2000;
const RESERVED_14 = 0x4000;
const RESERVED_15 = 0x8000;
const PARITY_ERROR_RESPONSE_ENABLE = 1 << 0;
const SERR_ENABLE = 1 << 1;
const ISA_ENABLE = 1 << 2;
const VGA_ENABLE = 1 << 3;
const RESERVED_4 = 1 << 4;
const MASTER_ABORT_MODE = 1 << 5;
const SECONDARY_BUS_RESET = 1 << 6;
const FAST_BACK_TO_BACK_ENABLE = 1 << 7;
const PRIMARY_DISCARD_TIMER = 1 << 8;
const SECONDARY_DISCARD_TIMER = 1 << 9;
const DISCARD_TIMER_STATUS = 1 << 10;
const DISCARD_TIMER_SERR_ENABLED = 1 << 11;
const RESERVED_12 = 1 << 12;
const RESERVED_13 = 1 << 13;
const RESERVED_14 = 1 << 14;
const RESERVED_15 = 1 << 15;
}
}
@ -194,6 +230,15 @@ pub struct PCIDevice {
pub capabilities: Vec<Capability>,
}
impl PCIDevice {
/// set the status bits
pub fn set_status_bits(&self, status: Status) {
let am = CSpaceAccessMethod::IO;
let status = am.read16(self.loc, 0x06) | status.bits;
am.write16(self.loc, 0x06, status)
}
}
pub enum PCIScanError {}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -276,6 +321,7 @@ pub enum InterruptPin {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BAR {
Memory(u64, u32, Prefetchable, Type),
/// first element is address, second element is size
IO(u32, u32),
}

View File

@ -0,0 +1,118 @@
use core::hint::spin_loop;
use alloc::vec::Vec;
use jinux_frame::offset_of;
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
use jinux_util::frame_ptr::InFramePtr;
use pod::Pod;
use crate::{
device::block::{BlkReq, BlkResp, ReqType, RespStatus, BLK_SIZE},
device::VirtioDeviceError,
queue::VirtQueue,
VitrioPciCommonCfg,
};
use super::{BLKFeatures, VirtioBLKConfig};
#[derive(Debug)]
pub struct BLKDevice {
config: InFramePtr<VirtioBLKConfig>,
queue: VirtQueue,
}
impl BLKDevice {
/// Create a new VirtIO-Block driver.
/// msix_vector_left should at least have one element or n elements where n is the virtqueue amount
pub(crate) fn new(
cap: &CapabilityVirtioData,
bars: [Option<BAR>; 6],
common_cfg: &InFramePtr<VitrioPciCommonCfg>,
notify_base_address: usize,
notify_off_multiplier: u32,
mut msix_vector_left: Vec<u16>,
) -> Result<Self, VirtioDeviceError> {
let config = VirtioBLKConfig::new(cap, bars);
let num_queues = common_cfg.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as u16;
if num_queues != 1 {
return Err(VirtioDeviceError::QueuesAmountDoNotMatch(num_queues, 1));
}
let queue = VirtQueue::new(
&common_cfg,
0 as usize,
128,
notify_base_address as usize,
notify_off_multiplier,
msix_vector_left.pop().unwrap(),
)
.expect("create virtqueue failed");
Ok(Self { config, queue })
}
/// Negotiate features for the device specified bits 0~23
pub(crate) fn negotiate_features(features: u64) -> u64 {
let feature = BLKFeatures::from_bits(features).unwrap();
let support_features = BLKFeatures::from_bits(features).unwrap();
(feature & support_features).bits
}
/// read data from block device, this function is blocking
pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) {
assert_eq!(buf.len(), BLK_SIZE);
let req = BlkReq {
type_: ReqType::In,
reserved: 0,
sector: block_id as u64,
};
let mut resp = BlkResp::default();
self.queue
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
.expect("add queue failed");
self.queue.notify();
while !self.queue.can_pop() {
spin_loop();
}
self.queue.pop_used().expect("pop used failed");
match resp.status {
RespStatus::Ok => {}
_ => panic!("io error in block device"),
};
}
/// write data to block device, this function is blocking
pub fn write_block(&mut self, block_id: usize, buf: &[u8]) {
assert_eq!(buf.len(), BLK_SIZE);
let req = BlkReq {
type_: ReqType::Out,
reserved: 0,
sector: block_id as u64,
};
let mut resp = BlkResp::default();
self.queue
.add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()])
.expect("add queue failed");
self.queue.notify();
while !self.queue.can_pop() {
spin_loop();
}
self.queue.pop_used().expect("pop used failed");
let st = resp.status;
match st {
RespStatus::Ok => {}
_ => panic!("io error in block device:{:?}", st),
};
}
pub fn read_block_nb(&mut self, block_id: usize, buf: &mut [u8], resp: &mut BlkResp) {
assert_eq!(buf.len(), BLK_SIZE);
let req = BlkReq {
type_: ReqType::In,
reserved: 0,
sector: block_id as u64,
};
self.queue
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
.unwrap();
self.queue.notify();
}
}

View File

@ -1,9 +1,76 @@
pub mod device;
use bitflags::bitflags;
use jinux_pci::capability::vendor::virtio::CapabilityVirtioData;
use jinux_pci::util::BAR;
use jinux_util::frame_ptr::InFramePtr;
pub const BLK_SIZE: usize = 512;
bitflags! {
/// features for virtio block device
pub(crate) struct BLKFeatures : u64{
const SIZE_MAX = 1 << 1;
const SEG_MAX = 1 << 2;
const GEOMETRY = 1 << 4;
const RO = 1 << 5;
const BLK_SIZE = 1 << 6;
const FLUSH = 1 << 9;
const TOPOLOGY = 1 << 10;
const CONFIG_WCE = 1 << 11;
const DISCARD = 1 << 13;
const WRITE_ZEROES = 1 << 14;
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod)]
pub struct BlkReq {
pub type_: ReqType,
pub reserved: u32,
pub sector: u64,
}
/// Response of a VirtIOBlk request.
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod)]
pub struct BlkResp {
pub status: RespStatus,
}
impl Default for BlkResp {
fn default() -> Self {
BlkResp {
status: RespStatus::_NotReady,
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Pod)]
pub enum ReqType {
In = 0,
Out = 1,
Flush = 4,
Discard = 11,
WriteZeroes = 13,
}
#[repr(u8)]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Pod)]
pub enum RespStatus {
/// Ok.
Ok = 0,
/// IoErr.
IoErr = 1,
/// Unsupported yet.
Unsupported = 2,
/// Not ready.
_NotReady = 3,
}
#[derive(Debug, Copy, Clone, Pod)]
#[repr(C)]
pub struct VirtioBLKConfig {

View File

@ -0,0 +1,154 @@
use crate::{device::VirtioDeviceError, queue::VirtQueue, VitrioPciCommonCfg};
use alloc::{boxed::Box, vec::Vec};
use bitflags::bitflags;
use jinux_frame::offset_of;
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
use jinux_util::frame_ptr::InFramePtr;
use pod::Pod;
use spin::Mutex;
use super::{
InputConfigSelect, InputEvent, VirtioInputConfig, QUEUE_EVENT, QUEUE_SIZE, QUEUE_STATUS,
};
bitflags! {
pub struct InputProp : u8{
const POINTER = 0x00;
const DIRECT = 0x01;
const BUTTONPAD = 0x02;
const SEMI_MT = 0x03;
const TOPBUTTONPAD = 0x04;
const POINTING_STICK = 0x05;
const ACCELEROMETER = 0x06;
}
}
pub const SYN: u8 = 0x00;
pub const KEY: u8 = 0x01;
pub const REL: u8 = 0x02;
pub const ABS: u8 = 0x03;
pub const MSC: u8 = 0x04;
pub const SW: u8 = 0x05;
pub const LED: u8 = 0x11;
pub const SND: u8 = 0x12;
pub const REP: u8 = 0x14;
pub const FF: u8 = 0x15;
pub const PWR: u8 = 0x16;
pub const FF_STATUS: u8 = 0x17;
/// Virtual human interface devices such as keyboards, mice and tablets.
///
/// An instance of the virtio device represents one such input device.
/// Device behavior mirrors that of the evdev layer in Linux,
/// making pass-through implementations on top of evdev easy.
#[derive(Debug)]
pub struct InputDevice {
config: InFramePtr<VirtioInputConfig>,
event_queue: Mutex<VirtQueue>,
status_queue: VirtQueue,
pub event_buf: Mutex<Box<[InputEvent; QUEUE_SIZE]>>,
}
impl InputDevice {
/// Create a new VirtIO-Input driver.
/// msix_vector_left should at least have one element or n elements where n is the virtqueue amount
pub fn new(
cap: &CapabilityVirtioData,
bars: [Option<BAR>; 6],
common_cfg: &InFramePtr<VitrioPciCommonCfg>,
notify_base_address: usize,
notify_off_multiplier: u32,
mut msix_vector_left: Vec<u16>,
) -> Result<Self, VirtioDeviceError> {
let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
let vector_left = msix_vector_left.len();
let mut next_msix_vector = msix_vector_left.pop().unwrap();
let mut event_queue = VirtQueue::new(
&common_cfg,
QUEUE_EVENT,
QUEUE_SIZE as u16,
notify_base_address,
notify_off_multiplier,
next_msix_vector,
)
.expect("create event virtqueue failed");
next_msix_vector = if vector_left == 1 {
next_msix_vector
} else {
msix_vector_left.pop().unwrap()
};
let status_queue = VirtQueue::new(
&common_cfg,
QUEUE_STATUS,
QUEUE_SIZE as u16,
notify_base_address,
notify_off_multiplier,
next_msix_vector,
)
.expect("create status virtqueue failed");
for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
let token = event_queue.add(&[], &[event.as_bytes_mut()]);
match token {
Ok(value) => {
assert_eq!(value, i as u16);
}
Err(_) => {
return Err(VirtioDeviceError::QueueUnknownError);
}
}
}
Ok(Self {
config: VirtioInputConfig::new(cap, bars),
event_queue: Mutex::new(event_queue),
status_queue,
event_buf: Mutex::new(event_buf),
})
}
// /// Acknowledge interrupt and process events.
// pub fn ack_interrupt(&mut self) -> bool {
// self.transport.ack_interrupt()
// }
/// Pop the pending event.
pub fn pop_pending_event(&self) -> Option<InputEvent> {
let mut lock = self.event_queue.lock();
if let Ok((token, _)) = lock.pop_used() {
if token >= QUEUE_SIZE as u16 {
return None;
}
let event = &mut self.event_buf.lock()[token as usize];
// requeue
if let Ok(new_token) = lock.add(&[], &[event.as_bytes_mut()]) {
// This only works because nothing happen between `pop_used` and `add` that affects
// the list of free descriptors in the queue, so `add` reuses the descriptor which
// was just freed by `pop_used`.
assert_eq!(new_token, token);
return Some(*event);
}
}
None
}
/// Query a specific piece of information by `select` and `subsel`, and write
/// result to `out`, return the result size.
pub fn query_config_select(&self, select: InputConfigSelect, subsel: u8, out: &mut [u8]) -> u8 {
self.config
.write_at(offset_of!(VirtioInputConfig, select), select as u8);
self.config
.write_at(offset_of!(VirtioInputConfig, subsel), subsel as u8);
let size = self.config.read_at(offset_of!(VirtioInputConfig, size));
let data: [u8; 128] = self.config.read_at(offset_of!(VirtioInputConfig, data));
out[..size as usize].copy_from_slice(&data[..size as usize]);
size
}
/// Negotiate features for the device specified bits 0~23
pub(crate) fn negotiate_features(features: u64) -> u64 {
assert_eq!(features, 0);
0
}
}

View File

@ -0,0 +1,94 @@
pub mod device;
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
use jinux_util::frame_ptr::InFramePtr;
/// Select value used for [`VirtIOInput::query_config_select()`].
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum InputConfigSelect {
/// Returns the name of the device, in u.string. subsel is zero.
IdName = 0x01,
/// Returns the serial number of the device, in u.string. subsel is zero.
IdSerial = 0x02,
/// Returns ID information of the device, in u.ids. subsel is zero.
IdDevids = 0x03,
/// Returns input properties of the device, in u.bitmap. subsel is zero.
/// Individual bits in the bitmap correspond to INPUT_PROP_* constants used
/// by the underlying evdev implementation.
PropBits = 0x10,
/// subsel specifies the event type using EV_* constants in the underlying
/// evdev implementation. If size is non-zero the event type is supported
/// and a bitmap of supported event codes is returned in u.bitmap. Individual
/// bits in the bitmap correspond to implementation-defined input event codes,
/// for example keys or pointing device axes.
EvBits = 0x11,
/// subsel specifies the absolute axis using ABS_* constants in the underlying
/// evdev implementation. Information about the axis will be returned in u.abs.
AbsInfo = 0x12,
}
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct VirtioInputConfig {
/// write only
select: u8,
/// write only
subsel: u8,
/// read only
size: u8,
_reversed: [u8; 5],
/// read only
data: [u8; 128],
}
impl VirtioInputConfig {
pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option<BAR>; 6]) -> InFramePtr<Self> {
let bar = cap.bar;
let offset = cap.offset;
match bars[bar as usize].expect("Virtio pci block cfg:bar is none") {
BAR::Memory(address, _, _, _) => InFramePtr::new(address as usize + offset as usize)
.expect("can not get in frame ptr for virtio block config"),
BAR::IO(_, _) => {
panic!("Virtio pci block cfg:bar is IO type")
}
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod)]
struct AbsInfo {
min: u32,
max: u32,
fuzz: u32,
flat: u32,
res: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod)]
struct DevIDs {
bustype: u16,
vendor: u16,
product: u16,
version: u16,
}
/// Both queues use the same `virtio_input_event` struct. `type`, `code` and `value`
/// are filled according to the Linux input layer (evdev) interface.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Pod)]
pub struct InputEvent {
/// Event type.
pub event_type: u16,
/// Event code.
pub code: u16,
/// Event value.
pub value: u32,
}
const QUEUE_EVENT: usize = 0;
const QUEUE_STATUS: usize = 1;
// a parameter that can change
const QUEUE_SIZE: usize = 64;

View File

@ -0,0 +1,160 @@
use crate::{device::block::device::BLKDevice, Feature, VirtioDeviceType, VitrioPciCommonCfg};
use alloc::vec::Vec;
use jinux_pci::{
capability::{vendor::virtio::CapabilityVirtioData, Capability},
util::BAR,
};
use jinux_util::frame_ptr::InFramePtr;
use self::input::device::InputDevice;
pub mod block;
pub mod input;
pub(crate) const PCI_VIRTIO_CAP_COMMON_CFG: u8 = 1;
pub(crate) const PCI_VIRTIO_CAP_NOTIFY_CFG: u8 = 2;
pub(crate) const PCI_VIRTIO_CAP_ISR_CFG: u8 = 3;
pub(crate) const PCI_VIRTIO_CAP_DEVICE_CFG: u8 = 4;
pub(crate) const PCI_VIRTIO_CAP_PCI_CFG: u8 = 5;
#[derive(Debug)]
pub enum VirtioDevice {
Network,
Block(BLKDevice),
Console,
Entropy,
TraditionalMemoryBalloon,
ScsiHost,
GPU,
Input(InputDevice),
Crypto,
Socket,
Unknown,
}
#[derive(Debug)]
pub enum VirtioDeviceError {
/// queues amount do not match the requirement
/// first element is actual value, second element is expect value
QueuesAmountDoNotMatch(u16, u16),
/// unknown error of queue
QueueUnknownError,
/// The input virtio capability list contains invalid element
CapabilityListError,
}
pub struct VirtioInfo {
pub device_type: VirtioDeviceType,
pub notify_base_address: u64,
pub notify_off_multiplier: u32,
pub common_cfg_frame_ptr: InFramePtr<VitrioPciCommonCfg>,
pub device_cap_cfg: CapabilityVirtioData,
}
impl VirtioInfo {
pub(crate) fn new(
device_type: VirtioDeviceType,
bars: [Option<BAR>; 6],
virtio_cap_list: Vec<&Capability>,
) -> Result<Self, VirtioDeviceError> {
let mut notify_base_address = 0;
let mut notify_off_multiplier = 0;
let mut common_cfg_frame_ptr_some = None;
let mut device_cap_cfg = None;
for cap in virtio_cap_list.iter() {
match cap.data {
jinux_pci::capability::CapabilityData::VNDR(vndr_data) => match vndr_data {
jinux_pci::capability::vendor::CapabilityVNDRData::VIRTIO(cap_data) => {
match cap_data.cfg_type {
PCI_VIRTIO_CAP_COMMON_CFG => {
common_cfg_frame_ptr_some =
Some(VitrioPciCommonCfg::new(&cap_data, bars));
}
PCI_VIRTIO_CAP_NOTIFY_CFG => {
notify_off_multiplier = cap_data.option.unwrap();
match bars[cap_data.bar as usize]
.expect("initialize PCIDevice failed, notify bar is None")
{
BAR::Memory(address, _, _, _) => {
notify_base_address = address + cap_data.offset as u64;
}
BAR::IO(_, _) => {
panic!("initialize PCIDevice failed, notify bar is IO Type")
}
};
}
PCI_VIRTIO_CAP_ISR_CFG => {}
PCI_VIRTIO_CAP_DEVICE_CFG => {
device_cap_cfg = Some(cap_data);
}
PCI_VIRTIO_CAP_PCI_CFG => {}
_ => panic!("unsupport cfg, cfg_type:{}", cap_data.cfg_type),
};
}
},
_ => {
return Err(VirtioDeviceError::CapabilityListError);
}
}
}
Ok(Self {
notify_base_address,
notify_off_multiplier,
common_cfg_frame_ptr: common_cfg_frame_ptr_some
.ok_or(VirtioDeviceError::CapabilityListError)?,
device_cap_cfg: device_cap_cfg.ok_or(VirtioDeviceError::CapabilityListError)?,
device_type,
})
}
}
impl VirtioDevice {
/// call this function after features_ok
pub(crate) fn new(
virtio_info: &VirtioInfo,
bars: [Option<BAR>; 6],
msix_vector_left: Vec<u16>,
) -> Result<Self, VirtioDeviceError> {
let device = match virtio_info.device_type {
VirtioDeviceType::Block => VirtioDevice::Block(BLKDevice::new(
&virtio_info.device_cap_cfg,
bars,
&virtio_info.common_cfg_frame_ptr,
virtio_info.notify_base_address as usize,
virtio_info.notify_off_multiplier,
msix_vector_left,
)?),
VirtioDeviceType::Input => VirtioDevice::Input(InputDevice::new(
&virtio_info.device_cap_cfg,
bars,
&virtio_info.common_cfg_frame_ptr,
virtio_info.notify_base_address as usize,
virtio_info.notify_off_multiplier,
msix_vector_left,
)?),
_ => {
panic!("initialize PCIDevice failed, unsupport Virtio Device Type")
}
};
Ok(device)
}
pub(crate) fn negotiate_features(features: u64, device_type: VirtioDeviceType) -> u64 {
let device_specified_features = features & ((1 << 24) - 1);
let device_support_features = match device_type {
VirtioDeviceType::Network => todo!(),
VirtioDeviceType::Block => BLKDevice::negotiate_features(device_specified_features),
VirtioDeviceType::Console => todo!(),
VirtioDeviceType::Entropy => todo!(),
VirtioDeviceType::TraditionalMemoryBalloon => todo!(),
VirtioDeviceType::ScsiHost => todo!(),
VirtioDeviceType::GPU => todo!(),
VirtioDeviceType::Input => InputDevice::negotiate_features(device_specified_features),
VirtioDeviceType::Crypto => todo!(),
VirtioDeviceType::Socket => todo!(),
};
let support_feature = Feature::from_bits_truncate(features);
// support_feature.remove(Feature::RING_EVENT_IDX);
features & (support_feature.bits | device_support_features)
}
}

View File

@ -4,27 +4,23 @@
#![allow(dead_code)]
extern crate alloc;
use alloc::{sync::Arc, vec::Vec};
use bitflags::bitflags;
use jinux_frame::{info, offset_of, TrapFrame};
use device::VirtioDevice;
use jinux_frame::{debug, info, offset_of, TrapFrame};
use jinux_pci::util::{PCIDevice, BAR};
use jinux_util::frame_ptr::InFramePtr;
use pod_derive::Pod;
use spin::{mutex::Mutex, MutexGuard};
use self::{block::VirtioBLKConfig, queue::VirtQueue};
use crate::device::VirtioInfo;
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, msix::MSIX};
#[macro_use]
extern crate pod_derive;
pub mod block;
pub mod queue;
pub mod device;
pub(crate) const PCI_VIRTIO_CAP_COMMON_CFG: u8 = 1;
pub(crate) const PCI_VIRTIO_CAP_NOTIFY_CFG: u8 = 2;
pub(crate) const PCI_VIRTIO_CAP_ISR_CFG: u8 = 3;
pub(crate) const PCI_VIRTIO_CAP_DEVICE_CFG: u8 = 4;
pub(crate) const PCI_VIRTIO_CAP_PCI_CFG: u8 = 5;
pub mod queue;
bitflags! {
/// The device status field.
@ -55,6 +51,30 @@ bitflags! {
}
}
bitflags! {
/// all device features, bits 0~23 are sepecified by device
/// if using this struct to translate u64, use from_bits_truncate function instead of from_bits
///
struct Feature: 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
// since virtio v1.1
const ACCESS_PLATFORM = 1 << 33;
const RING_PACKED = 1 << 34;
const IN_ORDER = 1 << 35;
const ORDER_PLATFORM = 1 << 36;
const SR_IOV = 1 << 37;
const NOTIFICATION_DATA = 1 << 38;
}
}
#[derive(Debug, Default, Copy, Clone, Pod)]
#[repr(C)]
pub struct VitrioPciCommonCfg {
@ -62,14 +82,14 @@ pub struct VitrioPciCommonCfg {
device_feature: u32,
driver_feature_select: u32,
driver_feature: u32,
config_msix_vector: u16,
pub config_msix_vector: u16,
num_queues: u16,
device_status: u8,
pub device_status: u8,
config_generation: u8,
queue_select: u16,
queue_size: u16,
queue_msix_vector: u16,
pub queue_msix_vector: u16,
queue_enable: u16,
queue_notify_off: u16,
queue_desc: u64,
@ -83,19 +103,22 @@ impl VitrioPciCommonCfg {
let offset = cap.offset;
match bars[bar as usize].expect("Virtio pci common cfg:bar is none") {
BAR::Memory(address, _, _, _) => {
info!("common_cfg addr:{:x}", (address as usize + offset as usize));
debug!("common_cfg addr:{:x}", (address as usize + offset as usize));
InFramePtr::new(address as usize + offset as usize)
.expect("cannot get InFramePtr in VitioPciCommonCfg")
}
BAR::IO(_, _) => {
panic!("Virtio pci common cfg:bar is IO type")
BAR::IO(first, second) => {
panic!(
"Virtio pci common cfg:bar is IO type, value:{:x}, {:x}",
first, second
)
}
}
}
}
#[derive(Debug)]
enum VirtioDeviceType {
#[derive(Debug, Clone, Copy)]
pub enum VirtioDeviceType {
Network,
Block,
Console,
@ -107,34 +130,17 @@ enum VirtioDeviceType {
Crypto,
Socket,
}
#[derive(Debug)]
enum VirtioDevice {
Network,
Block(InFramePtr<VirtioBLKConfig>),
Console,
Entropy,
TraditionalMemoryBalloon,
ScsiHost,
GPU,
Input,
Crypto,
Socket,
Unknown,
}
pub struct PCIVirtioDevice {
/// common config of one device
common_cfg: InFramePtr<VitrioPciCommonCfg>,
device: VirtioDevice,
queues: Vec<Arc<Mutex<VirtQueue>>>,
msix: MSIX,
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
pub device: VirtioDevice,
pub msix: MSIX,
}
impl PCIVirtioDevice {
/// create a new PCI Virtio Device, note that this function will stop with device status features ok
pub fn new(dev: Arc<PCIDevice>) -> Self {
if dev.id.vendor_id != 0x1af4 {
panic!("initialize PCIDevice failed, wrong PCI vendor id");
}
assert_eq!(dev.id.vendor_id, 0x1af4);
let device_type = match dev.id.device_id {
0x1000 | 0x1041 => VirtioDeviceType::Network,
0x1001 | 0x1042 => VirtioDeviceType::Block,
@ -143,58 +149,21 @@ impl PCIVirtioDevice {
0x1004 | 0x1045 => VirtioDeviceType::ScsiHost,
0x1005 | 0x1046 => VirtioDeviceType::Entropy,
// 0x1009 | 0x104a => VirtioDeviceType::,
0x1011 | 0x1052 => VirtioDeviceType::Input,
_ => {
panic!("initialize PCIDevice failed, unrecognized Virtio Device Type")
}
};
info!("PCI device:{:?}", device_type);
let bars = dev.bars;
let loc = dev.loc;
let mut notify_base_address = 0;
let mut notify_off_multiplier = 0;
let mut device = VirtioDevice::Unknown;
let mut msix = MSIX::default();
let mut common_cfg_frame_ptr_some = None;
let mut virtio_cap_list = Vec::new();
for cap in dev.capabilities.iter() {
match &cap.data {
jinux_pci::capability::CapabilityData::VNDR(vndr_data) => match vndr_data {
jinux_pci::capability::vendor::CapabilityVNDRData::VIRTIO(cap_data) => {
match cap_data.cfg_type {
PCI_VIRTIO_CAP_COMMON_CFG => {
common_cfg_frame_ptr_some =
Some(VitrioPciCommonCfg::new(cap_data, bars));
}
PCI_VIRTIO_CAP_NOTIFY_CFG => {
notify_off_multiplier = cap_data.option.unwrap();
match bars[cap_data.bar as usize]
.expect("initialize PCIDevice failed, notify bar is None")
{
BAR::Memory(address, _, _, _) => {
notify_base_address = address + cap_data.offset as u64;
}
BAR::IO(_, _) => {
panic!("initialize PCIDevice failed, notify bar is IO Type")
}
};
}
PCI_VIRTIO_CAP_ISR_CFG => {}
PCI_VIRTIO_CAP_DEVICE_CFG => {
device = match device_type {
VirtioDeviceType::Block => {
VirtioDevice::Block(VirtioBLKConfig::new(&cap_data, bars))
}
_ => {
panic!(
"initialize PCIDevice failed, unsupport Virtio Device Type"
)
}
}
}
PCI_VIRTIO_CAP_PCI_CFG => {}
_ => panic!("unsupport cfg, cfg_type:{}", cap_data.cfg_type),
};
}
},
jinux_pci::capability::CapabilityData::VNDR(_) => {
virtio_cap_list.push(cap);
}
jinux_pci::capability::CapabilityData::MSIX(cap_data) => {
msix = MSIX::new(&cap_data, bars, loc, cap.cap_ptr);
}
@ -206,86 +175,86 @@ impl PCIVirtioDevice {
}
}
}
let common_cfg_frame_ptr = if common_cfg_frame_ptr_some.is_none() {
panic!("Vitio Common cfg is None")
} else {
common_cfg_frame_ptr_some.unwrap()
};
info!(
"common_cfg_num_queues:{:x}",
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues))
);
// let b : InFramePtr<u8> = InFramePtr::new(common_cfg_frame_ptr.paddr()+19).expect("test");
// info!("test_Aaaaaa:{:#x?}",b.read());
if msix.table_size
!= common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as u16
{
panic!("the msix table size is not match with the number of queues");
}
let mut queues = Vec::new();
// create device
let virtio_info = VirtioInfo::new(device_type, bars, virtio_cap_list).unwrap();
let mut msix_vector_list: Vec<u16> = (0..msix.table_size).collect();
let config_msix_vector = msix_vector_list.pop().unwrap();
let common_cfg_frame_ptr = &virtio_info.common_cfg_frame_ptr;
let num_queues: u16 =
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues));
debug!("num_queues:{:x}", num_queues);
// the table size of msix should be equal to n+1 or 2 where n is the virtqueue amount
assert!(msix.table_size == 2 || msix.table_size == (num_queues + 1));
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, device_status),
DeviceStatus::ACKNOWLEDGE.bits(),
offset_of!(VitrioPciCommonCfg, config_msix_vector),
config_msix_vector,
);
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, device_status),
DeviceStatus::DRIVER.bits(),
(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER).bits(),
);
// negotiate features
// get the value of device features
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, device_feature_select),
0 as u32,
);
let mut low: u32 =
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, device_feature));
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, device_feature_select),
1 as u32,
);
let mut high: u32 =
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, device_feature));
let mut feature = (high as u64) << 32;
feature |= low as u64;
// let the device to negotiate Features
let driver_features = VirtioDevice::negotiate_features(feature, device_type);
debug!("support_features:{:x}", driver_features);
// write features back
low = driver_features as u32;
high = (driver_features >> 32) as u32;
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, driver_feature_select),
0 as u32,
);
common_cfg_frame_ptr.write_at(offset_of!(VitrioPciCommonCfg, driver_feature), low);
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, driver_feature_select),
1 as u32,
);
common_cfg_frame_ptr.write_at(offset_of!(VitrioPciCommonCfg, driver_feature), high);
// change to features ok status
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, device_status),
DeviceStatus::FEATURES_OK.bits(),
(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK).bits(),
);
for i in 0..common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as u16
{
queues.push(Arc::new(Mutex::new(
VirtQueue::new(
&common_cfg_frame_ptr,
i as usize,
16,
notify_base_address as usize,
notify_off_multiplier,
i,
)
.expect("create virtqueue failed"),
)));
}
let device = VirtioDevice::new(&virtio_info, bars, msix_vector_list).unwrap();
// change to driver ok status
common_cfg_frame_ptr.write_at(
offset_of!(VitrioPciCommonCfg, device_status),
DeviceStatus::DRIVER_OK.bits(),
(DeviceStatus::ACKNOWLEDGE
| DeviceStatus::DRIVER
| DeviceStatus::FEATURES_OK
| DeviceStatus::DRIVER_OK)
.bits(),
);
Self {
common_cfg: common_cfg_frame_ptr,
common_cfg: virtio_info.common_cfg_frame_ptr,
device,
queues,
msix,
}
}
pub fn get_queue(&self, queue_index: u16) -> MutexGuard<VirtQueue> {
self.queues
.get(queue_index as usize)
.expect("index out of range")
.lock()
}
/// register the queue interrupt functions, this function should call only once
pub fn register_queue_interrupt_functions<F>(&mut self, functions: &mut Vec<F>)
/// register all the interrupt functions except the config change, this function should call only once
pub fn register_interrupt_functions<F>(&mut self, function: &'static F)
where
F: Fn(&TrapFrame) + Send + Sync + 'static,
{
let len = functions.len();
if len
!= self
.common_cfg
.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as usize
{
panic!("the size of queue interrupt functions not equal to the number of queues, functions amount:{}, queues amount:{}",len,
self.common_cfg.read_at(offset_of!(VitrioPciCommonCfg,num_queues)));
}
functions.reverse();
for i in 0..len {
let function = functions.pop().unwrap();
for i in 0..self.msix.table_size as usize {
let msix = self.msix.table.get_mut(i).unwrap();
if !msix.irq_handle.is_empty() {
panic!("function `register_queue_interrupt_functions` called more than one time");
@ -293,19 +262,4 @@ impl PCIVirtioDevice {
msix.irq_handle.on_active(function);
}
}
fn check_subset(smaller: u32, bigger: u32) -> bool {
let mut temp: u32 = 1;
for _ in 0..31 {
if (smaller & temp) > (bigger & temp) {
return false;
}
temp <<= 1;
}
if (smaller & temp) > (bigger & temp) {
false
} else {
true
}
}
}

View File

@ -4,7 +4,10 @@ use super::VitrioPciCommonCfg;
use alloc::vec::Vec;
use bitflags::bitflags;
use core::sync::atomic::{fence, Ordering};
use jinux_frame::offset_of;
use jinux_frame::{
offset_of,
vm::{VmAllocOptions, VmFrame, VmFrameVec},
};
use jinux_util::frame_ptr::InFramePtr;
#[derive(Debug)]
pub enum QueueError {
@ -38,9 +41,13 @@ pub struct VirtQueue {
/// The number of used queues.
num_used: u16,
/// The head desc index of the free list.
free_head: u16,
pub free_head: u16,
/// the index of the next avail ring index
avail_idx: u16,
/// last service used index
last_used_idx: u16,
physical_frame_store: Vec<VmFrame>,
}
impl VirtQueue {
@ -49,26 +56,69 @@ impl VirtQueue {
cfg: &InFramePtr<VitrioPciCommonCfg>,
idx: usize,
size: u16,
cap_offset: usize,
notify_base_address: usize,
notify_off_multiplier: u32,
msix_vector: u16,
) -> Result<Self, QueueError> {
cfg.write_at(offset_of!(VitrioPciCommonCfg, queue_select), idx as u16);
assert_eq!(
cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_select)),
idx as u16
);
// info!("actual queue_size:{}",cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_size)));
if !size.is_power_of_two() || cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_size)) < size
{
return Err(QueueError::InvalidArgs);
}
// Allocate contiguous pages.
cfg.write_at(offset_of!(VitrioPciCommonCfg, queue_size), size);
cfg.write_at(
offset_of!(VitrioPciCommonCfg, queue_msix_vector),
msix_vector,
);
assert_eq!(
cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_msix_vector)),
msix_vector
);
let mut descs = Vec::new();
//allocate page
let mut frame_vec = Vec::new();
let vm_allocate_option = VmAllocOptions::new(1);
if cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_desc)) == 0 as u64 {
let frame = VmFrameVec::allocate(&vm_allocate_option)
.expect("cannot allocate physical frame for virtqueue")
.remove(0);
cfg.write_at(
offset_of!(VitrioPciCommonCfg, queue_desc),
frame.paddr() as u64,
);
frame_vec.push(frame);
}
if cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_driver)) == 0 as u64 {
let frame = VmFrameVec::allocate(&vm_allocate_option)
.expect("cannot allocate physical frame for virtqueue")
.remove(0);
cfg.write_at(
offset_of!(VitrioPciCommonCfg, queue_driver),
frame.paddr() as u64,
);
frame_vec.push(frame);
}
if cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_device)) == 0 as u64 {
let frame = VmFrameVec::allocate(&vm_allocate_option)
.expect("cannot allocate physical frame for virtqueue")
.remove(0);
cfg.write_at(
offset_of!(VitrioPciCommonCfg, queue_device),
frame.paddr() as u64,
);
frame_vec.push(frame);
}
for i in 0..size {
descs.push(
InFramePtr::new(
@ -85,13 +135,14 @@ impl VirtQueue {
let used =
InFramePtr::new(cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_device)) as usize)
.expect("can not get Inframeptr for virtio queue used ring");
let notify = InFramePtr::new(cap_offset + notify_off_multiplier as usize * idx)
let notify = InFramePtr::new(notify_base_address + notify_off_multiplier as usize * idx)
.expect("can not get Inframeptr for virtio queue notify");
// Link descriptors together.
for i in 0..(size - 1) {
let temp = descs.get(i as usize).unwrap();
temp.write_at(offset_of!(Descriptor, next), i + 1);
}
avail.write_at(offset_of!(AvailRing, flags), 0 as u16);
cfg.write_at(offset_of!(VitrioPciCommonCfg, queue_enable), 1 as u16);
Ok(VirtQueue {
descs,
@ -104,6 +155,7 @@ impl VirtQueue {
free_head: 0,
avail_idx: 0,
last_used_idx: 0,
physical_frame_store: frame_vec,
})
}
@ -161,6 +213,8 @@ impl VirtQueue {
self.avail_idx = self.avail_idx.wrapping_add(1);
self.avail
.write_at(offset_of!(AvailRing, idx), self.avail_idx);
fence(Ordering::SeqCst);
Ok(head)
}
@ -180,6 +234,13 @@ impl VirtQueue {
fn recycle_descriptors(&mut self, mut head: u16) {
let origin_free_head = self.free_head;
self.free_head = head;
let last_free_head = if head == 0 {
self.queue_size - 1
} else {
head - 1
};
let temp_desc = &mut self.descs[last_free_head as usize];
temp_desc.write_at(offset_of!(Descriptor, next), head);
loop {
let desc = &mut self.descs[head as usize];
let flags: DescFlags = desc.read_at(offset_of!(Descriptor, flags));
@ -204,11 +265,12 @@ impl VirtQueue {
fence(Ordering::SeqCst);
let last_used_slot = self.last_used_idx & (self.queue_size - 1);
let last_used = self.used.read_at(
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8) as *const UsedElem,
let index = self.used.read_at(
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8) as *const u32,
) as u16;
let len = self.used.read_at(
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8 + 4) as *const u32,
);
let index = last_used.id as u16;
let len = last_used.len;
self.recycle_descriptors(index);
self.last_used_idx = self.last_used_idx.wrapping_add(1);
@ -239,7 +301,8 @@ struct Descriptor {
impl Descriptor {
fn set_buf(&mut self, buf: &[u8]) {
self.addr = jinux_frame::virt_to_phys(buf.as_ptr() as usize) as u64;
self.addr = jinux_frame::translate_not_offset_virtual_address(buf.as_ptr() as usize) as u64;
self.len = buf.len() as u32;
}
}
@ -247,7 +310,7 @@ impl Descriptor {
fn set_buf(inframe_ptr: &InFramePtr<Descriptor>, buf: &[u8]) {
inframe_ptr.write_at(
offset_of!(Descriptor, addr),
jinux_frame::virt_to_phys(buf.as_ptr() as usize) as u64,
jinux_frame::translate_not_offset_virtual_address(buf.as_ptr() as usize) as u64,
);
inframe_ptr.write_at(offset_of!(Descriptor, len), buf.len() as u32);
}
@ -272,24 +335,26 @@ impl Default for DescFlags {
/// The driver uses the available ring to offer buffers to the device:
/// each ring entry refers to the head of a descriptor chain.
/// It is only written by the driver and read by the device.
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Pod)]
#[repr(C, align(2))]
#[derive(Debug, Copy, Clone, Pod)]
struct AvailRing {
flags: u16,
/// A driver MUST NOT decrement the idx.
idx: u16,
ring: [u16; 32], // actual size: queue_size
ring: [u16; 64], // actual size: queue_size
used_event: u16, // unused
}
/// The used ring is where the device returns buffers once it is done with them:
/// it is only written to by the device, and read by the driver.
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Pod)]
#[repr(C, align(4))]
#[derive(Debug, Copy, Clone, Pod)]
struct UsedRing {
// the flag in UsedRing
flags: u16,
// the next index of the used element in ring array
idx: u16,
ring: [UsedElem; 32], // actual size: queue_size
ring: [UsedElem; 64], // actual size: queue_size
avail_event: u16, // unused
}

View File

@ -15,6 +15,7 @@ typeflags = {path="../typeflags"}
typeflags-util = {path="../typeflags-util"}
jinux-rights-proc = {path="../jinux-rights-proc"}
jinux-util = {path="../jinux-util"}
virtio-input-decoder = "0.1.4"
# parse elf file
xmas-elf = "0.8.0"

View File

@ -0,0 +1,27 @@
use alloc::{sync::Arc, vec::Vec};
use jinux_frame::TrapFrame;
use lazy_static::lazy_static;
use spin::Mutex;
lazy_static! {
static ref KEYBOARD_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
Mutex::new(Vec::new());
}
pub fn init() {
jinux_frame::device::console::register_console_input_callback(handle_irq)
}
fn handle_irq(trap_frame: &TrapFrame) {
if KEYBOARD_CALLBACKS.is_locked() {
return;
}
let lock = KEYBOARD_CALLBACKS.lock();
for callback in lock.iter() {
callback.call(((jinux_frame::device::console::receive_char().unwrap()),));
}
}
pub fn register_console_callback(callback: Arc<dyn Fn(u8) + 'static + Send + Sync>) {
KEYBOARD_CALLBACKS.lock().push(callback);
}

View File

@ -1,5 +1,7 @@
pub mod console;
pub mod pci;
pub fn init() {
pci::init();
console::init();
}

View File

@ -6,12 +6,15 @@ pub fn init() {
for index in 0..jinux_pci::device_amount() {
let pci_device = jinux_pci::get_pci_devices(index)
.expect("initialize pci device failed: pci device is None");
if pci_device.id.vendor_id == 0x1af4
&& (pci_device.id.device_id == 0x1001 || pci_device.id.device_id == 0x1042)
{
info!("found virtio block device");
virtio::block::init(pci_device);
if pci_device.id.vendor_id == 0x1af4 {
if pci_device.id.device_id == 0x1001 || pci_device.id.device_id == 0x1042 {
info!("found virtio block device");
virtio::block::init(pci_device);
} else if pci_device.id.device_id == 0x1011 || pci_device.id.device_id == 0x1052 {
info!("found virtio input device");
virtio::input::init(pci_device);
}
}
}
info!("pci initialization complete")
info!("pci initialization complete");
}

View File

@ -1,66 +1,23 @@
use core::hint::spin_loop;
use crate::process::Process;
use alloc::sync::Arc;
use alloc::vec::Vec;
use jinux_frame::info;
use jinux_pci::msix::MSIX;
use jinux_pci::PCIDevice;
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::device::block::device::BLKDevice;
use jinux_virtio::device::block::BlkResp;
use jinux_virtio::PCIVirtioDevice;
use jinux_virtio::VitrioPciCommonCfg;
use lazy_static::lazy_static;
use spin::mutex::Mutex;
use super::BlockDevice;
pub const BLK_SIZE: usize = 512;
use jinux_frame::TrapFrame;
use pod::Pod;
pub struct VirtioBlockDevice {
virtio_device: PCIVirtioDevice,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod)]
pub struct BlkReq {
pub type_: ReqType,
pub reserved: u32,
pub sector: u64,
}
/// Response of a VirtIOBlk request.
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod)]
pub struct BlkResp {
pub status: RespStatus,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Pod)]
pub enum ReqType {
In = 0,
Out = 1,
Flush = 4,
Discard = 11,
WriteZeroes = 13,
}
#[repr(u8)]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Pod)]
pub enum RespStatus {
/// Ok.
Ok = 0,
/// IoErr.
IoErr = 1,
/// Unsupported yet.
Unsupported = 2,
/// Not ready.
_NotReady = 3,
}
impl Default for BlkResp {
fn default() -> Self {
BlkResp {
status: RespStatus::_NotReady,
}
}
blk_device: Mutex<BLKDevice>,
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
msix: MSIX,
}
lazy_static! {
@ -68,52 +25,19 @@ lazy_static! {
pub static ref BLOCK_DEVICE: Arc<Mutex<Option<VirtioBlockDevice>>> = Arc::new(Mutex::new(None)) ;
}
impl VirtioBlockDevice {
pub fn read_block_nb(&self, block_id: usize, buf: &mut [u8], res: &mut BlkResp) {
self.blk_device.lock().read_block_nb(block_id, buf, res);
}
}
impl BlockDevice for VirtioBlockDevice {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
assert_eq!(buf.len(), BLK_SIZE);
let req = BlkReq {
type_: ReqType::In,
reserved: 0,
sector: block_id as u64,
};
let mut resp = BlkResp::default();
let mut queue = self.virtio_device.get_queue(0);
queue
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
.expect("add queue failed");
queue.notify();
while !queue.can_pop() {
spin_loop();
}
queue.pop_used().expect("pop used failed");
match resp.status {
RespStatus::Ok => {}
_ => panic!("io error in block device"),
};
self.blk_device.lock().read_block(block_id, buf);
}
/// it is blocking now
fn write_block(&self, block_id: usize, buf: &[u8]) {
assert_eq!(buf.len(), BLK_SIZE);
let req = BlkReq {
type_: ReqType::Out,
reserved: 0,
sector: block_id as u64,
};
let mut resp = BlkResp::default();
let mut queue = self.virtio_device.get_queue(0);
queue
.add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()])
.expect("add queue failed");
queue.notify();
while !queue.can_pop() {
spin_loop();
}
queue.pop_used().expect("pop used failed");
match resp.status {
RespStatus::Ok => {}
_ => panic!("io error in block device"),
};
self.blk_device.lock().write_block(block_id, buf);
}
fn handle_irq(&self) {
info!("handle irq in block device!");
@ -124,12 +48,20 @@ impl VirtioBlockDevice {
fn new(mut virtio_device: PCIVirtioDevice) -> Self {
fn handle_block_device(frame: &TrapFrame) {
info!("pci block device queue interrupt");
BLOCK_DEVICE.lock().as_ref().unwrap().handle_irq()
BLOCK_DEVICE.lock().as_ref().unwrap().handle_irq();
}
virtio_device.register_interrupt_functions(&handle_block_device);
let blk_device = Mutex::new(match virtio_device.device {
jinux_virtio::device::VirtioDevice::Block(blk) => blk,
_ => {
panic!("Error when creating new block device, the input device is other type of virtio device");
}
});
Self {
blk_device,
common_cfg: virtio_device.common_cfg,
msix: virtio_device.msix,
}
let mut functions = Vec::new();
functions.push(handle_block_device);
virtio_device.register_queue_interrupt_functions(&mut functions);
Self { virtio_device }
}
}
@ -137,37 +69,38 @@ pub fn init(pci_device: Arc<PCIDevice>) {
let virtio_device = PCIVirtioDevice::new(pci_device);
let mut a = BLOCK_DEVICE.lock();
a.replace(VirtioBlockDevice::new(virtio_device));
let dev = a.as_ref().unwrap();
drop(a);
}
fn inner_block_device_test() {
let block_device = BLOCK_DEVICE.clone();
let mut write_buffer = [0u8; 512];
let mut read_buffer = [0u8; 512];
info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize);
info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize);
for i in 0..512 {
for byte in write_buffer.iter_mut() {
*byte = i as u8;
}
info!("write block");
block_device
.lock()
.as_ref()
.unwrap()
.write_block(i as usize, &write_buffer);
info!("read block");
block_device
.lock()
.as_ref()
.unwrap()
.read_block(i as usize, &mut read_buffer);
assert_eq!(write_buffer, read_buffer);
}
info!("block device test passed!");
}
#[allow(unused)]
pub fn block_device_test() {
fn inner_block_device_test() {
let block_device = BLOCK_DEVICE.clone();
let mut write_buffer = [0u8; 512];
let mut read_buffer = [0u8; 512];
info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize);
info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize);
for i in 0..512 {
for byte in write_buffer.iter_mut() {
*byte = i as u8;
}
block_device
.lock()
.as_ref()
.unwrap()
.write_block(i as usize, &write_buffer);
block_device
.lock()
.as_ref()
.unwrap()
.read_block(i as usize, &mut read_buffer);
assert_eq!(write_buffer, read_buffer);
}
info!("block device test passed!");
}
let test_process = Process::spawn_kernel_process(|| {
inner_block_device_test();
// inner_block_device_test();
});
}

View File

@ -0,0 +1,181 @@
use core::any::Any;
use core::sync::atomic::AtomicBool;
use alloc::collections::BTreeMap;
use alloc::{string::String, sync::Arc, vec::Vec};
use jinux_frame::{debug, info, offset_of, TrapFrame};
use jinux_pci::{msix::MSIX, PCIDevice};
use jinux_util::frame_ptr::InFramePtr;
use jinux_virtio::device::input::device::InputProp;
use jinux_virtio::VitrioPciCommonCfg;
use jinux_virtio::{
device::input::{device::InputDevice, InputConfigSelect},
PCIVirtioDevice,
};
use lazy_static::lazy_static;
use spin::Mutex;
use virtio_input_decoder::{DecodeType, Decoder};
pub trait INPUTDevice: Send + Sync + Any {
fn handle_irq(&self) -> Option<()>;
}
lazy_static! {
pub static ref KEYBOARD_EVENT: Mutex<Vec<DecodeType>> = Mutex::new(Vec::new());
pub static ref MOUSE_EVENT: Mutex<Vec<DecodeType>> = Mutex::new(Vec::new());
static ref KEYBOARD_CALLBACKS: Mutex<Vec<Arc<dyn Fn() + Send + Sync + 'static>>> =
Mutex::new(Vec::new());
static ref INPUT_DEVICE_LIST: Mutex<Vec<Arc<VirtioInputDevice>>> =
Mutex::new(Vec::with_capacity(2));
static ref INPUT_DEVICE_IRQ_HASH: Mutex<BTreeMap<u16, usize>> = Mutex::new(BTreeMap::new());
}
pub struct VirtioInputDevice {
input_device: InputDevice,
common_cfg: InFramePtr<VitrioPciCommonCfg>,
msix: Mutex<MSIX>,
is_keyboard: AtomicBool,
}
impl VirtioInputDevice {
fn new(virtio_device: PCIVirtioDevice, id: usize) -> Self {
let input_device = match virtio_device.device {
jinux_virtio::device::VirtioDevice::Input(dev) => dev,
_ => {
panic!("Error when creating new input device, the input device is other type of virtio device");
}
};
Self {
input_device,
common_cfg: virtio_device.common_cfg,
msix: Mutex::new(virtio_device.msix),
is_keyboard: AtomicBool::new(false),
}
}
fn register_interrupts(&self, id: usize) {
fn handle_input(frame: &TrapFrame) {
info!("in handle input");
let id = *INPUT_DEVICE_IRQ_HASH
.lock()
.get(&(frame.id as u16))
.expect("wrong irq number in input device trap handler");
INPUT_DEVICE_LIST
.lock()
.get(id)
.as_ref()
.unwrap()
.handle_irq();
}
fn config_space_change(frame: &TrapFrame) {}
let config_msix_vector =
self.common_cfg
.read_at(offset_of!(VitrioPciCommonCfg, config_msix_vector)) as usize;
let mut device_hash_lock = INPUT_DEVICE_IRQ_HASH.lock();
let mut msix = self.msix.lock();
for i in 0..msix.table_size as usize {
if i == config_msix_vector {
continue;
}
device_hash_lock.insert(msix.table.get(i).unwrap().irq_handle.num() as u16, id);
}
drop(device_hash_lock);
for i in 0..msix.table_size as usize {
let msix = msix.table.get_mut(i).unwrap();
if !msix.irq_handle.is_empty() {
panic!("function `register_queue_interrupt_functions` called more than one time");
}
if config_msix_vector == i {
msix.irq_handle.on_active(config_space_change);
} else {
msix.irq_handle.on_active(handle_input);
}
}
}
fn print_device_information(&self) {
let mut raw_name: [u8; 128] = [0; 128];
self.input_device
.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name);
let name = String::from_utf8(raw_name.to_vec()).unwrap();
info!("input device name:{}", name);
let mut prop: [u8; 128] = [0; 128];
self.input_device
.query_config_select(InputConfigSelect::PropBits, 0, &mut prop);
let input_prop = InputProp::from_bits(prop[0]).unwrap();
debug!("input device prop:{:?}", input_prop);
// if name.contains("Keyboard"){
// let mut raw_ev : [u8;128] = [0;128];
// let size = self.input_device.query_config_select(InputConfigSelect::EvBits, KEY, &mut raw_ev);
// info!("size:{}, raw_ev :{:x?}",size, raw_ev);
// }else{
// let mut raw_ev : [u8;128] = [0;128];
// let size = self.input_device.query_config_select(InputConfigSelect::EvBits, REL, &mut raw_ev);
// info!("size:{}, raw_ev :{:x?}",size, raw_ev);
// }
self.is_keyboard.store(
name.contains("Keyboard"),
core::sync::atomic::Ordering::Relaxed,
);
}
#[inline]
pub fn is_keyboard(&self) -> bool {
self.is_keyboard.load(core::sync::atomic::Ordering::Relaxed)
}
}
impl INPUTDevice for VirtioInputDevice {
fn handle_irq(&self) -> Option<()> {
let input = &self.input_device;
// one interrupt may contains serval input, so it should loop
loop {
let event = input.pop_pending_event()?;
let dtype = match Decoder::decode(
event.event_type as usize,
event.code as usize,
event.value as usize,
) {
Ok(dtype) => dtype,
Err(_) => return Some(()),
};
if self.is_keyboard() {
let mut lock = KEYBOARD_EVENT.lock();
lock.push(dtype);
drop(lock);
let lock = KEYBOARD_CALLBACKS.lock();
for callback in lock.iter() {
callback.call(());
}
} else {
let mut lock = MOUSE_EVENT.lock();
lock.push(dtype);
}
match dtype {
virtio_input_decoder::DecodeType::Key(key, r#type) => {
info!("{:?} {:?}", key, r#type);
}
virtio_input_decoder::DecodeType::Mouse(mouse) => info!("{:?}", mouse),
}
}
}
}
pub fn init(pci_device: Arc<PCIDevice>) {
let mut lock = INPUT_DEVICE_LIST.lock();
let id = lock.len();
let dev = Arc::new(VirtioInputDevice::new(PCIVirtioDevice::new(pci_device), id));
lock.push(dev.clone());
dev.register_interrupts(id);
drop(lock);
dev.print_device_information();
}
pub fn register_keyboard_callback(callback: Arc<dyn Fn() + 'static + Send + Sync>) {
KEYBOARD_CALLBACKS.lock().push(callback);
}

View File

@ -1,6 +1,7 @@
use core::any::Any;
pub mod block;
pub mod input;
pub trait BlockDevice: Send + Sync + Any {
fn read_block(&self, block_id: usize, buf: &mut [u8]);

View File

@ -14,6 +14,7 @@
// Since this is an incomplete feature, use this feature is unsafe.
// We should find a proper method to replace this feature with min_specialization, which is a sound feature.
#![feature(specialization)]
#![feature(fn_traits)]
use crate::{
prelude::*,
@ -47,8 +48,10 @@ pub mod vm;
extern crate pod_derive;
pub fn init() {
jinux_frame::disable_interrupts();
driver::init();
process::fifo_scheduler::init();
jinux_frame::enable_interrupts();
}
pub fn init_process() {

View File

@ -60,8 +60,6 @@ impl File for Tty {
if let Some(byte) = receive_char() {
self.ldisc.lock().push_char(byte);
return IoEvents::POLLIN;
} else {
return IoEvents::empty();
}
}
}

View File

@ -25,6 +25,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("[panic]:{:?}", info);
jinux_frame::panic_handler();
loop {}
}

View File

@ -0,0 +1,45 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(jinux_frame::test_runner)]
#![reexport_test_harness_main = "test_main"]
use bootloader::{entry_point, BootInfo};
extern crate alloc;
use alloc::sync::Arc;
use core::panic::PanicInfo;
use jinux_frame::println;
static mut INPUT_VALUE: u8 = 0;
entry_point!(kernel_test_main);
fn kernel_test_main(boot_info: &'static mut BootInfo) -> ! {
jinux_frame::init(boot_info);
jinux_std::driver::console::init();
test_main();
loop {}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
jinux_frame::test_panic_handler(info)
}
#[test_case]
fn test_input() {
jinux_frame::enable_interrupts();
println!("please input value into console to pass this test");
jinux_std::driver::console::register_console_callback(Arc::new(input_callback));
unsafe {
while INPUT_VALUE == 0 {
jinux_frame::hlt();
}
println!("input value:{}", INPUT_VALUE);
}
}
pub fn input_callback(input: u8) {
unsafe {
INPUT_VALUE = input;
}
}

View File

@ -28,10 +28,7 @@ fn panic(info: &PanicInfo) -> ! {
#[test_case]
fn test_timer() {
println!(
"If you want to pass this test, you may need to enable the interrupt in jinux_frame/lib.rs"
);
println!("make sure the Timer irq number 32 handler won't panic");
jinux_frame::enable_interrupts();
unsafe {
let timer = Timer::new(timer_callback).unwrap();
timer.set(Duration::from_secs(1));