feat: base PM scheduller

This commit is contained in:
Faynot
2026-06-28 20:31:23 +03:00
parent f5dd56a379
commit 1b38c7f445
9 changed files with 667 additions and 145 deletions

View File

@@ -2,11 +2,12 @@
pub mod address;
pub mod allocator;
pub mod buddy; // ← new: per-actor buddy allocator
pub mod buddy;
pub mod paging;
pub mod pm_manages;
pub mod pmm;
pub mod vmm;
pub mod pm_router;
/// Initialise the physical memory manager.
///

View File

@@ -16,6 +16,7 @@ bitflags! {
const DIRTY = 1 << 6;
const HUGE_PAGE = 1 << 7;
const GLOBAL = 1 << 8;
const COW = 1 << 9;
const NO_EXECUTE = 1 << 63;
}
}
@@ -48,6 +49,55 @@ impl PageTable {
}
}
pub fn get_flags(&self, virt: VirtAddr, hhdm: u64) -> Option<PageTableFlags> {
let p4_idx = ((virt.0 >> 39) & 0x1FF) as usize;
let p3_idx = ((virt.0 >> 30) & 0x1FF) as usize;
let p2_idx = ((virt.0 >> 21) & 0x1FF) as usize;
let p1_idx = ((virt.0 >> 12) & 0x1FF) as usize;
macro_rules! descend_ref {
($entry:expr) => {{
let e = $entry;
if e & PageTableFlags::PRESENT.bits() == 0 { return None; }
unsafe { &*PhysAddr(e & PTE_ADDR_MASK).to_virt(hhdm).as_mut_ptr::<PageTable>() }
}};
}
let p3 = descend_ref!(self.entries[p4_idx]);
let p3e = p3.entries[p3_idx];
if p3e & PageTableFlags::HUGE_PAGE.bits() != 0 {
return Some(PageTableFlags::from_bits_truncate(p3e));
}
let p2 = descend_ref!(p3e);
let p2e = p2.entries[p2_idx];
if p2e & PageTableFlags::HUGE_PAGE.bits() != 0 {
return Some(PageTableFlags::from_bits_truncate(p2e));
}
let p1 = descend_ref!(p2e);
let p1e = p1.entries[p1_idx];
if p1e & PageTableFlags::PRESENT.bits() == 0 { return None; }
Some(PageTableFlags::from_bits_truncate(p1e))
}
pub fn update_flags(&mut self, virt: VirtAddr, flags: PageTableFlags, hhdm: u64) -> Result<(), ()> {
let Some(p1) = self.walk_to_p1_mut(virt, hhdm, false) else { return Err(()); };
let p1_idx = ((virt.0 >> 12) & 0x1FF) as usize;
let entry = p1.entries[p1_idx];
if entry & PageTableFlags::PRESENT.bits() == 0 { return Err(()); }
p1.entries[p1_idx] = (entry & PTE_ADDR_MASK) | flags.bits();
unsafe {
asm!("invlpg [{}]", in(reg) virt.0, options(nostack, preserves_flags));
}
Ok(())
}
//Single-page operations
/// Map a single 4 KiB page.

184
kernel/src/mem/pm_router.rs Normal file
View File

