blob: e384317c56479c7866ff503098abb54bd8e682cc [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_component::client::connect_to_protocol_sync;
use starnix_core::task::CurrentTask;
use starnix_core::vfs::FsNodeOps;
use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
use starnix_logging::log_error;
use starnix_uapi::errors::Errno;
use starnix_uapi::user_address::ArchSpecific as _;
use std::sync::LazyLock;
#[derive(Clone)]
pub struct CpuinfoFile {}
impl CpuinfoFile {
pub fn new_node() -> impl FsNodeOps {
DynamicFile::new_node(Self {})
}
}
impl DynamicFileSource for CpuinfoFile {
fn generate(&self, current_task: &CurrentTask, sink: &mut DynamicFileBuf) -> Result<(), Errno> {
let is_qemu = SYSINFO.is_qemu();
for i in 0..zx::system_get_num_cpus() {
writeln!(sink, "processor\t: {}", i)?;
// Report emulated CPU as "QEMU Virtual CPU". Some LTP tests rely on this to detect
// that they running in a VM.
if is_qemu {
writeln!(sink, "model name\t: QEMU Virtual CPU")?;
}
if current_task.is_arch32() {
#[cfg(target_arch = "aarch64")]
{
arm32_write_features(sink, current_task.kernel().hwcaps.arch32)?;
}
#[cfg(not(target_arch = "aarch64"))]
{
unreachable!("32-bit programs are only supported on ARM.")
}
}
writeln!(sink)?;
}
Ok(())
}
}
#[cfg(target_arch = "aarch64")]
fn arm32_write_features(
sink: &mut DynamicFileBuf,
hwcap: starnix_core::task::HwCap,
) -> Result<(), Errno> {
write!(sink, "Features\t:")?;
const HWCAP_STRINGS: [&str; 28] = [
"swp",
"half",
"thumb",
"26bit",
"fastmult",
"fpa",
"vfp",
"edsp",
"java",
"iwmmxt",
"crunch",
"thumbee",
"neon",
"vfpv3",
"vfpv3d16",
"tls",
"vfpv4",
"idiva",
"idivt",
"vfpd32",
"lpae",
"evtstrm",
"fphp",
"asimdhp",
"asimddp",
"asimdfhm",
"asimdbf16",
"i8mm",
];
const HWCAP2_STRINGS: [&str; 7] = ["aes", "pmull", "sha1", "sha2", "crc32", "sb", "ssbs"];
for i in 0..HWCAP_STRINGS.len() {
if hwcap.hwcap & (1 << i) != 0 {
write!(sink, " {}", HWCAP_STRINGS[i])?;
}
}
for i in 0..HWCAP2_STRINGS.len() {
if hwcap.hwcap2 & (1 << i) != 0 {
write!(sink, " {}", HWCAP2_STRINGS[i])?;
}
}
writeln!(sink)?;
Ok(())
}
struct SysInfo {
board_name: String,
}
impl SysInfo {
fn is_qemu(&self) -> bool {
matches!(
self.board_name.as_str(),
"Standard PC (Q35 + ICH9, 2009)" | "qemu-arm64" | "qemu-riscv64"
)
}
fn fetch() -> Result<SysInfo, anyhow::Error> {
let sysinfo = connect_to_protocol_sync::<fidl_fuchsia_sysinfo::SysInfoMarker>()?;
let board_name = match sysinfo.get_board_name(zx::MonotonicInstant::INFINITE)? {
(zx::sys::ZX_OK, Some(name)) => name,
(_, _) => "Unknown".to_string(),
};
Ok(SysInfo { board_name })
}
}
const SYSINFO: LazyLock<SysInfo> = LazyLock::new(|| {
SysInfo::fetch().unwrap_or_else(|e| {
log_error!("Failed to fetch sysinfo: {e}");
SysInfo { board_name: "Unknown".to_string() }
})
});