mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 22:57:59 -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]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.177"
|
version = "0.2.178"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@@ -4318,7 +4318,10 @@ dependencies = [
|
|||||||
"darkfi-sdk",
|
"darkfi-sdk",
|
||||||
"darkfi-serial",
|
"darkfi-serial",
|
||||||
"easy-parallel",
|
"easy-parallel",
|
||||||
|
"libc",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot 0.12.5",
|
||||||
"randomx",
|
"randomx",
|
||||||
"serde",
|
"serde",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ license = "AGPL-3.0-only"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
libc = "0.2.178"
|
||||||
|
parking_lot = "0.12.5"
|
||||||
|
once_cell = "1.21.3"
|
||||||
|
|
||||||
# Darkfi
|
# Darkfi
|
||||||
darkfi = {path = "../../", features = ["async-daemonize", "validator", "rpc", "bs58"]}
|
darkfi = {path = "../../", features = ["async-daemonize", "validator", "rpc", "bs58"]}
|
||||||
darkfi-sdk = {path = "../../src/sdk"}
|
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
|
/// Miner benchmarking related methods
|
||||||
pub mod benchmark;
|
pub mod benchmark;
|
||||||
|
|
||||||
|
/// Hardware interfaces
|
||||||
|
pub mod hw;
|
||||||
|
|
||||||
/// darkfid JSON-RPC related methods
|
/// darkfid JSON-RPC related methods
|
||||||
mod rpc;
|
mod rpc;
|
||||||
use rpc::{polling_task, DarkfidRpcClient};
|
use rpc::{polling_task, DarkfidRpcClient};
|
||||||
|
|||||||
Reference in New Issue
Block a user