The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=014d7082a2398ec39e76b5f7b1f842fc9be6c51e

commit 014d7082a2398ec39e76b5f7b1f842fc9be6c51e
Author:     Jessica Clarke <jrt...@jrtc27.com>
AuthorDate: 2024-02-21 22:57:04 +0000
Commit:     Mark Johnston <ma...@freebsd.org>
CommitDate: 2024-04-10 15:17:56 +0000

    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
---
 usr.sbin/bhyve/aarch64/Makefile.inc       |   1 +
 usr.sbin/bhyve/aarch64/bhyverun_machdep.c |  60 +++++++
 usr.sbin/bhyve/aarch64/fdt.c              |  31 ++++
 usr.sbin/bhyve/aarch64/fdt.h              |   1 +
 usr.sbin/bhyve/rtc_pl031.c                | 279 ++++++++++++++++++++++++++++++
 usr.sbin/bhyve/rtc_pl031.h                |  40 +++++
 6 files changed, 412 insertions(+)

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
@@ -248,6 +248,37 @@ fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int 
intr)
        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)
 {
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 <jrt...@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 <jrt...@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_ */

Reply via email to