mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
refactor virtio code; APIC , console interrupt support
This commit is contained in:
parent
1d401fd8fc
commit
46497554d2
43
src/Cargo.lock
generated
43
src/Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
111
src/framework/jinux-frame/src/device/console.rs
Normal file
111
src/framework/jinux-frame/src/device/console.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::{cell::Cell, driver::pic, x86_64_util::*, IrqAllocateHandle, TrapFrame};
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct LineSts: u8 {
|
||||
const INPUT_FULL = 1;
|
||||
const OUTPUT_EMPTY = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
/// A port-mapped UART. Copied from uart_16550.
|
||||
const SERIAL_DATA: u16 = 0x3F8;
|
||||
const SERIAL_INT_EN: u16 = SERIAL_DATA + 1;
|
||||
const SERIAL_FIFO_CTRL: u16 = SERIAL_DATA + 2;
|
||||
const SERIAL_LINE_CTRL: u16 = SERIAL_DATA + 3;
|
||||
const SERIAL_MODEM_CTRL: u16 = SERIAL_DATA + 4;
|
||||
const SERIAL_LINE_STS: u16 = SERIAL_DATA + 5;
|
||||
lazy_static! {
|
||||
static ref CONSOLE_IRQ_CALLBACK: Cell<IrqAllocateHandle> =
|
||||
Cell::new(pic::allocate_irq(4).unwrap());
|
||||
}
|
||||
|
||||
/// Initializes the serial port.
|
||||
pub(crate) fn init() {
|
||||
// Disable interrupts
|
||||
out8(SERIAL_INT_EN, 0x00);
|
||||
// Enable DLAB
|
||||
out8(SERIAL_LINE_CTRL, 0x80);
|
||||
// Set maximum speed to 38400 bps by configuring DLL and DLM
|
||||
out8(SERIAL_DATA, 0x03);
|
||||
out8(SERIAL_INT_EN, 0x00);
|
||||
// Disable DLAB and set data word length to 8 bits
|
||||
out8(SERIAL_LINE_CTRL, 0x03);
|
||||
// Enable FIFO, clear TX/RX queues and
|
||||
// set interrupt watermark at 14 bytes
|
||||
out8(SERIAL_FIFO_CTRL, 0xC7);
|
||||
// Mark data terminal ready, signal request to send
|
||||
// and enable auxilliary output #2 (used as interrupt line for CPU)
|
||||
out8(SERIAL_MODEM_CTRL, 0x0B);
|
||||
// Enable interrupts
|
||||
out8(SERIAL_INT_EN, 0x01);
|
||||
}
|
||||
|
||||
pub fn register_console_input_callback<F>(callback: F)
|
||||
where
|
||||
F: Fn(&TrapFrame) + Sync + Send + 'static,
|
||||
{
|
||||
CONSOLE_IRQ_CALLBACK.get().on_active(callback);
|
||||
}
|
||||
|
||||
fn line_sts() -> LineSts {
|
||||
LineSts::from_bits_truncate(in8(SERIAL_LINE_STS))
|
||||
}
|
||||
|
||||
/// Sends a byte on the serial port.
|
||||
pub fn send(data: u8) {
|
||||
match data {
|
||||
8 | 0x7F => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
out8(SERIAL_DATA, 8);
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
out8(SERIAL_DATA, b' ');
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
out8(SERIAL_DATA, 8)
|
||||
}
|
||||
_ => {
|
||||
while !line_sts().contains(LineSts::OUTPUT_EMPTY) {}
|
||||
out8(SERIAL_DATA, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a byte on the serial port. non-blocking
|
||||
pub fn receive_char() -> Option<u8> {
|
||||
if line_sts().contains(LineSts::INPUT_FULL) {
|
||||
Some(in8(SERIAL_DATA))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for &c in s.as_bytes() {
|
||||
send(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! console_print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::device::console::print(format_args!($fmt $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! console_println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::device::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
|
||||
}
|
||||
}
|
@ -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| {});
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use uart_16550::SerialPort;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SERIAL: Mutex<SerialPort> = {
|
||||
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
|
||||
serial_port.init();
|
||||
Mutex::new(serial_port)
|
||||
};
|
||||
}
|
||||
|
||||
/// read a char from the keyboard input.
|
||||
/// FIXME: this function should **NOT** block. If no char receives, this function should return None immediately.
|
||||
/// However, the receive function on SERIAL will block until a char is received, which will block the whole kernel.
|
||||
/// A more correct implementation should be added once interrupt is ready. We should register the kerboard interrupt
|
||||
/// handler to wake up foreground processes which wait on IOEVENTS.
|
||||
pub fn receive_char() -> Option<u8> {
|
||||
let byte = SERIAL.lock().receive();
|
||||
Some(byte)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn _print(args: ::core::fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
SERIAL
|
||||
.lock()
|
||||
.write_fmt(args)
|
||||
.expect("Printing to serial failed");
|
||||
}
|
||||
|
||||
/// Prints to the host through the serial interface.
|
||||
#[macro_export]
|
||||
macro_rules! serial_print {
|
||||
($($arg:tt)*) => {
|
||||
$crate::device::serial::_print(format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
/// Prints to the host through the serial interface, appending a newline.
|
||||
#[macro_export]
|
||||
macro_rules! serial_println {
|
||||
() => ($crate::serial_print!("\n"));
|
||||
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
|
||||
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
|
||||
concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
45
src/framework/jinux-frame/src/driver/acpi.rs
Normal file
45
src/framework/jinux-frame/src/driver/acpi.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use crate::{info, mm::address::phys_to_virt};
|
||||
use acpi::{AcpiHandler, AcpiTables};
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
/// RSDP information, key is the signature, value is the virtual address of the signature
|
||||
pub(crate) static ref ACPI_TABLES : Mutex<AcpiTables<AcpiMemoryHandler>> = unsafe{
|
||||
Mutex::new(core::mem::MaybeUninit::zeroed().assume_init())
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AcpiMemoryHandler {}
|
||||
|
||||
impl AcpiHandler for AcpiMemoryHandler {
|
||||
unsafe fn map_physical_region<T>(
|
||||
&self,
|
||||
physical_address: usize,
|
||||
size: usize,
|
||||
) -> acpi::PhysicalMapping<Self, T> {
|
||||
acpi::PhysicalMapping::new(
|
||||
physical_address,
|
||||
NonNull::new(phys_to_virt(physical_address) as *mut T).unwrap(),
|
||||
size,
|
||||
size,
|
||||
self.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn unmap_physical_region<T>(region: &acpi::PhysicalMapping<Self, T>) {}
|
||||
}
|
||||
|
||||
pub fn init(rsdp: u64) {
|
||||
let a = unsafe { AcpiTables::from_rsdp(AcpiMemoryHandler {}, rsdp as usize).unwrap() };
|
||||
*ACPI_TABLES.lock() = a;
|
||||
|
||||
let c = ACPI_TABLES.lock();
|
||||
for (signature, sdt) in c.sdts.iter() {
|
||||
info!("ACPI found signature:{:?}", signature);
|
||||
}
|
||||
info!("acpi init complete");
|
||||
}
|
210
src/framework/jinux-frame/src/driver/apic.rs
Normal file
210
src/framework/jinux-frame/src/driver/apic.rs
Normal file
@ -0,0 +1,210 @@
|
||||
use crate::{cell::Cell, debug, x86_64_util};
|
||||
use lazy_static::lazy_static;
|
||||
use volatile::{
|
||||
access::{ReadOnly, ReadWrite, WriteOnly},
|
||||
Volatile,
|
||||
};
|
||||
|
||||
pub(crate) const IA32_APIC_BASE_MSR: u32 = 0x1B;
|
||||
pub(crate) const IA32_APIC_BASE_MSR_BSP: u32 = 0x100; // Processor is a BSP
|
||||
pub(crate) const IA32_APIC_BASE_MSR_ENABLE: u32 = 0x800;
|
||||
|
||||
const APIC_LVT_MASK_BITS: u32 = 1 << 16;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref APIC_INSTANCE: Cell<APIC> = Cell::new(APIC::new());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct APIC {
|
||||
local_apic_id_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
local_apic_version_register: Volatile<&'static u32, ReadOnly>,
|
||||
|
||||
task_priority_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
arbitration_priority_register: Volatile<&'static u32, ReadOnly>,
|
||||
processor_priority_register: Volatile<&'static u32, ReadOnly>,
|
||||
pub eoi_register: Volatile<&'static mut u32, WriteOnly>,
|
||||
remote_read_register: Volatile<&'static u32, ReadOnly>,
|
||||
logical_destination_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
destination_format_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
spurious_interrupt_vector_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
|
||||
/// total 256 bits, 32 bits per element
|
||||
isr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8],
|
||||
|
||||
/// total 256 bits, 32 bits per element
|
||||
tmr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8],
|
||||
|
||||
/// total 256 bits, 32 bits per element
|
||||
irr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8],
|
||||
|
||||
pub error_status_register: Volatile<&'static u32, ReadOnly>,
|
||||
|
||||
lvt_cmci_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
icr_bits_31_0: Volatile<&'static mut u32, ReadWrite>,
|
||||
icr_bits_63_32: Volatile<&'static mut u32, ReadWrite>,
|
||||
pub lvt_timer_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
lvt_thermal_sensor_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
lvt_performance_monitoring_counters_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
lvt_lint0_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
lvt_lint1_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
lvt_error_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
pub initial_count_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
pub current_count_register: Volatile<&'static u32, ReadOnly>,
|
||||
|
||||
pub divide_configuration_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
}
|
||||
|
||||
impl APIC {
|
||||
pub fn new() -> Self {
|
||||
let base_address = get_apic_base_address();
|
||||
|
||||
let local_apic_id_register = Self::new_read_write_volatile(base_address + 0x0020);
|
||||
let local_apic_version_register = Self::new_read_only_volatile(base_address + 0x0030);
|
||||
|
||||
let task_priority_register = Self::new_read_write_volatile(base_address + 0x0080);
|
||||
let arbitration_priority_register = Self::new_read_only_volatile(base_address + 0x0090);
|
||||
let processor_priority_register = Self::new_read_only_volatile(base_address + 0x00A0);
|
||||
let eoi_register = Self::new_write_only_volatile(base_address + 0x00B0);
|
||||
let remote_read_register = Self::new_read_only_volatile(base_address + 0x00C0);
|
||||
let logical_destination_register = Self::new_read_write_volatile(base_address + 0x00D0);
|
||||
let destination_format_register = Self::new_read_write_volatile(base_address + 0x00E0);
|
||||
let spurious_interrupt_vector_register =
|
||||
Self::new_read_write_volatile(base_address + 0x00F0);
|
||||
|
||||
let mut isr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8] =
|
||||
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
|
||||
for i in 0..8 {
|
||||
isr_per_32_bits[i] = Self::new_read_only_volatile(base_address + 0x0100 + i * 0x0010);
|
||||
}
|
||||
|
||||
let mut tmr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8] =
|
||||
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
|
||||
for i in 0..8 {
|
||||
tmr_per_32_bits[i] = Self::new_read_only_volatile(base_address + 0x0180 + i * 0x0010);
|
||||
}
|
||||
|
||||
let mut irr_per_32_bits: [Volatile<&'static u32, ReadOnly>; 8] =
|
||||
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
|
||||
for i in 0..8 {
|
||||
irr_per_32_bits[i] = Self::new_read_only_volatile(base_address + 0x0200 + i * 0x0010);
|
||||
}
|
||||
|
||||
let error_status_register = Self::new_read_only_volatile(base_address + 0x0280);
|
||||
|
||||
let lvt_cmci_register = Self::new_read_write_volatile(base_address + 0x02F0);
|
||||
let icr_bits_31_0 = Self::new_read_write_volatile(base_address + 0x0300);
|
||||
let icr_bits_63_32 = Self::new_read_write_volatile(base_address + 0x0310);
|
||||
let lvt_timer_register = Self::new_read_write_volatile(base_address + 0x0320);
|
||||
let lvt_thermal_sensor_register = Self::new_read_write_volatile(base_address + 0x0330);
|
||||
let lvt_performance_monitoring_counters_register =
|
||||
Self::new_read_write_volatile(base_address + 0x0340);
|
||||
let lvt_lint0_register = Self::new_read_write_volatile(base_address + 0x0350);
|
||||
let lvt_lint1_register = Self::new_read_write_volatile(base_address + 0x0360);
|
||||
let lvt_error_register = Self::new_read_write_volatile(base_address + 0x0370);
|
||||
let initial_count_register = Self::new_read_write_volatile(base_address + 0x0380);
|
||||
let current_count_register = Self::new_read_only_volatile(base_address + 0x0390);
|
||||
|
||||
let divide_configuration_register = Self::new_read_write_volatile(base_address + 0x03E0);
|
||||
|
||||
Self {
|
||||
local_apic_id_register,
|
||||
local_apic_version_register,
|
||||
task_priority_register,
|
||||
arbitration_priority_register,
|
||||
processor_priority_register,
|
||||
eoi_register,
|
||||
remote_read_register,
|
||||
logical_destination_register,
|
||||
destination_format_register,
|
||||
spurious_interrupt_vector_register,
|
||||
isr_per_32_bits,
|
||||
tmr_per_32_bits,
|
||||
irr_per_32_bits,
|
||||
error_status_register,
|
||||
lvt_cmci_register,
|
||||
icr_bits_31_0,
|
||||
icr_bits_63_32,
|
||||
lvt_timer_register,
|
||||
lvt_thermal_sensor_register,
|
||||
lvt_performance_monitoring_counters_register,
|
||||
lvt_lint0_register,
|
||||
lvt_lint1_register,
|
||||
lvt_error_register,
|
||||
initial_count_register,
|
||||
current_count_register,
|
||||
divide_configuration_register,
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn new_read_only_volatile(pa: usize) -> Volatile<&'static u32, ReadOnly> {
|
||||
Volatile::new_read_only(Self::convert_pa_to_u32_ref(pa))
|
||||
}
|
||||
#[inline(always)]
|
||||
fn new_read_write_volatile(pa: usize) -> Volatile<&'static mut u32, ReadWrite> {
|
||||
Volatile::new(Self::convert_pa_to_u32_ref(pa))
|
||||
}
|
||||
#[inline(always)]
|
||||
fn new_write_only_volatile(pa: usize) -> Volatile<&'static mut u32, WriteOnly> {
|
||||
Volatile::new_write_only(Self::convert_pa_to_u32_ref(pa))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn convert_pa_to_u32_ref(pa: usize) -> &'static mut u32 {
|
||||
unsafe { &mut *(crate::mm::address::phys_to_virt(pa) as *mut usize as *mut u32) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_apic() -> bool {
|
||||
let value = unsafe { x86_64_util::cpuid(1) };
|
||||
value.edx & 0x100 != 0
|
||||
}
|
||||
|
||||
pub(crate) fn init() {
|
||||
super::pic::disable_temp();
|
||||
|
||||
let apic_lock = APIC_INSTANCE.get();
|
||||
// enable apic
|
||||
set_apic_base_address(get_apic_base_address());
|
||||
let spurious = apic_lock.spurious_interrupt_vector_register.read();
|
||||
apic_lock
|
||||
.spurious_interrupt_vector_register
|
||||
.write(spurious | (0x100));
|
||||
let apic_id = apic_lock.local_apic_id_register.read() >> 24;
|
||||
let apic_ver = apic_lock.local_apic_version_register.read();
|
||||
|
||||
debug!(
|
||||
"APIC ID:{:x}, Version:{:x}, Max LVT:{:x}",
|
||||
apic_id,
|
||||
apic_ver & 0xff,
|
||||
(apic_ver >> 16) & 0xff
|
||||
);
|
||||
|
||||
debug!(
|
||||
"LDR:{:x}, DFR:{:x}",
|
||||
apic_lock.logical_destination_register.read(),
|
||||
apic_lock.destination_format_register.read()
|
||||
);
|
||||
debug!("spurious:{:x}", spurious);
|
||||
|
||||
drop(apic_lock);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn ack() {
|
||||
let lock = APIC_INSTANCE.get();
|
||||
lock.eoi_register.write(0);
|
||||
}
|
||||
|
||||
/// set APIC base address and enable it
|
||||
fn set_apic_base_address(address: usize) {
|
||||
x86_64_util::set_msr(
|
||||
IA32_APIC_BASE_MSR,
|
||||
address | IA32_APIC_BASE_MSR_ENABLE as usize,
|
||||
)
|
||||
}
|
||||
|
||||
/// get APIC base address
|
||||
fn get_apic_base_address() -> usize {
|
||||
x86_64_util::get_msr(IA32_APIC_BASE_MSR) & 0xf_ffff_f000
|
||||
}
|
153
src/framework/jinux-frame/src/driver/ioapic.rs
Normal file
153
src/framework/jinux-frame/src/driver/ioapic.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use acpi::PlatformInfo;
|
||||
|
||||
use super::acpi::ACPI_TABLES;
|
||||
use crate::cell::Cell;
|
||||
use crate::debug;
|
||||
use crate::mm::address::phys_to_virt;
|
||||
use crate::util::recycle_allocator::RecycleAllocator;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref IO_APIC: Cell<IoApic> =
|
||||
unsafe { Cell::new(core::mem::MaybeUninit::zeroed().assume_init()) };
|
||||
}
|
||||
const IOAPICID: u32 = 0x00;
|
||||
const IOAPICVER: u32 = 0x01;
|
||||
const IOAPICARB: u32 = 0x02;
|
||||
|
||||
const fn IoApicRedtbl(index: u8) -> u32 {
|
||||
0x10 + 2 * index as u32
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct IoApicRegister {
|
||||
address: u32,
|
||||
reserved: [u8; 0x10 - 0x04],
|
||||
data: u32,
|
||||
}
|
||||
impl IoApicRegister {
|
||||
pub fn read(self: &mut Self, reg: u32) -> u32 {
|
||||
self.address = reg & 0xff;
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn write(self: &mut Self, reg: u32, value: u32) {
|
||||
self.address = reg & 0xff;
|
||||
self.data = value;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IoApicEntryHandle {
|
||||
index: u8,
|
||||
}
|
||||
|
||||
impl IoApicEntryHandle {
|
||||
pub fn read(&mut self) -> u64 {
|
||||
let io_apic = IO_APIC.get();
|
||||
io_apic.read_irq(self.index)
|
||||
}
|
||||
|
||||
pub fn write(&mut self, value: u64) {
|
||||
let io_apic = IO_APIC.get();
|
||||
io_apic.write_irq(self.index, value);
|
||||
}
|
||||
|
||||
pub fn get_index(&self) -> u8 {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IoApicEntryHandle {
|
||||
fn drop(&mut self) {
|
||||
let io_apic = IO_APIC.get();
|
||||
// mask
|
||||
io_apic.write_irq(self.index, 1 << 16);
|
||||
io_apic.entry_allocator.dealloc(self.index as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IoApic {
|
||||
id: u8,
|
||||
version: u32,
|
||||
max_redirection_entry: u32,
|
||||
io_apic_register: &'static mut IoApicRegister,
|
||||
entry_allocator: RecycleAllocator,
|
||||
}
|
||||
|
||||
impl IoApic {
|
||||
fn read_irq(&mut self, irq_index: u8) -> u64 {
|
||||
let low = self.io_apic_register.read(IoApicRedtbl(irq_index)) as u64;
|
||||
let high = self.io_apic_register.read(IoApicRedtbl(irq_index) + 1) as u64;
|
||||
high << 32 | low
|
||||
}
|
||||
|
||||
fn write_irq(&mut self, irq_index: u8, value: u64) {
|
||||
let low = value as u32;
|
||||
let high = (value >> 32) as u32;
|
||||
self.io_apic_register.write(IoApicRedtbl(irq_index), low);
|
||||
self.io_apic_register
|
||||
.write(IoApicRedtbl(irq_index) + 1, high);
|
||||
}
|
||||
|
||||
pub fn allocate_entry(&mut self) -> Option<IoApicEntryHandle> {
|
||||
let id = self.entry_allocator.alloc();
|
||||
if id == usize::MAX {
|
||||
return None;
|
||||
}
|
||||
Some(IoApicEntryHandle { index: id as u8 })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let c = ACPI_TABLES.lock();
|
||||
|
||||
let platform_info = PlatformInfo::new(&*c).unwrap();
|
||||
|
||||
let mut ioapic_address = 0;
|
||||
match platform_info.interrupt_model {
|
||||
acpi::InterruptModel::Unknown => panic!("not found APIC in ACPI Table"),
|
||||
acpi::InterruptModel::Apic(apic) => {
|
||||
for io_apic in apic.io_apics.iter() {
|
||||
ioapic_address = io_apic.address;
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
if ioapic_address == 0 {
|
||||
return;
|
||||
}
|
||||
let io_apic_register =
|
||||
unsafe { &mut *(phys_to_virt(ioapic_address as usize) as *mut IoApicRegister) };
|
||||
|
||||
let id = (read_io_apic(io_apic_register, IOAPICID) & (0xF00_0000) >> 24) as u8;
|
||||
let raw_version = read_io_apic(io_apic_register, IOAPICVER);
|
||||
let version = raw_version & 0x1ff;
|
||||
let max_redirection_entry = ((raw_version & (0xFF_0000)) >> 16) + 1;
|
||||
debug!(
|
||||
"IOAPIC id: {}, version:{}, max_redirection_entry:{}",
|
||||
id, version, max_redirection_entry
|
||||
);
|
||||
|
||||
let io_apic = IoApic {
|
||||
id,
|
||||
version,
|
||||
max_redirection_entry,
|
||||
io_apic_register,
|
||||
entry_allocator: RecycleAllocator::with_start_max(0, max_redirection_entry as usize),
|
||||
};
|
||||
|
||||
*IO_APIC.get() = io_apic;
|
||||
}
|
||||
|
||||
fn read_io_apic(io_apic_register: &mut IoApicRegister, reg: u32) -> u32 {
|
||||
io_apic_register.address = reg & 0xff;
|
||||
io_apic_register.data
|
||||
}
|
||||
|
||||
fn write_io_apic(io_apic_register: &mut IoApicRegister, reg: u32, value: u32) {
|
||||
io_apic_register.address = reg & 0xff;
|
||||
io_apic_register.data = value;
|
||||
}
|
30
src/framework/jinux-frame/src/driver/mod.rs
Normal file
30
src/framework/jinux-frame/src/driver/mod.rs
Normal file
@ -0,0 +1,30 @@
|
||||
//! Driver for APIC, PIC, PIT etc.
|
||||
//! This module should inaccessible by other crate such as std, virtio etc.
|
||||
//!
|
||||
|
||||
pub mod acpi;
|
||||
pub mod apic;
|
||||
pub mod ioapic;
|
||||
pub mod pic;
|
||||
pub mod timer;
|
||||
|
||||
pub use apic::ack;
|
||||
pub use timer::TimerCallback;
|
||||
pub(crate) use timer::{add_timeout_list, TICK};
|
||||
|
||||
use crate::info;
|
||||
|
||||
pub(crate) fn init(rsdp: Option<u64>) {
|
||||
acpi::init(rsdp.unwrap());
|
||||
timer::init();
|
||||
if apic::has_apic() {
|
||||
ioapic::init();
|
||||
apic::init();
|
||||
} else {
|
||||
info!("No apic exists, using pic instead");
|
||||
unsafe {
|
||||
pic::enable();
|
||||
}
|
||||
}
|
||||
pic::init();
|
||||
}
|
123
src/framework/jinux-frame/src/driver/pic.rs
Normal file
123
src/framework/jinux-frame/src/driver/pic.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use crate::{info, trap::allocate_target_irq, x86_64_util::out8, IrqAllocateHandle};
|
||||
use core::sync::atomic::Ordering::Relaxed;
|
||||
use core::sync::atomic::{AtomicBool, AtomicU8};
|
||||
|
||||
const MASTER_CMD: u16 = 0x20;
|
||||
const MASTER_DATA: u16 = MASTER_CMD + 1;
|
||||
const SLAVE_CMD: u16 = 0xA0;
|
||||
const SLAVE_DATA: u16 = SLAVE_CMD + 1;
|
||||
const IRQ_OFFSET: u8 = 0x20;
|
||||
|
||||
const TIMER_IRQ_NUM: u8 = 32;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
/// store the irq, although we have APIC for manage interrupts
|
||||
/// but something like serial still need pic for register interrupts
|
||||
static ref IRQ_LOCK : Mutex<Vec<IrqAllocateHandle>> = Mutex::new(Vec::new());
|
||||
}
|
||||
|
||||
static MASK_MASTER: AtomicU8 = AtomicU8::new(0x00);
|
||||
|
||||
static MASK_SLAVE: AtomicU8 = AtomicU8::new(0x00);
|
||||
|
||||
static CHANGE_LOCK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// init the PIC device
|
||||
pub(crate) fn init() {
|
||||
if CHANGE_LOCK.load(Relaxed) {
|
||||
return;
|
||||
}
|
||||
let master_mask = !(MASK_MASTER.load(Relaxed));
|
||||
let slave_mask = !(MASK_SLAVE.load(Relaxed));
|
||||
info!(
|
||||
"PIC init, master mask:{:x} slave_mask:{:x}",
|
||||
master_mask, slave_mask
|
||||
);
|
||||
unsafe {
|
||||
set_mask(master_mask, slave_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/// allocate irq, for example, if timer need IRQ0, it will return IrqAllocateHandle with irq num: IRQ_OFFSET+0
|
||||
pub(crate) fn allocate_irq(index: u8) -> Option<IrqAllocateHandle> {
|
||||
if index >= 16 {
|
||||
return None;
|
||||
}
|
||||
if let Ok(irq) = allocate_target_irq(IRQ_OFFSET + index) {
|
||||
if index >= 8 {
|
||||
MASK_SLAVE.fetch_or(1 << (index - 8), Relaxed);
|
||||
} else {
|
||||
MASK_MASTER.fetch_or(1 << (index), Relaxed);
|
||||
}
|
||||
Some(irq)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// enable the PIC device, this function will permanent enable all the interrupts
|
||||
#[inline]
|
||||
pub(crate) unsafe fn enable() {
|
||||
CHANGE_LOCK.store(true, Relaxed);
|
||||
set_mask(0, 0);
|
||||
}
|
||||
|
||||
/// disable the PIC device, this function will permanent disable all the interrupts
|
||||
/// the interrupts mask may not exists after calling init function
|
||||
#[inline]
|
||||
pub(crate) unsafe fn disable() {
|
||||
CHANGE_LOCK.store(true, Relaxed);
|
||||
set_mask(0xFF, 0xFF);
|
||||
}
|
||||
|
||||
/// enable the PIC device, this function will allow all the interrupts
|
||||
/// the interrupts mask may not exists after calling init function
|
||||
#[inline]
|
||||
pub(crate) fn enable_temp() {
|
||||
unsafe {
|
||||
set_mask(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// disable the PIC device, this function will disable all the interrupts
|
||||
/// the interrupts mask may not exists after calling init function
|
||||
#[inline]
|
||||
pub(crate) fn disable_temp() {
|
||||
unsafe {
|
||||
set_mask(0xFF, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn set_mask(master_mask: u8, slave_mask: u8) {
|
||||
// Start initialization
|
||||
out8(MASTER_CMD, 0x11);
|
||||
out8(SLAVE_CMD, 0x11);
|
||||
|
||||
// Set offsets
|
||||
// map master PIC vector 0x00~0x07 to 0x20~0x27 IRQ number
|
||||
out8(MASTER_DATA, IRQ_OFFSET);
|
||||
// map slave PIC vector 0x00~0x07 to 0x28~0x2f IRQ number
|
||||
out8(SLAVE_DATA, IRQ_OFFSET + 0x08);
|
||||
|
||||
// Set up cascade, there is slave at IRQ2
|
||||
out8(MASTER_DATA, 4);
|
||||
out8(SLAVE_DATA, 2);
|
||||
|
||||
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
|
||||
out8(MASTER_DATA, 1);
|
||||
out8(SLAVE_DATA, 1);
|
||||
|
||||
// mask interrupts
|
||||
out8(MASTER_DATA, master_mask);
|
||||
out8(SLAVE_DATA, slave_mask);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn ack() {
|
||||
out8(MASTER_CMD, 0x20);
|
||||
}
|
70
src/framework/jinux-frame/src/driver/timer/apic.rs
Normal file
70
src/framework/jinux-frame/src/driver/timer/apic.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use crate::{
|
||||
config,
|
||||
driver::{apic::APIC_INSTANCE, pic, timer},
|
||||
info, x86_64_util, TrapFrame,
|
||||
};
|
||||
|
||||
pub fn init() {
|
||||
let apic_lock = APIC_INSTANCE.get();
|
||||
let handle = unsafe { crate::trap::IrqLine::acquire(timer::TIMER_IRQ_NUM) };
|
||||
let a = handle.on_active(init_function);
|
||||
// divide by 64
|
||||
apic_lock.divide_configuration_register.write(0b1001);
|
||||
apic_lock.initial_count_register.write(0xFFFF_FFFF);
|
||||
// apic_lock.lvt_timer_register.write(timer::TIMER_IRQ_NUM as u32);
|
||||
drop(apic_lock);
|
||||
|
||||
// init pic for now, disable it after the APIC Timer init is done
|
||||
pic::enable_temp();
|
||||
timer::pit::init();
|
||||
|
||||
static mut IS_FINISH: bool = false;
|
||||
// wait until it is finish
|
||||
x86_64_util::enable_interrupts();
|
||||
unsafe {
|
||||
while !IS_FINISH {
|
||||
x86_64_util::hlt();
|
||||
}
|
||||
}
|
||||
x86_64_util::disable_interrupts();
|
||||
drop(a);
|
||||
drop(handle);
|
||||
|
||||
fn init_function(trap_frame: &TrapFrame) {
|
||||
static mut IN_TIME: u8 = 0;
|
||||
static mut FIRST_TIME_COUNT: u32 = 0;
|
||||
unsafe {
|
||||
if IS_FINISH || IN_TIME == 0 {
|
||||
// drop the first entry, since it may not be the time we want
|
||||
IN_TIME += 1;
|
||||
let apic_lock = APIC_INSTANCE.get();
|
||||
let remain_ticks = apic_lock.current_count_register.read();
|
||||
FIRST_TIME_COUNT = 0xFFFF_FFFF - remain_ticks;
|
||||
pic::ack();
|
||||
return;
|
||||
}
|
||||
}
|
||||
pic::disable_temp();
|
||||
// stop APIC Timer, get the number of tick we need
|
||||
let apic_lock = APIC_INSTANCE.get();
|
||||
let remain_ticks = apic_lock.current_count_register.read();
|
||||
apic_lock.initial_count_register.write(0);
|
||||
let ticks = unsafe { 0xFFFF_FFFF - remain_ticks - FIRST_TIME_COUNT };
|
||||
// periodic mode, divide 64, freq: TIMER_FREQ Hz
|
||||
apic_lock.initial_count_register.write(ticks as u32);
|
||||
apic_lock
|
||||
.lvt_timer_register
|
||||
.write(timer::TIMER_IRQ_NUM as u32 | (1 << 17));
|
||||
apic_lock.divide_configuration_register.write(0b1001);
|
||||
|
||||
info!(
|
||||
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
||||
ticks,
|
||||
remain_ticks,
|
||||
config::TIMER_FREQ
|
||||
);
|
||||
unsafe {
|
||||
IS_FINISH = true;
|
||||
}
|
||||
}
|
||||
}
|
126
src/framework/jinux-frame/src/driver/timer/hpet.rs
Normal file
126
src/framework/jinux-frame/src/driver/timer/hpet.rs
Normal file
@ -0,0 +1,126 @@
|
||||
use acpi::{AcpiError, HpetInfo};
|
||||
use alloc::vec::Vec;
|
||||
use volatile::{
|
||||
access::{ReadOnly, ReadWrite},
|
||||
Volatile,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
cell::Cell,
|
||||
driver::{
|
||||
acpi::ACPI_TABLES,
|
||||
ioapic::{self, IoApicEntryHandle},
|
||||
},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
static ref HPET_INSTANCE: Cell<HPET> =
|
||||
unsafe { Cell::new(core::mem::MaybeUninit::zeroed().assume_init()) };
|
||||
}
|
||||
|
||||
const OFFSET_ID_REGISTER: usize = 0x000;
|
||||
const OFFSET_CONFIGURATION_REGISTER: usize = 0x010;
|
||||
const OFFSET_INTERRUPT_STATUS_REGISTER: usize = 0x020;
|
||||
const OFFSET_MAIN_COUNTER_VALUE_REGISTER: usize = 0x0F0;
|
||||
|
||||
const HPET_FREQ: usize = 1_000_000_000_000_000;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct HPETTimerRegister {
|
||||
configuration_and_capabilities_register: u32,
|
||||
timer_compartor_value_register: u32,
|
||||
fsb_interrupt_route_register: u32,
|
||||
}
|
||||
|
||||
struct HPET {
|
||||
io_apic_entry: IoApicEntryHandle,
|
||||
information_register: Volatile<&'static u32, ReadOnly>,
|
||||
general_configuration_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
general_interrupt_status_register: Volatile<&'static mut u32, ReadWrite>,
|
||||
|
||||
timer_registers: Vec<Volatile<&'static mut HPETTimerRegister, ReadWrite>>,
|
||||
}
|
||||
|
||||
impl HPET {
|
||||
fn new(base_address: usize) -> HPET {
|
||||
let information_register_ref = unsafe {
|
||||
&*(crate::mm::address::phys_to_virt(base_address + OFFSET_ID_REGISTER) as *mut usize
|
||||
as *mut u32)
|
||||
};
|
||||
let general_configuration_register_ref = unsafe {
|
||||
&mut *(crate::mm::address::phys_to_virt(base_address + OFFSET_CONFIGURATION_REGISTER)
|
||||
as *mut usize as *mut u32)
|
||||
};
|
||||
let general_interrupt_status_register_ref = unsafe {
|
||||
&mut *(crate::mm::address::phys_to_virt(base_address + OFFSET_INTERRUPT_STATUS_REGISTER)
|
||||
as *mut usize as *mut u32)
|
||||
};
|
||||
|
||||
let information_register = Volatile::new_read_only(information_register_ref);
|
||||
let general_configuration_register = Volatile::new(general_configuration_register_ref);
|
||||
let general_interrupt_status_register =
|
||||
Volatile::new(general_interrupt_status_register_ref);
|
||||
|
||||
let num_comparator = ((information_register.read() & 0x1F00) >> 8) as u8 + 1;
|
||||
|
||||
let mut comparators = Vec::with_capacity(num_comparator as usize);
|
||||
|
||||
for i in 0..num_comparator {
|
||||
let comp = Volatile::new(unsafe {
|
||||
&mut *(crate::mm::address::phys_to_virt(base_address + 0x100 + i as usize * 0x20)
|
||||
as *mut usize as *mut HPETTimerRegister)
|
||||
});
|
||||
comparators.push(comp);
|
||||
}
|
||||
|
||||
let mut io_apic_entry = ioapic::IO_APIC.get().allocate_entry().unwrap();
|
||||
let vector = super::TIMER_IRQ_NUM;
|
||||
// 0 for now
|
||||
let destination_apic_id: u8 = 0;
|
||||
let write_value = (destination_apic_id as u64) << 56 | vector as u64;
|
||||
|
||||
io_apic_entry.write(write_value);
|
||||
|
||||
HPET {
|
||||
io_apic_entry,
|
||||
information_register,
|
||||
general_configuration_register,
|
||||
general_interrupt_status_register,
|
||||
timer_registers: comparators,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hardware_rev(&self) -> u8 {
|
||||
(self.information_register.read() & 0xFF) as u8
|
||||
}
|
||||
|
||||
pub fn num_comparators(&self) -> u8 {
|
||||
((self.information_register.read() & 0x1F00) >> 8) as u8 + 1
|
||||
}
|
||||
|
||||
pub fn main_counter_is_64bits(&self) -> bool {
|
||||
(self.information_register.read() & 0x2000) != 0
|
||||
}
|
||||
|
||||
pub fn legacy_irq_capable(&self) -> bool {
|
||||
(self.information_register.read() & 0x8000) != 0
|
||||
}
|
||||
|
||||
pub fn pci_vendor_id(&self) -> u16 {
|
||||
((self.information_register.read() & 0xFFFF_0000) >> 16) as u16
|
||||
}
|
||||
}
|
||||
|
||||
/// HPET init, need to init IOAPIC before init this function
|
||||
pub fn init() -> Result<(), AcpiError> {
|
||||
let c = ACPI_TABLES.lock();
|
||||
|
||||
let hpet_info = HpetInfo::new(&*c)?;
|
||||
|
||||
// config IO APIC entry
|
||||
let hpet = HPET::new(hpet_info.base_address);
|
||||
*HPET_INSTANCE.get() = hpet;
|
||||
Ok(())
|
||||
}
|
@ -1,27 +1,16 @@
|
||||
use crate::cell::Cell;
|
||||
use crate::x86_64_util::out8;
|
||||
use crate::{IrqAllocateHandle, TrapFrame};
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{boxed::Box, collections::BinaryHeap};
|
||||
pub mod apic;
|
||||
pub mod hpet;
|
||||
pub mod pit;
|
||||
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::{boxed::Box, collections::BinaryHeap, sync::Arc, vec::Vec};
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
||||
const MASTER_CMD: u16 = 0x20;
|
||||
const MASTER_DATA: u16 = MASTER_CMD + 1;
|
||||
const SLAVE_CMD: u16 = 0xA0;
|
||||
const SLAVE_DATA: u16 = SLAVE_CMD + 1;
|
||||
|
||||
const TIMER_RATE: u32 = 1193182;
|
||||
/// This value represent the base timer frequency in Hz
|
||||
pub const TIMER_FREQ: u64 = 100;
|
||||
const TIMER_PERIOD_IO_PORT: u16 = 0x40;
|
||||
const TIMER_MODE_IO_PORT: u16 = 0x43;
|
||||
const TIMER_SQUARE_WAVE: u8 = 0x36;
|
||||
|
||||
const TIMER_IRQ_NUM: u8 = 32;
|
||||
use crate::{cell::Cell, IrqAllocateHandle, TrapFrame};
|
||||
|
||||
pub(crate) const TIMER_IRQ_NUM: u8 = 32;
|
||||
pub static mut TICK: u64 = 0;
|
||||
|
||||
lazy_static! {
|
||||
@ -31,49 +20,16 @@ lazy_static! {
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
// Start initialization
|
||||
out8(MASTER_CMD, 0x11);
|
||||
out8(SLAVE_CMD, 0x11);
|
||||
if super::apic::has_apic() {
|
||||
apic::init();
|
||||
} else {
|
||||
pit::init();
|
||||
}
|
||||
|
||||
// Set offsets
|
||||
// map master PIC vector 0x00~0x07 to 0x20~0x27 IRQ number
|
||||
out8(MASTER_DATA, 0x20);
|
||||
// map slave PIC vector 0x00~0x07 to 0x28~0x2f IRQ number
|
||||
out8(SLAVE_DATA, 0x28);
|
||||
|
||||
// Set up cascade, there is slave at IRQ2
|
||||
out8(MASTER_DATA, 4);
|
||||
out8(SLAVE_DATA, 2);
|
||||
|
||||
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
|
||||
out8(MASTER_DATA, 1);
|
||||
out8(SLAVE_DATA, 1);
|
||||
|
||||
// Unmask timer interrupt
|
||||
out8(MASTER_DATA, 0xFE);
|
||||
out8(SLAVE_DATA, 0xFF);
|
||||
|
||||
// Ack remaining interrupts
|
||||
out8(MASTER_CMD, 0x20);
|
||||
out8(SLAVE_CMD, 0x20);
|
||||
|
||||
// Initialize timer.
|
||||
let cycle = TIMER_RATE / TIMER_FREQ as u32; // 1ms per interrupt.
|
||||
out8(TIMER_MODE_IO_PORT, TIMER_SQUARE_WAVE);
|
||||
out8(TIMER_PERIOD_IO_PORT, (cycle & 0xFF) as _);
|
||||
out8(TIMER_PERIOD_IO_PORT, (cycle >> 8) as _);
|
||||
TIMER_IRQ.lock().on_active(timer_callback);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ack() {
|
||||
out8(MASTER_CMD, 0x20);
|
||||
}
|
||||
|
||||
fn timer_callback(trap_frame: &TrapFrame) {
|
||||
// FIXME: disable and enable interupt will cause infinity loop
|
||||
// x86_64_util::disable_interrupts();
|
||||
ack();
|
||||
let current_ms;
|
||||
unsafe {
|
||||
current_ms = TICK;
|
||||
@ -82,18 +38,16 @@ fn timer_callback(trap_frame: &TrapFrame) {
|
||||
let timeout_list = TIMEOUT_LIST.get();
|
||||
let mut callbacks: Vec<Arc<TimerCallback>> = Vec::new();
|
||||
while let Some(t) = timeout_list.peek() {
|
||||
if t.expire_ms <= current_ms {
|
||||
if t.expire_ms <= current_ms && t.is_enable() {
|
||||
callbacks.push(timeout_list.pop().unwrap());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for callback in callbacks {
|
||||
if callback.is_enable() {
|
||||
callback.callback.call((&callback,));
|
||||
}
|
||||
callback.callback.call((&callback,));
|
||||
}
|
||||
// x86_64_util::enable_interrupts();
|
||||
// crate::interrupt_ack();
|
||||
}
|
||||
|
||||
lazy_static! {
|
17
src/framework/jinux-frame/src/driver/timer/pit.rs
Normal file
17
src/framework/jinux-frame/src/driver/timer/pit.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! used for PIT Timer
|
||||
|
||||
use crate::{config::TIMER_FREQ, x86_64_util::out8};
|
||||
|
||||
const TIMER_RATE: u32 = 1193182;
|
||||
|
||||
const TIMER_PERIOD_IO_PORT: u16 = 0x40;
|
||||
const TIMER_MODE_IO_PORT: u16 = 0x43;
|
||||
const TIMER_SQUARE_WAVE: u8 = 0x36;
|
||||
|
||||
pub(crate) fn init() {
|
||||
// Initialize timer.
|
||||
let cycle = TIMER_RATE / TIMER_FREQ as u32;
|
||||
out8(TIMER_MODE_IO_PORT, TIMER_SQUARE_WAVE);
|
||||
out8(TIMER_PERIOD_IO_PORT, (cycle & 0xFF) as _);
|
||||
out8(TIMER_PERIOD_IO_PORT, (cycle >> 8) as _);
|
||||
}
|
@ -16,6 +16,7 @@ pub(crate) mod cell;
|
||||
pub mod config;
|
||||
pub mod cpu;
|
||||
pub mod device;
|
||||
mod driver;
|
||||
mod error;
|
||||
pub mod log;
|
||||
pub(crate) mod mm;
|
||||
@ -30,39 +31,48 @@ pub mod vm;
|
||||
pub(crate) mod x86_64_util;
|
||||
|
||||
use core::{mem, panic::PanicInfo};
|
||||
pub use driver::ack as apic_ack;
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use self::prelude::Result;
|
||||
pub(crate) use self::sync::up::UPSafeCell;
|
||||
pub use trap::interrupt_ack;
|
||||
pub use x86_64_util::{disable_interrupts, enable_interrupts, hlt};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use bootloader::{
|
||||
boot_info::{FrameBuffer, MemoryRegionKind},
|
||||
BootInfo,
|
||||
};
|
||||
pub use device::serial::receive_char;
|
||||
pub use device::console::receive_char;
|
||||
pub use mm::address::{align_down, align_up, is_aligned, virt_to_phys};
|
||||
pub use mm::page_table::translate_not_offset_virtual_address;
|
||||
pub use trap::{allocate_irq, IrqAllocateHandle, TrapFrame};
|
||||
use trap::{IrqCallbackHandle, IrqLine};
|
||||
pub use util::AlignExt;
|
||||
pub use x86_64::registers::rflags::read as get_rflags;
|
||||
pub use x86_64::registers::rflags::RFlags;
|
||||
use x86_64_util::enable_common_cpu_features;
|
||||
|
||||
static mut IRQ_CALLBACK_LIST: Vec<IrqCallbackHandle> = Vec::new();
|
||||
|
||||
// TODO: serial中断
|
||||
// 讨论syscall的中断启用
|
||||
#[cfg(not(feature = "serial_print"))]
|
||||
pub use crate::screen_print as print;
|
||||
#[cfg(not(feature = "serial_print"))]
|
||||
pub use crate::screen_println as println;
|
||||
|
||||
#[cfg(feature = "serial_print")]
|
||||
pub use crate::serial_print as print;
|
||||
pub use crate::console_print as print;
|
||||
#[cfg(feature = "serial_print")]
|
||||
pub use crate::serial_println as println;
|
||||
pub use crate::console_println as println;
|
||||
|
||||
pub fn init(boot_info: &'static mut BootInfo) {
|
||||
let siz = boot_info.framebuffer.as_ref().unwrap() as *const FrameBuffer as usize;
|
||||
let mut memory_init = false;
|
||||
// memory
|
||||
device::first_init(boot_info.framebuffer.as_mut().unwrap());
|
||||
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
|
||||
for region in boot_info.memory_regions.iter() {
|
||||
if region.kind == MemoryRegionKind::Usable {
|
||||
let start: u64 = region.start;
|
||||
@ -79,25 +89,25 @@ pub fn init(boot_info: &'static mut BootInfo) {
|
||||
if !memory_init {
|
||||
panic!("memory init failed");
|
||||
}
|
||||
device::init(boot_info.framebuffer.as_mut().unwrap());
|
||||
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
|
||||
trap::init();
|
||||
device::second_init();
|
||||
driver::init(boot_info.rsdp_addr.into_option());
|
||||
enable_common_cpu_features();
|
||||
unsafe {
|
||||
for i in 0..256 {
|
||||
IRQ_CALLBACK_LIST.push(IrqLine::acquire(i as u8).on_active(general_handler))
|
||||
}
|
||||
let value = x86_64_util::cpuid(1);
|
||||
}
|
||||
// uncomment below code to enable timer interrupt
|
||||
// x86_64_util::enable_interrupts_and_hlt();
|
||||
}
|
||||
fn general_handler(trap_frame: &TrapFrame) {
|
||||
println!("{:#x?}", trap_frame);
|
||||
println!("rip = 0x{:x}", trap_frame.rip);
|
||||
println!("rsp = 0x{:x}", trap_frame.rsp);
|
||||
println!("cr2 = 0x{:x}", trap_frame.cr2);
|
||||
// println!("rbx = 0x{:x}", trap_frame.)
|
||||
panic!("couldn't handler trap right now");
|
||||
// info!("general handler");
|
||||
// println!("{:#x?}", trap_frame);
|
||||
// println!("rip = 0x{:x}", trap_frame.rip);
|
||||
// println!("rsp = 0x{:x}", trap_frame.rsp);
|
||||
// println!("cr2 = 0x{:x}", trap_frame.cr2);
|
||||
// // println!("rbx = 0x{:x}", trap_frame.)
|
||||
// panic!("couldn't handler trap right now");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -114,14 +124,14 @@ where
|
||||
T: Fn(),
|
||||
{
|
||||
fn run(&self) {
|
||||
serial_print!("{}...\n", core::any::type_name::<T>());
|
||||
console_print!("{}...\n", core::any::type_name::<T>());
|
||||
self();
|
||||
serial_println!("[ok]");
|
||||
console_println!("[ok]");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_runner(tests: &[&dyn Testable]) {
|
||||
serial_println!("Running {} tests", tests.len());
|
||||
console_println!("Running {} tests", tests.len());
|
||||
for test in tests {
|
||||
test.run();
|
||||
}
|
||||
@ -129,11 +139,33 @@ pub fn test_runner(tests: &[&dyn Testable]) {
|
||||
}
|
||||
|
||||
pub fn test_panic_handler(info: &PanicInfo) -> ! {
|
||||
serial_println!("[failed]");
|
||||
serial_println!("Error: {}", info);
|
||||
console_println!("[failed]");
|
||||
console_println!("Error: {}", info);
|
||||
exit_qemu(QemuExitCode::Failed);
|
||||
}
|
||||
|
||||
pub fn panic_handler() {
|
||||
println!("[panic]: cr3:{:x}", x86_64_util::get_cr3());
|
||||
// let mut fp: usize;
|
||||
// let stop = unsafe{
|
||||
// Task::current().kstack.get_top()
|
||||
// };
|
||||
// info!("stop:{:x}",stop);
|
||||
// unsafe{
|
||||
// asm!("mov rbp, {}", out(reg) fp);
|
||||
// info!("fp:{:x}",fp);
|
||||
// println!("---START BACKTRACE---");
|
||||
// for i in 0..10 {
|
||||
// if fp == stop {
|
||||
// break;
|
||||
// }
|
||||
// println!("#{}:ra={:#x}", i, *((fp - 8) as *const usize));
|
||||
// info!("fp target:{:x}",*((fp ) as *const usize));
|
||||
// fp = *((fp - 16) as *const usize);
|
||||
// }
|
||||
// println!("---END BACKTRACE---");
|
||||
// }
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum QemuExitCode {
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RecycleAllocator {
|
||||
current: usize,
|
||||
recycled: Vec<usize>,
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -1,10 +1,11 @@
|
||||
use alloc::vec::Vec;
|
||||
use pod_derive::Pod;
|
||||
|
||||
use crate::util::{CSpaceAccessMethod, Location, BAR};
|
||||
|
||||
use super::capability::msix::CapabilityMSIXData;
|
||||
|
||||
use jinux_frame::{offset_of, IrqAllocateHandle};
|
||||
use jinux_frame::{debug, offset_of, IrqAllocateHandle};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -40,7 +41,7 @@ impl MSIX {
|
||||
) -> Self {
|
||||
let table_info = cap.table_info;
|
||||
let pba_info = cap.pba_info;
|
||||
let table_size = table_info & (0b11_1111_1111);
|
||||
let table_size = (table_info & (0b11_1111_1111)) + 1;
|
||||
let table_bar_address;
|
||||
let pba_bar_address;
|
||||
match bars[(pba_info & 0b111) as usize].expect("MSIX cfg:table bar is none") {
|
||||
@ -59,17 +60,35 @@ impl MSIX {
|
||||
panic!("MSIX cfg:table bar is IO type")
|
||||
}
|
||||
}
|
||||
// let pba_base_address = (pba_info >> 3 ) as u64 + pba_bar_address;
|
||||
// let table_base_address = (table_info >>3 ) as u64 + table_bar_address;
|
||||
let pba_base_address = (pba_info & (!(0b111 as u32))) as u64 + pba_bar_address;
|
||||
let table_base_address = (table_info & (!(0b111 as u32))) as u64 + table_bar_address;
|
||||
debug!("MSIX table size:{}, pba_info:{:x}, table_info:{:x}, pba_address:{:x}, table_address:{:x}",
|
||||
table_size,pba_info,table_info,pba_base_address,table_base_address);
|
||||
let mut cap = Self {
|
||||
table_size: table_size as u16,
|
||||
table: Vec::new(),
|
||||
pba_paddr: (pba_info / 8) as u64 + pba_bar_address,
|
||||
pba_paddr: pba_base_address,
|
||||
};
|
||||
let mut table_paddr = (table_info / 8) as u64 + table_bar_address;
|
||||
for _ in 0..table_size {
|
||||
// enable MSI-X disable INTx
|
||||
let am = CSpaceAccessMethod::IO;
|
||||
debug!("command before:{:x}", am.read16(loc, crate::PCI_COMMAND));
|
||||
am.write16(
|
||||
loc,
|
||||
crate::PCI_COMMAND,
|
||||
am.read16(loc, crate::PCI_COMMAND) | 0x40f,
|
||||
);
|
||||
debug!("command after:{:x}", am.read16(loc, crate::PCI_COMMAND));
|
||||
let message_control = am.read16(loc, cap_ptr + 2) | 0x8000;
|
||||
am.write16(loc, cap_ptr + 2, message_control);
|
||||
for i in 0..table_size {
|
||||
let value: InFramePtr<MSIXTableEntry> =
|
||||
InFramePtr::new(table_paddr as usize).expect("can not get in frame ptr for msix");
|
||||
// let mut value = MSIXTableEntry::default();
|
||||
InFramePtr::new(table_base_address as usize + 16 * i as usize)
|
||||
.expect("can not get in frame ptr for msix");
|
||||
// local APIC address: 0xFEE0_0000
|
||||
value.write_at(offset_of!(MSIXTableEntry, msg_addr), 0xFEE0_0000 as u32);
|
||||
value.write_at(offset_of!(MSIXTableEntry, msg_upper_addr), 0 as u32);
|
||||
// allocate irq number
|
||||
let handle = jinux_frame::allocate_irq().expect("not enough irq");
|
||||
value.write_at(offset_of!(MSIXTableEntry, msg_data), handle.num() as u32);
|
||||
@ -78,11 +97,7 @@ impl MSIX {
|
||||
table_entry: value,
|
||||
irq_handle: handle,
|
||||
});
|
||||
table_paddr += 16;
|
||||
}
|
||||
// enable MSI-X
|
||||
let am = CSpaceAccessMethod::IO;
|
||||
am.write8(loc, cap_ptr, 0b1000_0000);
|
||||
cap
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,34 @@ use crate::capability::Capability;
|
||||
use alloc::vec::Vec;
|
||||
use bitflags::bitflags;
|
||||
|
||||
pub enum PCIDeviceCommonCfgOffset {
|
||||
VendorId = 0x00,
|
||||
DeviceId = 0x02,
|
||||
Command = 0x04,
|
||||
Status = 0x06,
|
||||
RevisionId = 0x08,
|
||||
ClassCode = 0x09,
|
||||
CacheLineSize = 0x0C,
|
||||
LatencyTimer = 0x0D,
|
||||
HeaderType = 0x0E,
|
||||
BIST = 0x0F,
|
||||
BAR0 = 0x10,
|
||||
BAR1 = 0x14,
|
||||
BAR2 = 0x18,
|
||||
BAR3 = 0x1C,
|
||||
BAR4 = 0x20,
|
||||
BAR5 = 0x24,
|
||||
CardbusCisPtr = 0x28,
|
||||
SubsystemVendorId = 0x2C,
|
||||
SubsystemId = 0x2E,
|
||||
XROMBAR = 0x30,
|
||||
CapabilitiesPointer = 0x34,
|
||||
InterruptLine = 0x3C,
|
||||
InterruptPin = 0x3D,
|
||||
MinGrant = 0x3E,
|
||||
MaxLatency = 0x3F,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CSpaceAccessMethod {
|
||||
// The legacy, deprecated (as of PCI 2.0) IO-range method.
|
||||
@ -48,18 +76,26 @@ impl CSpaceAccessMethod {
|
||||
}
|
||||
|
||||
pub fn write8(self, loc: Location, offset: u16, val: u8) {
|
||||
let old = self.read32(loc, offset);
|
||||
let old = self.read32(loc, offset & (0xFFFC));
|
||||
let dest = offset as usize & 0b11 << 3;
|
||||
let mask = (0xFF << dest) as u32;
|
||||
self.write32(loc, offset, ((val as u32) << dest | (old & !mask)).to_le());
|
||||
self.write32(
|
||||
loc,
|
||||
offset & (0xFFFC),
|
||||
((val as u32) << dest | (old & !mask)).to_le(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts val to little endian before writing.
|
||||
pub fn write16(self, loc: Location, offset: u16, val: u16) {
|
||||
let old = self.read32(loc, offset);
|
||||
let old = self.read32(loc, offset & (0xFFFC));
|
||||
let dest = offset as usize & 0b10 << 3;
|
||||
let mask = (0xFFFF << dest) as u32;
|
||||
self.write32(loc, offset, ((val as u32) << dest | (old & !mask)).to_le());
|
||||
self.write32(
|
||||
loc,
|
||||
offset & (0xFFFC),
|
||||
((val as u32) << dest | (old & !mask)).to_le(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Takes a value in native endian, converts it to little-endian, and writes it to the PCI
|
||||
@ -73,7 +109,7 @@ impl CSpaceAccessMethod {
|
||||
CSpaceAccessMethod::IO => {
|
||||
jinux_frame::device::pci::PCI_ADDRESS_PORT
|
||||
.write_u32(loc.encode() | (offset as u32 & 0b11111100));
|
||||
jinux_frame::device::pci::PCI_ADDRESS_PORT.write_u32(val.to_le())
|
||||
jinux_frame::device::pci::PCI_DATA_PORT.write_u32(val.to_le())
|
||||
} //MemoryMapped(ptr) => {
|
||||
// // FIXME: Clarify whether the rules for GEP/GEPi forbid using regular .offset() here.
|
||||
// ::core::intrinsics::volatile_load(::core::intrinsics::arith_offset(ptr, offset as usize))
|
||||
@ -112,64 +148,64 @@ pub struct Identifier {
|
||||
|
||||
bitflags! {
|
||||
pub struct Command: u16 {
|
||||
const IO_SPACE = 0x0001;
|
||||
const MEMORY_SPACE = 0x0002;
|
||||
const BUS_MASTER = 0x0004;
|
||||
const SPECIAL_CYCLES = 0x0008;
|
||||
const MWI_ENABLE = 0x0010;
|
||||
const VGA_PALETTE_SNOOP = 0x0020;
|
||||
const PARITY_ERROR_RESPONSE = 0x0040;
|
||||
const STEPPING_CONTROL = 0x0080;
|
||||
const SERR_ENABLE = 0x0100;
|
||||
const FAST_BACK_TO_BACK_ENABLE = 0x0200;
|
||||
const INTERRUPT_DISABLE = 0x0400;
|
||||
const RESERVED_11 = 0x0800;
|
||||
const RESERVED_12 = 0x1000;
|
||||
const RESERVED_13 = 0x2000;
|
||||
const RESERVED_14 = 0x4000;
|
||||
const RESERVED_15 = 0x8000;
|
||||
const IO_SPACE = 1 << 0;
|
||||
const MEMORY_SPACE = 1 << 1;
|
||||
const BUS_MASTER = 1 << 2;
|
||||
const SPECIAL_CYCLES = 1 << 3;
|
||||
const MWI_ENABLE = 1 << 4;
|
||||
const VGA_PALETTE_SNOOP = 1 << 5;
|
||||
const PARITY_ERROR_RESPONSE = 1 << 6;
|
||||
const STEPPING_CONTROL = 1 << 7;
|
||||
const SERR_ENABLE = 1 << 8;
|
||||
const FAST_BACK_TO_BACK_ENABLE = 1 << 9;
|
||||
const INTERRUPT_DISABLE = 1 << 10;
|
||||
const RESERVED_11 = 1 << 11;
|
||||
const RESERVED_12 = 1 << 12;
|
||||
const RESERVED_13 = 1 << 13;
|
||||
const RESERVED_14 = 1 << 14;
|
||||
const RESERVED_15 = 1 << 15;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct Status: u16 {
|
||||
const RESERVED_0 = 0x0001;
|
||||
const RESERVED_1 = 0x0002;
|
||||
const RESERVED_2 = 0x0004;
|
||||
const INTERRUPT_STATUS = 0x0008;
|
||||
const CAPABILITIES_LIST = 0x0010;
|
||||
const MHZ66_CAPABLE = 0x0020;
|
||||
const RESERVED_6 = 0x0040;
|
||||
const FAST_BACK_TO_BACK_CAPABLE = 0x0080;
|
||||
const MASTER_DATA_PARITY_ERROR = 0x0100;
|
||||
const DEVSEL_MEDIUM_TIMING = 0x0200;
|
||||
const DEVSEL_SLOW_TIMING = 0x0400;
|
||||
const SIGNALED_TARGET_ABORT = 0x0800;
|
||||
const RECEIVED_TARGET_ABORT = 0x1000;
|
||||
const RECEIVED_MASTER_ABORT = 0x2000;
|
||||
const SIGNALED_SYSTEM_ERROR = 0x4000;
|
||||
const DETECTED_PARITY_ERROR = 0x8000;
|
||||
const RESERVED_0 = 1 << 0;
|
||||
const RESERVED_1 = 1 << 1;
|
||||
const RESERVED_2 = 1 << 2;
|
||||
const INTERRUPT_STATUS = 1 << 3;
|
||||
const CAPABILITIES_LIST = 1 << 4;
|
||||
const MHZ66_CAPABLE = 1 << 5;
|
||||
const RESERVED_6 = 1 << 6;
|
||||
const FAST_BACK_TO_BACK_CAPABLE = 1 << 7;
|
||||
const MASTER_DATA_PARITY_ERROR = 1 << 8;
|
||||
const DEVSEL_MEDIUM_TIMING = 1 << 9;
|
||||
const DEVSEL_SLOW_TIMING = 1 << 10;
|
||||
const SIGNALED_TARGET_ABORT = 1 << 11;
|
||||
const RECEIVED_TARGET_ABORT = 1 << 12;
|
||||
const RECEIVED_MASTER_ABORT = 1 << 13;
|
||||
const SIGNALED_SYSTEM_ERROR = 1 << 14;
|
||||
const DETECTED_PARITY_ERROR = 1 << 15;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct BridgeControl: u16 {
|
||||
const PARITY_ERROR_RESPONSE_ENABLE = 0x0001;
|
||||
const SERR_ENABLE = 0x0002;
|
||||
const ISA_ENABLE = 0x0004;
|
||||
const VGA_ENABLE = 0x0008;
|
||||
const RESERVED_4 = 0x0010;
|
||||
const MASTER_ABORT_MODE = 0x0020;
|
||||
const SECONDARY_BUS_RESET = 0x0040;
|
||||
const FAST_BACK_TO_BACK_ENABLE = 0x0080;
|
||||
const PRIMARY_DISCARD_TIMER = 0x0100;
|
||||
const SECONDARY_DISCARD_TIMER = 0x0200;
|
||||
const DISCARD_TIMER_STATUS = 0x0400;
|
||||
const DISCARD_TIMER_SERR_ENABLED = 0x0800;
|
||||
const RESERVED_12 = 0x1000;
|
||||
const RESERVED_13 = 0x2000;
|
||||
const RESERVED_14 = 0x4000;
|
||||
const RESERVED_15 = 0x8000;
|
||||
const PARITY_ERROR_RESPONSE_ENABLE = 1 << 0;
|
||||
const SERR_ENABLE = 1 << 1;
|
||||
const ISA_ENABLE = 1 << 2;
|
||||
const VGA_ENABLE = 1 << 3;
|
||||
const RESERVED_4 = 1 << 4;
|
||||
const MASTER_ABORT_MODE = 1 << 5;
|
||||
const SECONDARY_BUS_RESET = 1 << 6;
|
||||
const FAST_BACK_TO_BACK_ENABLE = 1 << 7;
|
||||
const PRIMARY_DISCARD_TIMER = 1 << 8;
|
||||
const SECONDARY_DISCARD_TIMER = 1 << 9;
|
||||
const DISCARD_TIMER_STATUS = 1 << 10;
|
||||
const DISCARD_TIMER_SERR_ENABLED = 1 << 11;
|
||||
const RESERVED_12 = 1 << 12;
|
||||
const RESERVED_13 = 1 << 13;
|
||||
const RESERVED_14 = 1 << 14;
|
||||
const RESERVED_15 = 1 << 15;
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +230,15 @@ pub struct PCIDevice {
|
||||
pub capabilities: Vec<Capability>,
|
||||
}
|
||||
|
||||
impl PCIDevice {
|
||||
/// set the status bits
|
||||
pub fn set_status_bits(&self, status: Status) {
|
||||
let am = CSpaceAccessMethod::IO;
|
||||
let status = am.read16(self.loc, 0x06) | status.bits;
|
||||
am.write16(self.loc, 0x06, status)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PCIScanError {}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -276,6 +321,7 @@ pub enum InterruptPin {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum BAR {
|
||||
Memory(u64, u32, Prefetchable, Type),
|
||||
/// first element is address, second element is size
|
||||
IO(u32, u32),
|
||||
}
|
||||
|
||||
|
118
src/services/comps/jinux-virtio/src/device/block/device.rs
Normal file
118
src/services/comps/jinux-virtio/src/device/block/device.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use core::hint::spin_loop;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use jinux_frame::offset_of;
|
||||
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
use pod::Pod;
|
||||
|
||||
use crate::{
|
||||
device::block::{BlkReq, BlkResp, ReqType, RespStatus, BLK_SIZE},
|
||||
device::VirtioDeviceError,
|
||||
queue::VirtQueue,
|
||||
VitrioPciCommonCfg,
|
||||
};
|
||||
|
||||
use super::{BLKFeatures, VirtioBLKConfig};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BLKDevice {
|
||||
config: InFramePtr<VirtioBLKConfig>,
|
||||
queue: VirtQueue,
|
||||
}
|
||||
|
||||
impl BLKDevice {
|
||||
/// Create a new VirtIO-Block driver.
|
||||
/// msix_vector_left should at least have one element or n elements where n is the virtqueue amount
|
||||
pub(crate) fn new(
|
||||
cap: &CapabilityVirtioData,
|
||||
bars: [Option<BAR>; 6],
|
||||
common_cfg: &InFramePtr<VitrioPciCommonCfg>,
|
||||
notify_base_address: usize,
|
||||
notify_off_multiplier: u32,
|
||||
mut msix_vector_left: Vec<u16>,
|
||||
) -> Result<Self, VirtioDeviceError> {
|
||||
let config = VirtioBLKConfig::new(cap, bars);
|
||||
let num_queues = common_cfg.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as u16;
|
||||
if num_queues != 1 {
|
||||
return Err(VirtioDeviceError::QueuesAmountDoNotMatch(num_queues, 1));
|
||||
}
|
||||
let queue = VirtQueue::new(
|
||||
&common_cfg,
|
||||
0 as usize,
|
||||
128,
|
||||
notify_base_address as usize,
|
||||
notify_off_multiplier,
|
||||
msix_vector_left.pop().unwrap(),
|
||||
)
|
||||
.expect("create virtqueue failed");
|
||||
Ok(Self { config, queue })
|
||||
}
|
||||
|
||||
/// Negotiate features for the device specified bits 0~23
|
||||
pub(crate) fn negotiate_features(features: u64) -> u64 {
|
||||
let feature = BLKFeatures::from_bits(features).unwrap();
|
||||
let support_features = BLKFeatures::from_bits(features).unwrap();
|
||||
(feature & support_features).bits
|
||||
}
|
||||
|
||||
/// read data from block device, this function is blocking
|
||||
pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) {
|
||||
assert_eq!(buf.len(), BLK_SIZE);
|
||||
let req = BlkReq {
|
||||
type_: ReqType::In,
|
||||
reserved: 0,
|
||||
sector: block_id as u64,
|
||||
};
|
||||
let mut resp = BlkResp::default();
|
||||
self.queue
|
||||
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
|
||||
.expect("add queue failed");
|
||||
self.queue.notify();
|
||||
while !self.queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
self.queue.pop_used().expect("pop used failed");
|
||||
match resp.status {
|
||||
RespStatus::Ok => {}
|
||||
_ => panic!("io error in block device"),
|
||||
};
|
||||
}
|
||||
/// write data to block device, this function is blocking
|
||||
pub fn write_block(&mut self, block_id: usize, buf: &[u8]) {
|
||||
assert_eq!(buf.len(), BLK_SIZE);
|
||||
let req = BlkReq {
|
||||
type_: ReqType::Out,
|
||||
reserved: 0,
|
||||
sector: block_id as u64,
|
||||
};
|
||||
let mut resp = BlkResp::default();
|
||||
self.queue
|
||||
.add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()])
|
||||
.expect("add queue failed");
|
||||
self.queue.notify();
|
||||
|
||||
while !self.queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
self.queue.pop_used().expect("pop used failed");
|
||||
let st = resp.status;
|
||||
match st {
|
||||
RespStatus::Ok => {}
|
||||
_ => panic!("io error in block device:{:?}", st),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn read_block_nb(&mut self, block_id: usize, buf: &mut [u8], resp: &mut BlkResp) {
|
||||
assert_eq!(buf.len(), BLK_SIZE);
|
||||
let req = BlkReq {
|
||||
type_: ReqType::In,
|
||||
reserved: 0,
|
||||
sector: block_id as u64,
|
||||
};
|
||||
self.queue
|
||||
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
|
||||
.unwrap();
|
||||
self.queue.notify();
|
||||
}
|
||||
}
|
@ -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 {
|
154
src/services/comps/jinux-virtio/src/device/input/device.rs
Normal file
154
src/services/comps/jinux-virtio/src/device/input/device.rs
Normal file
@ -0,0 +1,154 @@
|
||||
use crate::{device::VirtioDeviceError, queue::VirtQueue, VitrioPciCommonCfg};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use jinux_frame::offset_of;
|
||||
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
use pod::Pod;
|
||||
use spin::Mutex;
|
||||
|
||||
use super::{
|
||||
InputConfigSelect, InputEvent, VirtioInputConfig, QUEUE_EVENT, QUEUE_SIZE, QUEUE_STATUS,
|
||||
};
|
||||
|
||||
bitflags! {
|
||||
pub struct InputProp : u8{
|
||||
const POINTER = 0x00;
|
||||
const DIRECT = 0x01;
|
||||
const BUTTONPAD = 0x02;
|
||||
const SEMI_MT = 0x03;
|
||||
const TOPBUTTONPAD = 0x04;
|
||||
const POINTING_STICK = 0x05;
|
||||
const ACCELEROMETER = 0x06;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub const SYN: u8 = 0x00;
|
||||
pub const KEY: u8 = 0x01;
|
||||
pub const REL: u8 = 0x02;
|
||||
pub const ABS: u8 = 0x03;
|
||||
pub const MSC: u8 = 0x04;
|
||||
pub const SW: u8 = 0x05;
|
||||
pub const LED: u8 = 0x11;
|
||||
pub const SND: u8 = 0x12;
|
||||
pub const REP: u8 = 0x14;
|
||||
pub const FF: u8 = 0x15;
|
||||
pub const PWR: u8 = 0x16;
|
||||
pub const FF_STATUS: u8 = 0x17;
|
||||
|
||||
/// Virtual human interface devices such as keyboards, mice and tablets.
|
||||
///
|
||||
/// An instance of the virtio device represents one such input device.
|
||||
/// Device behavior mirrors that of the evdev layer in Linux,
|
||||
/// making pass-through implementations on top of evdev easy.
|
||||
#[derive(Debug)]
|
||||
pub struct InputDevice {
|
||||
config: InFramePtr<VirtioInputConfig>,
|
||||
event_queue: Mutex<VirtQueue>,
|
||||
status_queue: VirtQueue,
|
||||
pub event_buf: Mutex<Box<[InputEvent; QUEUE_SIZE]>>,
|
||||
}
|
||||
|
||||
impl InputDevice {
|
||||
/// Create a new VirtIO-Input driver.
|
||||
/// msix_vector_left should at least have one element or n elements where n is the virtqueue amount
|
||||
pub fn new(
|
||||
cap: &CapabilityVirtioData,
|
||||
bars: [Option<BAR>; 6],
|
||||
common_cfg: &InFramePtr<VitrioPciCommonCfg>,
|
||||
notify_base_address: usize,
|
||||
notify_off_multiplier: u32,
|
||||
mut msix_vector_left: Vec<u16>,
|
||||
) -> Result<Self, VirtioDeviceError> {
|
||||
let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
|
||||
let vector_left = msix_vector_left.len();
|
||||
let mut next_msix_vector = msix_vector_left.pop().unwrap();
|
||||
let mut event_queue = VirtQueue::new(
|
||||
&common_cfg,
|
||||
QUEUE_EVENT,
|
||||
QUEUE_SIZE as u16,
|
||||
notify_base_address,
|
||||
notify_off_multiplier,
|
||||
next_msix_vector,
|
||||
)
|
||||
.expect("create event virtqueue failed");
|
||||
next_msix_vector = if vector_left == 1 {
|
||||
next_msix_vector
|
||||
} else {
|
||||
msix_vector_left.pop().unwrap()
|
||||
};
|
||||
let status_queue = VirtQueue::new(
|
||||
&common_cfg,
|
||||
QUEUE_STATUS,
|
||||
QUEUE_SIZE as u16,
|
||||
notify_base_address,
|
||||
notify_off_multiplier,
|
||||
next_msix_vector,
|
||||
)
|
||||
.expect("create status virtqueue failed");
|
||||
|
||||
for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
|
||||
let token = event_queue.add(&[], &[event.as_bytes_mut()]);
|
||||
match token {
|
||||
Ok(value) => {
|
||||
assert_eq!(value, i as u16);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(VirtioDeviceError::QueueUnknownError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
config: VirtioInputConfig::new(cap, bars),
|
||||
event_queue: Mutex::new(event_queue),
|
||||
status_queue,
|
||||
event_buf: Mutex::new(event_buf),
|
||||
})
|
||||
}
|
||||
|
||||
// /// Acknowledge interrupt and process events.
|
||||
// pub fn ack_interrupt(&mut self) -> bool {
|
||||
// self.transport.ack_interrupt()
|
||||
// }
|
||||
|
||||
/// Pop the pending event.
|
||||
pub fn pop_pending_event(&self) -> Option<InputEvent> {
|
||||
let mut lock = self.event_queue.lock();
|
||||
if let Ok((token, _)) = lock.pop_used() {
|
||||
if token >= QUEUE_SIZE as u16 {
|
||||
return None;
|
||||
}
|
||||
let event = &mut self.event_buf.lock()[token as usize];
|
||||
// requeue
|
||||
if let Ok(new_token) = lock.add(&[], &[event.as_bytes_mut()]) {
|
||||
// This only works because nothing happen between `pop_used` and `add` that affects
|
||||
// the list of free descriptors in the queue, so `add` reuses the descriptor which
|
||||
// was just freed by `pop_used`.
|
||||
assert_eq!(new_token, token);
|
||||
return Some(*event);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Query a specific piece of information by `select` and `subsel`, and write
|
||||
/// result to `out`, return the result size.
|
||||
pub fn query_config_select(&self, select: InputConfigSelect, subsel: u8, out: &mut [u8]) -> u8 {
|
||||
self.config
|
||||
.write_at(offset_of!(VirtioInputConfig, select), select as u8);
|
||||
self.config
|
||||
.write_at(offset_of!(VirtioInputConfig, subsel), subsel as u8);
|
||||
let size = self.config.read_at(offset_of!(VirtioInputConfig, size));
|
||||
let data: [u8; 128] = self.config.read_at(offset_of!(VirtioInputConfig, data));
|
||||
out[..size as usize].copy_from_slice(&data[..size as usize]);
|
||||
size
|
||||
}
|
||||
|
||||
/// Negotiate features for the device specified bits 0~23
|
||||
pub(crate) fn negotiate_features(features: u64) -> u64 {
|
||||
assert_eq!(features, 0);
|
||||
0
|
||||
}
|
||||
}
|
94
src/services/comps/jinux-virtio/src/device/input/mod.rs
Normal file
94
src/services/comps/jinux-virtio/src/device/input/mod.rs
Normal file
@ -0,0 +1,94 @@
|
||||
pub mod device;
|
||||
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
|
||||
/// Select value used for [`VirtIOInput::query_config_select()`].
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum InputConfigSelect {
|
||||
/// Returns the name of the device, in u.string. subsel is zero.
|
||||
IdName = 0x01,
|
||||
/// Returns the serial number of the device, in u.string. subsel is zero.
|
||||
IdSerial = 0x02,
|
||||
/// Returns ID information of the device, in u.ids. subsel is zero.
|
||||
IdDevids = 0x03,
|
||||
/// Returns input properties of the device, in u.bitmap. subsel is zero.
|
||||
/// Individual bits in the bitmap correspond to INPUT_PROP_* constants used
|
||||
/// by the underlying evdev implementation.
|
||||
PropBits = 0x10,
|
||||
/// subsel specifies the event type using EV_* constants in the underlying
|
||||
/// evdev implementation. If size is non-zero the event type is supported
|
||||
/// and a bitmap of supported event codes is returned in u.bitmap. Individual
|
||||
/// bits in the bitmap correspond to implementation-defined input event codes,
|
||||
/// for example keys or pointing device axes.
|
||||
EvBits = 0x11,
|
||||
/// subsel specifies the absolute axis using ABS_* constants in the underlying
|
||||
/// evdev implementation. Information about the axis will be returned in u.abs.
|
||||
AbsInfo = 0x12,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct VirtioInputConfig {
|
||||
/// write only
|
||||
select: u8,
|
||||
/// write only
|
||||
subsel: u8,
|
||||
/// read only
|
||||
size: u8,
|
||||
_reversed: [u8; 5],
|
||||
/// read only
|
||||
data: [u8; 128],
|
||||
}
|
||||
|
||||
impl VirtioInputConfig {
|
||||
pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option<BAR>; 6]) -> InFramePtr<Self> {
|
||||
let bar = cap.bar;
|
||||
let offset = cap.offset;
|
||||
match bars[bar as usize].expect("Virtio pci block cfg:bar is none") {
|
||||
BAR::Memory(address, _, _, _) => InFramePtr::new(address as usize + offset as usize)
|
||||
.expect("can not get in frame ptr for virtio block config"),
|
||||
BAR::IO(_, _) => {
|
||||
panic!("Virtio pci block cfg:bar is IO type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct AbsInfo {
|
||||
min: u32,
|
||||
max: u32,
|
||||
fuzz: u32,
|
||||
flat: u32,
|
||||
res: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct DevIDs {
|
||||
bustype: u16,
|
||||
vendor: u16,
|
||||
product: u16,
|
||||
version: u16,
|
||||
}
|
||||
|
||||
/// Both queues use the same `virtio_input_event` struct. `type`, `code` and `value`
|
||||
/// are filled according to the Linux input layer (evdev) interface.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Pod)]
|
||||
pub struct InputEvent {
|
||||
/// Event type.
|
||||
pub event_type: u16,
|
||||
/// Event code.
|
||||
pub code: u16,
|
||||
/// Event value.
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
const QUEUE_EVENT: usize = 0;
|
||||
const QUEUE_STATUS: usize = 1;
|
||||
|
||||
// a parameter that can change
|
||||
const QUEUE_SIZE: usize = 64;
|
160
src/services/comps/jinux-virtio/src/device/mod.rs
Normal file
160
src/services/comps/jinux-virtio/src/device/mod.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use crate::{device::block::device::BLKDevice, Feature, VirtioDeviceType, VitrioPciCommonCfg};
|
||||
use alloc::vec::Vec;
|
||||
use jinux_pci::{
|
||||
capability::{vendor::virtio::CapabilityVirtioData, Capability},
|
||||
util::BAR,
|
||||
};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
|
||||
use self::input::device::InputDevice;
|
||||
|
||||
pub mod block;
|
||||
pub mod input;
|
||||
|
||||
pub(crate) const PCI_VIRTIO_CAP_COMMON_CFG: u8 = 1;
|
||||
pub(crate) const PCI_VIRTIO_CAP_NOTIFY_CFG: u8 = 2;
|
||||
pub(crate) const PCI_VIRTIO_CAP_ISR_CFG: u8 = 3;
|
||||
pub(crate) const PCI_VIRTIO_CAP_DEVICE_CFG: u8 = 4;
|
||||
pub(crate) const PCI_VIRTIO_CAP_PCI_CFG: u8 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VirtioDevice {
|
||||
Network,
|
||||
Block(BLKDevice),
|
||||
Console,
|
||||
Entropy,
|
||||
TraditionalMemoryBalloon,
|
||||
ScsiHost,
|
||||
GPU,
|
||||
Input(InputDevice),
|
||||
Crypto,
|
||||
Socket,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VirtioDeviceError {
|
||||
/// queues amount do not match the requirement
|
||||
/// first element is actual value, second element is expect value
|
||||
QueuesAmountDoNotMatch(u16, u16),
|
||||
/// unknown error of queue
|
||||
QueueUnknownError,
|
||||
/// The input virtio capability list contains invalid element
|
||||
CapabilityListError,
|
||||
}
|
||||
|
||||
pub struct VirtioInfo {
|
||||
pub device_type: VirtioDeviceType,
|
||||
pub notify_base_address: u64,
|
||||
pub notify_off_multiplier: u32,
|
||||
pub common_cfg_frame_ptr: InFramePtr<VitrioPciCommonCfg>,
|
||||
pub device_cap_cfg: CapabilityVirtioData,
|
||||
}
|
||||
|
||||
impl VirtioInfo {
|
||||
pub(crate) fn new(
|
||||
device_type: VirtioDeviceType,
|
||||
bars: [Option<BAR>; 6],
|
||||
virtio_cap_list: Vec<&Capability>,
|
||||
) -> Result<Self, VirtioDeviceError> {
|
||||
let mut notify_base_address = 0;
|
||||
let mut notify_off_multiplier = 0;
|
||||
let mut common_cfg_frame_ptr_some = None;
|
||||
let mut device_cap_cfg = None;
|
||||
for cap in virtio_cap_list.iter() {
|
||||
match cap.data {
|
||||
jinux_pci::capability::CapabilityData::VNDR(vndr_data) => match vndr_data {
|
||||
jinux_pci::capability::vendor::CapabilityVNDRData::VIRTIO(cap_data) => {
|
||||
match cap_data.cfg_type {
|
||||
PCI_VIRTIO_CAP_COMMON_CFG => {
|
||||
common_cfg_frame_ptr_some =
|
||||
Some(VitrioPciCommonCfg::new(&cap_data, bars));
|
||||
}
|
||||
PCI_VIRTIO_CAP_NOTIFY_CFG => {
|
||||
notify_off_multiplier = cap_data.option.unwrap();
|
||||
match bars[cap_data.bar as usize]
|
||||
.expect("initialize PCIDevice failed, notify bar is None")
|
||||
{
|
||||
BAR::Memory(address, _, _, _) => {
|
||||
notify_base_address = address + cap_data.offset as u64;
|
||||
}
|
||||
BAR::IO(_, _) => {
|
||||
panic!("initialize PCIDevice failed, notify bar is IO Type")
|
||||
}
|
||||
};
|
||||
}
|
||||
PCI_VIRTIO_CAP_ISR_CFG => {}
|
||||
PCI_VIRTIO_CAP_DEVICE_CFG => {
|
||||
device_cap_cfg = Some(cap_data);
|
||||
}
|
||||
PCI_VIRTIO_CAP_PCI_CFG => {}
|
||||
_ => panic!("unsupport cfg, cfg_type:{}", cap_data.cfg_type),
|
||||
};
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(VirtioDeviceError::CapabilityListError);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
notify_base_address,
|
||||
notify_off_multiplier,
|
||||
common_cfg_frame_ptr: common_cfg_frame_ptr_some
|
||||
.ok_or(VirtioDeviceError::CapabilityListError)?,
|
||||
device_cap_cfg: device_cap_cfg.ok_or(VirtioDeviceError::CapabilityListError)?,
|
||||
device_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioDevice {
|
||||
/// call this function after features_ok
|
||||
pub(crate) fn new(
|
||||
virtio_info: &VirtioInfo,
|
||||
bars: [Option<BAR>; 6],
|
||||
msix_vector_left: Vec<u16>,
|
||||
) -> Result<Self, VirtioDeviceError> {
|
||||
let device = match virtio_info.device_type {
|
||||
VirtioDeviceType::Block => VirtioDevice::Block(BLKDevice::new(
|
||||
&virtio_info.device_cap_cfg,
|
||||
bars,
|
||||
&virtio_info.common_cfg_frame_ptr,
|
||||
virtio_info.notify_base_address as usize,
|
||||
virtio_info.notify_off_multiplier,
|
||||
msix_vector_left,
|
||||
)?),
|
||||
VirtioDeviceType::Input => VirtioDevice::Input(InputDevice::new(
|
||||
&virtio_info.device_cap_cfg,
|
||||
bars,
|
||||
&virtio_info.common_cfg_frame_ptr,
|
||||
virtio_info.notify_base_address as usize,
|
||||
virtio_info.notify_off_multiplier,
|
||||
msix_vector_left,
|
||||
)?),
|
||||
_ => {
|
||||
panic!("initialize PCIDevice failed, unsupport Virtio Device Type")
|
||||
}
|
||||
};
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
pub(crate) fn negotiate_features(features: u64, device_type: VirtioDeviceType) -> u64 {
|
||||
let device_specified_features = features & ((1 << 24) - 1);
|
||||
let device_support_features = match device_type {
|
||||
VirtioDeviceType::Network => todo!(),
|
||||
VirtioDeviceType::Block => BLKDevice::negotiate_features(device_specified_features),
|
||||
VirtioDeviceType::Console => todo!(),
|
||||
VirtioDeviceType::Entropy => todo!(),
|
||||
VirtioDeviceType::TraditionalMemoryBalloon => todo!(),
|
||||
VirtioDeviceType::ScsiHost => todo!(),
|
||||
VirtioDeviceType::GPU => todo!(),
|
||||
VirtioDeviceType::Input => InputDevice::negotiate_features(device_specified_features),
|
||||
VirtioDeviceType::Crypto => todo!(),
|
||||
VirtioDeviceType::Socket => todo!(),
|
||||
};
|
||||
let support_feature = Feature::from_bits_truncate(features);
|
||||
// support_feature.remove(Feature::RING_EVENT_IDX);
|
||||
features & (support_feature.bits | device_support_features)
|
||||
}
|
||||
}
|
@ -4,27 +4,23 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use jinux_frame::{info, offset_of, TrapFrame};
|
||||
use device::VirtioDevice;
|
||||
use jinux_frame::{debug, info, offset_of, TrapFrame};
|
||||
use jinux_pci::util::{PCIDevice, BAR};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
use pod_derive::Pod;
|
||||
|
||||
use spin::{mutex::Mutex, MutexGuard};
|
||||
|
||||
use self::{block::VirtioBLKConfig, queue::VirtQueue};
|
||||
use crate::device::VirtioInfo;
|
||||
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, msix::MSIX};
|
||||
#[macro_use]
|
||||
extern crate pod_derive;
|
||||
|
||||
pub mod block;
|
||||
pub mod queue;
|
||||
pub mod device;
|
||||
|
||||
pub(crate) const PCI_VIRTIO_CAP_COMMON_CFG: u8 = 1;
|
||||
pub(crate) const PCI_VIRTIO_CAP_NOTIFY_CFG: u8 = 2;
|
||||
pub(crate) const PCI_VIRTIO_CAP_ISR_CFG: u8 = 3;
|
||||
pub(crate) const PCI_VIRTIO_CAP_DEVICE_CFG: u8 = 4;
|
||||
pub(crate) const PCI_VIRTIO_CAP_PCI_CFG: u8 = 5;
|
||||
pub mod queue;
|
||||
|
||||
bitflags! {
|
||||
/// The device status field.
|
||||
@ -55,6 +51,30 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// all device features, bits 0~23 are sepecified by device
|
||||
/// if using this struct to translate u64, use from_bits_truncate function instead of from_bits
|
||||
///
|
||||
struct Feature: u64 {
|
||||
|
||||
// device independent
|
||||
const NOTIFY_ON_EMPTY = 1 << 24; // legacy
|
||||
const ANY_LAYOUT = 1 << 27; // legacy
|
||||
const RING_INDIRECT_DESC = 1 << 28;
|
||||
const RING_EVENT_IDX = 1 << 29;
|
||||
const UNUSED = 1 << 30; // legacy
|
||||
const VERSION_1 = 1 << 32; // detect legacy
|
||||
|
||||
// since virtio v1.1
|
||||
const ACCESS_PLATFORM = 1 << 33;
|
||||
const RING_PACKED = 1 << 34;
|
||||
const IN_ORDER = 1 << 35;
|
||||
const ORDER_PLATFORM = 1 << 36;
|
||||
const SR_IOV = 1 << 37;
|
||||
const NOTIFICATION_DATA = 1 << 38;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct VitrioPciCommonCfg {
|
||||
@ -62,14 +82,14 @@ pub struct VitrioPciCommonCfg {
|
||||
device_feature: u32,
|
||||
driver_feature_select: u32,
|
||||
driver_feature: u32,
|
||||
config_msix_vector: u16,
|
||||
pub config_msix_vector: u16,
|
||||
num_queues: u16,
|
||||
device_status: u8,
|
||||
pub device_status: u8,
|
||||
config_generation: u8,
|
||||
|
||||
queue_select: u16,
|
||||
queue_size: u16,
|
||||
queue_msix_vector: u16,
|
||||
pub queue_msix_vector: u16,
|
||||
queue_enable: u16,
|
||||
queue_notify_off: u16,
|
||||
queue_desc: u64,
|
||||
@ -83,19 +103,22 @@ impl VitrioPciCommonCfg {
|
||||
let offset = cap.offset;
|
||||
match bars[bar as usize].expect("Virtio pci common cfg:bar is none") {
|
||||
BAR::Memory(address, _, _, _) => {
|
||||
info!("common_cfg addr:{:x}", (address as usize + offset as usize));
|
||||
debug!("common_cfg addr:{:x}", (address as usize + offset as usize));
|
||||
InFramePtr::new(address as usize + offset as usize)
|
||||
.expect("cannot get InFramePtr in VitioPciCommonCfg")
|
||||
}
|
||||
BAR::IO(_, _) => {
|
||||
panic!("Virtio pci common cfg:bar is IO type")
|
||||
BAR::IO(first, second) => {
|
||||
panic!(
|
||||
"Virtio pci common cfg:bar is IO type, value:{:x}, {:x}",
|
||||
first, second
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum VirtioDeviceType {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VirtioDeviceType {
|
||||
Network,
|
||||
Block,
|
||||
Console,
|
||||
@ -107,34 +130,17 @@ enum VirtioDeviceType {
|
||||
Crypto,
|
||||
Socket,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum VirtioDevice {
|
||||
Network,
|
||||
Block(InFramePtr<VirtioBLKConfig>),
|
||||
Console,
|
||||
Entropy,
|
||||
TraditionalMemoryBalloon,
|
||||
ScsiHost,
|
||||
GPU,
|
||||
Input,
|
||||
Crypto,
|
||||
Socket,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub struct PCIVirtioDevice {
|
||||
/// common config of one device
|
||||
common_cfg: InFramePtr<VitrioPciCommonCfg>,
|
||||
device: VirtioDevice,
|
||||
queues: Vec<Arc<Mutex<VirtQueue>>>,
|
||||
msix: MSIX,
|
||||
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
|
||||
pub device: VirtioDevice,
|
||||
pub msix: MSIX,
|
||||
}
|
||||
|
||||
impl PCIVirtioDevice {
|
||||
/// create a new PCI Virtio Device, note that this function will stop with device status features ok
|
||||
pub fn new(dev: Arc<PCIDevice>) -> Self {
|
||||
if dev.id.vendor_id != 0x1af4 {
|
||||
panic!("initialize PCIDevice failed, wrong PCI vendor id");
|
||||
}
|
||||
assert_eq!(dev.id.vendor_id, 0x1af4);
|
||||
let device_type = match dev.id.device_id {
|
||||
0x1000 | 0x1041 => VirtioDeviceType::Network,
|
||||
0x1001 | 0x1042 => VirtioDeviceType::Block,
|
||||
@ -143,58 +149,21 @@ impl PCIVirtioDevice {
|
||||
0x1004 | 0x1045 => VirtioDeviceType::ScsiHost,
|
||||
0x1005 | 0x1046 => VirtioDeviceType::Entropy,
|
||||
// 0x1009 | 0x104a => VirtioDeviceType::,
|
||||
0x1011 | 0x1052 => VirtioDeviceType::Input,
|
||||
_ => {
|
||||
panic!("initialize PCIDevice failed, unrecognized Virtio Device Type")
|
||||
}
|
||||
};
|
||||
info!("PCI device:{:?}", device_type);
|
||||
let bars = dev.bars;
|
||||
let loc = dev.loc;
|
||||
let mut notify_base_address = 0;
|
||||
let mut notify_off_multiplier = 0;
|
||||
let mut device = VirtioDevice::Unknown;
|
||||
let mut msix = MSIX::default();
|
||||
let mut common_cfg_frame_ptr_some = None;
|
||||
let mut virtio_cap_list = Vec::new();
|
||||
for cap in dev.capabilities.iter() {
|
||||
match &cap.data {
|
||||
jinux_pci::capability::CapabilityData::VNDR(vndr_data) => match vndr_data {
|
||||
jinux_pci::capability::vendor::CapabilityVNDRData::VIRTIO(cap_data) => {
|
||||
match cap_data.cfg_type {
|
||||
PCI_VIRTIO_CAP_COMMON_CFG => {
|
||||
common_cfg_frame_ptr_some =
|
||||
Some(VitrioPciCommonCfg::new(cap_data, bars));
|
||||
}
|
||||
PCI_VIRTIO_CAP_NOTIFY_CFG => {
|
||||
notify_off_multiplier = cap_data.option.unwrap();
|
||||
match bars[cap_data.bar as usize]
|
||||
.expect("initialize PCIDevice failed, notify bar is None")
|
||||
{
|
||||
BAR::Memory(address, _, _, _) => {
|
||||
notify_base_address = address + cap_data.offset as u64;
|
||||
}
|
||||
BAR::IO(_, _) => {
|
||||
panic!("initialize PCIDevice failed, notify bar is IO Type")
|
||||
}
|
||||
};
|
||||
}
|
||||
PCI_VIRTIO_CAP_ISR_CFG => {}
|
||||
PCI_VIRTIO_CAP_DEVICE_CFG => {
|
||||
device = match device_type {
|
||||
VirtioDeviceType::Block => {
|
||||
VirtioDevice::Block(VirtioBLKConfig::new(&cap_data, bars))
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"initialize PCIDevice failed, unsupport Virtio Device Type"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
PCI_VIRTIO_CAP_PCI_CFG => {}
|
||||
_ => panic!("unsupport cfg, cfg_type:{}", cap_data.cfg_type),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
jinux_pci::capability::CapabilityData::VNDR(_) => {
|
||||
virtio_cap_list.push(cap);
|
||||
}
|
||||
jinux_pci::capability::CapabilityData::MSIX(cap_data) => {
|
||||
msix = MSIX::new(&cap_data, bars, loc, cap.cap_ptr);
|
||||
}
|
||||
@ -206,86 +175,86 @@ impl PCIVirtioDevice {
|
||||
}
|
||||
}
|
||||
}
|
||||
let common_cfg_frame_ptr = if common_cfg_frame_ptr_some.is_none() {
|
||||
panic!("Vitio Common cfg is None")
|
||||
} else {
|
||||
common_cfg_frame_ptr_some.unwrap()
|
||||
};
|
||||
info!(
|
||||
"common_cfg_num_queues:{:x}",
|
||||
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues))
|
||||
);
|
||||
// let b : InFramePtr<u8> = InFramePtr::new(common_cfg_frame_ptr.paddr()+19).expect("test");
|
||||
// info!("test_Aaaaaa:{:#x?}",b.read());
|
||||
if msix.table_size
|
||||
!= common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as u16
|
||||
{
|
||||
panic!("the msix table size is not match with the number of queues");
|
||||
}
|
||||
let mut queues = Vec::new();
|
||||
// create device
|
||||
let virtio_info = VirtioInfo::new(device_type, bars, virtio_cap_list).unwrap();
|
||||
let mut msix_vector_list: Vec<u16> = (0..msix.table_size).collect();
|
||||
let config_msix_vector = msix_vector_list.pop().unwrap();
|
||||
let common_cfg_frame_ptr = &virtio_info.common_cfg_frame_ptr;
|
||||
let num_queues: u16 =
|
||||
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues));
|
||||
debug!("num_queues:{:x}", num_queues);
|
||||
// the table size of msix should be equal to n+1 or 2 where n is the virtqueue amount
|
||||
assert!(msix.table_size == 2 || msix.table_size == (num_queues + 1));
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, device_status),
|
||||
DeviceStatus::ACKNOWLEDGE.bits(),
|
||||
offset_of!(VitrioPciCommonCfg, config_msix_vector),
|
||||
config_msix_vector,
|
||||
);
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, device_status),
|
||||
DeviceStatus::DRIVER.bits(),
|
||||
(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER).bits(),
|
||||
);
|
||||
// negotiate features
|
||||
// get the value of device features
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, device_feature_select),
|
||||
0 as u32,
|
||||
);
|
||||
let mut low: u32 =
|
||||
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, device_feature));
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, device_feature_select),
|
||||
1 as u32,
|
||||
);
|
||||
let mut high: u32 =
|
||||
common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, device_feature));
|
||||
let mut feature = (high as u64) << 32;
|
||||
feature |= low as u64;
|
||||
// let the device to negotiate Features
|
||||
let driver_features = VirtioDevice::negotiate_features(feature, device_type);
|
||||
debug!("support_features:{:x}", driver_features);
|
||||
// write features back
|
||||
low = driver_features as u32;
|
||||
high = (driver_features >> 32) as u32;
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, driver_feature_select),
|
||||
0 as u32,
|
||||
);
|
||||
common_cfg_frame_ptr.write_at(offset_of!(VitrioPciCommonCfg, driver_feature), low);
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, driver_feature_select),
|
||||
1 as u32,
|
||||
);
|
||||
common_cfg_frame_ptr.write_at(offset_of!(VitrioPciCommonCfg, driver_feature), high);
|
||||
|
||||
// change to features ok status
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, device_status),
|
||||
DeviceStatus::FEATURES_OK.bits(),
|
||||
(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK).bits(),
|
||||
);
|
||||
for i in 0..common_cfg_frame_ptr.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as u16
|
||||
{
|
||||
queues.push(Arc::new(Mutex::new(
|
||||
VirtQueue::new(
|
||||
&common_cfg_frame_ptr,
|
||||
i as usize,
|
||||
16,
|
||||
notify_base_address as usize,
|
||||
notify_off_multiplier,
|
||||
i,
|
||||
)
|
||||
.expect("create virtqueue failed"),
|
||||
)));
|
||||
}
|
||||
let device = VirtioDevice::new(&virtio_info, bars, msix_vector_list).unwrap();
|
||||
|
||||
// change to driver ok status
|
||||
common_cfg_frame_ptr.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, device_status),
|
||||
DeviceStatus::DRIVER_OK.bits(),
|
||||
(DeviceStatus::ACKNOWLEDGE
|
||||
| DeviceStatus::DRIVER
|
||||
| DeviceStatus::FEATURES_OK
|
||||
| DeviceStatus::DRIVER_OK)
|
||||
.bits(),
|
||||
);
|
||||
Self {
|
||||
common_cfg: common_cfg_frame_ptr,
|
||||
common_cfg: virtio_info.common_cfg_frame_ptr,
|
||||
device,
|
||||
queues,
|
||||
msix,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_queue(&self, queue_index: u16) -> MutexGuard<VirtQueue> {
|
||||
self.queues
|
||||
.get(queue_index as usize)
|
||||
.expect("index out of range")
|
||||
.lock()
|
||||
}
|
||||
|
||||
/// register the queue interrupt functions, this function should call only once
|
||||
pub fn register_queue_interrupt_functions<F>(&mut self, functions: &mut Vec<F>)
|
||||
/// register all the interrupt functions except the config change, this function should call only once
|
||||
pub fn register_interrupt_functions<F>(&mut self, function: &'static F)
|
||||
where
|
||||
F: Fn(&TrapFrame) + Send + Sync + 'static,
|
||||
{
|
||||
let len = functions.len();
|
||||
if len
|
||||
!= self
|
||||
.common_cfg
|
||||
.read_at(offset_of!(VitrioPciCommonCfg, num_queues)) as usize
|
||||
{
|
||||
panic!("the size of queue interrupt functions not equal to the number of queues, functions amount:{}, queues amount:{}",len,
|
||||
self.common_cfg.read_at(offset_of!(VitrioPciCommonCfg,num_queues)));
|
||||
}
|
||||
|
||||
functions.reverse();
|
||||
for i in 0..len {
|
||||
let function = functions.pop().unwrap();
|
||||
for i in 0..self.msix.table_size as usize {
|
||||
let msix = self.msix.table.get_mut(i).unwrap();
|
||||
if !msix.irq_handle.is_empty() {
|
||||
panic!("function `register_queue_interrupt_functions` called more than one time");
|
||||
@ -293,19 +262,4 @@ impl PCIVirtioDevice {
|
||||
msix.irq_handle.on_active(function);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_subset(smaller: u32, bigger: u32) -> bool {
|
||||
let mut temp: u32 = 1;
|
||||
for _ in 0..31 {
|
||||
if (smaller & temp) > (bigger & temp) {
|
||||
return false;
|
||||
}
|
||||
temp <<= 1;
|
||||
}
|
||||
if (smaller & temp) > (bigger & temp) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,10 @@ use super::VitrioPciCommonCfg;
|
||||
use alloc::vec::Vec;
|
||||
use bitflags::bitflags;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use jinux_frame::offset_of;
|
||||
use jinux_frame::{
|
||||
offset_of,
|
||||
vm::{VmAllocOptions, VmFrame, VmFrameVec},
|
||||
};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
#[derive(Debug)]
|
||||
pub enum QueueError {
|
||||
@ -38,9 +41,13 @@ pub struct VirtQueue {
|
||||
/// The number of used queues.
|
||||
num_used: u16,
|
||||
/// The head desc index of the free list.
|
||||
free_head: u16,
|
||||
pub free_head: u16,
|
||||
/// the index of the next avail ring index
|
||||
avail_idx: u16,
|
||||
/// last service used index
|
||||
last_used_idx: u16,
|
||||
|
||||
physical_frame_store: Vec<VmFrame>,
|
||||
}
|
||||
|
||||
impl VirtQueue {
|
||||
@ -49,26 +56,69 @@ impl VirtQueue {
|
||||
cfg: &InFramePtr<VitrioPciCommonCfg>,
|
||||
idx: usize,
|
||||
size: u16,
|
||||
cap_offset: usize,
|
||||
notify_base_address: usize,
|
||||
notify_off_multiplier: u32,
|
||||
msix_vector: u16,
|
||||
) -> Result<Self, QueueError> {
|
||||
cfg.write_at(offset_of!(VitrioPciCommonCfg, queue_select), idx as u16);
|
||||
|
||||
assert_eq!(
|
||||
cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_select)),
|
||||
idx as u16
|
||||
);
|
||||
// info!("actual queue_size:{}",cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_size)));
|
||||
if !size.is_power_of_two() || cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_size)) < size
|
||||
{
|
||||
return Err(QueueError::InvalidArgs);
|
||||
}
|
||||
// Allocate contiguous pages.
|
||||
|
||||
cfg.write_at(offset_of!(VitrioPciCommonCfg, queue_size), size);
|
||||
cfg.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, queue_msix_vector),
|
||||
msix_vector,
|
||||
);
|
||||
assert_eq!(
|
||||
cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_msix_vector)),
|
||||
msix_vector
|
||||
);
|
||||
|
||||
let mut descs = Vec::new();
|
||||
|
||||
//allocate page
|
||||
let mut frame_vec = Vec::new();
|
||||
let vm_allocate_option = VmAllocOptions::new(1);
|
||||
if cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_desc)) == 0 as u64 {
|
||||
let frame = VmFrameVec::allocate(&vm_allocate_option)
|
||||
.expect("cannot allocate physical frame for virtqueue")
|
||||
.remove(0);
|
||||
cfg.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, queue_desc),
|
||||
frame.paddr() as u64,
|
||||
);
|
||||
frame_vec.push(frame);
|
||||
}
|
||||
|
||||
if cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_driver)) == 0 as u64 {
|
||||
let frame = VmFrameVec::allocate(&vm_allocate_option)
|
||||
.expect("cannot allocate physical frame for virtqueue")
|
||||
.remove(0);
|
||||
cfg.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, queue_driver),
|
||||
frame.paddr() as u64,
|
||||
);
|
||||
frame_vec.push(frame);
|
||||
}
|
||||
|
||||
if cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_device)) == 0 as u64 {
|
||||
let frame = VmFrameVec::allocate(&vm_allocate_option)
|
||||
.expect("cannot allocate physical frame for virtqueue")
|
||||
.remove(0);
|
||||
cfg.write_at(
|
||||
offset_of!(VitrioPciCommonCfg, queue_device),
|
||||
frame.paddr() as u64,
|
||||
);
|
||||
frame_vec.push(frame);
|
||||
}
|
||||
|
||||
for i in 0..size {
|
||||
descs.push(
|
||||
InFramePtr::new(
|
||||
@ -85,13 +135,14 @@ impl VirtQueue {
|
||||
let used =
|
||||
InFramePtr::new(cfg.read_at(offset_of!(VitrioPciCommonCfg, queue_device)) as usize)
|
||||
.expect("can not get Inframeptr for virtio queue used ring");
|
||||
let notify = InFramePtr::new(cap_offset + notify_off_multiplier as usize * idx)
|
||||
let notify = InFramePtr::new(notify_base_address + notify_off_multiplier as usize * idx)
|
||||
.expect("can not get Inframeptr for virtio queue notify");
|
||||
// Link descriptors together.
|
||||
for i in 0..(size - 1) {
|
||||
let temp = descs.get(i as usize).unwrap();
|
||||
temp.write_at(offset_of!(Descriptor, next), i + 1);
|
||||
}
|
||||
avail.write_at(offset_of!(AvailRing, flags), 0 as u16);
|
||||
cfg.write_at(offset_of!(VitrioPciCommonCfg, queue_enable), 1 as u16);
|
||||
Ok(VirtQueue {
|
||||
descs,
|
||||
@ -104,6 +155,7 @@ impl VirtQueue {
|
||||
free_head: 0,
|
||||
avail_idx: 0,
|
||||
last_used_idx: 0,
|
||||
physical_frame_store: frame_vec,
|
||||
})
|
||||
}
|
||||
|
||||
@ -161,6 +213,8 @@ impl VirtQueue {
|
||||
self.avail_idx = self.avail_idx.wrapping_add(1);
|
||||
self.avail
|
||||
.write_at(offset_of!(AvailRing, idx), self.avail_idx);
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
@ -180,6 +234,13 @@ impl VirtQueue {
|
||||
fn recycle_descriptors(&mut self, mut head: u16) {
|
||||
let origin_free_head = self.free_head;
|
||||
self.free_head = head;
|
||||
let last_free_head = if head == 0 {
|
||||
self.queue_size - 1
|
||||
} else {
|
||||
head - 1
|
||||
};
|
||||
let temp_desc = &mut self.descs[last_free_head as usize];
|
||||
temp_desc.write_at(offset_of!(Descriptor, next), head);
|
||||
loop {
|
||||
let desc = &mut self.descs[head as usize];
|
||||
let flags: DescFlags = desc.read_at(offset_of!(Descriptor, flags));
|
||||
@ -204,11 +265,12 @@ impl VirtQueue {
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let last_used_slot = self.last_used_idx & (self.queue_size - 1);
|
||||
let last_used = self.used.read_at(
|
||||
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8) as *const UsedElem,
|
||||
let index = self.used.read_at(
|
||||
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8) as *const u32,
|
||||
) as u16;
|
||||
let len = self.used.read_at(
|
||||
(offset_of!(UsedRing, ring) as usize + last_used_slot as usize * 8 + 4) as *const u32,
|
||||
);
|
||||
let index = last_used.id as u16;
|
||||
let len = last_used.len;
|
||||
|
||||
self.recycle_descriptors(index);
|
||||
self.last_used_idx = self.last_used_idx.wrapping_add(1);
|
||||
@ -239,7 +301,8 @@ struct Descriptor {
|
||||
|
||||
impl Descriptor {
|
||||
fn set_buf(&mut self, buf: &[u8]) {
|
||||
self.addr = jinux_frame::virt_to_phys(buf.as_ptr() as usize) as u64;
|
||||
self.addr = jinux_frame::translate_not_offset_virtual_address(buf.as_ptr() as usize) as u64;
|
||||
|
||||
self.len = buf.len() as u32;
|
||||
}
|
||||
}
|
||||
@ -247,7 +310,7 @@ impl Descriptor {
|
||||
fn set_buf(inframe_ptr: &InFramePtr<Descriptor>, buf: &[u8]) {
|
||||
inframe_ptr.write_at(
|
||||
offset_of!(Descriptor, addr),
|
||||
jinux_frame::virt_to_phys(buf.as_ptr() as usize) as u64,
|
||||
jinux_frame::translate_not_offset_virtual_address(buf.as_ptr() as usize) as u64,
|
||||
);
|
||||
inframe_ptr.write_at(offset_of!(Descriptor, len), buf.len() as u32);
|
||||
}
|
||||
@ -272,24 +335,26 @@ impl Default for DescFlags {
|
||||
/// The driver uses the available ring to offer buffers to the device:
|
||||
/// each ring entry refers to the head of a descriptor chain.
|
||||
/// It is only written by the driver and read by the device.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone, Pod)]
|
||||
#[repr(C, align(2))]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct AvailRing {
|
||||
flags: u16,
|
||||
/// A driver MUST NOT decrement the idx.
|
||||
idx: u16,
|
||||
ring: [u16; 32], // actual size: queue_size
|
||||
ring: [u16; 64], // actual size: queue_size
|
||||
used_event: u16, // unused
|
||||
}
|
||||
|
||||
/// The used ring is where the device returns buffers once it is done with them:
|
||||
/// it is only written to by the device, and read by the driver.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone, Pod)]
|
||||
#[repr(C, align(4))]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct UsedRing {
|
||||
// the flag in UsedRing
|
||||
flags: u16,
|
||||
// the next index of the used element in ring array
|
||||
idx: u16,
|
||||
ring: [UsedElem; 32], // actual size: queue_size
|
||||
ring: [UsedElem; 64], // actual size: queue_size
|
||||
avail_event: u16, // unused
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
27
src/services/libs/jinux-std/src/driver/console.rs
Normal file
27
src/services/libs/jinux-std/src/driver/console.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use jinux_frame::TrapFrame;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref KEYBOARD_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
|
||||
Mutex::new(Vec::new());
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
jinux_frame::device::console::register_console_input_callback(handle_irq)
|
||||
}
|
||||
|
||||
fn handle_irq(trap_frame: &TrapFrame) {
|
||||
if KEYBOARD_CALLBACKS.is_locked() {
|
||||
return;
|
||||
}
|
||||
let lock = KEYBOARD_CALLBACKS.lock();
|
||||
for callback in lock.iter() {
|
||||
callback.call(((jinux_frame::device::console::receive_char().unwrap()),));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_console_callback(callback: Arc<dyn Fn(u8) + 'static + Send + Sync>) {
|
||||
KEYBOARD_CALLBACKS.lock().push(callback);
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
pub mod console;
|
||||
pub mod pci;
|
||||
|
||||
pub fn init() {
|
||||
pci::init();
|
||||
console::init();
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -1,66 +1,23 @@
|
||||
use core::hint::spin_loop;
|
||||
|
||||
use crate::process::Process;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use jinux_frame::info;
|
||||
use jinux_pci::msix::MSIX;
|
||||
use jinux_pci::PCIDevice;
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
use jinux_virtio::device::block::device::BLKDevice;
|
||||
use jinux_virtio::device::block::BlkResp;
|
||||
use jinux_virtio::PCIVirtioDevice;
|
||||
use jinux_virtio::VitrioPciCommonCfg;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::mutex::Mutex;
|
||||
|
||||
use super::BlockDevice;
|
||||
pub const BLK_SIZE: usize = 512;
|
||||
use jinux_frame::TrapFrame;
|
||||
use pod::Pod;
|
||||
pub struct VirtioBlockDevice {
|
||||
virtio_device: PCIVirtioDevice,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
pub struct BlkReq {
|
||||
pub type_: ReqType,
|
||||
pub reserved: u32,
|
||||
pub sector: u64,
|
||||
}
|
||||
|
||||
/// Response of a VirtIOBlk request.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
pub struct BlkResp {
|
||||
pub status: RespStatus,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
pub enum ReqType {
|
||||
In = 0,
|
||||
Out = 1,
|
||||
Flush = 4,
|
||||
Discard = 11,
|
||||
WriteZeroes = 13,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Pod)]
|
||||
pub enum RespStatus {
|
||||
/// Ok.
|
||||
Ok = 0,
|
||||
/// IoErr.
|
||||
IoErr = 1,
|
||||
/// Unsupported yet.
|
||||
Unsupported = 2,
|
||||
/// Not ready.
|
||||
_NotReady = 3,
|
||||
}
|
||||
|
||||
impl Default for BlkResp {
|
||||
fn default() -> Self {
|
||||
BlkResp {
|
||||
status: RespStatus::_NotReady,
|
||||
}
|
||||
}
|
||||
blk_device: Mutex<BLKDevice>,
|
||||
pub common_cfg: InFramePtr<VitrioPciCommonCfg>,
|
||||
msix: MSIX,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -68,52 +25,19 @@ lazy_static! {
|
||||
pub static ref BLOCK_DEVICE: Arc<Mutex<Option<VirtioBlockDevice>>> = Arc::new(Mutex::new(None)) ;
|
||||
}
|
||||
|
||||
impl VirtioBlockDevice {
|
||||
pub fn read_block_nb(&self, block_id: usize, buf: &mut [u8], res: &mut BlkResp) {
|
||||
self.blk_device.lock().read_block_nb(block_id, buf, res);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDevice for VirtioBlockDevice {
|
||||
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
|
||||
assert_eq!(buf.len(), BLK_SIZE);
|
||||
let req = BlkReq {
|
||||
type_: ReqType::In,
|
||||
reserved: 0,
|
||||
sector: block_id as u64,
|
||||
};
|
||||
let mut resp = BlkResp::default();
|
||||
let mut queue = self.virtio_device.get_queue(0);
|
||||
queue
|
||||
.add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()])
|
||||
.expect("add queue failed");
|
||||
queue.notify();
|
||||
while !queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
queue.pop_used().expect("pop used failed");
|
||||
match resp.status {
|
||||
RespStatus::Ok => {}
|
||||
_ => panic!("io error in block device"),
|
||||
};
|
||||
self.blk_device.lock().read_block(block_id, buf);
|
||||
}
|
||||
/// it is blocking now
|
||||
fn write_block(&self, block_id: usize, buf: &[u8]) {
|
||||
assert_eq!(buf.len(), BLK_SIZE);
|
||||
let req = BlkReq {
|
||||
type_: ReqType::Out,
|
||||
reserved: 0,
|
||||
sector: block_id as u64,
|
||||
};
|
||||
let mut resp = BlkResp::default();
|
||||
let mut queue = self.virtio_device.get_queue(0);
|
||||
queue
|
||||
.add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()])
|
||||
.expect("add queue failed");
|
||||
queue.notify();
|
||||
|
||||
while !queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
queue.pop_used().expect("pop used failed");
|
||||
match resp.status {
|
||||
RespStatus::Ok => {}
|
||||
_ => panic!("io error in block device"),
|
||||
};
|
||||
self.blk_device.lock().write_block(block_id, buf);
|
||||
}
|
||||
fn handle_irq(&self) {
|
||||
info!("handle irq in block device!");
|
||||
@ -124,12 +48,20 @@ impl VirtioBlockDevice {
|
||||
fn new(mut virtio_device: PCIVirtioDevice) -> Self {
|
||||
fn handle_block_device(frame: &TrapFrame) {
|
||||
info!("pci block device queue interrupt");
|
||||
BLOCK_DEVICE.lock().as_ref().unwrap().handle_irq()
|
||||
BLOCK_DEVICE.lock().as_ref().unwrap().handle_irq();
|
||||
}
|
||||
virtio_device.register_interrupt_functions(&handle_block_device);
|
||||
let blk_device = Mutex::new(match virtio_device.device {
|
||||
jinux_virtio::device::VirtioDevice::Block(blk) => blk,
|
||||
_ => {
|
||||
panic!("Error when creating new block device, the input device is other type of virtio device");
|
||||
}
|
||||
});
|
||||
Self {
|
||||
blk_device,
|
||||
common_cfg: virtio_device.common_cfg,
|
||||
msix: virtio_device.msix,
|
||||
}
|
||||
let mut functions = Vec::new();
|
||||
functions.push(handle_block_device);
|
||||
virtio_device.register_queue_interrupt_functions(&mut functions);
|
||||
Self { virtio_device }
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,37 +69,38 @@ pub fn init(pci_device: Arc<PCIDevice>) {
|
||||
let virtio_device = PCIVirtioDevice::new(pci_device);
|
||||
let mut a = BLOCK_DEVICE.lock();
|
||||
a.replace(VirtioBlockDevice::new(virtio_device));
|
||||
let dev = a.as_ref().unwrap();
|
||||
drop(a);
|
||||
}
|
||||
|
||||
fn inner_block_device_test() {
|
||||
let block_device = BLOCK_DEVICE.clone();
|
||||
let mut write_buffer = [0u8; 512];
|
||||
let mut read_buffer = [0u8; 512];
|
||||
info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize);
|
||||
info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize);
|
||||
for i in 0..512 {
|
||||
for byte in write_buffer.iter_mut() {
|
||||
*byte = i as u8;
|
||||
}
|
||||
info!("write block");
|
||||
block_device
|
||||
.lock()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.write_block(i as usize, &write_buffer);
|
||||
info!("read block");
|
||||
block_device
|
||||
.lock()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read_block(i as usize, &mut read_buffer);
|
||||
assert_eq!(write_buffer, read_buffer);
|
||||
}
|
||||
info!("block device test passed!");
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn block_device_test() {
|
||||
fn inner_block_device_test() {
|
||||
let block_device = BLOCK_DEVICE.clone();
|
||||
let mut write_buffer = [0u8; 512];
|
||||
let mut read_buffer = [0u8; 512];
|
||||
info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize);
|
||||
info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize);
|
||||
for i in 0..512 {
|
||||
for byte in write_buffer.iter_mut() {
|
||||
*byte = i as u8;
|
||||
}
|
||||
block_device
|
||||
.lock()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.write_block(i as usize, &write_buffer);
|
||||
block_device
|
||||
.lock()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read_block(i as usize, &mut read_buffer);
|
||||
assert_eq!(write_buffer, read_buffer);
|
||||
}
|
||||
info!("block device test passed!");
|
||||
}
|
||||
|
||||
let test_process = Process::spawn_kernel_process(|| {
|
||||
inner_block_device_test();
|
||||
// inner_block_device_test();
|
||||
});
|
||||
}
|
||||
|
181
src/services/libs/jinux-std/src/driver/pci/virtio/input.rs
Normal file
181
src/services/libs/jinux-std/src/driver/pci/virtio/input.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use core::any::Any;
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::{string::String, sync::Arc, vec::Vec};
|
||||
use jinux_frame::{debug, info, offset_of, TrapFrame};
|
||||
use jinux_pci::{msix::MSIX, PCIDevice};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
use jinux_virtio::device::input::device::InputProp;
|
||||
use jinux_virtio::VitrioPciCommonCfg;
|
||||
use jinux_virtio::{
|
||||
device::input::{device::InputDevice, InputConfigSelect},
|
||||
PCIVirtioDevice,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use virtio_input_decoder::{DecodeType, Decoder};
|
||||
|
||||
pub trait INPUTDevice: Send + Sync + Any {
|
||||
fn handle_irq(&self) -> Option<()>;
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref KEYBOARD_EVENT: Mutex<Vec<DecodeType>> = Mutex::new(Vec::new());
|
||||
pub static ref MOUSE_EVENT: Mutex<Vec<DecodeType>> = Mutex::new(Vec::new());
|
||||
static ref KEYBOARD_CALLBACKS: Mutex<Vec<Arc<dyn Fn() + Send + Sync + 'static>>> =
|
||||
Mutex::new(Vec::new());
|
||||
static ref INPUT_DEVICE_LIST: Mutex<Vec<Arc<VirtioInputDevice>>> =
|
||||
Mutex::new(Vec::with_capacity(2));
|
||||
static ref INPUT_DEVICE_IRQ_HASH: Mutex<BTreeMap<u16, usize>> = Mutex::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
pub struct VirtioInputDevice {
|
||||
input_device: InputDevice,
|
||||
common_cfg: InFramePtr<VitrioPciCommonCfg>,
|
||||
msix: Mutex<MSIX>,
|
||||
is_keyboard: AtomicBool,
|
||||
}
|
||||
|
||||
impl VirtioInputDevice {
|
||||
fn new(virtio_device: PCIVirtioDevice, id: usize) -> Self {
|
||||
let input_device = match virtio_device.device {
|
||||
jinux_virtio::device::VirtioDevice::Input(dev) => dev,
|
||||
_ => {
|
||||
panic!("Error when creating new input device, the input device is other type of virtio device");
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
input_device,
|
||||
common_cfg: virtio_device.common_cfg,
|
||||
msix: Mutex::new(virtio_device.msix),
|
||||
is_keyboard: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_interrupts(&self, id: usize) {
|
||||
fn handle_input(frame: &TrapFrame) {
|
||||
info!("in handle input");
|
||||
let id = *INPUT_DEVICE_IRQ_HASH
|
||||
.lock()
|
||||
.get(&(frame.id as u16))
|
||||
.expect("wrong irq number in input device trap handler");
|
||||
INPUT_DEVICE_LIST
|
||||
.lock()
|
||||
.get(id)
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.handle_irq();
|
||||
}
|
||||
fn config_space_change(frame: &TrapFrame) {}
|
||||
|
||||
let config_msix_vector =
|
||||
self.common_cfg
|
||||
.read_at(offset_of!(VitrioPciCommonCfg, config_msix_vector)) as usize;
|
||||
let mut device_hash_lock = INPUT_DEVICE_IRQ_HASH.lock();
|
||||
let mut msix = self.msix.lock();
|
||||
for i in 0..msix.table_size as usize {
|
||||
if i == config_msix_vector {
|
||||
continue;
|
||||
}
|
||||
device_hash_lock.insert(msix.table.get(i).unwrap().irq_handle.num() as u16, id);
|
||||
}
|
||||
drop(device_hash_lock);
|
||||
for i in 0..msix.table_size as usize {
|
||||
let msix = msix.table.get_mut(i).unwrap();
|
||||
if !msix.irq_handle.is_empty() {
|
||||
panic!("function `register_queue_interrupt_functions` called more than one time");
|
||||
}
|
||||
if config_msix_vector == i {
|
||||
msix.irq_handle.on_active(config_space_change);
|
||||
} else {
|
||||
msix.irq_handle.on_active(handle_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_device_information(&self) {
|
||||
let mut raw_name: [u8; 128] = [0; 128];
|
||||
self.input_device
|
||||
.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name);
|
||||
let name = String::from_utf8(raw_name.to_vec()).unwrap();
|
||||
info!("input device name:{}", name);
|
||||
let mut prop: [u8; 128] = [0; 128];
|
||||
self.input_device
|
||||
.query_config_select(InputConfigSelect::PropBits, 0, &mut prop);
|
||||
|
||||
let input_prop = InputProp::from_bits(prop[0]).unwrap();
|
||||
debug!("input device prop:{:?}", input_prop);
|
||||
|
||||
// if name.contains("Keyboard"){
|
||||
// let mut raw_ev : [u8;128] = [0;128];
|
||||
// let size = self.input_device.query_config_select(InputConfigSelect::EvBits, KEY, &mut raw_ev);
|
||||
// info!("size:{}, raw_ev :{:x?}",size, raw_ev);
|
||||
|
||||
// }else{
|
||||
// let mut raw_ev : [u8;128] = [0;128];
|
||||
// let size = self.input_device.query_config_select(InputConfigSelect::EvBits, REL, &mut raw_ev);
|
||||
// info!("size:{}, raw_ev :{:x?}",size, raw_ev);
|
||||
// }
|
||||
self.is_keyboard.store(
|
||||
name.contains("Keyboard"),
|
||||
core::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_keyboard(&self) -> bool {
|
||||
self.is_keyboard.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl INPUTDevice for VirtioInputDevice {
|
||||
fn handle_irq(&self) -> Option<()> {
|
||||
let input = &self.input_device;
|
||||
// one interrupt may contains serval input, so it should loop
|
||||
loop {
|
||||
let event = input.pop_pending_event()?;
|
||||
let dtype = match Decoder::decode(
|
||||
event.event_type as usize,
|
||||
event.code as usize,
|
||||
event.value as usize,
|
||||
) {
|
||||
Ok(dtype) => dtype,
|
||||
Err(_) => return Some(()),
|
||||
};
|
||||
if self.is_keyboard() {
|
||||
let mut lock = KEYBOARD_EVENT.lock();
|
||||
lock.push(dtype);
|
||||
drop(lock);
|
||||
let lock = KEYBOARD_CALLBACKS.lock();
|
||||
for callback in lock.iter() {
|
||||
callback.call(());
|
||||
}
|
||||
} else {
|
||||
let mut lock = MOUSE_EVENT.lock();
|
||||
lock.push(dtype);
|
||||
}
|
||||
match dtype {
|
||||
virtio_input_decoder::DecodeType::Key(key, r#type) => {
|
||||
info!("{:?} {:?}", key, r#type);
|
||||
}
|
||||
virtio_input_decoder::DecodeType::Mouse(mouse) => info!("{:?}", mouse),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(pci_device: Arc<PCIDevice>) {
|
||||
let mut lock = INPUT_DEVICE_LIST.lock();
|
||||
let id = lock.len();
|
||||
let dev = Arc::new(VirtioInputDevice::new(PCIVirtioDevice::new(pci_device), id));
|
||||
lock.push(dev.clone());
|
||||
dev.register_interrupts(id);
|
||||
drop(lock);
|
||||
dev.print_device_information();
|
||||
}
|
||||
|
||||
pub fn register_keyboard_callback(callback: Arc<dyn Fn() + 'static + Send + Sync>) {
|
||||
KEYBOARD_CALLBACKS.lock().push(callback);
|
||||
}
|
@ -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]);
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
||||
|
45
src/tests/console_input.rs
Normal file
45
src/tests/console_input.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
use bootloader::{entry_point, BootInfo};
|
||||
extern crate alloc;
|
||||
use alloc::sync::Arc;
|
||||
use core::panic::PanicInfo;
|
||||
use jinux_frame::println;
|
||||
|
||||
static mut INPUT_VALUE: u8 = 0;
|
||||
|
||||
entry_point!(kernel_test_main);
|
||||
|
||||
fn kernel_test_main(boot_info: &'static mut BootInfo) -> ! {
|
||||
jinux_frame::init(boot_info);
|
||||
jinux_std::driver::console::init();
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_input() {
|
||||
jinux_frame::enable_interrupts();
|
||||
println!("please input value into console to pass this test");
|
||||
jinux_std::driver::console::register_console_callback(Arc::new(input_callback));
|
||||
unsafe {
|
||||
while INPUT_VALUE == 0 {
|
||||
jinux_frame::hlt();
|
||||
}
|
||||
println!("input value:{}", INPUT_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_callback(input: u8) {
|
||||
unsafe {
|
||||
INPUT_VALUE = input;
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user