diff --git a/usr.sbin/bhyve/aarch64/Makefile.inc b/usr.sbin/bhyve/aarch64/Makefile.inc index 2c7a3cac105e..e2ea4414ca19 100644 --- a/usr.sbin/bhyve/aarch64/Makefile.inc +++ b/usr.sbin/bhyve/aarch64/Makefile.inc @@ -1,8 +1,9 @@ SRCS+= \ fdt.c \ + rtc_pl031.c \ uart_pl011.c .PATH: ${BHYVE_SYSDIR}/sys/arm64/vmm SRCS+= vmm_instruction_emul.c BHYVE_FDT_SUPPORT= diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c index 2aa7d2d9b4fd..a5fd3f054706 100644 --- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c +++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c @@ -1,349 +1,409 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2011 NetApp, Inc. * All rights reserved. * * 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 NETAPP, INC ``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 NETAPP, INC 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 #include #include #include #include #include #include #include #include #include #include #include #include "bhyverun.h" #include "config.h" #include "debug.h" #include "fdt.h" #include "mem.h" #include "pci_emul.h" #include "pci_irq.h" +#include "rtc_pl031.h" #include "uart_emul.h" /* Start of mem + 1M */ #define FDT_BASE 0x100000 #define FDT_SIZE (64 * 1024) /* Start of lowmem + 64K */ #define UART_MMIO_BASE 0x10000 #define UART_MMIO_SIZE 0x1000 #define UART_INTR 32 +#define RTC_MMIO_BASE 0x11000 +#define RTC_MMIO_SIZE 0x1000 +#define RTC_INTR 33 #define GIC_DIST_BASE 0x2f000000 #define GIC_DIST_SIZE 0x10000 #define GIC_REDIST_BASE 0x2f100000 #define GIC_REDIST_SIZE(ncpu) ((ncpu) * 2 * PAGE_SIZE_64K) #define PCIE_INTA 34 #define PCIE_INTB 35 #define PCIE_INTC 36 #define PCIE_INTD 37 void bhyve_init_config(void) { init_config(); /* Set default values prior to option parsing. */ set_config_bool("acpi_tables", false); set_config_bool("acpi_tables_in_memory", false); set_config_value("memory.size", "256M"); } void bhyve_usage(int code) { const char *progname; progname = getprogname(); fprintf(stderr, "Usage: %s [-CDHhSW]\n" " %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n" " %*s [-k config_file] [-m mem] [-o var=value]\n" " %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n" " -C: include guest memory in core file\n" " -c: number of CPUs and/or topology specification\n" " -D: destroy on power-off\n" " -h: help\n" " -k: key=value flat config file\n" " -m: memory size\n" " -o: set config 'var' to 'value'\n" " -p: pin 'vcpu' to 'hostcpu'\n" " -S: guest memory cannot be swapped\n" " -s: PCI slot config\n" " -U: UUID\n" " -W: force virtio to use single-vector MSI\n", progname, (int)strlen(progname), "", (int)strlen(progname), "", (int)strlen(progname), ""); exit(code); } void bhyve_optparse(int argc, char **argv) { const char *optstr; int c; optstr = "hCDSWk:f:o:p:c:s:m:U:"; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'c': if (bhyve_topology_parse(optarg) != 0) { errx(EX_USAGE, "invalid cpu topology '%s'", optarg); } break; case 'C': set_config_bool("memory.guest_in_core", true); break; case 'D': set_config_bool("destroy_on_poweroff", true); break; case 'k': bhyve_parse_simple_config_file(optarg); break; case 'm': set_config_value("memory.size", optarg); break; case 'o': if (!bhyve_parse_config_option(optarg)) { errx(EX_USAGE, "invalid configuration option '%s'", optarg); } break; case 'p': if (bhyve_pincpu_parse(optarg) != 0) { errx(EX_USAGE, "invalid vcpu pinning configuration '%s'", optarg); } break; case 's': if (strncmp(optarg, "help", strlen(optarg)) == 0) { pci_print_supported_devices(); exit(0); } else if (pci_parse_slot(optarg) != 0) exit(4); else break; case 'S': set_config_bool("memory.wired", true); break; case 'U': set_config_value("uuid", optarg); break; case 'W': set_config_bool("virtio_msix", false); break; case 'h': bhyve_usage(0); default: bhyve_usage(1); } } } void bhyve_init_vcpu(struct vcpu *vcpu __unused) { } void bhyve_start_vcpu(struct vcpu *vcpu, bool bsp __unused) { fbsdrun_addcpu(vcpu_id(vcpu)); } /* * Load the specified boot code at the beginning of high memory. */ static void load_bootrom(struct vmctx *ctx, const char *path, uint64_t *elrp) { struct stat sb; void *data, *gptr; vm_paddr_t loadaddr; off_t size; int fd; fd = open(path, O_RDONLY); if (fd < 0) err(1, "open(%s)", path); if (fstat(fd, &sb) != 0) err(1, "fstat(%s)", path); size = sb.st_size; loadaddr = vm_get_highmem_base(ctx); gptr = vm_map_gpa(ctx, loadaddr, round_page(size)); data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) err(1, "mmap(%s)", path); (void)close(fd); memcpy(gptr, data, size); if (munmap(data, size) != 0) err(1, "munmap(%s)", path); *elrp = loadaddr; } static void mmio_uart_intr_assert(void *arg) { struct vmctx *ctx = arg; vm_assert_irq(ctx, UART_INTR); } static void mmio_uart_intr_deassert(void *arg) { struct vmctx *ctx = arg; vm_deassert_irq(ctx, UART_INTR); } static int mmio_uart_mem_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) { struct uart_pl011_softc *sc = arg1; long reg; reg = (addr - arg2) >> 2; if (dir == MEM_F_WRITE) uart_pl011_write(sc, reg, *val); else *val = uart_pl011_read(sc, reg); return (0); } static bool init_mmio_uart(struct vmctx *ctx) { struct uart_pl011_softc *sc; struct mem_range mr; const char *path; int error; path = get_config_value("console"); if (path == NULL) return (false); sc = uart_pl011_init(mmio_uart_intr_assert, mmio_uart_intr_deassert, ctx); if (uart_pl011_tty_open(sc, path) != 0) { EPRINTLN("Unable to initialize backend '%s' for mmio uart", path); assert(0); } bzero(&mr, sizeof(struct mem_range)); mr.name = "uart"; mr.base = UART_MMIO_BASE; mr.size = UART_MMIO_SIZE; mr.flags = MEM_F_RW; mr.handler = mmio_uart_mem_handler; mr.arg1 = sc; mr.arg2 = mr.base; error = register_mem(&mr); assert(error == 0); return (true); } +static void +mmio_rtc_intr_assert(void *arg) +{ + struct vmctx *ctx = arg; + + vm_assert_irq(ctx, RTC_INTR); +} + +static void +mmio_rtc_intr_deassert(void *arg) +{ + struct vmctx *ctx = arg; + + vm_deassert_irq(ctx, RTC_INTR); +} + +static int +mmio_rtc_mem_handler(struct vcpu *vcpu __unused, int dir, + uint64_t addr, int size __unused, uint64_t *val, void *arg1, long arg2) +{ + struct rtc_pl031_softc *sc = arg1; + long reg; + + reg = addr - arg2; + if (dir == MEM_F_WRITE) + rtc_pl031_write(sc, reg, *val); + else + *val = rtc_pl031_read(sc, reg); + + return (0); +} + +static void +init_mmio_rtc(struct vmctx *ctx) +{ + struct rtc_pl031_softc *sc; + struct mem_range mr; + int error; + + sc = rtc_pl031_init(mmio_rtc_intr_assert, mmio_rtc_intr_deassert, + ctx); + + bzero(&mr, sizeof(struct mem_range)); + mr.name = "rtc"; + mr.base = RTC_MMIO_BASE; + mr.size = RTC_MMIO_SIZE; + mr.flags = MEM_F_RW; + mr.handler = mmio_rtc_mem_handler; + mr.arg1 = sc; + mr.arg2 = mr.base; + error = register_mem(&mr); + assert(error == 0); +} + static vm_paddr_t fdt_gpa(struct vmctx *ctx) { return (vm_get_highmem_base(ctx) + FDT_BASE); } int bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp) { const char *bootrom; uint64_t elr; int error; int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD}; bootrom = get_config_value("bootrom"); if (bootrom == NULL) { warnx("no bootrom specified"); return (ENOENT); } load_bootrom(ctx, bootrom, &elr); error = vm_set_register(bsp, VM_REG_GUEST_PC, elr); if (error != 0) { warn("vm_set_register(GUEST_PC)"); return (error); } error = fdt_init(ctx, guest_ncpus, fdt_gpa(ctx), FDT_SIZE); if (error != 0) return (error); fdt_add_gic(GIC_DIST_BASE, GIC_DIST_SIZE, GIC_REDIST_BASE, GIC_REDIST_SIZE(guest_ncpus)); error = vm_attach_vgic(ctx, GIC_DIST_BASE, GIC_DIST_SIZE, GIC_REDIST_BASE, GIC_REDIST_SIZE(guest_ncpus)); if (error != 0) { warn("vm_attach_vgic()"); return (error); } if (init_mmio_uart(ctx)) fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR); + init_mmio_rtc(ctx); + fdt_add_rtc(RTC_MMIO_BASE, RTC_MMIO_SIZE, RTC_INTR); fdt_add_timer(); pci_irq_init(pcie_intrs); fdt_add_pcie(pcie_intrs); return (0); } int bhyve_init_platform_late(struct vmctx *ctx, struct vcpu *bsp __unused) { int error; fdt_finalize(); error = vm_set_register(bsp, VM_REG_GUEST_X0, fdt_gpa(ctx)); assert(error == 0); return (0); } diff --git a/usr.sbin/bhyve/aarch64/fdt.c b/usr.sbin/bhyve/aarch64/fdt.c index e8c959a65f3b..3fb97a40c241 100644 --- a/usr.sbin/bhyve/aarch64/fdt.c +++ b/usr.sbin/bhyve/aarch64/fdt.c @@ -1,363 +1,394 @@ /*- * 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 #include #include #include #include #include #include #include "config.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", 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); } diff --git a/usr.sbin/bhyve/aarch64/fdt.h b/usr.sbin/bhyve/aarch64/fdt.h index 6534266173d0..c19d19d34a46 100644 --- a/usr.sbin/bhyve/aarch64/fdt.h +++ b/usr.sbin/bhyve/aarch64/fdt.h @@ -1,47 +1,48 @@ /*- * 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. */ #ifndef _FDT_H_ #define _FDT_H_ #include struct vmctx; int fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t addrp, vm_size_t size); void fdt_add_gic(uint64_t dist_base, uint64_t dist_size, uint64_t redist_base, uint64_t redist_size); void fdt_add_timer(void); void fdt_add_pcie(int intrs[static 4]); void fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr); +void fdt_add_rtc(uint64_t rtc_base, uint64_t rtc_size, int intr); void fdt_finalize(void); #endif /* _FDT_H_ */ diff --git a/usr.sbin/bhyve/rtc_pl031.c b/usr.sbin/bhyve/rtc_pl031.c new file mode 100644 index 000000000000..e334de6f92bb --- /dev/null +++ b/usr.sbin/bhyve/rtc_pl031.c @@ -0,0 +1,279 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Jessica Clarke + * + * 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 + +#include +#include +#include +#include +#include + +#include "config.h" +#include "mevent.h" +#include "rtc_pl031.h" + +#define RTCDR 0x000 +#define RTCMR 0x004 +#define RTCLR 0x008 +#define RTCCR 0x00C +#define RTCIMSC 0x010 +#define RTCRIS 0x014 +#define RTCMIS 0x018 +#define RTCICR 0x01C + +#define RTCPeriphID0 0xFE0 +#define RTCPeriphID1 0xFE4 +#define RTCPeriphID2 0xFE8 +#define RTCPeriphID3 0xFEC +#define _RTCPeriphID_VAL 0x00141031 +#define RTCPeriphID_VAL(_n) ((_RTCPeriphID_VAL >> (8 * (_n))) & 0xff) + +#define RTCCellID0 0xFF0 +#define RTCCellID1 0xFF4 +#define RTCCellID2 0xFF8 +#define RTCCellID3 0xFFC +#define _RTCCellID_VAL 0xb105f00d +#define RTCCellID_VAL(_n) ((_RTCCellID_VAL >> (8 * (_n))) & 0xff) + +struct rtc_pl031_softc { + pthread_mutex_t mtx; + + time_t last_tick; + uint32_t dr; + uint32_t mr; + uint32_t lr; + uint8_t imsc; + uint8_t ris; + uint8_t prev_mis; + + struct mevent *mevp; + + void *arg; + rtc_pl031_intr_func_t intr_assert; + rtc_pl031_intr_func_t intr_deassert; +}; + +static void rtc_pl031_callback(int fd, enum ev_type type, void *param); + +/* + * Returns the current RTC time as number of seconds since 00:00:00 Jan 1, 1970 + */ +static time_t +rtc_pl031_time(void) +{ + struct tm tm; + time_t t; + + time(&t); + if (get_config_bool_default("rtc.use_localtime", false)) { + localtime_r(&t, &tm); + t = timegm(&tm); + } + return (t); +} + +static void +rtc_pl031_update_mis(struct rtc_pl031_softc *sc) +{ + uint8_t mis; + + mis = sc->ris & sc->imsc; + if (mis == sc->prev_mis) + return; + + sc->prev_mis = mis; + if (mis) + (*sc->intr_assert)(sc->arg); + else + (*sc->intr_deassert)(sc->arg); +} + +static uint64_t +rtc_pl031_next_match_ticks(struct rtc_pl031_softc *sc) +{ + uint32_t ticks; + + ticks = sc->mr - sc->dr; + if (ticks == 0) + return ((uint64_t)1 << 32); + + return (ticks); +} + +static int +rtc_pl031_next_timer_msecs(struct rtc_pl031_softc *sc) +{ + uint64_t ticks; + + ticks = rtc_pl031_next_match_ticks(sc); + return (MIN(ticks * 1000, INT_MAX)); +} + +static void +rtc_pl031_update_timer(struct rtc_pl031_softc *sc) +{ + mevent_timer_update(sc->mevp, rtc_pl031_next_timer_msecs(sc)); +} + +static void +rtc_pl031_tick(struct rtc_pl031_softc *sc, bool from_timer) +{ + bool match; + time_t now, ticks; + + now = rtc_pl031_time(); + ticks = now - sc->last_tick; + match = ticks >= 0 && + (uint64_t)ticks >= rtc_pl031_next_match_ticks(sc); + sc->dr += ticks; + sc->last_tick = now; + + if (match) { + sc->ris = 1; + rtc_pl031_update_mis(sc); + } + + if (match || from_timer || ticks < 0) + rtc_pl031_update_timer(sc); +} + +static void +rtc_pl031_callback(int fd __unused, enum ev_type type __unused, void *param) +{ + struct rtc_pl031_softc *sc = param; + + pthread_mutex_lock(&sc->mtx); + rtc_pl031_tick(sc, true); + pthread_mutex_unlock(&sc->mtx); +} + +void +rtc_pl031_write(struct rtc_pl031_softc *sc, int offset, uint32_t value) +{ + pthread_mutex_lock(&sc->mtx); + rtc_pl031_tick(sc, false); + switch (offset) { + case RTCMR: + sc->mr = value; + rtc_pl031_update_timer(sc); + break; + case RTCLR: + sc->lr = value; + sc->dr = sc->lr; + rtc_pl031_update_timer(sc); + break; + case RTCIMSC: + sc->imsc = value & 1; + rtc_pl031_update_mis(sc); + break; + case RTCICR: + sc->ris &= ~value; + rtc_pl031_update_mis(sc); + break; + default: + /* Ignore writes to read-only/unassigned/ID registers */ + break; + } + pthread_mutex_unlock(&sc->mtx); +} + +uint32_t +rtc_pl031_read(struct rtc_pl031_softc *sc, int offset) +{ + uint32_t reg; + + pthread_mutex_lock(&sc->mtx); + rtc_pl031_tick(sc, false); + switch (offset) { + case RTCDR: + reg = sc->dr; + break; + case RTCMR: + reg = sc->mr; + break; + case RTCLR: + reg = sc->lr; + break; + case RTCCR: + /* RTC enabled from reset */ + reg = 1; + break; + case RTCIMSC: + reg = sc->imsc; + break; + case RTCRIS: + reg = sc->ris; + break; + case RTCMIS: + reg = sc->ris & sc->imsc; + break; + case RTCPeriphID0: + case RTCPeriphID1: + case RTCPeriphID2: + case RTCPeriphID3: + reg = RTCPeriphID_VAL(offset - RTCPeriphID0); + break; + case RTCCellID0: + case RTCCellID1: + case RTCCellID2: + case RTCCellID3: + reg = RTCCellID_VAL(offset - RTCCellID0); + break; + default: + /* Return 0 in reads from unasigned registers */ + reg = 0; + break; + } + pthread_mutex_unlock(&sc->mtx); + + return (reg); +} + +struct rtc_pl031_softc * +rtc_pl031_init(rtc_pl031_intr_func_t intr_assert, + rtc_pl031_intr_func_t intr_deassert, void *arg) +{ + struct rtc_pl031_softc *sc; + time_t now; + + sc = calloc(1, sizeof(struct rtc_pl031_softc)); + + pthread_mutex_init(&sc->mtx, NULL); + + now = rtc_pl031_time(); + sc->dr = now; + sc->last_tick = now; + sc->arg = arg; + sc->intr_assert = intr_assert; + sc->intr_deassert = intr_deassert; + + sc->mevp = mevent_add(rtc_pl031_next_timer_msecs(sc), EVF_TIMER, + rtc_pl031_callback, sc); + + return (sc); +} diff --git a/usr.sbin/bhyve/aarch64/fdt.h b/usr.sbin/bhyve/rtc_pl031.h similarity index 69% copy from usr.sbin/bhyve/aarch64/fdt.h copy to usr.sbin/bhyve/rtc_pl031.h index 6534266173d0..8e4ef685908a 100644 --- a/usr.sbin/bhyve/aarch64/fdt.h +++ b/usr.sbin/bhyve/rtc_pl031.h @@ -1,47 +1,40 @@ /*- * 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. + * Copyright (c) 2024 Jessica Clarke * * 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. */ -#ifndef _FDT_H_ -#define _FDT_H_ - -#include +#ifndef _RTC_PL031_H_ +#define _RTC_PL031_H_ -struct vmctx; +struct rtc_pl031_softc; +typedef void (*rtc_pl031_intr_func_t)(void *arg); -int fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t addrp, - vm_size_t size); -void fdt_add_gic(uint64_t dist_base, uint64_t dist_size, - uint64_t redist_base, uint64_t redist_size); -void fdt_add_timer(void); -void fdt_add_pcie(int intrs[static 4]); -void fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr); -void fdt_finalize(void); +struct rtc_pl031_softc *rtc_pl031_init(rtc_pl031_intr_func_t intr_assert, + rtc_pl031_intr_func_t intr_deassert, void *arg); +void rtc_pl031_write(struct rtc_pl031_softc *sc, int offset, + uint32_t value); +uint32_t rtc_pl031_read(struct rtc_pl031_softc *sc, int offset); -#endif /* _FDT_H_ */ +#endif /* _RTC_PL031_H_ */