-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboard-config.xml
More file actions
182 lines (172 loc) · 12.9 KB
/
Copy pathboard-config.xml
File metadata and controls
182 lines (172 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<?xml version="1.0" encoding="UTF-8"?>
<!--
board-config.xml — single source of truth for "which optional features are
armed in this build" AND "what the guest's DTB needs to advertise for them
to actually do anything", generated together by gen_config.py so the two
can never drift apart.
WHY THIS EXISTS: every opt-in feature in this tree needs TWO things kept in
lockstep by a human today — a build-time CFLAGS define (or a runtime
dbgmon toggle) AND a matching device-tree node, added by hand with fdtput.
This session hit that exact mismatch twice: vcpu2 was armed once with no
cpu@2 node in the live DTB (only a separate, never-merged bananapi-2cpu.dtb
had it), and vcpu1 was built with no cpu@1 node at all — in both cases the
code was live and correct but the guest could never ask for the core
because nothing told it to. gen_config.py reads THIS file once and emits
both config.mk (Makefile CFLAGS) and the DTB edits (idempotent fdtput
calls, safe to re-run), so "armed in the build" and "advertised in the
DTB" are the same statement instead of two things to remember separately.
Edit this file, run `python3 gen_config.py`, then `make dbg` — that is the
whole workflow. Nothing here is consulted directly by the Makefile or by
el2 C code; gen_config.py is the only reader, and config.mk (generated,
gitignored) is the only thing the Makefile actually includes.
-->
<bzdk-config board="bananapi-m64" target="dbg">
<!-- Real, hardware-determined core roles (see microkernel/CLAUDE.md's
"Core layout"). Not settable here — documentation only, so a reader
of this file sees the whole picture in one place; gen_config.py does
not act on this element at all. -->
<cpu-roles>
<cpu id="0" role="guest-vcpu0" note="runs the guest, takes its traps"/>
<cpu id="1" role="debug-watchdog" note="EMAC/dbgmon/GDB/watchdog kick; survives guest wedges. NOT reassignable without accepting vcpu1's tradeoff below."/>
<cpu id="2" role="guest-vcpu2" note="a real third guest vCPU, hardware-proven 2026-08-27: hw.ncpu=3, kern.smp.cpus=3, cpu2:rendezvous nonzero in vmstat -i, three parallel spinners each completing identical work. Trades away vblk_async.c's async eMMC offload -- smp.c's cpu==2 dispatch tries vcpu2_run() first and falls back only while this feature is off. Getting here took three fixes: the missing vgic_init()+daifclr, CPU2's own CNTP tick (so vtimer_mask_watchdog() can run there), and own_cpu_mask() in vgicd.c, which had hardcoded the CPU0/CPU1 pair and was silently stripping CPU2 out of every cross-core SGI target list."/>
<cpu id="3" role="idle-or-guest-vcpu3" note="idle unless vcpu3 below is armed (a 4th FreeBSD vCPU). NEVER armed on hardware yet. The three fixes that made CPU2 work are all generic now -- own_cpu_mask() reads every core's own dbg_vcpuN gate rather than a hardcoded pair -- so CPU3 is expected to behave the same, but expected is not measured. Mutually exclusive at LINK TIME with the `dual` target's Zephyr-on-CPU3 guest via vcpu3.h's bzdos_cpu3_owner marker: not buildable together regardless of what this file says."/>
</cpu-roles>
<!-- Build-time feature gates. `enabled` maps to -D<MACRO>=1 in config.mk;
gen_config.py derives <MACRO> from the feature's own `define`
attribute so this file names features once, not per call site. -->
<!-- `mkvar` is the EXACT Makefile variable this feature controls (VCPU2,
HV_HDMI, ...) — gen_config.py writes `<mkvar> = 0|1` into config.mk
verbatim, no name-guessing, so it always matches whatever the
Makefile's own `ifeq ($(<mkvar>),1)` block checks. -->
<features>
<feature name="hv_hdmi" mkvar="HV_HDMI" enabled="true"/>
<feature name="hdmi_1080p" mkvar="HDMI_MODE_1080P" enabled="true"/>
<!-- EXPERIMENTAL, see vcpu2.h/vcpu1.h before flipping either to true:
both trade an independent crash-recovery witness for guest compute.
needs-dtb-cpu is what ties the build flag to the DTB edit below —
gen_config.py refuses to enable the flag without also being able to
write the matching cpu@N node (or, symmetrically, warns if a cpu@N
node exists in the DTB but its feature is disabled here, since that
means a core the guest can ask for but the build won't hand over). -->
<feature name="vcpu2" mkvar="VCPU2" enabled="true" needs-dtb-cpu="2"/>
<feature name="vcpu1" mkvar="VCPU1" enabled="true" needs-dtb-cpu="1"/>
<!-- NEW, board-UNVERIFIED (2026-08-27): vcpu3.c ports vcpu2.c's fixed
entry sequence (vgic_init() + EL2 IRQ/FIQ unmask included from the
start) to CPU3. Left disabled here deliberately, unlike vcpu1/vcpu2
above: those two have SOME hardware track record (vcpu1 4h09m+
continuous, vcpu2 at least reaching guest boot before this session's
fix); vcpu3 has none yet. Arm with `make dbg VCPU3=1` or flip this to
"true" once vcpu2's fix is confirmed on real hardware, see
RELEASE-0.0.2.md/PROGRESS.md for that result once it exists. -->
<feature name="vcpu3" mkvar="VCPU3" enabled="true" needs-dtb-cpu="3"/>
<!-- guest_dram_2g: hand the guest the board's whole 2 GiB instead of the low
GiB, giving up snapshot/restore, whose mirror IS that high GiB. Turned on
2026-08-27 for a measured reason, not a preference: the board is the
project's own build host and a single Mesa NIR generator peaks at 648 MB
against ~850 MB of usermem, so Mesa could not be built at any -j.
dtb-memory-size makes gen_config.py widen /memory in the same breath:
the flag and its DTB half must never be set apart. Mutually exclusive
with the `dual` build (Zephyr's slice sits in that high GiB); that is a
compile error in stage2_zephyr.h, not a footnote. Snapshots need this
GiB only because snapshot.c copies guest DRAM eagerly; a copy-on-write
implementation would cost ~2 MiB of page tables per GiB instead and
retire this trade entirely. -->
<feature name="guest_dram_2g" mkvar="GUEST_DRAM_2G" enabled="true" dtb-memory-size="0x78000000"/>
<!-- Guest hardware-enablement features (2026-08-27 survey of the "dark"
A64 peripheral list, docs/guest-hw-enablement.md). Each flips ONLY a
`status` property on real silicon node(s) already present in the
upstream-derived DTB tree (bananapi-min.dtb) — verified against a
scratch `dtc -I dtb -O dts` decompile before this file was written,
these nodes already carry correct clocks/resets/regulator/pinctrl
references copied from sun50i-a64.dtsi/sun50i-a64-bananapi-m64.dts,
so "enable" means exactly one `status = "disabled"` to `"okay"` edit
per node, nothing added. `dtb-only="true"` means gen_config.py does
NOT require an `mkvar` and writes nothing to config.mk — there is no
EL2 C code gated by any of these, only what the guest's own DTB
advertises, so a build-time flag would be pure noise. See
`<soc-nodes>` below for exactly which node path(s) each one owns.
ALL default OFF, per this file's existing "one feature per device,
one board cycle at a time" idiom. -->
<feature name="guest_usb_host1" dtb-only="true" enabled="true"/>
<feature name="guest_wifi_sdio" dtb-only="true" enabled="false"/>
<feature name="guest_audio_codec" dtb-only="true" enabled="true"/>
<feature name="guest_mipi_dsi" dtb-only="true" enabled="false"/>
<feature name="guest_mipi_csi" dtb-only="true" enabled="false"/>
<feature name="guest_ir" dtb-only="true" enabled="true"/>
</features>
<!-- Deliberately NOT a feature: a "guest_usb_host0" for usb@1c1a000/
usb@1c1a400 (EHCI0/OHCI0). Both sit on PHY0 ("pmu0"), the SAME silicon
usb@1c19000 (MUSB OTG) uses — and MUSB is this hypervisor's OWN
CDC-ACM debug/break-glass gadget (usbacm.c, driven live from CPU1).
Handing the guest's generic-ehci/generic-ohci + aw_usbphy drivers
write access to PHY0's registers is a real hardware race against that
channel, not a software one, and untested — the very thing R0/R2 in
SESSION-RULES.md exist to protect. gen_config.py's FORBIDDEN_DTB_NODES
enforces this structurally (a soc-node entry naming either path is a
hard validate() error, not just a comment to notice), matching this
file's own "note you have to notice is not a safeguard" lesson from
reconcile_drift() above. Only usb@1c1b000/usb@1c1b400 (EHCI1/OHCI1,
PHY1/"pmu1", electrically independent) are exposed, as
guest_usb_host1 below. -->
<!-- Also deliberately NOT wired: dai@1c22c00 (the codec-i2s DAI) under
guest_audio_codec. It is the ONLY node in the audio chain with a
`dmas` property (dmas = <&dma-controller 0x0f ...>), and
dma-controller@1c02000 is the SoC's general-purpose memory-to-memory
DMA engine — no SMMU gates it, and docs/dma-bypass-stage2.md
documents it today as "provably unused" precisely because every
consumer that could arm it is disabled. Enabling the DAI re-arms it
with zero stage-2 mitigation (that doc's own draft denial patch,
STAGE2_DENY_DMA_CTRL, is written but explicitly NOT wired in yet, and
needs board-validated boot-through before it can be). guest_audio_codec
below therefore gets the guest's a33_codec/sun50i_a64_acodec drivers to
ATTACH (register-level control only) but not to produce sound —
correct and expected, not a bug in this config. -->
<!-- guest_mipi_dsi / guest_mipi_csi: landed with NO known FreeBSD driver
for either allwinner,sun50i-a64-mipi-dsi or allwinner,sun50i-a64-csi
anywhere in the releng/15.1-based tree searched for this survey
(docs/guest-hw-enablement.md) — flipping either `status` is safe
(neither node carries a `dmas` property or shares a PHY with anything
else this project depends on) but will NOT attach a new device; it
exists so the DTB half is ready the day a driver appears, not as a
feature expected to do anything today. -->
<!-- virtio-mmio devices inside the 2 MiB trapped block at 0x0A000000
(vblk_emmc.h's "MMIO window" comment — the canonical explanation of
why this block exists and why adding a device here needs no stage2.c
change). `always` devices are linked into every dbg build regardless
of any feature flag (vblk/vnet — the guest simply won't find them
without a DTB node, same as everything else here); devices with
`enabled-by` only get their DTB node written when that feature is on.
spi values must stay inside the documented free gap 0x68..0x73
(docs/virtio-blk-dtb.md) and must not repeat — gen_config.py checks
both before writing anything. -->
<devices>
<virtio-mmio name="vblk_emmc" base="0x0A000000" spi="105" device-id="2" always="true"/>
<virtio-mmio name="vnet_emac" base="0x0A001000" spi="106" device-id="1" always="true"/>
<virtio-mmio name="scanout" base="0x0A002000" spi="none" device-id="none" enabled-by="hv_hdmi" no-dtb-node="true"/>
<virtio-mmio name="vinput" base="0x0A003000" spi="107" device-id="18" always="true"/>
<virtio-mmio name="vblk_sd" base="0x0A004000" spi="108" device-id="2" always="true"/>
</devices>
<!-- Real A64 silicon nodes whose `status` gen_config.py flips to track one
guest-hardware-enablement feature above. `okay` when the feature is
enabled, `disabled` (self-healed back, same both-directions philosophy
as reconcile_drift() above) when it is not. Multiple <soc-node> entries
under the same feature are a cluster that only does anything together
— gen_config.py does not special-case that, it just flips every listed
path to the same status. usb@1c1a000/usb@1c1a400 (EHCI0/OHCI0, shares
the MUSB debug channel's PHY0) and dai@1c22c00 (re-arms the
provably-unused system DMA controller) are deliberately absent — see
the two comments above and gen_config.py's FORBIDDEN_DTB_NODES, which
makes listing either here a hard validate() error, not just a review
note. -->
<soc-nodes>
<soc-node feature="guest_usb_host1" path="/soc/usb@1c1b000"/> <!-- EHCI1, PHY1 -->
<soc-node feature="guest_usb_host1" path="/soc/usb@1c1b400"/> <!-- OHCI1, PHY1 -->
<soc-node feature="guest_wifi_sdio" path="/soc/mmc@1c10000"/> <!-- SMHC1; wifi@1 (brcm,bcm4329-fmac) child has no status of its own, follows the parent -->
<soc-node feature="guest_audio_codec" path="/soc/codec@1c22e00"/> <!-- digital codec, the "codec" side -->
<soc-node feature="guest_audio_codec" path="/soc/codec-analog@1f015c0"/>
<soc-node feature="guest_mipi_dsi" path="/soc/dsi@1ca0000"/>
<soc-node feature="guest_mipi_dsi" path="/soc/d-phy@1ca1000"/>
<soc-node feature="guest_mipi_csi" path="/soc/csi@1cb0000"/>
<soc-node feature="guest_ir" path="/soc/ir@1f02000"/>
</soc-nodes>
</bzdk-config>