diff --git a/crates/cardwire-core/src/gpu/ebpf.rs b/crates/cardwire-core/src/gpu/ebpf.rs index 6da1a75..a196879 100644 --- a/crates/cardwire-core/src/gpu/ebpf.rs +++ b/crates/cardwire-core/src/gpu/ebpf.rs @@ -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 { diff --git a/crates/cardwire-daemon/src/models.rs b/crates/cardwire-daemon/src/models.rs index 3c231d4..79e4b4f 100644 --- a/crates/cardwire-daemon/src/models.rs +++ b/crates/cardwire-daemon/src/models.rs @@ -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, @@ -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); diff --git a/crates/cardwire-ebpf/src/bpf.c b/crates/cardwire-ebpf/src/bpf.c index 671405b..b891117 100644 --- a/crates/cardwire-ebpf/src/bpf.c +++ b/crates/cardwire-ebpf/src/bpf.c @@ -82,6 +82,13 @@ 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); @@ -89,6 +96,13 @@ struct { __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) { @@ -176,11 +190,18 @@ 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)) { @@ -188,8 +209,8 @@ static __always_inline int is_blocked_device(struct dentry *d) } } } - - 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; diff --git a/crates/cardwire-ebpf/src/lib.rs b/crates/cardwire-ebpf/src/lib.rs index 4585d69..46a43ee 100644 --- a/crates/cardwire-ebpf/src/lib.rs +++ b/crates/cardwire-ebpf/src/lib.rs @@ -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) @@ -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(()) + } }