blob: c5eccdc71a7eb15f84072ce3d487b8fb308a3dbf [file] [log] [blame] [edit]
// 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());
}
}