mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
refactor virtio code; APIC , console interrupt support
This commit is contained in:
@ -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 {
|
||||
|
Reference in New Issue
Block a user