aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorJessica Clarke <jrtc27@jrtc27.com>2024-02-21 22:57:04 +0000
committerMark Johnston <markj@FreeBSD.org>2024-04-10 15:17:56 +0000
commit014d7082a2398ec39e76b5f7b1f842fc9be6c51e (patch)
treeda79c49347f417f5dfd246359eb35b8763409b80 /usr.sbin
parente3bd5730ca925cd74dece9c52bcc10237b058546 (diff)
downloadsrc-014d7082a2398ec39e76b5f7b1f842fc9be6c51e.tar.gz
src-014d7082a2398ec39e76b5f7b1f842fc9be6c51e.zip
bhyve: Implement a PL031 RTC on arm64
Unlike amd64's, this RTC is implemented entirely in userspace. This is the same RTC as is provided by QEMU's virt machine. Reviewed by: jhb MFC after: 2 weeks Obtained from: CheriBSD
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bhyve/aarch64/Makefile.inc1
-rw-r--r--usr.sbin/bhyve/aarch64/bhyverun_machdep.c60
-rw-r--r--usr.sbin/bhyve/aarch64/fdt.c31
-rw-r--r--usr.sbin/bhyve/aarch64/fdt.h1
-rw-r--r--usr.sbin/bhyve/rtc_pl031.c279
-rw-r--r--usr.sbin/bhyve/rtc_pl031.h40
6 files changed, 412 insertions, 0 deletions
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,5 +1,6 @@
SRCS+= \
fdt.c \
+ rtc_pl031.c \
uart_pl011.c
.PATH: ${BHYVE_SYSDIR}/sys/arm64/vmm
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
@@ -48,6 +48,7 @@
#include "mem.h"
#include "pci_emul.h"
#include "pci_irq.h"
+#include "rtc_pl031.h"
#include "uart_emul.h"
/* Start of mem + 1M */
@@ -58,6 +59,9 @@
#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
@@ -287,6 +291,60 @@ init_mmio_uart(struct vmctx *ctx)
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)
{
@@ -328,6 +386,8 @@ bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp)
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);
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
@@ -249,6 +249,37 @@ 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, *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;
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
@@ -42,6 +42,7 @@ void fdt_add_gic(uint64_t dist_base, uint64_t dist_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 <jrtc27@FreeBSD.org>
+ *
+ * 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 <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <time.h>
+
+#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/rtc_pl031.h b/usr.sbin/bhyve/rtc_pl031.h
new file mode 100644
index 000000000000..8e4ef685908a
--- /dev/null
+++ b/usr.sbin/bhyve/rtc_pl031.h
@@ -0,0 +1,40 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jessica Clarke <jrtc27@FreeBSD.org>
+ *
+ * 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 _RTC_PL031_H_
+#define _RTC_PL031_H_
+
+struct rtc_pl031_softc;
+typedef void (*rtc_pl031_intr_func_t)(void *arg);
+
+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 /* _RTC_PL031_H_ */