diff --git a/src/Cargo.lock b/src/Cargo.lock index c98842a7..f4bf0ebf 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -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" diff --git a/src/framework/jinux-frame/Cargo.toml b/src/framework/jinux-frame/Cargo.toml index fed9a932..69fe57cc 100644 --- a/src/framework/jinux-frame/Cargo.toml +++ b/src/framework/jinux-frame/Cargo.toml @@ -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" diff --git a/src/framework/jinux-frame/src/config.rs b/src/framework/jinux-frame/src/config.rs index f62b6c64..b7922b83 100644 --- a/src/framework/jinux-frame/src/config.rs +++ b/src/framework/jinux-frame/src/config.rs @@ -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; diff --git a/src/framework/jinux-frame/src/device/console.rs b/src/framework/jinux-frame/src/device/console.rs new file mode 100644 index 00000000..33bba592 --- /dev/null +++ b/src/framework/jinux-frame/src/device/console.rs @@ -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 = + 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(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 { + 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)+)?)) + } +} diff --git a/src/framework/jinux-frame/src/device/mod.rs b/src/framework/jinux-frame/src/device/mod.rs index d8a0dbfa..0861faa7 100644 --- a/src/framework/jinux-frame/src/device/mod.rs +++ b/src/framework/jinux-frame/src/device/mod.rs @@ -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| {}); } diff --git a/src/framework/jinux-frame/src/device/serial.rs b/src/framework/jinux-frame/src/device/serial.rs deleted file mode 100644 index 764016a2..00000000 --- a/src/framework/jinux-frame/src/device/serial.rs +++ /dev/null @@ -1,47 +0,0 @@ -use lazy_static::lazy_static; -use spin::Mutex; -use uart_16550::SerialPort; - -lazy_static! { - pub static ref SERIAL: Mutex = { - 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 { - 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)*)); -} diff --git a/src/framework/jinux-frame/src/driver/acpi.rs b/src/framework/jinux-frame/src/driver/acpi.rs new file mode 100644 index 00000000..d191d5d3 --- /dev/null +++ b/src/framework/jinux-frame/src/driver/acpi.rs @@ -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> = unsafe{ + Mutex::new(core::mem::MaybeUninit::zeroed().assume_init()) + }; +} + +#[derive(Debug, Clone)] +pub struct AcpiMemoryHandler {} + +impl AcpiHandler for AcpiMemoryHandler { + unsafe fn map_physical_region( + &self, + physical_address: usize, + size: usize, + ) -> acpi::PhysicalMapping { + acpi::PhysicalMapping::new( + physical_address, + NonNull::new(phys_to_virt(physical_address) as *mut T).unwrap(), + size, + size, + self.clone(), + ) + } + + fn unmap_physical_region(region: &acpi::PhysicalMapping) {} +} + +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"); +} diff --git a/src/framework/jinux-frame/src/driver/apic.rs b/src/framework/jinux-frame/src/driver/apic.rs new file mode 100644 index 00000000..d2ec5a8d --- /dev/null +++ b/src/framework/jinux-frame/src/driver/apic.rs @@ -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 = 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 +} diff --git a/src/framework/jinux-frame/src/driver/ioapic.rs b/src/framework/jinux-frame/src/driver/ioapic.rs new file mode 100644 index 00000000..49f030d3 --- /dev/null +++ b/src/framework/jinux-frame/src/driver/ioapic.rs @@ -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 = + 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 { + 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; +} diff --git a/src/framework/jinux-frame/src/driver/mod.rs b/src/framework/jinux-frame/src/driver/mod.rs new file mode 100644 index 00000000..399e830d --- /dev/null +++ b/src/framework/jinux-frame/src/driver/mod.rs @@ -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) { + 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(); +} diff --git a/src/framework/jinux-frame/src/driver/pic.rs b/src/framework/jinux-frame/src/driver/pic.rs new file mode 100644 index 00000000..45893270 --- /dev/null +++ b/src/framework/jinux-frame/src/driver/pic.rs @@ -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> = 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 { + 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); +} diff --git a/src/framework/jinux-frame/src/driver/timer/apic.rs b/src/framework/jinux-frame/src/driver/timer/apic.rs new file mode 100644 index 00000000..b32bc0f0 --- /dev/null +++ b/src/framework/jinux-frame/src/driver/timer/apic.rs @@ -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; + } + } +} diff --git a/src/framework/jinux-frame/src/driver/timer/hpet.rs b/src/framework/jinux-frame/src/driver/timer/hpet.rs new file mode 100644 index 00000000..6d6c2bee --- /dev/null +++ b/src/framework/jinux-frame/src/driver/timer/hpet.rs @@ -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 = + 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>, +} + +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(()) +} diff --git a/src/framework/jinux-frame/src/device/pic.rs b/src/framework/jinux-frame/src/driver/timer/mod.rs similarity index 59% rename from src/framework/jinux-frame/src/device/pic.rs rename to src/framework/jinux-frame/src/driver/timer/mod.rs index d9a7819e..df7314ab 100644 --- a/src/framework/jinux-frame/src/device/pic.rs +++ b/src/framework/jinux-frame/src/driver/timer/mod.rs @@ -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> = 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! { diff --git a/src/framework/jinux-frame/src/driver/timer/pit.rs b/src/framework/jinux-frame/src/driver/timer/pit.rs new file mode 100644 index 00000000..3065cf34 --- /dev/null +++ b/src/framework/jinux-frame/src/driver/timer/pit.rs @@ -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 _); +} diff --git a/src/framework/jinux-frame/src/lib.rs b/src/framework/jinux-frame/src/lib.rs index 630a6e4b..c4a312da 100644 --- a/src/framework/jinux-frame/src/lib.rs +++ b/src/framework/jinux-frame/src/lib.rs @@ -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 = 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::()); + console_print!("{}...\n", core::any::type_name::()); 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 { diff --git a/src/framework/jinux-frame/src/log.rs b/src/framework/jinux-frame/src/log.rs index 4ff1f49d..62dc2817 100644 --- a/src/framework/jinux-frame/src/log.rs +++ b/src/framework/jinux-frame/src/log.rs @@ -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. diff --git a/src/framework/jinux-frame/src/mm/mod.rs b/src/framework/jinux-frame/src/mm/mod.rs index be548c59..f38202ef 100644 --- a/src/framework/jinux-frame/src/mm/mod.rs +++ b/src/framework/jinux-frame/src/mm/mod.rs @@ -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, + ) } diff --git a/src/framework/jinux-frame/src/mm/page_table.rs b/src/framework/jinux-frame/src/mm/page_table.rs index 795e23cd..96835d62 100644 --- a/src/framework/jinux-frame/src/mm/page_table.rs +++ b/src/framework/jinux-frame/src/mm/page_table.rs @@ -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]); } } diff --git a/src/framework/jinux-frame/src/timer.rs b/src/framework/jinux-frame/src/timer.rs index a30d0332..67b45709 100644 --- a/src/framework/jinux-frame/src/timer.rs +++ b/src/framework/jinux-frame/src/timer.rs @@ -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, diff --git a/src/framework/jinux-frame/src/trap/handler.rs b/src/framework/jinux-frame/src/trap/handler.rs index b7ba5308..48d6bbbb 100644 --- a/src/framework/jinux-frame/src/trap/handler.rs +++ b/src/framework/jinux-frame/src/trap/handler.rs @@ -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 { diff --git a/src/framework/jinux-frame/src/trap/mod.rs b/src/framework/jinux-frame/src/trap/mod.rs index 6643b4ea..ffdedecd 100644 --- a/src/framework/jinux-frame/src/trap/mod.rs +++ b/src/framework/jinux-frame/src/trap/mod.rs @@ -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 { diff --git a/src/framework/jinux-frame/src/user.rs b/src/framework/jinux-frame/src/user.rs index 899e72d1..27d4ebec 100644 --- a/src/framework/jinux-frame/src/user.rs +++ b/src/framework/jinux-frame/src/user.rs @@ -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; } diff --git a/src/framework/jinux-frame/src/util/recycle_allocator.rs b/src/framework/jinux-frame/src/util/recycle_allocator.rs index 8d1932bd..37077472 100644 --- a/src/framework/jinux-frame/src/util/recycle_allocator.rs +++ b/src/framework/jinux-frame/src/util/recycle_allocator.rs @@ -1,5 +1,6 @@ use alloc::vec::Vec; +#[derive(Debug)] pub struct RecycleAllocator { current: usize, recycled: Vec, diff --git a/src/framework/jinux-frame/src/x86_64_util.rs b/src/framework/jinux-frame/src/x86_64_util.rs index 1ad85820..5bb9b0f1 100644 --- a/src/framework/jinux-frame/src/x86_64_util.rs +++ b/src/framework/jinux-frame/src/x86_64_util.rs @@ -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 { diff --git a/src/jinux-boot/src/main.rs b/src/jinux-boot/src/main.rs index 40f4803b..2b479898 100644 --- a/src/jinux-boot/src/main.rs +++ b/src/jinux-boot/src/main.rs @@ -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", diff --git a/src/services/comps/jinux-pci/src/lib.rs b/src/services/comps/jinux-pci/src/lib.rs index 856a7be7..e25970e1 100644 --- a/src/services/comps/jinux-pci/src/lib.rs +++ b/src/services/comps/jinux-pci/src/lib.rs @@ -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 ); diff --git a/src/services/comps/jinux-pci/src/msix.rs b/src/services/comps/jinux-pci/src/msix.rs index a34125ee..0d08a8ef 100644 --- a/src/services/comps/jinux-pci/src/msix.rs +++ b/src/services/comps/jinux-pci/src/msix.rs @@ -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 = - 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 } } diff --git a/src/services/comps/jinux-pci/src/util.rs b/src/services/comps/jinux-pci/src/util.rs index 06315cf6..117e5028 100644 --- a/src/services/comps/jinux-pci/src/util.rs +++ b/src/services/comps/jinux-pci/src/util.rs @@ -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, } +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), } diff --git a/src/services/comps/jinux-virtio/src/device/block/device.rs b/src/services/comps/jinux-virtio/src/device/block/device.rs new file mode 100644 index 00000000..673d42f6 --- /dev/null +++ b/src/services/comps/jinux-virtio/src/device/block/device.rs @@ -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, + 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; 6], + common_cfg: &InFramePtr, + notify_base_address: usize, + notify_off_multiplier: u32, + mut msix_vector_left: Vec, + ) -> Result { + 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(); + } +} diff --git a/src/services/comps/jinux-virtio/src/block.rs b/src/services/comps/jinux-virtio/src/device/block/mod.rs similarity index 53% rename from src/services/comps/jinux-virtio/src/block.rs rename to src/services/comps/jinux-virtio/src/device/block/mod.rs index ccbd3396..635e14fe 100644 --- a/src/services/comps/jinux-virtio/src/block.rs +++ b/src/services/comps/jinux-virtio/src/device/block/mod.rs @@ -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 { diff --git a/src/services/comps/jinux-virtio/src/device/input/device.rs b/src/services/comps/jinux-virtio/src/device/input/device.rs new file mode 100644 index 00000000..2f870653 --- /dev/null +++ b/src/services/comps/jinux-virtio/src/device/input/device.rs @@ -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, + event_queue: Mutex, + status_queue: VirtQueue, + pub event_buf: Mutex>, +} + +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; 6], + common_cfg: &InFramePtr, + notify_base_address: usize, + notify_off_multiplier: u32, + mut msix_vector_left: Vec, + ) -> Result { + 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 { + 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 + } +} diff --git a/src/services/comps/jinux-virtio/src/device/input/mod.rs b/src/services/comps/jinux-virtio/src/device/input/mod.rs new file mode 100644 index 00000000..06ceca1c --- /dev/null +++ b/src/services/comps/jinux-virtio/src/device/input/mod.rs @@ -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; 6]) -> InFramePtr { + 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; diff --git a/src/services/comps/jinux-virtio/src/device/mod.rs b/src/services/comps/jinux-virtio/src/device/mod.rs new file mode 100644 index 00000000..1b59dd29 --- /dev/null +++ b/src/services/comps/jinux-virtio/src/device/mod.rs @@ -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, + pub device_cap_cfg: CapabilityVirtioData, +} + +impl VirtioInfo { + pub(crate) fn new( + device_type: VirtioDeviceType, + bars: [Option; 6], + virtio_cap_list: Vec<&Capability>, + ) -> Result { + 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; 6], + msix_vector_left: Vec, + ) -> Result { + 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) + } +} diff --git a/src/services/comps/jinux-virtio/src/lib.rs b/src/services/comps/jinux-virtio/src/lib.rs index 3a6197b5..fe0b1bb6 100644 --- a/src/services/comps/jinux-virtio/src/lib.rs +++ b/src/services/comps/jinux-virtio/src/lib.rs @@ -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), - Console, - Entropy, - TraditionalMemoryBalloon, - ScsiHost, - GPU, - Input, - Crypto, - Socket, - Unknown, -} - pub struct PCIVirtioDevice { /// common config of one device - common_cfg: InFramePtr, - device: VirtioDevice, - queues: Vec>>, - msix: MSIX, + pub common_cfg: InFramePtr, + 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) -> 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 = 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 = (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 { - 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(&mut self, functions: &mut Vec) + /// register all the interrupt functions except the config change, this function should call only once + pub fn register_interrupt_functions(&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 - } - } } diff --git a/src/services/comps/jinux-virtio/src/queue.rs b/src/services/comps/jinux-virtio/src/queue.rs index 3e235189..bf875789 100644 --- a/src/services/comps/jinux-virtio/src/queue.rs +++ b/src/services/comps/jinux-virtio/src/queue.rs @@ -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, } impl VirtQueue { @@ -49,26 +56,69 @@ impl VirtQueue { cfg: &InFramePtr, idx: usize, size: u16, - cap_offset: usize, + notify_base_address: usize, notify_off_multiplier: u32, msix_vector: u16, ) -> Result { 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, 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 } diff --git a/src/services/libs/jinux-std/Cargo.toml b/src/services/libs/jinux-std/Cargo.toml index 23626473..716e1382 100644 --- a/src/services/libs/jinux-std/Cargo.toml +++ b/src/services/libs/jinux-std/Cargo.toml @@ -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" diff --git a/src/services/libs/jinux-std/src/driver/console.rs b/src/services/libs/jinux-std/src/driver/console.rs new file mode 100644 index 00000000..1f85e500 --- /dev/null +++ b/src/services/libs/jinux-std/src/driver/console.rs @@ -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>> = + 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) { + KEYBOARD_CALLBACKS.lock().push(callback); +} diff --git a/src/services/libs/jinux-std/src/driver/mod.rs b/src/services/libs/jinux-std/src/driver/mod.rs index 0546e9fa..9fdcc109 100644 --- a/src/services/libs/jinux-std/src/driver/mod.rs +++ b/src/services/libs/jinux-std/src/driver/mod.rs @@ -1,5 +1,7 @@ +pub mod console; pub mod pci; pub fn init() { pci::init(); + console::init(); } diff --git a/src/services/libs/jinux-std/src/driver/pci/mod.rs b/src/services/libs/jinux-std/src/driver/pci/mod.rs index ca2290a1..b1314a9b 100644 --- a/src/services/libs/jinux-std/src/driver/pci/mod.rs +++ b/src/services/libs/jinux-std/src/driver/pci/mod.rs @@ -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"); } diff --git a/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs b/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs index bec9f748..5ebf816c 100644 --- a/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs +++ b/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs @@ -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, + pub common_cfg: InFramePtr, + msix: MSIX, } lazy_static! { @@ -68,52 +25,19 @@ lazy_static! { pub static ref BLOCK_DEVICE: Arc>> = 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) { 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(); }); } diff --git a/src/services/libs/jinux-std/src/driver/pci/virtio/input.rs b/src/services/libs/jinux-std/src/driver/pci/virtio/input.rs new file mode 100644 index 00000000..0336830d --- /dev/null +++ b/src/services/libs/jinux-std/src/driver/pci/virtio/input.rs @@ -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> = Mutex::new(Vec::new()); + pub static ref MOUSE_EVENT: Mutex> = Mutex::new(Vec::new()); + static ref KEYBOARD_CALLBACKS: Mutex>> = + Mutex::new(Vec::new()); + static ref INPUT_DEVICE_LIST: Mutex>> = + Mutex::new(Vec::with_capacity(2)); + static ref INPUT_DEVICE_IRQ_HASH: Mutex> = Mutex::new(BTreeMap::new()); +} + +pub struct VirtioInputDevice { + input_device: InputDevice, + common_cfg: InFramePtr, + msix: Mutex, + 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) { + 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) { + KEYBOARD_CALLBACKS.lock().push(callback); +} diff --git a/src/services/libs/jinux-std/src/driver/pci/virtio/mod.rs b/src/services/libs/jinux-std/src/driver/pci/virtio/mod.rs index 2aa917bd..ab9270ce 100644 --- a/src/services/libs/jinux-std/src/driver/pci/virtio/mod.rs +++ b/src/services/libs/jinux-std/src/driver/pci/virtio/mod.rs @@ -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]); diff --git a/src/services/libs/jinux-std/src/lib.rs b/src/services/libs/jinux-std/src/lib.rs index c452d25d..1c769193 100644 --- a/src/services/libs/jinux-std/src/lib.rs +++ b/src/services/libs/jinux-std/src/lib.rs @@ -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() { diff --git a/src/services/libs/jinux-std/src/tty/mod.rs b/src/services/libs/jinux-std/src/tty/mod.rs index f27db42f..28c08a05 100644 --- a/src/services/libs/jinux-std/src/tty/mod.rs +++ b/src/services/libs/jinux-std/src/tty/mod.rs @@ -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(); } } } diff --git a/src/src/main.rs b/src/src/main.rs index a3a68486..e4d0df13 100644 --- a/src/src/main.rs +++ b/src/src/main.rs @@ -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 {} } diff --git a/src/tests/console_input.rs b/src/tests/console_input.rs new file mode 100644 index 00000000..fd793e78 --- /dev/null +++ b/src/tests/console_input.rs @@ -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; + } +} diff --git a/src/tests/timer_test.rs b/src/tests/timer_test.rs index 4eb58f70..efc6023c 100644 --- a/src/tests/timer_test.rs +++ b/src/tests/timer_test.rs @@ -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));