board_aarch64.rs1.89%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::collections::HashMap;16
use std::path::Path;17
use std::sync::Arc;18
19
use crate::arch::layout::{20
DEVICE_TREE_LIMIT, DEVICE_TREE_START, GIC_DIST_START, GIC_MSI_START,21
GIC_V2_CPU_INTERFACE_START, GIC_V3_REDIST_START, IO_END, IO_START, MEM_64_START,22
PCIE_CONFIG_START, PCIE_MMIO_32_NON_PREFETCHABLE_END, PCIE_MMIO_32_NON_PREFETCHABLE_START,23
PCIE_MMIO_32_PREFETCHABLE_END, PCIE_MMIO_32_PREFETCHABLE_START, PL011_START, PL031_START,24
RAM_32_SIZE, RAM_32_START,25
};26
use crate::arch::reg::MpidrEl1;27
use crate::board::{Board, BoardConfig, CpuTopology, PCIE_MMIO_64_SIZE, Result, VcpuGuard};28
use crate::firmware::dt::{DeviceTree, Node, PropVal};29
use crate::hv::{GicV2, GicV2m, GicV3, Hypervisor, Its, Vcpu, Vm};30
use crate::loader::{Executable, InitState, Payload};31
use crate::mem::{MemRegion, MemRegionType};32
33
enum Gic<V>34
where35
V: Vm,36
{37
V2(V::GicV2),38
V3(V::GicV3),39
}40
41
enum Msi<V>42
where43
V: Vm,44
{45
V2m(V::GicV2m),46
Its(V::Its),47
}48
49
pub struct ArchBoard<V>50
where51
V: Vm,52
{53
gic: Gic<V>,54
msi: Option<Msi<V>>,55
}56
57
impl<V: Vm> ArchBoard<V> {58
pub fn new<H>(_hv: &H, vm: &V, config: &BoardConfig) -> Result<Self>59
where60
H: Hypervisor<Vm = V>,61
{62
let gic = match vm.create_gic_v3(GIC_DIST_START, GIC_V3_REDIST_START, config.cpu.count) {63
Ok(v3) => Gic::V3(v3),64
Err(e) => {65
log::error!("Cannot create GIC v3: {e:?}trying v2...");66
Gic::V2(vm.create_gic_v2(GIC_DIST_START, GIC_V2_CPU_INTERFACE_START)?)67
}68
};69
70
let create_gic_v2m = || match vm.create_gic_v2m(GIC_MSI_START) {71
Ok(v2m) => Some(Msi::V2m(v2m)),72
Err(e) => {73
log::error!("Cannot create GIC v2m: {e:?}");74
None75
}76
};77
78
let msi = if matches!(gic, Gic::V3(_)) {79
match vm.create_its(GIC_MSI_START) {80
Ok(its) => Some(Msi::Its(its)),81
Err(e) => {82
log::error!("Cannot create ITS: {e:?}trying v2m...");83
create_gic_v2m()84
}85
}86
} else {87
create_gic_v2m()88
};89
90
Ok(ArchBoard { gic, msi })91
}92
}93
94
fn encode_mpidr(topology: &CpuTopology, index: u16) -> MpidrEl1 {6x95
let (socket_id, core_id, thread_id) = topology.encode(index);6x96
let mut mpidr = MpidrEl1(0);6x97
mpidr.set_aff0(thread_id);6x98
mpidr.set_aff1(core_id as u8);6x99
mpidr.set_aff2(socket_id);6x100
mpidr6x101
}6x102
103
impl<V> Board<V>104
where105
V: Vm,106
{107
pub fn encode_cpu_identity(&self, index: u16) -> u64 {108
encode_mpidr(&self.config.cpu.topology, index).0109
}110
111
pub fn setup_firmware(&self, _: &Path, _: &Payload, _: &V::Vcpu) -> Result<InitState> {112
unimplemented!()113
}114
115
pub fn init_ap(&self, _id: u16, _vcpu: &mut V::Vcpu, _vcpus: &VcpuGuard) -> Result<()> {116
Ok(())117
}118
119
pub fn init_boot_vcpu(&self, vcpu: &mut V::Vcpu, init_state: &InitState) -> Result<()> {120
vcpu.set_regs(&init_state.regs)?;121
vcpu.set_sregs(&init_state.sregs)?;122
Ok(())123
}124
125
pub fn init_vcpu(&self, index: u16, vcpu: &mut V::Vcpu) -> Result<()> {126
self.reset_vcpu(index, vcpu)127
}128
129
pub fn reset_vcpu(&self, index: u16, vcpu: &mut V::Vcpu) -> Result<()> {130
vcpu.reset(index == 0)?;131
Ok(())132
}133
134
pub fn create_ram(&self) -> Result<()> {135
let mem_size = self.config.mem.size;136
let memory = &self.memory;137
138
let low_mem_size = std::cmp::min(mem_size, RAM_32_SIZE);139
let pages_low = self.create_ram_pages(low_mem_size, c"ram-low")?;140
memory.add_region(141
RAM_32_START,142
Arc::new(MemRegion::with_ram(pages_low, MemRegionType::Ram)),143
)?;144
145
let high_mem_size = mem_size.saturating_sub(RAM_32_SIZE);146
if high_mem_size > 0 {147
let pages_high = self.create_ram_pages(high_mem_size, c"ram-high")?;148
memory.add_region(149
MEM_64_START,150
Arc::new(MemRegion::with_ram(pages_high, MemRegionType::Ram)),151
)?;152
}153
154
Ok(())155
}156
157
pub fn coco_init(&self, _: Arc<V::Memory>) -> Result<()> {158
Ok(())159
}160
161
pub fn coco_finalize(&self, _id: u16, _vcpus: &VcpuGuard) -> Result<()> {162
Ok(())163
}164
165
pub fn arch_init(&self) -> Result<()> {166
match &self.arch.gic {167
Gic::V2(v2) => v2.init(),168
Gic::V3(v3) => v3.init(),169
}?;170
match &self.arch.msi {171
Some(Msi::V2m(v2m)) => v2m.init(),172
Some(Msi::Its(its)) => its.init(),173
None => Ok(()),174
}?;175
Ok(())176
}177
178
fn create_chosen_node(&self, init_state: &InitState, root: &mut Node) {179
let payload = self.payload.read();180
let Some(payload) = payload.as_ref() else {181
return;182
};183
if !matches!(payload.executable, Some(Executable::Linux(_))) {184
return;185
}186
let mut node = Node::default();187
if let Some(cmdline) = &payload.cmdline {188
let bytes = cmdline.as_bytes_with_nul().to_owned();189
node.props.insert("bootargs", PropVal::Bytes(bytes));190
}191
if let Some(initramfs_range) = &init_state.initramfs {192
node.props.insert(193
"linux,initrd-start",194
PropVal::U32(initramfs_range.start as u32),195
);196
node.props197
.insert("linux,initrd-end", PropVal::U32(initramfs_range.end as u32));198
}199
node.props.insert(200
"stdout-path",201
PropVal::String(format!("/pl011@{PL011_START:x}")),202
);203
root.nodes.push(("chosen".to_owned(), node));204
}205
206
pub fn create_memory_node(&self, root: &mut Node) {207
let regions = self.memory.mem_region_entries();208
for (start, region) in regions {209
if region.type_ != MemRegionType::Ram {210
continue;211
};212
let node = Node {213
props: HashMap::from([214
("device_type", PropVal::Str("memory")),215
("reg", PropVal::U64List(vec![start, region.size])),216
]),217
nodes: Vec::new(),218
};219
root.nodes.push((format!("memory@{start:x}"), node));220
}221
}222
223
pub fn create_cpu_nodes(&self, root: &mut Node) {224
let topology = &self.config.cpu.topology;225
226
let thread_node = |socket_id: u8, core_id: u16, thread_id: u8| {227
let phandle = PHANDLE_CPU | topology.decode(socket_id, core_id, thread_id) as u32;228
Node {229
props: HashMap::from([("cpu", PropVal::PHandle(phandle))]),230
nodes: Vec::new(),231
}232
};233
let core_node = |socket_id: u8, core_id: u16| {234
if topology.smt {235
Node {236
props: HashMap::new(),237
nodes: vec![238
("thread0".to_owned(), thread_node(socket_id, core_id, 0)),239
("thread1".to_owned(), thread_node(socket_id, core_id, 1)),240
],241
}242
} else {243
thread_node(socket_id, core_id, 0)244
}245
};246
let socket_node = |socket_id: u8| Node {247
props: HashMap::new(),248
nodes: vec![(249
"cluster0".to_owned(),250
Node {251
props: HashMap::new(),252
nodes: (0..topology.cores)253
.map(|core_id| (format!("core{core_id}"), core_node(socket_id, core_id)))254
.collect(),255
},256
)],257
};258
let cpu_map_node = Node {259
props: HashMap::new(),260
nodes: (0..topology.sockets)261
.map(|socket_id| (format!("socket{socket_id}"), socket_node(socket_id)))262
.collect(),263
};264
265
let cpus_nodes = (0..(self.config.cpu.count))266
.map(|index| {267
let mpidr = self.encode_cpu_identity(index);268
(269
format!("cpu@{mpidr:x}"),270
Node {271
props: HashMap::from([272
("device_type", PropVal::Str("cpu")),273
("compatible", PropVal::Str("arm,arm-v8")),274
("enable-method", PropVal::Str("psci")),275
("reg", PropVal::U64(mpidr)),276
("phandle", PropVal::PHandle(PHANDLE_CPU | index as u32)),277
]),278
nodes: Vec::new(),279
},280
)281
})282
.chain([("cpu-map".to_owned(), cpu_map_node)])283
.collect();284
285
let cpus = Node {286
props: HashMap::from([287
("#address-cells", PropVal::U32(2)),288
("#size-cells", PropVal::U32(0)),289
]),290
nodes: cpus_nodes,291
};292
root.nodes.push(("cpus".to_owned(), cpus));293
}294
295
fn create_clock_node(&self, root: &mut Node) {296
let node = Node {297
props: HashMap::from([298
("compatible", PropVal::Str("fixed-clock")),299
("clock-frequency", PropVal::U32(24000000)),300
("clock-output-names", PropVal::Str("clk24mhz")),301
("phandle", PropVal::PHandle(PHANDLE_CLOCK)),302
("#clock-cells", PropVal::U32(0)),303
]),304
nodes: Vec::new(),305
};306
root.nodes.push(("apb-pclk".to_owned(), node));307
}308
309
fn create_pl011_node(&self, root: &mut Node) {310
let pin = 1;311
let edge_trigger = 1;312
let spi = 0;313
let node = Node {314
props: HashMap::from([315
("compatible", PropVal::Str("arm,primecell\0arm,pl011")),316
("reg", PropVal::U64List(vec![PL011_START, 0x1000])),317
("interrupts", PropVal::U32List(vec![spi, pin, edge_trigger])),318
("clock-names", PropVal::Str("uartclk\0apb_pclk")),319
(320
"clocks",321
PropVal::U32List(vec![PHANDLE_CLOCK, PHANDLE_CLOCK]),322
),323
]),324
nodes: Vec::new(),325
};326
root.nodes.push((format!("pl011@{PL011_START:x}"), node));327
}328
329
fn create_pl031_node(&self, root: &mut Node) {330
let node = Node {331
props: HashMap::from([332
("compatible", PropVal::Str("arm,primecell\0arm,pl031")),333
("reg", PropVal::U64List(vec![PL031_START, 0x1000])),334
("clock-names", PropVal::Str("apb_pclk")),335
("clocks", PropVal::U32List(vec![PHANDLE_CLOCK])),336
]),337
nodes: Vec::new(),338
};339
root.nodes.push((format!("pl031@{PL031_START:x}"), node));340
}341
342
// Documentation/devicetree/bindings/timer/arm,arch_timer.yaml343
fn create_timer_node(&self, root: &mut Node) {344
let mut interrupts = vec![];345
let irq_pins = [13, 14, 11, 10];346
let ppi = 1;347
let level_trigger = 4;348
let cpu_mask = match self.arch.gic {349
Gic::V2(_) => (1 << self.config.cpu.count) - 1,350
Gic::V3 { .. } => 0,351
};352
for pin in irq_pins {353
interrupts.extend([ppi, pin, (cpu_mask << 8) | level_trigger]);354
}355
let node = Node {356
props: HashMap::from([357
("compatible", PropVal::Str("arm,armv8-timer")),358
("interrupts", PropVal::U32List(interrupts)),359
("always-on", PropVal::Empty),360
]),361
nodes: Vec::new(),362
};363
root.nodes.push(("timer".to_owned(), node));364
}365
366
fn create_gic_msi_node(&self) -> Vec<(String, Node)> {367
let Some(msi) = &self.arch.msi else {368
return Vec::new();369
};370
match msi {371
Msi::Its(_) => {372
let node = Node {373
props: HashMap::from([374
("compatible", PropVal::Str("arm,gic-v3-its")),375
("msi-controller", PropVal::Empty),376
("#msi-cells", PropVal::U32(1)),377
("reg", PropVal::U64List(vec![GIC_MSI_START, 128 << 10])),378
("phandle", PropVal::PHandle(PHANDLE_MSI)),379
]),380
nodes: Vec::new(),381
};382
vec![(format!("its@{GIC_MSI_START:x}"), node)]383
}384
Msi::V2m(_) => {385
let node = Node {386
props: HashMap::from([387
("compatible", PropVal::Str("arm,gic-v2m-frame")),388
("msi-controller", PropVal::Empty),389
("reg", PropVal::U64List(vec![GIC_MSI_START, 64 << 10])),390
("phandle", PropVal::PHandle(PHANDLE_MSI)),391
]),392
nodes: Vec::new(),393
};394
vec![(format!("v2m@{GIC_MSI_START:x}"), node)]395
}396
}397
}398
399
fn create_gic_node(&self, root: &mut Node) {400
let msi = self.create_gic_msi_node();401
let node = match self.arch.gic {402
// Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml403
Gic::V2(_) => Node {404
props: HashMap::from([405
("compatible", PropVal::Str("arm,cortex-a15-gic")),406
("#interrupt-cells", PropVal::U32(3)),407
(408
"reg",409
PropVal::U64List(vec![410
GIC_DIST_START,411
0x1000,412
GIC_V2_CPU_INTERFACE_START,413
0x2000,414
]),415
),416
("phandle", PropVal::U32(PHANDLE_GIC)),417
("interrupt-controller", PropVal::Empty),418
]),419
nodes: msi,420
},421
// Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml422
Gic::V3(_) => Node {423
props: HashMap::from([424
("compatible", PropVal::Str("arm,gic-v3")),425
("#interrupt-cells", PropVal::U32(3)),426
("#address-cells", PropVal::U32(2)),427
("#size-cells", PropVal::U32(2)),428
("interrupt-controller", PropVal::Empty),429
("ranges", PropVal::Empty),430
(431
"reg",432
PropVal::U64List(vec![433
GIC_DIST_START,434
64 << 10,435
GIC_V3_REDIST_START,436
self.config.cpu.count as u64 * (128 << 10),437
]),438
),439
("phandle", PropVal::U32(PHANDLE_GIC)),440
]),441
nodes: msi,442
},443
};444
root.nodes.push((format!("intc@{GIC_DIST_START:x}"), node));445
}446
447
// Documentation/devicetree/bindings/arm/psci.yaml448
fn create_psci_node(&self, root: &mut Node) {449
let node = Node {450
props: HashMap::from([451
("method", PropVal::Str("hvc")),452
("compatible", PropVal::Str("arm,psci-0.2\0arm,psci")),453
]),454
nodes: Vec::new(),455
};456
root.nodes.push(("psci".to_owned(), node));457
}458
459
// https://elinux.org/Device_Tree_Usage#PCI_Host_Bridge460
// Documentation/devicetree/bindings/pci/host-generic-pci.yaml461
// IEEE Std 1275-1994462
fn create_pci_bridge_node(&self, root: &mut Node) {463
let Some(max_bus) = self.pci_bus.segment.max_bus() else {464
return;465
};466
let pcie_mmio_64_start = self.config.pcie_mmio_64_start();467
let prefetchable = 1 << 30;468
let io = 0b01 << 24;469
let mem_32 = 0b10 << 24;470
let mem_64 = 0b11 << 24;471
let node = Node {472
props: HashMap::from([473
("compatible", PropVal::Str("pci-host-ecam-generic")),474
("device_type", PropVal::Str("pci")),475
("reg", PropVal::U64List(vec![PCIE_CONFIG_START, 256 << 20])),476
("bus-range", PropVal::U32List(vec![0, max_bus as u32])),477
("#address-cells", PropVal::U32(3)),478
("#size-cells", PropVal::U32(2)),479
(480
"ranges",481
PropVal::U32List(vec![482
io,483
0,484
0,485
0,486
IO_START as u32,487
0,488
(IO_END - IO_START) as u32,489
mem_32 | prefetchable,490
0,491
PCIE_MMIO_32_PREFETCHABLE_START as u32,492
0,493
PCIE_MMIO_32_PREFETCHABLE_START as u32,494
0,495
(PCIE_MMIO_32_PREFETCHABLE_END - PCIE_MMIO_32_PREFETCHABLE_START) as u32,496
mem_32,497
0,498
PCIE_MMIO_32_NON_PREFETCHABLE_START as u32,499
0,500
PCIE_MMIO_32_NON_PREFETCHABLE_START as u32,501
0,502
(PCIE_MMIO_32_NON_PREFETCHABLE_END - PCIE_MMIO_32_NON_PREFETCHABLE_START)503
as u32,504
mem_64 | prefetchable,505
(pcie_mmio_64_start >> 32) as u32,506
pcie_mmio_64_start as u32,507
(pcie_mmio_64_start >> 32) as u32,508
pcie_mmio_64_start as u32,509
(PCIE_MMIO_64_SIZE >> 32) as u32,510
PCIE_MMIO_64_SIZE as u32,511
]),512
),513
(514
"msi-map",515
// Identity map from RID (BDF) to msi-specifier.516
// Documentation/devicetree/bindings/pci/pci-msi.txt517
PropVal::U32List(vec![0, PHANDLE_MSI, 0, 0x10000]),518
),519
]),520
nodes: Vec::new(),521
};522
root.nodes523
.push((format!("pci@{PCIE_CONFIG_START:x}"), node));524
}525
526
pub fn create_firmware_data(&self, init_state: &InitState) -> Result<()> {527
let mut device_tree = DeviceTree::new();528
let root = &mut device_tree.root;529
root.props.insert("#address-cells", PropVal::U32(2));530
root.props.insert("#size-cells", PropVal::U32(2));531
root.props.insert("model", PropVal::Str("linux,dummy-virt"));532
root.props533
.insert("compatible", PropVal::Str("linux,dummy-virt"));534
root.props535
.insert("interrupt-parent", PropVal::PHandle(PHANDLE_GIC));536
537
self.create_chosen_node(init_state, root);538
self.create_pl011_node(root);539
self.create_pl031_node(root);540
self.create_memory_node(root);541
self.create_cpu_nodes(root);542
self.create_gic_node(root);543
if self.arch.msi.is_some() {544
self.create_pci_bridge_node(root);545
}546
self.create_clock_node(root);547
self.create_timer_node(root);548
self.create_psci_node(root);549
log::debug!("device tree: {device_tree:#x?}");550
let blob = device_tree.to_blob();551
let ram = self.memory.ram_bus();552
assert!(blob.len() as u64 <= DEVICE_TREE_LIMIT);553
ram.write_range(DEVICE_TREE_START, blob.len() as u64, &*blob)?;554
Ok(())555
}556
}557
558
const PHANDLE_GIC: u32 = 1;559
const PHANDLE_CLOCK: u32 = 2;560
const PHANDLE_MSI: u32 = 3;561
const PHANDLE_CPU: u32 = 1 << 31;562
563
#[cfg(test)]564
#[path = "board_aarch64_test.rs"]565
mod tests;566