• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / alioth / 17385983897

01 Sep 2025 07:41PM UTC coverage: 18.009% (-0.1%) from 18.149%
17385983897

Pull #281

github

web-flow
Merge 782c57b11 into 6ec9a6d6b
Pull Request #281: Port to Apple Hypervisor framework

0 of 154 new or added lines in 11 files covered. (0.0%)

1166 existing lines in 29 files now uncovered.

1362 of 7563 relevant lines covered (18.01%)

18.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/alioth/src/board/aarch64.rs
1
// Copyright 2024 Google LLC
2
//
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 at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// 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 and
13
// limitations under the License.
14

15
use std::collections::HashMap;
16
use std::sync::Arc;
17

18
use parking_lot::Mutex;
19

20
use crate::arch::layout::{
21
    DEVICE_TREE_LIMIT, DEVICE_TREE_START, GIC_DIST_START, GIC_MSI_START,
22
    GIC_V2_CPU_INTERFACE_START, GIC_V3_REDIST_START, MEM_64_START, PCIE_CONFIG_START,
23
    PCIE_MMIO_32_NON_PREFETCHABLE_END, PCIE_MMIO_32_NON_PREFETCHABLE_START,
24
    PCIE_MMIO_32_PREFETCHABLE_END, PCIE_MMIO_32_PREFETCHABLE_START, PL011_START, RAM_32_SIZE,
25
    RAM_32_START,
26
};
27
use crate::arch::reg::SReg;
28
use crate::board::{Board, BoardConfig, PCIE_MMIO_64_SIZE, Result, VcpuGuard};
29
use crate::firmware::dt::{DeviceTree, Node, PropVal};
30
use crate::hv::{GicV2, GicV2m, GicV3, Hypervisor, Its, Vcpu, Vm};
31
use crate::loader::{ExecType, InitState};
32
use crate::mem::mapped::ArcMemPages;
33
use crate::mem::{MemRegion, MemRegionType};
34

35
enum Gic<V>
36
where
37
    V: Vm,
38
{
39
    V2(V::GicV2),
40
    V3(V::GicV3),
41
}
42

43
enum Msi<V>
44
where
45
    V: Vm,
46
{
47
    V2m(V::GicV2m),
48
    Its(V::Its),
49
}
50

51
pub struct ArchBoard<V>
52
where
53
    V: Vm,
54
{
55
    gic: Gic<V>,
56
    msi: Option<Msi<V>>,
57
    mpidrs: Mutex<Vec<u64>>,
58
}
59

60
impl<V: Vm> ArchBoard<V> {
61
    pub fn new<H>(_hv: &H, vm: &V, config: &BoardConfig) -> Result<Self>
62
    where
63
        H: Hypervisor<Vm = V>,
64
    {
NEW
65
        let gic = match vm.create_gic_v3(GIC_DIST_START, GIC_V3_REDIST_START, config.num_cpu) {
×
NEW
66
            Ok(v3) => Gic::V3(v3),
×
NEW
67
            Err(e) => {
×
NEW
68
                log::error!("Cannot create GIC v3: {e:?}trying v2...");
×
NEW
69
                Gic::V2(vm.create_gic_v2(GIC_DIST_START, GIC_V2_CPU_INTERFACE_START)?)
×
70
            }
71
        };
72

NEW
73
        let create_gic_v2m = || match vm.create_gic_v2m(GIC_MSI_START) {
×
NEW
74
            Ok(v2m) => Some(Msi::V2m(v2m)),
×
75
            Err(e) => {
×
NEW
76
                log::error!("Cannot create GIC v2m: {e:?}");
×
NEW
77
                None
×
78
            }
79
        };
80

NEW
81
        let msi = if matches!(gic, Gic::V3(_)) {
×
NEW
82
            match vm.create_its(GIC_MSI_START) {
×
NEW
83
                Ok(its) => Some(Msi::Its(its)),
×
NEW
84
                Err(e) => {
×
NEW
85
                    log::error!("Cannot create ITS: {e:?}trying v2m...");
×
NEW
86
                    create_gic_v2m()
×
87
                }
88
            }
89
        } else {
NEW
90
            create_gic_v2m()
×
91
        };
92

93
        let mpidrs = Mutex::new(vec![u64::MAX; config.num_cpu as usize]);
×
NEW
94
        Ok(ArchBoard { gic, msi, mpidrs })
×
95
    }
96
}
97

