keyboard through PCI&VirtIO
This commit is contained in:
@@ -10,4 +10,3 @@ build-std-features = ["compiler-builtins-mem"]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tilm.ld",
|
||||
]
|
||||
runner = "just runner"
|
||||
|
||||
18
justfile
18
justfile
@@ -1,4 +1,5 @@
|
||||
release := ""
|
||||
qemu_flags := ""
|
||||
cargo_flags := "" + if release != "" { "--release" } else { "" }
|
||||
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
|
||||
|
||||
@@ -15,31 +16,28 @@ build_user_prog prog:
|
||||
RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }}
|
||||
riscv64-elf-strip {{ bin_path / prog }}
|
||||
cp {{ bin_path / prog }} {{ "mnt/usr/bin" / prog }}
|
||||
# riscv64-elf-objcopy -O binary {{ bin_path / prog }} {{ "mnt/usr/bin" / prog }}
|
||||
|
||||
build: mount_filesystem (map_dir "user" f"just release=\"{{release}}\" cargo_flags=\"{{cargo_flags}}\" build_user_prog")
|
||||
cargo b {{ cargo_flags }}
|
||||
just sync_filesystem
|
||||
|
||||
run: build
|
||||
cargo r {{ cargo_flags }}
|
||||
run: build (runner f"{{bin_path / "kernel-rust"}}")
|
||||
|
||||
map_dir dir recipe:
|
||||
@for file in `ls {{ dir }}`; do \
|
||||
{{ recipe }} $file ; \
|
||||
done
|
||||
|
||||
qemu := "qemu-system-riscv64 \
|
||||
qemu := f"qemu-system-riscv64 \
|
||||
-machine virt \
|
||||
-serial mon:stdio \
|
||||
-device bochs-display \
|
||||
-global virtio-mmio.force-legacy=false \
|
||||
-device virtio-keyboard-device,bus=virtio-mmio-bus.0 \
|
||||
-device virtio-keyboard-pci \
|
||||
-device loader,file=disk.img,addr=0x90000000 \
|
||||
-bios none -m 512M"
|
||||
-bios none -m 512M {{qemu_flags}}"
|
||||
|
||||
# \
|
||||
# -d guest_errors,unimp \
|
||||
# -trace \"virtio*\""
|
||||
# -trace \"virtio*\"
|
||||
# -d guest_errors,unimp,int"
|
||||
|
||||
perf: build
|
||||
{{ qemu }} -perfmap -kernel {{ bin_path / "kernel-rust" }}&
|
||||
|
||||
@@ -7,12 +7,7 @@ use core::arch::naked_asm;
|
||||
use crate::{
|
||||
clear_csr,
|
||||
interrupt::{setup_machine_trap_handler, setup_supervisor_trap_handler},
|
||||
mret, set_csr, supervisor_mode_entry,
|
||||
virtio::{
|
||||
Virtqueue,
|
||||
input::{VirtioInputDriver, init_plic_m_mode},
|
||||
},
|
||||
write_csr,
|
||||
mret, set_csr, supervisor_mode_entry, write_csr,
|
||||
};
|
||||
|
||||
pub mod sbi;
|
||||
|
||||
@@ -215,7 +215,7 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
|
||||
// 2. Écrire l'IRQ (Complete) <--- INDISPENSABLE
|
||||
core::ptr::write_volatile(S_MODE_CLAIM_COMPLETE, irq);
|
||||
KBD_DRIVER.handle_interrupt();
|
||||
KBD_DRIVER.as_mut().unwrap().handle_interrupt();
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ macro_rules! print {
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => {
|
||||
$crate::print!("\n\r");
|
||||
$crate::print!("\n\r")
|
||||
};
|
||||
($($args:expr),*) => {
|
||||
$crate::print!($($args),*);
|
||||
$crate::println!();
|
||||
$crate::println!()
|
||||
};
|
||||
}
|
||||
|
||||
24
src/main.rs
24
src/main.rs
@@ -16,11 +16,15 @@ use log::info;
|
||||
|
||||
use crate::{
|
||||
io::init_log,
|
||||
pci::scan_pci_for_virtio_keyboard,
|
||||
riscv::enable_supervisor_interrupt,
|
||||
scheduler::{SCHEDULER, idle},
|
||||
user::{proc2, test},
|
||||
vga::Vga,
|
||||
virtio::{Virtqueue, input::{VirtioInputDriver, init_plic_m_mode}},
|
||||
virtio::{
|
||||
Virtqueue,
|
||||
input::{VirtioPciDriver, init_plic_pci},
|
||||
},
|
||||
virtual_fs::init_file_system,
|
||||
};
|
||||
|
||||
@@ -32,6 +36,7 @@ mod fs;
|
||||
mod interrupt;
|
||||
mod io;
|
||||
mod panic_handler;
|
||||
mod pci;
|
||||
mod process;
|
||||
mod riscv;
|
||||
mod scheduler;
|
||||
@@ -57,9 +62,8 @@ const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>
|
||||
|
||||
// 1. Allouer de la mémoire statique alignée pour la queue
|
||||
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||
// 2. Initialisation (adresse 0x10001000 typique pour QEMU virt machine)
|
||||
pub static mut KBD_DRIVER: VirtioInputDriver =
|
||||
unsafe { VirtioInputDriver::new(0x10001000, &mut KBD_QUEUE) };
|
||||
pub static mut KBD_DRIVER: Option<VirtioPciDriver> = None;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn supervisor_mode_entry() {
|
||||
unsafe {
|
||||
@@ -84,8 +88,16 @@ pub extern "C" fn supervisor_mode_entry() {
|
||||
enable_supervisor_interrupt();
|
||||
|
||||
unsafe {
|
||||
KBD_DRIVER.init();
|
||||
init_plic_m_mode();
|
||||
let pci_info = scan_pci_for_virtio_keyboard().unwrap();
|
||||
KBD_DRIVER = Some(VirtioPciDriver::new(
|
||||
pci_info.common_cfg,
|
||||
pci_info.notify_cfg,
|
||||
pci_info.isr_cfg,
|
||||
pci_info.notify_multiplier,
|
||||
&mut KBD_QUEUE,
|
||||
));
|
||||
KBD_DRIVER.as_mut().unwrap().init();
|
||||
init_plic_pci(34);
|
||||
}
|
||||
idle();
|
||||
}
|
||||
|
||||
174
src/pci.rs
Normal file
174
src/pci.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
use crate::println;
|
||||
|
||||
// Configuration pour RISC-V Virt
|
||||
const PCI_ECAM_BASE: usize = 0x3000_0000;
|
||||
const VIRTIO_VENDOR_ID: u16 = 0x1af4;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct VirtioPciRegion {
|
||||
pub bar_address: usize,
|
||||
pub offset: u32,
|
||||
pub length: u32,
|
||||
}
|
||||
// Helpers ECAM
|
||||
fn pci_read_u32(bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
let addr = PCI_ECAM_BASE
|
||||
| ((bus as usize) << 20)
|
||||
| ((dev as usize) << 15)
|
||||
| ((func as usize) << 12)
|
||||
| (offset as usize);
|
||||
unsafe { core::ptr::read_volatile(addr as *const u32) }
|
||||
}
|
||||
|
||||
fn pci_write_u32(bus: u8, dev: u8, func: u8, offset: u16, val: u32) {
|
||||
let addr = PCI_ECAM_BASE
|
||||
| ((bus as usize) << 20)
|
||||
| ((dev as usize) << 15)
|
||||
| ((func as usize) << 12)
|
||||
| (offset as usize);
|
||||
unsafe { core::ptr::write_volatile(addr as *mut u32, val) }
|
||||
}
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct VirtioPciCaps {
|
||||
pub common_cfg: usize,
|
||||
pub notify_cfg: usize,
|
||||
pub isr_cfg: usize,
|
||||
pub notify_multiplier: u32,
|
||||
}
|
||||
|
||||
pub struct PciDevice {
|
||||
device_id: u8,
|
||||
}
|
||||
|
||||
impl PciDevice {
|
||||
pub fn new(device_id: u8) -> Self {
|
||||
Self { device_id }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pci_iter() {
|
||||
«
|
||||
}
|
||||
|
||||
pub fn scan_pci_for_virtio_keyboard() -> Option<VirtioPciCaps> {
|
||||
// Sur RISC-V Virt, on scanne généralement le bus 0
|
||||
for dev in 0..32 {
|
||||
let vdev = pci_read_u32(0, dev, 0, 0x00);
|
||||
let vendor = (vdev & 0xffff) as u16;
|
||||
let device = (vdev >> 16) as u16;
|
||||
|
||||
// On cherche le Vendor ID VirtIO (0x1AF4)
|
||||
// et le Device ID Keyboard (0x1012 ou 0x1052 pour Modern)
|
||||
if vendor == 0x1af4 && (device >= 0x1000 && device <= 0x107f) {
|
||||
// Dans ta boucle de scan, après avoir trouvé le device
|
||||
let interrupt_pin = (pci_read_u32(0, dev, 0, 0x3C) >> 8) & 0xFF; // Offset 0x3D
|
||||
let irq = 32 + (dev as u32 + interrupt_pin - 1) % 4;
|
||||
|
||||
let old_val = pci_read_u32(0, 2, 0, 0x3C);
|
||||
|
||||
// On garde les 24 bits du haut (Interrupt Pin, etc.) et on change les 8 bits du bas
|
||||
let new_val = (old_val & 0xFFFFFF00) | irq;
|
||||
|
||||
pci_write_u32(0, 2, 0, 0x3C, new_val);
|
||||
|
||||
println!(
|
||||
"VirtIO Keyboard sur Slot {}, PIN {}, mappé sur IRQ PLIC {}",
|
||||
dev, interrupt_pin, irq
|
||||
); // ACTIVER l'accès mémoire et le bus mastering (PCI Command)
|
||||
let cmd = pci_read_u32(0, dev, 0, 0x04);
|
||||
pci_write_u32(0, dev, 0, 0x04, cmd | 0x6);
|
||||
|
||||
let mut common_cfg = 0;
|
||||
let mut notify_cfg = 0;
|
||||
let mut isr_cfg = 0;
|
||||
|
||||
// On initialise le multiplicateur à 1 par défaut
|
||||
let mut notify_multiplier: u32 = 1;
|
||||
|
||||
// 1. Activer le Bus Master et le Memory Space globalement pour ce périphérique avant de commencer
|
||||
let old_cmd = pci_read_u32(0, dev, 0, 0x04);
|
||||
pci_write_u32(0, dev, 0, 0x04, old_cmd | 0x06);
|
||||
|
||||
// 2. Boucle d'assignation des BARs (AVANT de lire les capabilities)
|
||||
let mut bar_idx = 0;
|
||||
while bar_idx < 6 {
|
||||
let bar_reg = 0x10 + (bar_idx * 4);
|
||||
let bar_val = pci_read_u32(0, dev, 0, bar_reg);
|
||||
|
||||
// Si le BAR veut de la mémoire (bit 0 == 0) et n'est pas mappé
|
||||
if bar_val & 0x1 == 0 && (bar_val & 0xFFFF_FFF0) == 0 {
|
||||
let new_addr =
|
||||
0x5100_0000 + (dev as u32 * 0x100_000) + (bar_idx as u32 * 0x4000);
|
||||
println!("ALLOCATE BAR {} at {:x}", bar_idx, new_addr);
|
||||
pci_write_u32(0, dev, 0, bar_reg, new_addr | (bar_val & 0xF));
|
||||
|
||||
// Gérer les BAR 64-bits (ils consomment deux slots)
|
||||
if (bar_val >> 1) & 0x3 == 2 {
|
||||
println!("bar {} is 64 bits", bar_idx);
|
||||
pci_write_u32(0, dev, 0, bar_reg + 4, 0);
|
||||
|
||||
bar_idx += 1
|
||||
}
|
||||
pci_write_u32(0, dev, 0, bar_reg, new_addr);
|
||||
let confirm = pci_read_u32(0, dev, 0, bar_reg);
|
||||
println!("BAR confirmé : {:x}", confirm);
|
||||
}
|
||||
bar_idx += 1
|
||||
}
|
||||
let mut cap_ptr = (pci_read_u32(0, dev, 0, 0x34) & 0xFF) as u16;
|
||||
|
||||
while cap_ptr != 0 {
|
||||
let header = pci_read_u32(0, dev, 0, cap_ptr);
|
||||
let cap_id = (header & 0xFF) as u8;
|
||||
let next_ptr = ((header >> 8) & 0xFF) as u16;
|
||||
let v_type = ((header >> 24) & 0xFF) as u8;
|
||||
|
||||
if cap_id == 0x09 {
|
||||
// VirtIO Vendor Capability
|
||||
let bar_idx = (pci_read_u32(0, dev, 0, cap_ptr + 4) & 0xFF) as u8;
|
||||
let offset = pci_read_u32(0, dev, 0, cap_ptr + 8) as usize;
|
||||
|
||||
// VirtIO : bar_idx doit être entre 0 et 5. 0xFF signifie "ignoré".
|
||||
if bar_idx <= 5 {
|
||||
let bar_reg_offset = 0x10 + (bar_idx as u16 * 4);
|
||||
let mut bar_val = pci_read_u32(0, dev, 0, bar_reg_offset);
|
||||
|
||||
let bar_addr = (bar_val & 0xFFFF_FFF0) as usize;
|
||||
let final_addr = bar_addr + offset;
|
||||
|
||||
match v_type {
|
||||
1 => {
|
||||
common_cfg = final_addr;
|
||||
println!("[VirtIO] CommonCfg trouvé à 0x{:x}", final_addr);
|
||||
}
|
||||
2 => {
|
||||
notify_cfg = final_addr;
|
||||
// TRÈS IMPORTANT : Lire le multiplicateur (offset 16 de la capability)
|
||||
notify_multiplier = pci_read_u32(0, dev, 0, cap_ptr + 16);
|
||||
println!(
|
||||
"[VirtIO] NotifyCfg trouvé à 0x{:x} (mult: {})",
|
||||
final_addr, notify_multiplier
|
||||
);
|
||||
}
|
||||
3 => {
|
||||
isr_cfg = final_addr;
|
||||
println!("[VirtIO] IsrCfg trouvé à 0x{:x}", final_addr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
cap_ptr = next_ptr;
|
||||
}
|
||||
if common_cfg != 0 && notify_cfg != 0 && isr_cfg != 0 {
|
||||
return Some(VirtioPciCaps {
|
||||
common_cfg,
|
||||
notify_cfg,
|
||||
isr_cfg,
|
||||
notify_multiplier,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -5,24 +5,33 @@ use core::{
|
||||
|
||||
use crate::{
|
||||
println,
|
||||
uart::write_char_uart,
|
||||
virtio::{
|
||||
QUEUE_SIZE, STATUS_ACKNOWLEDGE, STATUS_DRIVER, STATUS_DRIVER_OK, VIRTIO_MMIO_INTERRUPT_ACK,
|
||||
VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN,
|
||||
VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VirtioInputEvent, Virtqueue,
|
||||
},
|
||||
virtio::{QUEUE_SIZE, VirtioInputEvent, Virtqueue},
|
||||
};
|
||||
pub struct VirtioInputDriver {
|
||||
base_addr: usize,
|
||||
|
||||
pub struct VirtioPciDriver {
|
||||
// Les 3 zones clés du PCI Modern
|
||||
common_cfg: usize,
|
||||
notify_cfg: usize,
|
||||
isr_cfg: usize,
|
||||
|
||||
queue: &'static mut Virtqueue,
|
||||
event_pool: [VirtioInputEvent; QUEUE_SIZE],
|
||||
last_used_idx: u16,
|
||||
notify_off: u32, // Multiplier from PCI notify capability (used to compute notify address)
|
||||
}
|
||||
|
||||
impl VirtioInputDriver {
|
||||
pub const unsafe fn new(base_addr: usize, queue_mem: &'static mut Virtqueue) -> Self {
|
||||
impl VirtioPciDriver {
|
||||
pub const unsafe fn new(
|
||||
common_cfg: usize,
|
||||
notify_cfg: usize,
|
||||
isr_cfg: usize,
|
||||
notify_mult: u32,
|
||||
queue_mem: &'static mut Virtqueue,
|
||||
) -> Self {
|
||||
Self {
|
||||
base_addr,
|
||||
common_cfg,
|
||||
notify_cfg,
|
||||
isr_cfg,
|
||||
queue: queue_mem,
|
||||
event_pool: [VirtioInputEvent {
|
||||
event_type: 0,
|
||||
@@ -30,146 +39,127 @@ impl VirtioInputDriver {
|
||||
value: 0,
|
||||
}; QUEUE_SIZE],
|
||||
last_used_idx: 0,
|
||||
notify_off: notify_mult,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
unsafe {
|
||||
// 1. Reset & Status (Ack + Driver)
|
||||
self.write_reg(0x070, 0);
|
||||
self.write_reg(0x070, 1 | 2);
|
||||
println!("{:x}", self.common_cfg);
|
||||
// --- 1. Reset & Status ---
|
||||
self.write_common_u8(0x14, 0); // device_status
|
||||
self.write_common_u8(0x14, 1 | 2); // ACK | DRIVER
|
||||
|
||||
// 2. Négociation Features (Obligatoire en Modern pour débloquer les queues)
|
||||
self.write_reg(0x024, 1); // Select Page 1
|
||||
let f1 = self.read_reg(0x010);
|
||||
self.write_reg(0x020, f1 | 1); // On accepte VERSION_1 (Bit 32 global)
|
||||
// --- 2. Négociation Features (Bit 32 = Version 1) ---
|
||||
self.write_common_u32(0x08, 1); // driver_feature_select = page 1
|
||||
let f1 = self.read_common_u32(0x0C); // driver_feature
|
||||
self.write_common_u32(0x0C, f1 | 1); // Accepter VIRTIO_F_VERSION_1
|
||||
|
||||
self.write_reg(0x070, 1 | 2 | 8); // STATUS_FEATURES_OK
|
||||
if (self.read_reg(0x070) & 8) == 0 {
|
||||
panic!("Features rejected");
|
||||
}
|
||||
|
||||
// 3. Configuration de la Queue
|
||||
self.write_reg(0x030, 0); // Select Queue 0
|
||||
let max = self.read_reg(0x034); // QUEUE_NUM_MAX
|
||||
self.write_reg(0x038, QUEUE_SIZE as u32);
|
||||
|
||||
// 4. Envoi des adresses 64 bits (Plus besoin de PFN !)
|
||||
let desc_addr = &self.queue.descriptors as *const _ as u64;
|
||||
self.write_reg(0x080, (desc_addr & 0xffffffff) as u32);
|
||||
self.write_reg(0x084, (desc_addr >> 32) as u32);
|
||||
|
||||
let avail_addr = &self.queue.available as *const _ as u64;
|
||||
self.write_reg(0x090, (avail_addr & 0xffffffff) as u32);
|
||||
self.write_reg(0x094, (avail_addr >> 32) as u32);
|
||||
|
||||
let used_addr = &self.queue.used as *const _ as u64;
|
||||
self.write_reg(0x0a0, (used_addr & 0xffffffff) as u32);
|
||||
self.write_reg(0x0a4, (used_addr >> 32) as u32);
|
||||
|
||||
// 5. REMPLISSAGE INITIAL
|
||||
for i in 0..QUEUE_SIZE {
|
||||
self.queue.descriptors[i].addr = &self.event_pool[i] as *const _ as u64;
|
||||
self.queue.descriptors[i].len = core::mem::size_of::<VirtioInputEvent>() as u32;
|
||||
self.queue.descriptors[i].flags = 2; // Writeable
|
||||
self.queue.available.ring[i] = i as u16;
|
||||
}
|
||||
self.queue
|
||||
.available
|
||||
.idx
|
||||
.store(QUEUE_SIZE as u16, Ordering::Release);
|
||||
|
||||
// 6. ACTIVATION (L'étape que tout le monde oublie en Modern)
|
||||
self.write_reg(0x044, 1); // QUEUE_READY
|
||||
|
||||
// 7. DRIVER_OK
|
||||
self.write_reg(0x070, 1 | 2 | 4 | 8);
|
||||
|
||||
self.activate_queue();
|
||||
self.write_common_u8(0x14, 1 | 2 | 8); // FEATURES_OK
|
||||
if (self.read_common_u8(0x14) & 8) == 0 {
|
||||
panic!("PCI Virtio: Features rejected");
|
||||
}
|
||||
}
|
||||
unsafe fn activate_queue(&mut self) {
|
||||
|
||||
// --- 3. Configuration de la Queue 0 ---
|
||||
self.write_common_u16(0x16, 0); // queue_select = 0
|
||||
|
||||
// Fixer la taille
|
||||
self.write_common_u16(0x18, QUEUE_SIZE as u16); // queue_size
|
||||
|
||||
// --- 4. Adresses 64 bits ---
|
||||
let desc_addr = &self.queue.descriptors as *const _ as u64;
|
||||
println!("desc_addr: {:x}", desc_addr);
|
||||
self.write_common_u32(0x20, (desc_addr & 0xffffffff) as u32); // queue_desc_lo
|
||||
self.write_common_u32(0x24, (desc_addr >> 32) as u32); // queue_desc_hi
|
||||
|
||||
let avail_addr = &self.queue.available as *const _ as u64;
|
||||
self.write_common_u32(0x28, (avail_addr & 0xffffffff) as u32); // queue_avail_lo
|
||||
self.write_common_u32(0x2C, (avail_addr >> 32) as u32); // queue_avail_hi
|
||||
|
||||
let used_addr = &self.queue.used as *const _ as u64;
|
||||
self.write_common_u32(0x30, (used_addr & 0xffffffff) as u32); // queue_used_lo
|
||||
self.write_common_u32(0x34, (used_addr >> 32) as u32); // queue_used_hi
|
||||
|
||||
// --- 5. Remplissage initial ---
|
||||
for i in 0..QUEUE_SIZE {
|
||||
self.queue.descriptors[i].addr = &self.event_pool[i] as *const _ as u64;
|
||||
self.queue.descriptors[i].len = core::mem::size_of::<VirtioInputEvent>() as u32;
|
||||
self.queue.descriptors[i].flags = 2; // VRING_DESC_F_WRITE
|
||||
self.queue.available.ring[i] = i as u16;
|
||||
}
|
||||
self.queue
|
||||
.available
|
||||
.idx
|
||||
.store(QUEUE_SIZE as u16, Ordering::Release);
|
||||
|
||||
// --- 6. Activation ---
|
||||
self.write_common_u16(0x1C, 1); // queue_enable = 1
|
||||
|
||||
if self.read_common_u16(0x1C) == 0 {
|
||||
panic!("Le périphérique refuse d'activer la queue !");
|
||||
}
|
||||
|
||||
// --- 7. Driver OK (must be set after queue is enabled) ---
|
||||
self.write_common_u8(0x14, 1 | 2 | 4 | 8);
|
||||
|
||||
// --- 8. PREMIER KICK ---
|
||||
// Compute the notify address for queue 0 using the multiplier provided by PCI capability
|
||||
let notify_addr = self.notify_cfg + (self.notify_off as usize * 0);
|
||||
|
||||
unsafe {
|
||||
// 1. Sélectionner la queue
|
||||
self.write_reg(0x030, 0);
|
||||
|
||||
// 2. Écrire la taille (doit correspondre à tes structures Rust)
|
||||
self.write_reg(0x038, QUEUE_SIZE as u32);
|
||||
|
||||
// 3. Écrire les adresses (ORDRE CRITIQUE : LOW puis HIGH)
|
||||
let desc_addr = &self.queue.descriptors as *const _ as u64;
|
||||
self.write_reg(0x080, desc_addr as u32); // DescLow
|
||||
self.write_reg(0x084, (desc_addr >> 32) as u32); // DescHigh
|
||||
|
||||
let avail_addr = &self.queue.available as *const _ as u64;
|
||||
self.write_reg(0x090, avail_addr as u32); // AvailLow
|
||||
self.write_reg(0x094, (avail_addr >> 32) as u32); // AvailHigh
|
||||
|
||||
let used_addr = &self.queue.used as *const _ as u64;
|
||||
self.write_reg(0x0a0, used_addr as u32); // UsedLow
|
||||
self.write_reg(0x0a4, (used_addr >> 32) as u32); // UsedHigh
|
||||
|
||||
// 4. LE KICK : Activer la queue
|
||||
self.write_reg(0x044, 1); // QUEUE_READY = 1
|
||||
|
||||
// 5. SYNC : On s'assure que le périphérique a bien pris le READY
|
||||
if self.read_reg(0x044) == 0 {
|
||||
panic!("La queue refuse de passer en READY. Vérifiez les adresses !");
|
||||
}
|
||||
core::ptr::write_volatile(notify_addr as *mut u16, 0); // 0 = index de la queue
|
||||
}
|
||||
}
|
||||
|
||||
/// Appelé lors d'une interruption clavier
|
||||
pub fn handle_interrupt(&mut self) {
|
||||
let used_idx = self.queue.used.idx.load(Ordering::Acquire);
|
||||
// 1. Lire et acquitter l'ISR PCI (Indispensable pour baisser la ligne IRQ)
|
||||
let isr = unsafe { read_volatile(self.isr_cfg as *const u8) };
|
||||
if isr & 1 == 0 {
|
||||
return;
|
||||
} // Pas une interruption de queue
|
||||
|
||||
let used_idx = self.queue.used.idx.load(Ordering::Acquire);
|
||||
while self.last_used_idx != used_idx {
|
||||
let ring_slot = self.last_used_idx as usize % QUEUE_SIZE;
|
||||
let used_elem = &self.queue.used.ring[ring_slot];
|
||||
|
||||
let event = &self.event_pool[used_elem.id as usize];
|
||||
|
||||
if event.event_type == 1 {
|
||||
// EV_KEY
|
||||
self.on_key(event.code, event.value);
|
||||
println!("key pressed");
|
||||
// self.on_key(event.code, event.value);
|
||||
}
|
||||
|
||||
// Recyclage du descripteur : on le rend disponible à nouveau
|
||||
let avail_idx = self.queue.available.idx.load(Ordering::Relaxed) as usize % QUEUE_SIZE;
|
||||
self.queue.available.ring[avail_idx] = used_elem.id as u16;
|
||||
// Recyclage
|
||||
let avail_head = self.queue.available.idx.load(Ordering::Relaxed) as usize % QUEUE_SIZE;
|
||||
self.queue.available.ring[avail_head] = used_elem.id as u16;
|
||||
self.queue.available.idx.fetch_add(1, Ordering::Release);
|
||||
|
||||
self.last_used_idx = self.last_used_idx.wrapping_add(1);
|
||||
}
|
||||
|
||||
// Acquitter l'interruption
|
||||
// Notifier qu'on a traité les buffers (si besoin)
|
||||
unsafe {
|
||||
self.write_reg(VIRTIO_MMIO_INTERRUPT_ACK, 1);
|
||||
write_volatile(self.notify_cfg as *mut u16, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_key(&self, code: u16, value: u32) {
|
||||
let state = match value {
|
||||
1 => "Pressed",
|
||||
0 => "Released",
|
||||
2 => "Repeat",
|
||||
_ => "Unknown",
|
||||
};
|
||||
// write_char_uart((b'0' + (code / 10) as u8 % 10) as char);
|
||||
// write_char_uart((b'0' + (code % 10) as u8) as char);
|
||||
write_char_uart(code as u8 as char);
|
||||
write_char_uart('\n');
|
||||
write_char_uart('\r');
|
||||
// Ici, implémentez votre conversion Scancode -> ASCII
|
||||
// println!("Key Code: {} - State: {}", code, state);
|
||||
// Helpers de lecture/écriture (Note : PCI utilise des tailles mixtes u8/u16/u32)
|
||||
unsafe fn write_common_u8(&self, off: usize, val: u8) {
|
||||
write_volatile((self.common_cfg + off) as *mut u8, val);
|
||||
}
|
||||
|
||||
unsafe fn write_reg(&self, offset: usize, val: u32) {
|
||||
unsafe { write_volatile((self.base_addr + offset) as *mut u32, val) };
|
||||
unsafe fn write_common_u16(&self, off: usize, val: u16) {
|
||||
write_volatile((self.common_cfg + off) as *mut u16, val);
|
||||
}
|
||||
|
||||
unsafe fn read_reg(&self, offset: usize) -> u32 {
|
||||
unsafe { read_volatile((self.base_addr + offset) as *const u32) }
|
||||
unsafe fn write_common_u32(&self, off: usize, val: u32) {
|
||||
write_volatile((self.common_cfg + off) as *mut u32, val);
|
||||
}
|
||||
unsafe fn read_common_u8(&self, off: usize) -> u8 {
|
||||
read_volatile((self.common_cfg + off) as *const u8)
|
||||
}
|
||||
unsafe fn read_common_u16(&self, off: usize) -> u16 {
|
||||
read_volatile((self.common_cfg + off) as *const u16)
|
||||
}
|
||||
unsafe fn read_common_u32(&self, off: usize) -> u32 {
|
||||
read_volatile((self.common_cfg + off) as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,16 +167,20 @@ pub const PLIC_BASE: usize = 0x0c00_0000;
|
||||
pub const IRQ_VIRTIO: u32 = 1;
|
||||
|
||||
pub const S_MODE_CLAIM_COMPLETE: *mut u32 = 0x0c20_1004 as *mut u32;
|
||||
pub unsafe fn init_plic_m_mode() {
|
||||
// 1. Priority : identique pour tous les modes
|
||||
let priority_ptr = (PLIC_BASE + 4 * IRQ_VIRTIO as usize) as *mut u32;
|
||||
pub fn init_plic_pci(irq: u32) {
|
||||
let priority_ptr = (PLIC_BASE + (irq as usize * 4)) as *mut u32;
|
||||
unsafe { priority_ptr.write_volatile(1) };
|
||||
|
||||
// 2. Enable : Pour Hart 0 M-Mode, l'offset est 0x2000
|
||||
let enable_ptr = (PLIC_BASE + 0x2080) as *mut u32;
|
||||
unsafe { enable_ptr.write_volatile(1 << IRQ_VIRTIO) };
|
||||
|
||||
// 3. Threshold : Pour Hart 0 M-Mode, l'offset est 0x200000
|
||||
// Mettre le Threshold à 0 pour Hart 0 S-Mode (offset 0x201000)
|
||||
let threshold_ptr = (PLIC_BASE + 0x201000) as *mut u32;
|
||||
unsafe { threshold_ptr.write_volatile(0) };
|
||||
|
||||
// Activer l'IRQ pour le S-Mode (Hart 0)
|
||||
// Attention : l'enable bit pour l'IRQ 33 est le bit 1 du deuxième mot u32
|
||||
let enable_reg_offset = (irq / 32) as usize * 4;
|
||||
let enable_ptr = (PLIC_BASE + 0x2080 + enable_reg_offset) as *mut u32;
|
||||
unsafe {
|
||||
let current = enable_ptr.read_volatile();
|
||||
enable_ptr.write_volatile(current | (1 << (irq % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user