diff --git a/alioth/src/board/aarch64.rs b/alioth/src/board/aarch64.rs index efcf7e3..b448f2c 100644 --- a/alioth/src/board/aarch64.rs +++ b/alioth/src/board/aarch64.rs @@ -12,14 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::HashMap; use std::sync::Arc; +use parking_lot::Mutex; + use crate::arch::layout::{ - GIC_V2_CPU_INTERFACE_START, GIC_V2_DIST_START, MEM_64_START, RAM_32_SIZE, RAM_32_START, + DEVICE_TREE_LIMIT, DEVICE_TREE_START, GIC_V2_CPU_INTERFACE_START, GIC_V2_DIST_START, + MEM_64_START, PL011_START, RAM_32_SIZE, RAM_32_START, }; +use crate::arch::reg::SReg; use crate::board::{Board, BoardConfig, Result, VcpuGuard}; +use crate::firmware::dt::{DeviceTree, Node, PropVal}; use crate::hv::{GicV2, Hypervisor, Vcpu, Vm}; -use crate::loader::InitState; +use crate::loader::{ExecType, InitState}; use crate::mem::mapped::ArcMemPages; use crate::mem::{AddrOpt, MemRegion, MemRegionType}; @@ -28,15 +34,17 @@ where V: Vm, { gic_v2: V::GicV2, + mpidrs: Mutex>, } impl ArchBoard { - pub fn new(_hv: &H, vm: &V, _config: &BoardConfig) -> Result + pub fn new(_hv: &H, vm: &V, config: &BoardConfig) -> Result where H: Hypervisor, { let gic_v2 = vm.create_gic_v2(GIC_V2_DIST_START, GIC_V2_CPU_INTERFACE_START)?; - Ok(ArchBoard { gic_v2 }) + let mpidrs = Mutex::new(vec![u64::MAX; config.num_cpu as usize]); + Ok(ArchBoard { gic_v2, mpidrs }) } } @@ -60,6 +68,7 @@ where pub fn init_vcpu(&self, id: u32, vcpu: &mut V::Vcpu) -> Result<()> { vcpu.reset(id == 0)?; + self.arch.mpidrs.lock()[id as usize] = vcpu.get_sreg(SReg::MPIDR_EL1)?; Ok(()) } @@ -101,12 +110,236 @@ where unimplemented!() } - pub fn create_firmware_data(&self, _init_state: &InitState) -> Result<()> { - unimplemented!() - } - pub fn arch_init(&self) -> Result<()> { self.arch.gic_v2.init()?; Ok(()) } + + fn create_chosen_node(&self, init_state: &InitState, root: &mut Node) { + let payload = self.payload.read(); + let Some(payload) = payload.as_ref() else { + return; + }; + if !matches!(payload.exec_type, ExecType::Linux) { + return; + } + let mut node = Node::default(); + if let Some(cmd_line) = &payload.cmd_line { + node.props + .insert("bootargs", PropVal::String(cmd_line.clone())); + } + if let Some(initramfs_range) = &init_state.initramfs { + node.props.insert( + "linux,initrd-start", + PropVal::U32(initramfs_range.start as u32), + ); + node.props + .insert("linux,initrd-end", PropVal::U32(initramfs_range.end as u32)); + } + node.props.insert( + "stdout-path", + PropVal::String(format!("/pl011@{:x}", PL011_START)), + ); + root.nodes.insert("chosen".to_owned(), node); + } + + pub fn create_memory_node(&self, root: &mut Node) { + let regions = self.memory.mem_region_entries(); + for (start, region) in regions { + if region.type_ != MemRegionType::Ram { + continue; + }; + let node = Node { + props: HashMap::from([ + ("device_type", PropVal::Str("memory")), + ("reg", PropVal::U64List(vec![start, region.size])), + ]), + nodes: HashMap::new(), + }; + root.nodes.insert(format!("memory@{start:x}"), node); + } + } + + pub fn create_cpu_nodes(&self, root: &mut Node) { + let mpidrs = self.arch.mpidrs.lock(); + + let mut cpu_nodes = mpidrs + .iter() + .map(|mpidr| { + let reg = mpidr & 0xff_00ff_ffff; + ( + format!("cpu@{reg}"), + Node { + props: HashMap::from([ + ("device_type", PropVal::Str("cpu")), + ("compatible", PropVal::Str("arm,arm-v8")), + ("enable-method", PropVal::Str("psci")), + ("reg", PropVal::U32(reg as u32)), + ("phandle", PropVal::PHandle(reg as u32 | (1 << 16))), + ]), + nodes: HashMap::new(), + }, + ) + }) + .collect::>(); + let cores = mpidrs + .iter() + .map(|mpidr| { + let reg = mpidr & 0xff_00ff_ffff; + ( + format!("core{reg}"), + Node { + props: HashMap::from([("cpu", PropVal::PHandle(reg as u32 | (1 << 16)))]), + nodes: HashMap::new(), + }, + ) + }) + .collect(); + let cpu_map = Node { + props: HashMap::new(), + nodes: HashMap::from([( + "socket0".to_owned(), + Node { + props: HashMap::new(), + nodes: HashMap::from([( + "cluster0".to_owned(), + Node { + props: HashMap::new(), + nodes: cores, + }, + )]), + }, + )]), + }; + cpu_nodes.insert("cpu-map".to_owned(), cpu_map); + let cpus = Node { + props: HashMap::from([ + ("#address-cells", PropVal::U32(1)), + ("#size-cells", PropVal::U32(0)), + ]), + nodes: cpu_nodes, + }; + root.nodes.insert("cpus".to_owned(), cpus); + } + + fn create_clock_node(&self, root: &mut Node) { + let node = Node { + props: HashMap::from([ + ("compatible", PropVal::Str("fixed-clock")), + ("clock-frequency", PropVal::U32(24000000)), + ("clock-output-names", PropVal::Str("clk24mhz")), + ("phandle", PropVal::PHandle(PHANDLE_CLOCK)), + ("#clock-cells", PropVal::U32(0)), + ]), + nodes: HashMap::new(), + }; + root.nodes.insert("apb-pclk".to_owned(), node); + } + + fn create_pl011_node(&self, root: &mut Node) { + let pin = 1; + let edge_trigger = 1; + let spi = 0; + let node = Node { + props: HashMap::from([ + ("compatible", PropVal::Str("arm,primecell\0arm,pl011")), + ("reg", PropVal::U64List(vec![PL011_START, 0x1000])), + ("interrupts", PropVal::U32List(vec![spi, pin, edge_trigger])), + ("clock-names", PropVal::Str("uartclk\0apb_pclk")), + ( + "clocks", + PropVal::U32List(vec![PHANDLE_CLOCK, PHANDLE_CLOCK]), + ), + ]), + nodes: HashMap::new(), + }; + root.nodes.insert(format!("pl011@{:x}", PL011_START), node); + } + + // Documentation/devicetree/bindings/timer/arm,arch_timer.yaml + fn create_timer_node(&self, root: &mut Node) { + let mut interrupts = vec![]; + let irq_pins = [13, 14, 11, 10]; + let ppi = 1; + let level_trigger = 4; + let cpu_mask = (1 << self.config.num_cpu) - 1; + for pin in irq_pins { + interrupts.extend([ppi, pin, cpu_mask << 8 | level_trigger]); + } + let node = Node { + props: HashMap::from([ + ("compatible", PropVal::Str("arm,armv8-timer")), + ("interrupts", PropVal::U32List(interrupts)), + ("always-on", PropVal::Empty), + ]), + nodes: HashMap::new(), + }; + root.nodes.insert("timer".to_owned(), node); + } + + // Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml + fn create_gicv2_node(&self, root: &mut Node) { + let node = Node { + props: HashMap::from([ + ("compatible", PropVal::Str("arm,cortex-a15-gic")), + ("#interrupt-cells", PropVal::U32(3)), + ( + "reg", + PropVal::U64List(vec![ + GIC_V2_DIST_START, + 0x1000, + GIC_V2_CPU_INTERFACE_START, + 0x2000, + ]), + ), + ("phandle", PropVal::U32(PHANDLE_GIC)), + ("interrupt-controller", PropVal::Empty), + ]), + nodes: HashMap::new(), + }; + root.nodes + .insert(format!("intc@{GIC_V2_DIST_START:x}"), node); + } + + // Documentation/devicetree/bindings/arm/psci.yaml + fn create_psci_node(&self, root: &mut Node) { + let node = Node { + props: HashMap::from([ + ("method", PropVal::Str("hvc")), + ("compatible", PropVal::Str("arm,psci-0.2\0arm,psci")), + ]), + nodes: HashMap::new(), + }; + root.nodes.insert("psci".to_owned(), node); + } + + pub fn create_firmware_data(&self, init_state: &InitState) -> Result<()> { + let mut device_tree = DeviceTree::new(); + let root = &mut device_tree.root; + root.props.insert("#address-cells", PropVal::U32(2)); + root.props.insert("#size-cells", PropVal::U32(2)); + root.props.insert("model", PropVal::Str("linux,dummy-virt")); + root.props + .insert("compatible", PropVal::Str("linux,dummy-virt")); + root.props + .insert("interrupt-parent", PropVal::PHandle(PHANDLE_GIC)); + + self.create_chosen_node(init_state, root); + self.create_pl011_node(root); + self.create_memory_node(root); + self.create_cpu_nodes(root); + self.create_gicv2_node(root); + self.create_clock_node(root); + self.create_timer_node(root); + self.create_psci_node(root); + log::debug!("device tree: {:#x?}", device_tree); + let blob = device_tree.to_blob(); + let ram = self.memory.ram_bus(); + assert!(blob.len() as u64 <= DEVICE_TREE_LIMIT); + ram.write_range(DEVICE_TREE_START, blob.len() as u64, &*blob)?; + Ok(()) + } } + +const PHANDLE_GIC: u32 = 1; +const PHANDLE_CLOCK: u32 = 2;