Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/cardwire-core/src/gpu/ebpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ impl GpuBlocker {
self.inner.set_vulkan_block(block).map_err(map_gpu_error)?;
Ok(())
}
pub fn set_file_block(&mut self, file: &str) -> GpuResult<()> {
self.inner.set_file_block(file).map_err(map_gpu_error)?;
Ok(())
}
pub fn set_nvidia_file_block(&mut self, file: &str) -> GpuResult<()> {
self.inner
.set_nvidia_file_block(file)
.map_err(map_gpu_error)?;
Ok(())
}
}

pub fn is_gpu_blocked(blocker: &GpuBlocker, gpu: &Gpu) -> GpuResult<bool> {
Expand Down
27 changes: 27 additions & 0 deletions crates/cardwire-daemon/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ use std::{collections::BTreeMap, fmt};
use tokio::sync::RwLock;
use zbus::fdo::Error;

const BLOCKED_PCI_FILES: &[&str] = &[
"config",
"current_link_speed",
"max_link_speed",
"max_link_width",
"current_link_width",
];
// Files that get blocked when the vulkan block is on
const BLOCKED_NVIDIA_FILES: &[&str] = &[
"libGLX_nvidia.so.0",
"nvidia_icd.json",
"nvidia_icd.x86_64.json",
];

#[derive(Deserialize, Serialize, PartialEq, zbus::zvariant::Type, Clone, Copy, Default)]
pub enum Modes {
Integrated,
Expand Down Expand Up @@ -105,6 +119,19 @@ impl Daemon {
let mut blocker = self.state.ebpf_blocker.write().await;
// Apply vulkan block
blocker.set_vulkan_block(config.block_nvidia_vulkan())?;

// Apply file blocks
for file in BLOCKED_PCI_FILES {
blocker.set_file_block(file)?;
}
for gpu in self.state.gpu_list.values() {
if gpu.is_nvidia() {
for file in BLOCKED_NVIDIA_FILES {
blocker.set_nvidia_file_block(file)?;
}
break;
}
}
// Dropping the locks prevent set_mode being stuck
drop(blocker);
drop(config);
Expand Down
29 changes: 25 additions & 4 deletions crates/cardwire-ebpf/src/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,27 @@ struct {
__type(value, __u8);
} BLOCKED_PCI SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, char[30]);
__type(value, __u8);
} BLOCKED_PCI_FILES SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 64);
__type(key, __u32);
__type(value, __u8);
} SETTINGS SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, char[30]);
__type(value, __u8);
} BLOCKED_NVIDIA_FILES SEC(".maps");

/* Safely read and compare kernel qstr */
static __always_inline int qstr_eq(struct qstr q, const char *name, __u32 len)
{
Expand Down Expand Up @@ -176,20 +190,27 @@ static __always_inline int is_blocked_device(struct dentry *d)
}
}
struct qstr q = BPF_CORE_READ(d, d_name);
// ignore long files
if (!q.name || q.len > 30) {
return 0;
}
char buf[32] = {};
if (bpf_core_read_str(buf, sizeof(buf), q.name) < 0) {
return 0;
}
// Blocks vulkan nvidia_icd, it's dangerous and will only work if one nvidia gpu is blocked
__u32 block_vulkan_key = 0;
if (bpf_map_lookup_elem(&SETTINGS, &block_vulkan_key)) {
if (qstr_eq(q, "nvidia_icd.json", 15) ||
qstr_eq(q, "nvidia_icd.x86_64.json", 22)) {
if (bpf_map_lookup_elem(&BLOCKED_PCI_FILES, buf)) {
__u32 id0 = 0, id1 = 1;
if (bpf_map_lookup_elem(&BLOCKED_NVIDIAID, &id0) &&
!bpf_map_lookup_elem(&BLOCKED_NVIDIAID, &id1)) {
return -ENOENT;
}
}
}

if (qstr_eq(q, "config", 6)) {
// PCI Part
if (bpf_map_lookup_elem(&BLOCKED_PCI_FILES, buf)) {
char pci_addr[16] = {};
if (get_pci_addr(d, pci_addr, sizeof(pci_addr)) != 0) {
return 0;
Expand Down
36 changes: 36 additions & 0 deletions crates/cardwire-ebpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,26 @@ impl EbpfBlocker {
Ok(Self { ebpf })
}

/// turn a pci string into a u8 array with a fixed 16 size
fn pci_key(pci: &str) -> [u8; 16] {
let mut key = [0u8; 16];
let bytes = pci.as_bytes();
// leave one byte for terminator
let len = bytes.len().min(15);
key[..len].copy_from_slice(&bytes[..len]);
key[len] = 0;
key
}
/// turn a file string into a u8 array with a fixed 30 size
fn file_key(file: &str) -> [u8; 30] {
let mut key = [0u8; 30];
let bytes = file.as_bytes();
// leave one byte for terminator
let len = bytes.len().min(29);
key[..len].copy_from_slice(&bytes[..len]);
key[len] = 0;
key
}

fn missing_entity(kind: &str, name: &str) -> CardwireEbpfError {
CardwireEbpfError::missing_entity(kind, name)
Expand Down Expand Up @@ -258,4 +270,28 @@ impl EbpfBlocker {
}
Ok(())
}

pub fn set_nvidia_file_block(&mut self, file: &str) -> CardwireEbpfResult<()> {
let mut map: HashMap<_, [u8; 30], u8> = HashMap::try_from(
self.ebpf
.map_mut("BLOCKED_NVIDIA_FILES")
.ok_or_else(|| Self::missing_entity("map", "BLOCKED_PCI"))?,
)
.map_err(CardwireEbpfError::aya)?;
let key = Self::file_key(file);
map.insert(key, 1, 0).map_err(CardwireEbpfError::aya)?;
Ok(())
}

pub fn set_file_block(&mut self, file: &str) -> CardwireEbpfResult<()> {
let mut map: HashMap<_, [u8; 30], u8> = HashMap::try_from(
self.ebpf
.map_mut("BLOCKED_PCI_FILES")
.ok_or_else(|| Self::missing_entity("map", "BLOCKED_PCI"))?,
)
.map_err(CardwireEbpfError::aya)?;
let key = Self::file_key(file);
map.insert(key, 1, 0).map_err(CardwireEbpfError::aya)?;
Ok(())
}
}