kernel: add real MSR scheme access
This commit is contained in:
@@ -247,10 +247,12 @@ fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt, backup_stack_end: usize) {
|
||||
current_idt[IpiKind::Switch as usize].set_func(ipi::switch);
|
||||
current_idt[IpiKind::Tlb as usize].set_func(ipi::tlb);
|
||||
current_idt[IpiKind::Pit as usize].set_func(ipi::pit);
|
||||
current_idt[IpiKind::Msr as usize].set_func(ipi::msr);
|
||||
idt.set_reserved_mut(IpiKind::Wakeup as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Switch as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Tlb as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Pit as u8, true);
|
||||
idt.set_reserved_mut(IpiKind::Msr as u8, true);
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
arch::device::local_apic::the_local_apic, context, percpu::PercpuBlock, sync::CleanLockToken,
|
||||
};
|
||||
use x86::msr::{rdmsr, wrmsr};
|
||||
|
||||
interrupt!(wakeup, || {
|
||||
unsafe { the_local_apic().eoi() };
|
||||
@@ -26,3 +27,20 @@ interrupt!(pit, || {
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
context::switch::tick(&mut token);
|
||||
});
|
||||
|
||||
interrupt!(msr, || {
|
||||
let percpu = PercpuBlock::current();
|
||||
let mailbox = percpu.msr_mailbox();
|
||||
let op = mailbox.request();
|
||||
|
||||
let result = if op.is_write {
|
||||
unsafe { wrmsr(op.msr as u32, op.value) };
|
||||
op.value
|
||||
} else {
|
||||
unsafe { rdmsr(op.msr as u32) }
|
||||
};
|
||||
|
||||
mailbox.finish_handle(result);
|
||||
|
||||
unsafe { the_local_apic().eoi() };
|
||||
});
|
||||
|
||||
@@ -5,9 +5,10 @@ pub enum IpiKind {
|
||||
Tlb = 0x41,
|
||||
Switch = 0x42,
|
||||
Pit = 0x43,
|
||||
Msr = 0x44,
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
Profile = 0x44,
|
||||
Profile = 0x45,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
||||
+64
-1
@@ -4,7 +4,7 @@ use alloc::{
|
||||
};
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
|
||||
sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicU64, AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
use rmm::Arch;
|
||||
@@ -46,9 +46,65 @@ pub struct PercpuBlock {
|
||||
|
||||
pub misc_arch_info: crate::arch::device::ArchPercpuMisc,
|
||||
|
||||
pub msr_mailbox: MsrMailbox,
|
||||
|
||||
pub stats: CpuStats,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct MsrMailbox {
|
||||
cpu_id: AtomicU32,
|
||||
msr: AtomicU64,
|
||||
value: AtomicU64,
|
||||
kind: AtomicU8,
|
||||
done: AtomicBool,
|
||||
}
|
||||
|
||||
impl MsrMailbox {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
cpu_id: AtomicU32::new(u32::MAX),
|
||||
msr: AtomicU64::new(0),
|
||||
value: AtomicU64::new(0),
|
||||
kind: AtomicU8::new(0),
|
||||
done: AtomicBool::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_request(&self, cpu_id: u32, msr: u32, is_write: bool, value: u64) {
|
||||
self.done.store(false, Ordering::Relaxed);
|
||||
self.cpu_id.store(cpu_id, Ordering::Relaxed);
|
||||
self.msr.store(msr as u64, Ordering::Relaxed);
|
||||
self.value.store(value, Ordering::Relaxed);
|
||||
self.kind.store(is_write as u8, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn wait_done(&self) -> bool {
|
||||
self.done.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn request(&self) -> MsrRequest {
|
||||
MsrRequest {
|
||||
msr: self.msr.load(Ordering::Acquire),
|
||||
value: self.value.load(Ordering::Acquire),
|
||||
is_write: self.kind.load(Ordering::Acquire) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish_handle(&self, value: u64) {
|
||||
self.value.store(value, Ordering::Release);
|
||||
self.done.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn read_value(&self) -> u64 { self.value.load(Ordering::Acquire) }
|
||||
}
|
||||
|
||||
pub struct MsrRequest {
|
||||
pub msr: u64,
|
||||
pub value: u64,
|
||||
pub is_write: bool,
|
||||
}
|
||||
|
||||
static ALL_PERCPU_BLOCKS: [AtomicPtr<PercpuBlock>; MAX_CPU_COUNT as usize] =
|
||||
[const { AtomicPtr::new(core::ptr::null_mut()) }; MAX_CPU_COUNT as usize];
|
||||
|
||||
@@ -199,7 +255,14 @@ impl PercpuBlock {
|
||||
|
||||
misc_arch_info: ArchPercpuMisc::default(),
|
||||
|
||||
msr_mailbox: MsrMailbox::new(),
|
||||
|
||||
stats: CpuStats::default(),
|
||||
}
|
||||
}
|
||||
pub fn msr_mailbox(&self) -> &MsrMailbox { &self.msr_mailbox }
|
||||
}
|
||||
|
||||
pub fn get_percpu_block(cpu: LogicalCpuId) -> Option<&'static PercpuBlock> {
|
||||
unsafe { ALL_PERCPU_BLOCKS[cpu.get() as usize].load(Ordering::Acquire).as_ref() }
|
||||
}
|
||||
|
||||
+44
-49
@@ -11,61 +11,19 @@
|
||||
//! `scheme:msr` interface for ring-3 access, but this kernel-side
|
||||
//! helper is for the scheme to forward requests to the active CPU).
|
||||
//!
|
||||
//! Note: in this kernel fork, MSR access is implemented as a per-CPU
|
||||
//! `Arc<Mutex<HashMap<u32, u64>>>` storage. The hardware MSRs are
|
||||
//! accessible only from ring 0 (kernel); this scheme is a thin wrapper
|
||||
//! that validates CPU + register index and lets userspace store/retrieve
|
||||
//! the values. This matches the existing
|
||||
//! `local/recipes/system/redbear-power/source/src/msr.rs` library
|
||||
//! expectations on a Linux host and gives `cpufreqd` a real R/W path
|
||||
//! on Redox bare metal.
|
||||
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use spin::Mutex;
|
||||
use x86::msr::{rdmsr, wrmsr};
|
||||
|
||||
use crate::cpu_count;
|
||||
use crate::ipi::{ipi_single, IpiKind};
|
||||
use crate::percpu::PercpuBlock;
|
||||
use syscall::{
|
||||
error::{Error, Result, EBADF, EINVAL, ENOENT, EPERM},
|
||||
error::{Error, Result, EBUSY, EBADF, EINVAL, ENOENT, EPERM, ETIMEDOUT},
|
||||
};
|
||||
use crate::scheme::CallerCtx;
|
||||
use crate::sync::CleanLockToken;
|
||||
use crate::syscall::usercopy::{UserSliceRo, UserSliceWo};
|
||||
|
||||
const MSR_BUCKETS: usize = 1024;
|
||||
|
||||
/// One bucket entry: a (cpu, msr) → value mapping.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct MsrEntry {
|
||||
cpu: u32,
|
||||
msr: u32,
|
||||
value: u64,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
static MSR_STORE: Mutex<[MsrEntry; MSR_BUCKETS]> = Mutex::new(
|
||||
[MsrEntry { cpu: 0, msr: 0, value: 0, valid: false }; MSR_BUCKETS],
|
||||
);
|
||||
static NEXT_SLOT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn store_msr(cpu: u32, msr: u32, value: u64) {
|
||||
let mut table = MSR_STORE.lock();
|
||||
for entry in table.iter_mut() {
|
||||
if entry.valid && entry.cpu == cpu && entry.msr == msr {
|
||||
entry.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let slot = NEXT_SLOT.fetch_add(1, Ordering::Relaxed) as usize % MSR_BUCKETS;
|
||||
table[slot] = MsrEntry { cpu, msr, value, valid: true };
|
||||
}
|
||||
|
||||
fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
let table = MSR_STORE.lock();
|
||||
table
|
||||
.iter()
|
||||
.find(|e| e.valid && e.cpu == cpu && e.msr == msr)
|
||||
.map(|e| e.value)
|
||||
}
|
||||
const MSR_TIMEOUT_SPINS: usize = 1_000_000;
|
||||
|
||||
/// Open: `msr/{cpu}/0x{msr}` (read or write, root only).
|
||||
pub fn open(
|
||||
@@ -102,7 +60,7 @@ pub fn open(
|
||||
pub fn read(handle: u64, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result<usize> {
|
||||
let cpu = (handle >> 32) as u32;
|
||||
let msr = (handle & 0xFFFFFFFF) as u32;
|
||||
let value = read_msr(cpu, msr).unwrap_or(0);
|
||||
let value = access_msr(cpu, msr, None)?;
|
||||
let bytes = value.to_le_bytes();
|
||||
let n = buf.copy_common_bytes_from_slice(&bytes)?;
|
||||
Ok(n)
|
||||
@@ -114,6 +72,43 @@ pub fn write(handle: u64, buf: UserSliceRo, _token: &mut CleanLockToken) -> Resu
|
||||
let mut bytes = [0u8; 8];
|
||||
let n = buf.copy_common_bytes_to_slice(&mut bytes)?;
|
||||
let value = u64::from_le_bytes(bytes);
|
||||
store_msr(cpu, msr, value);
|
||||
let _ = access_msr(cpu, msr, Some(value))?;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn access_msr(cpu: u32, msr: u32, write: Option<u64>) -> Result<u64> {
|
||||
let current_cpu = PercpuBlock::current().cpu_id.get();
|
||||
if cpu == current_cpu {
|
||||
return unsafe {
|
||||
if let Some(value) = write {
|
||||
wrmsr(msr, value);
|
||||
Ok(value)
|
||||
} else {
|
||||
Ok(rdmsr(msr))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let Some(target) = crate::percpu::get_percpu_block(crate::cpu_set::LogicalCpuId::new(cpu))
|
||||
else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
|
||||
let mailbox = target.msr_mailbox();
|
||||
if !mailbox.wait_done() {
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
|
||||
mailbox.begin_request(cpu, msr, write.is_some(), write.unwrap_or(0));
|
||||
ipi_single(IpiKind::Msr, target);
|
||||
|
||||
for _ in 0..MSR_TIMEOUT_SPINS {
|
||||
if mailbox.wait_done() {
|
||||
let value = mailbox.read_value();
|
||||
return Ok(value);
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
Err(Error::new(ETIMEDOUT))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user