| 508 | } |
| 509 | |
| 510 | fn create_memory_node( |
| 511 | fdt: &mut FdtWriter, |
| 512 | guest_mem: &GuestMemoryMmap, |
| 513 | numa_nodes: &NumaNodes, |
| 514 | ) -> FdtWriterResult<()> { |
| 515 | // See https://github.com/torvalds/linux/blob/58ae0b51506802713aa0e9956d1853ba4c722c98/Documentation/devicetree/bindings/numa.txt |
| 516 | // for NUMA setting in memory node. |
| 517 | let has_generic_initiator = numa_nodes.values().any(|node| node.device_id.is_some()); |
| 518 | if has_generic_initiator { |
| 519 | info!("Skipping NUMA memory node encoding in FDT with Generic Initiator devices"); |
| 520 | } |
| 521 | // Skipping NUMA encoding in FDT when Generic Initiator devices |
| 522 | // are present allowed guest kernels to boot and |
| 523 | // rely solely on ACPI tables to setup NUMA |
| 524 | if numa_nodes.len() > 1 && !has_generic_initiator { |
| 525 | for numa_node_idx in 0..numa_nodes.len() { |
| 526 | let numa_node = numa_nodes.get(&(numa_node_idx as u32)); |
| 527 | let mut mem_reg_prop: Vec<u64> = Vec::new(); |
| 528 | let mut node_memory_addr: u64 = 0; |
| 529 | // Each memory zone of numa will have its own memory node, but |
| 530 | // different numa nodes should not share same memory zones. |
| 531 | for memory_region in numa_node.unwrap().memory_regions.iter() { |
| 532 | let memory_region_start_addr: u64 = memory_region.start_addr().raw_value(); |
| 533 | let memory_region_size: u64 = memory_region.size() as u64; |
| 534 | mem_reg_prop.push(memory_region_start_addr); |
| 535 | mem_reg_prop.push(memory_region_size); |
| 536 | // Set the node address the first non-zero region address |
| 537 | if node_memory_addr == 0 { |
| 538 | node_memory_addr = memory_region_start_addr; |
| 539 | } |
| 540 | } |
| 541 | // Only create a memory node if this NUMA node has memory regions |
| 542 | if !mem_reg_prop.is_empty() { |
| 543 | let memory_node_name = format!("memory@{node_memory_addr:x}"); |
| 544 | let memory_node = fdt.begin_node(&memory_node_name)?; |
| 545 | fdt.property_string("device_type", "memory")?; |
| 546 | fdt.property_array_u64("reg", &mem_reg_prop)?; |
| 547 | fdt.property_u32("numa-node-id", numa_node_idx as u32)?; |
| 548 | fdt.end_node(memory_node)?; |
| 549 | } |
| 550 | } |
| 551 | } else { |
| 552 | // Note: memory regions from "GuestMemory" are sorted and non-zero sized. |
| 553 | let ram_regions = { |
| 554 | let mut ram_regions = Vec::new(); |
| 555 | let mut current_start = guest_mem |
| 556 | .iter() |
| 557 | .next() |
| 558 | .map(GuestMemoryRegion::start_addr) |
| 559 | .expect("GuestMemory must have one memory region at least") |
| 560 | .raw_value(); |
| 561 | let mut current_end = current_start; |
| 562 | |
| 563 | for (start, size) in guest_mem |
| 564 | .iter() |
| 565 | .map(|m| (m.start_addr().raw_value(), m.len())) |
| 566 | { |
| 567 | if current_end == start { |