fn create_cpu_nodes()

in src/arch/src/aarch64/fdt.rs [122:224]


fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> Result<()> {
    // Since the L1 caches are not shareable among CPUs and they are direct attributes of the
    // cpu in the device tree, we process the L1 and non-L1 caches separately.
    // We use sysfs for extracting the cache information.
    let mut l1_caches: Vec<CacheEntry> = Vec::new();
    let mut non_l1_caches: Vec<CacheEntry> = Vec::new();
    // We use sysfs for extracting the cache information.
    read_cache_config(&mut l1_caches, &mut non_l1_caches)
        .map_err(|e| Error::ReadCacheInfo(e.to_string()))?;

    // See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/arm/cpus.yaml.
    let cpus = fdt.begin_node("cpus")?;
    // As per documentation, on ARM v8 64-bit systems value should be set to 2.
    fdt.property_u32("#address-cells", 0x02)?;
    fdt.property_u32("#size-cells", 0x0)?;
    let num_cpus = vcpu_mpidr.len();
    for (cpu_index, mpidr) in vcpu_mpidr.iter().enumerate() {
        let cpu = fdt.begin_node(&format!("cpu@{:x}", cpu_index))?;
        fdt.property_string("device_type", "cpu")?;
        fdt.property_string("compatible", "arm,arm-v8")?;
        // The power state coordination interface (PSCI) needs to be enabled for
        // all vcpus.
        fdt.property_string("enable-method", "psci")?;
        // Set the field to first 24 bits of the MPIDR - Multiprocessor Affinity Register.
        // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABHBJCI.html.
        fdt.property_u64("reg", mpidr & 0x7FFFFF)?;

        for cache in l1_caches.iter() {
            // Please check out
            // https://github.com/devicetree-org/devicetree-specification/releases/download/v0.3/devicetree-specification-v0.3.pdf,
            // section 3.8.
            if let Some(size) = cache.size_ {
                fdt.property_u32(cache.type_.of_cache_size(), size as u32)?;
            }
            if let Some(line_size) = cache.line_size {
                fdt.property_u32(cache.type_.of_cache_line_size(), line_size as u32)?;
            }
            if let Some(number_of_sets) = cache.number_of_sets {
                fdt.property_u32(cache.type_.of_cache_sets(), number_of_sets as u32)?;
            }
        }

        // Some of the non-l1 caches can be shared amongst CPUs. You can see an example of a shared scenario
        // in https://github.com/devicetree-org/devicetree-specification/releases/download/v0.3/devicetree-specification-v0.3.pdf,
        // 3.8.1 Example.
        let mut prev_level = 1;
        let mut cache_node: Option<FdtWriterNode> = None;
        for cache in non_l1_caches.iter() {
            // We append the next-level-cache node (the node that specifies the cache hierarchy)
            // in the next iteration. For example,
            // L2-cache {
            //      cache-size = <0x8000> ----> first iteration
            //      next-level-cache = <&l3-cache> ---> second iteration
            // }
            // The cpus per unit cannot be 0 since the sysfs will also include the current cpu
            // in the list of shared cpus so it needs to be at least 1. Firecracker trusts the host.
            // The operation is safe since we already checked when creating cache attributes that
            // cpus_per_unit is not 0 (.e look for mask_str2bit_count function).
            let cache_phandle = LAST_CACHE_PHANDLE
                - (num_cpus * (cache.level - 2) as usize + cpu_index / cache.cpus_per_unit as usize)
                    as u32;

            if prev_level != cache.level {
                fdt.property_u32("next-level-cache", cache_phandle)?;
                if prev_level > 1 && cache_node.is_some() {
                    fdt.end_node(cache_node.take().unwrap())?;
                }
            }

            if cpu_index % cache.cpus_per_unit as usize == 0 {
                cache_node = Some(fdt.begin_node(&format!(
                    "l{}-{}-cache",
                    cache.level,
                    cpu_index / cache.cpus_per_unit as usize
                ))?);
                fdt.property_u32("phandle", cache_phandle)?;
                fdt.property_string("compatible", "cache")?;
                fdt.property_u32("cache-level", cache.level as u32)?;
                if let Some(size) = cache.size_ {
                    fdt.property_u32(cache.type_.of_cache_size(), size as u32)?;
                }
                if let Some(line_size) = cache.line_size {
                    fdt.property_u32(cache.type_.of_cache_line_size(), line_size as u32)?;
                }
                if let Some(number_of_sets) = cache.number_of_sets {
                    fdt.property_u32(cache.type_.of_cache_sets(), number_of_sets as u32)?;
                }
                if let Some(cache_type) = cache.type_.of_cache_type() {
                    fdt.property_null(cache_type)?;
                }
                prev_level = cache.level;
            }
        }
        if let Some(node) = cache_node {
            fdt.end_node(node)?;
        }

        fdt.end_node(cpu)?;
    }
    fdt.end_node(cpus)?;

    Ok(())
}