Ver Fonte

minerd: Add Linux MSR operations impl

x há 7 meses atrás
pai
commit
f2363a806b

+ 5 - 2
Cargo.lock

@@ -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",

+ 4 - 0
bin/minerd/Cargo.toml

@@ -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 - 0
bin/minerd/src/hw/mod.rs

@@ -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 - 0
bin/minerd/src/hw/msr/error.rs

@@ -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 - 0
bin/minerd/src/hw/msr/mod.rs

@@ -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 - 0
bin/minerd/src/hw/msr/msr_item.rs

@@ -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 - 0
bin/minerd/src/hw/msr/msr_linux.rs

@@ -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)
+    }
+}

+ 3 - 0
bin/minerd/src/lib.rs

@@ -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};