mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-07 22:04:03 -05:00
minerd: Add Linux MSR operations impl
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -4094,9 +4094,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -4318,7 +4318,10 @@ dependencies = [
|
||||
"darkfi-sdk",
|
||||
"darkfi-serial",
|
||||
"easy-parallel",
|
||||
"libc",
|
||||
"num-bigint",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.5",
|
||||
"randomx",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
|
||||
@@ -9,6 +9,10 @@ license = "AGPL-3.0-only"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.178"
|
||||
parking_lot = "0.12.5"
|
||||
once_cell = "1.21.3"
|
||||
|
||||
# Darkfi
|
||||
darkfi = {path = "../../", features = ["async-daemonize", "validator", "rpc", "bs58"]}
|
||||
darkfi-sdk = {path = "../../src/sdk"}
|
||||
|
||||
20
bin/minerd/src/hw/mod.rs
Normal file
20
bin/minerd/src/hw/mod.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2025 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// Model-Specific Registers
|
||||
pub mod msr;
|
||||
85
bin/minerd/src/hw/msr/error.rs
Normal file
85
bin/minerd/src/hw/msr/error.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2025 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{fmt, io};
|
||||
|
||||
pub type MsrResult<T> = Result<T, MsrError>;
|
||||
|
||||
/// Errors that can occur during MSR operations
|
||||
#[derive(Debug)]
|
||||
pub enum MsrError {
|
||||
/// MSR module/driver is not available
|
||||
NotAvailable(String),
|
||||
|
||||
/// Failed to read MSR
|
||||
ReadError { reg: u32, cpu: i32, source: io::Error },
|
||||
|
||||
/// Failed to write MSR
|
||||
WriteError { reg: u32, cpu: i32, source: io::Error },
|
||||
|
||||
/// No CPU units available
|
||||
NoCpuUnits,
|
||||
|
||||
/// Permission denied
|
||||
PermissionDenied(String),
|
||||
|
||||
/// Driver installation failed (Windows)
|
||||
DriverError(String),
|
||||
|
||||
/// Generic IO error
|
||||
Io(io::Error),
|
||||
|
||||
/// Platform not supported
|
||||
PlatformNotSupported,
|
||||
}
|
||||
|
||||
impl fmt::Display for MsrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
MsrError::NotAvailable(msg) => write!(f, "MSR not available: {}", msg),
|
||||
MsrError::ReadError { reg, cpu, source } => {
|
||||
write!(f, "Failed to read MSR 0x{:08x} on CPU {}: {}", reg, cpu, source)
|
||||
}
|
||||
MsrError::WriteError { reg, cpu, source } => {
|
||||
write!(f, "Failed to write MSR 0x{:08x} on CPU {}: {}", reg, cpu, source)
|
||||
}
|
||||
MsrError::NoCpuUnits => write!(f, "No CPU units available"),
|
||||
MsrError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
|
||||
MsrError::DriverError(msg) => write!(f, "Driver error: {}", msg),
|
||||
MsrError::Io(err) => write!(f, "IO error: {}", err),
|
||||
MsrError::PlatformNotSupported => write!(f, "Platform not supported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for MsrError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
MsrError::ReadError { source, .. } => Some(source),
|
||||
MsrError::WriteError { source, .. } => Some(source),
|
||||
MsrError::Io(err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for MsrError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
MsrError::Io(err)
|
||||
}
|
||||
}
|
||||
214
bin/minerd/src/hw/msr/mod.rs
Normal file
214
bin/minerd/src/hw/msr/mod.rs
Normal file
@@ -0,0 +1,214 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2025 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Cross-platform module for reading and writing CPU Model Specific Registers
|
||||
//!
|
||||
//! ## Platform support
|
||||
//!
|
||||
//! - Linux: Uses `/dev/cpu*/msr` interface with automatic module loading
|
||||
//! - Windows: Uses WinRing0 driver with automatic installation
|
||||
//!
|
||||
//! ## Requirements
|
||||
//!
|
||||
//! - Linux: root privileges and the `msr` kernel module
|
||||
//! - Windows: Administrator privileges and `WinRing0x64.sys`
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
mod error;
|
||||
mod msr_item;
|
||||
|
||||
use error::{MsrError, MsrResult};
|
||||
use msr_item::MsrItem;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod msr_linux;
|
||||
|
||||
/*
|
||||
#[cfg(target_os = "windows")]
|
||||
mod msr_win;
|
||||
*/
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use msr_linux::MsrImpl;
|
||||
|
||||
#[cfg(not(any(target_os = "linux")))]
|
||||
use unsupported::MsrImpl;
|
||||
|
||||
/// Global weak reference to the MSR singleton
|
||||
static INSTANCE: Lazy<Mutex<Weak<Msr>>> = Lazy::new(|| Mutex::new(Weak::new()));
|
||||
|
||||
/// MSR Interface
|
||||
pub struct Msr {
|
||||
inner: MsrImpl,
|
||||
}
|
||||
|
||||
impl Msr {
|
||||
/// Get or create the MSR singleton
|
||||
///
|
||||
/// Returns `None` if MSR is not available on this system.
|
||||
pub fn get(units: Vec<i32>) -> Option<Arc<Self>> {
|
||||
let mut instance = INSTANCE.lock();
|
||||
|
||||
// Try to upgrade the weak reference
|
||||
if let Some(msr) = instance.upgrade() {
|
||||
if msr.is_available() {
|
||||
return Some(msr);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new instance
|
||||
let msr = Arc::new(Self { inner: MsrImpl::new(units) });
|
||||
|
||||
if msr.is_available() {
|
||||
*instance = Arc::downgrade(&msr);
|
||||
Some(msr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new MSR instance without using the singleton.
|
||||
///
|
||||
/// Useful for testing or when multiple independent instances are needed.
|
||||
pub fn new_instance(units: Vec<i32>) -> Self {
|
||||
Self { inner: MsrImpl::new(units) }
|
||||
}
|
||||
|
||||
/// Check if MSR operations are available
|
||||
pub fn is_available(&self) -> bool {
|
||||
self.inner.is_available()
|
||||
}
|
||||
|
||||
/// Get the CPU units this MSR operates on
|
||||
pub fn units(&self) -> &[i32] {
|
||||
self.inner.units()
|
||||
}
|
||||
|
||||
/// Write an MsrItem to a CPU
|
||||
///
|
||||
/// * `item`: The MSR item to write
|
||||
/// * `cpu`: CPU index (-1 for default/first)
|
||||
/// * `verbose`: Log warnings on failure
|
||||
pub fn write_item(&self, item: &MsrItem, cpu: i32, verbose: bool) -> bool {
|
||||
self.inner.write_item(item, cpu, verbose)
|
||||
}
|
||||
|
||||
/// Write to an MSR register with optional mask
|
||||
///
|
||||
/// If a mask is provided, this performs a read-modify-write
|
||||
///
|
||||
/// * `reg`: MSR register address
|
||||
/// * `value`: Value to write
|
||||
/// * `cpu`: CPU index (-1 for default/first)
|
||||
/// * `mask`: Bit mask for partial updates (use NO_MASK for full write)
|
||||
/// * `verbose`: Log warnings on failure
|
||||
pub fn write(&self, reg: u32, value: u64, cpu: i32, mask: u64, verbose: bool) -> bool {
|
||||
self.inner.write(reg, value, cpu, mask, verbose)
|
||||
}
|
||||
|
||||
/// Execute a callback for each CPU unit
|
||||
///
|
||||
/// The callback receives the CPU ID and should return `true` to continue
|
||||
/// or `false` to abort.
|
||||
pub fn write_all<F>(&self, callback: F) -> bool
|
||||
where
|
||||
F: FnMut(i32) -> bool + Send,
|
||||
{
|
||||
self.inner.write_all(callback)
|
||||
}
|
||||
|
||||
/// Read an MSR register and return as MsrItem
|
||||
///
|
||||
/// * `reg`: MSR register address
|
||||
/// * `cpu`: CPU index (-1 for default/first)
|
||||
/// * `verbose`: Log warnings on failure
|
||||
pub fn read(&self, reg: u32, cpu: i32, verbose: bool) -> Option<MsrItem> {
|
||||
self.inner.read(reg, cpu, verbose)
|
||||
}
|
||||
|
||||
/// Low-level MSR read
|
||||
///
|
||||
/// Returns the raw value or error
|
||||
pub fn rdmsr(&self, reg: u32, cpu: i32) -> MsrResult<u64> {
|
||||
self.inner.rdmsr(reg, cpu)
|
||||
}
|
||||
|
||||
/// Low-level MSR write
|
||||
///
|
||||
/// Writes the value directly without masking
|
||||
pub fn wrmsr(&self, reg: u32, value: u64, cpu: i32) -> MsrResult<()> {
|
||||
self.inner.wrmsr(reg, value, cpu)
|
||||
}
|
||||
}
|
||||
|
||||
/// Impls for unsupported platforms.
|
||||
#[cfg(not(any(target_os = "linux")))]
|
||||
mod unsupported {
|
||||
use super::*;
|
||||
use tracing::warn;
|
||||
|
||||
pub struct MsrImpl {
|
||||
units: Vec<i32>,
|
||||
}
|
||||
|
||||
impl MsrImpl {
|
||||
pub fn new(units: Vec<i32>) -> Self {
|
||||
warn!("[msr] MSR not supported on this platform");
|
||||
Self { units }
|
||||
}
|
||||
|
||||
pub fn is_available(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn units(&self) -> &[i32] {
|
||||
&self.units
|
||||
}
|
||||
|
||||
pub fn write_all<F>(&self, _callback: F) -> bool
|
||||
where
|
||||
F: FnMut(i32) -> bool,
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
pub fn rdmsr(&self, reg: u32, cpu: i32) -> MsrResult<u64> {
|
||||
Err(crate::error::MsrError::PlatformNotSupported)
|
||||
}
|
||||
|
||||
pub fn wrmsr(&self, _reg: u32, _value: u64, _cpu: i32) -> MsrResult<()> {
|
||||
Err(crate::error::MsrError::PlatformNotSupported)
|
||||
}
|
||||
|
||||
pub fn write(&self, _reg: u32, _value: u64, _cpu: i32, _mask: u64, _verbose: bool) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn write_item(&self, _item: &MsrItem, _cpu: i32, _verbose: bool) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn read(&self, _reg: u32, _cpu: i32, _verbose: bool) -> Option<MsrItem> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
175
bin/minerd/src/hw/msr/msr_item.rs
Normal file
175
bin/minerd/src/hw/msr/msr_item.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2025 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
/// Sentinel value indicating no mask should be applied
|
||||
pub const NO_MASK: u64 = u64::MAX;
|
||||
|
||||
/// Represents a single MSR operation.
|
||||
///
|
||||
/// Contains the register address, value to write/read, and an optional
|
||||
/// mask for partial register updates.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct MsrItem {
|
||||
/// The MSR register address
|
||||
reg: u32,
|
||||
/// The value to read/write
|
||||
value: u64,
|
||||
/// Mask for partial updates (`NO_MASK` means full update)
|
||||
mask: u64,
|
||||
}
|
||||
|
||||
impl Default for MsrItem {
|
||||
fn default() -> Self {
|
||||
Self { reg: 0, value: 0, mask: NO_MASK }
|
||||
}
|
||||
}
|
||||
|
||||
impl MsrItem {
|
||||
/// Create a new MsrItem with optional mask
|
||||
pub const fn new(reg: u32, value: u64) -> Self {
|
||||
Self { reg, value, mask: NO_MASK }
|
||||
}
|
||||
|
||||
/// Create a new MsrItem with a specific mask
|
||||
pub const fn with_mask(reg: u32, value: u64, mask: u64) -> Self {
|
||||
Self { reg, value, mask }
|
||||
}
|
||||
|
||||
/// Check if this item is valid (register > 0)
|
||||
#[inline]
|
||||
pub const fn is_valid(&self) -> bool {
|
||||
self.reg > 0
|
||||
}
|
||||
|
||||
/// Get the register address
|
||||
#[inline]
|
||||
pub const fn reg(&self) -> u32 {
|
||||
self.reg
|
||||
}
|
||||
|
||||
/// Get the value
|
||||
#[inline]
|
||||
pub const fn value(&self) -> u64 {
|
||||
self.value
|
||||
}
|
||||
|
||||
/// Get the mask
|
||||
#[inline]
|
||||
pub const fn mask(&self) -> u64 {
|
||||
self.mask
|
||||
}
|
||||
|
||||
/// Check if this item has a mask
|
||||
#[inline]
|
||||
pub const fn has_mask(&self) -> bool {
|
||||
self.mask != NO_MASK
|
||||
}
|
||||
|
||||
/// Apply mask to combine old and new values.
|
||||
///
|
||||
/// The masked bits from `new_value` replace the corresponding bits in
|
||||
/// `old_value`, while unmasked bits retain the `old_value`.
|
||||
///
|
||||
/// ```text
|
||||
/// let old = 0xFF00_FF00;
|
||||
/// let new = 0x1234_5678;
|
||||
/// let mask = 0xFFFF_0000;
|
||||
///
|
||||
/// // Upper 16 bits from new, lower 16 bits from old:
|
||||
/// assert_eq!(MsrItem::masked_value(old, new, mask), 0x1234_FF00);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn masked_value(old_value: u64, new_value: u64, mask: u64) -> u64 {
|
||||
(new_value & mask) | (old_value & !mask)
|
||||
}
|
||||
|
||||
/// Set the value, useful for updating after a read
|
||||
#[inline]
|
||||
pub fn set_value(&mut self, value: u64) {
|
||||
self.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for parsing MsrItem from a string
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ParseMsrItemError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseMsrItemError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Failed to parse MsrItem: {}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseMsrItemError {}
|
||||
|
||||
impl FromStr for MsrItem {
|
||||
type Err = ParseMsrItemError;
|
||||
|
||||
/// Parse an MsrItem from a string in format `REG:VALUE` or `REG:VALUE:MASK`
|
||||
///
|
||||
/// Values can be decimal or hexadecimal.
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let parts: Vec<&str> = s.split(':').collect();
|
||||
|
||||
if parts.len() < 2 {
|
||||
return Err(ParseMsrItemError {
|
||||
message: "Expected format REG:VALUE or REG:VALUE:MASK".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
let reg = parse_number(parts[0])
|
||||
.map_err(|e| ParseMsrItemError { message: format!("Invalid register: {}", e) })?
|
||||
as u32;
|
||||
|
||||
let value = parse_number(parts[1])
|
||||
.map_err(|e| ParseMsrItemError { message: format!("Invalid value: {}", e) })?;
|
||||
|
||||
let mask = if parts.len() > 2 {
|
||||
parse_number(parts[2])
|
||||
.map_err(|e| ParseMsrItemError { message: format!("Invalid mask: {}", e) })?
|
||||
} else {
|
||||
NO_MASK
|
||||
};
|
||||
|
||||
Ok(Self { reg, value, mask })
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a number from string, supporting decimal and hexadecimal
|
||||
fn parse_number(s: &str) -> Result<u64, std::num::ParseIntError> {
|
||||
let s = s.trim();
|
||||
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
|
||||
u64::from_str_radix(hex, 16)
|
||||
} else {
|
||||
s.parse::<u64>()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MsrItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.mask != NO_MASK {
|
||||
write!(f, "0x{:x}:0x{:x}:0x{:x}", self.reg, self.value, self.mask)
|
||||
} else {
|
||||
write!(f, "0x{:x}:0x{:x}", self.reg, self.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
197
bin/minerd/src/hw/msr/msr_linux.rs
Normal file
197
bin/minerd/src/hw/msr/msr_linux.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2025 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Linux-specific MSR implementation using `/dev/cpu/*/msr`
|
||||
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::{self, Write},
|
||||
os::fd::AsRawFd,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
use super::{msr_item::NO_MASK, MsrError, MsrItem, MsrResult};
|
||||
|
||||
pub struct MsrImpl {
|
||||
available: bool,
|
||||
units: Vec<i32>,
|
||||
}
|
||||
|
||||
impl MsrImpl {
|
||||
/// Create a new Linux MSR interface
|
||||
pub fn new(units: Vec<i32>) -> Self {
|
||||
let available = Self::msr_allow_writes() || Self::msr_modprobe();
|
||||
|
||||
if !available {
|
||||
warn!("[msr] MSR kernel module not available");
|
||||
}
|
||||
|
||||
Self { available, units }
|
||||
}
|
||||
|
||||
/// Check if MSR operations are available
|
||||
pub fn is_available(&self) -> bool {
|
||||
self.available
|
||||
}
|
||||
|
||||
/// Get the CPU units
|
||||
pub fn units(&self) -> &[i32] {
|
||||
&self.units
|
||||
}
|
||||
|
||||
/// Execute a callback for each CPU unit
|
||||
pub fn write_all<F>(&self, mut callback: F) -> bool
|
||||
where
|
||||
F: FnMut(i32) -> bool,
|
||||
{
|
||||
for &cpu in &self.units {
|
||||
if !callback(cpu) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Read an MSR register
|
||||
pub fn rdmsr(&self, reg: u32, cpu: i32) -> MsrResult<u64> {
|
||||
let cpu_id = self.resolve_cpu(cpu)?;
|
||||
let path = format!("/dev/cpu/{}/msr", cpu_id);
|
||||
|
||||
let file =
|
||||
File::open(&path).map_err(|e| MsrError::ReadError { reg, cpu: cpu_id, source: e })?;
|
||||
|
||||
let mut value = 0u64;
|
||||
let bytes_read = unsafe {
|
||||
libc::pread(
|
||||
file.as_raw_fd(),
|
||||
&mut value as *mut u64 as *mut libc::c_void,
|
||||
std::mem::size_of::<u64>(),
|
||||
reg as libc::off_t,
|
||||
)
|
||||
};
|
||||
|
||||
if bytes_read == std::mem::size_of::<u64>() as isize {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(MsrError::ReadError { reg, cpu: cpu_id, source: io::Error::last_os_error() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Write an MSR register
|
||||
pub fn wrmsr(&self, reg: u32, value: u64, cpu: i32) -> MsrResult<()> {
|
||||
let cpu_id = self.resolve_cpu(cpu)?;
|
||||
let path = format!("/dev/cpu/{}/msr", cpu_id);
|
||||
|
||||
let file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(&path)
|
||||
.map_err(|e| MsrError::WriteError { reg, cpu: cpu_id, source: e })?;
|
||||
|
||||
let bytes_written = unsafe {
|
||||
libc::pwrite(
|
||||
file.as_raw_fd(),
|
||||
&value as *const u64 as *const libc::c_void,
|
||||
std::mem::size_of::<u64>(),
|
||||
reg as libc::off_t,
|
||||
)
|
||||
};
|
||||
|
||||
if bytes_written == std::mem::size_of::<u64>() as isize {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MsrError::WriteError { reg, cpu: cpu_id, source: io::Error::last_os_error() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Write MSR with mask support (read-modify-write)
|
||||
pub fn write(&self, reg: u32, value: u64, cpu: i32, mask: u64, verbose: bool) -> bool {
|
||||
let write_value = if mask != NO_MASK {
|
||||
match self.rdmsr(reg, cpu) {
|
||||
Ok(old_value) => MsrItem::masked_value(old_value, value, mask),
|
||||
Err(e) => {
|
||||
if verbose {
|
||||
warn!("[msr] Cannot read MSR 0x{:08x}: {}", reg, e);
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
||||
match self.wrmsr(reg, write_value, cpu) {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
if verbose {
|
||||
warn!("[msr] Cannot set MSR 0x{:08x} to 0x{:016x}: {}", reg, write_value, e);
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write an MsrItem
|
||||
pub fn write_item(&self, item: &MsrItem, cpu: i32, verbose: bool) -> bool {
|
||||
self.write(item.reg(), item.value(), cpu, item.mask(), verbose)
|
||||
}
|
||||
|
||||
/// Read MSR and return as MsrItem
|
||||
pub fn read(&self, reg: u32, cpu: i32, verbose: bool) -> Option<MsrItem> {
|
||||
match self.rdmsr(reg, cpu) {
|
||||
Ok(value) => Some(MsrItem::new(reg, value)),
|
||||
Err(e) => {
|
||||
if verbose {
|
||||
warn!("[msr] Cannot read MSR 0x{:08x}: {}", reg, e);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve CPU index (-1 means use first available)
|
||||
fn resolve_cpu(&self, cpu: i32) -> MsrResult<i32> {
|
||||
if cpu < 0 {
|
||||
self.units.first().copied().ok_or(MsrError::NoCpuUnits)
|
||||
} else {
|
||||
Ok(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to enable MSR writes via sysfs
|
||||
fn msr_allow_writes() -> bool {
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open("/sys/module/msr/parameters/allow_writes")
|
||||
.and_then(|mut file| file.write_all(b"on"))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Try to load MSR module via modprobe
|
||||
fn msr_modprobe() -> bool {
|
||||
Command::new("/sbin/modprobe")
|
||||
.args(["msr", "allow_writes=on"])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.status()
|
||||
.map(|status| status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,9 @@ use darkfi_sdk::crypto::keypair::{Address, Keypair, Network, StandardAddress};
|
||||
/// Miner benchmarking related methods
|
||||
pub mod benchmark;
|
||||
|
||||
/// Hardware interfaces
|
||||
pub mod hw;
|
||||
|
||||
/// darkfid JSON-RPC related methods
|
||||
mod rpc;
|
||||
use rpc::{polling_task, DarkfidRpcClient};
|
||||
|
||||
Reference in New Issue
Block a user