Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bhyve/aarch64/fdt.c
107607 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2022 The FreeBSD Foundation
5
*
6
* This software was developed by Andrew Turner under sponsorship from
7
* the FreeBSD Foundation.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*/
30
31
#include <sys/param.h>
32
33
#include <assert.h>
34
#include <errno.h>
35
#include <stdio.h>
36
#include <unistd.h>
37
38
#include <libfdt.h>
39
#include <vmmapi.h>
40
41
#include "config.h"
42
#include "bhyve_machdep.h"
43
#include "bhyverun.h"
44
#include "fdt.h"
45
46
#define SET_PROP_U32(prop, idx, val) \
47
((uint32_t *)(prop))[(idx)] = cpu_to_fdt32(val)
48
#define SET_PROP_U64(prop, idx, val) \
49
((uint64_t *)(prop))[(idx)] = cpu_to_fdt64(val)
50
51
#define GIC_SPI 0
52
#define GIC_PPI 1
53
#define IRQ_TYPE_LEVEL_HIGH 4
54
#define IRQ_TYPE_LEVEL_LOW 8
55
56
#define GIC_FIRST_PPI 16
57
#define GIC_FIRST_SPI 32
58
59
static void *fdtroot;
60
static uint32_t gic_phandle = 0;
61
static uint32_t apb_pclk_phandle;
62
63
static uint32_t
64
assign_phandle(void *fdt)
65
{
66
static uint32_t next_phandle = 1;
67
uint32_t phandle;
68
69
phandle = next_phandle;
70
next_phandle++;
71
fdt_property_u32(fdt, "phandle", phandle);
72
73
return (phandle);
74
}
75
76
static void
77
set_single_reg(void *fdt, uint64_t start, uint64_t len)
78
{
79
void *reg;
80
81
fdt_property_placeholder(fdt, "reg", 2 * sizeof(uint64_t), &reg);
82
SET_PROP_U64(reg, 0, start);
83
SET_PROP_U64(reg, 1, len);
84
}
85
86
static void
87
add_cpu(void *fdt, int cpuid)
88
{
89
char node_name[16];
90
91
snprintf(node_name, sizeof(node_name), "cpu@%d", cpuid);
92
93
fdt_begin_node(fdt, node_name);
94
fdt_property_string(fdt, "device_type", "cpu");
95
fdt_property_string(fdt, "compatible", "arm,armv8");
96
fdt_property_u64(fdt, "reg", cpu_to_mpidr[cpuid]);
97
fdt_property_string(fdt, "enable-method", "psci");
98
fdt_end_node(fdt);
99
}
100
101
static void
102
add_cpus(void *fdt, int ncpu)
103
{
104
int cpuid;
105
106
fdt_begin_node(fdt, "cpus");
107
/* XXX: Needed given the root #address-cells? */
108
fdt_property_u32(fdt, "#address-cells", 2);
109
fdt_property_u32(fdt, "#size-cells", 0);
110
111
for (cpuid = 0; cpuid < ncpu; cpuid++) {
112
add_cpu(fdt, cpuid);
113
}
114
fdt_end_node(fdt);
115
}
116
117
int
118
fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t fdtaddr, vm_size_t fdtsize)
119
{
120
void *fdt;
121
const char *bootargs;
122
123
fdt = paddr_guest2host(ctx, fdtaddr, fdtsize);
124
if (fdt == NULL)
125
return (EFAULT);
126
127
fdt_create(fdt, (int)fdtsize);
128
129
/* Add the memory reserve map (needed even if none is reserved) */
130
fdt_finish_reservemap(fdt);
131
132
/* Create the root node */
133
fdt_begin_node(fdt, "");
134
135
fdt_property_string(fdt, "compatible", "freebsd,bhyve");
136
fdt_property_u32(fdt, "#address-cells", 2);
137
fdt_property_u32(fdt, "#size-cells", 2);
138
139
fdt_begin_node(fdt, "chosen");
140
fdt_property_string(fdt, "stdout-path", "serial0:115200n8");
141
bootargs = get_config_value("fdt.bootargs");
142
if (bootargs != NULL)
143
fdt_property_string(fdt, "bootargs", bootargs);
144
fdt_end_node(fdt);
145
146
fdt_begin_node(fdt, "memory");
147
fdt_property_string(fdt, "device_type", "memory");
148
/* There is no lowmem on arm64. */
149
assert(vm_get_lowmem_size(ctx) == 0);
150
set_single_reg(fdt, vm_get_highmem_base(ctx), vm_get_highmem_size(ctx));
151
fdt_end_node(fdt);
152
153
add_cpus(fdt, ncpu);
154
155
fdt_begin_node(fdt, "psci");
156
fdt_property_string(fdt, "compatible", "arm,psci-1.0");
157
fdt_property_string(fdt, "method", "hvc");
158
fdt_end_node(fdt);
159
160
fdt_begin_node(fdt, "apb-pclk");
161
fdt_property_string(fdt, "compatible", "fixed-clock");
162
fdt_property_string(fdt, "clock-output-names", "clk24mhz");
163
fdt_property_u32(fdt, "#clock-cells", 0);
164
fdt_property_u32(fdt, "clock-frequency", 24000000);
165
apb_pclk_phandle = assign_phandle(fdt);
166
fdt_end_node(fdt);
167
168
/* Finalized by fdt_finalized(). */
169
fdtroot = fdt;
170
171
return (0);
172
}
173
174
void
175
fdt_add_gic(uint64_t dist_base, uint64_t dist_size,
176
uint64_t redist_base, uint64_t redist_size)
177
{
178
char node_name[32];
179
void *fdt, *prop;
180
181
fdt = fdtroot;
182
183
snprintf(node_name, sizeof(node_name), "interrupt-controller@%lx",
184
(unsigned long)dist_base);
185
fdt_begin_node(fdt, node_name);
186
187
gic_phandle = assign_phandle(fdt);
188
fdt_property_string(fdt, "compatible", "arm,gic-v3");
189
fdt_property(fdt, "interrupt-controller", NULL, 0);
190
fdt_property(fdt, "msi-controller", NULL, 0);
191
/* XXX: Needed given the root #address-cells? */
192
fdt_property_u32(fdt, "#address-cells", 2);
193
fdt_property_u32(fdt, "#interrupt-cells", 3);
194
fdt_property_placeholder(fdt, "reg", 4 * sizeof(uint64_t), &prop);
195
/* GICD */
196
SET_PROP_U64(prop, 0, dist_base);
197
SET_PROP_U64(prop, 1, dist_size);
198
/* GICR */
199
SET_PROP_U64(prop, 2, redist_base);
200
SET_PROP_U64(prop, 3, redist_size);
201
202
fdt_property_placeholder(fdt, "mbi-ranges", 2 * sizeof(uint32_t),
203
&prop);
204
SET_PROP_U32(prop, 0, 256);
205
SET_PROP_U32(prop, 1, 64);
206
207
fdt_end_node(fdt);
208
209
fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
210
}
211
212
void
213
fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr)
214
{
215
void *fdt, *interrupts, *prop;
216
char node_name[32];
217
218
assert(gic_phandle != 0);
219
assert(apb_pclk_phandle != 0);
220
assert(intr >= GIC_FIRST_SPI);
221
222
fdt = fdtroot;
223
224
snprintf(node_name, sizeof(node_name), "serial@%lx", uart_base);
225
fdt_begin_node(fdt, node_name);
226
#define UART_COMPAT "arm,pl011\0arm,primecell"
227
fdt_property(fdt, "compatible", UART_COMPAT, sizeof(UART_COMPAT));
228
#undef UART_COMPAT
229
set_single_reg(fdt, uart_base, uart_size);
230
fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
231
fdt_property_placeholder(fdt, "interrupts", 3 * sizeof(uint32_t),
232
&interrupts);
233
SET_PROP_U32(interrupts, 0, GIC_SPI);
234
SET_PROP_U32(interrupts, 1, intr - GIC_FIRST_SPI);
235
SET_PROP_U32(interrupts, 2, IRQ_TYPE_LEVEL_HIGH);
236
fdt_property_placeholder(fdt, "clocks", 2 * sizeof(uint32_t), &prop);
237
SET_PROP_U32(prop, 0, apb_pclk_phandle);
238
SET_PROP_U32(prop, 1, apb_pclk_phandle);
239
#define UART_CLK_NAMES "uartclk\0apb_pclk"
240
fdt_property(fdt, "clock-names", UART_CLK_NAMES,
241
sizeof(UART_CLK_NAMES));
242
#undef UART_CLK_NAMES
243
244
fdt_end_node(fdt);
245
246
snprintf(node_name, sizeof(node_name), "/serial@%lx", uart_base);
247
fdt_begin_node(fdt, "aliases");
248
fdt_property_string(fdt, "serial0", node_name);
249
fdt_end_node(fdt);
250
}
251
252
void
253
fdt_add_rtc(uint64_t rtc_base, uint64_t rtc_size, int intr)
254
{
255
void *fdt, *interrupts, *prop;
256
char node_name[32];
257
258
assert(gic_phandle != 0);
259
assert(apb_pclk_phandle != 0);
260
assert(intr >= GIC_FIRST_SPI);
261
262
fdt = fdtroot;
263
264
snprintf(node_name, sizeof(node_name), "rtc@%lx", rtc_base);
265
fdt_begin_node(fdt, node_name);
266
#define RTC_COMPAT "arm,pl031\0arm,primecell"
267
fdt_property(fdt, "compatible", RTC_COMPAT, sizeof(RTC_COMPAT));
268
#undef RTC_COMPAT
269
set_single_reg(fdt, rtc_base, rtc_size);
270
fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
271
fdt_property_placeholder(fdt, "interrupts", 3 * sizeof(uint32_t),
272
&interrupts);
273
SET_PROP_U32(interrupts, 0, GIC_SPI);
274
SET_PROP_U32(interrupts, 1, intr - GIC_FIRST_SPI);
275
SET_PROP_U32(interrupts, 2, IRQ_TYPE_LEVEL_HIGH);
276
fdt_property_placeholder(fdt, "clocks", sizeof(uint32_t), &prop);
277
SET_PROP_U32(prop, 0, apb_pclk_phandle);
278
fdt_property_string(fdt, "clock-names", "apb_pclk");
279
280
fdt_end_node(fdt);
281
}
282
283
void
284
fdt_add_timer(void)
285
{
286
void *fdt, *interrupts;
287
uint32_t irqs[] = { 13, 14, 11 };
288
289
assert(gic_phandle != 0);
290
291
fdt = fdtroot;
292
293
fdt_begin_node(fdt, "timer");
294
fdt_property_string(fdt, "compatible", "arm,armv8-timer");
295
fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
296
fdt_property_placeholder(fdt, "interrupts", 9 * sizeof(uint32_t),
297
&interrupts);
298
for (u_int i = 0; i < nitems(irqs); i++) {
299
SET_PROP_U32(interrupts, i * 3 + 0, GIC_PPI);
300
SET_PROP_U32(interrupts, i * 3 + 1, irqs[i]);
301
SET_PROP_U32(interrupts, i * 3 + 2, IRQ_TYPE_LEVEL_LOW);
302
}
303
fdt_end_node(fdt);
304
}
305
306
void
307
fdt_add_pcie(int intrs[static 4])
308
{
309
void *fdt, *prop;
310
int slot, pin, intr, i;
311
312
assert(gic_phandle != 0);
313
314
fdt = fdtroot;
315
316
fdt_begin_node(fdt, "pcie@1f0000000");
317
fdt_property_string(fdt, "compatible", "pci-host-ecam-generic");
318
fdt_property_u32(fdt, "#address-cells", 3);
319
fdt_property_u32(fdt, "#size-cells", 2);
320
fdt_property_string(fdt, "device_type", "pci");
321
fdt_property_u64(fdt, "bus-range", (0ul << 32) | 1);
322
set_single_reg(fdt, 0xe0000000, 0x10000000);
323
fdt_property_placeholder(fdt, "ranges",
324
2 * 7 * sizeof(uint32_t), &prop);
325
SET_PROP_U32(prop, 0, 0x01000000);
326
327
SET_PROP_U32(prop, 1, 0);
328
SET_PROP_U32(prop, 2, 0xdf000000);
329
330
SET_PROP_U32(prop, 3, 0);
331
SET_PROP_U32(prop, 4, 0xdf000000);
332
333
SET_PROP_U32(prop, 5, 0);
334
SET_PROP_U32(prop, 6, 0x01000000);
335
336
SET_PROP_U32(prop, 7, 0x02000000);
337
338
SET_PROP_U32(prop, 8, 0);
339
SET_PROP_U32(prop, 9, 0xa0000000);
340
341
SET_PROP_U32(prop, 10, 0);
342
SET_PROP_U32(prop, 11, 0xa0000000);
343
344
SET_PROP_U32(prop, 12, 0);
345
SET_PROP_U32(prop, 13, 0x3f000000);
346
347
fdt_property_placeholder(fdt, "msi-map", 4 * sizeof(uint32_t), &prop);
348
SET_PROP_U32(prop, 0, 0); /* RID base */
349
SET_PROP_U32(prop, 1, gic_phandle); /* MSI parent */
350
SET_PROP_U32(prop, 2, 0); /* MSI base */
351
SET_PROP_U32(prop, 3, 0x10000); /* RID length */
352
fdt_property_u32(fdt, "msi-parent", gic_phandle);
353
354
fdt_property_u32(fdt, "#interrupt-cells", 1);
355
fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
356
357
/*
358
* Describe standard swizzled interrupts routing (pins rotated by one
359
* for each consecutive slot). Must match pci_irq_route().
360
*/
361
fdt_property_placeholder(fdt, "interrupt-map-mask",
362
4 * sizeof(uint32_t), &prop);
363
SET_PROP_U32(prop, 0, 3 << 11);
364
SET_PROP_U32(prop, 1, 0);
365
SET_PROP_U32(prop, 2, 0);
366
SET_PROP_U32(prop, 3, 7);
367
fdt_property_placeholder(fdt, "interrupt-map",
368
160 * sizeof(uint32_t), &prop);
369
for (i = 0; i < 16; ++i) {
370
pin = i % 4;
371
slot = i / 4;
372
intr = intrs[(pin + slot) % 4];
373
assert(intr >= GIC_FIRST_SPI);
374
SET_PROP_U32(prop, 10 * i + 0, slot << 11);
375
SET_PROP_U32(prop, 10 * i + 1, 0);
376
SET_PROP_U32(prop, 10 * i + 2, 0);
377
SET_PROP_U32(prop, 10 * i + 3, pin + 1);
378
SET_PROP_U32(prop, 10 * i + 4, gic_phandle);
379
SET_PROP_U32(prop, 10 * i + 5, 0);
380
SET_PROP_U32(prop, 10 * i + 6, 0);
381
SET_PROP_U32(prop, 10 * i + 7, GIC_SPI);
382
SET_PROP_U32(prop, 10 * i + 8, intr - GIC_FIRST_SPI);
383
SET_PROP_U32(prop, 10 * i + 9, IRQ_TYPE_LEVEL_HIGH);
384
}
385
386
fdt_end_node(fdt);
387
}
388
389
void
390
fdt_finalize(void)
391
{
392
fdt_end_node(fdtroot);
393
394
fdt_finish(fdtroot);
395
}
396
397