mirror of
https://github.com/google/alioth.git
synced 2024-11-28 09:26:21 +00:00
feat(board): create devicetree for aarch64 guests
Signed-off-by: Changyuan Lyu <changyuanl@google.com>
This commit is contained in:
parent
5634465cda
commit
1c1148639e
1 changed files with 241 additions and 8 deletions
|
@ -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<Vec<u64>>,
|
||||
}
|
||||
|
||||
impl<V: Vm> ArchBoard<V> {
|
||||
pub fn new<H>(_hv: &H, vm: &V, _config: &BoardConfig) -> Result<Self>
|
||||
pub fn new<H>(_hv: &H, vm: &V, config: &BoardConfig) -> Result<Self>
|
||||
where
|
||||
H: Hypervisor<Vm = V>,
|
||||
{
|
||||
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::<HashMap<_, _>>();
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue