| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use anyhow::{anyhow, Result}; |
| use regex::Regex; |
| use std::{collections::HashMap, path::PathBuf, time::Duration}; |
| |
| pub type SteamAppId = u64; |
| |
| #[cfg(not(test))] |
| pub const UNMOUNTER_INTERVAL: Duration = Duration::from_millis(1000); |
| #[cfg(test)] |
| pub const UNMOUNTER_INTERVAL: Duration = Duration::from_millis(10); |
| |
| pub const DLC_HANDLER_INTERVAL: Duration = Duration::from_millis(1000); |
| pub const MAX_CONCURRENT_DLC_INSTALLS: usize = 1; |
| // Limit the number of DLC installation queue to: |
| // - Prevent 'hogging' DlcService. DlcService does not support parallel |
| // downloads or queues yet. |
| // - Prevent the user from increasing shadercached memory footprint infinitely. |
| // VM user can queue shader cache DLC installations by design. |
| pub const MAX_INSTALL_QUEUE_SIZE: usize = 5; |
| |
| pub const PRECOMPILED_CACHE_DIR: &str = "precompiled_cache"; |
| |
| // GPU device id reported by the pcie ID |
| #[cfg(test)] |
| lazy_static! { |
| pub static ref IMAGE_LOADER: PathBuf = tempfile::tempdir().unwrap().into_path(); |
| pub static ref CRYPTO_HOME: PathBuf = tempfile::tempdir().unwrap().into_path(); |
| pub static ref GPU_DEVICE_ID: u16 = 0x9a40; |
| } |
| |
| #[cfg(not(test))] |
| lazy_static! { |
| pub static ref IMAGE_LOADER: PathBuf = std::path::Path::new("/run/imageloader").to_path_buf(); |
| pub static ref CRYPTO_HOME: PathBuf = |
| std::path::Path::new("/run/daemon-store/shadercached").to_path_buf(); |
| pub static ref GPU_DEVICE_ID: u16 = get_gpu_device_id().unwrap_or(0); |
| } |
| |
| lazy_static! { |
| pub static ref GPU_DEVICE_DLC_VARIANT: &'static str = { |
| // These suffixes are non-technical names to create buckets for each |
| // device id variants per board. |
| const DLC_VARIANT_AXE: &str = "-axe"; |
| const DLC_VARIANT_BATRIDER: &str = "-batrider"; |
| const DLC_VARIANT_CLINKZ: &str = "-clinkz"; |
| const DLC_VARIANT_DAZZLE: &str = "-dazzle"; |
| const DLC_VARIANT_ENIGMA: &str = "-enigma"; |
| const DLC_VARIANT_GRIMSTROKE: &str = "-grimstroke"; |
| |
| let variant_mapping: HashMap<u16, &str> = HashMap::from([ |
| // axe variant |
| (u16::from_str_radix("9a49", 16).unwrap(), DLC_VARIANT_AXE), // volteer |
| (u16::from_str_radix("46a6", 16).unwrap(), DLC_VARIANT_AXE), // brya |
| (u16::from_str_radix("46d0", 16).unwrap(), DLC_VARIANT_AXE), // nissa |
| (u16::from_str_radix("9b41", 16).unwrap(), DLC_VARIANT_AXE), // hatch |
| (u16::from_str_radix("7d45", 16).unwrap(), DLC_VARIANT_AXE), // rex |
| |
| // batrider variant |
| (u16::from_str_radix("9a40", 16).unwrap(), DLC_VARIANT_BATRIDER), // volteer |
| (u16::from_str_radix("46b3", 16).unwrap(), DLC_VARIANT_BATRIDER), // brya |
| (u16::from_str_radix("9bcc", 16).unwrap(), DLC_VARIANT_BATRIDER), // hatch |
| |
| // clinkz variant |
| (u16::from_str_radix("9a78", 16).unwrap(), DLC_VARIANT_CLINKZ), // volteer |
| (u16::from_str_radix("a7a0", 16).unwrap(), DLC_VARIANT_CLINKZ), // brya |
| (u16::from_str_radix("9bca", 16).unwrap(), DLC_VARIANT_CLINKZ), // hatch |
| |
| // dazzle variant |
| (u16::from_str_radix("a7a9", 16).unwrap(), DLC_VARIANT_DAZZLE), // brya |
| (u16::from_str_radix("9bac", 16).unwrap(), DLC_VARIANT_DAZZLE), // hatch |
| |
| // enigma variant |
| (u16::from_str_radix("a7a1", 16).unwrap(), DLC_VARIANT_ENIGMA), // brya |
| (u16::from_str_radix("9b21", 16).unwrap(), DLC_VARIANT_ENIGMA), // hatch |
| |
| // grimstroke variant |
| (u16::from_str_radix("46a8", 16).unwrap(), DLC_VARIANT_GRIMSTROKE), // brya |
| ]); |
| |
| // If no device id is detected or not found in |variant_mapping|, |
| // shadercached should attempt to install axe variant. |
| variant_mapping.get(&*GPU_DEVICE_ID).unwrap_or(&DLC_VARIANT_AXE) |
| }; |
| |
| pub static ref BOOT_ID: String = { |
| const BOOT_ID_FILE: &str = "/proc/sys/kernel/random/boot_id"; |
| let contents = std::fs::read(BOOT_ID_FILE).expect( |
| "Expected to be able to read the boot id file"); |
| hex::encode(openssl::sha::sha256(&contents)) |
| }; |
| |
| pub static ref OS_BUILD_ID: String = { |
| if let Ok(os_release) = sys_info::linux_os_release() { |
| if let Some(build_id) = os_release.build_id { |
| return hex::encode(openssl::sha::sha256(build_id.as_bytes())) |
| } |
| } |
| |
| // Fall back to boot id |
| BOOT_ID.to_string() |
| }; |
| } |
| |
| pub fn steam_app_id_to_dlc(steam_app_id: SteamAppId) -> String { |
| format!( |
| "borealis-shader-cache-{}-dlc{}", |
| steam_app_id, *GPU_DEVICE_DLC_VARIANT |
| ) |
| } |
| |
| pub fn dlc_to_steam_app_id(dlc_name: &str) -> Result<SteamAppId> { |
| lazy_static! { |
| static ref RE: Regex = Regex::new(r"borealis-shader-cache-([0-9]+)-dlc(-.+)?").unwrap(); |
| } |
| if let Some(capture) = RE.captures(dlc_name) { |
| if let Some(steam_app_id_match) = capture.get(1) { |
| return steam_app_id_match |
| .as_str() |
| .parse::<SteamAppId>() |
| .map_err(|e| anyhow!(e)); |
| } |
| } |
| Err(anyhow!("Not a valid DLC")) |
| } |
| |
| #[cfg(not(test))] |
| fn get_gpu_device_id() -> Result<u16> { |
| use log::warn; |
| use std::process::{Command, Stdio}; |
| // This function is called only once to initialize pub lazy static constant |
| // GPU_DEVICE_ID, so we don't need to make the Regex object static. |
| let regex = Regex::new(r"\[([0-9a-f]{4})\]").unwrap(); |
| let output = Command::new("lspci") |
| .args(["-nn", "-d", "::0300", "-mm"]) // -d ::0300 returns only VGA device |
| .stdout(Stdio::piped()) |
| .output()?; |
| let vga_pcie_info = String::from_utf8(output.stdout)?; |
| |
| // Match the regex pattern for all occurrences |
| let mut all_captures = regex.captures_iter(&vga_pcie_info); |
| // Get the 3rd match, which has the GPU PCIE device ID |
| if let Some(capture) = all_captures.nth(2) { |
| // For the capture (ex. [abcd]), get the first inner match |
| if let Some(device_id_match) = capture.get(1) { |
| if let Ok(id) = u16::from_str_radix(device_id_match.as_str(), 16) { |
| return Ok(id); |
| } else { |
| warn!("Failed to parse device ID: {}", device_id_match.as_str()); |
| } |
| } else { |
| warn!("Unable to extract PCI device ID, {}", vga_pcie_info); |
| } |
| } else { |
| warn!("Unexpected VGA PCI information, {}", vga_pcie_info); |
| } |
| |
| Err(anyhow!("Unable to determine PCI device ID!")) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| #[test] |
| fn test_steam_app_id_to_dlc() { |
| assert_eq!( |
| super::steam_app_id_to_dlc(32), |
| "borealis-shader-cache-32-dlc-batrider" |
| ); |
| assert_eq!( |
| super::steam_app_id_to_dlc(123), |
| "borealis-shader-cache-123-dlc-batrider" |
| ); |
| assert_eq!( |
| super::steam_app_id_to_dlc(0000), |
| "borealis-shader-cache-0-dlc-batrider" |
| ); |
| } |
| |
| #[test] |
| fn test_dlc_to_steam_app_id() { |
| assert_eq!( |
| super::dlc_to_steam_app_id("borealis-shader-cache-32-dlc-axe").unwrap(), |
| 32 |
| ); |
| assert_eq!( |
| super::dlc_to_steam_app_id("borealis-shader-cache-000-dlc-batrider").unwrap(), |
| 0 |
| ); |
| assert_eq!( |
| super::dlc_to_steam_app_id("borealis-shader-cache-000-dlc").unwrap(), |
| 0 |
| ); |
| assert!(super::dlc_to_steam_app_id("borealis-shader-cache-213").is_err()); |
| assert!(super::dlc_to_steam_app_id("213-dlc").is_err()); |
| assert!(super::dlc_to_steam_app_id("not-a-valid-one").is_err()); |
| assert!(super::dlc_to_steam_app_id("borealis-dlc").is_err()); |
| assert!(super::dlc_to_steam_app_id("borealis-shader-cache-two-dlc").is_err()); |
| } |
| } |