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(())
}
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
Following are the auxiliary function for creating the different nodes that we append to our FDT.
Following are the auxiliary function for creating the different nodes that we append to our FDT .
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(())
}
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 ( " " ) ? ; // As per documentation, on ARM v8 64-bit systems value should be set to 2. fdt . property_u32 ( " " , 0x02 ) ? ; fdt . property_u32 ( " " , 0x0 ) ? ; let num_cpus = vcpu_mpidr . len ( ) ; for ( cpu_index , mpidr ) in vcpu_mpidr . iter ( ) . enumerate ( ) { let cpu = fdt . begin_node ( & format ! ( " " cpu_index ) ) ? ; fdt . property_string ( " " , " " ) ? ; fdt . property_string ( " " , " " ) ? ; // The power state coordination interface (PSCI) needs to be enabled for // all vcpus. fdt . property_string ( " " , " " ) ? ; // 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 ( " " , 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 ( " " , 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 ! ( " " cache level cpu_index cache cpus_per_unit as usize ) ) ? ) ; fdt . property_u32 ( " " , cache_phandle ) ? ; fdt . property_string ( " " , " " ) ? ; fdt . property_u32 ( " " , 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 ( ( ) ) }
Following are the auxiliary function for creating the different nodes that we append to our FDT.
Following are the auxiliary function for creating the different nodes that we append to our FDT .
fdt
&mut FdtWriter
vcpu_mpidr
&[u64]
fdt
&mut FdtWriter
vcpu_mpidr
&[u64]