@@ -0,0 +1,184 @@
use core::sync::atomic::{AtomicU16, AtomicU8, AtomicBool, Ordering};
use core::cell::UnsafeCell;
use alloc::vec::Vec;
use alloc::boxed::Box;
use crate::mem::pm_manages::{PMActor, PMRequest, PMResponse, PMResult};
const CHANNEL_COUNT: usize = 65536;
const STATE_FREE: u8 = 0;
const STATE_PENDING: u8 = 1;
const STATE_READY: u8 = 2;
#[repr(align(64))]
pub struct Channel {
state: AtomicU8,
next_free: AtomicU16,
result: UnsafeCell<Option<PMResult>>,
}
unsafe impl Send for Channel {}
unsafe impl Sync for Channel {}
pub struct PMRouter {
channels: Box<[Channel]>,
free_head: AtomicU16,
}
struct GlobalRouter {
is_ready: AtomicBool,
inner: UnsafeCell<Option<PMRouter>>,
}
unsafe impl Sync for GlobalRouter {}
unsafe impl Send for GlobalRouter {}
static ROUTER: GlobalRouter = GlobalRouter {
is_ready: AtomicBool::new(false),
inner: UnsafeCell::new(None),
};
pub fn init() {
if ROUTER.is_ready.load(Ordering::Acquire) {
panic!("PMRouter is already initialized!");
}
let mut channels = Vec::with_capacity(CHANNEL_COUNT);
for i in 0..CHANNEL_COUNT {
channels.push(Channel {
state: AtomicU8::new(STATE_FREE),
next_free: AtomicU16::new((i + 1) as u16),
result: UnsafeCell::new(None),
});
}
channels[CHANNEL_COUNT - 1].next_free.store(0, Ordering::Relaxed);
let router = PMRouter {
channels: channels.into_boxed_slice(),
free_head: AtomicU16::new(1),
};
unsafe {
*ROUTER.inner.get() = Some(router);
}
ROUTER.is_ready.store(true, Ordering::Release);
}
#[inline(always)]
pub fn get_router() -> &'static PMRouter {
if ROUTER.is_ready.load(Ordering::Acquire) {
unsafe {
(*ROUTER.inner.get()).as_ref().unwrap_unchecked()
}
} else {
panic!("FATAL: PMRouter is accessed before initialization!")
}
}
impl PMRouter {
pub fn alloc_channel(&self) -> Option<u16> {
let mut head = self.free_head.load(Ordering::Acquire);
loop {
if head == 0 {
return None;
}
let next = self.channels[head as usize].next_free.load(Ordering::Relaxed);
match self.free_head.compare_exchange_weak(
head,
next,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
self.channels[head as usize].state.store(STATE_PENDING, Ordering::Release);
return Some(head);
}
Err(new_head) => head = new_head,
}
}
}
pub fn route_responses(&self, responses: Vec<PMResponse>) {
for resp in responses {
if resp.channel_id == 0 {
continue;
}
let idx = resp.channel_id as usize;
if idx >= CHANNEL_COUNT {
panic!("PMRouter: Received response for out-of-bounds channel_id: {}", idx);
}
let channel = &self.channels[idx];
unsafe {
*channel.result.get() = Some(resp.result);
}
channel.state.store(STATE_READY, Ordering::Release);
// Когда в будущем реализуешь Focus Mode, здесь нужно вызывать сигнал пробуждения конкретного процесса/потока (wake_up(thread_id)).
}
}
pub fn wait_for_response(&self, id: u16) -> PMResult {
let channel = &self.channels[id as usize];
while channel.state.load(Ordering::Acquire) != STATE_READY {
core::hint::spin_loop();
// TODO: Для "Focus Mode" и полноценного планировщика:
// scheduler::yield_to_actor();
}
let result = unsafe {
(*channel.result.get()).take().expect("PMRouter: Data missing on READY state")
};
channel.state.store(STATE_FREE, Ordering::Release);
let mut head = self.free_head.load(Ordering::Relaxed);
loop {
channel.next_free.store(head, Ordering::Relaxed);
match self.free_head.compare_exchange_weak(
head,
id,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(new_head) => head = new_head,
}
}
result
}
}
pub fn request_and_wait<F>(actor: &PMActor, req_builder: F) -> PMResult
where
F: FnOnce(u16) -> PMRequest
{
let router = get_router();
let channel_id = router.alloc_channel().expect("FATAL: Out of PM routing channels");
let req = req_builder(channel_id);
actor.submit_request(req).expect("FATAL: PMActor inbox is full");
// Временно вручную прокручиваем сообщения актёра (если мы пока работаем в 1 потоке).
// Когда актёры переедут на отдельные ядра/треды, эту строчку нужно будет убрать,
// так как актёр сам будет вызывать process_messages в бесконечном цикле.
// router.route_responses(actor.process_messages()); // Включать только при тестировании в single-core!
router.wait_for_response(channel_id)
}
pub fn dispatch(responses: Vec<PMResponse>) {
get_router().route_responses(responses);
}

View File

