diff --git a/arch/src/aarch64/fdt.rs b/arch/src/aarch64/fdt.rs index a4269526e..7845e7010 100644 --- a/arch/src/aarch64/fdt.rs +++ b/arch/src/aarch64/fdt.rs @@ -24,6 +24,8 @@ use super::layout::{ IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START, PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, }; +use std::fs; +use std::path::Path; use vm_fdt::{FdtWriter, FdtWriterResult}; use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion}; @@ -40,8 +42,12 @@ const VIRTIO_IOMMU_PHANDLE: u32 = 5; // NOTE: Keep FIRST_VCPU_PHANDLE the last PHANDLE defined. // This is a value for uniquely identifying the FDT node containing the first vCPU. // The last number of vCPU phandle depends on the number of vCPUs. -const FIRST_VCPU_PHANDLE: u32 = 6; +const FIRST_VCPU_PHANDLE: u32 = 8; +// This is a value for uniquely identifying the FDT node containing the L2 cache info +const L2_CACHE_PHANDLE: u32 = 6; +// This is a value for uniquely identifying the FDT node containing the L3 cache info +const L3_CACHE_PHANDLE: u32 = 7; // Read the documentation specified when appending the root node to the FDT. const ADDRESS_CELLS: u32 = 0x2; const SIZE_CELLS: u32 = 0x2; @@ -81,6 +87,97 @@ pub enum Error { } type Result = result::Result; +pub enum CacheLevel { + /// L1 data cache + L1D = 0, + /// L1 instruction cache + L1I = 1, + /// L2 cache + L2 = 2, + /// L3 cache + L3 = 3, +} + +/// NOTE: CACHE SIZE file directory example, +/// "/sys/devices/system/cpu/cpu0/cache/index0/size". +pub fn get_cache_size(cache_level: CacheLevel) -> u32 { + let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); + match cache_level { + CacheLevel::L1D => file_directory += "/index0/size", + CacheLevel::L1I => file_directory += "/index1/size", + CacheLevel::L2 => file_directory += "/index2/size", + CacheLevel::L3 => file_directory += "/index3/size", + } + + let file_path = Path::new(&file_directory); + if !file_path.exists() { + error!("File: {} not exist.", file_directory); + 0 + } else { + info!("File: {} exist.", file_directory); + + let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); + // The content of the file is as simple as a size, like: "32K" + let src = src.trim(); + let src_digits: u32 = src[0..src.len() - 1].parse().unwrap(); + let src_unit = &src[src.len() - 1..]; + + src_digits + * match src_unit { + "K" => 1024, + "M" => 1024u32.pow(2), + "G" => 1024u32.pow(3), + _ => 1, + } + } +} + +/// NOTE: CACHE COHERENCY LINE SIZE file directory example, +/// "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size". +pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 { + let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); + match cache_level { + CacheLevel::L1D => file_directory += "/index0/coherency_line_size", + CacheLevel::L1I => file_directory += "/index1/coherency_line_size", + CacheLevel::L2 => file_directory += "/index2/coherency_line_size", + CacheLevel::L3 => file_directory += "/index3/coherency_line_size", + } + + let file_path = Path::new(&file_directory); + if !file_path.exists() { + error!("File: {} not exist.", file_directory); + 0 + } else { + info!("File: {} exist.", file_directory); + + let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); + src.trim().parse::().unwrap() + } +} + +/// NOTE: CACHE NUMBER OF SETS file directory example, +/// "/sys/devices/system/cpu/cpu0/cache/index0/number_of_sets". +pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 { + let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string(); + match cache_level { + CacheLevel::L1D => file_directory += "/index0/number_of_sets", + CacheLevel::L1I => file_directory += "/index1/number_of_sets", + CacheLevel::L2 => file_directory += "/index2/number_of_sets", + CacheLevel::L3 => file_directory += "/index3/number_of_sets", + } + + let file_path = Path::new(&file_directory); + if !file_path.exists() { + error!("File: {} not exist.", file_directory); + 0 + } else { + info!("File: {} exist.", file_directory); + + let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted."); + src.trim().parse::().unwrap() + } +} + /// Creates the flattened device tree for this aarch64 VM. #[allow(clippy::too_many_arguments)] pub fn create_fdt( @@ -159,6 +256,54 @@ fn create_cpu_nodes( let num_cpus = vcpu_mpidr.len(); + // Add cache info. + // L1 Data Cache Info. + let mut l1_d_cache_size: u32 = 0; + let mut l1_d_cache_line_size: u32 = 0; + let mut l1_d_cache_sets: u32 = 0; + + // L1 Instruction Cache Info. + let mut l1_i_cache_size: u32 = 0; + let mut l1_i_cache_line_size: u32 = 0; + let mut l1_i_cache_sets: u32 = 0; + + // L2 Cache Info. + let mut l2_cache_size: u32 = 0; + let mut l2_cache_line_size: u32 = 0; + let mut l2_cache_sets: u32 = 0; + + // L3 Cache Info. + let mut l3_cache_size: u32 = 0; + let mut l3_cache_line_size: u32 = 0; + let mut l3_cache_sets: u32 = 0; + + let cache_path = Path::new("/sys/devices/system/cpu/cpu0/cache"); + let cache_exist: bool = cache_path.exists(); + if !cache_exist { + error!("cache sysfs system does not exist."); + } else { + info!("cache sysfs system exists."); + // L1 Data Cache Info. + l1_d_cache_size = get_cache_size(CacheLevel::L1D); + l1_d_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1D); + l1_d_cache_sets = get_cache_number_of_sets(CacheLevel::L1D); + + // L1 Instruction Cache Info. + l1_i_cache_size = get_cache_size(CacheLevel::L1I); + l1_i_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1I); + l1_i_cache_sets = get_cache_number_of_sets(CacheLevel::L1I); + + // L2 Cache Info. + l2_cache_size = get_cache_size(CacheLevel::L2); + l2_cache_line_size = get_cache_coherency_line_size(CacheLevel::L2); + l2_cache_sets = get_cache_number_of_sets(CacheLevel::L2); + + // L3 Cache Info. + l3_cache_size = get_cache_size(CacheLevel::L3); + l3_cache_line_size = get_cache_coherency_line_size(CacheLevel::L3); + l3_cache_sets = get_cache_number_of_sets(CacheLevel::L3); + } + for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) { let cpu_name = format!("cpu@{cpu_id:x}"); let cpu_node = fdt.begin_node(&cpu_name)?; @@ -173,6 +318,21 @@ fn create_cpu_nodes( fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?; fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?; + if cache_exist && l1_d_cache_size != 0 && l1_i_cache_size != 0 { + // Add cache info. + fdt.property_u32("d-cache-size", l1_d_cache_size)?; + fdt.property_u32("d-cache-line-size", l1_d_cache_line_size)?; + fdt.property_u32("d-cache-sets", l1_d_cache_sets)?; + + fdt.property_u32("i-cache-size", l1_i_cache_size)?; + fdt.property_u32("i-cache-line-size", l1_i_cache_line_size)?; + fdt.property_u32("i-cache-sets", l1_i_cache_sets)?; + + if l2_cache_size != 0 { + fdt.property_u32("next-level-cache", L2_CACHE_PHANDLE)?; + } + } + // Add `numa-node-id` property if there is any numa config. if numa_nodes.len() > 1 { for numa_node_idx in 0..numa_nodes.len() { @@ -186,6 +346,36 @@ fn create_cpu_nodes( fdt.end_node(cpu_node)?; } + if cache_exist && l2_cache_size != 0 { + let l2_cache_name = "l2-cache0"; + let l2_cache_node = fdt.begin_node(l2_cache_name)?; + fdt.property_u32("phandle", L2_CACHE_PHANDLE)?; + fdt.property_string("compatible", "cache")?; + fdt.property_u32("cache-size", l2_cache_size)?; + fdt.property_u32("cache-line-size", l2_cache_line_size)?; + fdt.property_u32("cache-sets", l2_cache_sets)?; + fdt.property_u32("cache-level", 2)?; + + if l3_cache_size != 0 { + fdt.property_u32("next-level-cache", L3_CACHE_PHANDLE)?; + } + + fdt.end_node(l2_cache_node)?; + } + + if cache_exist && l3_cache_size != 0 { + let l3_cache_name = "l3-cache0"; + let l3_cache_node = fdt.begin_node(l3_cache_name)?; + fdt.property_u32("phandle", L3_CACHE_PHANDLE)?; + fdt.property_string("compatible", "cache")?; + fdt.property_null("cache-unified")?; + fdt.property_u32("cache-size", l3_cache_size)?; + fdt.property_u32("cache-line-size", l3_cache_line_size)?; + fdt.property_u32("cache-sets", l3_cache_sets)?; + fdt.property_u32("cache-level", 3)?; + fdt.end_node(l3_cache_node)?; + } + if let Some(topology) = vcpu_topology { let (threads_per_core, cores_per_package, packages) = topology; let cpu_map_node = fdt.begin_node("cpu-map")?;