diff --git a/kernel/src/cap/mod.rs b/kernel/src/cap/mod.rs index 0f2eae0..154ca64 100644 --- a/kernel/src/cap/mod.rs +++ b/kernel/src/cap/mod.rs @@ -10,6 +10,7 @@ pub use descriptor::*; pub struct CNodeSlot { pub cap: Capability, pub parent_idx: Option, + pub children: Vec, } pub struct CNode { @@ -23,6 +24,7 @@ impl CNode { slots.push(Locked::new(CNodeSlot { cap: Capability::empty(), parent_idx: None, + children: Vec::new(), })); } Self { slots } @@ -42,12 +44,10 @@ impl CNode { return Err("Index out of bounds"); } - // Защита от дедлока: если src == dest, операция не имеет смысла if src == dest { return Err("Source and destination slots must be different"); } - // Захватываем блокировки в строгом порядке индексов для предотвращения инверсии блокировок (Lock Ranking) let mut _guard_low; let mut _guard_high; @@ -68,72 +68,72 @@ impl CNode { return Err("Insufficient rights to mint (Missing GRANT flag)"); } - // Права дочернего дескриптора не могут превышать права родительского let final_rights = src_slot.cap.rights & rights; + dest_slot.children.clear(); dest_slot.cap = src_slot.cap; dest_slot.cap.rights = final_rights; dest_slot.cap.relation = relation; dest_slot.parent_idx = Some(src); + src_slot.children.push(dest); + Ok(()) } - /// Публичный метод отзыва прав. - /// Каскадно аннулирует все дочерние дескрипторы и отправляет их токены в lock-free очередь VMM. pub fn revoke(&self, slot_idx: usize) -> Result<(), &'static str> { if slot_idx >= self.slots.len() { return Err("Index out of bounds"); } - // Запускаем рекурсивный отзыв self.revoke_internal(slot_idx); Ok(()) } - /// Внутренний метод каскадного удаления. - /// Вынесен отдельно, чтобы избежать удержания блокировок при переходе на следующий уровень рекурсии. fn revoke_internal(&self, slot_idx: usize) { - // 1. Сначала рекурсивно ищем и уничтожаем всех потомков данного слота - for i in 0..self.slots.len() { - let is_child = { - let child = self.slots[i].lock(); - child.parent_idx == Some(slot_idx) - }; + let children: Vec = { + let slot = self.slots[slot_idx].lock(); + slot.children.clone() + }; - if is_child { - // Рекурсивный спуск. Блокировка с child[i] к этому моменту уже снята, дедлока нет. - self.revoke_internal(i); + for child in children { + let is_valid_child = { + let c = self.slots[child].lock(); + c.parent_idx == Some(slot_idx) && c.cap.is_valid() + }; + if is_valid_child { + self.revoke_internal(child); } } - // 2. Теперь уничтожаем сам дескриптор в текущем слоте и отправляем его токен на отзыв в MMU - let token_to_revoke = { + let (token_to_revoke, parent_idx) = { let mut slot = self.slots[slot_idx].lock(); - if slot.cap.is_valid() { - let token = slot.cap.token_sig; - - // Перезаписываем пустой заглушкой (уничтожаем сильную ссылку ядра) + let parent = slot.parent_idx; + let token = if slot.cap.is_valid() { + let t = slot.cap.token_sig; slot.cap = Capability::empty(); slot.parent_idx = None; - - Some(token) + slot.children.clear(); + Some(t) } else { None - } + }; + (token, parent) }; - // 3. Если слот содержал валидный токен, асинхронно уведомляем VMM актёра через lock-free кольцевой буфер if let Some(token) = token_to_revoke { - // В Elyz 0xDEAD_BEEF используется для сырого Untyped, его в MMU слать бессмысленно if token != 0 && token != 0xDEAD_BEEF { if let Err(e) = MMU_REVOCATION_QUEUE.push(token) { - // Переполнение очереди отзыва — критический сбой планировщика ресурсов ядра panic!("FATAL: Критическое переполнение очереди отзыва MMU: {}", e); } } } + + if let Some(parent) = parent_idx { + let mut parent_slot = self.slots[parent].lock(); + parent_slot.children.retain(|&c| c != slot_idx); + } } pub fn get_cap(&self, slot: usize) -> Option { diff --git a/kernel/src/cpu/mod.rs b/kernel/src/cpu/mod.rs index 7177b8f..4d0959f 100644 --- a/kernel/src/cpu/mod.rs +++ b/kernel/src/cpu/mod.rs @@ -1,3 +1,4 @@ pub mod idt; pub mod interrupts; pub mod lapic; +pub mod smp; diff --git a/kernel/src/cpu/smp.rs b/kernel/src/cpu/smp.rs new file mode 100644 index 0000000..695740b --- /dev/null +++ b/kernel/src/cpu/smp.rs @@ -0,0 +1,61 @@ +use core::sync::atomic::{AtomicU64, Ordering}; +use limine::mp; +use crate::mem::vmm::ACTIVE_CPUS_MASK; + +static KERNEL_PML4: AtomicU64 = AtomicU64::new(0); + +pub fn set_kernel_pml4(phys: u64) { + KERNEL_PML4.store(phys, Ordering::Release); +} + +pub fn start_aps(mp_res: &limine::response::MpResponse) { + let bsp_lapic = mp_res.bsp_lapic_id(); + + let mut ap_count = 0; + for cpu in mp_res.cpus() { + if cpu.lapic_id == bsp_lapic { + continue; + } + cpu.goto_address.write(ap_entry); + ap_count += 1; + } + + if ap_count == 0 { + return; + } + + let target_mask: u64 = mp_res.cpus().iter() + .filter(|cpu| cpu.lapic_id != bsp_lapic && (cpu.lapic_id as u64) < 64) + .fold(0, |mask, cpu| mask | (1u64 << cpu.lapic_id)); + + while ACTIVE_CPUS_MASK.load(Ordering::Acquire) & target_mask != target_mask { + core::hint::spin_loop(); + } +} + +extern "C" fn ap_entry(cpu: &mp::Cpu) -> ! { + let pml4 = KERNEL_PML4.load(Ordering::Acquire); + if pml4 != 0 { + unsafe { + core::arch::asm!("mov cr3, {0}", in(reg) pml4, + options(nostack, preserves_flags)); + } + } + + unsafe { + (*core::ptr::addr_of!(crate::cpu::interrupts::IDT)).load(); + } + + crate::cpu::lapic::init(); + + let lapic_id = cpu.lapic_id; + if (lapic_id as u64) < 64 { + ACTIVE_CPUS_MASK.fetch_or(1u64 << lapic_id, Ordering::AcqRel); + } + + unsafe { core::arch::asm!("sti", options(nomem, nostack, preserves_flags)); } + + loop { + unsafe { core::arch::asm!("hlt", options(nomem, nostack, preserves_flags)); } + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index d7e6cf0..be835fd 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -8,7 +8,7 @@ use core::fmt::{self, Write}; use alloc::vec::Vec; use limine::BaseRevision; -use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker, HhdmRequest, MemoryMapRequest, ExecutableAddressRequest}; +use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker, HhdmRequest, MemoryMapRequest, ExecutableAddressRequest, MpRequest}; use crate::mem::paging::{PageTable, PageTableFlags}; use crate::mem::address::{PhysAddr, VirtAddr}; @@ -161,6 +161,9 @@ static BASE_REVISION: BaseRevision = BaseRevision::new(); #[used] #[unsafe(link_section = ".requests")] static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); +#[used] #[unsafe(link_section = ".requests")] +static MP_REQUEST: MpRequest = MpRequest::new(); + #[used] #[unsafe(link_section = ".requests_start_marker")] static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new(); @@ -224,6 +227,7 @@ unsafe extern "C" fn kmain() -> ! { info!(console, "MMU", "Activating Kernel Page Tables..."); unsafe { p4.activate(p4_phys); } + cpu::smp::set_kernel_pml4(p4_phys.0); let heap_start = 0xFFFF_9000_0000_0000; let heap_size = 8 * 1024 * 1024; @@ -250,6 +254,19 @@ unsafe extern "C" fn kmain() -> ! { unsafe { core::arch::asm!("sti", options(nomem, nostack, preserves_flags)); } + if let Some(mp_res) = MP_REQUEST.get_response() { + let cpu_ct = mp_res.cpus().len(); + let bsp_id = mp_res.bsp_lapic_id(); + info!(console, "SMP", "Detected {} CPU(s), BSP APIC ID: {}", cpu_ct, bsp_id); + if cpu_ct > 1 { + cpu::smp::start_aps(mp_res); + let active = crate::mem::vmm::ACTIVE_CPUS_MASK.load(core::sync::atomic::Ordering::Relaxed); + info!(console, "SMP", "All APs online. Active mask: {:#018x}", active); + } + } else { + info!(console, "SMP", "No MP response — running single-core"); + } + let root_cnode = cap::CNode::new(256); if let Some(frame) = mem::pmm::alloc_frame() { let mem_cap = Capability {