98
impl<V> Board<V>
99
where
100
    V: Vm,
101
{
102
    pub fn setup_firmware(&self, _fw: &mut ArcMemPages) -> Result<()> {
×
103
        unimplemented!()
104
    }
105

106
    pub fn init_ap(&self, _id: u32, _vcpu: &mut V::Vcpu, _vcpus: &VcpuGuard) -> Result<()> {
×
107
        Ok(())
×
108
    }
109

110
    pub fn init_boot_vcpu(&self, vcpu: &mut V::Vcpu, init_state: &InitState) -> Result<()> {
×
111
        vcpu.set_regs(&init_state.regs)?;
×
112
        vcpu.set_sregs(&init_state.sregs)?;
×
113
        Ok(())
×
114
    }
115

116
    pub fn init_vcpu(&self, id: u32, vcpu: &mut V::Vcpu) -> Result<()> {
×
117
        vcpu.reset(id == 0)?;
×
118
        self.arch.mpidrs.lock()[id as usize] = vcpu.get_sreg(SReg::MPIDR_EL1)?;
×
119
        Ok(())
×
120
    }
121

122
    pub fn reset_vcpu(&self, id: u32, vcpu: &mut V::Vcpu) -> Result<()> {
×
123
        vcpu.reset(id == 0)?;
×
124
        Ok(())
×
125
    }
126

127
    pub fn create_ram(&self) -> Result<()> {
×
128
        let mem_size = self.config.mem.size;
×
129
        let memory = &self.memory;
×
130

131
        let low_mem_size = std::cmp::min(mem_size, RAM_32_SIZE);
×
132
        let pages_low = self.create_ram_pages(low_mem_size, c"ram-low")?;
×
133
        memory.add_region(
×
134
            RAM_32_START,
×
135
            Arc::new(MemRegion::with_ram(pages_low, MemRegionType::Ram)),
×
136
        )?;
137

138
        let high_mem_size = mem_size.saturating_sub(RAM_32_SIZE);
×
139
        if high_mem_size > 0 {
×
140
            let pages_high = self.create_ram_pages(high_mem_size, c"ram-high")?;
×
141
            memory.add_region(
×
142
                MEM_64_START,
×
143
                Arc::new(MemRegion::with_ram(pages_high, MemRegionType::Ram)),
×
144
            )?;
145
        }
146

147
        Ok(())
×
148
    }
149

150
    pub fn coco_init(&self, _id: u32) -> Result<()> {
×
151
        Ok(())
×
152
    }
153

154
    pub fn coco_finalize(&self, _id: u32, _vcpus: &VcpuGuard) -> Result<()> {
×
155
        Ok(())
×
156
    }
157

158
    pub fn arch_init(&self) -> Result<()> {
×
159
        match &self.arch.gic {
×
NEW
160
            Gic::V2(v2) => v2.init(),
×
NEW
161
            Gic::V3(v3) => v3.init(),
×
162
        }?;
NEW
163
        match &self.arch.msi {
×
NEW
164
            Some(Msi::V2m(v2m)) => v2m.init(),
×
NEW
165
            Some(Msi::Its(its)) => its.init(),
×
NEW
166
            None => Ok(()),
×
167
        }?;
UNCOV
168
        Ok(())
×
169
    }
170

171
    fn create_chosen_node(&self, init_state: &InitState, root: &mut Node) {
×
172
        let payload = self.payload.read();
×
173
        let Some(payload) = payload.as_ref() else {
×
174
            return;
×
175
        };
176
        if !matches!(payload.exec_type, ExecType::Linux) {
×
177
            return;
×
178
        }
179
        let mut node = Node::default();
×
180
        if let Some(cmd_line) = &payload.cmd_line {
×
181
            node.props
×
182
                .insert("bootargs", PropVal::String(cmd_line.clone()));
×
183
        }
184
        if let Some(initramfs_range) = &init_state.initramfs {
×
185
            node.props.insert(
×
186
                "linux,initrd-start",
187
                PropVal::U32(initramfs_range.start as u32),
×
188
            );
189
            node.props
×
190
                .insert("linux,initrd-end", PropVal::U32(initramfs_range.end as u32));
×
191
        }
192
        node.props.insert(
×
193
            "stdout-path",
194
            PropVal::String(format!("/pl011@{PL011_START:x}")),
×
195
        );
196
        root.nodes.insert("chosen".to_owned(), node);
×
197
    }
198

199
    pub fn create_memory_node(&self, root: &mut Node) {
×
200
        let regions = self.memory.mem_region_entries();
×
201
        for (start, region) in regions {
×
202
            if region.type_ != MemRegionType::Ram {
×
203
                continue;
×
204
            };
205
            let node = Node {
206
                props: HashMap::from([
×
207
                    ("device_type", PropVal::Str("memory")),
208
                    ("reg", PropVal::U64List(vec![start, region.size])),
209
                ]),
210
                nodes: HashMap::new(),
×
211
            };
212
            root.nodes.insert(format!("memory@{start:x}"), node);
×
213
        }
214
    }
215

216
    pub fn create_cpu_nodes(&self, root: &mut Node) {
×
217
        let mpidrs = self.arch.mpidrs.lock();
×
218

219
        let mut cpu_nodes = mpidrs
×
220
            .iter()
221
            .map(|mpidr| {
×
222
                let reg = mpidr & 0xff_00ff_ffff;
×
223
                (
224
                    format!("cpu@{reg}"),
×
225
                    Node {
×
226
                        props: HashMap::from([
×
227
                            ("device_type", PropVal::Str("cpu")),
×
228
                            ("compatible", PropVal::Str("arm,arm-v8")),
×
229
                            ("enable-method", PropVal::Str("psci")),
×
230
                            ("reg", PropVal::U32(reg as u32)),
×
231
                            ("phandle", PropVal::PHandle(reg as u32 | (1 << 16))),
×
232
                        ]),
233
                        nodes: HashMap::new(),
×
234
                    },
235
                )
236
            })
237
            .collect::<HashMap<_, _>>();
238
        let cores = mpidrs
×
239
            .iter()
240
            .map(|mpidr| {
×
241
                let reg = mpidr & 0xff_00ff_ffff;
×
242
                (
243
                    format!("core{reg}"),
×
244
                    Node {
×
245
                        props: HashMap::from([("cpu", PropVal::PHandle(reg as u32 | (1 << 16)))]),
×
246
                        nodes: HashMap::new(),
×
247
                    },
248
                )
249
            })
250
            .collect();
251
        let cpu_map = Node {
252
            props: HashMap::new(),
×
253
            nodes: HashMap::from([(
×
254
                "socket0".to_owned(),
255
                Node {
256
                    props: HashMap::new(),
257
                    nodes: HashMap::from([(
258
                        "cluster0".to_owned(),
259
                        Node {
260
                            props: HashMap::new(),
261
                            nodes: cores,
262
                        },
263
                    )]),
264
                },
265
            )]),
266
        };
267
        cpu_nodes.insert("cpu-map".to_owned(), cpu_map);
×
268
        let cpus = Node {
269
            props: HashMap::from([
×
270
                ("#address-cells", PropVal::U32(1)),
271
                ("#size-cells", PropVal::U32(0)),
272
            ]),
273
            nodes: cpu_nodes,
274
        };
275
        root.nodes.insert("cpus".to_owned(), cpus);
×
276
    }
277

278
    fn create_clock_node(&self, root: &mut Node) {
×
279
        let node = Node {
280
            props: HashMap::from([
×
281
                ("compatible", PropVal::Str("fixed-clock")),
282
                ("clock-frequency", PropVal::U32(24000000)),
283
                ("clock-output-names", PropVal::Str("clk24mhz")),
284
                ("phandle", PropVal::PHandle(PHANDLE_CLOCK)),
285
                ("#clock-cells", PropVal::U32(0)),
286
            ]),
287
            nodes: HashMap::new(),
×
288
        };
289
        root.nodes.insert("apb-pclk".to_owned(), node);
×
290
    }
291

292
    fn create_pl011_node(&self, root: &mut Node) {
×
293
        let pin = 1;
×
294
        let edge_trigger = 1;
×
295
        let spi = 0;
×
296
        let node = Node {
297
            props: HashMap::from([
×
298
                ("compatible", PropVal::Str("arm,primecell\0arm,pl011")),
299
                ("reg", PropVal::U64List(vec![PL011_START, 0x1000])),
300
                ("interrupts", PropVal::U32List(vec![spi, pin, edge_trigger])),
301
                ("clock-names", PropVal::Str("uartclk\0apb_pclk")),
302
                (
303
                    "clocks",
304
                    PropVal::U32List(vec![PHANDLE_CLOCK, PHANDLE_CLOCK]),
305
                ),
306
            ]),
307
            nodes: HashMap::new(),
×
308
        };
309
        root.nodes.insert(format!("pl011@{PL011_START:x}"), node);
×
310
    }
311

312
    // Documentation/devicetree/bindings/timer/arm,arch_timer.yaml
313
    fn create_timer_node(&self, root: &mut Node) {
×
314
        let mut interrupts = vec![];
×
315
        let irq_pins = [13, 14, 11, 10];
×
316
        let ppi = 1;
×
317
        let level_trigger = 4;
×
318
        let cpu_mask = match self.arch.gic {
×
319
            Gic::V2(_) => (1 << self.config.num_cpu) - 1,
×
320
            Gic::V3 { .. } => 0,
×
321
        };
322
        for pin in irq_pins {
×
323
            interrupts.extend([ppi, pin, (cpu_mask << 8) | level_trigger]);
×
324
        }
325
        let node = Node {
326
            props: HashMap::from([
×
327
                ("compatible", PropVal::Str("arm,armv8-timer")),
328
                ("interrupts", PropVal::U32List(interrupts)),
329
                ("always-on", PropVal::Empty),
330
            ]),
331
            nodes: HashMap::new(),
×
332
        };
333
        root.nodes.insert("timer".to_owned(), node);
×
334
    }
335

NEW
336
    fn create_gic_msi_node(&self) -> HashMap<String, Node> {
×
NEW
337
        let Some(msi) = &self.arch.msi else {
×
NEW
338
            return HashMap::new();
×
339
        };
NEW
340
        match msi {
×
NEW
341
            Msi::Its(_) => {
×
342
                let node = Node {
NEW
343
                    props: HashMap::from([
×
344
                        ("compatible", PropVal::Str("arm,gic-v3-its")),
345
                        ("msi-controller", PropVal::Empty),
346
                        ("#msi-cells", PropVal::U32(1)),
347
                        ("reg", PropVal::U64List(vec![GIC_MSI_START, 128 << 10])),
348
                        ("phandle", PropVal::PHandle(PHANDLE_MSI)),
349
                    ]),
NEW
350
                    nodes: HashMap::new(),
×
351
                };
NEW
352
                HashMap::from([(format!("its@{GIC_MSI_START:x}"), node)])
×
353
            }
NEW
354
            Msi::V2m(_) => {
×
355
                let node = Node {
NEW
356
                    props: HashMap::from([
×
357
                        ("compatible", PropVal::Str("arm,gic-v2m-frame")),
358
                        ("msi-controller", PropVal::Empty),
359
                        ("reg", PropVal::U64List(vec![GIC_MSI_START, 64 << 10])),
360
                        ("phandle", PropVal::PHandle(PHANDLE_MSI)),
361
                    ]),
NEW
362
                    nodes: HashMap::new(),
×
363
                };
NEW
364
                HashMap::from([(format!("v2m@{GIC_MSI_START:x}"), node)])
×
365
            }
366
        }
367
    }
368

NEW
369
    fn create_gic_node(&self, root: &mut Node) {
×
NEW
370
        let msi = self.create_gic_msi_node();
×
NEW
371
        let node = match self.arch.gic {
×
372
            // Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
373
            Gic::V2(_) => Node {
NEW
374
                props: HashMap::from([
×
375
                    ("compatible", PropVal::Str("arm,cortex-a15-gic")),
376
                    ("#interrupt-cells", PropVal::U32(3)),
377
                    (
378
                        "reg",
379
                        PropVal::U64List(vec![
380
                            GIC_DIST_START,
381
                            0x1000,
382
                            GIC_V2_CPU_INTERFACE_START,
383
                            0x2000,
384
                        ]),
385
                    ),
386
                    ("phandle", PropVal::U32(PHANDLE_GIC)),
387
                    ("interrupt-controller", PropVal::Empty),
388
                ]),
389
                nodes: msi,
390
            },
391
            // Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
392
            Gic::V3(_) => Node {
NEW
393
                props: HashMap::from([
×
394
                    ("compatible", PropVal::Str("arm,gic-v3")),
395
                    ("#interrupt-cells", PropVal::U32(3)),
396
                    ("#address-cells", PropVal::U32(2)),
397
                    ("#size-cells", PropVal::U32(2)),
398
                    ("interrupt-controller", PropVal::Empty),
399
                    ("ranges", PropVal::Empty),
400
                    (
401
                        "reg",
402
                        PropVal::U64List(vec![
403
                            GIC_DIST_START,
404
                            64 << 10,
405
                            GIC_V3_REDIST_START,
406
                            self.config.num_cpu as u64 * (128 << 10),
407
                        ]),
408
                    ),
409
                    ("phandle", PropVal::U32(PHANDLE_GIC)),
410
                ]),
411
                nodes: msi,
412
            },
413
        };
NEW
414
        root.nodes.insert(format!("intc@{GIC_DIST_START:x}"), node);
×
415
    }
416

417
    // Documentation/devicetree/bindings/arm/psci.yaml
418
    fn create_psci_node(&self, root: &mut Node) {
×
419
        let node = Node {
420
            props: HashMap::from([
×
421
                ("method", PropVal::Str("hvc")),
422
                ("compatible", PropVal::Str("arm,psci-0.2\0arm,psci")),
423
            ]),
424
            nodes: HashMap::new(),
×
425
        };
426
        root.nodes.insert("psci".to_owned(), node);
×
427
    }
428

429
    // https://elinux.org/Device_Tree_Usage#PCI_Host_Bridge
430
    // Documentation/devicetree/bindings/pci/host-generic-pci.yaml
431
    // IEEE Std 1275-1994
432
    fn create_pci_bridge_node(&self, root: &mut Node) {
×
433
        let devices = self.pci_bus.segment.devices.read();
×
434
        let Some(max_bus) = devices.keys().map(|bdf| bdf.bus()).max() else {
×
435
            return;
×
436
        };
437
        let pcie_mmio_64_start = self.config.pcie_mmio_64_start();
×
438
        let prefetchable = 1 << 30;
×
439
        let mem_32 = 0b10 << 24;
×
440
        let mem_64 = 0b11 << 24;
×
441
        let node = Node {
442
            props: HashMap::from([
×
443
                ("compatible", PropVal::Str("pci-host-ecam-generic")),
444
                ("device_type", PropVal::Str("pci")),
445
                ("reg", PropVal::U64List(vec![PCIE_CONFIG_START, 256 << 20])),
446
                ("bus-range", PropVal::U64List(vec![0, max_bus as u64])),
447
                ("#address-cells", PropVal::U32(3)),
448
                ("#size-cells", PropVal::U32(2)),
449
                (
450
                    "ranges",
451
                    PropVal::U32List(vec![
452
                        mem_32 | prefetchable,
453
                        0,
454
                        PCIE_MMIO_32_PREFETCHABLE_START as u32,
455
                        0,
456
                        PCIE_MMIO_32_PREFETCHABLE_START as u32,
457
                        0,
458
                        (PCIE_MMIO_32_PREFETCHABLE_END - PCIE_MMIO_32_PREFETCHABLE_START) as u32,
459
                        mem_32,
460
                        0,
461
                        PCIE_MMIO_32_NON_PREFETCHABLE_START as u32,
462
                        0,
463
                        PCIE_MMIO_32_NON_PREFETCHABLE_START as u32,
464
                        0,
465
                        (PCIE_MMIO_32_NON_PREFETCHABLE_END - PCIE_MMIO_32_NON_PREFETCHABLE_START)
466
                            as u32,
467
                        mem_64 | prefetchable,
468
                        (pcie_mmio_64_start >> 32) as u32,
469
                        pcie_mmio_64_start as u32,
470
                        (pcie_mmio_64_start >> 32) as u32,
471
                        pcie_mmio_64_start as u32,
472
                        (PCIE_MMIO_64_SIZE >> 32) as u32,
473
                        PCIE_MMIO_64_SIZE as u32,
474
                    ]),
475
                ),
476
                (
477
                    "msi-map",
478
                    // Identity map from RID (BDF) to msi-specifier.
479
                    // Documentation/devicetree/bindings/pci/pci-msi.txt
480
                    PropVal::U32List(vec![0, PHANDLE_MSI, 0, 0x10000]),
481
                ),
482
                ("msi-parent", PropVal::PHandle(PHANDLE_MSI)),
483
            ]),
484
            nodes: HashMap::new(),
×
485
        };
486
        root.nodes
×
487
            .insert(format!("pci@{PCIE_CONFIG_START:x}"), node);
×
488
    }
489

490
    pub fn create_firmware_data(&self, init_state: &InitState) -> Result<()> {
×
491
        let mut device_tree = DeviceTree::new();
×
492
        let root = &mut device_tree.root;
×
493
        root.props.insert("#address-cells", PropVal::U32(2));
×
494
        root.props.insert("#size-cells", PropVal::U32(2));
×
495
        root.props.insert("model", PropVal::Str("linux,dummy-virt"));
×
496
        root.props
×
497
            .insert("compatible", PropVal::Str("linux,dummy-virt"));
×
498
        root.props
×
499
            .insert("interrupt-parent", PropVal::PHandle(PHANDLE_GIC));
×
500

501
        self.create_chosen_node(init_state, root);
×
502
        self.create_pl011_node(root);
×
503
        self.create_memory_node(root);
×
504
        self.create_cpu_nodes(root);
×
NEW
505
        self.create_gic_node(root);
×
NEW
506
        if self.arch.msi.is_some() {
×
NEW
507
            self.create_pci_bridge_node(root);
×
508
        }
509
        self.create_clock_node(root);
×
510
        self.create_timer_node(root);
×
511
        self.create_psci_node(root);
×
512
        log::debug!("device tree: {device_tree:#x?}");
×
513
        let blob = device_tree.to_blob();
×
514
        let ram = self.memory.ram_bus();
×
515
        assert!(blob.len() as u64 <= DEVICE_TREE_LIMIT);
×
516
        ram.write_range(DEVICE_TREE_START, blob.len() as u64, &*blob)?;
×
517
        Ok(())
×
518
    }
519
}
520

521
const PHANDLE_GIC: u32 = 1;
522
const PHANDLE_CLOCK: u32 = 2;
523
const PHANDLE_MSI: u32 = 3;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc