From 70bd58e5ecffc178d9ecfafe919bf25ae248b8d4 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 1 Feb 2025 19:31:26 +0300 Subject: [PATCH] many changes and especially identify ps/2 --- Cargo.lock | 32 +++++++++++++ Cargo.toml | 1 + README.md | 1 + src/kernel/irq.rs | 110 +++++++++++++++++++++++++++++++++--------- src/kernel/mod.rs | 8 ++-- src/kernel/pit.rs | 38 +++++++++++++++ src/kernel/ps2.rs | 112 ++++++++++++++++++++++++++++++++++++++----- src/kernel/thread.rs | 0 8 files changed, 264 insertions(+), 38 deletions(-) create mode 100644 src/kernel/pit.rs delete mode 100644 src/kernel/thread.rs diff --git a/Cargo.lock b/Cargo.lock index 22149bb..466b2fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "bit_field" version = "0.10.2" @@ -14,11 +20,22 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "mxrox" version = "0.1.0" dependencies = [ "no-std-compat 0.4.1", + "spin", "stable-vec", "x86_64", ] @@ -41,6 +58,21 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stable-vec" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 6f1a565..f3712ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" stable-vec = "0.4.1" no-std-compat = {version = "0.4.1", features = ["alloc"]} x86_64 = {version = "0.15.2", features = ["instructions"]} +spin = "0.9.8" [profile.dev] panic = "abort" diff --git a/README.md b/README.md index bc718ea..2f0ff10 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Internet resources where I found most information about OS dev - https://wiki.osdev.org/PS/2 - https://os.phil-opp.com/ - https://wiki.osdev.org/Interrupts_Tutorial +- https://wiki.osdev.org/Interrupt_Vector_Table ### Contributing diff --git a/src/kernel/irq.rs b/src/kernel/irq.rs index 9352c56..eed1dd1 100644 --- a/src/kernel/irq.rs +++ b/src/kernel/irq.rs @@ -1,10 +1,24 @@ use core::ptr::write_volatile; use core::arch::asm; -const APIC_BASE: *mut u32 = 0xFEE00000 as *mut u32; +const APIC_BASE: u32 = 0xFEE00000; + +const APIC_EOI: u32 = APIC_BASE + 0x0B0; +const APIC_SVR: u32 = APIC_BASE + 0x0F0; + +const PIC1_CMD: u16 = 0x20; +const PIC1_DATA: u16 = 0x21; +const PIC2_CMD: u16 = 0xA0; +const PIC2_DATA: u16 = 0xA1; + +const ICW1_INIT: u8 = 0x10; +const ICW1_ICW4: u8 = 0x01; +const ICW4_8086: u8 = 0x01; + +const PIC_EOI: u8 = 0x20; /* End-of-interrupt command code */ + +const APIC_ENABLED: bool = false; -const APIC_EOI: *mut u32 = unsafe { APIC_BASE.add(0x0B0) }; -const APIC_SVR: *mut u32 = unsafe { APIC_BASE.add(0x0F0) }; #[repr(C, packed)] #[derive(Clone, Copy)] @@ -33,9 +47,70 @@ static mut IDT: Idt = Idt { }; IDT_SIZE], }; -fn register_idt(handler: u32) { +unsafe fn is_apic_available() -> bool { + if !APIC_ENABLED { + return false; + } + + let edx: u32; + + asm!( + "cpuid", + inout("eax") 1 => _, + lateout("edx") edx + ); + + (edx & (1 << 9)) != 0 +} + +unsafe fn init_idt() { + asm!("lidt [{}]", "sti", in(reg) &IDT as *const _); +} + +unsafe fn init_apic() { + write_volatile(APIC_SVR as *mut u32, 0x100 | 0x1); +} + +unsafe fn send_apic_eoi() { + write_volatile(APIC_EOI as *mut u32, 0); +} + +unsafe fn send_pic_eoi(irq: usize) { + if irq >= 8 { write_volatile(PIC2_CMD as *mut u8, PIC_EOI); } + write_volatile(PIC1_CMD as *mut u8, PIC_EOI); +} + +unsafe fn init_pic() { + write_volatile(PIC1_CMD as *mut u8, ICW1_INIT | ICW1_ICW4); + write_volatile(PIC2_CMD as *mut u8, ICW1_INIT | ICW1_ICW4); + + write_volatile(PIC1_DATA as *mut u8, 0x20); + write_volatile(PIC2_DATA as *mut u8, 0x28); + + write_volatile(PIC1_DATA as *mut u8, 4); + write_volatile(PIC2_DATA as *mut u8, 2); + + write_volatile(PIC1_DATA as *mut u8, ICW4_8086); + write_volatile(PIC2_DATA as *mut u8, ICW4_8086); + + write_volatile(PIC1_DATA as *mut u8, 0xFB); + write_volatile(PIC2_DATA as *mut u8, 0xFF); +} + +pub fn send_eoi(irq: usize) { unsafe { - let entry = &mut IDT.entries[32]; + if is_apic_available() { + send_apic_eoi() + } else { + send_pic_eoi(irq) + } + } +} + +/// all interrupt vectors you can find here: https://wiki.osdev.org/Interrupt_Vector_Table +pub fn register_idt(handler: u32, int_vec: u8) { + unsafe { + let entry = &mut IDT.entries[int_vec as usize]; entry.offset_low = handler as u16; entry.selector = 0x08; @@ -45,24 +120,13 @@ fn register_idt(handler: u32) { } } -unsafe fn load_idt() { - asm!("lidt [{}]", "sti", in(reg) &IDT as *const _); -} - -unsafe fn enable_apic() { - let sv_reg = APIC_SVR as *mut u32; - let mut value = 0x100 | 0x1; - write_volatile(sv_reg, value); -} - -unsafe fn send_eoi() { - let eoi_reg = APIC_EOI as *mut u32; - write_volatile(eoi_reg, 0); -} - -pub fn init_apic() { +pub fn init_interrupts() { unsafe { - load_idt(); - enable_apic(); + init_idt(); + if is_apic_available() { + init_apic(); + } else { + init_pic(); + } } } \ No newline at end of file diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 50e231d..4eb3c05 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1,5 +1,6 @@ -use irq::init_apic; +use irq::init_interrupts; use heap::init_heap; +use pit::init_pit; use ps2::init_ps2; use vga::{ fill_with_color, @@ -13,7 +14,7 @@ use vga::{ mod vga; mod ps2; mod irq; -mod thread; +mod pit; mod heap; mod util; @@ -24,8 +25,9 @@ pub fn show_error(message: &str) { pub fn init_kernel() { init_heap(16400, 16384); + init_pit(); init_ps2(); - init_apic(); + init_interrupts(); fill_with_color(VGA_COLOR_BLACK); diff --git a/src/kernel/pit.rs b/src/kernel/pit.rs new file mode 100644 index 0000000..099af5f --- /dev/null +++ b/src/kernel/pit.rs @@ -0,0 +1,38 @@ +use core::{ptr::write_volatile, sync::atomic::{AtomicUsize, Ordering}}; +use alloc::{sync::Arc, vec::Vec}; +use spin::RwLock; +use core::sync::atomic::AtomicBool; + +use super::irq::{register_idt, send_eoi}; + +const DIVISOR: u16 = ((1193182u32 + 500u32) / 1000) as u16; + +static TIMERS: RwLock>> = RwLock::new(Vec::new()); + +extern "C" fn pit_handler() { + for t in TIMERS.read().iter() { + t.fetch_add(1, Ordering::SeqCst); + } + send_eoi(0); +} + +pub fn init_pit() { + unsafe { + write_volatile(0x43 as *mut u8, 0x36); + write_volatile(0x40 as *mut u8, (DIVISOR & 0xFF) as u8); + write_volatile(0x40 as *mut u8, ((DIVISOR >> 8) & 0xFF) as u8); + } + register_idt(pit_handler as u32, 0x08); +} + +pub fn sleep(millis: usize) { + let atomic = Arc::new(AtomicUsize::new(0)); + + TIMERS.write().push(atomic.clone()); + + while atomic.load(Ordering::SeqCst) < millis { + core::hint::spin_loop(); // Уменьшаем нагрузку на CPU + } + + TIMERS.write().retain(|t| !Arc::ptr_eq(t, &atomic)); +} \ No newline at end of file diff --git a/src/kernel/ps2.rs b/src/kernel/ps2.rs index cf413f1..bd69920 100644 --- a/src/kernel/ps2.rs +++ b/src/kernel/ps2.rs @@ -1,25 +1,113 @@ use core::ptr::{read_volatile, write_volatile}; -const DATA_PORT: *mut u8 = 0x60 as *mut u8; -const STATUS_PORT: *mut u8 = 0x64 as *mut u8; +use alloc::vec::Vec; -fn write_ps2_data(data: u8) { - unsafe { write_volatile(DATA_PORT, data) } +use crate::kernel::irq::register_idt; + +use super::{irq::send_eoi, pit::sleep}; + +const DATA_PORT: u16 = 0x60; +const STATUS_PORT: u16 = 0x64; + +const DEVICE1_IRQ: usize = 1; +const DEVICE2_IRQ: usize = 12; + +static mut DEVICE1_TYPE: Vec = Vec::new(); +static mut DEVICE2_TYPE: Vec = Vec::new(); + +unsafe fn send_data(data: u8) { + write_volatile(DATA_PORT as *mut u8, data) } -fn read_ps2_data() -> u8 { - unsafe { read_volatile(DATA_PORT) } +unsafe fn read_data() -> u8 { + read_volatile(DATA_PORT as *mut u8) +} + +unsafe fn send_command(command: u8) { + write_volatile(STATUS_PORT as *mut u8, command) } -fn send_ps2_command(command: u8) { - unsafe { write_volatile(STATUS_PORT, command) } +unsafe fn read_status() -> u8 { + read_volatile(STATUS_PORT as *mut u8) } -fn read_ps2_status() -> u8 { - unsafe { read_volatile(STATUS_PORT) } -} +unsafe fn on_device_1_irq() { + let data = read_data(); + send_eoi(DEVICE1_IRQ); +} + +unsafe fn on_device_2_irq() { + let data = read_data(); + send_eoi(DEVICE2_IRQ); +} + +unsafe fn is_data_available() -> bool { + (read_status() >> 0) & 1 == 1 +} + +// https://togglebit.io/posts/rust-bitwise/ + +unsafe fn read_identify_reply() -> Vec { + let mut reply = Vec::new(); + + for _ in 0..2 { + if is_data_available() { + reply.push(read_data()); + } else { + sleep(10); + + if is_data_available() { + reply.push(read_data()); + } else { + break; + } + } + } + + reply +} + +unsafe fn send_identify() -> Vec { + send_data(0xF5); // Disable Scanning command + while !is_data_available() || read_data() != 0xFA { + sleep(1); + } + + send_data(0xF2); // Identify command + while !is_data_available() || read_data() != 0xFA { + sleep(1); + } + + let reply = read_identify_reply(); + + send_data(0xF4); + + reply +} + +unsafe fn is_ps2_2_device_enabled() -> bool { + true +} pub fn init_ps2() { + unsafe { + send_command(0xAD); // disable device 1 + if is_ps2_2_device_enabled() { + DEVICE2_TYPE = send_identify(); + send_command(0xA7); // disable device 2 + } - todo!() + read_data(); // flush device data + send_command(0x60); // set CCB command + send_command(0x000000u8); // clear CCB + + send_command(0xAE); // enable device 1 + if is_ps2_2_device_enabled() { + DEVICE1_TYPE = send_identify(); + send_command(0xA8); // enable device 2 + } + } + + register_idt(on_device_1_irq as u32, 0x09); // bind handler for device 1 + register_idt(on_device_2_irq as u32, 0x74); // bind handler for device 2 } \ No newline at end of file diff --git a/src/kernel/thread.rs b/src/kernel/thread.rs deleted file mode 100644 index e69de29..0000000