@@ -5,6 +5,7 @@ pub const PAGE_SIZE: u64 = 4096;
pub struct BitmapPMM {
bitmap: &'static mut [u8],
ref_counts: &'static mut [u16],
total_pages: usize,
used_pages: usize,
/// Byte index hint: next search starts here to amortise O(N) scans.
@@ -28,30 +29,36 @@ impl BitmapPMM {
.unwrap_or(0);
let total_pages = (max_addr / PAGE_SIZE) as usize;
let bitmap_size = total_pages.div_ceil(8);
// Find a usable region large enough to hold the bitmap.
let bitmap_phys = mmap.entries().iter()
let bitmap_size = total_pages.div_ceil(8);
let ref_counts_size = total_pages * core::mem::size_of::<u16>();
let total_meta_size = bitmap_size + ref_counts_size;
let meta_phys = mmap.entries().iter()
.find(|e| {
e.entry_type == limine::memory_map::EntryType::USABLE
&& e.length >= bitmap_size as u64
&& e.length >= total_meta_size as u64
})
.map(|e| e.base)
.expect("PMM: no usable region large enough for the bitmap");
.expect("PMM: no usable region large enough for metadata");
let bitmap_ptr = (meta_phys + hhdm_offset) as *mut u8;
let ref_counts_ptr = (meta_phys + hhdm_offset + bitmap_size as u64) as *mut u16;
let bitmap_ptr = (bitmap_phys + hhdm_offset) as *mut u8;
// Mark everything as used (all bits = 1) and free usable entries below.
let bitmap = unsafe { core::slice::from_raw_parts_mut(bitmap_ptr, bitmap_size) };
bitmap.fill(0xFF);
let ref_counts = unsafe { core::slice::from_raw_parts_mut(ref_counts_ptr, total_pages) };
ref_counts.fill(1);
let mut pmm = Self {
bitmap,
ref_counts,
total_pages,
used_pages: total_pages,
last_byte: 0,
};
// Free all usable pages …
for entry in mmap.entries() {
if entry.entry_type == limine::memory_map::EntryType::USABLE {
for addr in (entry.base..entry.base + entry.length).step_by(PAGE_SIZE as usize) {
@@ -60,12 +67,11 @@ impl BitmapPMM {
}
}
// … then re-lock the bitmap pages themselves …
for addr in (bitmap_phys..bitmap_phys + bitmap_size as u64).step_by(PAGE_SIZE as usize) {
let meta_end = (meta_phys + total_meta_size as u64 + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
for addr in (meta_phys..meta_end).step_by(PAGE_SIZE as usize) {
pmm.lock_frame(PhysAddr(addr));
}
// … and the null page (physical 0x0 must never be returned as a valid frame).
pmm.lock_frame(PhysAddr(0));
*PMM.lock() = Some(pmm);
@@ -73,37 +79,52 @@ impl BitmapPMM {
//Core operations
/// Mark a frame as free. Idempotent (double-free is a no-op, not UB).
pub fn free_frame(&mut self, phys_addr: PhysAddr) {
let idx = (phys_addr.0 / PAGE_SIZE) as usize;
if idx >= self.total_pages { return; }
let byte = idx / 8;
let bit = idx % 8;
if self.bitmap[byte] & (1 << bit) != 0 {
self.bitmap[byte] &= !(1 << bit);
self.used_pages -= 1;
// Pull the hint back so the freed page can be found quickly.
if byte < self.last_byte { self.last_byte = byte; }
self.ref_counts[idx] = self.ref_counts[idx].saturating_sub(1);
if self.ref_counts[idx] == 0 {
self.bitmap[byte] &= !(1 << bit);
self.used_pages -= 1;
if byte < self.last_byte { self.last_byte = byte; }
}
}
}
/// Mark a frame as allocated (reserved). Idempotent.
pub fn lock_frame(&mut self, phys_addr: PhysAddr) {
let idx = (phys_addr.0 / PAGE_SIZE) as usize;
if idx >= self.total_pages { return; }
let byte = idx / 8;
let bit = idx % 8;
if self.bitmap[byte] & (1 << bit) == 0 {
self.bitmap[byte] |= 1 << bit;
self.ref_counts[idx] = 1;
self.used_pages += 1;
} else if self.ref_counts[idx] == 0 {
self.ref_counts[idx] = 1;
}
}
pub fn inc_ref_frame(&mut self, phys_addr: PhysAddr) {
let idx = (phys_addr.0 / PAGE_SIZE) as usize;
if idx >= self.total_pages { return; }
let byte = idx / 8;
let bit = idx % 8;
if self.bitmap[byte] & (1 << bit) != 0 {
self.ref_counts[idx] = self.ref_counts[idx].saturating_add(1);
}
}
/// Allocate one physical frame.
///
/// Uses a two-pass search (linear scan from `last_byte` hint, then wraps
/// to 0 if not found in the first pass) to avoid returning `None` when
/// free frames exist before the hint.
pub fn alloc_frame(&mut self) -> Option<PhysAddr> {
let len = self.bitmap.len();
@@ -115,7 +136,6 @@ impl BitmapPMM {
};
for byte_idx in from..to {
// Fast path: skip fully-used bytes.
if self.bitmap[byte_idx] == 0xFF { continue; }
for bit in 0..8u8 {
@@ -123,8 +143,8 @@ impl BitmapPMM {
let page_idx = byte_idx * 8 + bit as usize;
if page_idx >= self.total_pages { return None; }
// Mark allocated.
self.bitmap[byte_idx] |= 1 << bit;
self.ref_counts[page_idx] = 1;
self.used_pages += 1;
self.last_byte = byte_idx;
@@ -134,17 +154,9 @@ impl BitmapPMM {
}
}
None // genuinely out of memory
None
}
/// Try to allocate `count` **contiguous** physical frames.
///
/// Returns the base physical address of the run, or `None` if no run of
/// sufficient length exists. This is needed for (e.g.) allocating 2 MiB
/// huge-page aligned regions or DMA buffers that must be physically
/// contiguous.
///
/// O(N) worst-case; use sparingly and prefer small counts.
pub fn alloc_contiguous(&mut self, count: usize) -> Option<PhysAddr> {
if count == 0 { return None; }
@@ -154,13 +166,15 @@ impl BitmapPMM {
for page_idx in 0..self.total_pages {
let byte = page_idx / 8;
let bit = page_idx % 8;
if self.bitmap[byte] & (1 << bit) == 0 {
if run_len == 0 { run_start = page_idx; }
run_len += 1;
if run_len == count {
// Lock every frame in the run.
for i in run_start..run_start + count {
self.bitmap[i / 8] |= 1 << (i % 8);
self.ref_counts[i] = 1;
}
self.used_pages += count;
self.last_byte = run_start / 8;
@@ -191,6 +205,12 @@ pub fn free_frame(addr: PhysAddr) {
}
}
pub fn inc_ref_frame(addr: PhysAddr) {
if let Some(pmm) = PMM.lock().as_mut() {
pmm.inc_ref_frame(addr);
}
}
pub fn get_stats() -> (usize, usize) {
if let Some(pmm) = PMM.lock().as_ref() {
(pmm.used_pages(), pmm.total_pages())
@@ -198,3 +218,7 @@ pub fn get_stats() -> (usize, usize) {
(0, 0)
}
}

View File

@@ -27,7 +27,7 @@
#![allow(dead_code)]
use alloc::vec::Vec;
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering, AtomicU64, AtomicU16};
use crate::mem::address::{PhysAddr, VirtAddr};
use crate::mem::allocator::Locked;
@@ -35,8 +35,37 @@ use crate::mem::paging::{PageTable, PageTableFlags};
use crate::mem::pmm;
use crate::events::MMU_REVOCATION_QUEUE;
use core::hint::spin_loop;
extern crate alloc;
pub static ACTIVE_CPUS_MASK: AtomicU64 = AtomicU64::new(1);
static SHOOTDOWN_LOCK: Locked<()> = Locked::new(());
static SHOOTDOWN_ASID: AtomicU16 = AtomicU16::new(0);
static SHOOTDOWN_ACK: AtomicU64 = AtomicU64::new(0);
#[inline]
fn local_tlb_flush_asid(asid: u16) {
if INVPCID_SUPPORTED.load(Ordering::Relaxed) {
#[repr(C, packed)]
struct InvpcidDesc { pcid: u64, addr: u64 }
let desc = InvpcidDesc { pcid: asid as u64, addr: 0 };
unsafe {
core::arch::asm!(
"invpcid {ty}, [{desc}]",
ty = in(reg) 1u64, // type 1 = single-context flush
desc = in(reg) &desc,
options(nostack, preserves_flags),
);
}
} else {
tlb_flush_all();
}
}
// Error type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmError {
@@ -91,6 +120,7 @@ bitflags::bitflags! {
const PINNED = 1 << 6;
const NOCACHE = 1 << 7;
const MMIO = 1 << 8;
const COW = 1 << 9;
}
}
@@ -104,6 +134,7 @@ impl VmaFlags {
if self.contains(Self::NOCACHE) || self.contains(Self::MMIO) {
f |= PageTableFlags::NO_CACHE | PageTableFlags::WRITE_THROUGH;
}
if self.contains(Self::COW) { f |= PageTableFlags::COW; }
f
}
}
@@ -317,6 +348,77 @@ impl AddressSpace {
Ok(())
}
pub fn clone_for_fork(&mut self, child_cap_token: u64) -> Result<Self, VmError> {
let mut child = AddressSpace::new(self.hhdm)?;
let child_pml4 = unsafe { &mut *child.pml4_raw() };
let parent_pml4 = unsafe { &mut *self.pml4_raw() };
let hhdm = self.hhdm;
for region in &mut self.regions {
let mut child_region = VmaRegion {
virt_start: region.virt_start,
virt_end: region.virt_end,
flags: region.flags,
cap_token: child_cap_token,
backing: match &region.backing {
VmaBacking::Physical(base) => VmaBacking::Physical(*base),
VmaBacking::Shared { owner_cap, phys_base } => VmaBacking::Shared { owner_cap: *owner_cap, phys_base: *phys_base },
VmaBacking::Anonymous(frames) => {
let mut new_frames = Vec::with_capacity(frames.len());
new_frames.resize_with(frames.len(), || None);
VmaBacking::Anonymous(new_frames)
}
}
};
match &mut region.backing {
VmaBacking::Physical(base) => {
child_pml4.map_region(region.virt_start, *base, region.size(), region.flags.to_page_flags(), hhdm);
}
VmaBacking::Shared { phys_base, .. } => {
child_pml4.map_region(region.virt_start, *phys_base, region.size(), region.flags.to_page_flags(), hhdm);
}
VmaBacking::Anonymous(frames) => {
let cow_needed = region.flags.contains(VmaFlags::WRITE);
if cow_needed {
region.flags.insert(VmaFlags::COW);
child_region.flags.insert(VmaFlags::COW);
}
let VmaBacking::Anonymous(ref mut child_frames) = child_region.backing else { unreachable!() };
for (i, frame_opt) in frames.iter().enumerate() {
if let Some(frame) = frame_opt {
let virt = VirtAddr(region.virt_start.0 + i as u64 * 4096);
pmm::inc_ref_frame(*frame);
let mut page_flags = region.flags.to_page_flags();
if cow_needed {
page_flags.remove(PageTableFlags::WRITABLE);
page_flags.insert(PageTableFlags::COW);
let _ = parent_pml4.update_flags(virt, page_flags, hhdm);
}
child_pml4.map_page(virt, *frame, page_flags, hhdm);
child_frames[i] = Some(*frame);
}
}
}
}
child.regions.push(child_region);
}
tlb_flush_asid(self.asid);
Ok(child)
}
fn insert_sorted(&mut self, region: VmaRegion) {
let pos = self.regions
.partition_point(|r| r.virt_start.0 < region.virt_start.0);
@@ -518,27 +620,62 @@ impl AddressSpace {
let hhdm = self.hhdm;
let idx = self.find_idx(fault_addr).ok_or(VmError::RegionNotFound)?;
{
let region = &self.regions[idx];
if write && !region.flags.contains(VmaFlags::WRITE) {
return Err(VmError::PermissionDenied);
}
if !region.flags.contains(VmaFlags::LAZY) {
return Err(VmError::UnexpectedFault);
}
let (virt_start, region_flags) = {
let r = &self.regions[idx];
(r.virt_start, r.flags)
};
if write && !region_flags.contains(VmaFlags::WRITE) {
return Err(VmError::PermissionDenied);
}
let region = &mut self.regions[idx];
let page_idx = ((fault_addr.0 - region.virt_start.0) / 4096) as usize;
let page_virt = VirtAddr(region.virt_start.0 + page_idx as u64 * 4096);
let page_flags = region.flags.to_page_flags();
let page_idx = ((fault_addr.0 - virt_start.0) / 4096) as usize;
let page_virt = VirtAddr(virt_start.0 + page_idx as u64 * 4096);
let pml4 = unsafe { &mut *self.pml4_raw() };
let current_pte_flags = pml4.get_flags(page_virt, hhdm);
let is_cow = current_pte_flags.map_or(false, |f| f.contains(PageTableFlags::COW));
if write && is_cow {
let region = &mut self.regions[idx];
let VmaBacking::Anonymous(ref mut frames) = region.backing else {
return Err(VmError::UnexpectedFault);
};
let old_frame = frames[page_idx].expect("COW fault on unmapped page");
let new_frame = pmm::alloc_frame().ok_or(VmError::OutOfMemory)?;
unsafe {
core::ptr::copy_nonoverlapping(
old_frame.to_virt(hhdm).as_ptr::<u8>(),
new_frame.to_virt(hhdm).as_mut_ptr::<u8>(),
4096,
);
}
frames[page_idx] = Some(new_frame);
let mut target_flags = region.flags.to_page_flags();
target_flags.remove(PageTableFlags::COW);
target_flags.insert(PageTableFlags::WRITABLE);
pml4.map_page(page_virt, new_frame, target_flags, hhdm);
pmm::free_frame(old_frame);
return Ok(());
}
if !region_flags.contains(VmaFlags::LAZY) {
return Err(VmError::UnexpectedFault);
}
let region = &mut self.regions[idx];
let VmaBacking::Anonymous(ref mut frames) = region.backing else {
return Err(VmError::RegionNotFound);
};
if frames[page_idx].is_some() {
// SMP race: another core already mapped this page.
return Ok(());
}
@@ -548,13 +685,16 @@ impl AddressSpace {
}
frames[page_idx] = Some(frame);
let pml4 = unsafe { &mut *self.pml4_raw() };
pml4.map_page(page_virt, frame, page_flags, hhdm);
let mut target_flags = region.flags.to_page_flags();
if region.flags.contains(VmaFlags::COW) {
target_flags.remove(PageTableFlags::WRITABLE);
}
pml4.map_page(page_virt, frame, target_flags, hhdm);
Ok(())
}
/// Unmap the VMA containing `virt`, free its frames (if owned), flush TLB.
pub fn unmap_region(&mut self, virt: VirtAddr) -> Result<(), VmError> {
let idx = self.find_idx(virt).ok_or(VmError::RegionNotFound)?;
let region = self.regions.remove(idx);
@@ -648,22 +788,26 @@ impl Drop for AddressSpace {
/// **SMP note**: on multi-core systems a TLB-shootdown IPI to all remote cores
/// must be added once the LAPIC driver and scheduler are online.
pub fn tlb_flush_asid(asid: u16) {
if INVPCID_SUPPORTED.load(Ordering::Relaxed) {
#[repr(C, packed)]
struct InvpcidDesc { pcid: u64, addr: u64 }
// 1. Всегда сбрасываем локальный кэш
local_tlb_flush_asid(asid);
let desc = InvpcidDesc { pcid: asid as u64, addr: 0 };
unsafe {
core::arch::asm!(
"invpcid {ty}, [{desc}]",
ty = in(reg) 1u64, // type 1 = single-context flush
desc = in(reg) &desc,
options(nostack, preserves_flags),
);
}
} else {
// Fallback: full TLB flush via CR3 reload (clears all PCID entries).
tlb_flush_all();
let active_cpus = ACTIVE_CPUS_MASK.load(Ordering::Acquire);
let current_core = crate::cpu::lapic::current_core_id();
let target_mask = active_cpus & !(1u64 << current_core);
if target_mask == 0 {
return;
}
let _guard = SHOOTDOWN_LOCK.lock();
SHOOTDOWN_ASID.store(asid, Ordering::Release);
SHOOTDOWN_ACK.store(0, Ordering::Release);
crate::cpu::lapic::broadcast_ipi_exclude_self(crate::cpu::interrupts::TLB_SHOOTDOWN_VECTOR);
while SHOOTDOWN_ACK.load(Ordering::Acquire) & target_mask != target_mask {
spin_loop();
}
}
@@ -680,10 +824,18 @@ pub fn tlb_flush_all() {
}
}
pub fn handle_tlb_shootdown_ipi() {
let asid = SHOOTDOWN_ASID.load(Ordering::Acquire);
local_tlb_flush_asid(asid);
let current_core = crate::cpu::lapic::current_core_id();
SHOOTDOWN_ACK.fetch_or(1u64 << current_core, Ordering::AcqRel);
}
// Global kernel address space
/// The one kernel address space. Initialised once during boot.
pub static KERNEL_SPACE: Locked<Option<AddressSpace>> = Locked::new(None);
/// Register the already-active PML4 as the kernel address space.
///
/// ASID 0 = PCID 0 = kernel (no per-process PCID tagging).