diff options
Diffstat (limited to 'usr.sbin/bhyve/aarch64/fdt.c')
| -rw-r--r-- | usr.sbin/bhyve/aarch64/fdt.c | 395 | 
1 files changed, 395 insertions, 0 deletions
diff --git a/usr.sbin/bhyve/aarch64/fdt.c b/usr.sbin/bhyve/aarch64/fdt.c new file mode 100644 index 000000000000..8832a99a6cf1 --- /dev/null +++ b/usr.sbin/bhyve/aarch64/fdt.c @@ -0,0 +1,395 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 The FreeBSD Foundation + * + * This software was developed by Andrew Turner under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include <libfdt.h> +#include <vmmapi.h> + +#include "config.h" +#include "bhyve_machdep.h" +#include "bhyverun.h" +#include "fdt.h" + +#define	SET_PROP_U32(prop, idx, val)	\ +    ((uint32_t *)(prop))[(idx)] = cpu_to_fdt32(val) +#define	SET_PROP_U64(prop, idx, val)	\ +    ((uint64_t *)(prop))[(idx)] = cpu_to_fdt64(val) + +#define	GIC_SPI			0 +#define	GIC_PPI			1 +#define	IRQ_TYPE_LEVEL_HIGH	4 +#define	IRQ_TYPE_LEVEL_LOW	8 + +#define	GIC_FIRST_PPI		16 +#define	GIC_FIRST_SPI		32 + +static void *fdtroot; +static uint32_t gic_phandle = 0; +static uint32_t apb_pclk_phandle; + +static uint32_t +assign_phandle(void *fdt) +{ +	static uint32_t next_phandle = 1; +	uint32_t phandle; + +	phandle = next_phandle; +	next_phandle++; +	fdt_property_u32(fdt, "phandle", phandle); + +	return (phandle); +} + +static void +set_single_reg(void *fdt, uint64_t start, uint64_t len) +{ +	void *reg; + +	fdt_property_placeholder(fdt, "reg", 2 * sizeof(uint64_t), ®); +	SET_PROP_U64(reg, 0, start); +	SET_PROP_U64(reg, 1, len); +} + +static void +add_cpu(void *fdt, int cpuid) +{ +	char node_name[16]; + +	snprintf(node_name, sizeof(node_name), "cpu@%d", cpuid); + +	fdt_begin_node(fdt, node_name); +	fdt_property_string(fdt, "device_type", "cpu"); +	fdt_property_string(fdt, "compatible", "arm,armv8"); +	fdt_property_u64(fdt, "reg", cpu_to_mpidr[cpuid]); +	fdt_property_string(fdt, "enable-method", "psci"); +	fdt_end_node(fdt); +} + +static void +add_cpus(void *fdt, int ncpu) +{ +	int cpuid; + +	fdt_begin_node(fdt, "cpus"); +	/* XXX: Needed given the root #address-cells? */ +	fdt_property_u32(fdt, "#address-cells", 2); +	fdt_property_u32(fdt, "#size-cells", 0); + +	for (cpuid = 0; cpuid < ncpu; cpuid++) { +		add_cpu(fdt, cpuid); +	} +	fdt_end_node(fdt); +} + +int +fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t fdtaddr, vm_size_t fdtsize) +{ +	void *fdt; +	const char *bootargs; + +	fdt = paddr_guest2host(ctx, fdtaddr, fdtsize); +	if (fdt == NULL) +		return (EFAULT); + +	fdt_create(fdt, (int)fdtsize); + +	/* Add the memory reserve map (needed even if none is reserved) */ +	fdt_finish_reservemap(fdt); + +	/* Create the root node */ +	fdt_begin_node(fdt, ""); + +	fdt_property_string(fdt, "compatible", "freebsd,bhyve"); +	fdt_property_u32(fdt, "#address-cells", 2); +	fdt_property_u32(fdt, "#size-cells", 2); + +	fdt_begin_node(fdt, "chosen"); +	fdt_property_string(fdt, "stdout-path", "serial0:115200n8"); +	bootargs = get_config_value("fdt.bootargs"); +	if (bootargs != NULL) +		fdt_property_string(fdt, "bootargs", bootargs); +	fdt_end_node(fdt); + +	fdt_begin_node(fdt, "memory"); +	fdt_property_string(fdt, "device_type", "memory"); +	/* There is no lowmem on arm64. */ +	assert(vm_get_lowmem_size(ctx) == 0); +	set_single_reg(fdt, vm_get_highmem_base(ctx), vm_get_highmem_size(ctx)); +	fdt_end_node(fdt); + +	add_cpus(fdt, ncpu); + +	fdt_begin_node(fdt, "psci"); +	fdt_property_string(fdt, "compatible", "arm,psci-1.0"); +	fdt_property_string(fdt, "method", "hvc"); +	fdt_end_node(fdt); + +	fdt_begin_node(fdt, "apb-pclk"); +	fdt_property_string(fdt, "compatible", "fixed-clock"); +	fdt_property_string(fdt, "clock-output-names", "clk24mhz"); +	fdt_property_u32(fdt, "#clock-cells", 0); +	fdt_property_u32(fdt, "clock-frequency", 24000000); +	apb_pclk_phandle = assign_phandle(fdt); +	fdt_end_node(fdt); + +	/* Finalized by fdt_finalized(). */ +	fdtroot = fdt; + +	return (0); +} + +void +fdt_add_gic(uint64_t dist_base, uint64_t dist_size, +    uint64_t redist_base, uint64_t redist_size) +{ +	char node_name[32]; +	void *fdt, *prop; + +	fdt = fdtroot; + +	snprintf(node_name, sizeof(node_name), "interrupt-controller@%lx", +	    (unsigned long)dist_base); +	fdt_begin_node(fdt, node_name); + +	gic_phandle = assign_phandle(fdt); +	fdt_property_string(fdt, "compatible", "arm,gic-v3"); +	fdt_property(fdt, "interrupt-controller", NULL, 0); +	fdt_property(fdt, "msi-controller", NULL, 0); +	/* XXX: Needed given the root #address-cells? */ +	fdt_property_u32(fdt, "#address-cells", 2); +	fdt_property_u32(fdt, "#interrupt-cells", 3); +	fdt_property_placeholder(fdt, "reg", 4 * sizeof(uint64_t), &prop); +	/* GICD */ +	SET_PROP_U64(prop, 0, dist_base); +	SET_PROP_U64(prop, 1, dist_size); +	/* GICR */ +	SET_PROP_U64(prop, 2, redist_base); +	SET_PROP_U64(prop, 3, redist_size); + +	fdt_property_placeholder(fdt, "mbi-ranges", 2 * sizeof(uint32_t), +	    &prop); +	SET_PROP_U32(prop, 0, 256); +	SET_PROP_U32(prop, 1, 64); + +	fdt_end_node(fdt); + +	fdt_property_u32(fdt, "interrupt-parent", gic_phandle); +} + +void +fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr) +{ +	void *fdt, *interrupts, *prop; +	char node_name[32]; + +	assert(gic_phandle != 0); +	assert(apb_pclk_phandle != 0); +	assert(intr >= GIC_FIRST_SPI); + +	fdt = fdtroot; + +	snprintf(node_name, sizeof(node_name), "serial@%lx", uart_base); +	fdt_begin_node(fdt, node_name); +#define	UART_COMPAT	"arm,pl011\0arm,primecell" +	fdt_property(fdt, "compatible", UART_COMPAT, sizeof(UART_COMPAT)); +#undef UART_COMPAT +	set_single_reg(fdt, uart_base, uart_size); +	fdt_property_u32(fdt, "interrupt-parent", gic_phandle); +	fdt_property_placeholder(fdt, "interrupts", 3 * sizeof(uint32_t), +	    &interrupts); +	SET_PROP_U32(interrupts, 0, GIC_SPI); +	SET_PROP_U32(interrupts, 1, intr - GIC_FIRST_SPI); +	SET_PROP_U32(interrupts, 2, IRQ_TYPE_LEVEL_HIGH); +	fdt_property_placeholder(fdt, "clocks", 2 * sizeof(uint32_t), &prop); +	SET_PROP_U32(prop, 0, apb_pclk_phandle); +	SET_PROP_U32(prop, 1, apb_pclk_phandle); +#define	UART_CLK_NAMES	"uartclk\0apb_pclk" +	fdt_property(fdt, "clock-names", UART_CLK_NAMES, +	    sizeof(UART_CLK_NAMES)); +#undef UART_CLK_NAMES + +	fdt_end_node(fdt); + +	snprintf(node_name, sizeof(node_name), "/serial@%lx", uart_base); +	fdt_begin_node(fdt, "aliases"); +	fdt_property_string(fdt, "serial0", node_name); +	fdt_end_node(fdt); +} + +void +fdt_add_rtc(uint64_t rtc_base, uint64_t rtc_size, int intr) +{ +	void *fdt, *interrupts, *prop; +	char node_name[32]; + +	assert(gic_phandle != 0); +	assert(apb_pclk_phandle != 0); +	assert(intr >= GIC_FIRST_SPI); + +	fdt = fdtroot; + +	snprintf(node_name, sizeof(node_name), "rtc@%lx", rtc_base); +	fdt_begin_node(fdt, node_name); +#define	RTC_COMPAT	"arm,pl031\0arm,primecell" +	fdt_property(fdt, "compatible", RTC_COMPAT, sizeof(RTC_COMPAT)); +#undef RTC_COMPAT +	set_single_reg(fdt, rtc_base, rtc_size); +	fdt_property_u32(fdt, "interrupt-parent", gic_phandle); +	fdt_property_placeholder(fdt, "interrupts", 3 * sizeof(uint32_t), +	    &interrupts); +	SET_PROP_U32(interrupts, 0, GIC_SPI); +	SET_PROP_U32(interrupts, 1, intr - GIC_FIRST_SPI); +	SET_PROP_U32(interrupts, 2, IRQ_TYPE_LEVEL_HIGH); +	fdt_property_placeholder(fdt, "clocks", sizeof(uint32_t), &prop); +	SET_PROP_U32(prop, 0, apb_pclk_phandle); +	fdt_property_string(fdt, "clock-names", "apb_pclk"); + +	fdt_end_node(fdt); +} + +void +fdt_add_timer(void) +{ +	void *fdt, *interrupts; +	uint32_t irqs[] = { 13, 14, 11 }; + +	assert(gic_phandle != 0); + +	fdt = fdtroot; + +	fdt_begin_node(fdt, "timer"); +	fdt_property_string(fdt, "compatible", "arm,armv8-timer"); +	fdt_property_u32(fdt, "interrupt-parent", gic_phandle); +	fdt_property_placeholder(fdt, "interrupts", 9 * sizeof(uint32_t), +	    &interrupts); +	for (u_int i = 0; i < nitems(irqs); i++) { +		SET_PROP_U32(interrupts, i * 3 + 0, GIC_PPI); +		SET_PROP_U32(interrupts, i * 3 + 1, irqs[i]); +		SET_PROP_U32(interrupts, i * 3 + 2, IRQ_TYPE_LEVEL_LOW); +	} +	fdt_end_node(fdt); +} + +void +fdt_add_pcie(int intrs[static 4]) +{ +	void *fdt, *prop; +	int slot, pin, intr, i; + +	assert(gic_phandle != 0); + +	fdt = fdtroot; + +	fdt_begin_node(fdt, "pcie@1f0000000"); +	fdt_property_string(fdt, "compatible", "pci-host-ecam-generic"); +	fdt_property_u32(fdt, "#address-cells", 3); +	fdt_property_u32(fdt, "#size-cells", 2); +	fdt_property_string(fdt, "device_type", "pci"); +	fdt_property_u64(fdt, "bus-range", (0ul << 32) | 1); +	set_single_reg(fdt, 0xe0000000, 0x10000000); +	fdt_property_placeholder(fdt, "ranges", +	    2 * 7 * sizeof(uint32_t), &prop); +	SET_PROP_U32(prop, 0, 0x01000000); + +	SET_PROP_U32(prop, 1, 0); +	SET_PROP_U32(prop, 2, 0xdf000000); + +	SET_PROP_U32(prop, 3, 0); +	SET_PROP_U32(prop, 4, 0xdf000000); + +	SET_PROP_U32(prop, 5, 0); +	SET_PROP_U32(prop, 6, 0x01000000); + +	SET_PROP_U32(prop, 7, 0x02000000); + +	SET_PROP_U32(prop, 8, 0); +	SET_PROP_U32(prop, 9, 0xa0000000); + +	SET_PROP_U32(prop, 10, 0); +	SET_PROP_U32(prop, 11, 0xa0000000); + +	SET_PROP_U32(prop, 12, 0); +	SET_PROP_U32(prop, 13, 0x3f000000); + +	fdt_property_placeholder(fdt, "msi-map", 4 * sizeof(uint32_t), &prop); +	SET_PROP_U32(prop, 0, 0);		/* RID base */ +	SET_PROP_U32(prop, 1, gic_phandle);	/* MSI parent */ +	SET_PROP_U32(prop, 2, 0);		/* MSI base */ +	SET_PROP_U32(prop, 3, 0x10000);		/* RID length */ +	fdt_property_u32(fdt, "msi-parent", gic_phandle); + +	fdt_property_u32(fdt, "#interrupt-cells", 1); +	fdt_property_u32(fdt, "interrupt-parent", gic_phandle); + +	/* +	 * Describe standard swizzled interrupts routing (pins rotated by one +	 * for each consecutive slot). Must match pci_irq_route(). +	 */ +	fdt_property_placeholder(fdt, "interrupt-map-mask", +	    4 * sizeof(uint32_t), &prop); +	SET_PROP_U32(prop, 0, 3 << 11); +	SET_PROP_U32(prop, 1, 0); +	SET_PROP_U32(prop, 2, 0); +	SET_PROP_U32(prop, 3, 7); +	fdt_property_placeholder(fdt, "interrupt-map", +	    160 * sizeof(uint32_t), &prop); +	for (i = 0; i < 16; ++i) { +		pin = i % 4; +		slot = i / 4; +		intr = intrs[(pin + slot) % 4]; +		assert(intr >= GIC_FIRST_SPI); +		SET_PROP_U32(prop, 10 * i + 0, slot << 11); +		SET_PROP_U32(prop, 10 * i + 1, 0); +		SET_PROP_U32(prop, 10 * i + 2, 0); +		SET_PROP_U32(prop, 10 * i + 3, pin + 1); +		SET_PROP_U32(prop, 10 * i + 4, gic_phandle); +		SET_PROP_U32(prop, 10 * i + 5, 0); +		SET_PROP_U32(prop, 10 * i + 6, 0); +		SET_PROP_U32(prop, 10 * i + 7, GIC_SPI); +		SET_PROP_U32(prop, 10 * i + 8, intr - GIC_FIRST_SPI); +		SET_PROP_U32(prop, 10 * i + 9, IRQ_TYPE_LEVEL_HIGH); +	} + +	fdt_end_node(fdt); +} + +void +fdt_finalize(void) +{ +	fdt_end_node(fdtroot); + +	fdt_finish(fdtroot); +}  | 
