aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.h1
-rw-r--r--sys/dev/gpio/acpi_gpiobus.c1
-rw-r--r--sys/dev/gpio/gpiobus.c79
-rw-r--r--sys/dev/gpio/gpiobus_internal.h (renamed from sys/dev/sound/midi/sequencer.h)74
-rw-r--r--sys/dev/gpio/gpiobusvar.h17
-rw-r--r--sys/dev/gpio/gpiopps.c2
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c1
-rw-r--r--sys/dev/gpio/pl061.c12
-rw-r--r--sys/dev/gpio/pl061.h1
-rw-r--r--sys/dev/gpio/pl061_acpi.c15
-rw-r--r--sys/dev/gpio/pl061_fdt.c15
-rw-r--r--sys/dev/gpio/qoriq_gpio.c11
-rw-r--r--sys/dev/hwt/hwt.c242
-rw-r--r--sys/dev/hwt/hwt_backend.c289
-rw-r--r--sys/dev/hwt/hwt_backend.h87
-rw-r--r--sys/dev/hwt/hwt_config.c108
-rw-r--r--sys/dev/hwt/hwt_config.h36
-rw-r--r--sys/dev/hwt/hwt_context.c201
-rw-r--r--sys/dev/hwt/hwt_context.h86
-rw-r--r--sys/dev/hwt/hwt_contexthash.c134
-rw-r--r--sys/dev/hwt/hwt_contexthash.h42
-rw-r--r--sys/dev/hwt/hwt_cpu.c115
-rw-r--r--sys/dev/hwt/hwt_cpu.h45
-rw-r--r--sys/dev/hwt/hwt_hook.c323
-rw-r--r--sys/dev/hwt/hwt_hook.h56
-rw-r--r--sys/dev/hwt/hwt_intr.h33
-rw-r--r--sys/dev/hwt/hwt_ioctl.c445
-rw-r--r--sys/dev/hwt/hwt_ioctl.h35
-rw-r--r--sys/dev/hwt/hwt_owner.c157
-rw-r--r--sys/dev/hwt/hwt_owner.h45
-rw-r--r--sys/dev/hwt/hwt_ownerhash.c141
-rw-r--r--sys/dev/hwt/hwt_ownerhash.h42
-rw-r--r--sys/dev/hwt/hwt_record.c302
-rw-r--r--sys/dev/hwt/hwt_record.h47
-rw-r--r--sys/dev/hwt/hwt_thread.c162
-rw-r--r--sys/dev/hwt/hwt_thread.h64
-rw-r--r--sys/dev/hwt/hwt_vm.c503
-rw-r--r--sys/dev/hwt/hwt_vm.h47
-rw-r--r--sys/dev/ichiic/ig4_pci.c12
-rw-r--r--sys/dev/iicbus/gpio/tca64xx.c3
-rw-r--r--sys/dev/md/md.c25
-rw-r--r--sys/dev/mem/memutil.c19
-rw-r--r--sys/dev/nvmf/host/nvmf.c119
-rw-r--r--sys/dev/nvmf/host/nvmf_var.h6
-rw-r--r--sys/dev/nvmf/nvmf.h11
-rw-r--r--sys/dev/random/random_harvestq.c126
-rw-r--r--sys/dev/random/random_harvestq.h11
-rw-r--r--sys/dev/random/randomdev.c6
-rw-r--r--sys/dev/regulator/regulator_fixed.c8
-rw-r--r--sys/dev/sound/midi/midi.c670
-rw-r--r--sys/dev/sound/midi/midi.h7
-rw-r--r--sys/dev/sound/midi/mpu401.c44
-rw-r--r--sys/dev/sound/midi/mpu_if.m11
-rw-r--r--sys/dev/sound/midi/sequencer.c2107
-rw-r--r--sys/dev/sound/midi/synth_if.m312
-rw-r--r--sys/dev/sound/pcm/mixer.c4
-rw-r--r--sys/dev/sound/pcm/sndstat.c7
-rw-r--r--sys/dev/sound/pcm/sound.h8
-rw-r--r--sys/dev/vmm/vmm_dev.c1
59 files changed, 4144 insertions, 3389 deletions
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.h b/sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.h
index 1d844a67c928..c4c9e789cf3e 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.h
@@ -39,6 +39,7 @@ struct auxiliary_device_id {
char name[AUXILIARY_NAME_SIZE];
uint64_t driver_data;
};
+#define MODULE_DEVICE_TABLE_BUS_auxiliary(_bus, _table)
struct auxiliary_device {
struct device dev;
diff --git a/sys/dev/gpio/acpi_gpiobus.c b/sys/dev/gpio/acpi_gpiobus.c
index 2987af634866..f9468e0deda0 100644
--- a/sys/dev/gpio/acpi_gpiobus.c
+++ b/sys/dev/gpio/acpi_gpiobus.c
@@ -36,6 +36,7 @@
#include <dev/gpio/gpiobusvar.h>
#include <dev/gpio/acpi_gpiobusvar.h>
+#include <dev/gpio/gpiobus_internal.h>
#include "gpiobus_if.h"
diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c
index 2e2618805e7b..ab7f13177969 100644
--- a/sys/dev/gpio/gpiobus.c
+++ b/sys/dev/gpio/gpiobus.c
@@ -39,6 +39,7 @@
#include <sys/sbuf.h>
#include <dev/gpio/gpiobusvar.h>
+#include <dev/gpio/gpiobus_internal.h>
#include "gpiobus_if.h"
@@ -213,20 +214,40 @@ gpio_pin_is_active(gpio_pin_t pin, bool *active)
return (0);
}
+/*
+ * Note that this function should only
+ * be used in cases where a pre-existing
+ * gpiobus_pin structure exists. In most
+ * cases, the gpio_pin_get_by_* functions
+ * suffice.
+ */
+int
+gpio_pin_acquire(gpio_pin_t gpio)
+{
+ device_t busdev;
+
+ KASSERT(gpio != NULL, ("GPIO pin is NULL."));
+ KASSERT(gpio->dev != NULL, ("GPIO pin device is NULL."));
+
+ busdev = GPIO_GET_BUS(gpio->dev);
+ if (busdev == NULL)
+ return (ENXIO);
+
+ return (gpiobus_acquire_pin(busdev, gpio->pin));
+}
+
void
gpio_pin_release(gpio_pin_t gpio)
{
device_t busdev;
- if (gpio == NULL)
- return;
-
+ KASSERT(gpio != NULL, ("GPIO pin is NULL."));
KASSERT(gpio->dev != NULL, ("GPIO pin device is NULL."));
busdev = GPIO_GET_BUS(gpio->dev);
- if (busdev != NULL)
- gpiobus_release_pin(busdev, gpio->pin);
+ KASSERT(busdev != NULL, ("gpiobus dev is NULL."));
+ gpiobus_release_pin(busdev, gpio->pin);
free(gpio, M_DEVBUF);
}
@@ -293,7 +314,7 @@ gpiobus_print_pins(struct gpiobus_ivar *devi, struct sbuf *sb)
}
device_t
-gpiobus_attach_bus(device_t dev)
+gpiobus_add_bus(device_t dev)
{
device_t busdev;
@@ -307,8 +328,24 @@ gpiobus_attach_bus(device_t dev)
#ifdef FDT
ofw_gpiobus_register_provider(dev);
#endif
- bus_attach_children(dev);
+ return (busdev);
+}
+
+/*
+ * Attach a gpiobus child.
+ * Note that the controller is expected
+ * to be fully initialized at this point.
+ */
+device_t
+gpiobus_attach_bus(device_t dev)
+{
+ device_t busdev;
+ busdev = gpiobus_add_bus(dev);
+ if (busdev == NULL)
+ return (NULL);
+
+ bus_attach_children(dev);
return (busdev);
}
@@ -385,14 +422,13 @@ gpiobus_acquire_pin(device_t bus, uint32_t pin)
sc = device_get_softc(bus);
/* Consistency check. */
if (pin >= sc->sc_npins) {
- device_printf(bus,
- "invalid pin %d, max: %d\n", pin, sc->sc_npins - 1);
- return (-1);
+ panic("%s: invalid pin %d, max: %d",
+ device_get_nameunit(bus), pin, sc->sc_npins - 1);
}
/* Mark pin as mapped and give warning if it's already mapped. */
if (sc->sc_pins[pin].mapped) {
device_printf(bus, "warning: pin %d is already mapped\n", pin);
- return (-1);
+ return (EBUSY);
}
sc->sc_pins[pin].mapped = 1;
@@ -400,7 +436,7 @@ gpiobus_acquire_pin(device_t bus, uint32_t pin)
}
/* Release mapped pin */
-int
+void
gpiobus_release_pin(device_t bus, uint32_t pin)
{
struct gpiobus_softc *sc;
@@ -408,19 +444,15 @@ gpiobus_release_pin(device_t bus, uint32_t pin)
sc = device_get_softc(bus);
/* Consistency check. */
if (pin >= sc->sc_npins) {
- device_printf(bus,
- "invalid pin %d, max=%d\n",
- pin, sc->sc_npins - 1);
- return (-1);
+ panic("%s: invalid pin %d, max: %d",
+ device_get_nameunit(bus), pin, sc->sc_npins - 1);
}
- if (!sc->sc_pins[pin].mapped) {
- device_printf(bus, "pin %d is not mapped\n", pin);
- return (-1);
- }
- sc->sc_pins[pin].mapped = 0;
+ if (!sc->sc_pins[pin].mapped)
+ panic("%s: pin %d is not mapped", device_get_nameunit(bus),
+ pin);
- return (0);
+ sc->sc_pins[pin].mapped = 0;
}
static int
@@ -435,8 +467,7 @@ gpiobus_acquire_child_pins(device_t dev, device_t child)
device_printf(child, "cannot acquire pin %d\n",
devi->pins[i]);
while (--i >= 0) {
- (void)gpiobus_release_pin(dev,
- devi->pins[i]);
+ gpiobus_release_pin(dev, devi->pins[i]);
}
gpiobus_free_ivars(devi);
return (EBUSY);
diff --git a/sys/dev/sound/midi/sequencer.h b/sys/dev/gpio/gpiobus_internal.h
index 22ea0ae6c1b6..de3f57663132 100644
--- a/sys/dev/sound/midi/sequencer.h
+++ b/sys/dev/gpio/gpiobus_internal.h
@@ -1,8 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2003 Mathew Kanner
- * Copyright (c) 1999 Seigo Tanimura
+ * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,65 +24,24 @@
* 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 file for the midi sequence driver.
- */
-
-#ifndef _SEQUENCER_H_
-#define _SEQUENCER_H_
-
-#define NSEQ_MAX 16
+#ifndef __GPIOBUS_INTERNAL_H__
+#define __GPIOBUS_INTERNAL_H__
/*
- * many variables should be reduced to a range. Here define a macro
+ * Functions shared between gpiobus and other bus classes that derive from it;
+ * these should not be called directly by other drivers.
*/
-
-#define RANGE(var, low, high) (var) = \
-((var)<(low)?(low) : (var)>(high)?(high) : (var))
-
-#ifdef _KERNEL
-
-void seq_timer(void *arg);
-
-SYSCTL_DECL(_hw_midi_seq);
-
-extern int seq_debug;
-
-#define SEQ_DEBUG(y, x) \
- do { \
- if (seq_debug >= y) { \
- (x); \
- } \
- } while (0)
-
-SYSCTL_DECL(_hw_midi);
-
-#endif /* _KERNEL */
-
-#define SYNTHPROP_MIDI 1
-#define SYNTHPROP_SYNTH 2
-#define SYNTHPROP_RX 4
-#define SYNTHPROP_TX 8
-
-struct _midi_cmdtab {
- int cmd;
- char *name;
-};
-typedef struct _midi_cmdtab midi_cmdtab;
-extern midi_cmdtab cmdtab_seqevent[];
-extern midi_cmdtab cmdtab_seqioctl[];
-extern midi_cmdtab cmdtab_timer[];
-extern midi_cmdtab cmdtab_seqcv[];
-extern midi_cmdtab cmdtab_seqccmn[];
-
-char *midi_cmdname(int cmd, midi_cmdtab * tab);
-
-enum {
- MORE,
- TIMERARMED,
- QUEUEFULL
-};
-
+int gpiobus_attach(device_t);
+int gpiobus_detach(device_t);
+int gpiobus_init_softc(device_t);
+int gpiobus_alloc_ivars(struct gpiobus_ivar *);
+void gpiobus_free_ivars(struct gpiobus_ivar *);
+int gpiobus_read_ivar(device_t, device_t, int, uintptr_t *);
+int gpiobus_acquire_pin(device_t, uint32_t);
+void gpiobus_release_pin(device_t, uint32_t);
+
+extern driver_t gpiobus_driver;
#endif
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h
index 74783e112f89..7f504236a774 100644
--- a/sys/dev/gpio/gpiobusvar.h
+++ b/sys/dev/gpio/gpiobusvar.h
@@ -156,6 +156,8 @@ int gpio_pin_get_by_bus_pinnum(device_t _bus, uint32_t _pinnum, gpio_pin_t *_gp)
/* Acquire a pin by child and index (used by direct children of gpiobus). */
int gpio_pin_get_by_child_index(device_t _child, uint32_t _idx, gpio_pin_t *_gp);
+/* Acquire a pin from an existing gpio_pin_t. */
+int gpio_pin_acquire(gpio_pin_t gpio);
/* Release a pin acquired via any gpio_pin_get_xxx() function. */
void gpio_pin_release(gpio_pin_t gpio);
@@ -167,22 +169,9 @@ int gpio_pin_setflags(gpio_pin_t pin, uint32_t flags);
struct resource *gpio_alloc_intr_resource(device_t consumer_dev, int *rid,
u_int alloc_flags, gpio_pin_t pin, uint32_t intr_mode);
-/*
- * Functions shared between gpiobus and other bus classes that derive from it;
- * these should not be called directly by other drivers.
- */
int gpio_check_flags(uint32_t, uint32_t);
+device_t gpiobus_add_bus(device_t);
device_t gpiobus_attach_bus(device_t);
int gpiobus_detach_bus(device_t);
-int gpiobus_attach(device_t);
-int gpiobus_detach(device_t);
-int gpiobus_init_softc(device_t);
-int gpiobus_alloc_ivars(struct gpiobus_ivar *);
-void gpiobus_free_ivars(struct gpiobus_ivar *);
-int gpiobus_read_ivar(device_t, device_t, int, uintptr_t *);
-int gpiobus_acquire_pin(device_t, uint32_t);
-int gpiobus_release_pin(device_t, uint32_t);
-
-extern driver_t gpiobus_driver;
#endif /* __GPIOBUS_H__ */
diff --git a/sys/dev/gpio/gpiopps.c b/sys/dev/gpio/gpiopps.c
index bb8afa5e062c..82620a50a798 100644
--- a/sys/dev/gpio/gpiopps.c
+++ b/sys/dev/gpio/gpiopps.c
@@ -160,7 +160,7 @@ gpiopps_detach(device_t dev)
if (sc->ires != NULL)
bus_release_resource(dev, SYS_RES_IRQ, sc->irid, sc->ires);
if (sc->gpin != NULL)
- gpiobus_release_pin(GPIO_GET_BUS(sc->gpin->dev), sc->gpin->pin);
+ gpio_pin_release(sc->gpin);
return (0);
}
diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c
index 32dc5b55e698..fc5fb03d6824 100644
--- a/sys/dev/gpio/ofw_gpiobus.c
+++ b/sys/dev/gpio/ofw_gpiobus.c
@@ -36,6 +36,7 @@
#include <sys/module.h>
#include <dev/gpio/gpiobusvar.h>
+#include <dev/gpio/gpiobus_internal.h>
#include <dev/ofw/ofw_bus.h>
#include "gpiobus_if.h"
diff --git a/sys/dev/gpio/pl061.c b/sys/dev/gpio/pl061.c
index cc39790322b6..87d4310a6396 100644
--- a/sys/dev/gpio/pl061.c
+++ b/sys/dev/gpio/pl061.c
@@ -487,14 +487,21 @@ pl061_attach(device_t dev)
}
}
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN);
+
+ if (sc->sc_xref != 0 && !intr_pic_register(dev, sc->sc_xref)) {
+ device_printf(dev, "couldn't register PIC\n");
+ PL061_LOCK_DESTROY(sc);
+ goto free_isrc;
+ }
+
sc->sc_busdev = gpiobus_attach_bus(dev);
if (sc->sc_busdev == NULL) {
device_printf(dev, "couldn't attach gpio bus\n");
+ PL061_LOCK_DESTROY(sc);
goto free_isrc;
}
- mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN);
-
return (0);
free_isrc:
@@ -503,6 +510,7 @@ free_isrc:
* for (irq = 0; irq < PL061_NUM_GPIO; irq++)
* intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
*/
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdlr);
bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
sc->sc_irq_res);
free_pic:
diff --git a/sys/dev/gpio/pl061.h b/sys/dev/gpio/pl061.h
index 809a1168493d..d9fe23e502b1 100644
--- a/sys/dev/gpio/pl061.h
+++ b/sys/dev/gpio/pl061.h
@@ -46,6 +46,7 @@ struct pl061_softc {
struct resource *sc_mem_res;
struct resource *sc_irq_res;
void *sc_irq_hdlr;
+ intptr_t sc_xref;
int sc_mem_rid;
int sc_irq_rid;
struct pl061_pin_irqsrc sc_isrcs[PL061_NUM_GPIO];
diff --git a/sys/dev/gpio/pl061_acpi.c b/sys/dev/gpio/pl061_acpi.c
index f5885025083e..8e9921261e4e 100644
--- a/sys/dev/gpio/pl061_acpi.c
+++ b/sys/dev/gpio/pl061_acpi.c
@@ -67,19 +67,12 @@ pl061_acpi_probe(device_t dev)
static int
pl061_acpi_attach(device_t dev)
{
- int error;
+ struct pl061_softc *sc;
- error = pl061_attach(dev);
- if (error != 0)
- return (error);
+ sc = device_get_softc(dev);
+ sc->sc_xref = ACPI_GPIO_XREF;
- if (!intr_pic_register(dev, ACPI_GPIO_XREF)) {
- device_printf(dev, "couldn't register PIC\n");
- pl061_detach(dev);
- error = ENXIO;
- }
-
- return (error);
+ return (pl061_attach(dev));
}
static device_method_t pl061_acpi_methods[] = {
diff --git a/sys/dev/gpio/pl061_fdt.c b/sys/dev/gpio/pl061_fdt.c
index aa22298b43c6..681b3ccdfdeb 100644
--- a/sys/dev/gpio/pl061_fdt.c
+++ b/sys/dev/gpio/pl061_fdt.c
@@ -61,19 +61,12 @@ pl061_fdt_probe(device_t dev)
static int
pl061_fdt_attach(device_t dev)
{
- int error;
+ struct pl061_softc *sc;
- error = pl061_attach(dev);
- if (error != 0)
- return (error);
+ sc = device_get_softc(dev);
+ sc->sc_xref = OF_xref_from_node(ofw_bus_get_node(dev));
- if (!intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)))) {
- device_printf(dev, "couldn't register PIC\n");
- pl061_detach(dev);
- error = ENXIO;
- }
-
- return (error);
+ return (pl061_attach(dev));
}
static device_method_t pl061_fdt_methods[] = {
diff --git a/sys/dev/gpio/qoriq_gpio.c b/sys/dev/gpio/qoriq_gpio.c
index 25dfccede29f..8b44cd256c79 100644
--- a/sys/dev/gpio/qoriq_gpio.c
+++ b/sys/dev/gpio/qoriq_gpio.c
@@ -369,11 +369,6 @@ qoriq_gpio_attach(device_t dev)
for (i = 0; i <= MAXPIN; i++)
sc->sc_pins[i].gp_caps = DEFAULT_CAPS;
- sc->busdev = gpiobus_attach_bus(dev);
- if (sc->busdev == NULL) {
- qoriq_gpio_detach(dev);
- return (ENOMEM);
- }
/*
* Enable the GPIO Input Buffer for all GPIOs.
* This is safe on devices without a GPIBE register, because those
@@ -384,6 +379,12 @@ qoriq_gpio_attach(device_t dev)
OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL) {
+ qoriq_gpio_detach(dev);
+ return (ENOMEM);
+ }
+
return (0);
}
diff --git a/sys/dev/hwt/hwt.c b/sys/dev/hwt/hwt.c
new file mode 100644
index 000000000000..c476e6031ba8
--- /dev/null
+++ b/sys/dev/hwt/hwt.c
@@ -0,0 +1,242 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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.
+ */
+
+/*
+ * Hardware Tracing framework.
+ *
+ * The framework manages hardware tracing units that collect information
+ * about software execution and store it as events in highly compressed format
+ * into DRAM. The events cover information about control flow changes of a
+ * program, whether branches taken or not, exceptions taken, timing information,
+ * cycles elapsed and more. That allows us to restore entire program flow of a
+ * given application without performance impact.
+ *
+ * Design overview.
+ *
+ * The framework provides character devices for mmap(2) and ioctl(2) system
+ * calls to allow user to manage CPU (hardware) tracing units.
+ *
+ * /dev/hwt:
+ * .ioctl:
+ * hwt_ioctl():
+ * a) HWT_IOC_ALLOC
+ * Allocates kernel tracing context CTX based on requested mode
+ * of operation. Verifies the information that comes with the
+ * request (pid, cpus), allocates unique ID for the context.
+ * Creates a new character device for CTX management.
+ *
+ * /dev/hwt_%d[_%d], ident[, thread_id]
+ * .mmap
+ * Maps tracing buffers of the corresponding thread to userspace.
+ * .ioctl
+ * hwt_thread_ioctl():
+ * a) HWT_IOC_START
+ * Enables tracing unit for a given context.
+ * b) HWT_IOC_RECORD_GET
+ * Transfers (small) record entries collected during program
+ * execution for a given context to userspace, such as mmaping
+ * tables of executable and dynamic libraries, interpreter,
+ * kernel mappings, tid of threads created, etc.
+ * c) HWT_IOC_SET_CONFIG
+ * Allows to specify backend-specific configuration of the
+ * trace unit.
+ * d) HWT_IOC_WAKEUP
+ * Wakes up a thread that is currently sleeping.
+ * e) HWT_IOC_BUFPTR_GET
+ * Transfers current hardware pointer in the filling buffer
+ * to the userspace.
+ * f) HWT_IOC_SVC_BUF
+ * To avoid data loss, userspace may notify kernel it has
+ * copied out the given buffer, so kernel is ok to overwrite
+ *
+ * HWT context lifecycle in THREAD mode of operation:
+ * 1. User invokes HWT_IOC_ALLOC ioctl with information about pid to trace and
+ * size of the buffers for the trace data to allocate.
+ * Some architectures may have different tracing units supported, so user
+ * also provides backend name to use for this context, e.g. "coresight".
+ * 2. Kernel allocates context, lookups the proc for the given pid. Then it
+ * creates first hwt_thread in the context and allocates trace buffers for
+ * it. Immediately, kernel initializes tracing backend.
+ * Kernel creates character device and returns unique identificator of
+ * trace context to the user.
+ * 3. To manage the new context, user opens the character device created.
+ * User invokes HWT_IOC_START ioctl, kernel marks context as RUNNING.
+ * At this point any HWT hook invocation by scheduler enables/disables
+ * tracing for threads associated with the context (threads of the proc).
+ * Any new threads creation (of the target proc) procedures will be invoking
+ * corresponding hooks in HWT framework, so that new hwt_thread and buffers
+ * allocated, character device for mmap(2) created on the fly.
+ * 4. User issues HWT_IOC_RECORD_GET ioctl to fetch information about mmaping
+ * tables and threads created during application startup.
+ * 5. User mmaps tracing buffers of each thread to userspace (using
+ * /dev/hwt_%d_%d % (ident, thread_id) character devices).
+ * 6. User can repeat 4 if expected thread is not yet created during target
+ * application execution.
+ * 7. User issues HWT_IOC_BUFPTR_GET ioctl to get current filling level of the
+ * hardware buffer of a given thread.
+ * 8. User invokes trace decoder library to process available data and see the
+ * results in human readable form.
+ * 9. User repeats 7 if needed.
+ *
+ * HWT context lifecycle in CPU mode of operation:
+ * 1. User invokes HWT_IOC_ALLOC ioctl providing a set of CPU to trace within
+ * single CTX.
+ * 2. Kernel verifies the set of CPU and allocates tracing context, creates
+ * a buffer for each CPU.
+ * Kernel creates a character device for every CPU provided in the request.
+ * Kernel initialized tracing backend.
+ * 3. User opens character devices of interest to map the buffers to userspace.
+ * User can start tracing by invoking HWT_IOC_START on any of character
+ * device within the context, entire context will be marked as RUNNING.
+ * 4. The rest is similar to the THREAD mode.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_ownerhash.h>
+#include <dev/hwt/hwt_backend.h>
+#include <dev/hwt/hwt_record.h>
+#include <dev/hwt/hwt_ioctl.h>
+#include <dev/hwt/hwt_hook.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static eventhandler_tag hwt_exit_tag;
+static struct cdev *hwt_cdev;
+static struct cdevsw hwt_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "hwt",
+ .d_mmap_single = NULL,
+ .d_ioctl = hwt_ioctl
+};
+
+static void
+hwt_process_exit(void *arg __unused, struct proc *p)
+{
+ struct hwt_owner *ho;
+
+ /* Stop HWTs associated with exiting owner, if any. */
+ ho = hwt_ownerhash_lookup(p);
+ if (ho)
+ hwt_owner_shutdown(ho);
+}
+
+static int
+hwt_load(void)
+{
+ struct make_dev_args args;
+ int error;
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &hwt_cdevsw;
+ args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0660;
+ args.mda_si_drv1 = NULL;
+
+ hwt_backend_load();
+ hwt_ctx_load();
+ hwt_contexthash_load();
+ hwt_ownerhash_load();
+ hwt_record_load();
+
+ error = make_dev_s(&args, &hwt_cdev, "hwt");
+ if (error != 0)
+ return (error);
+
+ hwt_exit_tag = EVENTHANDLER_REGISTER(process_exit, hwt_process_exit,
+ NULL, EVENTHANDLER_PRI_ANY);
+
+ hwt_hook_load();
+
+ return (0);
+}
+
+static int
+hwt_unload(void)
+{
+
+ hwt_hook_unload();
+ EVENTHANDLER_DEREGISTER(process_exit, hwt_exit_tag);
+ destroy_dev(hwt_cdev);
+ hwt_record_unload();
+ hwt_ownerhash_unload();
+ hwt_contexthash_unload();
+ hwt_ctx_unload();
+ hwt_backend_unload();
+
+ return (0);
+}
+
+static int
+hwt_modevent(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = hwt_load();
+ break;
+ case MOD_UNLOAD:
+ error = hwt_unload();
+ break;
+ default:
+ error = 0;
+ break;
+ }
+
+ return (error);
+}
+
+static moduledata_t hwt_mod = {
+ "hwt",
+ hwt_modevent,
+ NULL
+};
+
+DECLARE_MODULE(hwt, hwt_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(hwt, 1);
diff --git a/sys/dev/hwt/hwt_backend.c b/sys/dev/hwt/hwt_backend.c
new file mode 100644
index 000000000000..1ba5db0d3d09
--- /dev/null
+++ b/sys/dev/hwt/hwt_backend.c
@@ -0,0 +1,289 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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.
+ */
+
+/* Hardware Trace (HWT) framework. */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_backend.h>
+
+#define HWT_BACKEND_DEBUG
+#undef HWT_BACKEND_DEBUG
+
+#ifdef HWT_BACKEND_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static struct mtx hwt_backend_mtx;
+
+struct hwt_backend_entry {
+ struct hwt_backend *backend;
+ LIST_ENTRY(hwt_backend_entry) next;
+};
+
+static LIST_HEAD(, hwt_backend_entry) hwt_backends;
+
+static MALLOC_DEFINE(M_HWT_BACKEND, "hwt_backend", "HWT backend");
+
+int
+hwt_backend_init(struct hwt_context *ctx)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwt_backend->ops->hwt_backend_init(ctx);
+
+ return (error);
+}
+
+void
+hwt_backend_deinit(struct hwt_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_deinit(ctx);
+}
+
+int
+hwt_backend_configure(struct hwt_context *ctx, int cpu_id, int thread_id)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwt_backend->ops->hwt_backend_configure(ctx, cpu_id,
+ thread_id);
+
+ return (error);
+}
+
+void
+hwt_backend_enable(struct hwt_context *ctx, int cpu_id)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_enable(ctx, cpu_id);
+}
+
+void
+hwt_backend_disable(struct hwt_context *ctx, int cpu_id)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_disable(ctx, cpu_id);
+}
+
+void
+hwt_backend_enable_smp(struct hwt_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_enable_smp(ctx);
+}
+
+void
+hwt_backend_disable_smp(struct hwt_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_disable_smp(ctx);
+}
+
+void __unused
+hwt_backend_dump(struct hwt_context *ctx, int cpu_id)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_dump(cpu_id);
+}
+
+int
+hwt_backend_read(struct hwt_context *ctx, struct hwt_vm *vm, int *ident,
+ vm_offset_t *offset, uint64_t *data)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwt_backend->ops->hwt_backend_read(vm, ident,
+ offset, data);
+
+ return (error);
+}
+
+struct hwt_backend *
+hwt_backend_lookup(const char *name)
+{
+ struct hwt_backend_entry *entry;
+ struct hwt_backend *backend;
+
+ HWT_BACKEND_LOCK();
+ LIST_FOREACH(entry, &hwt_backends, next) {
+ backend = entry->backend;
+ if (strcmp(backend->name, name) == 0) {
+ HWT_BACKEND_UNLOCK();
+ return (backend);
+ }
+ }
+ HWT_BACKEND_UNLOCK();
+
+ return (NULL);
+}
+
+int
+hwt_backend_register(struct hwt_backend *backend)
+{
+ struct hwt_backend_entry *entry;
+
+ if (backend == NULL ||
+ backend->name == NULL ||
+ backend->ops == NULL)
+ return (EINVAL);
+
+ entry = malloc(sizeof(struct hwt_backend_entry), M_HWT_BACKEND,
+ M_WAITOK | M_ZERO);
+ entry->backend = backend;
+
+ HWT_BACKEND_LOCK();
+ LIST_INSERT_HEAD(&hwt_backends, entry, next);
+ HWT_BACKEND_UNLOCK();
+
+ return (0);
+}
+
+int
+hwt_backend_unregister(struct hwt_backend *backend)
+{
+ struct hwt_backend_entry *entry, *tmp;
+
+ if (backend == NULL)
+ return (EINVAL);
+
+ /* TODO: check if not in use */
+
+ HWT_BACKEND_LOCK();
+ LIST_FOREACH_SAFE(entry, &hwt_backends, next, tmp) {
+ if (entry->backend == backend) {
+ LIST_REMOVE(entry, next);
+ HWT_BACKEND_UNLOCK();
+ free(entry, M_HWT_BACKEND);
+ return (0);
+ }
+ }
+ HWT_BACKEND_UNLOCK();
+
+ return (ENOENT);
+}
+
+void
+hwt_backend_load(void)
+{
+
+ mtx_init(&hwt_backend_mtx, "hwt backend", NULL, MTX_DEF);
+ LIST_INIT(&hwt_backends);
+}
+
+void
+hwt_backend_unload(void)
+{
+
+ /* TODO: ensure all unregistered */
+
+ mtx_destroy(&hwt_backend_mtx);
+}
+
+void
+hwt_backend_stop(struct hwt_context *ctx)
+{
+ dprintf("%s\n", __func__);
+
+ ctx->hwt_backend->ops->hwt_backend_stop(ctx);
+}
+
+int
+hwt_backend_svc_buf(struct hwt_context *ctx, void *data, size_t data_size,
+ int data_version)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwt_backend->ops->hwt_backend_svc_buf(ctx, data, data_size,
+ data_version);
+
+ return (error);
+}
+
+int
+hwt_backend_thread_alloc(struct hwt_context *ctx, struct hwt_thread *thr)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ if (ctx->hwt_backend->ops->hwt_backend_thread_alloc == NULL)
+ return (0);
+ KASSERT(thr->private == NULL,
+ ("%s: thread private data is not NULL\n", __func__));
+ error = ctx->hwt_backend->ops->hwt_backend_thread_alloc(thr);
+
+ return (error);
+}
+
+void
+hwt_backend_thread_free(struct hwt_thread *thr)
+{
+ dprintf("%s\n", __func__);
+
+ if (thr->backend->ops->hwt_backend_thread_free == NULL)
+ return;
+ KASSERT(thr->private != NULL,
+ ("%s: thread private data is NULL\n", __func__));
+ thr->backend->ops->hwt_backend_thread_free(thr);
+
+ return;
+}
diff --git a/sys/dev/hwt/hwt_backend.h b/sys/dev/hwt/hwt_backend.h
new file mode 100644
index 000000000000..3b6c9442a7a6
--- /dev/null
+++ b/sys/dev/hwt/hwt_backend.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_BACKEND_H_
+#define _DEV_HWT_HWT_BACKEND_H_
+
+struct hwt_backend_ops {
+ int (*hwt_backend_init)(struct hwt_context *);
+ int (*hwt_backend_deinit)(struct hwt_context *);
+ int (*hwt_backend_configure)(struct hwt_context *, int cpu_id,
+ int thread_id);
+ int (*hwt_backend_svc_buf)(struct hwt_context *, void *data,
+ size_t data_size, int data_version);
+ void (*hwt_backend_enable)(struct hwt_context *, int cpu_id);
+ void (*hwt_backend_disable)(struct hwt_context *, int cpu_id);
+ int (*hwt_backend_read)(struct hwt_vm *, int *ident,
+ vm_offset_t *offset, uint64_t *data);
+ void (*hwt_backend_stop)(struct hwt_context *);
+ /* For backends that are tied to local CPU registers */
+ int (*hwt_backend_enable_smp)(struct hwt_context *);
+ int (*hwt_backend_disable_smp)(struct hwt_context *);
+ /* Allocation and initialization of backend-specific thread data. */
+ int (*hwt_backend_thread_alloc)(struct hwt_thread *);
+ void (*hwt_backend_thread_free)(struct hwt_thread *);
+ /* Debugging only. */
+ void (*hwt_backend_dump)(int cpu_id);
+};
+
+struct hwt_backend {
+ const char *name;
+ struct hwt_backend_ops *ops;
+ /* buffers require kernel virtual addresses */
+ bool kva_req;
+};
+
+int hwt_backend_init(struct hwt_context *ctx);
+void hwt_backend_deinit(struct hwt_context *ctx);
+int hwt_backend_configure(struct hwt_context *ctx, int cpu_id, int thread_id);
+void hwt_backend_enable(struct hwt_context *ctx, int cpu_id);
+void hwt_backend_disable(struct hwt_context *ctx, int cpu_id);
+void hwt_backend_enable_smp(struct hwt_context *ctx);
+void hwt_backend_disable_smp(struct hwt_context *ctx);
+void hwt_backend_dump(struct hwt_context *ctx, int cpu_id);
+int hwt_backend_read(struct hwt_context *ctx, struct hwt_vm *vm, int *ident,
+ vm_offset_t *offset, uint64_t *data);
+int hwt_backend_register(struct hwt_backend *);
+int hwt_backend_unregister(struct hwt_backend *);
+void hwt_backend_stop(struct hwt_context *);
+int hwt_backend_svc_buf(struct hwt_context *ctx, void *data, size_t data_size,
+ int data_version);
+struct hwt_backend * hwt_backend_lookup(const char *name);
+int hwt_backend_thread_alloc(struct hwt_context *ctx, struct hwt_thread *);
+void hwt_backend_thread_free(struct hwt_thread *);
+
+void hwt_backend_load(void);
+void hwt_backend_unload(void);
+
+#define HWT_BACKEND_LOCK() mtx_lock(&hwt_backend_mtx)
+#define HWT_BACKEND_UNLOCK() mtx_unlock(&hwt_backend_mtx)
+
+#endif /* !_DEV_HWT_HWT_BACKEND_H_ */
+
diff --git a/sys/dev/hwt/hwt_config.c b/sys/dev/hwt/hwt_config.c
new file mode 100644
index 000000000000..30688e7fc76b
--- /dev/null
+++ b/sys/dev/hwt/hwt_config.c
@@ -0,0 +1,108 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/lock.h>
+#include <sys/hwt.h>
+
+#include <vm/vm.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_record.h>
+
+#define HWT_MAXCONFIGSIZE PAGE_SIZE
+
+#define HWT_CONFIG_DEBUG
+#undef HWT_CONFIG_DEBUG
+
+#ifdef HWT_CONFIG_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_CONFIG, "hwt_config", "HWT config");
+
+int
+hwt_config_set(struct thread *td, struct hwt_context *ctx,
+ struct hwt_set_config *sconf)
+{
+ size_t config_size;
+ void *old_config;
+ void *config;
+ int error;
+
+ config_size = sconf->config_size;
+ if (config_size == 0)
+ return (0);
+
+ if (config_size > HWT_MAXCONFIGSIZE)
+ return (EFBIG);
+
+ config = malloc(config_size, M_HWT_CONFIG, M_WAITOK | M_ZERO);
+
+ error = copyin(sconf->config, config, config_size);
+ if (error) {
+ free(config, M_HWT_CONFIG);
+ return (error);
+ }
+
+ HWT_CTX_LOCK(ctx);
+ old_config = ctx->config;
+ ctx->config = config;
+ ctx->config_size = sconf->config_size;
+ ctx->config_version = sconf->config_version;
+ HWT_CTX_UNLOCK(ctx);
+
+ if (old_config != NULL)
+ free(old_config, M_HWT_CONFIG);
+
+ return (error);
+}
+
+void
+hwt_config_free(struct hwt_context *ctx)
+{
+
+ if (ctx->config == NULL)
+ return;
+
+ free(ctx->config, M_HWT_CONFIG);
+
+ ctx->config = NULL;
+}
diff --git a/sys/dev/hwt/hwt_config.h b/sys/dev/hwt/hwt_config.h
new file mode 100644
index 000000000000..47485583063c
--- /dev/null
+++ b/sys/dev/hwt/hwt_config.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_CONFIG_H_
+#define _DEV_HWT_HWT_CONFIG_H_
+
+int hwt_config_set(struct thread *td, struct hwt_context *ctx,
+ struct hwt_set_config *sconf);
+void hwt_config_free(struct hwt_context *ctx);
+
+#endif /* !_DEV_HWT_HWT_CONFIG_H_ */
diff --git a/sys/dev/hwt/hwt_context.c b/sys/dev/hwt/hwt_context.c
new file mode 100644
index 000000000000..9af76cffc928
--- /dev/null
+++ b/sys/dev/hwt/hwt_context.c
@@ -0,0 +1,201 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/bitstring.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_vm.h>
+#include <dev/hwt/hwt_cpu.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_CTX, "hwt_ctx", "Hardware Trace");
+
+static bitstr_t *ident_set;
+static int ident_set_size;
+static struct mtx ident_set_mutex;
+
+static int
+hwt_ctx_ident_alloc(int *new_ident)
+{
+
+ mtx_lock(&ident_set_mutex);
+ bit_ffc(ident_set, ident_set_size, new_ident);
+ if (*new_ident == -1) {
+ mtx_unlock(&ident_set_mutex);
+ return (ENOMEM);
+ }
+ bit_set(ident_set, *new_ident);
+ mtx_unlock(&ident_set_mutex);
+
+ return (0);
+}
+
+static void
+hwt_ctx_ident_free(int ident)
+{
+
+ mtx_lock(&ident_set_mutex);
+ bit_clear(ident_set, ident);
+ mtx_unlock(&ident_set_mutex);
+}
+
+int
+hwt_ctx_alloc(struct hwt_context **ctx0)
+{
+ struct hwt_context *ctx;
+ int error;
+
+ ctx = malloc(sizeof(struct hwt_context), M_HWT_CTX, M_WAITOK | M_ZERO);
+
+ TAILQ_INIT(&ctx->records);
+ TAILQ_INIT(&ctx->threads);
+ TAILQ_INIT(&ctx->cpus);
+ mtx_init(&ctx->mtx, "ctx", NULL, MTX_SPIN);
+ mtx_init(&ctx->rec_mtx, "ctx_rec", NULL, MTX_DEF);
+ refcount_init(&ctx->refcnt, 0);
+
+ error = hwt_ctx_ident_alloc(&ctx->ident);
+ if (error) {
+ printf("could not allocate ident bit str\n");
+ return (error);
+ }
+
+ *ctx0 = ctx;
+
+ return (0);
+}
+
+static void
+hwt_ctx_free_cpus(struct hwt_context *ctx)
+{
+ struct hwt_cpu *cpu;
+
+ do {
+ HWT_CTX_LOCK(ctx);
+ cpu = TAILQ_FIRST(&ctx->cpus);
+ if (cpu)
+ TAILQ_REMOVE(&ctx->cpus, cpu, next);
+ HWT_CTX_UNLOCK(ctx);
+
+ if (cpu == NULL)
+ break;
+
+ /* TODO: move vm_free() to cpu_free()? */
+ hwt_vm_free(cpu->vm);
+ hwt_cpu_free(cpu);
+ } while (1);
+}
+
+static void
+hwt_ctx_free_threads(struct hwt_context *ctx)
+{
+ struct hwt_thread *thr;
+
+ dprintf("%s: remove threads\n", __func__);
+
+ do {
+ HWT_CTX_LOCK(ctx);
+ thr = TAILQ_FIRST(&ctx->threads);
+ if (thr)
+ TAILQ_REMOVE(&ctx->threads, thr, next);
+ HWT_CTX_UNLOCK(ctx);
+
+ if (thr == NULL)
+ break;
+
+ HWT_THR_LOCK(thr);
+ /* TODO: check if thr is sleeping before waking it up. */
+ wakeup(thr);
+ HWT_THR_UNLOCK(thr);
+
+ if (refcount_release(&thr->refcnt))
+ hwt_thread_free(thr);
+ } while (1);
+}
+
+void
+hwt_ctx_free(struct hwt_context *ctx)
+{
+
+ if (ctx->mode == HWT_MODE_CPU)
+ hwt_ctx_free_cpus(ctx);
+ else
+ hwt_ctx_free_threads(ctx);
+
+ hwt_config_free(ctx);
+ hwt_ctx_ident_free(ctx->ident);
+ free(ctx, M_HWT_CTX);
+}
+
+void
+hwt_ctx_put(struct hwt_context *ctx)
+{
+
+ refcount_release(&ctx->refcnt);
+}
+
+void
+hwt_ctx_load(void)
+{
+
+ ident_set_size = (1 << 8);
+ ident_set = bit_alloc(ident_set_size, M_HWT_CTX, M_WAITOK);
+ mtx_init(&ident_set_mutex, "ident set", NULL, MTX_DEF);
+}
+
+void
+hwt_ctx_unload(void)
+{
+
+ mtx_destroy(&ident_set_mutex);
+ free(ident_set, M_HWT_CTX);
+}
diff --git a/sys/dev/hwt/hwt_context.h b/sys/dev/hwt/hwt_context.h
new file mode 100644
index 000000000000..cafb197ae348
--- /dev/null
+++ b/sys/dev/hwt/hwt_context.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_CONTEXT_H_
+#define _DEV_HWT_HWT_CONTEXT_H_
+
+enum hwt_ctx_state {
+ CTX_STATE_STOPPED,
+ CTX_STATE_RUNNING,
+};
+
+struct hwt_context {
+ TAILQ_HEAD(, hwt_record_entry) records;
+
+ LIST_ENTRY(hwt_context) next_hch; /* Entry in contexthash. */
+ LIST_ENTRY(hwt_context) next_hwts; /* Entry in ho->hwts. */
+
+ int mode;
+ int ident;
+
+ int kqueue_fd;
+ struct thread *hwt_td;
+
+ /* CPU mode. */
+ cpuset_t cpu_map;
+ TAILQ_HEAD(, hwt_cpu) cpus;
+
+ /* Thread mode. */
+ struct proc *proc; /* Target proc. */
+ pid_t pid; /* Target pid. */
+ TAILQ_HEAD(, hwt_thread) threads;
+ int thread_counter;
+ int pause_on_mmap;
+
+ size_t bufsize; /* Trace bufsize for each vm.*/
+
+ void *config;
+ size_t config_size;
+ int config_version;
+
+ struct hwt_owner *hwt_owner;
+ struct hwt_backend *hwt_backend;
+
+ struct mtx mtx;
+ struct mtx rec_mtx;
+ enum hwt_ctx_state state;
+ int refcnt;
+};
+
+#define HWT_CTX_LOCK(ctx) mtx_lock_spin(&(ctx)->mtx)
+#define HWT_CTX_UNLOCK(ctx) mtx_unlock_spin(&(ctx)->mtx)
+#define HWT_CTX_ASSERT_LOCKED(ctx) mtx_assert(&(ctx)->mtx, MA_OWNED)
+
+int hwt_ctx_alloc(struct hwt_context **ctx0);
+void hwt_ctx_free(struct hwt_context *ctx);
+void hwt_ctx_put(struct hwt_context *ctx);
+
+void hwt_ctx_load(void);
+void hwt_ctx_unload(void);
+
+#endif /* !_DEV_HWT_HWT_CONTEXT_H_ */
diff --git a/sys/dev/hwt/hwt_contexthash.c b/sys/dev/hwt/hwt_contexthash.c
new file mode 100644
index 000000000000..5682b7d38e5e
--- /dev/null
+++ b/sys/dev/hwt/hwt_contexthash.c
@@ -0,0 +1,134 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define HWT_CONTEXTHASH_SIZE 1024
+
+static MALLOC_DEFINE(M_HWT_CONTEXTHASH, "hwt_chash", "Hardware Trace");
+
+/*
+ * Hash function. Discard the lower 2 bits of the pointer since
+ * these are always zero for our uses. The hash multiplier is
+ * round((2^LONG_BIT) * ((sqrt(5)-1)/2)).
+ */
+
+#define _HWT_HM 11400714819323198486u /* hash multiplier */
+#define HWT_HASH_PTR(P, M) ((((unsigned long) (P) >> 2) * _HWT_HM) & (M))
+
+static struct mtx hwt_contexthash_mtx;
+static u_long hwt_contexthashmask;
+static LIST_HEAD(hwt_contexthash, hwt_context) *hwt_contexthash;
+
+/*
+ * To use by hwt_switch_in/out() and hwt_record() only.
+ * This function returns with refcnt acquired.
+ */
+struct hwt_context *
+hwt_contexthash_lookup(struct proc *p)
+{
+ struct hwt_contexthash *hch;
+ struct hwt_context *ctx;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwt_contexthashmask);
+ hch = &hwt_contexthash[hindex];
+
+ HWT_CTXHASH_LOCK();
+ LIST_FOREACH(ctx, hch, next_hch) {
+ if (ctx->proc == p) {
+ refcount_acquire(&ctx->refcnt);
+ HWT_CTXHASH_UNLOCK();
+ return (ctx);
+ }
+ }
+ HWT_CTXHASH_UNLOCK();
+
+ return (NULL);
+}
+
+void
+hwt_contexthash_insert(struct hwt_context *ctx)
+{
+ struct hwt_contexthash *hch;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(ctx->proc, hwt_contexthashmask);
+ hch = &hwt_contexthash[hindex];
+
+ HWT_CTXHASH_LOCK();
+ LIST_INSERT_HEAD(hch, ctx, next_hch);
+ HWT_CTXHASH_UNLOCK();
+}
+
+void
+hwt_contexthash_remove(struct hwt_context *ctx)
+{
+
+ HWT_CTXHASH_LOCK();
+ LIST_REMOVE(ctx, next_hch);
+ HWT_CTXHASH_UNLOCK();
+}
+
+void
+hwt_contexthash_load(void)
+{
+
+ hwt_contexthash = hashinit(HWT_CONTEXTHASH_SIZE, M_HWT_CONTEXTHASH,
+ &hwt_contexthashmask);
+ mtx_init(&hwt_contexthash_mtx, "hwt ctx hash", "hwt ctx", MTX_SPIN);
+}
+
+void
+hwt_contexthash_unload(void)
+{
+
+ mtx_destroy(&hwt_contexthash_mtx);
+ hashdestroy(hwt_contexthash, M_HWT_CONTEXTHASH, hwt_contexthashmask);
+}
diff --git a/sys/dev/hwt/hwt_contexthash.h b/sys/dev/hwt/hwt_contexthash.h
new file mode 100644
index 000000000000..c3ab7acd2a74
--- /dev/null
+++ b/sys/dev/hwt/hwt_contexthash.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_CONTEXTHASH_H_
+#define _DEV_HWT_HWT_CONTEXTHASH_H_
+
+struct hwt_context * hwt_contexthash_lookup(struct proc *p);
+void hwt_contexthash_insert(struct hwt_context *ctx);
+void hwt_contexthash_remove(struct hwt_context *ctx);
+
+void hwt_contexthash_load(void);
+void hwt_contexthash_unload(void);
+
+#define HWT_CTXHASH_LOCK() mtx_lock_spin(&hwt_contexthash_mtx)
+#define HWT_CTXHASH_UNLOCK() mtx_unlock_spin(&hwt_contexthash_mtx)
+
+#endif /* !_DEV_HWT_HWT_CONTEXTHASH_H_ */
diff --git a/sys/dev/hwt/hwt_cpu.c b/sys/dev/hwt/hwt_cpu.c
new file mode 100644
index 000000000000..7d38eb082e65
--- /dev/null
+++ b/sys/dev/hwt/hwt_cpu.c
@@ -0,0 +1,115 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/hwt.h>
+
+#include <vm/vm.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_record.h>
+#include <dev/hwt/hwt_cpu.h>
+
+#define HWT_CPU_DEBUG
+#undef HWT_CPU_DEBUG
+
+#ifdef HWT_CPU_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_CPU, "hwt_cpu", "HWT cpu");
+
+struct hwt_cpu *
+hwt_cpu_alloc(void)
+{
+ struct hwt_cpu *cpu;
+
+ cpu = malloc(sizeof(struct hwt_cpu), M_HWT_CPU, M_WAITOK | M_ZERO);
+
+ return (cpu);
+}
+
+void
+hwt_cpu_free(struct hwt_cpu *cpu)
+{
+
+ free(cpu, M_HWT_CPU);
+}
+
+struct hwt_cpu *
+hwt_cpu_first(struct hwt_context *ctx)
+{
+ struct hwt_cpu *cpu;
+
+ HWT_CTX_ASSERT_LOCKED(ctx);
+
+ cpu = TAILQ_FIRST(&ctx->cpus);
+
+ KASSERT(cpu != NULL, ("cpu is NULL"));
+
+ return (cpu);
+}
+
+struct hwt_cpu *
+hwt_cpu_get(struct hwt_context *ctx, int cpu_id)
+{
+ struct hwt_cpu *cpu, *tcpu;
+
+ HWT_CTX_ASSERT_LOCKED(ctx);
+
+ TAILQ_FOREACH_SAFE(cpu, &ctx->cpus, next, tcpu) {
+ KASSERT(cpu != NULL, ("cpu is NULL"));
+ if (cpu->cpu_id == cpu_id) {
+ return cpu;
+ }
+ }
+
+ return (NULL);
+}
+
+void
+hwt_cpu_insert(struct hwt_context *ctx, struct hwt_cpu *cpu)
+{
+
+ HWT_CTX_ASSERT_LOCKED(ctx);
+
+ TAILQ_INSERT_TAIL(&ctx->cpus, cpu, next);
+}
diff --git a/sys/dev/hwt/hwt_cpu.h b/sys/dev/hwt/hwt_cpu.h
new file mode 100644
index 000000000000..92b89229b6e4
--- /dev/null
+++ b/sys/dev/hwt/hwt_cpu.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_CPU_H_
+#define _DEV_HWT_HWT_CPU_H_
+
+struct hwt_cpu {
+ int cpu_id;
+ struct hwt_vm *vm;
+ TAILQ_ENTRY(hwt_cpu) next;
+};
+
+struct hwt_cpu * hwt_cpu_alloc(void);
+void hwt_cpu_free(struct hwt_cpu *cpu);
+
+struct hwt_cpu * hwt_cpu_first(struct hwt_context *ctx);
+struct hwt_cpu * hwt_cpu_get(struct hwt_context *ctx, int cpu_id);
+void hwt_cpu_insert(struct hwt_context *ctx, struct hwt_cpu *cpu);
+
+#endif /* !_DEV_HWT_HWT_CPU_H_ */
diff --git a/sys/dev/hwt/hwt_hook.c b/sys/dev/hwt/hwt_hook.c
new file mode 100644
index 000000000000..258279b14f20
--- /dev/null
+++ b/sys/dev/hwt/hwt_hook.c
@@ -0,0 +1,323 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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.
+ */
+
+/* Hardware Trace (HWT) framework. */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/refcount.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_backend.h>
+#include <dev/hwt/hwt_record.h>
+#include <dev/hwt/hwt_vm.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static void
+hwt_switch_in(struct thread *td)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+ int cpu_id;
+
+ p = td->td_proc;
+
+ cpu_id = PCPU_GET(cpuid);
+
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL)
+ return;
+
+ if (ctx->state != CTX_STATE_RUNNING) {
+ hwt_ctx_put(ctx);
+ return;
+ }
+
+ thr = hwt_thread_lookup(ctx, td);
+ if (thr == NULL) {
+ hwt_ctx_put(ctx);
+ return;
+ }
+
+ dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr,
+ thr->thread_id, td->td_tid, cpu_id);
+
+ hwt_backend_configure(ctx, cpu_id, thr->thread_id);
+ hwt_backend_enable(ctx, cpu_id);
+
+ hwt_ctx_put(ctx);
+}
+
+static void
+hwt_switch_out(struct thread *td)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+ int cpu_id;
+
+ p = td->td_proc;
+
+ cpu_id = PCPU_GET(cpuid);
+
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL)
+ return;
+
+ if (ctx->state != CTX_STATE_RUNNING) {
+ hwt_ctx_put(ctx);
+ return;
+ }
+ thr = hwt_thread_lookup(ctx, td);
+ if (thr == NULL) {
+ hwt_ctx_put(ctx);
+ return;
+ }
+
+ dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr,
+ thr->thread_id, td->td_tid, cpu_id);
+
+ hwt_backend_disable(ctx, cpu_id);
+
+ hwt_ctx_put(ctx);
+}
+
+static void
+hwt_hook_thread_exit(struct thread *td)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+ int cpu_id;
+
+ p = td->td_proc;
+
+ cpu_id = PCPU_GET(cpuid);
+
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL)
+ return;
+
+ thr = hwt_thread_lookup(ctx, td);
+ if (thr == NULL) {
+ hwt_ctx_put(ctx);
+ return;
+ }
+
+ thr->state = HWT_THREAD_STATE_EXITED;
+
+ dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr,
+ thr->thread_id, td->td_tid, cpu_id);
+
+ if (ctx->state == CTX_STATE_RUNNING)
+ hwt_backend_disable(ctx, cpu_id);
+
+ hwt_ctx_put(ctx);
+}
+
+static void
+hwt_hook_mmap(struct thread *td)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+ int pause;
+
+ p = td->td_proc;
+
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL)
+ return;
+
+ /* The ctx state could be any here. */
+
+ pause = ctx->pause_on_mmap ? 1 : 0;
+
+ thr = hwt_thread_lookup(ctx, td);
+ if (thr == NULL) {
+ hwt_ctx_put(ctx);
+ return;
+ }
+
+ /*
+ * msleep(9) atomically releases the mtx lock, so take refcount
+ * to ensure that thr is not destroyed.
+ * It could not be destroyed prior to this call as we are holding ctx
+ * refcnt.
+ */
+ refcount_acquire(&thr->refcnt);
+ hwt_ctx_put(ctx);
+
+ if (pause) {
+ HWT_THR_LOCK(thr);
+ msleep(thr, &thr->mtx, PCATCH, "hwt-mmap", 0);
+ HWT_THR_UNLOCK(thr);
+ }
+
+ if (refcount_release(&thr->refcnt))
+ hwt_thread_free(thr);
+}
+
+static int
+hwt_hook_thread_create(struct thread *td)
+{
+ struct hwt_record_entry *entry;
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ char path[MAXPATHLEN];
+ size_t bufsize;
+ struct proc *p;
+ int thread_id, kva_req;
+ int error;
+
+ p = td->td_proc;
+
+ /* Step 1. Get CTX and collect information needed. */
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL)
+ return (ENXIO);
+ thread_id = atomic_fetchadd_int(&ctx->thread_counter, 1);
+ bufsize = ctx->bufsize;
+ kva_req = ctx->hwt_backend->kva_req;
+ sprintf(path, "hwt_%d_%d", ctx->ident, thread_id);
+ hwt_ctx_put(ctx);
+
+ /* Step 2. Allocate some memory without holding ctx ref. */
+ error = hwt_thread_alloc(&thr, path, bufsize, kva_req);
+ if (error) {
+ printf("%s: could not allocate thread, error %d\n",
+ __func__, error);
+ return (error);
+ }
+
+ entry = hwt_record_entry_alloc();
+ entry->record_type = HWT_RECORD_THREAD_CREATE;
+ entry->thread_id = thread_id;
+
+ /* Step 3. Get CTX once again. */
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL) {
+ hwt_record_entry_free(entry);
+ hwt_thread_free(thr);
+ /* ctx->thread_counter does not matter. */
+ return (ENXIO);
+ }
+ /* Allocate backend-specific thread data. */
+ error = hwt_backend_thread_alloc(ctx, thr);
+ if (error != 0) {
+ dprintf("%s: failed to allocate backend thread data\n",
+ __func__);
+ return (error);
+ }
+
+ thr->vm->ctx = ctx;
+ thr->ctx = ctx;
+ thr->backend = ctx->hwt_backend;
+ thr->thread_id = thread_id;
+ thr->td = td;
+
+ HWT_CTX_LOCK(ctx);
+ hwt_thread_insert(ctx, thr, entry);
+ HWT_CTX_UNLOCK(ctx);
+
+ /* Notify userspace. */
+ hwt_record_wakeup(ctx);
+
+ hwt_ctx_put(ctx);
+
+ return (0);
+}
+
+static void
+hwt_hook_handler(struct thread *td, int func, void *arg)
+{
+ struct proc *p;
+
+ p = td->td_proc;
+ if ((p->p_flag2 & P2_HWT) == 0)
+ return;
+
+ switch (func) {
+ case HWT_SWITCH_IN:
+ hwt_switch_in(td);
+ break;
+ case HWT_SWITCH_OUT:
+ hwt_switch_out(td);
+ break;
+ case HWT_THREAD_CREATE:
+ hwt_hook_thread_create(td);
+ break;
+ case HWT_THREAD_SET_NAME:
+ /* TODO. */
+ break;
+ case HWT_THREAD_EXIT:
+ hwt_hook_thread_exit(td);
+ break;
+ case HWT_EXEC:
+ case HWT_MMAP:
+ hwt_record_td(td, arg, M_WAITOK | M_ZERO);
+ hwt_hook_mmap(td);
+ break;
+ case HWT_RECORD:
+ hwt_record_td(td, arg, M_WAITOK | M_ZERO);
+ break;
+ };
+}
+
+void
+hwt_hook_load(void)
+{
+
+ hwt_hook = hwt_hook_handler;
+}
+
+void
+hwt_hook_unload(void)
+{
+
+ hwt_hook = NULL;
+}
diff --git a/sys/dev/hwt/hwt_hook.h b/sys/dev/hwt/hwt_hook.h
new file mode 100644
index 000000000000..a8eccba3ec43
--- /dev/null
+++ b/sys/dev/hwt/hwt_hook.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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/hwt_record.h>
+
+#ifndef _DEV_HWT_HWT_HOOK_H_
+#define _DEV_HWT_HWT_HOOK_H_
+
+#define HWT_SWITCH_IN 0
+#define HWT_SWITCH_OUT 1
+#define HWT_THREAD_EXIT 2
+#define HWT_THREAD_CREATE 3
+#define HWT_THREAD_SET_NAME 4
+#define HWT_RECORD 5
+#define HWT_MMAP 6
+#define HWT_EXEC 7
+
+#define HWT_CALL_HOOK(td, func, arg) \
+do { \
+ if (hwt_hook != NULL) \
+ (hwt_hook)((td), (func), (arg)); \
+} while (0)
+
+#define HWT_HOOK_INSTALLED (hwt_hook != NULL)
+
+extern void (*hwt_hook)(struct thread *td, int func, void *arg);
+
+void hwt_hook_load(void);
+void hwt_hook_unload(void);
+
+#endif /* !_DEV_HWT_HWT_HOOK_H_ */
diff --git a/sys/dev/hwt/hwt_intr.h b/sys/dev/hwt/hwt_intr.h
new file mode 100644
index 000000000000..e601969f001c
--- /dev/null
+++ b/sys/dev/hwt/hwt_intr.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2023-2025 Bojan Novković <bnovkov@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 _DEV_HWT_HWT_INTR_H_
+#define _DEV_HWT_HWT_INTR_H_
+
+#include <machine/frame.h>
+
+extern int (*hwt_intr)(struct trapframe *tf);
+
+#endif /* !_DEV_HWT_HWT_INTR_H_ */
diff --git a/sys/dev/hwt/hwt_ioctl.c b/sys/dev/hwt/hwt_ioctl.c
new file mode 100644
index 000000000000..592db4931bb4
--- /dev/null
+++ b/sys/dev/hwt/hwt_ioctl.c
@@ -0,0 +1,445 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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.
+ */
+
+/* Hardware Trace (HWT) framework. */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/smp.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_cpu.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_ownerhash.h>
+#include <dev/hwt/hwt_backend.h>
+#include <dev/hwt/hwt_record.h>
+#include <dev/hwt/hwt_ioctl.h>
+#include <dev/hwt/hwt_vm.h>
+
+#define HWT_IOCTL_DEBUG
+#undef HWT_IOCTL_DEBUG
+
+#ifdef HWT_IOCTL_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+/* No real reason for these limitations just sanity checks. */
+#define HWT_MAXBUFSIZE (32UL * 1024 * 1024 * 1024) /* 32 GB */
+
+static MALLOC_DEFINE(M_HWT_IOCTL, "hwt_ioctl", "Hardware Trace");
+
+/*
+ * Check if owner process *o can trace target process *t.
+ */
+
+static int
+hwt_priv_check(struct proc *o, struct proc *t)
+{
+ struct ucred *oc, *tc;
+ int error;
+ int i;
+
+ PROC_LOCK(o);
+ oc = o->p_ucred;
+ crhold(oc);
+ PROC_UNLOCK(o);
+
+ PROC_LOCK_ASSERT(t, MA_OWNED);
+ tc = t->p_ucred;
+ crhold(tc);
+
+ error = 0;
+
+ /*
+ * The effective uid of the HWT owner should match at least one
+ * of the effective / real / saved uids of the target process.
+ */
+
+ if (oc->cr_uid != tc->cr_uid &&
+ oc->cr_uid != tc->cr_svuid &&
+ oc->cr_uid != tc->cr_ruid) {
+ error = EPERM;
+ goto done;
+ }
+
+ /*
+ * Everyone of the target's group ids must be in the owner's
+ * group list.
+ */
+ for (i = 0; i < tc->cr_ngroups; i++)
+ if (!groupmember(tc->cr_groups[i], oc)) {
+ error = EPERM;
+ goto done;
+ }
+
+ /* Check the read and saved GIDs too. */
+ if (!groupmember(tc->cr_rgid, oc) ||
+ !groupmember(tc->cr_svgid, oc)) {
+ error = EPERM;
+ goto done;
+ }
+
+done:
+ crfree(tc);
+ crfree(oc);
+
+ return (error);
+}
+
+static int
+hwt_ioctl_alloc_mode_thread(struct thread *td, struct hwt_owner *ho,
+ struct hwt_backend *backend, struct hwt_alloc *halloc)
+{
+ struct thread **threads, *td1;
+ struct hwt_record_entry *entry;
+ struct hwt_context *ctx, *ctx1;
+ struct hwt_thread *thr;
+ char path[MAXPATHLEN];
+ struct proc *p;
+ int thread_id;
+ int error;
+ int cnt;
+ int i;
+
+ /* Check if the owner have this pid configured already. */
+ ctx = hwt_owner_lookup_ctx(ho, halloc->pid);
+ if (ctx)
+ return (EEXIST);
+
+ /* Allocate a new HWT context. */
+ error = hwt_ctx_alloc(&ctx);
+ if (error)
+ return (error);
+ ctx->bufsize = halloc->bufsize;
+ ctx->pid = halloc->pid;
+ ctx->hwt_backend = backend;
+ ctx->hwt_owner = ho;
+ ctx->mode = HWT_MODE_THREAD;
+ ctx->hwt_td = td;
+ ctx->kqueue_fd = halloc->kqueue_fd;
+
+ error = copyout(&ctx->ident, halloc->ident, sizeof(int));
+ if (error) {
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ /* Now get the victim proc. */
+ p = pfind(halloc->pid);
+ if (p == NULL) {
+ hwt_ctx_free(ctx);
+ return (ENXIO);
+ }
+
+ /* Ensure we can trace it. */
+ error = hwt_priv_check(td->td_proc, p);
+ if (error) {
+ PROC_UNLOCK(p);
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ /* Ensure it is not being traced already. */
+ ctx1 = hwt_contexthash_lookup(p);
+ if (ctx1) {
+ refcount_release(&ctx1->refcnt);
+ PROC_UNLOCK(p);
+ hwt_ctx_free(ctx);
+ return (EEXIST);
+ }
+
+ /* Allocate hwt threads and buffers. */
+
+ cnt = 0;
+
+ FOREACH_THREAD_IN_PROC(p, td1) {
+ cnt += 1;
+ }
+
+ KASSERT(cnt > 0, ("no threads"));
+
+ threads = malloc(sizeof(struct thread *) * cnt, M_HWT_IOCTL,
+ M_NOWAIT | M_ZERO);
+ if (threads == NULL) {
+ PROC_UNLOCK(p);
+ hwt_ctx_free(ctx);
+ return (ENOMEM);
+ }
+
+ i = 0;
+
+ FOREACH_THREAD_IN_PROC(p, td1) {
+ threads[i++] = td1;
+ }
+
+ ctx->proc = p;
+ PROC_UNLOCK(p);
+
+ for (i = 0; i < cnt; i++) {
+ thread_id = atomic_fetchadd_int(&ctx->thread_counter, 1);
+ sprintf(path, "hwt_%d_%d", ctx->ident, thread_id);
+
+ error = hwt_thread_alloc(&thr, path, ctx->bufsize,
+ ctx->hwt_backend->kva_req);
+ if (error) {
+ free(threads, M_HWT_IOCTL);
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+ /* Allocate backend-specific thread data. */
+ error = hwt_backend_thread_alloc(ctx, thr);
+ if (error != 0) {
+ dprintf("%s: failed to allocate thread backend data\n",
+ __func__);
+ free(threads, M_HWT_IOCTL);
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ /*
+ * Insert a THREAD_CREATE record so userspace picks up
+ * the thread's tracing buffers.
+ */
+ entry = hwt_record_entry_alloc();
+ entry->record_type = HWT_RECORD_THREAD_CREATE;
+ entry->thread_id = thread_id;
+
+ thr->vm->ctx = ctx;
+ thr->td = threads[i];
+ thr->ctx = ctx;
+ thr->backend = ctx->hwt_backend;
+ thr->thread_id = thread_id;
+
+ HWT_CTX_LOCK(ctx);
+ hwt_thread_insert(ctx, thr, entry);
+ HWT_CTX_UNLOCK(ctx);
+ }
+
+ free(threads, M_HWT_IOCTL);
+
+ error = hwt_backend_init(ctx);
+ if (error) {
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ /* hwt_owner_insert_ctx? */
+ mtx_lock(&ho->mtx);
+ LIST_INSERT_HEAD(&ho->hwts, ctx, next_hwts);
+ mtx_unlock(&ho->mtx);
+
+ /*
+ * Hooks are now in action after this, but the ctx is not in RUNNING
+ * state.
+ */
+ hwt_contexthash_insert(ctx);
+
+ p = pfind(halloc->pid);
+ if (p) {
+ p->p_flag2 |= P2_HWT;
+ PROC_UNLOCK(p);
+ }
+
+ return (0);
+}
+
+static int
+hwt_ioctl_alloc_mode_cpu(struct thread *td, struct hwt_owner *ho,
+ struct hwt_backend *backend, struct hwt_alloc *halloc)
+{
+ struct hwt_context *ctx;
+ struct hwt_cpu *cpu;
+ struct hwt_vm *vm;
+ char path[MAXPATHLEN];
+ size_t cpusetsize;
+ cpuset_t cpu_map;
+ int cpu_count = 0;
+ int cpu_id;
+ int error;
+
+ CPU_ZERO(&cpu_map);
+ cpusetsize = min(halloc->cpusetsize, sizeof(cpuset_t));
+ error = copyin(halloc->cpu_map, &cpu_map, cpusetsize);
+ if (error)
+ return (error);
+
+ CPU_FOREACH_ISSET(cpu_id, &cpu_map) {
+#ifdef SMP
+ /* Ensure CPU is not halted. */
+ if (CPU_ISSET(cpu_id, &hlt_cpus_mask))
+ return (ENXIO);
+#endif
+#if 0
+ /* TODO: Check if the owner have this cpu configured already. */
+ ctx = hwt_owner_lookup_ctx_by_cpu(ho, halloc->cpu);
+ if (ctx)
+ return (EEXIST);
+#endif
+
+ cpu_count++;
+ }
+
+ if (cpu_count == 0)
+ return (ENODEV);
+
+ /* Allocate a new HWT context. */
+ error = hwt_ctx_alloc(&ctx);
+ if (error)
+ return (error);
+ ctx->bufsize = halloc->bufsize;
+ ctx->hwt_backend = backend;
+ ctx->hwt_owner = ho;
+ ctx->mode = HWT_MODE_CPU;
+ ctx->cpu_map = cpu_map;
+ ctx->hwt_td = td;
+ ctx->kqueue_fd = halloc->kqueue_fd;
+
+ error = copyout(&ctx->ident, halloc->ident, sizeof(int));
+ if (error) {
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ CPU_FOREACH_ISSET(cpu_id, &cpu_map) {
+ sprintf(path, "hwt_%d_%d", ctx->ident, cpu_id);
+ error = hwt_vm_alloc(ctx->bufsize, ctx->hwt_backend->kva_req,
+ path, &vm);
+ if (error) {
+ /* TODO: remove all allocated cpus. */
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ cpu = hwt_cpu_alloc();
+ cpu->cpu_id = cpu_id;
+ cpu->vm = vm;
+
+ vm->cpu = cpu;
+ vm->ctx = ctx;
+
+ HWT_CTX_LOCK(ctx);
+ hwt_cpu_insert(ctx, cpu);
+ HWT_CTX_UNLOCK(ctx);
+ }
+
+ error = hwt_backend_init(ctx);
+ if (error) {
+ /* TODO: remove all allocated cpus. */
+ hwt_ctx_free(ctx);
+ return (error);
+ }
+
+ /* hwt_owner_insert_ctx? */
+ mtx_lock(&ho->mtx);
+ LIST_INSERT_HEAD(&ho->hwts, ctx, next_hwts);
+ mtx_unlock(&ho->mtx);
+
+ hwt_record_kernel_objects(ctx);
+
+ return (0);
+}
+
+static int
+hwt_ioctl_alloc(struct thread *td, struct hwt_alloc *halloc)
+{
+ char backend_name[HWT_BACKEND_MAXNAMELEN];
+ struct hwt_backend *backend;
+ struct hwt_owner *ho;
+ int error;
+
+ if (halloc->bufsize > HWT_MAXBUFSIZE)
+ return (EINVAL);
+ if (halloc->bufsize % PAGE_SIZE)
+ return (EINVAL);
+ if (halloc->backend_name == NULL)
+ return (EINVAL);
+
+ error = copyinstr(halloc->backend_name, (void *)backend_name,
+ HWT_BACKEND_MAXNAMELEN, NULL);
+ if (error)
+ return (error);
+
+ backend = hwt_backend_lookup(backend_name);
+ if (backend == NULL)
+ return (ENODEV);
+
+ /* First get the owner. */
+ ho = hwt_ownerhash_lookup(td->td_proc);
+ if (ho == NULL) {
+ /* Create a new owner. */
+ ho = hwt_owner_alloc(td->td_proc);
+ if (ho == NULL)
+ return (ENOMEM);
+ hwt_ownerhash_insert(ho);
+ }
+
+ switch (halloc->mode) {
+ case HWT_MODE_THREAD:
+ error = hwt_ioctl_alloc_mode_thread(td, ho, backend, halloc);
+ break;
+ case HWT_MODE_CPU:
+ error = hwt_ioctl_alloc_mode_cpu(td, ho, backend, halloc);
+ break;
+ default:
+ error = ENXIO;
+ };
+
+ return (error);
+}
+
+int
+hwt_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+ int error;
+
+ switch (cmd) {
+ case HWT_IOC_ALLOC:
+ /* Allocate HWT context. */
+ error = hwt_ioctl_alloc(td, (struct hwt_alloc *)addr);
+ return (error);
+ default:
+ return (ENXIO);
+ };
+}
diff --git a/sys/dev/hwt/hwt_ioctl.h b/sys/dev/hwt/hwt_ioctl.h
new file mode 100644
index 000000000000..ce4270dc0d44
--- /dev/null
+++ b/sys/dev/hwt/hwt_ioctl.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_IOCTL_H
+#define _DEV_HWT_HWT_IOCTL_H
+
+int hwt_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td);
+
+#endif /* !_DEV_HWT_HWT_IOCTL_H */
diff --git a/sys/dev/hwt/hwt_owner.c b/sys/dev/hwt/hwt_owner.c
new file mode 100644
index 000000000000..3c82040578de
--- /dev/null
+++ b/sys/dev/hwt/hwt_owner.c
@@ -0,0 +1,157 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_cpu.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_ownerhash.h>
+#include <dev/hwt/hwt_backend.h>
+#include <dev/hwt/hwt_vm.h>
+#include <dev/hwt/hwt_record.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_OWNER, "hwt_owner", "Hardware Trace");
+
+struct hwt_context *
+hwt_owner_lookup_ctx(struct hwt_owner *ho, pid_t pid)
+{
+ struct hwt_context *ctx;
+
+ mtx_lock(&ho->mtx);
+ LIST_FOREACH(ctx, &ho->hwts, next_hwts) {
+ if (ctx->pid == pid) {
+ mtx_unlock(&ho->mtx);
+ return (ctx);
+ }
+ }
+ mtx_unlock(&ho->mtx);
+
+ return (NULL);
+}
+
+#if 0
+struct hwt_context *
+hwt_owner_lookup_ctx_by_cpu(struct hwt_owner *ho, int cpu)
+{
+ struct hwt_context *ctx;
+
+ mtx_lock(&ho->mtx);
+ LIST_FOREACH(ctx, &ho->hwts, next_hwts) {
+ if (ctx->cpu == cpu) {
+ mtx_unlock(&ho->mtx);
+ return (ctx);
+ }
+ }
+ mtx_unlock(&ho->mtx);
+
+ return (NULL);
+}
+#endif
+
+struct hwt_owner *
+hwt_owner_alloc(struct proc *p)
+{
+ struct hwt_owner *ho;
+
+ ho = malloc(sizeof(struct hwt_owner), M_HWT_OWNER,
+ M_WAITOK | M_ZERO);
+ ho->p = p;
+
+ LIST_INIT(&ho->hwts);
+ mtx_init(&ho->mtx, "hwts", NULL, MTX_DEF);
+
+ return (ho);
+}
+
+void
+hwt_owner_shutdown(struct hwt_owner *ho)
+{
+ struct hwt_context *ctx;
+
+ dprintf("%s: stopping hwt owner\n", __func__);
+
+ while (1) {
+ mtx_lock(&ho->mtx);
+ ctx = LIST_FIRST(&ho->hwts);
+ if (ctx)
+ LIST_REMOVE(ctx, next_hwts);
+ mtx_unlock(&ho->mtx);
+
+ if (ctx == NULL)
+ break;
+
+ if (ctx->mode == HWT_MODE_THREAD)
+ hwt_contexthash_remove(ctx);
+
+ /*
+ * A hook could be still dealing with this ctx right here.
+ */
+
+ HWT_CTX_LOCK(ctx);
+ ctx->state = 0;
+ HWT_CTX_UNLOCK(ctx);
+
+ /* Ensure hooks invocation is now completed. */
+ while (refcount_load(&ctx->refcnt) > 0)
+ continue;
+
+ /*
+ * Note that a thread could be still sleeping on msleep(9).
+ */
+
+ hwt_backend_deinit(ctx);
+ hwt_record_free_all(ctx);
+ hwt_ctx_free(ctx);
+ }
+
+ hwt_ownerhash_remove(ho);
+ free(ho, M_HWT_OWNER);
+}
diff --git a/sys/dev/hwt/hwt_owner.h b/sys/dev/hwt/hwt_owner.h
new file mode 100644
index 000000000000..2ac569a55050
--- /dev/null
+++ b/sys/dev/hwt/hwt_owner.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_OWNER_H_
+#define _DEV_HWT_HWT_OWNER_H_
+
+struct hwt_owner {
+ struct proc *p;
+ struct mtx mtx; /* Protects hwts. */
+ LIST_HEAD(, hwt_context) hwts; /* Owned HWTs. */
+ LIST_ENTRY(hwt_owner) next; /* Entry in hwt owner hash. */
+};
+
+
+struct hwt_context * hwt_owner_lookup_ctx(struct hwt_owner *ho, pid_t pid);
+struct hwt_owner * hwt_owner_alloc(struct proc *p);
+void hwt_owner_shutdown(struct hwt_owner *ho);
+struct hwt_context * hwt_owner_lookup_ctx_by_cpu(struct hwt_owner *ho, int cpu);
+
+#endif /* !_DEV_HWT_HWT_OWNER_H_ */
diff --git a/sys/dev/hwt/hwt_ownerhash.c b/sys/dev/hwt/hwt_ownerhash.c
new file mode 100644
index 000000000000..7c9e2232bac4
--- /dev/null
+++ b/sys/dev/hwt/hwt_ownerhash.c
@@ -0,0 +1,141 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+#include <sys/hwt.h>
+
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_ownerhash.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define HWT_OWNERHASH_SIZE 1024
+
+static MALLOC_DEFINE(M_HWT_OWNERHASH, "hwt_ohash", "Hardware Trace");
+
+/*
+ * Hash function. Discard the lower 2 bits of the pointer since
+ * these are always zero for our uses. The hash multiplier is
+ * round((2^LONG_BIT) * ((sqrt(5)-1)/2)).
+ */
+
+#define _HWT_HM 11400714819323198486u /* hash multiplier */
+#define HWT_HASH_PTR(P, M) ((((unsigned long) (P) >> 2) * _HWT_HM) & (M))
+
+static struct mtx hwt_ownerhash_mtx;
+static u_long hwt_ownerhashmask;
+static LIST_HEAD(hwt_ownerhash, hwt_owner) *hwt_ownerhash;
+
+struct hwt_owner *
+hwt_ownerhash_lookup(struct proc *p)
+{
+ struct hwt_ownerhash *hoh;
+ struct hwt_owner *ho;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwt_ownerhashmask);
+ hoh = &hwt_ownerhash[hindex];
+
+ HWT_OWNERHASH_LOCK();
+ LIST_FOREACH(ho, hoh, next) {
+ if (ho->p == p) {
+ HWT_OWNERHASH_UNLOCK();
+ return (ho);
+ }
+ }
+ HWT_OWNERHASH_UNLOCK();
+
+ return (NULL);
+}
+
+void
+hwt_ownerhash_insert(struct hwt_owner *ho)
+{
+ struct hwt_ownerhash *hoh;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(ho->p, hwt_ownerhashmask);
+ hoh = &hwt_ownerhash[hindex];
+
+ HWT_OWNERHASH_LOCK();
+ LIST_INSERT_HEAD(hoh, ho, next);
+ HWT_OWNERHASH_UNLOCK();
+}
+
+void
+hwt_ownerhash_remove(struct hwt_owner *ho)
+{
+
+ /* Destroy hwt owner. */
+ HWT_OWNERHASH_LOCK();
+ LIST_REMOVE(ho, next);
+ HWT_OWNERHASH_UNLOCK();
+}
+
+void
+hwt_ownerhash_load(void)
+{
+
+ hwt_ownerhash = hashinit(HWT_OWNERHASH_SIZE, M_HWT_OWNERHASH,
+ &hwt_ownerhashmask);
+ mtx_init(&hwt_ownerhash_mtx, "hwt-owner-hash", "hwt-owner", MTX_DEF);
+}
+
+void
+hwt_ownerhash_unload(void)
+{
+ struct hwt_ownerhash *hoh;
+ struct hwt_owner *ho, *tmp;
+
+ HWT_OWNERHASH_LOCK();
+ for (hoh = hwt_ownerhash;
+ hoh <= &hwt_ownerhash[hwt_ownerhashmask];
+ hoh++) {
+ LIST_FOREACH_SAFE(ho, hoh, next, tmp) {
+ /* TODO: module is in use ? */
+ }
+ }
+ HWT_OWNERHASH_UNLOCK();
+
+ mtx_destroy(&hwt_ownerhash_mtx);
+ hashdestroy(hwt_ownerhash, M_HWT_OWNERHASH, hwt_ownerhashmask);
+}
diff --git a/sys/dev/hwt/hwt_ownerhash.h b/sys/dev/hwt/hwt_ownerhash.h
new file mode 100644
index 000000000000..4a7bc958d0f7
--- /dev/null
+++ b/sys/dev/hwt/hwt_ownerhash.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_OWNERHASH_H_
+#define _DEV_HWT_HWT_OWNERHASH_H_
+
+struct hwt_owner * hwt_ownerhash_lookup(struct proc *p);
+void hwt_ownerhash_insert(struct hwt_owner *ho);
+void hwt_ownerhash_remove(struct hwt_owner *ho);
+
+void hwt_ownerhash_load(void);
+void hwt_ownerhash_unload(void);
+
+#define HWT_OWNERHASH_LOCK() mtx_lock(&hwt_ownerhash_mtx)
+#define HWT_OWNERHASH_UNLOCK() mtx_unlock(&hwt_ownerhash_mtx)
+
+#endif /* !_DEV_HWT_HWT_OWNERHASH_H_ */
diff --git a/sys/dev/hwt/hwt_record.c b/sys/dev/hwt/hwt_record.c
new file mode 100644
index 000000000000..850ea6f8c5be
--- /dev/null
+++ b/sys/dev/hwt/hwt_record.c
@@ -0,0 +1,302 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/hwt.h>
+#include <sys/linker.h>
+#include <sys/pmckern.h> /* linker_hwpmc_list_objects */
+
+#include <vm/vm.h>
+#include <vm/uma.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_record.h>
+
+#define HWT_RECORD_DEBUG
+#undef HWT_RECORD_DEBUG
+
+#ifdef HWT_RECORD_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_RECORD, "hwt_record", "Hardware Trace");
+static uma_zone_t record_zone = NULL;
+
+static struct hwt_record_entry *
+hwt_record_clone(struct hwt_record_entry *ent, int flags)
+{
+ struct hwt_record_entry *entry;
+
+ entry = uma_zalloc(record_zone, flags);
+ if (entry == NULL)
+ return (NULL);
+ memcpy(entry, ent, sizeof(struct hwt_record_entry));
+ switch (ent->record_type) {
+ case HWT_RECORD_MMAP:
+ case HWT_RECORD_EXECUTABLE:
+ case HWT_RECORD_KERNEL:
+ entry->fullpath = strdup(ent->fullpath, M_HWT_RECORD);
+ break;
+ default:
+ break;
+ }
+
+ return (entry);
+}
+
+static void
+hwt_record_to_user(struct hwt_record_entry *ent,
+ struct hwt_record_user_entry *usr)
+{
+ usr->record_type = ent->record_type;
+ switch (ent->record_type) {
+ case HWT_RECORD_MMAP:
+ case HWT_RECORD_EXECUTABLE:
+ case HWT_RECORD_KERNEL:
+ usr->addr = ent->addr;
+ usr->baseaddr = ent->baseaddr;
+ strncpy(usr->fullpath, ent->fullpath, MAXPATHLEN);
+ break;
+ case HWT_RECORD_BUFFER:
+ usr->buf_id = ent->buf_id;
+ usr->curpage = ent->curpage;
+ usr->offset = ent->offset;
+ break;
+ case HWT_RECORD_THREAD_CREATE:
+ case HWT_RECORD_THREAD_SET_NAME:
+ usr->thread_id = ent->thread_id;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+hwt_record_load(void)
+{
+ record_zone = uma_zcreate("HWT records",
+ sizeof(struct hwt_record_entry), NULL, NULL, NULL, NULL, 0, 0);
+}
+
+void
+hwt_record_unload(void)
+{
+ uma_zdestroy(record_zone);
+}
+
+void
+hwt_record_ctx(struct hwt_context *ctx, struct hwt_record_entry *ent, int flags)
+{
+ struct hwt_record_entry *entry;
+
+ KASSERT(ent != NULL, ("ent is NULL"));
+ entry = hwt_record_clone(ent, flags);
+ if (entry == NULL) {
+ /* XXX: Not sure what to do here other than logging an error. */
+ return;
+ }
+
+ HWT_CTX_LOCK(ctx);
+ TAILQ_INSERT_TAIL(&ctx->records, entry, next);
+ HWT_CTX_UNLOCK(ctx);
+ hwt_record_wakeup(ctx);
+}
+
+void
+hwt_record_td(struct thread *td, struct hwt_record_entry *ent, int flags)
+{
+ struct hwt_record_entry *entry;
+ struct hwt_context *ctx;
+ struct proc *p;
+
+ p = td->td_proc;
+
+ KASSERT(ent != NULL, ("ent is NULL"));
+ entry = hwt_record_clone(ent, flags);
+ if (entry == NULL) {
+ /* XXX: Not sure what to do here other than logging an error. */
+ return;
+ }
+ ctx = hwt_contexthash_lookup(p);
+ if (ctx == NULL) {
+ hwt_record_entry_free(entry);
+ return;
+ }
+ HWT_CTX_LOCK(ctx);
+ TAILQ_INSERT_TAIL(&ctx->records, entry, next);
+ HWT_CTX_UNLOCK(ctx);
+ hwt_record_wakeup(ctx);
+
+ hwt_ctx_put(ctx);
+}
+
+struct hwt_record_entry *
+hwt_record_entry_alloc(void)
+{
+ return (uma_zalloc(record_zone, M_WAITOK | M_ZERO));
+}
+
+void
+hwt_record_entry_free(struct hwt_record_entry *entry)
+{
+
+ switch (entry->record_type) {
+ case HWT_RECORD_MMAP:
+ case HWT_RECORD_EXECUTABLE:
+ case HWT_RECORD_KERNEL:
+ free(entry->fullpath, M_HWT_RECORD);
+ break;
+ default:
+ break;
+ }
+
+ uma_zfree(record_zone, entry);
+}
+
+static int
+hwt_record_grab(struct hwt_context *ctx,
+ struct hwt_record_user_entry *user_entry, int nitems_req, int wait)
+{
+ struct hwt_record_entry *entry;
+ int i;
+
+ if (wait) {
+ mtx_lock(&ctx->rec_mtx);
+ if (TAILQ_FIRST(&ctx->records) == NULL) {
+ /* Wait until we have new records. */
+ msleep(ctx, &ctx->rec_mtx, PCATCH, "recsnd", 0);
+ }
+ mtx_unlock(&ctx->rec_mtx);
+ }
+
+ for (i = 0; i < nitems_req; i++) {
+ HWT_CTX_LOCK(ctx);
+ entry = TAILQ_FIRST(&ctx->records);
+ if (entry)
+ TAILQ_REMOVE_HEAD(&ctx->records, next);
+ HWT_CTX_UNLOCK(ctx);
+
+ if (entry == NULL)
+ break;
+ hwt_record_to_user(entry, &user_entry[i]);
+ hwt_record_entry_free(entry);
+ }
+
+ return (i);
+}
+
+void
+hwt_record_free_all(struct hwt_context *ctx)
+{
+ struct hwt_record_entry *entry;
+
+ while (1) {
+ HWT_CTX_LOCK(ctx);
+ entry = TAILQ_FIRST(&ctx->records);
+ if (entry)
+ TAILQ_REMOVE_HEAD(&ctx->records, next);
+ HWT_CTX_UNLOCK(ctx);
+
+ if (entry == NULL)
+ break;
+
+ hwt_record_entry_free(entry);
+ }
+}
+
+int
+hwt_record_send(struct hwt_context *ctx, struct hwt_record_get *record_get)
+{
+ struct hwt_record_user_entry *user_entry;
+ int nitems_req;
+ int error;
+ int i;
+
+ nitems_req = 0;
+
+ error = copyin(record_get->nentries, &nitems_req, sizeof(int));
+ if (error)
+ return (error);
+
+ if (nitems_req < 1 || nitems_req > 1024)
+ return (ENXIO);
+
+ user_entry = malloc(sizeof(struct hwt_record_user_entry) * nitems_req,
+ M_HWT_RECORD, M_WAITOK | M_ZERO);
+
+ i = hwt_record_grab(ctx, user_entry, nitems_req, record_get->wait);
+ if (i > 0)
+ error = copyout(user_entry, record_get->records,
+ sizeof(struct hwt_record_user_entry) * i);
+
+ if (error == 0)
+ error = copyout(&i, record_get->nentries, sizeof(int));
+
+ free(user_entry, M_HWT_RECORD);
+
+ return (error);
+}
+
+void
+hwt_record_kernel_objects(struct hwt_context *ctx)
+{
+ struct hwt_record_entry *entry;
+ struct pmckern_map_in *kobase;
+ int i;
+
+ kobase = linker_hwpmc_list_objects();
+ for (i = 0; kobase[i].pm_file != NULL; i++) {
+ entry = hwt_record_entry_alloc();
+ entry->record_type = HWT_RECORD_KERNEL;
+ entry->fullpath = strdup(kobase[i].pm_file, M_HWT_RECORD);
+ entry->addr = kobase[i].pm_address;
+
+ HWT_CTX_LOCK(ctx);
+ TAILQ_INSERT_HEAD(&ctx->records, entry, next);
+ HWT_CTX_UNLOCK(ctx);
+ }
+ free(kobase, M_LINKER);
+}
+
+void
+hwt_record_wakeup(struct hwt_context *ctx)
+{
+ wakeup(ctx);
+}
diff --git a/sys/dev/hwt/hwt_record.h b/sys/dev/hwt/hwt_record.h
new file mode 100644
index 000000000000..3f347ca67d54
--- /dev/null
+++ b/sys/dev/hwt/hwt_record.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_RECORD_H_
+#define _DEV_HWT_HWT_RECORD_H_
+
+struct hwt_record_get;
+
+void hwt_record_load(void);
+void hwt_record_unload(void);
+
+int hwt_record_send(struct hwt_context *ctx, struct hwt_record_get *record_get);
+void hwt_record_td(struct thread *td, struct hwt_record_entry *ent, int flags);
+void hwt_record_ctx(struct hwt_context *ctx, struct hwt_record_entry *ent,
+ int flags);
+struct hwt_record_entry * hwt_record_entry_alloc(void);
+void hwt_record_entry_free(struct hwt_record_entry *entry);
+void hwt_record_kernel_objects(struct hwt_context *ctx);
+void hwt_record_free_all(struct hwt_context *ctx);
+void hwt_record_wakeup(struct hwt_context *ctx);
+
+#endif /* !_DEV_HWT_HWT_RECORD_H_ */
diff --git a/sys/dev/hwt/hwt_thread.c b/sys/dev/hwt/hwt_thread.c
new file mode 100644
index 000000000000..827c068a681f
--- /dev/null
+++ b/sys/dev/hwt/hwt_thread.c
@@ -0,0 +1,162 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwt.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_phys.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_ownerhash.h>
+#include <dev/hwt/hwt_backend.h>
+#include <dev/hwt/hwt_vm.h>
+#include <dev/hwt/hwt_record.h>
+
+#define HWT_THREAD_DEBUG
+#undef HWT_THREAD_DEBUG
+
+#ifdef HWT_THREAD_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_THREAD, "hwt_thread", "Hardware Trace");
+
+struct hwt_thread *
+hwt_thread_first(struct hwt_context *ctx)
+{
+ struct hwt_thread *thr;
+
+ HWT_CTX_ASSERT_LOCKED(ctx);
+
+ thr = TAILQ_FIRST(&ctx->threads);
+
+ KASSERT(thr != NULL, ("thr is NULL"));
+
+ return (thr);
+}
+
+/*
+ * To use by hwt_switch_in/out() only.
+ */
+struct hwt_thread *
+hwt_thread_lookup(struct hwt_context *ctx, struct thread *td)
+{
+ struct hwt_thread *thr;
+
+ /* Caller of this func holds ctx refcnt right here. */
+
+ HWT_CTX_LOCK(ctx);
+ TAILQ_FOREACH(thr, &ctx->threads, next) {
+ if (thr->td == td) {
+ HWT_CTX_UNLOCK(ctx);
+ return (thr);
+ }
+ }
+ HWT_CTX_UNLOCK(ctx);
+
+ /*
+ * We are here because the hook on thread creation failed to allocate
+ * a thread.
+ */
+
+ return (NULL);
+}
+
+int
+hwt_thread_alloc(struct hwt_thread **thr0, char *path, size_t bufsize,
+ int kva_req)
+{
+ struct hwt_thread *thr;
+ struct hwt_vm *vm;
+ int error;
+
+ error = hwt_vm_alloc(bufsize, kva_req, path, &vm);
+ if (error)
+ return (error);
+
+ thr = malloc(sizeof(struct hwt_thread), M_HWT_THREAD,
+ M_WAITOK | M_ZERO);
+ thr->vm = vm;
+
+ mtx_init(&thr->mtx, "thr", NULL, MTX_DEF);
+
+ refcount_init(&thr->refcnt, 1);
+
+ vm->thr = thr;
+
+ *thr0 = thr;
+
+ return (0);
+}
+
+void
+hwt_thread_free(struct hwt_thread *thr)
+{
+
+ hwt_vm_free(thr->vm);
+ /* Free private backend data, if any. */
+ if (thr->private != NULL)
+ hwt_backend_thread_free(thr);
+ free(thr, M_HWT_THREAD);
+}
+
+/*
+ * Inserts a new thread and a thread creation record into the
+ * context notifies userspace about the newly created thread.
+ */
+void
+hwt_thread_insert(struct hwt_context *ctx, struct hwt_thread *thr,
+ struct hwt_record_entry *entry)
+{
+
+ HWT_CTX_ASSERT_LOCKED(ctx);
+ TAILQ_INSERT_TAIL(&ctx->threads, thr, next);
+ TAILQ_INSERT_TAIL(&ctx->records, entry, next);
+}
diff --git a/sys/dev/hwt/hwt_thread.h b/sys/dev/hwt/hwt_thread.h
new file mode 100644
index 000000000000..ccc29aeb3494
--- /dev/null
+++ b/sys/dev/hwt/hwt_thread.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_THREAD_H_
+#define _DEV_HWT_HWT_THREAD_H_
+
+struct hwt_record_entry;
+
+struct hwt_thread {
+ struct hwt_vm *vm;
+ struct hwt_context *ctx;
+ struct hwt_backend *backend;
+ struct thread *td;
+ TAILQ_ENTRY(hwt_thread) next;
+ int thread_id;
+ int state;
+#define HWT_THREAD_STATE_EXITED (1 << 0)
+ struct mtx mtx;
+ u_int refcnt;
+ int cpu_id; /* last cpu_id */
+ void *private; /* backend-specific private data */
+};
+
+/* Thread allocation. */
+int hwt_thread_alloc(struct hwt_thread **thr0, char *path, size_t bufsize,
+ int kva_req);
+void hwt_thread_free(struct hwt_thread *thr);
+
+/* Thread list mgt. */
+void hwt_thread_insert(struct hwt_context *ctx, struct hwt_thread *thr, struct hwt_record_entry *entry);
+struct hwt_thread * hwt_thread_first(struct hwt_context *ctx);
+struct hwt_thread * hwt_thread_lookup(struct hwt_context *ctx,
+ struct thread *td);
+
+#define HWT_THR_LOCK(thr) mtx_lock(&(thr)->mtx)
+#define HWT_THR_UNLOCK(thr) mtx_unlock(&(thr)->mtx)
+#define HWT_THR_ASSERT_LOCKED(thr) mtx_assert(&(thr)->mtx, MA_OWNED)
+
+#endif /* !_DEV_HWT_HWT_THREAD_H_ */
diff --git a/sys/dev/hwt/hwt_vm.c b/sys/dev/hwt/hwt_vm.c
new file mode 100644
index 000000000000..6c55e218dcec
--- /dev/null
+++ b/sys/dev/hwt/hwt_vm.c
@@ -0,0 +1,503 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwt.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_phys.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwt_context.h>
+#include <dev/hwt/hwt_contexthash.h>
+#include <dev/hwt/hwt_config.h>
+#include <dev/hwt/hwt_cpu.h>
+#include <dev/hwt/hwt_owner.h>
+#include <dev/hwt/hwt_ownerhash.h>
+#include <dev/hwt/hwt_thread.h>
+#include <dev/hwt/hwt_backend.h>
+#include <dev/hwt/hwt_vm.h>
+#include <dev/hwt/hwt_record.h>
+
+#define HWT_THREAD_DEBUG
+#undef HWT_THREAD_DEBUG
+
+#ifdef HWT_THREAD_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_VM, "hwt_vm", "Hardware Trace");
+
+static int
+hwt_vm_fault(vm_object_t vm_obj, vm_ooffset_t offset,
+ int prot, vm_page_t *mres)
+{
+
+ return (0);
+}
+
+static int
+hwt_vm_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
+ vm_ooffset_t foff, struct ucred *cred, u_short *color)
+{
+
+ *color = 0;
+
+ return (0);
+}
+
+static void
+hwt_vm_dtor(void *handle)
+{
+
+}
+
+static struct cdev_pager_ops hwt_vm_pager_ops = {
+ .cdev_pg_fault = hwt_vm_fault,
+ .cdev_pg_ctor = hwt_vm_ctor,
+ .cdev_pg_dtor = hwt_vm_dtor
+};
+
+static int
+hwt_vm_alloc_pages(struct hwt_vm *vm, int kva_req)
+{
+ vm_paddr_t low, high, boundary;
+ vm_memattr_t memattr;
+#ifdef __aarch64__
+ uintptr_t va;
+#endif
+ int alignment;
+ vm_page_t m;
+ int pflags;
+ int tries;
+ int i;
+
+ alignment = PAGE_SIZE;
+ low = 0;
+ high = -1UL;
+ boundary = 0;
+ pflags = VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_ZERO;
+ memattr = VM_MEMATTR_DEVICE;
+
+ if (kva_req) {
+ vm->kvaddr = kva_alloc(vm->npages * PAGE_SIZE);
+ if (!vm->kvaddr)
+ return (ENOMEM);
+ }
+
+ vm->obj = cdev_pager_allocate(vm, OBJT_MGTDEVICE,
+ &hwt_vm_pager_ops, vm->npages * PAGE_SIZE, PROT_READ, 0,
+ curthread->td_ucred);
+
+ for (i = 0; i < vm->npages; i++) {
+ tries = 0;
+retry:
+ m = vm_page_alloc_noobj_contig(pflags, 1, low, high,
+ alignment, boundary, memattr);
+ if (m == NULL) {
+ if (tries < 3) {
+ if (!vm_page_reclaim_contig(pflags, 1, low,
+ high, alignment, boundary))
+ vm_wait(NULL);
+ tries++;
+ goto retry;
+ }
+
+ return (ENOMEM);
+ }
+
+#if 0
+ /* TODO: could not clean device memory on arm64. */
+ if ((m->flags & PG_ZERO) == 0)
+ pmap_zero_page(m);
+#endif
+
+#ifdef __aarch64__
+ va = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m));
+ cpu_dcache_wb_range((void *)va, PAGE_SIZE);
+#endif
+
+ m->valid = VM_PAGE_BITS_ALL;
+ m->oflags &= ~VPO_UNMANAGED;
+ m->flags |= PG_FICTITIOUS;
+ vm->pages[i] = m;
+
+ VM_OBJECT_WLOCK(vm->obj);
+ vm_page_insert(m, vm->obj, i);
+ if (kva_req)
+ pmap_qenter(vm->kvaddr + i * PAGE_SIZE, &m, 1);
+ VM_OBJECT_WUNLOCK(vm->obj);
+ }
+
+ return (0);
+}
+
+static int
+hwt_vm_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
+{
+
+ dprintf("%s\n", __func__);
+
+ return (0);
+}
+
+static int
+hwt_vm_mmap_single(struct cdev *cdev, vm_ooffset_t *offset,
+ vm_size_t mapsize, struct vm_object **objp, int nprot)
+{
+ struct hwt_vm *vm;
+
+ vm = cdev->si_drv1;
+
+ if (nprot != PROT_READ || *offset != 0)
+ return (ENXIO);
+
+ vm_object_reference(vm->obj);
+ *objp = vm->obj;
+
+ return (0);
+}
+
+static void
+hwt_vm_start_cpu_mode(struct hwt_context *ctx)
+{
+ cpuset_t enable_cpus;
+ int cpu_id;
+
+ CPU_ZERO(&enable_cpus);
+
+ CPU_FOREACH_ISSET(cpu_id, &ctx->cpu_map) {
+#ifdef SMP
+ /* Ensure CPU is not halted. */
+ if (CPU_ISSET(cpu_id, &hlt_cpus_mask))
+ continue;
+#endif
+
+ hwt_backend_configure(ctx, cpu_id, cpu_id);
+
+ CPU_SET(cpu_id, &enable_cpus);
+ }
+
+ if (ctx->hwt_backend->ops->hwt_backend_enable_smp == NULL) {
+ CPU_FOREACH_ISSET(cpu_id, &enable_cpus)
+ hwt_backend_enable(ctx, cpu_id);
+ } else {
+ /* Some backends require enabling all CPUs at once. */
+ hwt_backend_enable_smp(ctx);
+ }
+}
+
+static int
+hwt_vm_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+ struct hwt_record_get *rget;
+ struct hwt_set_config *sconf;
+ struct hwt_bufptr_get *ptr_get;
+ struct hwt_svc_buf *sbuf;
+
+ struct hwt_context *ctx;
+ struct hwt_vm *vm;
+ struct hwt_owner *ho;
+
+ vm_offset_t offset;
+ int ident;
+ int error;
+ uint64_t data = 0;
+ void *data2;
+ size_t data_size;
+ int data_version;
+
+ vm = dev->si_drv1;
+ KASSERT(vm != NULL, ("si_drv1 is NULL"));
+
+ ctx = vm->ctx;
+
+ /* Ensure process is registered owner of this HWT. */
+ ho = hwt_ownerhash_lookup(td->td_proc);
+ if (ho == NULL)
+ return (ENXIO);
+
+ if (ctx->hwt_owner != ho)
+ return (EPERM);
+
+ switch (cmd) {
+ case HWT_IOC_START:
+ dprintf("%s: start tracing\n", __func__);
+
+ HWT_CTX_LOCK(ctx);
+ if (ctx->state == CTX_STATE_RUNNING) {
+ /* Already running ? */
+ HWT_CTX_UNLOCK(ctx);
+ return (ENXIO);
+ }
+ ctx->state = CTX_STATE_RUNNING;
+ HWT_CTX_UNLOCK(ctx);
+
+ if (ctx->mode == HWT_MODE_CPU)
+ hwt_vm_start_cpu_mode(ctx);
+ else {
+ /*
+ * Tracing backend will be configured and enabled
+ * during hook invocation. See hwt_hook.c.
+ */
+ }
+
+ break;
+
+ case HWT_IOC_STOP:
+ if (ctx->state == CTX_STATE_STOPPED)
+ return (ENXIO);
+ hwt_backend_stop(ctx);
+ ctx->state = CTX_STATE_STOPPED;
+ break;
+
+ case HWT_IOC_RECORD_GET:
+ rget = (struct hwt_record_get *)addr;
+ error = hwt_record_send(ctx, rget);
+ if (error)
+ return (error);
+ break;
+
+ case HWT_IOC_SET_CONFIG:
+ if (ctx->state == CTX_STATE_RUNNING) {
+ return (ENXIO);
+ }
+ sconf = (struct hwt_set_config *)addr;
+ error = hwt_config_set(td, ctx, sconf);
+ if (error)
+ return (error);
+ ctx->pause_on_mmap = sconf->pause_on_mmap ? 1 : 0;
+ break;
+
+ case HWT_IOC_WAKEUP:
+
+ if (ctx->mode == HWT_MODE_CPU)
+ return (ENXIO);
+
+ KASSERT(vm->thr != NULL, ("thr is NULL"));
+
+ wakeup(vm->thr);
+
+ break;
+
+ case HWT_IOC_BUFPTR_GET:
+ ptr_get = (struct hwt_bufptr_get *)addr;
+
+ error = hwt_backend_read(ctx, vm, &ident, &offset, &data);
+ if (error)
+ return (error);
+
+ if (ptr_get->ident)
+ error = copyout(&ident, ptr_get->ident, sizeof(int));
+ if (error)
+ return (error);
+
+ if (ptr_get->offset)
+ error = copyout(&offset, ptr_get->offset,
+ sizeof(vm_offset_t));
+ if (error)
+ return (error);
+
+ if (ptr_get->data)
+ error = copyout(&data, ptr_get->data, sizeof(uint64_t));
+ if (error)
+ return (error);
+
+ break;
+
+ case HWT_IOC_SVC_BUF:
+ if (ctx->state == CTX_STATE_STOPPED) {
+ return (ENXIO);
+ }
+
+ sbuf = (struct hwt_svc_buf *)addr;
+ data_size = sbuf->data_size;
+ data_version = sbuf->data_version;
+
+ if (data_size == 0 || data_size > PAGE_SIZE)
+ return (EINVAL);
+
+ data2 = malloc(data_size, M_HWT_VM, M_WAITOK | M_ZERO);
+ error = copyin(sbuf->data, data2, data_size);
+ if (error) {
+ free(data2, M_HWT_VM);
+ return (error);
+ }
+
+ error = hwt_backend_svc_buf(ctx, data2, data_size, data_version);
+ if (error) {
+ free(data2, M_HWT_VM);
+ return (error);
+ }
+
+ free(data2, M_HWT_VM);
+ break;
+
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static struct cdevsw hwt_vm_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "hwt",
+ .d_open = hwt_vm_open,
+ .d_mmap_single = hwt_vm_mmap_single,
+ .d_ioctl = hwt_vm_ioctl,
+};
+
+static int
+hwt_vm_create_cdev(struct hwt_vm *vm, char *path)
+{
+ struct make_dev_args args;
+ int error;
+
+ dprintf("%s: path %s\n", __func__, path);
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &hwt_vm_cdevsw;
+ args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0660;
+ args.mda_si_drv1 = vm;
+
+ error = make_dev_s(&args, &vm->cdev, "%s", path);
+ if (error != 0)
+ return (error);
+
+ return (0);
+}
+
+static int
+hwt_vm_alloc_buffers(struct hwt_vm *vm, int kva_req)
+{
+ int error;
+
+ vm->pages = malloc(sizeof(struct vm_page *) * vm->npages,
+ M_HWT_VM, M_WAITOK | M_ZERO);
+
+ error = hwt_vm_alloc_pages(vm, kva_req);
+ if (error) {
+ printf("%s: could not alloc pages\n", __func__);
+ return (error);
+ }
+
+ return (0);
+}
+
+static void
+hwt_vm_destroy_buffers(struct hwt_vm *vm)
+{
+ vm_page_t m;
+ int i;
+
+ if (vm->ctx->hwt_backend->kva_req && vm->kvaddr != 0) {
+ pmap_qremove(vm->kvaddr, vm->npages);
+ kva_free(vm->kvaddr, vm->npages * PAGE_SIZE);
+ }
+ VM_OBJECT_WLOCK(vm->obj);
+ for (i = 0; i < vm->npages; i++) {
+ m = vm->pages[i];
+ if (m == NULL)
+ break;
+
+ vm_page_busy_acquire(m, 0);
+ cdev_pager_free_page(vm->obj, m);
+ m->flags &= ~PG_FICTITIOUS;
+ vm_page_unwire_noq(m);
+ vm_page_free(m);
+
+ }
+ vm_pager_deallocate(vm->obj);
+ VM_OBJECT_WUNLOCK(vm->obj);
+
+ free(vm->pages, M_HWT_VM);
+}
+
+void
+hwt_vm_free(struct hwt_vm *vm)
+{
+
+ dprintf("%s\n", __func__);
+
+ if (vm->cdev)
+ destroy_dev_sched(vm->cdev);
+ hwt_vm_destroy_buffers(vm);
+ free(vm, M_HWT_VM);
+}
+
+int
+hwt_vm_alloc(size_t bufsize, int kva_req, char *path, struct hwt_vm **vm0)
+{
+ struct hwt_vm *vm;
+ int error;
+
+ vm = malloc(sizeof(struct hwt_vm), M_HWT_VM, M_WAITOK | M_ZERO);
+ vm->npages = bufsize / PAGE_SIZE;
+
+ error = hwt_vm_alloc_buffers(vm, kva_req);
+ if (error) {
+ free(vm, M_HWT_VM);
+ return (error);
+ }
+
+ error = hwt_vm_create_cdev(vm, path);
+ if (error) {
+ hwt_vm_free(vm);
+ return (error);
+ }
+
+ *vm0 = vm;
+
+ return (0);
+}
diff --git a/sys/dev/hwt/hwt_vm.h b/sys/dev/hwt/hwt_vm.h
new file mode 100644
index 000000000000..5002bd43e093
--- /dev/null
+++ b/sys/dev/hwt/hwt_vm.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 _DEV_HWT_HWT_VM_H_
+#define _DEV_HWT_HWT_VM_H_
+
+struct hwt_vm {
+ vm_page_t *pages;
+ int npages;
+ vm_object_t obj;
+ vm_offset_t kvaddr;
+ struct cdev *cdev;
+
+ struct hwt_context *ctx;
+ struct hwt_cpu *cpu; /* cpu mode only. */
+ struct hwt_thread *thr; /* thr mode only. */
+};
+
+int hwt_vm_alloc(size_t bufsize, int kva_req, char *path, struct hwt_vm **vm0);
+void hwt_vm_free(struct hwt_vm *vm);
+
+#endif /* !_DEV_HWT_HWT_VM_H_ */
diff --git a/sys/dev/ichiic/ig4_pci.c b/sys/dev/ichiic/ig4_pci.c
index 0195466150eb..3a49e220e335 100644
--- a/sys/dev/ichiic/ig4_pci.c
+++ b/sys/dev/ichiic/ig4_pci.c
@@ -186,6 +186,12 @@ static int ig4iic_pci_detach(device_t dev);
#define PCI_CHIP_METEORLAKE_M_I2C_3 0x7e518086
#define PCI_CHIP_METEORLAKE_M_I2C_4 0x7e7a8086
#define PCI_CHIP_METEORLAKE_M_I2C_5 0x7e7b8086
+#define PCI_CHIP_ARROWLAKE_U_I2C_0 0x77788086
+#define PCI_CHIP_ARROWLAKE_U_I2C_1 0x77798086
+#define PCI_CHIP_ARROWLAKE_U_I2C_2 0x777a8086
+#define PCI_CHIP_ARROWLAKE_U_I2C_3 0x777b8086
+#define PCI_CHIP_ARROWLAKE_U_I2C_4 0x77508086
+#define PCI_CHIP_ARROWLAKE_U_I2C_5 0x77518086
struct ig4iic_pci_device {
uint32_t devid;
@@ -316,6 +322,12 @@ static struct ig4iic_pci_device ig4iic_pci_devices[] = {
{ PCI_CHIP_METEORLAKE_M_I2C_3, "Intel Meteor Lake-M I2C Controller-3", IG4_TIGERLAKE},
{ PCI_CHIP_METEORLAKE_M_I2C_4, "Intel Meteor Lake-M I2C Controller-4", IG4_TIGERLAKE},
{ PCI_CHIP_METEORLAKE_M_I2C_5, "Intel Meteor Lake-M I2C Controller-5", IG4_TIGERLAKE},
+ { PCI_CHIP_ARROWLAKE_U_I2C_0, "Intel Arrow Lake-H/U I2C Controller-0", IG4_TIGERLAKE},
+ { PCI_CHIP_ARROWLAKE_U_I2C_1, "Intel Arrow Lake-H/U I2C Controller-1", IG4_TIGERLAKE},
+ { PCI_CHIP_ARROWLAKE_U_I2C_2, "Intel Arrow Lake-H/U I2C Controller-2", IG4_TIGERLAKE},
+ { PCI_CHIP_ARROWLAKE_U_I2C_3, "Intel Arrow Lake-H/U I2C Controller-3", IG4_TIGERLAKE},
+ { PCI_CHIP_ARROWLAKE_U_I2C_4, "Intel Arrow Lake-H/U I2C Controller-4", IG4_TIGERLAKE},
+ { PCI_CHIP_ARROWLAKE_U_I2C_5, "Intel Arrow Lake-H/U I2C Controller-5", IG4_TIGERLAKE},
};
static int
diff --git a/sys/dev/iicbus/gpio/tca64xx.c b/sys/dev/iicbus/gpio/tca64xx.c
index 3b3bca9936f1..cd011ae9be75 100644
--- a/sys/dev/iicbus/gpio/tca64xx.c
+++ b/sys/dev/iicbus/gpio/tca64xx.c
@@ -261,14 +261,13 @@ tca64xx_attach(device_t dev)
sc->addr = iicbus_get_addr(dev);
mtx_init(&sc->mtx, "tca64xx gpio", "gpio", MTX_DEF);
+ OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
sc->busdev = gpiobus_attach_bus(dev);
if (sc->busdev == NULL) {
device_printf(dev, "Could not create busdev child\n");
return (ENXIO);
}
- OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
-
#ifdef DEBUG
switch (sc->chip) {
case TCA6416_TYPE:
diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c
index b842d4f2fd8e..741a7c013f7d 100644
--- a/sys/dev/md/md.c
+++ b/sys/dev/md/md.c
@@ -1559,19 +1559,26 @@ mddestroy(struct md_s *sc, struct thread *td)
mtx_destroy(&sc->queue_mtx);
switch (sc->type) {
case MD_VNODE:
- vn_lock(sc->s_vnode.vnode, LK_EXCLUSIVE | LK_RETRY);
- sc->s_vnode.vnode->v_vflag &= ~VV_MD;
- VOP_UNLOCK(sc->s_vnode.vnode);
- (void)vn_close(sc->s_vnode.vnode, sc->flags & MD_READONLY ?
- FREAD : (FREAD|FWRITE), sc->cred, td);
- kva_free(sc->s_vnode.kva, maxphys + PAGE_SIZE);
+ if (sc->s_vnode.vnode != NULL) {
+ vn_lock(sc->s_vnode.vnode, LK_EXCLUSIVE | LK_RETRY);
+ sc->s_vnode.vnode->v_vflag &= ~VV_MD;
+ VOP_UNLOCK(sc->s_vnode.vnode);
+ (void)vn_close(sc->s_vnode.vnode,
+ sc->flags & MD_READONLY ? FREAD : (FREAD|FWRITE),
+ sc->cred, td);
+ }
+ if (sc->s_vnode.kva != 0)
+ kva_free(sc->s_vnode.kva, maxphys + PAGE_SIZE);
break;
case MD_SWAP:
- vm_object_deallocate(sc->s_swap.object);
+ if (sc->s_swap.object != NULL)
+ vm_object_deallocate(sc->s_swap.object);
break;
case MD_MALLOC:
- destroy_indir(sc, sc->s_malloc.indir);
- uma_zdestroy(sc->s_malloc.uma);
+ if (sc->s_malloc.indir != NULL)
+ destroy_indir(sc, sc->s_malloc.indir);
+ if (sc->s_malloc.uma != NULL)
+ uma_zdestroy(sc->s_malloc.uma);
break;
case MD_PRELOAD:
case MD_NULL:
diff --git a/sys/dev/mem/memutil.c b/sys/dev/mem/memutil.c
index cf9714d6ec8f..20ce337df0ab 100644
--- a/sys/dev/mem/memutil.c
+++ b/sys/dev/mem/memutil.c
@@ -26,15 +26,14 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/param.h>
+#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/memrange.h>
-#include <sys/rwlock.h>
-#include <sys/systm.h>
+#include <sys/sx.h>
-static struct rwlock mr_lock;
+static struct sx mr_lock;
/*
* Implementation-neutral, kernel-callable functions for manipulating
@@ -46,7 +45,7 @@ mem_range_init(void)
if (mem_range_softc.mr_op == NULL)
return;
- rw_init(&mr_lock, "memrange");
+ sx_init(&mr_lock, "memrange");
mem_range_softc.mr_op->init(&mem_range_softc);
}
@@ -56,7 +55,7 @@ mem_range_destroy(void)
if (mem_range_softc.mr_op == NULL)
return;
- rw_destroy(&mr_lock);
+ sx_destroy(&mr_lock);
}
int
@@ -67,12 +66,12 @@ mem_range_attr_get(struct mem_range_desc *mrd, int *arg)
if (mem_range_softc.mr_op == NULL)
return (EOPNOTSUPP);
nd = *arg;
- rw_rlock(&mr_lock);
+ sx_slock(&mr_lock);
if (nd == 0)
*arg = mem_range_softc.mr_ndesc;
else
bcopy(mem_range_softc.mr_desc, mrd, nd * sizeof(*mrd));
- rw_runlock(&mr_lock);
+ sx_sunlock(&mr_lock);
return (0);
}
@@ -83,8 +82,8 @@ mem_range_attr_set(struct mem_range_desc *mrd, int *arg)
if (mem_range_softc.mr_op == NULL)
return (EOPNOTSUPP);
- rw_wlock(&mr_lock);
+ sx_xlock(&mr_lock);
ret = mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg);
- rw_wunlock(&mr_lock);
+ sx_xunlock(&mr_lock);
return (ret);
}
diff --git a/sys/dev/nvmf/host/nvmf.c b/sys/dev/nvmf/host/nvmf.c
index dbdd4568bdf1..1ac0d142443b 100644
--- a/sys/dev/nvmf/host/nvmf.c
+++ b/sys/dev/nvmf/host/nvmf.c
@@ -27,6 +27,7 @@
#include <dev/nvmf/host/nvmf_var.h>
static struct cdevsw nvmf_cdevsw;
+static struct taskqueue *nvmf_tq;
bool nvmf_fail_disconnect = false;
SYSCTL_BOOL(_kern_nvmf, OID_AUTO, fail_on_disconnection, CTLFLAG_RWTUN,
@@ -34,7 +35,10 @@ SYSCTL_BOOL(_kern_nvmf, OID_AUTO, fail_on_disconnection, CTLFLAG_RWTUN,
MALLOC_DEFINE(M_NVMF, "nvmf", "NVMe over Fabrics host");
+static void nvmf_controller_loss_task(void *arg, int pending);
static void nvmf_disconnect_task(void *arg, int pending);
+static void nvmf_request_reconnect(struct nvmf_softc *sc);
+static void nvmf_request_reconnect_task(void *arg, int pending);
static void nvmf_shutdown_pre_sync(void *arg, int howto);
static void nvmf_shutdown_post_sync(void *arg, int howto);
@@ -294,6 +298,9 @@ nvmf_establish_connection(struct nvmf_softc *sc, nvlist_t *nvl)
admin = nvlist_get_nvlist(nvl, "admin");
io = nvlist_get_nvlist_array(nvl, "io", &num_io_queues);
kato = dnvlist_get_number(nvl, "kato", 0);
+ sc->reconnect_delay = dnvlist_get_number(nvl, "reconnect_delay", 0);
+ sc->controller_loss_timeout = dnvlist_get_number(nvl,
+ "controller_loss_timeout", 0);
/* Setup the admin queue. */
sc->admin = nvmf_init_qp(sc, trtype, admin, "admin queue", 0);
@@ -504,6 +511,10 @@ nvmf_attach(device_t dev)
callout_init(&sc->ka_tx_timer, 1);
sx_init(&sc->connection_lock, "nvmf connection");
TASK_INIT(&sc->disconnect_task, 0, nvmf_disconnect_task, sc);
+ TIMEOUT_TASK_INIT(nvmf_tq, &sc->controller_loss_task, 0,
+ nvmf_controller_loss_task, sc);
+ TIMEOUT_TASK_INIT(nvmf_tq, &sc->request_reconnect_task, 0,
+ nvmf_request_reconnect_task, sc);
oid = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "ioq",
@@ -603,7 +614,9 @@ out:
nvmf_destroy_aer(sc);
- taskqueue_drain(taskqueue_thread, &sc->disconnect_task);
+ taskqueue_drain_timeout(nvmf_tq, &sc->request_reconnect_task);
+ taskqueue_drain_timeout(nvmf_tq, &sc->controller_loss_task);
+ taskqueue_drain(nvmf_tq, &sc->disconnect_task);
sx_destroy(&sc->connection_lock);
nvlist_destroy(sc->rparams);
free(sc->cdata, M_NVMF);
@@ -613,7 +626,7 @@ out:
void
nvmf_disconnect(struct nvmf_softc *sc)
{
- taskqueue_enqueue(taskqueue_thread, &sc->disconnect_task);
+ taskqueue_enqueue(nvmf_tq, &sc->disconnect_task);
}
static void
@@ -676,6 +689,74 @@ nvmf_disconnect_task(void *arg, int pending __unused)
nvmf_destroy_qp(sc->admin);
sc->admin = NULL;
+ if (sc->reconnect_delay != 0)
+ nvmf_request_reconnect(sc);
+ if (sc->controller_loss_timeout != 0)
+ taskqueue_enqueue_timeout(nvmf_tq,
+ &sc->controller_loss_task, sc->controller_loss_timeout *
+ hz);
+
+ sx_xunlock(&sc->connection_lock);
+}
+
+static void
+nvmf_controller_loss_task(void *arg, int pending)
+{
+ struct nvmf_softc *sc = arg;
+ device_t dev;
+ int error;
+
+ bus_topo_lock();
+ sx_xlock(&sc->connection_lock);
+ if (sc->admin != NULL || sc->detaching) {
+ /* Reconnected or already detaching. */
+ sx_xunlock(&sc->connection_lock);
+ bus_topo_unlock();
+ return;
+ }
+
+ sc->controller_timedout = true;
+ sx_xunlock(&sc->connection_lock);
+
+ /*
+ * XXX: Doing this from here is a bit ugly. We don't have an
+ * extra reference on `dev` but bus_topo_lock should block any
+ * concurrent device_delete_child invocations.
+ */
+ dev = sc->dev;
+ error = device_delete_child(root_bus, dev);
+ if (error != 0)
+ device_printf(dev,
+ "failed to detach after controller loss: %d\n", error);
+ bus_topo_unlock();
+}
+
+static void
+nvmf_request_reconnect(struct nvmf_softc *sc)
+{
+ char buf[64];
+
+ sx_assert(&sc->connection_lock, SX_LOCKED);
+
+ snprintf(buf, sizeof(buf), "name=\"%s\"", device_get_nameunit(sc->dev));
+ devctl_notify("nvme", "controller", "RECONNECT", buf);
+ taskqueue_enqueue_timeout(nvmf_tq, &sc->request_reconnect_task,
+ sc->reconnect_delay * hz);
+}
+
+static void
+nvmf_request_reconnect_task(void *arg, int pending)
+{
+ struct nvmf_softc *sc = arg;
+
+ sx_xlock(&sc->connection_lock);
+ if (sc->admin != NULL || sc->detaching || sc->controller_timedout) {
+ /* Reconnected or already detaching. */
+ sx_xunlock(&sc->connection_lock);
+ return;
+ }
+
+ nvmf_request_reconnect(sc);
sx_xunlock(&sc->connection_lock);
}
@@ -699,7 +780,7 @@ nvmf_reconnect_host(struct nvmf_softc *sc, struct nvmf_ioc_nv *nv)
}
sx_xlock(&sc->connection_lock);
- if (sc->admin != NULL || sc->detaching) {
+ if (sc->admin != NULL || sc->detaching || sc->controller_timedout) {
error = EBUSY;
goto out;
}
@@ -745,6 +826,9 @@ nvmf_reconnect_host(struct nvmf_softc *sc, struct nvmf_ioc_nv *nv)
nvmf_reconnect_sim(sc);
nvmf_rescan_all_ns(sc);
+
+ taskqueue_cancel_timeout(nvmf_tq, &sc->request_reconnect_task, NULL);
+ taskqueue_cancel_timeout(nvmf_tq, &sc->controller_loss_task, NULL);
out:
sx_xunlock(&sc->connection_lock);
nvlist_destroy(nvl);
@@ -852,7 +936,21 @@ nvmf_detach(device_t dev)
}
free(sc->io, M_NVMF);
- taskqueue_drain(taskqueue_thread, &sc->disconnect_task);
+ taskqueue_drain(nvmf_tq, &sc->disconnect_task);
+ if (taskqueue_cancel_timeout(nvmf_tq, &sc->request_reconnect_task,
+ NULL) != 0)
+ taskqueue_drain_timeout(nvmf_tq, &sc->request_reconnect_task);
+
+ /*
+ * Don't cancel/drain the controller loss task if that task
+ * has fired and is triggering the detach.
+ */
+ if (!sc->controller_timedout) {
+ if (taskqueue_cancel_timeout(nvmf_tq, &sc->controller_loss_task,
+ NULL) != 0)
+ taskqueue_drain_timeout(nvmf_tq,
+ &sc->controller_loss_task);
+ }
if (sc->admin != NULL)
nvmf_destroy_qp(sc->admin);
@@ -1154,14 +1252,25 @@ static struct cdevsw nvmf_cdevsw = {
static int
nvmf_modevent(module_t mod, int what, void *arg)
{
+ int error;
+
switch (what) {
case MOD_LOAD:
- return (nvmf_ctl_load());
+ error = nvmf_ctl_load();
+ if (error != 0)
+ return (error);
+
+ nvmf_tq = taskqueue_create("nvmf", M_WAITOK | M_ZERO,
+ taskqueue_thread_enqueue, &nvmf_tq);
+ taskqueue_start_threads(&nvmf_tq, 1, PWAIT, "nvmf taskq");
+ return (0);
case MOD_QUIESCE:
return (0);
case MOD_UNLOAD:
nvmf_ctl_unload();
destroy_dev_drain(&nvmf_cdevsw);
+ if (nvmf_tq != NULL)
+ taskqueue_free(nvmf_tq);
return (0);
default:
return (EOPNOTSUPP);
diff --git a/sys/dev/nvmf/host/nvmf_var.h b/sys/dev/nvmf/host/nvmf_var.h
index e45a31f413a4..606245b3969c 100644
--- a/sys/dev/nvmf/host/nvmf_var.h
+++ b/sys/dev/nvmf/host/nvmf_var.h
@@ -75,9 +75,15 @@ struct nvmf_softc {
struct callout ka_rx_timer;
sbintime_t ka_rx_sbt;
+ struct timeout_task request_reconnect_task;
+ struct timeout_task controller_loss_task;
+ uint32_t reconnect_delay;
+ uint32_t controller_loss_timeout;
+
struct sx connection_lock;
struct task disconnect_task;
bool detaching;
+ bool controller_timedout;
u_int num_aer;
struct nvmf_aer *aer;
diff --git a/sys/dev/nvmf/nvmf.h b/sys/dev/nvmf/nvmf.h
index d4e7b1511e9d..9b2b4c1dea40 100644
--- a/sys/dev/nvmf/nvmf.h
+++ b/sys/dev/nvmf/nvmf.h
@@ -27,6 +27,13 @@
#define NVMF_NN (1024)
/*
+ * Default timeouts for Fabrics hosts. These match values used by
+ * Linux.
+ */
+#define NVMF_DEFAULT_RECONNECT_DELAY 10
+#define NVMF_DEFAULT_CONTROLLER_LOSS 600
+
+/*
* (data, size) is the userspace buffer for a packed nvlist.
*
* For requests that copyout an nvlist, len is the amount of data
@@ -68,6 +75,8 @@ struct nvmf_ioc_nv {
*
* number trtype
* number kato (optional)
+ * number reconnect_delay (optional)
+ * number controller_loss_timeout (optional)
* qpair handoff nvlist admin
* qpair handoff nvlist array io
* binary cdata struct nvme_controller_data
@@ -81,6 +90,8 @@ struct nvmf_ioc_nv {
* string hostnqn
* number num_io_queues
* number kato (optional)
+ * number reconnect_delay (optional)
+ * number controller_loss_timeout (optional)
* number io_qsize
* bool sq_flow_control
*
diff --git a/sys/dev/random/random_harvestq.c b/sys/dev/random/random_harvestq.c
index 379b64ac15f1..395310b115fb 100644
--- a/sys/dev/random/random_harvestq.c
+++ b/sys/dev/random/random_harvestq.c
@@ -131,36 +131,25 @@ static struct harvest_context {
/* The context of the kernel thread processing harvested entropy */
struct proc *hc_kthread_proc;
/*
- * Lockless ring buffer holding entropy events
- * If ring.in == ring.out,
- * the buffer is empty.
- * If ring.in != ring.out,
- * the buffer contains harvested entropy.
- * If (ring.in + 1) == ring.out (mod RANDOM_RING_MAX),
- * the buffer is full.
- *
- * NOTE: ring.in points to the last added element,
- * and ring.out points to the last consumed element.
- *
- * The ring.in variable needs locking as there are multiple
- * sources to the ring. Only the sources may change ring.in,
- * but the consumer may examine it.
- *
- * The ring.out variable does not need locking as there is
- * only one consumer. Only the consumer may change ring.out,
- * but the sources may examine it.
+ * A pair of buffers for queued events. New events are added to the
+ * active queue while the kthread processes the other one in parallel.
*/
- struct entropy_ring {
+ struct entropy_buffer {
struct harvest_event ring[RANDOM_RING_MAX];
- volatile u_int in;
- volatile u_int out;
- } hc_entropy_ring;
+ u_int pos;
+ } hc_entropy_buf[2];
+ u_int hc_active_buf;
struct fast_entropy_accumulator {
volatile u_int pos;
uint32_t buf[RANDOM_ACCUM_MAX];
} hc_entropy_fast_accumulator;
} harvest_context;
+#define RANDOM_HARVEST_INIT_LOCK() mtx_init(&harvest_context.hc_mtx, \
+ "entropy harvest mutex", NULL, MTX_SPIN)
+#define RANDOM_HARVEST_LOCK() mtx_lock_spin(&harvest_context.hc_mtx)
+#define RANDOM_HARVEST_UNLOCK() mtx_unlock_spin(&harvest_context.hc_mtx)
+
static struct kproc_desc random_proc_kp = {
"rand_harvestq",
random_kthread,
@@ -178,43 +167,48 @@ random_harvestq_fast_process_event(struct harvest_event *event)
static void
random_kthread(void)
{
- u_int maxloop, ring_out, i;
+ struct harvest_context *hc;
- /*
- * Locking is not needed as this is the only place we modify ring.out, and
- * we only examine ring.in without changing it. Both of these are volatile,
- * and this is a unique thread.
- */
+ hc = &harvest_context;
for (random_kthread_control = 1; random_kthread_control;) {
- /* Deal with events, if any. Restrict the number we do in one go. */
- maxloop = RANDOM_RING_MAX;
- while (harvest_context.hc_entropy_ring.out != harvest_context.hc_entropy_ring.in) {
- ring_out = (harvest_context.hc_entropy_ring.out + 1)%RANDOM_RING_MAX;
- random_harvestq_fast_process_event(harvest_context.hc_entropy_ring.ring + ring_out);
- harvest_context.hc_entropy_ring.out = ring_out;
- if (!--maxloop)
- break;
- }
+ struct entropy_buffer *buf;
+ u_int entries;
+
+ /* Deal with queued events. */
+ RANDOM_HARVEST_LOCK();
+ buf = &hc->hc_entropy_buf[hc->hc_active_buf];
+ entries = buf->pos;
+ buf->pos = 0;
+ hc->hc_active_buf = (hc->hc_active_buf + 1) %
+ nitems(hc->hc_entropy_buf);
+ RANDOM_HARVEST_UNLOCK();
+ for (u_int i = 0; i < entries; i++)
+ random_harvestq_fast_process_event(&buf->ring[i]);
+
+ /* Poll sources of noise. */
random_sources_feed();
+
/* XXX: FIX!! Increase the high-performance data rate? Need some measurements first. */
- for (i = 0; i < RANDOM_ACCUM_MAX; i++) {
- if (harvest_context.hc_entropy_fast_accumulator.buf[i]) {
- random_harvest_direct(harvest_context.hc_entropy_fast_accumulator.buf + i, sizeof(harvest_context.hc_entropy_fast_accumulator.buf[0]), RANDOM_UMA);
- harvest_context.hc_entropy_fast_accumulator.buf[i] = 0;
+ for (u_int i = 0; i < RANDOM_ACCUM_MAX; i++) {
+ if (hc->hc_entropy_fast_accumulator.buf[i]) {
+ random_harvest_direct(&hc->hc_entropy_fast_accumulator.buf[i],
+ sizeof(hc->hc_entropy_fast_accumulator.buf[0]), RANDOM_UMA);
+ hc->hc_entropy_fast_accumulator.buf[i] = 0;
}
}
/* XXX: FIX!! This is a *great* place to pass hardware/live entropy to random(9) */
- tsleep_sbt(&harvest_context.hc_kthread_proc, 0, "-",
+ tsleep_sbt(&hc->hc_kthread_proc, 0, "-",
SBT_1S/RANDOM_KTHREAD_HZ, 0, C_PREL(1));
}
random_kthread_control = -1;
- wakeup(&harvest_context.hc_kthread_proc);
+ wakeup(&hc->hc_kthread_proc);
kproc_exit(0);
/* NOTREACHED */
}
-/* This happens well after SI_SUB_RANDOM */
SYSINIT(random_device_h_proc, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, kproc_start,
&random_proc_kp);
+_Static_assert(SI_SUB_KICK_SCHEDULER > SI_SUB_RANDOM,
+ "random kthread starting before subsystem initialization");
static void
rs_epoch_init(void *dummy __unused)
@@ -305,7 +299,6 @@ random_sources_feed(void)
explicit_bzero(entropy, sizeof(entropy));
}
-/* ARGSUSED */
static int
random_check_uint_harvestmask(SYSCTL_HANDLER_ARGS)
{
@@ -336,7 +329,6 @@ SYSCTL_PROC(_kern_random_harvest, OID_AUTO, mask,
random_check_uint_harvestmask, "IU",
"Entropy harvesting mask");
-/* ARGSUSED */
static int
random_print_harvestmask(SYSCTL_HANDLER_ARGS)
{
@@ -390,7 +382,6 @@ static const char *random_source_descr[ENTROPYSOURCE] = {
/* "ENTROPYSOURCE" */
};
-/* ARGSUSED */
static int
random_print_harvestmask_symbolic(SYSCTL_HANDLER_ARGS)
{
@@ -423,7 +414,6 @@ SYSCTL_PROC(_kern_random_harvest, OID_AUTO, mask_symbolic,
random_print_harvestmask_symbolic, "A",
"Entropy harvesting mask (symbolic)");
-/* ARGSUSED */
static void
random_harvestq_init(void *unused __unused)
{
@@ -433,7 +423,7 @@ random_harvestq_init(void *unused __unused)
hc_source_mask = almost_everything_mask;
RANDOM_HARVEST_INIT_LOCK();
- harvest_context.hc_entropy_ring.in = harvest_context.hc_entropy_ring.out = 0;
+ harvest_context.hc_active_buf = 0;
}
SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_THIRD, random_harvestq_init, NULL);
@@ -453,7 +443,7 @@ random_early_prime(char *entropy, size_t len)
return (0);
for (i = 0; i < len; i += sizeof(event.he_entropy)) {
- event.he_somecounter = (uint32_t)get_cyclecount();
+ event.he_somecounter = random_get_cyclecount();
event.he_size = sizeof(event.he_entropy);
event.he_source = RANDOM_CACHED;
event.he_destination =
@@ -493,7 +483,6 @@ random_prime_loader_file(const char *type)
* known to the kernel, and inserting it directly into the hashing
* module, currently Fortuna.
*/
-/* ARGSUSED */
static void
random_harvestq_prime(void *unused __unused)
{
@@ -522,7 +511,6 @@ random_harvestq_prime(void *unused __unused)
}
SYSINIT(random_device_prime, SI_SUB_RANDOM, SI_ORDER_MIDDLE, random_harvestq_prime, NULL);
-/* ARGSUSED */
static void
random_harvestq_deinit(void *unused __unused)
{
@@ -540,9 +528,9 @@ SYSUNINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_THIRD, random_harvestq_d
* This is supposed to be fast; do not do anything slow in here!
* It is also illegal (and morally reprehensible) to insert any
* high-rate data here. "High-rate" is defined as a data source
- * that will usually cause lots of failures of the "Lockless read"
- * check a few lines below. This includes the "always-on" sources
- * like the Intel "rdrand" or the VIA Nehamiah "xstore" sources.
+ * that is likely to fill up the buffer in much less than 100ms.
+ * This includes the "always-on" sources like the Intel "rdrand"
+ * or the VIA Nehamiah "xstore" sources.
*/
/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
* counters are built in, but on older hardware it will do a real time clock
@@ -551,28 +539,29 @@ SYSUNINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_THIRD, random_harvestq_d
void
random_harvest_queue_(const void *entropy, u_int size, enum random_entropy_source origin)
{
+ struct harvest_context *hc;
+ struct entropy_buffer *buf;
struct harvest_event *event;
- u_int ring_in;
- KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
+ KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE,
+ ("%s: origin %d invalid", __func__, origin));
+
+ hc = &harvest_context;
RANDOM_HARVEST_LOCK();
- ring_in = (harvest_context.hc_entropy_ring.in + 1)%RANDOM_RING_MAX;
- if (ring_in != harvest_context.hc_entropy_ring.out) {
- /* The ring is not full */
- event = harvest_context.hc_entropy_ring.ring + ring_in;
- event->he_somecounter = (uint32_t)get_cyclecount();
+ buf = &hc->hc_entropy_buf[hc->hc_active_buf];
+ if (buf->pos < RANDOM_RING_MAX) {
+ event = &buf->ring[buf->pos++];
+ event->he_somecounter = random_get_cyclecount();
event->he_source = origin;
- event->he_destination = harvest_context.hc_destination[origin]++;
+ event->he_destination = hc->hc_destination[origin]++;
if (size <= sizeof(event->he_entropy)) {
event->he_size = size;
memcpy(event->he_entropy, entropy, size);
- }
- else {
+ } else {
/* Big event, so squash it */
event->he_size = sizeof(event->he_entropy[0]);
event->he_entropy[0] = jenkins_hash(entropy, size, (uint32_t)(uintptr_t)event);
}
- harvest_context.hc_entropy_ring.in = ring_in;
}
RANDOM_HARVEST_UNLOCK();
}
@@ -589,7 +578,8 @@ random_harvest_fast_(const void *entropy, u_int size)
u_int pos;
pos = harvest_context.hc_entropy_fast_accumulator.pos;
- harvest_context.hc_entropy_fast_accumulator.buf[pos] ^= jenkins_hash(entropy, size, (uint32_t)get_cyclecount());
+ harvest_context.hc_entropy_fast_accumulator.buf[pos] ^=
+ jenkins_hash(entropy, size, random_get_cyclecount());
harvest_context.hc_entropy_fast_accumulator.pos = (pos + 1)%RANDOM_ACCUM_MAX;
}
@@ -606,7 +596,7 @@ random_harvest_direct_(const void *entropy, u_int size, enum random_entropy_sour
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
size = MIN(size, sizeof(event.he_entropy));
- event.he_somecounter = (uint32_t)get_cyclecount();
+ event.he_somecounter = random_get_cyclecount();
event.he_size = size;
event.he_source = origin;
event.he_destination = harvest_context.hc_destination[origin]++;
diff --git a/sys/dev/random/random_harvestq.h b/sys/dev/random/random_harvestq.h
index 69a9dfabd44a..7804bf52aa4f 100644
--- a/sys/dev/random/random_harvestq.h
+++ b/sys/dev/random/random_harvestq.h
@@ -27,6 +27,9 @@
#ifndef SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
+#include <sys/types.h>
+#include <machine/cpu.h>
+
#define HARVESTSIZE 2 /* Max length in words of each harvested entropy unit */
/* These are used to queue harvested packets of entropy. The entropy
@@ -40,8 +43,10 @@ struct harvest_event {
uint8_t he_source; /* origin of the entropy */
};
-#define RANDOM_HARVEST_INIT_LOCK(x) mtx_init(&harvest_context.hc_mtx, "entropy harvest mutex", NULL, MTX_SPIN)
-#define RANDOM_HARVEST_LOCK(x) mtx_lock_spin(&harvest_context.hc_mtx)
-#define RANDOM_HARVEST_UNLOCK(x) mtx_unlock_spin(&harvest_context.hc_mtx)
+static inline uint32_t
+random_get_cyclecount(void)
+{
+ return ((uint32_t)get_cyclecount());
+}
#endif /* SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED */
diff --git a/sys/dev/random/randomdev.c b/sys/dev/random/randomdev.c
index 6d637ab5a53e..9d1c7b1167c8 100644
--- a/sys/dev/random/randomdev.c
+++ b/sys/dev/random/randomdev.c
@@ -303,14 +303,14 @@ randomdev_accumulate(uint8_t *buf, u_int count)
/* Extra timing here is helpful to scrape scheduler jitter entropy */
randomdev_hash_init(&hash);
- timestamp = (uint32_t)get_cyclecount();
+ timestamp = random_get_cyclecount();
randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
randomdev_hash_iterate(&hash, buf, count);
- timestamp = (uint32_t)get_cyclecount();
+ timestamp = random_get_cyclecount();
randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
randomdev_hash_finish(&hash, entropy_data);
for (i = 0; i < RANDOM_KEYSIZE_WORDS; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) {
- event.he_somecounter = (uint32_t)get_cyclecount();
+ event.he_somecounter = random_get_cyclecount();
event.he_size = sizeof(event.he_entropy);
event.he_source = RANDOM_CACHED;
event.he_destination = destination++; /* Harmless cheating */
diff --git a/sys/dev/regulator/regulator_fixed.c b/sys/dev/regulator/regulator_fixed.c
index 0a76da7140a0..55cdb5e4aeae 100644
--- a/sys/dev/regulator/regulator_fixed.c
+++ b/sys/dev/regulator/regulator_fixed.c
@@ -100,12 +100,8 @@ static struct gpio_entry *
regnode_get_gpio_entry(struct gpiobus_pin *gpio_pin)
{
struct gpio_entry *entry, *tmp;
- device_t busdev;
int rv;
- busdev = GPIO_GET_BUS(gpio_pin->dev);
- if (busdev == NULL)
- return (NULL);
entry = malloc(sizeof(struct gpio_entry), M_FIXEDREGULATOR,
M_WAITOK | M_ZERO);
@@ -122,8 +118,8 @@ regnode_get_gpio_entry(struct gpiobus_pin *gpio_pin)
}
/* Reserve pin. */
- /* XXX Can we call gpiobus_acquire_pin() with gpio_list_mtx held? */
- rv = gpiobus_acquire_pin(busdev, gpio_pin->pin);
+ /* XXX Can we call gpio_pin_acquire() with gpio_list_mtx held? */
+ rv = gpio_pin_acquire(gpio_pin);
if (rv != 0) {
mtx_unlock(&gpio_list_mtx);
free(entry, M_FIXEDREGULATOR);
diff --git a/sys/dev/sound/midi/midi.c b/sys/dev/sound/midi/midi.c
index fbfb69de2913..6753f864ba9c 100644
--- a/sys/dev/sound/midi/midi.c
+++ b/sys/dev/sound/midi/midi.c
@@ -30,12 +30,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
- /*
- * Parts of this file started out as NetBSD: midi.c 1.31
- * They are mostly gone. Still the most obvious will be the state
- * machine midi_in
- */
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>
@@ -66,7 +60,6 @@
#include "mpu_if.h"
#include <dev/sound/midi/midiq.h>
-#include "synth_if.h"
MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
#ifndef KOBJMETHOD_END
@@ -79,17 +72,6 @@ enum midi_states {
MIDI_IN_START, MIDI_IN_SYSEX, MIDI_IN_DATA
};
-/*
- * The MPU interface current has init() uninit() inqsize() outqsize()
- * callback() : fiddle with the tx|rx status.
- */
-
-#include "mpu_if.h"
-
-/*
- * /dev/rmidi Structure definitions
- */
-
#define MIDI_NAMELEN 16
struct snd_midi {
KOBJ_FIELDS;
@@ -115,95 +97,13 @@ struct snd_midi {
* complete command packets. */
struct proc *async;
struct cdev *dev;
- struct synth_midi *synth;
- int synth_flags;
TAILQ_ENTRY(snd_midi) link;
};
-struct synth_midi {
- KOBJ_FIELDS;
- struct snd_midi *m;
-};
-
-static synth_open_t midisynth_open;
-static synth_close_t midisynth_close;
-static synth_writeraw_t midisynth_writeraw;
-static synth_killnote_t midisynth_killnote;
-static synth_startnote_t midisynth_startnote;
-static synth_setinstr_t midisynth_setinstr;
-static synth_alloc_t midisynth_alloc;
-static synth_controller_t midisynth_controller;
-static synth_bender_t midisynth_bender;
-
-static kobj_method_t midisynth_methods[] = {
- KOBJMETHOD(synth_open, midisynth_open),
- KOBJMETHOD(synth_close, midisynth_close),
- KOBJMETHOD(synth_writeraw, midisynth_writeraw),
- KOBJMETHOD(synth_setinstr, midisynth_setinstr),
- KOBJMETHOD(synth_startnote, midisynth_startnote),
- KOBJMETHOD(synth_killnote, midisynth_killnote),
- KOBJMETHOD(synth_alloc, midisynth_alloc),
- KOBJMETHOD(synth_controller, midisynth_controller),
- KOBJMETHOD(synth_bender, midisynth_bender),
- KOBJMETHOD_END
-};
-
-DEFINE_CLASS(midisynth, midisynth_methods, 0);
-
-/*
- * Module Exports & Interface
- *
- * struct midi_chan *midi_init(MPU_CLASS cls, int unit, int chan,
- * void *cookie)
- * int midi_uninit(struct snd_midi *)
- *
- * 0 == no error
- * EBUSY or other error
- *
- * int midi_in(struct snd_midi *, char *buf, int count)
- * int midi_out(struct snd_midi *, char *buf, int count)
- *
- * midi_{in,out} return actual size transfered
- *
- */
-
-/*
- * midi_devs tailq, holder of all rmidi instances protected by midistat_lock
- */
-
TAILQ_HEAD(, snd_midi) midi_devs;
-/*
- * /dev/midistat variables and declarations, protected by midistat_lock
- */
-
struct sx mstat_lock;
-static int midistat_isopen = 0;
-static struct sbuf midistat_sbuf;
-static struct cdev *midistat_dev;
-
-/*
- * /dev/midistat dev_t declarations
- */
-
-static d_open_t midistat_open;
-static d_close_t midistat_close;
-static d_read_t midistat_read;
-
-static struct cdevsw midistat_cdevsw = {
- .d_version = D_VERSION,
- .d_open = midistat_open,
- .d_close = midistat_close,
- .d_read = midistat_read,
- .d_name = "midistat",
-};
-
-/*
- * /dev/rmidi dev_t declarations, struct variable access is protected by
- * locks contained within the structure.
- */
-
static d_open_t midi_open;
static d_close_t midi_close;
static d_ioctl_t midi_ioctl;
@@ -222,41 +122,18 @@ static struct cdevsw midi_cdevsw = {
.d_name = "rmidi",
};
-/*
- * Prototypes of library functions
- */
-
static int midi_destroy(struct snd_midi *, int);
-static int midistat_prepare(struct sbuf * s);
static int midi_load(void);
static int midi_unload(void);
-/*
- * Misc declr.
- */
SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"Midi driver");
-static SYSCTL_NODE(_hw_midi, OID_AUTO, stat, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
- "Status device");
int midi_debug;
/* XXX: should this be moved into debug.midi? */
SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
-int midi_dumpraw;
-SYSCTL_INT(_hw_midi, OID_AUTO, dumpraw, CTLFLAG_RW, &midi_dumpraw, 0, "");
-
-int midi_instroff;
-SYSCTL_INT(_hw_midi, OID_AUTO, instroff, CTLFLAG_RW, &midi_instroff, 0, "");
-
-int midistat_verbose;
-SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW,
- &midistat_verbose, 0, "");
-
#define MIDI_DEBUG(l,a) if(midi_debug>=l) a
-/*
- * CODE START
- */
void
midistat_lock(void)
@@ -285,9 +162,6 @@ midistat_lockassert(void)
* what unit number is used.
*
* It is an error to call midi_init with an already used unit/channel combo.
- *
- * Returns NULL on error
- *
*/
struct snd_midi *
midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
@@ -326,9 +200,6 @@ midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel));
m = malloc(sizeof(*m), M_MIDI, M_WAITOK | M_ZERO);
- m->synth = malloc(sizeof(*m->synth), M_MIDI, M_WAITOK | M_ZERO);
- kobj_init((kobj_t)m->synth, &midisynth_class);
- m->synth->m = m;
kobj_init((kobj_t)m, cls);
inqsize = MPU_INQSIZE(m, cookie);
outqsize = MPU_OUTQSIZE(m, cookie);
@@ -393,7 +264,6 @@ err2:
if (MIDIQ_BUF(m->outq))
free(MIDIQ_BUF(m->outq), M_MIDI);
err1:
- free(m->synth, M_MIDI);
free(m, M_MIDI);
err0:
midistat_unlock();
@@ -405,9 +275,7 @@ err0:
* midi_uninit does not call MIDI_UNINIT, as since this is the implementors
* entry point. midi_uninit if fact, does not send any methods. A call to
* midi_uninit is a defacto promise that you won't manipulate ch anymore
- *
*/
-
int
midi_uninit(struct snd_midi *m)
{
@@ -440,13 +308,6 @@ exit:
return err;
}
-/*
- * midi_in: process all data until the queue is full, then discards the rest.
- * Since midi_in is a state machine, data discards can cause it to get out of
- * whack. Process as much as possible. It calls, wakeup, selnotify and
- * psignal at most once.
- */
-
#ifdef notdef
static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
@@ -460,6 +321,12 @@ static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
#define MIDI_SYSEX_START 0xF0
#define MIDI_SYSEX_END 0xF7
+/*
+ * midi_in: process all data until the queue is full, then discards the rest.
+ * Since midi_in is a state machine, data discards can cause it to get out of
+ * whack. Process as much as possible. It calls, wakeup, selnotify and
+ * psignal at most once.
+ */
int
midi_in(struct snd_midi *m, uint8_t *buf, int size)
{
@@ -627,9 +494,6 @@ midi_out(struct snd_midi *m, uint8_t *buf, int size)
return used;
}
-/*
- * /dev/rmidi#.# device access functions
- */
int
midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
@@ -934,434 +798,6 @@ midi_poll(struct cdev *i_dev, int events, struct thread *td)
}
/*
- * /dev/midistat device functions
- *
- */
-static int
-midistat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
-{
- int error;
-
- MIDI_DEBUG(1, printf("midistat_open\n"));
-
- midistat_lock();
- if (midistat_isopen) {
- midistat_unlock();
- return EBUSY;
- }
- midistat_isopen = 1;
- sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND);
- error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM;
- if (error)
- midistat_isopen = 0;
- midistat_unlock();
- return error;
-}
-
-static int
-midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
-{
- MIDI_DEBUG(1, printf("midistat_close\n"));
- midistat_lock();
- if (!midistat_isopen) {
- midistat_unlock();
- return EBADF;
- }
- sbuf_delete(&midistat_sbuf);
- midistat_isopen = 0;
- midistat_unlock();
- return 0;
-}
-
-static int
-midistat_read(struct cdev *i_dev, struct uio *uio, int flag)
-{
- long l;
- int err;
-
- MIDI_DEBUG(4, printf("midistat_read\n"));
- midistat_lock();
- if (!midistat_isopen) {
- midistat_unlock();
- return EBADF;
- }
- if (uio->uio_offset < 0 || uio->uio_offset > sbuf_len(&midistat_sbuf)) {
- midistat_unlock();
- return EINVAL;
- }
- err = 0;
- l = lmin(uio->uio_resid, sbuf_len(&midistat_sbuf) - uio->uio_offset);
- if (l > 0) {
- err = uiomove(sbuf_data(&midistat_sbuf) + uio->uio_offset, l,
- uio);
- }
- midistat_unlock();
- return err;
-}
-
-/*
- * Module library functions
- */
-
-static int
-midistat_prepare(struct sbuf *s)
-{
- struct snd_midi *m;
-
- midistat_lockassert();
-
- sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n");
- if (TAILQ_EMPTY(&midi_devs)) {
- sbuf_printf(s, "No devices installed.\n");
- sbuf_finish(s);
- return sbuf_len(s);
- }
- sbuf_printf(s, "Installed devices:\n");
-
- TAILQ_FOREACH(m, &midi_devs, link) {
- mtx_lock(&m->lock);
- sbuf_printf(s, "%s [%d/%d:%s]", m->name, m->unit, m->channel,
- MPU_PROVIDER(m, m->cookie));
- sbuf_printf(s, "%s", MPU_DESCR(m, m->cookie, midistat_verbose));
- sbuf_printf(s, "\n");
- mtx_unlock(&m->lock);
- }
-
- sbuf_finish(s);
- return sbuf_len(s);
-}
-
-#ifdef notdef
-/*
- * Convert IOCTL command to string for debugging
- */
-
-static char *
-midi_cmdname(int cmd)
-{
- static struct {
- int cmd;
- char *name;
- } *tab, cmdtab_midiioctl[] = {
-#define A(x) {x, ## x}
- /*
- * Once we have some real IOCTLs define, the following will
- * be relavant.
- *
- * A(SNDCTL_MIDI_PRETIME), A(SNDCTL_MIDI_MPUMODE),
- * A(SNDCTL_MIDI_MPUCMD), A(SNDCTL_SYNTH_INFO),
- * A(SNDCTL_MIDI_INFO), A(SNDCTL_SYNTH_MEMAVL),
- * A(SNDCTL_FM_LOAD_INSTR), A(SNDCTL_FM_4OP_ENABLE),
- * A(MIOSPASSTHRU), A(MIOGPASSTHRU), A(AIONWRITE),
- * A(AIOGSIZE), A(AIOSSIZE), A(AIOGFMT), A(AIOSFMT),
- * A(AIOGMIX), A(AIOSMIX), A(AIOSTOP), A(AIOSYNC),
- * A(AIOGCAP),
- */
-#undef A
- {
- -1, "unknown"
- },
- };
-
- for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++);
- return tab->name;
-}
-
-#endif /* notdef */
-
-/*
- * midisynth
- */
-
-int
-midisynth_open(void *n, void *arg, int flags)
-{
- struct snd_midi *m = ((struct synth_midi *)n)->m;
- int retval;
-
- MIDI_DEBUG(1, printf("midisynth_open %s %s\n",
- flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
-
- if (m == NULL)
- return ENXIO;
-
- mtx_lock(&m->lock);
- mtx_lock(&m->qlock);
-
- retval = 0;
-
- if (flags & FREAD) {
- if (MIDIQ_SIZE(m->inq) == 0)
- retval = ENXIO;
- else if (m->flags & M_RX)
- retval = EBUSY;
- if (retval)
- goto err;
- }
- if (flags & FWRITE) {
- if (MIDIQ_SIZE(m->outq) == 0)
- retval = ENXIO;
- else if (m->flags & M_TX)
- retval = EBUSY;
- if (retval)
- goto err;
- }
- m->busy++;
-
- /*
- * TODO: Consider m->async = 0;
- */
-
- if (flags & FREAD) {
- m->flags |= M_RX | M_RXEN;
- /*
- * Only clear the inq, the outq might still have data to drain
- * from a previous session
- */
- MIDIQ_CLEAR(m->inq);
- m->rchan = 0;
- }
-
- if (flags & FWRITE) {
- m->flags |= M_TX;
- m->wchan = 0;
- }
- m->synth_flags = flags & (FREAD | FWRITE);
-
- MPU_CALLBACK(m, m->cookie, m->flags);
-
-err: mtx_unlock(&m->qlock);
- mtx_unlock(&m->lock);
- MIDI_DEBUG(2, printf("midisynth_open: return %d.\n", retval));
- return retval;
-}
-
-int
-midisynth_close(void *n)
-{
- struct snd_midi *m = ((struct synth_midi *)n)->m;
- int retval;
- int oldflags;
-
- MIDI_DEBUG(1, printf("midisynth_close %s %s\n",
- m->synth_flags & FREAD ? "M_RX" : "",
- m->synth_flags & FWRITE ? "M_TX" : ""));
-
- if (m == NULL)
- return ENXIO;
-
- mtx_lock(&m->lock);
- mtx_lock(&m->qlock);
-
- if ((m->synth_flags & FREAD && !(m->flags & M_RX)) ||
- (m->synth_flags & FWRITE && !(m->flags & M_TX))) {
- retval = ENXIO;
- goto err;
- }
- m->busy--;
-
- oldflags = m->flags;
-
- if (m->synth_flags & FREAD)
- m->flags &= ~(M_RX | M_RXEN);
- if (m->synth_flags & FWRITE)
- m->flags &= ~M_TX;
-
- if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
- MPU_CALLBACK(m, m->cookie, m->flags);
-
- MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
-
- mtx_unlock(&m->qlock);
- mtx_unlock(&m->lock);
- retval = 0;
-err: return retval;
-}
-
-/*
- * Always blocking.
- */
-
-int
-midisynth_writeraw(void *n, uint8_t *buf, size_t len)
-{
- struct snd_midi *m = ((struct synth_midi *)n)->m;
- int retval;
- int used;
- int i;
-
- MIDI_DEBUG(4, printf("midisynth_writeraw\n"));
-
- retval = 0;
-
- if (m == NULL)
- return ENXIO;
-
- mtx_lock(&m->lock);
- mtx_lock(&m->qlock);
-
- if (!(m->flags & M_TX))
- goto err1;
-
- if (midi_dumpraw)
- printf("midi dump: ");
-
- while (len > 0) {
- while (MIDIQ_AVAIL(m->outq) == 0) {
- if (!(m->flags & M_TXEN)) {
- m->flags |= M_TXEN;
- MPU_CALLBACK(m, m->cookie, m->flags);
- }
- mtx_unlock(&m->lock);
- m->wchan = 1;
- MIDI_DEBUG(3, printf("midisynth_writeraw msleep\n"));
- retval = msleep(&m->wchan, &m->qlock,
- PCATCH | PDROP, "midi TX", 0);
- /*
- * We slept, maybe things have changed since last
- * dying check
- */
- if (retval == EINTR)
- goto err0;
-
- if (retval)
- goto err0;
- mtx_lock(&m->lock);
- mtx_lock(&m->qlock);
- m->wchan = 0;
- if (!m->busy)
- goto err1;
- }
-
- /*
- * We are certain than data can be placed on the queue
- */
-
- used = MIN(MIDIQ_AVAIL(m->outq), len);
- used = MIN(used, MIDI_WSIZE);
- MIDI_DEBUG(5,
- printf("midi_synth: resid %zu len %jd avail %jd\n",
- len, (intmax_t)MIDIQ_LEN(m->outq),
- (intmax_t)MIDIQ_AVAIL(m->outq)));
-
- if (midi_dumpraw)
- for (i = 0; i < used; i++)
- printf("%x ", buf[i]);
-
- MIDIQ_ENQ(m->outq, buf, used);
- len -= used;
-
- /*
- * Inform the bottom half that data can be written
- */
- if (!(m->flags & M_TXEN)) {
- m->flags |= M_TXEN;
- MPU_CALLBACK(m, m->cookie, m->flags);
- }
- }
- /*
- * If we Made it here then transfer is good
- */
- if (midi_dumpraw)
- printf("\n");
-
- retval = 0;
-err1: mtx_unlock(&m->qlock);
- mtx_unlock(&m->lock);
-err0: return retval;
-}
-
-static int
-midisynth_killnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
-{
- u_char c[3];
-
- if (note > 127 || chn > 15)
- return (EINVAL);
-
- if (vel > 127)
- vel = 127;
-
- if (vel == 64) {
- c[0] = 0x90 | (chn & 0x0f); /* Note on. */
- c[1] = (u_char)note;
- c[2] = 0;
- } else {
- c[0] = 0x80 | (chn & 0x0f); /* Note off. */
- c[1] = (u_char)note;
- c[2] = (u_char)vel;
- }
-
- return midisynth_writeraw(n, c, 3);
-}
-
-static int
-midisynth_setinstr(void *n, uint8_t chn, uint16_t instr)
-{
- u_char c[2];
-
- if (instr > 127 || chn > 15)
- return EINVAL;
-
- c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */
- c[1] = instr + midi_instroff;
-
- return midisynth_writeraw(n, c, 2);
-}
-
-static int
-midisynth_startnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
-{
- u_char c[3];
-
- if (note > 127 || chn > 15)
- return EINVAL;
-
- if (vel > 127)
- vel = 127;
-
- c[0] = 0x90 | (chn & 0x0f); /* Note on. */
- c[1] = (u_char)note;
- c[2] = (u_char)vel;
-
- return midisynth_writeraw(n, c, 3);
-}
-static int
-midisynth_alloc(void *n, uint8_t chan, uint8_t note)
-{
- return chan;
-}
-
-static int
-midisynth_controller(void *n, uint8_t chn, uint8_t ctrlnum, uint16_t val)
-{
- u_char c[3];
-
- if (ctrlnum > 127 || chn > 15)
- return EINVAL;
-
- c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */
- c[1] = ctrlnum;
- c[2] = val;
- return midisynth_writeraw(n, c, 3);
-}
-
-static int
-midisynth_bender(void *n, uint8_t chn, uint16_t val)
-{
- u_char c[3];
-
- if (val > 16383 || chn > 15)
- return EINVAL;
-
- c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */
- c[1] = (u_char)val & 0x7f;
- c[2] = (u_char)(val >> 7) & 0x7f;
-
- return midisynth_writeraw(n, c, 3);
-}
-
-/*
* Single point of midi destructions.
*/
static int
@@ -1381,24 +817,16 @@ midi_destroy(struct snd_midi *m, int midiuninit)
free(MIDIQ_BUF(m->outq), M_MIDI);
mtx_destroy(&m->qlock);
mtx_destroy(&m->lock);
- free(m->synth, M_MIDI);
free(m, M_MIDI);
return 0;
}
-/*
- * Load and unload functions, creates the /dev/midistat device
- */
-
static int
midi_load(void)
{
sx_init(&mstat_lock, "midistat lock");
TAILQ_INIT(&midi_devs);
- midistat_dev = make_dev(&midistat_cdevsw, MIDI_DEV_MIDICTL, UID_ROOT,
- GID_WHEEL, 0666, "midistat");
-
return 0;
}
@@ -1411,9 +839,6 @@ midi_unload(void)
MIDI_DEBUG(1, printf("midi_unload()\n"));
retval = EBUSY;
midistat_lock();
- if (midistat_isopen)
- goto exit0;
-
TAILQ_FOREACH_SAFE(m, &midi_devs, link, tmp) {
mtx_lock(&m->lock);
if (m->busy)
@@ -1421,28 +846,21 @@ midi_unload(void)
else
retval = midi_destroy(m, 1);
if (retval)
- goto exit1;
+ goto exit;
}
midistat_unlock();
- destroy_dev(midistat_dev);
- /*
- * Made it here then unload is complete
- */
sx_destroy(&mstat_lock);
return 0;
-exit1:
+exit:
mtx_unlock(&m->lock);
-exit0:
midistat_unlock();
if (retval)
MIDI_DEBUG(2, printf("midi_unload: failed\n"));
return retval;
}
-extern int seq_modevent(module_t mod, int type, void *data);
-
static int
midi_modevent(module_t mod, int type, void *data)
{
@@ -1453,14 +871,10 @@ midi_modevent(module_t mod, int type, void *data)
switch (type) {
case MOD_LOAD:
retval = midi_load();
- if (retval == 0)
- retval = seq_modevent(mod, type, data);
break;
case MOD_UNLOAD:
retval = midi_unload();
- if (retval == 0)
- retval = seq_modevent(mod, type, data);
break;
default:
@@ -1470,73 +884,5 @@ midi_modevent(module_t mod, int type, void *data)
return retval;
}
-kobj_t
-midimapper_addseq(void *arg1, int *unit, void **cookie)
-{
- unit = NULL;
-
- return (kobj_t)arg1;
-}
-
-int
-midimapper_open_locked(void *arg1, void **cookie)
-{
- int retval = 0;
- struct snd_midi *m;
-
- midistat_lockassert();
- TAILQ_FOREACH(m, &midi_devs, link) {
- retval++;
- }
-
- return retval;
-}
-
-int
-midimapper_open(void *arg1, void **cookie)
-{
- int retval;
-
- midistat_lock();
- retval = midimapper_open_locked(arg1, cookie);
- midistat_unlock();
-
- return retval;
-}
-
-int
-midimapper_close(void *arg1, void *cookie)
-{
- return 0;
-}
-
-kobj_t
-midimapper_fetch_synth_locked(void *arg, void *cookie, int unit)
-{
- struct snd_midi *m;
- int retval = 0;
-
- midistat_lockassert();
- TAILQ_FOREACH(m, &midi_devs, link) {
- if (unit == retval)
- return (kobj_t)m->synth;
- retval++;
- }
-
- return NULL;
-}
-
-kobj_t
-midimapper_fetch_synth(void *arg, void *cookie, int unit)
-{
- kobj_t synth;
-
- midistat_lock();
- synth = midimapper_fetch_synth_locked(arg, cookie, unit);
- midistat_unlock();
-
- return synth;
-}
-
DEV_MODULE(midi, midi_modevent, NULL);
MODULE_VERSION(midi, 1);
diff --git a/sys/dev/sound/midi/midi.h b/sys/dev/sound/midi/midi.h
index 2254fab690e9..286e84264ef3 100644
--- a/sys/dev/sound/midi/midi.h
+++ b/sys/dev/sound/midi/midi.h
@@ -51,11 +51,4 @@ int midi_uninit(struct snd_midi *_m);
int midi_out(struct snd_midi *_m, uint8_t *_buf, int _size);
int midi_in(struct snd_midi *_m, uint8_t *_buf, int _size);
-kobj_t midimapper_addseq(void *arg1, int *unit, void **cookie);
-int midimapper_open_locked(void *arg1, void **cookie);
-int midimapper_open(void *arg1, void **cookie);
-int midimapper_close(void *arg1, void *cookie);
-kobj_t midimapper_fetch_synth_locked(void *arg, void *cookie, int unit);
-kobj_t midimapper_fetch_synth(void *arg, void *cookie, int unit);
-
#endif
diff --git a/sys/dev/sound/midi/mpu401.c b/sys/dev/sound/midi/mpu401.c
index 2be285bc0040..224ebb1b01f4 100644
--- a/sys/dev/sound/midi/mpu401.c
+++ b/sys/dev/sound/midi/mpu401.c
@@ -88,8 +88,6 @@ static int mpu401_minqsize(struct snd_midi *, void *);
static int mpu401_moutqsize(struct snd_midi *, void *);
static void mpu401_mcallback(struct snd_midi *, void *, int);
static void mpu401_mcallbackp(struct snd_midi *, void *, int);
-static const char *mpu401_mdescr(struct snd_midi *, void *, int);
-static const char *mpu401_mprovider(struct snd_midi *, void *);
static kobj_method_t mpu401_methods[] = {
KOBJMETHOD(mpu_init, mpu401_minit),
@@ -98,8 +96,6 @@ static kobj_method_t mpu401_methods[] = {
KOBJMETHOD(mpu_outqsize, mpu401_moutqsize),
KOBJMETHOD(mpu_callback, mpu401_mcallback),
KOBJMETHOD(mpu_callbackp, mpu401_mcallbackp),
- KOBJMETHOD(mpu_descr, mpu401_mdescr),
- KOBJMETHOD(mpu_provider, mpu401_mprovider),
KOBJMETHOD_END
};
@@ -122,24 +118,12 @@ mpu401_intr(struct mpu401 *m)
int i;
int s;
-/*
- printf("mpu401_intr\n");
-*/
#define RXRDY(m) ( (STATUS(m) & MPU_INPUTBUSY) == 0)
#define TXRDY(m) ( (STATUS(m) & MPU_OUTPUTBUSY) == 0)
-#if 0
-#define D(x,l) printf("mpu401_intr %d %x %s %s\n",l, x, x&MPU_INPUTBUSY?"RX":"", x&MPU_OUTPUTBUSY?"TX":"")
-#else
-#define D(x,l)
-#endif
i = 0;
s = STATUS(m);
- D(s, 1);
while ((s & MPU_INPUTBUSY) == 0 && i < MPU_INTR_BUF) {
b[i] = READ(m);
-/*
- printf("mpu401_intr in i %d d %d\n", i, b[i]);
-*/
i++;
s = STATUS(m);
}
@@ -148,15 +132,9 @@ mpu401_intr(struct mpu401 *m)
i = 0;
while (!(s & MPU_OUTPUTBUSY) && i < MPU_INTR_BUF) {
if (midi_out(m->mid, b, 1)) {
-/*
- printf("mpu401_intr out i %d d %d\n", i, b[0]);
-*/
WRITE(m, *b);
} else {
-/*
- printf("mpu401_intr write: no output\n");
-*/
return 0;
}
i++;
@@ -262,13 +240,7 @@ static void
mpu401_mcallback(struct snd_midi *sm, void *arg, int flags)
{
struct mpu401 *m = arg;
-#if 0
- printf("mpu401_callback %s %s %s %s\n",
- flags & M_RX ? "M_RX" : "",
- flags & M_TX ? "M_TX" : "",
- flags & M_RXEN ? "M_RXEN" : "",
- flags & M_TXEN ? "M_TXEN" : "");
-#endif
+
if (flags & M_TXEN && m->si) {
callout_reset(&m->timer, 1, mpu401_timeout, m);
}
@@ -278,19 +250,5 @@ mpu401_mcallback(struct snd_midi *sm, void *arg, int flags)
static void
mpu401_mcallbackp(struct snd_midi *sm, void *arg, int flags)
{
-/* printf("mpu401_callbackp\n"); */
mpu401_mcallback(sm, arg, flags);
}
-
-static const char *
-mpu401_mdescr(struct snd_midi *sm, void *arg, int verbosity)
-{
-
- return "descr mpu401";
-}
-
-static const char *
-mpu401_mprovider(struct snd_midi *m, void *arg)
-{
- return "provider mpu401";
-}
diff --git a/sys/dev/sound/midi/mpu_if.m b/sys/dev/sound/midi/mpu_if.m
index b7cb586c5dd0..835d887f703a 100644
--- a/sys/dev/sound/midi/mpu_if.m
+++ b/sys/dev/sound/midi/mpu_if.m
@@ -56,17 +56,6 @@ METHOD void callback {
int _flags;
};
-METHOD const char * provider {
- struct snd_midi *_kobj;
- void *_cookie;
-};
-
-METHOD const char * descr {
- struct snd_midi *_kobj;
- void *_cookie;
- int _verbosity;
-};
-
METHOD int uninit {
struct snd_midi *_kobj;
void *_cookie;
diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c
deleted file mode 100644
index 03b71688175c..000000000000
--- a/sys/dev/sound/midi/sequencer.c
+++ /dev/null
@@ -1,2107 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2003 Mathew Kanner
- * Copyright (c) 1993 Hannu Savolainen
- * 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 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.
- */
-
-/*
- * The sequencer personality manager.
- */
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/ioccom.h>
-
-#include <sys/filio.h>
-#include <sys/lock.h>
-#include <sys/sockio.h>
-#include <sys/fcntl.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-
-#include <sys/kernel.h> /* for DATA_SET */
-
-#include <sys/module.h>
-#include <sys/conf.h>
-#include <sys/file.h>
-#include <sys/uio.h>
-#include <sys/syslog.h>
-#include <sys/errno.h>
-#include <sys/malloc.h>
-#include <sys/bus.h>
-#include <machine/resource.h>
-#include <machine/bus.h>
-#include <machine/clock.h> /* for DELAY */
-#include <sys/soundcard.h>
-#include <sys/rman.h>
-#include <sys/mman.h>
-#include <sys/poll.h>
-#include <sys/mutex.h>
-#include <sys/condvar.h>
-#include <sys/kthread.h>
-#include <sys/unistd.h>
-#include <sys/selinfo.h>
-#include <sys/sx.h>
-
-#ifdef HAVE_KERNEL_OPTION_HEADERS
-#include "opt_snd.h"
-#endif
-
-#include <dev/sound/midi/midi.h>
-#include <dev/sound/midi/midiq.h>
-#include "synth_if.h"
-
-#include <dev/sound/midi/sequencer.h>
-
-#define TMR_TIMERBASE 13
-
-#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
- * synthesizer and MIDI output) */
-#define SND_DEV_MUSIC 8 /* /dev/music, level 2 interface */
-
-/* Length of a sequencer event. */
-#define EV_SZ 8
-#define IEV_SZ 8
-
-/* Lookup modes */
-#define LOOKUP_EXIST (0)
-#define LOOKUP_OPEN (1)
-#define LOOKUP_CLOSE (2)
-
-#define MIDIDEV(y) (dev2unit(y) & 0x0f)
-
-/* These are the entries to the sequencer driver. */
-static d_open_t mseq_open;
-static d_close_t mseq_close;
-static d_ioctl_t mseq_ioctl;
-static d_read_t mseq_read;
-static d_write_t mseq_write;
-static d_poll_t mseq_poll;
-
-static struct cdevsw seq_cdevsw = {
- .d_version = D_VERSION,
- .d_open = mseq_open,
- .d_close = mseq_close,
- .d_read = mseq_read,
- .d_write = mseq_write,
- .d_ioctl = mseq_ioctl,
- .d_poll = mseq_poll,
- .d_name = "sequencer",
-};
-
-struct seq_softc {
- KOBJ_FIELDS;
-
- struct mtx seq_lock, q_lock;
- struct cv empty_cv, reset_cv, in_cv, out_cv, state_cv, th_cv;
-
- MIDIQ_HEAD(, u_char) in_q, out_q;
-
- u_long flags;
- /* Flags (protected by flag_mtx of mididev_info) */
- int fflags; /* Access mode */
- int music;
-
- int out_water; /* Sequence output threshould */
- snd_sync_parm sync_parm; /* AIOSYNC parameter set */
- struct thread *sync_thread; /* AIOSYNCing thread */
- struct selinfo in_sel, out_sel;
- int midi_number;
- struct cdev *seqdev, *musicdev;
- int unit;
- int maxunits;
- kobj_t *midis;
- int *midi_flags;
- kobj_t mapper;
- void *mapper_cookie;
- struct timeval timerstop, timersub;
- int timerbase, tempo;
- int timerrun;
- int done;
- int playing;
- int recording;
- int busy;
- int pre_event_timeout;
- int waiting;
-};
-
-/*
- * Module specific stuff, including how many sequecers
- * we currently own.
- */
-
-SYSCTL_NODE(_hw_midi, OID_AUTO, seq, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
- "Midi sequencer");
-
-int seq_debug;
-/* XXX: should this be moved into debug.midi? */
-SYSCTL_INT(_hw_midi_seq, OID_AUTO, debug, CTLFLAG_RW, &seq_debug, 0, "");
-
-midi_cmdtab cmdtab_seqevent[] = {
- {SEQ_NOTEOFF, "SEQ_NOTEOFF"},
- {SEQ_NOTEON, "SEQ_NOTEON"},
- {SEQ_WAIT, "SEQ_WAIT"},
- {SEQ_PGMCHANGE, "SEQ_PGMCHANGE"},
- {SEQ_SYNCTIMER, "SEQ_SYNCTIMER"},
- {SEQ_MIDIPUTC, "SEQ_MIDIPUTC"},
- {SEQ_DRUMON, "SEQ_DRUMON"},
- {SEQ_DRUMOFF, "SEQ_DRUMOFF"},
- {SEQ_ECHO, "SEQ_ECHO"},
- {SEQ_AFTERTOUCH, "SEQ_AFTERTOUCH"},
- {SEQ_CONTROLLER, "SEQ_CONTROLLER"},
- {SEQ_BALANCE, "SEQ_BALANCE"},
- {SEQ_VOLMODE, "SEQ_VOLMODE"},
- {SEQ_FULLSIZE, "SEQ_FULLSIZE"},
- {SEQ_PRIVATE, "SEQ_PRIVATE"},
- {SEQ_EXTENDED, "SEQ_EXTENDED"},
- {EV_SEQ_LOCAL, "EV_SEQ_LOCAL"},
- {EV_TIMING, "EV_TIMING"},
- {EV_CHN_COMMON, "EV_CHN_COMMON"},
- {EV_CHN_VOICE, "EV_CHN_VOICE"},
- {EV_SYSEX, "EV_SYSEX"},
- {-1, NULL},
-};
-
-midi_cmdtab cmdtab_seqioctl[] = {
- {SNDCTL_SEQ_RESET, "SNDCTL_SEQ_RESET"},
- {SNDCTL_SEQ_SYNC, "SNDCTL_SEQ_SYNC"},
- {SNDCTL_SYNTH_INFO, "SNDCTL_SYNTH_INFO"},
- {SNDCTL_SEQ_CTRLRATE, "SNDCTL_SEQ_CTRLRATE"},
- {SNDCTL_SEQ_GETOUTCOUNT, "SNDCTL_SEQ_GETOUTCOUNT"},
- {SNDCTL_SEQ_GETINCOUNT, "SNDCTL_SEQ_GETINCOUNT"},
- {SNDCTL_SEQ_PERCMODE, "SNDCTL_SEQ_PERCMODE"},
- {SNDCTL_FM_LOAD_INSTR, "SNDCTL_FM_LOAD_INSTR"},
- {SNDCTL_SEQ_TESTMIDI, "SNDCTL_SEQ_TESTMIDI"},
- {SNDCTL_SEQ_RESETSAMPLES, "SNDCTL_SEQ_RESETSAMPLES"},
- {SNDCTL_SEQ_NRSYNTHS, "SNDCTL_SEQ_NRSYNTHS"},
- {SNDCTL_SEQ_NRMIDIS, "SNDCTL_SEQ_NRMIDIS"},
- {SNDCTL_SEQ_GETTIME, "SNDCTL_SEQ_GETTIME"},
- {SNDCTL_MIDI_INFO, "SNDCTL_MIDI_INFO"},
- {SNDCTL_SEQ_THRESHOLD, "SNDCTL_SEQ_THRESHOLD"},
- {SNDCTL_SYNTH_MEMAVL, "SNDCTL_SYNTH_MEMAVL"},
- {SNDCTL_FM_4OP_ENABLE, "SNDCTL_FM_4OP_ENABLE"},
- {SNDCTL_PMGR_ACCESS, "SNDCTL_PMGR_ACCESS"},
- {SNDCTL_SEQ_PANIC, "SNDCTL_SEQ_PANIC"},
- {SNDCTL_SEQ_OUTOFBAND, "SNDCTL_SEQ_OUTOFBAND"},
- {SNDCTL_TMR_TIMEBASE, "SNDCTL_TMR_TIMEBASE"},
- {SNDCTL_TMR_START, "SNDCTL_TMR_START"},
- {SNDCTL_TMR_STOP, "SNDCTL_TMR_STOP"},
- {SNDCTL_TMR_CONTINUE, "SNDCTL_TMR_CONTINUE"},
- {SNDCTL_TMR_TEMPO, "SNDCTL_TMR_TEMPO"},
- {SNDCTL_TMR_SOURCE, "SNDCTL_TMR_SOURCE"},
- {SNDCTL_TMR_METRONOME, "SNDCTL_TMR_METRONOME"},
- {SNDCTL_TMR_SELECT, "SNDCTL_TMR_SELECT"},
- {SNDCTL_MIDI_PRETIME, "SNDCTL_MIDI_PRETIME"},
- {AIONWRITE, "AIONWRITE"},
- {AIOGSIZE, "AIOGSIZE"},
- {AIOSSIZE, "AIOSSIZE"},
- {AIOGFMT, "AIOGFMT"},
- {AIOSFMT, "AIOSFMT"},
- {AIOGMIX, "AIOGMIX"},
- {AIOSMIX, "AIOSMIX"},
- {AIOSTOP, "AIOSTOP"},
- {AIOSYNC, "AIOSYNC"},
- {AIOGCAP, "AIOGCAP"},
- {-1, NULL},
-};
-
-midi_cmdtab cmdtab_timer[] = {
- {TMR_WAIT_REL, "TMR_WAIT_REL"},
- {TMR_WAIT_ABS, "TMR_WAIT_ABS"},
- {TMR_STOP, "TMR_STOP"},
- {TMR_START, "TMR_START"},
- {TMR_CONTINUE, "TMR_CONTINUE"},
- {TMR_TEMPO, "TMR_TEMPO"},
- {TMR_ECHO, "TMR_ECHO"},
- {TMR_CLOCK, "TMR_CLOCK"},
- {TMR_SPP, "TMR_SPP"},
- {TMR_TIMESIG, "TMR_TIMESIG"},
- {-1, NULL},
-};
-
-midi_cmdtab cmdtab_seqcv[] = {
- {MIDI_NOTEOFF, "MIDI_NOTEOFF"},
- {MIDI_NOTEON, "MIDI_NOTEON"},
- {MIDI_KEY_PRESSURE, "MIDI_KEY_PRESSURE"},
- {-1, NULL},
-};
-
-midi_cmdtab cmdtab_seqccmn[] = {
- {MIDI_CTL_CHANGE, "MIDI_CTL_CHANGE"},
- {MIDI_PGM_CHANGE, "MIDI_PGM_CHANGE"},
- {MIDI_CHN_PRESSURE, "MIDI_CHN_PRESSURE"},
- {MIDI_PITCH_BEND, "MIDI_PITCH_BEND"},
- {MIDI_SYSTEM_PREFIX, "MIDI_SYSTEM_PREFIX"},
- {-1, NULL},
-};
-
-#ifndef KOBJMETHOD_END
-#define KOBJMETHOD_END { NULL, NULL }
-#endif
-
-/*
- * static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m);
- */
-
-static kobj_method_t seq_methods[] = {
- /* KOBJMETHOD(mpu_provider,mpu401_mprovider), */
- KOBJMETHOD_END
-};
-
-DEFINE_CLASS(sequencer, seq_methods, 0);
-
-/* The followings are the local function. */
-static int seq_convertold(u_char *event, u_char *out);
-
-/*
- * static void seq_midiinput(struct seq_softc * scp, void *md);
- */
-static void seq_reset(struct seq_softc *scp);
-static int seq_sync(struct seq_softc *scp);
-
-static int seq_processevent(struct seq_softc *scp, u_char *event);
-
-static int seq_timing(struct seq_softc *scp, u_char *event);
-static int seq_local(struct seq_softc *scp, u_char *event);
-
-static int seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event);
-static int seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event);
-static int seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event);
-
-static int seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md);
-void seq_copytoinput(struct seq_softc *scp, u_char *event, int len);
-int seq_modevent(module_t mod, int type, void *data);
-struct seq_softc *seqs[10];
-static struct mtx seqinfo_mtx;
-static u_long nseq = 0;
-
-static void timer_start(struct seq_softc *t);
-static void timer_stop(struct seq_softc *t);
-static void timer_setvals(struct seq_softc *t, int tempo, int timerbase);
-static void timer_wait(struct seq_softc *t, int ticks, int wait_abs);
-static int timer_now(struct seq_softc *t);
-
-static void
-timer_start(struct seq_softc *t)
-{
- t->timerrun = 1;
- getmicrotime(&t->timersub);
-}
-
-static void
-timer_continue(struct seq_softc *t)
-{
- struct timeval now;
-
- if (t->timerrun == 1)
- return;
- t->timerrun = 1;
- getmicrotime(&now);
- timevalsub(&now, &t->timerstop);
- timevaladd(&t->timersub, &now);
-}
-
-static void
-timer_stop(struct seq_softc *t)
-{
- t->timerrun = 0;
- getmicrotime(&t->timerstop);
-}
-
-static void
-timer_setvals(struct seq_softc *t, int tempo, int timerbase)
-{
- t->tempo = tempo;
- t->timerbase = timerbase;
-}
-
-static void
-timer_wait(struct seq_softc *t, int ticks, int wait_abs)
-{
- struct timeval now, when;
- int ret;
- unsigned long long i;
-
- while (t->timerrun == 0) {
- SEQ_DEBUG(2, printf("Timer wait when timer isn't running\n"));
- /*
- * The old sequencer used timeouts that only increased
- * the timer when the timer was running.
- * Hence the sequencer would stick (?) if the
- * timer was disabled.
- */
- cv_wait(&t->reset_cv, &t->seq_lock);
- if (t->playing == 0)
- return;
- }
-
- i = ticks * 60ull * 1000000ull / (t->tempo * t->timerbase);
-
- when.tv_sec = i / 1000000;
- when.tv_usec = i % 1000000;
-
-#if 0
- printf("timer_wait tempo %d timerbase %d ticks %d abs %d u_sec %llu\n",
- t->tempo, t->timerbase, ticks, wait_abs, i);
-#endif
-
- if (wait_abs != 0) {
- getmicrotime(&now);
- timevalsub(&now, &t->timersub);
- timevalsub(&when, &now);
- }
- if (when.tv_sec < 0 || when.tv_usec < 0) {
- SEQ_DEBUG(3,
- printf("seq_timer error negative time %lds.%06lds\n",
- (long)when.tv_sec, (long)when.tv_usec));
- return;
- }
- i = when.tv_sec * 1000000ull;
- i += when.tv_usec;
- i *= hz;
- i /= 1000000ull;
-#if 0
- printf("seq_timer usec %llu ticks %llu\n",
- when.tv_sec * 1000000ull + when.tv_usec, i);
-#endif
- t->waiting = 1;
- ret = cv_timedwait(&t->reset_cv, &t->seq_lock, i + 1);
- t->waiting = 0;
-
- if (ret != EWOULDBLOCK)
- SEQ_DEBUG(3, printf("seq_timer didn't timeout\n"));
-
-}
-
-static int
-timer_now(struct seq_softc *t)
-{
- struct timeval now;
- unsigned long long i;
- int ret;
-
- if (t->timerrun == 0)
- now = t->timerstop;
- else
- getmicrotime(&now);
-
- timevalsub(&now, &t->timersub);
-
- i = now.tv_sec * 1000000ull;
- i += now.tv_usec;
- i *= t->timerbase;
-/* i /= t->tempo; */
- i /= 1000000ull;
-
- ret = i;
- /*
- * printf("timer_now: %llu %d\n", i, ret);
- */
-
- return ret;
-}
-
-static void
-seq_eventthread(void *arg)
-{
- struct seq_softc *scp = arg;
- u_char event[EV_SZ];
-
- mtx_lock(&scp->seq_lock);
- SEQ_DEBUG(2, printf("seq_eventthread started\n"));
- while (scp->done == 0) {
-restart:
- while (scp->playing == 0) {
- cv_wait(&scp->state_cv, &scp->seq_lock);
- if (scp->done)
- goto done;
- }
-
- while (MIDIQ_EMPTY(scp->out_q)) {
- cv_broadcast(&scp->empty_cv);
- cv_wait(&scp->out_cv, &scp->seq_lock);
- if (scp->playing == 0)
- goto restart;
- if (scp->done)
- goto done;
- }
-
- MIDIQ_DEQ(scp->out_q, event, EV_SZ);
-
- if (MIDIQ_AVAIL(scp->out_q) < scp->out_water) {
- cv_broadcast(&scp->out_cv);
- selwakeup(&scp->out_sel);
- }
- seq_processevent(scp, event);
- }
-
-done:
- cv_broadcast(&scp->th_cv);
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(2, printf("seq_eventthread finished\n"));
- kproc_exit(0);
-}
-
-/*
- * seq_processevent: This maybe called by the event thread or the IOCTL
- * handler for queued and out of band events respectively.
- */
-static int
-seq_processevent(struct seq_softc *scp, u_char *event)
-{
- int ret;
- kobj_t m;
-
- ret = 0;
-
- if (event[0] == EV_SEQ_LOCAL)
- ret = seq_local(scp, event);
- else if (event[0] == EV_TIMING)
- ret = seq_timing(scp, event);
- else if (event[0] != EV_CHN_VOICE &&
- event[0] != EV_CHN_COMMON &&
- event[0] != EV_SYSEX &&
- event[0] != SEQ_MIDIPUTC) {
- ret = 1;
- SEQ_DEBUG(2, printf("seq_processevent not known %d\n",
- event[0]));
- } else if (seq_fetch_mid(scp, event[1], &m) != 0) {
- ret = 1;
- SEQ_DEBUG(2, printf("seq_processevent midi unit not found %d\n",
- event[1]));
- } else
- switch (event[0]) {
- case EV_CHN_VOICE:
- ret = seq_chnvoice(scp, m, event);
- break;
- case EV_CHN_COMMON:
- ret = seq_chncommon(scp, m, event);
- break;
- case EV_SYSEX:
- ret = seq_sysex(scp, m, event);
- break;
- case SEQ_MIDIPUTC:
- mtx_unlock(&scp->seq_lock);
- ret = SYNTH_WRITERAW(m, &event[2], 1);
- mtx_lock(&scp->seq_lock);
- break;
- }
- return ret;
-}
-
-static int
-seq_addunit(void)
-{
- struct seq_softc *scp;
- int ret;
- u_char *buf;
-
- gone_in(15, "Warning! MIDI sequencer to be removed soon: no longer "
- "needed or used\n");
-
- /* Allocate the softc. */
- ret = ENOMEM;
- scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (scp == NULL) {
- SEQ_DEBUG(1, printf("seq_addunit: softc allocation failed.\n"));
- goto err;
- }
- kobj_init((kobj_t)scp, &sequencer_class);
-
- buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO);
- if (buf == NULL)
- goto err;
- MIDIQ_INIT(scp->in_q, buf, EV_SZ * 1024);
- buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO);
- if (buf == NULL)
- goto err;
- MIDIQ_INIT(scp->out_q, buf, EV_SZ * 1024);
- ret = EINVAL;
-
- scp->midis = malloc(sizeof(kobj_t) * 32, M_TEMP, M_NOWAIT | M_ZERO);
- scp->midi_flags = malloc(sizeof(*scp->midi_flags) * 32, M_TEMP,
- M_NOWAIT | M_ZERO);
-
- if (scp->midis == NULL || scp->midi_flags == NULL)
- goto err;
-
- scp->flags = 0;
-
- mtx_init(&scp->seq_lock, "seqflq", NULL, 0);
- cv_init(&scp->state_cv, "seqstate");
- cv_init(&scp->empty_cv, "seqempty");
- cv_init(&scp->reset_cv, "seqtimer");
- cv_init(&scp->out_cv, "seqqout");
- cv_init(&scp->in_cv, "seqqin");
- cv_init(&scp->th_cv, "seqstart");
-
- /*
- * Init the damn timer
- */
-
- scp->mapper = midimapper_addseq(scp, &scp->unit, &scp->mapper_cookie);
- if (scp->mapper == NULL)
- goto err;
-
- scp->seqdev = make_dev(&seq_cdevsw, SND_DEV_SEQ, UID_ROOT, GID_WHEEL,
- 0666, "sequencer%d", scp->unit);
-
- scp->musicdev = make_dev(&seq_cdevsw, SND_DEV_MUSIC, UID_ROOT,
- GID_WHEEL, 0666, "music%d", scp->unit);
-
- if (scp->seqdev == NULL || scp->musicdev == NULL)
- goto err;
- /*
- * TODO: Add to list of sequencers this module provides
- */
-
- ret =
- kproc_create
- (seq_eventthread, scp, NULL, RFHIGHPID, 0,
- "sequencer %02d", scp->unit);
-
- if (ret)
- goto err;
-
- scp->seqdev->si_drv1 = scp->musicdev->si_drv1 = scp;
-
- SEQ_DEBUG(2, printf("sequencer %d created scp %p\n", scp->unit, scp));
-
- ret = 0;
-
- mtx_lock(&seqinfo_mtx);
- seqs[nseq++] = scp;
- mtx_unlock(&seqinfo_mtx);
-
- goto ok;
-
-err:
- if (scp != NULL) {
- if (scp->seqdev != NULL)
- destroy_dev(scp->seqdev);
- if (scp->musicdev != NULL)
- destroy_dev(scp->musicdev);
- /*
- * TODO: Destroy mutex and cv
- */
- if (scp->midis != NULL)
- free(scp->midis, M_TEMP);
- if (scp->midi_flags != NULL)
- free(scp->midi_flags, M_TEMP);
- if (scp->out_q.b)
- free(scp->out_q.b, M_TEMP);
- if (scp->in_q.b)
- free(scp->in_q.b, M_TEMP);
- free(scp, M_DEVBUF);
- }
-ok:
- return ret;
-}
-
-static int
-seq_delunit(int unit)
-{
- struct seq_softc *scp = seqs[unit];
- int i;
-
- //SEQ_DEBUG(4, printf("seq_delunit: %d\n", unit));
- SEQ_DEBUG(1, printf("seq_delunit: 1 \n"));
- mtx_lock(&scp->seq_lock);
-
- scp->playing = 0;
- scp->done = 1;
- cv_broadcast(&scp->out_cv);
- cv_broadcast(&scp->state_cv);
- cv_broadcast(&scp->reset_cv);
- SEQ_DEBUG(1, printf("seq_delunit: 2 \n"));
- cv_wait(&scp->th_cv, &scp->seq_lock);
- SEQ_DEBUG(1, printf("seq_delunit: 3.0 \n"));
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(1, printf("seq_delunit: 3.1 \n"));
-
- cv_destroy(&scp->state_cv);
- SEQ_DEBUG(1, printf("seq_delunit: 4 \n"));
- cv_destroy(&scp->empty_cv);
- SEQ_DEBUG(1, printf("seq_delunit: 5 \n"));
- cv_destroy(&scp->reset_cv);
- SEQ_DEBUG(1, printf("seq_delunit: 6 \n"));
- cv_destroy(&scp->out_cv);
- SEQ_DEBUG(1, printf("seq_delunit: 7 \n"));
- cv_destroy(&scp->in_cv);
- SEQ_DEBUG(1, printf("seq_delunit: 8 \n"));
- cv_destroy(&scp->th_cv);
-
- SEQ_DEBUG(1, printf("seq_delunit: 10 \n"));
- if (scp->seqdev)
- destroy_dev(scp->seqdev);
- SEQ_DEBUG(1, printf("seq_delunit: 11 \n"));
- if (scp->musicdev)
- destroy_dev(scp->musicdev);
- SEQ_DEBUG(1, printf("seq_delunit: 12 \n"));
- scp->seqdev = scp->musicdev = NULL;
- if (scp->midis != NULL)
- free(scp->midis, M_TEMP);
- SEQ_DEBUG(1, printf("seq_delunit: 13 \n"));
- if (scp->midi_flags != NULL)
- free(scp->midi_flags, M_TEMP);
- SEQ_DEBUG(1, printf("seq_delunit: 14 \n"));
- free(scp->out_q.b, M_TEMP);
- SEQ_DEBUG(1, printf("seq_delunit: 15 \n"));
- free(scp->in_q.b, M_TEMP);
-
- SEQ_DEBUG(1, printf("seq_delunit: 16 \n"));
-
- mtx_destroy(&scp->seq_lock);
- SEQ_DEBUG(1, printf("seq_delunit: 17 \n"));
- free(scp, M_DEVBUF);
-
- mtx_lock(&seqinfo_mtx);
- for (i = unit; i < (nseq - 1); i++)
- seqs[i] = seqs[i + 1];
- nseq--;
- mtx_unlock(&seqinfo_mtx);
-
- return 0;
-}
-
-int
-seq_modevent(module_t mod, int type, void *data)
-{
- int retval, r;
-
- retval = 0;
-
- switch (type) {
- case MOD_LOAD:
- mtx_init(&seqinfo_mtx, "seqmod", NULL, 0);
- retval = seq_addunit();
- break;
-
- case MOD_UNLOAD:
- while (nseq) {
- r = seq_delunit(nseq - 1);
- if (r) {
- retval = r;
- break;
- }
- }
- if (nseq == 0) {
- retval = 0;
- mtx_destroy(&seqinfo_mtx);
- }
- break;
-
- default:
- break;
- }
-
- return retval;
-}
-
-static int
-seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md)
-{
-
- if (unit >= scp->midi_number || unit < 0)
- return EINVAL;
-
- *md = scp->midis[unit];
-
- return 0;
-}
-
-int
-mseq_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
-{
- struct seq_softc *scp = i_dev->si_drv1;
- int i;
-
- gone_in(15, "Warning! MIDI sequencer to be removed soon: no longer "
- "needed or used\n");
-
- if (scp == NULL)
- return ENXIO;
-
- SEQ_DEBUG(3, printf("seq_open: scp %p unit %d, flags 0x%x.\n",
- scp, scp->unit, flags));
-
- /*
- * Mark this device busy.
- */
-
- midistat_lock();
- mtx_lock(&scp->seq_lock);
- if (scp->busy) {
- mtx_unlock(&scp->seq_lock);
- midistat_unlock();
- SEQ_DEBUG(2, printf("seq_open: unit %d is busy.\n", scp->unit));
- return EBUSY;
- }
- scp->fflags = flags;
- /*
- if ((scp->fflags & O_NONBLOCK) != 0)
- scp->flags |= SEQ_F_NBIO;
- */
- scp->music = MIDIDEV(i_dev) == SND_DEV_MUSIC;
-
- /*
- * Enumerate the available midi devices
- */
- scp->midi_number = 0;
- scp->maxunits = midimapper_open_locked(scp->mapper, &scp->mapper_cookie);
-
- if (scp->maxunits == 0)
- SEQ_DEBUG(2, printf("seq_open: no midi devices\n"));
-
- for (i = 0; i < scp->maxunits; i++) {
- scp->midis[scp->midi_number] =
- midimapper_fetch_synth_locked(scp->mapper,
- scp->mapper_cookie, i);
- if (scp->midis[scp->midi_number]) {
- if (SYNTH_OPEN(scp->midis[scp->midi_number], scp,
- scp->fflags) != 0)
- scp->midis[scp->midi_number] = NULL;
- else {
- scp->midi_flags[scp->midi_number] =
- SYNTH_QUERY(scp->midis[scp->midi_number]);
- scp->midi_number++;
- }
- }
- }
- midistat_unlock();
-
- timer_setvals(scp, 60, 100);
-
- timer_start(scp);
- timer_stop(scp);
- /*
- * actually, if we're in rdonly mode, we should start the timer
- */
- /*
- * TODO: Handle recording now
- */
-
- scp->out_water = MIDIQ_SIZE(scp->out_q) / 2;
-
- scp->busy = 1;
- mtx_unlock(&scp->seq_lock);
-
- SEQ_DEBUG(2, printf("seq_open: opened, mode %s.\n",
- scp->music ? "music" : "sequencer"));
- SEQ_DEBUG(2,
- printf("Sequencer %d %p opened maxunits %d midi_number %d:\n",
- scp->unit, scp, scp->maxunits, scp->midi_number));
- for (i = 0; i < scp->midi_number; i++)
- SEQ_DEBUG(3, printf(" midi %d %p\n", i, scp->midis[i]));
-
- return 0;
-}
-
-/*
- * mseq_close
- */
-int
-mseq_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
-{
- int i;
- struct seq_softc *scp = i_dev->si_drv1;
- int ret;
-
- if (scp == NULL)
- return ENXIO;
-
- SEQ_DEBUG(2, printf("seq_close: unit %d.\n", scp->unit));
-
- mtx_lock(&scp->seq_lock);
-
- ret = ENXIO;
- if (scp->busy == 0)
- goto err;
-
- seq_reset(scp);
- seq_sync(scp);
-
- for (i = 0; i < scp->midi_number; i++)
- if (scp->midis[i])
- SYNTH_CLOSE(scp->midis[i]);
-
- midimapper_close(scp->mapper, scp->mapper_cookie);
-
- timer_stop(scp);
-
- scp->busy = 0;
- ret = 0;
-
-err:
- SEQ_DEBUG(3, printf("seq_close: closed ret = %d.\n", ret));
- mtx_unlock(&scp->seq_lock);
- return ret;
-}
-
-int
-mseq_read(struct cdev *i_dev, struct uio *uio, int ioflag)
-{
- int retval, used;
- struct seq_softc *scp = i_dev->si_drv1;
-
-#define SEQ_RSIZE 32
- u_char buf[SEQ_RSIZE];
-
- if (scp == NULL)
- return ENXIO;
-
- SEQ_DEBUG(7, printf("mseq_read: unit %d, resid %zd.\n",
- scp->unit, uio->uio_resid));
-
- mtx_lock(&scp->seq_lock);
- if ((scp->fflags & FREAD) == 0) {
- SEQ_DEBUG(2, printf("mseq_read: unit %d is not for reading.\n",
- scp->unit));
- retval = EIO;
- goto err1;
- }
- /*
- * Begin recording.
- */
- /*
- * if ((scp->flags & SEQ_F_READING) == 0)
- */
- /*
- * TODO, start recording if not alread
- */
-
- /*
- * I think the semantics are to return as soon
- * as possible.
- * Second thought, it doesn't seem like midimoutain
- * expects that at all.
- * TODO: Look up in some sort of spec
- */
-
- while (uio->uio_resid > 0) {
- while (MIDIQ_EMPTY(scp->in_q)) {
- retval = EWOULDBLOCK;
- /*
- * I wish I knew which one to care about
- */
-
- if (scp->fflags & O_NONBLOCK)
- goto err1;
- if (ioflag & O_NONBLOCK)
- goto err1;
-
- retval = cv_wait_sig(&scp->in_cv, &scp->seq_lock);
- if (retval != 0)
- goto err1;
- }
-
- used = MIN(MIDIQ_LEN(scp->in_q), uio->uio_resid);
- used = MIN(used, SEQ_RSIZE);
-
- SEQ_DEBUG(8, printf("midiread: uiomove cc=%d\n", used));
- MIDIQ_DEQ(scp->in_q, buf, used);
- mtx_unlock(&scp->seq_lock);
- retval = uiomove(buf, used, uio);
- mtx_lock(&scp->seq_lock);
- if (retval)
- goto err1;
- }
-
- retval = 0;
-err1:
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(6, printf("mseq_read: ret %d, resid %zd.\n",
- retval, uio->uio_resid));
-
- return retval;
-}
-
-int
-mseq_write(struct cdev *i_dev, struct uio *uio, int ioflag)
-{
- u_char event[EV_SZ], newevent[EV_SZ], ev_code;
- struct seq_softc *scp = i_dev->si_drv1;
- int retval;
- int used;
-
- SEQ_DEBUG(7, printf("seq_write: unit %d, resid %zd.\n",
- scp->unit, uio->uio_resid));
-
- if (scp == NULL)
- return ENXIO;
-
- mtx_lock(&scp->seq_lock);
-
- if ((scp->fflags & FWRITE) == 0) {
- SEQ_DEBUG(2, printf("seq_write: unit %d is not for writing.\n",
- scp->unit));
- retval = EIO;
- goto err0;
- }
- while (uio->uio_resid > 0) {
- while (MIDIQ_AVAIL(scp->out_q) == 0) {
- retval = EWOULDBLOCK;
- if (scp->fflags & O_NONBLOCK)
- goto err0;
- if (ioflag & O_NONBLOCK)
- goto err0;
- SEQ_DEBUG(8, printf("seq_write cvwait\n"));
-
- scp->playing = 1;
- cv_broadcast(&scp->out_cv);
- cv_broadcast(&scp->state_cv);
-
- retval = cv_wait_sig(&scp->out_cv, &scp->seq_lock);
- /*
- * We slept, maybe things have changed since last
- * dying check
- */
- if (retval != 0)
- goto err0;
-#if 0
- /*
- * Useless test
- */
- if (scp != i_dev->si_drv1)
- retval = ENXIO;
-#endif
- }
-
- used = MIN(uio->uio_resid, 4);
-
- SEQ_DEBUG(8, printf("seqout: resid %zd len %jd avail %jd\n",
- uio->uio_resid, (intmax_t)MIDIQ_LEN(scp->out_q),
- (intmax_t)MIDIQ_AVAIL(scp->out_q)));
-
- if (used != 4) {
- retval = ENXIO;
- goto err0;
- }
- mtx_unlock(&scp->seq_lock);
- retval = uiomove(event, used, uio);
- mtx_lock(&scp->seq_lock);
- if (retval)
- goto err0;
-
- ev_code = event[0];
- SEQ_DEBUG(8, printf("seq_write: unit %d, event %s.\n",
- scp->unit, midi_cmdname(ev_code, cmdtab_seqevent)));
-
- /* Have a look at the event code. */
- if (ev_code == SEQ_FULLSIZE) {
- /*
- * TODO: restore code for SEQ_FULLSIZE
- */
-#if 0
- /*
- * A long event, these are the patches/samples for a
- * synthesizer.
- */
- midiunit = *(u_short *)&event[2];
- mtx_lock(&sd->seq_lock);
- ret = lookup_mididev(scp, midiunit, LOOKUP_OPEN, &md);
- mtx_unlock(&sd->seq_lock);
- if (ret != 0)
- return (ret);
-
- SEQ_DEBUG(printf("seq_write: loading a patch to the unit %d.\n", midiunit));
-
- ret = md->synth.loadpatch(md, *(short *)&event[0], buf,
- p + 4, count, 0);
- return (ret);
-#else
- /*
- * For now, just flush the darn buffer
- */
- SEQ_DEBUG(2,
- printf("seq_write: SEQ_FULLSIZE flusing buffer.\n"));
- while (uio->uio_resid > 0) {
- mtx_unlock(&scp->seq_lock);
- retval = uiomove(event, MIN(EV_SZ, uio->uio_resid), uio);
- mtx_lock(&scp->seq_lock);
- if (retval)
- goto err0;
- }
- retval = 0;
- goto err0;
-#endif
- }
- retval = EINVAL;
- if (ev_code >= 128) {
- int error;
-
- /*
- * Some sort of an extended event. The size is eight
- * bytes. scoop extra info.
- */
- if (scp->music && ev_code == SEQ_EXTENDED) {
- SEQ_DEBUG(2, printf("seq_write: invalid level two event %x.\n", ev_code));
- goto err0;
- }
- mtx_unlock(&scp->seq_lock);
- if (uio->uio_resid < 4)
- error = EINVAL;
- else
- error = uiomove((caddr_t)&event[4], 4, uio);
- mtx_lock(&scp->seq_lock);
- if (error) {
- SEQ_DEBUG(2,
- printf("seq_write: user memory mangled?\n"));
- goto err0;
- }
- } else {
- /*
- * Size four event.
- */
- if (scp->music) {
- SEQ_DEBUG(2, printf("seq_write: four byte event in music mode.\n"));
- goto err0;
- }
- }
- if (ev_code == SEQ_MIDIPUTC) {
- /*
- * TODO: event[2] is unit number to receive char.
- * Range check it.
- */
- }
- if (scp->music) {
-#ifdef not_ever_ever
- if (event[0] == EV_TIMING &&
- (event[1] == TMR_START || event[1] == TMR_STOP)) {
- /*
- * For now, try to make midimoutain work by
- * forcing these events to be processed
- * immediately.
- */
- seq_processevent(scp, event);
- } else
- MIDIQ_ENQ(scp->out_q, event, EV_SZ);
-#else
- MIDIQ_ENQ(scp->out_q, event, EV_SZ);
-#endif
- } else {
- if (seq_convertold(event, newevent) > 0)
- MIDIQ_ENQ(scp->out_q, newevent, EV_SZ);
-#if 0
- else
- goto err0;
-#endif
- }
- }
-
- scp->playing = 1;
- cv_broadcast(&scp->state_cv);
- cv_broadcast(&scp->out_cv);
-
- retval = 0;
-
-err0:
- SEQ_DEBUG(6,
- printf("seq_write done: leftover buffer length %zd retval %d\n",
- uio->uio_resid, retval));
- mtx_unlock(&scp->seq_lock);
- return retval;
-}
-
-int
-mseq_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
- struct thread *td)
-{
- int midiunit, ret, tmp;
- struct seq_softc *scp = i_dev->si_drv1;
- struct synth_info *synthinfo;
- struct midi_info *midiinfo;
- u_char event[EV_SZ];
- u_char newevent[EV_SZ];
-
- kobj_t md;
-
- /*
- * struct snd_size *sndsize;
- */
-
- if (scp == NULL)
- return ENXIO;
-
- SEQ_DEBUG(6, printf("seq_ioctl: unit %d, cmd %s.\n",
- scp->unit, midi_cmdname(cmd, cmdtab_seqioctl)));
-
- ret = 0;
-
- switch (cmd) {
- case SNDCTL_SEQ_GETTIME:
- /*
- * ioctl needed by libtse
- */
- mtx_lock(&scp->seq_lock);
- *(int *)arg = timer_now(scp);
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(6, printf("seq_ioctl: gettime %d.\n", *(int *)arg));
- ret = 0;
- break;
- case SNDCTL_TMR_METRONOME:
- /* fallthrough */
- case SNDCTL_TMR_SOURCE:
- /*
- * Not implemented
- */
- ret = 0;
- break;
- case SNDCTL_TMR_TEMPO:
- event[1] = TMR_TEMPO;
- event[4] = *(int *)arg & 0xFF;
- event[5] = (*(int *)arg >> 8) & 0xFF;
- event[6] = (*(int *)arg >> 16) & 0xFF;
- event[7] = (*(int *)arg >> 24) & 0xFF;
- goto timerevent;
- case SNDCTL_TMR_TIMEBASE:
- event[1] = TMR_TIMERBASE;
- event[4] = *(int *)arg & 0xFF;
- event[5] = (*(int *)arg >> 8) & 0xFF;
- event[6] = (*(int *)arg >> 16) & 0xFF;
- event[7] = (*(int *)arg >> 24) & 0xFF;
- goto timerevent;
- case SNDCTL_TMR_START:
- event[1] = TMR_START;
- goto timerevent;
- case SNDCTL_TMR_STOP:
- event[1] = TMR_STOP;
- goto timerevent;
- case SNDCTL_TMR_CONTINUE:
- event[1] = TMR_CONTINUE;
-timerevent:
- event[0] = EV_TIMING;
- mtx_lock(&scp->seq_lock);
- if (!scp->music) {
- ret = EINVAL;
- mtx_unlock(&scp->seq_lock);
- break;
- }
- seq_processevent(scp, event);
- mtx_unlock(&scp->seq_lock);
- break;
- case SNDCTL_TMR_SELECT:
- SEQ_DEBUG(2,
- printf("seq_ioctl: SNDCTL_TMR_SELECT not supported\n"));
- ret = EINVAL;
- break;
- case SNDCTL_SEQ_SYNC:
- if (mode == O_RDONLY) {
- ret = 0;
- break;
- }
- mtx_lock(&scp->seq_lock);
- ret = seq_sync(scp);
- mtx_unlock(&scp->seq_lock);
- break;
- case SNDCTL_SEQ_PANIC:
- /* fallthrough */
- case SNDCTL_SEQ_RESET:
- /*
- * SNDCTL_SEQ_PANIC == SNDCTL_SEQ_RESET
- */
- mtx_lock(&scp->seq_lock);
- seq_reset(scp);
- mtx_unlock(&scp->seq_lock);
- ret = 0;
- break;
- case SNDCTL_SEQ_TESTMIDI:
- mtx_lock(&scp->seq_lock);
- /*
- * TODO: SNDCTL_SEQ_TESTMIDI now means "can I write to the
- * device?".
- */
- mtx_unlock(&scp->seq_lock);
- break;
-#if 0
- case SNDCTL_SEQ_GETINCOUNT:
- if (mode == O_WRONLY)
- *(int *)arg = 0;
- else {
- mtx_lock(&scp->seq_lock);
- *(int *)arg = scp->in_q.rl;
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(printf("seq_ioctl: incount %d.\n",
- *(int *)arg));
- }
- ret = 0;
- break;
- case SNDCTL_SEQ_GETOUTCOUNT:
- if (mode == O_RDONLY)
- *(int *)arg = 0;
- else {
- mtx_lock(&scp->seq_lock);
- *(int *)arg = scp->out_q.fl;
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(printf("seq_ioctl: outcount %d.\n",
- *(int *)arg));
- }
- ret = 0;
- break;
-#endif
- case SNDCTL_SEQ_CTRLRATE:
- if (*(int *)arg != 0) {
- ret = EINVAL;
- break;
- }
- mtx_lock(&scp->seq_lock);
- *(int *)arg = scp->timerbase;
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(3, printf("seq_ioctl: ctrlrate %d.\n", *(int *)arg));
- ret = 0;
- break;
- /*
- * TODO: ioctl SNDCTL_SEQ_RESETSAMPLES
- */
-#if 0
- case SNDCTL_SEQ_RESETSAMPLES:
- mtx_lock(&scp->seq_lock);
- ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md);
- mtx_unlock(&scp->seq_lock);
- if (ret != 0)
- break;
- ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg,
- SND_DEV_MIDIN), cmd, arg, mode, td);
- break;
-#endif
- case SNDCTL_SEQ_NRSYNTHS:
- mtx_lock(&scp->seq_lock);
- *(int *)arg = scp->midi_number;
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(3, printf("seq_ioctl: synths %d.\n", *(int *)arg));
- ret = 0;
- break;
- case SNDCTL_SEQ_NRMIDIS:
- mtx_lock(&scp->seq_lock);
- if (scp->music)
- *(int *)arg = 0;
- else {
- /*
- * TODO: count the numbder of devices that can WRITERAW
- */
- *(int *)arg = scp->midi_number;
- }
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(3, printf("seq_ioctl: midis %d.\n", *(int *)arg));
- ret = 0;
- break;
- /*
- * TODO: ioctl SNDCTL_SYNTH_MEMAVL
- */
-#if 0
- case SNDCTL_SYNTH_MEMAVL:
- mtx_lock(&scp->seq_lock);
- ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md);
- mtx_unlock(&scp->seq_lock);
- if (ret != 0)
- break;
- ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg,
- SND_DEV_MIDIN), cmd, arg, mode, td);
- break;
-#endif
- case SNDCTL_SEQ_OUTOFBAND:
- for (ret = 0; ret < EV_SZ; ret++)
- event[ret] = (u_char)arg[0];
-
- mtx_lock(&scp->seq_lock);
- if (scp->music)
- ret = seq_processevent(scp, event);
- else {
- if (seq_convertold(event, newevent) > 0)
- ret = seq_processevent(scp, newevent);
- else
- ret = EINVAL;
- }
- mtx_unlock(&scp->seq_lock);
- break;
- case SNDCTL_SYNTH_INFO:
- synthinfo = (struct synth_info *)arg;
- midiunit = synthinfo->device;
- mtx_lock(&scp->seq_lock);
- if (seq_fetch_mid(scp, midiunit, &md) == 0) {
- bzero(synthinfo, sizeof(*synthinfo));
- synthinfo->name[0] = 'f';
- synthinfo->name[1] = 'a';
- synthinfo->name[2] = 'k';
- synthinfo->name[3] = 'e';
- synthinfo->name[4] = 's';
- synthinfo->name[5] = 'y';
- synthinfo->name[6] = 'n';
- synthinfo->name[7] = 't';
- synthinfo->name[8] = 'h';
- synthinfo->device = midiunit;
- synthinfo->synth_type = SYNTH_TYPE_MIDI;
- synthinfo->capabilities = scp->midi_flags[midiunit];
- ret = 0;
- } else
- ret = EINVAL;
- mtx_unlock(&scp->seq_lock);
- break;
- case SNDCTL_MIDI_INFO:
- midiinfo = (struct midi_info *)arg;
- midiunit = midiinfo->device;
- mtx_lock(&scp->seq_lock);
- if (seq_fetch_mid(scp, midiunit, &md) == 0) {
- bzero(midiinfo, sizeof(*midiinfo));
- midiinfo->name[0] = 'f';
- midiinfo->name[1] = 'a';
- midiinfo->name[2] = 'k';
- midiinfo->name[3] = 'e';
- midiinfo->name[4] = 'm';
- midiinfo->name[5] = 'i';
- midiinfo->name[6] = 'd';
- midiinfo->name[7] = 'i';
- midiinfo->device = midiunit;
- midiinfo->capabilities = scp->midi_flags[midiunit];
- /*
- * TODO: What devtype?
- */
- midiinfo->dev_type = 0x01;
- ret = 0;
- } else
- ret = EINVAL;
- mtx_unlock(&scp->seq_lock);
- break;
- case SNDCTL_SEQ_THRESHOLD:
- mtx_lock(&scp->seq_lock);
- RANGE(*(int *)arg, 1, MIDIQ_SIZE(scp->out_q) - 1);
- scp->out_water = *(int *)arg;
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(3, printf("seq_ioctl: water %d.\n", *(int *)arg));
- ret = 0;
- break;
- case SNDCTL_MIDI_PRETIME:
- tmp = *(int *)arg;
- if (tmp < 0)
- tmp = 0;
- mtx_lock(&scp->seq_lock);
- scp->pre_event_timeout = (hz * tmp) / 10;
- *(int *)arg = scp->pre_event_timeout;
- mtx_unlock(&scp->seq_lock);
- SEQ_DEBUG(3, printf("seq_ioctl: pretime %d.\n", *(int *)arg));
- ret = 0;
- break;
- case SNDCTL_FM_4OP_ENABLE:
- case SNDCTL_PMGR_IFACE:
- case SNDCTL_PMGR_ACCESS:
- /*
- * Patch manager and fm are ded, ded, ded.
- */
- /* fallthrough */
- default:
- /*
- * TODO: Consider ioctl default case.
- * Old code used to
- * if ((scp->fflags & O_ACCMODE) == FREAD) {
- * ret = EIO;
- * break;
- * }
- * Then pass on the ioctl to device 0
- */
- SEQ_DEBUG(2,
- printf("seq_ioctl: unsupported IOCTL %ld.\n", cmd));
- ret = EINVAL;
- break;
- }
-
- return ret;
-}
-
-int
-mseq_poll(struct cdev *i_dev, int events, struct thread *td)
-{
- int ret, lim;
- struct seq_softc *scp = i_dev->si_drv1;
-
- SEQ_DEBUG(3, printf("seq_poll: unit %d.\n", scp->unit));
- SEQ_DEBUG(1, printf("seq_poll: unit %d.\n", scp->unit));
-
- mtx_lock(&scp->seq_lock);
-
- ret = 0;
-
- /* Look up the appropriate queue and select it. */
- if ((events & (POLLOUT | POLLWRNORM)) != 0) {
- /* Start playing. */
- scp->playing = 1;
- cv_broadcast(&scp->state_cv);
- cv_broadcast(&scp->out_cv);
-
- lim = scp->out_water;
-
- if (MIDIQ_AVAIL(scp->out_q) < lim)
- /* No enough space, record select. */
- selrecord(td, &scp->out_sel);
- else
- /* We can write now. */
- ret |= events & (POLLOUT | POLLWRNORM);
- }
- if ((events & (POLLIN | POLLRDNORM)) != 0) {
- /* TODO: Start recording. */
-
- /* Find out the boundary. */
- lim = 1;
- if (MIDIQ_LEN(scp->in_q) < lim)
- /* No data ready, record select. */
- selrecord(td, &scp->in_sel);
- else
- /* We can read now. */
- ret |= events & (POLLIN | POLLRDNORM);
- }
- mtx_unlock(&scp->seq_lock);
-
- return (ret);
-}
-
-#if 0
-static void
-sein_qtr(void *p, void /* mididev_info */ *md)
-{
- struct seq_softc *scp;
-
- scp = (struct seq_softc *)p;
-
- mtx_lock(&scp->seq_lock);
-
- /* Restart playing if we have the data to output. */
- if (scp->queueout_pending)
- seq_callback(scp, SEQ_CB_START | SEQ_CB_WR);
- /* Check the midi device if we are reading. */
- if ((scp->flags & SEQ_F_READING) != 0)
- seq_midiinput(scp, md);
-
- mtx_unlock(&scp->seq_lock);
-}
-
-#endif
-/*
- * seq_convertold
- * Was the old playevent. Use this to convert and old
- * style /dev/sequencer event to a /dev/music event
- */
-static int
-seq_convertold(u_char *event, u_char *out)
-{
- int used;
- u_char dev, chn, note, vel;
-
- out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] =
- out[7] = 0;
-
- dev = 0;
- chn = event[1];
- note = event[2];
- vel = event[3];
-
- used = 0;
-
-restart:
- /*
- * TODO: Debug statement
- */
- switch (event[0]) {
- case EV_TIMING:
- case EV_CHN_VOICE:
- case EV_CHN_COMMON:
- case EV_SYSEX:
- case EV_SEQ_LOCAL:
- out[0] = event[0];
- out[1] = event[1];
- out[2] = event[2];
- out[3] = event[3];
- out[4] = event[4];
- out[5] = event[5];
- out[6] = event[6];
- out[7] = event[7];
- used += 8;
- break;
- case SEQ_NOTEOFF:
- out[0] = EV_CHN_VOICE;
- out[1] = dev;
- out[2] = MIDI_NOTEOFF;
- out[3] = chn;
- out[4] = note;
- out[5] = 255;
- used += 4;
- break;
-
- case SEQ_NOTEON:
- out[0] = EV_CHN_VOICE;
- out[1] = dev;
- out[2] = MIDI_NOTEON;
- out[3] = chn;
- out[4] = note;
- out[5] = vel;
- used += 4;
- break;
-
- /*
- * wait delay = (event[2] << 16) + (event[3] << 8) + event[4]
- */
-
- case SEQ_PGMCHANGE:
- out[0] = EV_CHN_COMMON;
- out[1] = dev;
- out[2] = MIDI_PGM_CHANGE;
- out[3] = chn;
- out[4] = note;
- out[5] = vel;
- used += 4;
- break;
-/*
- out[0] = EV_TIMING;
- out[1] = dev;
- out[2] = MIDI_PGM_CHANGE;
- out[3] = chn;
- out[4] = note;
- out[5] = vel;
- SEQ_DEBUG(4,printf("seq_playevent: synctimer\n"));
- break;
-*/
-
- case SEQ_MIDIPUTC:
- SEQ_DEBUG(4,
- printf("seq_playevent: put data 0x%02x, unit %d.\n",
- event[1], event[2]));
- /*
- * Pass through to the midi device.
- * device = event[2]
- * data = event[1]
- */
- out[0] = SEQ_MIDIPUTC;
- out[1] = dev;
- out[2] = chn;
- used += 4;
- break;
-#ifdef notyet
- case SEQ_ECHO:
- /*
- * This isn't handled here yet because I don't know if I can
- * just use four bytes events. There might be consequences
- * in the _read routing
- */
- if (seq_copytoinput(scp, event, 4) == EAGAIN) {
- ret = QUEUEFULL;
- break;
- }
- ret = MORE;
- break;
-#endif
- case SEQ_EXTENDED:
- switch (event[1]) {
- case SEQ_NOTEOFF:
- case SEQ_NOTEON:
- case SEQ_PGMCHANGE:
- event++;
- used = 4;
- goto restart;
- break;
- case SEQ_AFTERTOUCH:
- /*
- * SYNTH_AFTERTOUCH(md, event[3], event[4])
- */
- case SEQ_BALANCE:
- /*
- * SYNTH_PANNING(md, event[3], (char)event[4])
- */
- case SEQ_CONTROLLER:
- /*
- * SYNTH_CONTROLLER(md, event[3], event[4], *(short *)&event[5])
- */
- case SEQ_VOLMODE:
- /*
- * SYNTH_VOLUMEMETHOD(md, event[3])
- */
- default:
- SEQ_DEBUG(2,
- printf("seq_convertold: SEQ_EXTENDED type %d"
- "not handled\n", event[1]));
- break;
- }
- break;
- case SEQ_WAIT:
- out[0] = EV_TIMING;
- out[1] = TMR_WAIT_REL;
- out[4] = event[2];
- out[5] = event[3];
- out[6] = event[4];
-
- SEQ_DEBUG(5, printf("SEQ_WAIT %d",
- event[2] + (event[3] << 8) + (event[4] << 24)));
-
- used += 4;
- break;
-
- case SEQ_ECHO:
- case SEQ_SYNCTIMER:
- case SEQ_PRIVATE:
- default:
- SEQ_DEBUG(2,
- printf("seq_convertold: event type %d not handled %d %d %d\n",
- event[0], event[1], event[2], event[3]));
- break;
- }
- return used;
-}
-
-/*
- * Writting to the sequencer buffer never blocks and drops
- * input which cannot be queued
- */
-void
-seq_copytoinput(struct seq_softc *scp, u_char *event, int len)
-{
-
- mtx_assert(&scp->seq_lock, MA_OWNED);
-
- if (MIDIQ_AVAIL(scp->in_q) < len) {
- /*
- * ENOROOM? EINPUTDROPPED? ETOUGHLUCK?
- */
- SEQ_DEBUG(2, printf("seq_copytoinput: queue full\n"));
- } else {
- MIDIQ_ENQ(scp->in_q, event, len);
- selwakeup(&scp->in_sel);
- cv_broadcast(&scp->in_cv);
- }
-
-}
-
-static int
-seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event)
-{
- int ret, voice;
- u_char cmd, chn, note, parm;
-
- ret = 0;
- cmd = event[2];
- chn = event[3];
- note = event[4];
- parm = event[5];
-
- mtx_assert(&scp->seq_lock, MA_OWNED);
-
- SEQ_DEBUG(5, printf("seq_chnvoice: unit %d, dev %d, cmd %s,"
- " chn %d, note %d, parm %d.\n", scp->unit, event[1],
- midi_cmdname(cmd, cmdtab_seqcv), chn, note, parm));
-
- voice = SYNTH_ALLOC(md, chn, note);
-
- mtx_unlock(&scp->seq_lock);
-
- switch (cmd) {
- case MIDI_NOTEON:
- if (note < 128 || note == 255) {
-#if 0
- if (scp->music && chn == 9) {
- /*
- * This channel is a percussion. The note
- * number is the patch number.
- */
- /*
- mtx_unlock(&scp->seq_lock);
- if (SYNTH_SETINSTR(md, voice, 128 + note)
- == EAGAIN) {
- mtx_lock(&scp->seq_lock);
- return (QUEUEFULL);
- }
- mtx_lock(&scp->seq_lock);
- */
- note = 60; /* Middle C. */
- }
-#endif
- if (scp->music) {
- /*
- mtx_unlock(&scp->seq_lock);
- if (SYNTH_SETUPVOICE(md, voice, chn)
- == EAGAIN) {
- mtx_lock(&scp->seq_lock);
- return (QUEUEFULL);
- }
- mtx_lock(&scp->seq_lock);
- */
- }
- SYNTH_STARTNOTE(md, voice, note, parm);
- }
- break;
- case MIDI_NOTEOFF:
- SYNTH_KILLNOTE(md, voice, note, parm);
- break;
- case MIDI_KEY_PRESSURE:
- SYNTH_AFTERTOUCH(md, voice, parm);
- break;
- default:
- ret = 1;
- SEQ_DEBUG(2, printf("seq_chnvoice event type %d not handled\n",
- event[1]));
- break;
- }
-
- mtx_lock(&scp->seq_lock);
- return ret;
-}
-
-static int
-seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event)
-{
- int ret;
- u_short w14;
- u_char cmd, chn, p1;
-
- ret = 0;
- cmd = event[2];
- chn = event[3];
- p1 = event[4];
- w14 = *(u_short *)&event[6];
-
- SEQ_DEBUG(5, printf("seq_chncommon: unit %d, dev %d, cmd %s, chn %d,"
- " p1 %d, w14 %d.\n", scp->unit, event[1],
- midi_cmdname(cmd, cmdtab_seqccmn), chn, p1, w14));
- mtx_unlock(&scp->seq_lock);
- switch (cmd) {
- case MIDI_PGM_CHANGE:
- SEQ_DEBUG(4, printf("seq_chncommon pgmchn chn %d pg %d\n",
- chn, p1));
- SYNTH_SETINSTR(md, chn, p1);
- break;
- case MIDI_CTL_CHANGE:
- SEQ_DEBUG(4, printf("seq_chncommon ctlch chn %d pg %d %d\n",
- chn, p1, w14));
- SYNTH_CONTROLLER(md, chn, p1, w14);
- break;
- case MIDI_PITCH_BEND:
- if (scp->music) {
- /*
- * TODO: MIDI_PITCH_BEND
- */
-#if 0
- mtx_lock(&md->synth.vc_mtx);
- md->synth.chn_info[chn].bender_value = w14;
- if (md->midiunit >= 0) {
- /*
- * Handle all of the notes playing on this
- * channel.
- */
- key = ((int)chn << 8);
- for (i = 0; i < md->synth.alloc.max_voice; i++)
- if ((md->synth.alloc.map[i] & 0xff00) == key) {
- mtx_unlock(&md->synth.vc_mtx);
- mtx_unlock(&scp->seq_lock);
- if (md->synth.bender(md, i, w14) == EAGAIN) {
- mtx_lock(&scp->seq_lock);
- return (QUEUEFULL);
- }
- mtx_lock(&scp->seq_lock);
- }
- } else {
- mtx_unlock(&md->synth.vc_mtx);
- mtx_unlock(&scp->seq_lock);
- if (md->synth.bender(md, chn, w14) == EAGAIN) {
- mtx_lock(&scp->seq_lock);
- return (QUEUEFULL);
- }
- mtx_lock(&scp->seq_lock);
- }
-#endif
- } else
- SYNTH_BENDER(md, chn, w14);
- break;
- default:
- ret = 1;
- SEQ_DEBUG(2,
- printf("seq_chncommon event type %d not handled.\n",
- event[1]));
- break;
- }
- mtx_lock(&scp->seq_lock);
- return ret;
-}
-
-static int
-seq_timing(struct seq_softc *scp, u_char *event)
-{
- int param;
- int ret;
-
- ret = 0;
- param = event[4] + (event[5] << 8) +
- (event[6] << 16) + (event[7] << 24);
-
- SEQ_DEBUG(5, printf("seq_timing: unit %d, cmd %d, param %d.\n",
- scp->unit, event[1], param));
- switch (event[1]) {
- case TMR_WAIT_REL:
- timer_wait(scp, param, 0);
- break;
- case TMR_WAIT_ABS:
- timer_wait(scp, param, 1);
- break;
- case TMR_START:
- timer_start(scp);
- cv_broadcast(&scp->reset_cv);
- break;
- case TMR_STOP:
- timer_stop(scp);
- /*
- * The following cv_broadcast isn't needed since we only
- * wait for 0->1 transitions. It probably won't hurt
- */
- cv_broadcast(&scp->reset_cv);
- break;
- case TMR_CONTINUE:
- timer_continue(scp);
- cv_broadcast(&scp->reset_cv);
- break;
- case TMR_TEMPO:
- if (param < 8)
- param = 8;
- if (param > 360)
- param = 360;
- SEQ_DEBUG(4, printf("Timer set tempo %d\n", param));
- timer_setvals(scp, param, scp->timerbase);
- break;
- case TMR_TIMERBASE:
- if (param < 1)
- param = 1;
- if (param > 1000)
- param = 1000;
- SEQ_DEBUG(4, printf("Timer set timerbase %d\n", param));
- timer_setvals(scp, scp->tempo, param);
- break;
- case TMR_ECHO:
- /*
- * TODO: Consider making 4-byte events for /dev/sequencer
- * PRO: Maybe needed by legacy apps
- * CON: soundcard.h has been warning for a while many years
- * to expect 8 byte events.
- */
-#if 0
- if (scp->music)
- seq_copytoinput(scp, event, 8);
- else {
- param = (param << 8 | SEQ_ECHO);
- seq_copytoinput(scp, (u_char *)&param, 4);
- }
-#else
- seq_copytoinput(scp, event, 8);
-#endif
- break;
- default:
- SEQ_DEBUG(2, printf("seq_timing event type %d not handled.\n",
- event[1]));
- ret = 1;
- break;
- }
- return ret;
-}
-
-static int
-seq_local(struct seq_softc *scp, u_char *event)
-{
- int ret;
-
- ret = 0;
- mtx_assert(&scp->seq_lock, MA_OWNED);
-
- SEQ_DEBUG(5, printf("seq_local: unit %d, cmd %d\n", scp->unit,
- event[1]));
- switch (event[1]) {
- default:
- SEQ_DEBUG(1, printf("seq_local event type %d not handled\n",
- event[1]));
- ret = 1;
- break;
- }
- return ret;
-}
-
-static int
-seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event)
-{
- int i, l;
-
- mtx_assert(&scp->seq_lock, MA_OWNED);
- SEQ_DEBUG(5, printf("seq_sysex: unit %d device %d\n", scp->unit,
- event[1]));
- l = 0;
- for (i = 0; i < 6 && event[i + 2] != 0xff; i++)
- l = i + 1;
- if (l > 0) {
- mtx_unlock(&scp->seq_lock);
- if (SYNTH_SENDSYSEX(md, &event[2], l) == EAGAIN) {
- mtx_lock(&scp->seq_lock);
- return 1;
- }
- mtx_lock(&scp->seq_lock);
- }
- return 0;
-}
-
-/*
- * Reset no longer closes the raw devices nor seq_sync's
- * Callers are IOCTL and seq_close
- */
-static void
-seq_reset(struct seq_softc *scp)
-{
- int chn, i;
- kobj_t m;
-
- mtx_assert(&scp->seq_lock, MA_OWNED);
-
- SEQ_DEBUG(5, printf("seq_reset: unit %d.\n", scp->unit));
-
- /*
- * Stop reading and writing.
- */
-
- /* scp->recording = 0; */
- scp->playing = 0;
- cv_broadcast(&scp->state_cv);
- cv_broadcast(&scp->out_cv);
- cv_broadcast(&scp->reset_cv);
-
- /*
- * For now, don't reset the timers.
- */
- MIDIQ_CLEAR(scp->in_q);
- MIDIQ_CLEAR(scp->out_q);
-
- for (i = 0; i < scp->midi_number; i++) {
- m = scp->midis[i];
- mtx_unlock(&scp->seq_lock);
- SYNTH_RESET(m);
- for (chn = 0; chn < 16; chn++) {
- SYNTH_CONTROLLER(m, chn, 123, 0);
- SYNTH_CONTROLLER(m, chn, 121, 0);
- SYNTH_BENDER(m, chn, 1 << 13);
- }
- mtx_lock(&scp->seq_lock);
- }
-}
-
-/*
- * seq_sync
- * *really* flush the output queue
- * flush the event queue, then flush the synthsisers.
- * Callers are IOCTL and close
- */
-
-#define SEQ_SYNC_TIMEOUT 8
-static int
-seq_sync(struct seq_softc *scp)
-{
- int i, rl, sync[16], done;
-
- mtx_assert(&scp->seq_lock, MA_OWNED);
-
- SEQ_DEBUG(4, printf("seq_sync: unit %d.\n", scp->unit));
-
- /*
- * Wait until output queue is empty. Check every so often to see if
- * the queue is moving along. If it isn't just abort.
- */
- while (!MIDIQ_EMPTY(scp->out_q)) {
- if (!scp->playing) {
- scp->playing = 1;
- cv_broadcast(&scp->state_cv);
- cv_broadcast(&scp->out_cv);
- }
- rl = MIDIQ_LEN(scp->out_q);
-
- i = cv_timedwait_sig(&scp->out_cv,
- &scp->seq_lock, SEQ_SYNC_TIMEOUT * hz);
-
- if (i == EINTR || i == ERESTART) {
- if (i == EINTR) {
- /*
- * XXX: I don't know why we stop playing
- */
- scp->playing = 0;
- cv_broadcast(&scp->out_cv);
- }
- return i;
- }
- if (i == EWOULDBLOCK && rl == MIDIQ_LEN(scp->out_q) &&
- scp->waiting == 0) {
- /*
- * A queue seems to be stuck up. Give up and clear
- * queues.
- */
- MIDIQ_CLEAR(scp->out_q);
- scp->playing = 0;
- cv_broadcast(&scp->state_cv);
- cv_broadcast(&scp->out_cv);
- cv_broadcast(&scp->reset_cv);
-
- /*
- * TODO: Consider if the raw devices need to be flushed
- */
-
- SEQ_DEBUG(1, printf("seq_sync queue stuck, aborting\n"));
-
- return i;
- }
- }
-
- scp->playing = 0;
- /*
- * Since syncing a midi device might block, unlock scp->seq_lock.
- */
-
- mtx_unlock(&scp->seq_lock);
- for (i = 0; i < scp->midi_number; i++)
- sync[i] = 1;
-
- do {
- done = 1;
- for (i = 0; i < scp->midi_number; i++)
- if (sync[i]) {
- if (SYNTH_INSYNC(scp->midis[i]) == 0)
- sync[i] = 0;
- else
- done = 0;
- }
- if (!done)
- DELAY(5000);
-
- } while (!done);
-
- mtx_lock(&scp->seq_lock);
- return 0;
-}
-
-char *
-midi_cmdname(int cmd, midi_cmdtab *tab)
-{
- while (tab->name != NULL) {
- if (cmd == tab->cmd)
- return (tab->name);
- tab++;
- }
-
- return ("unknown");
-}
diff --git a/sys/dev/sound/midi/synth_if.m b/sys/dev/sound/midi/synth_if.m
deleted file mode 100644
index a763b3422bc6..000000000000
--- a/sys/dev/sound/midi/synth_if.m
+++ /dev/null
@@ -1,312 +0,0 @@
-#-
-# Copyright (c) 2003 Mathew Kanner
-# 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 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.
-#
-#
-
-INTERFACE synth;
-
-#include <sys/systm.h>
-
-CODE {
-
-synth_killnote_t nokillnote;
-synth_startnote_t nostartnote;
-synth_setinstr_t nosetinstr;
-synth_hwcontrol_t nohwcontrol;
-synth_aftertouch_t noaftertouch;
-synth_panning_t nopanning;
-synth_controller_t nocontroller;
-synth_volumemethod_t novolumemethod;
-synth_bender_t nobender;
-synth_setupvoice_t nosetupvoice;
-synth_sendsysex_t nosendsysex;
-synth_allocvoice_t noallocvoice;
-synth_writeraw_t nowriteraw;
-synth_reset_t noreset;
-synth_shortname_t noshortname;
-synth_open_t noopen;
-synth_close_t noclose;
-synth_query_t noquery;
-synth_insync_t noinsync;
-synth_alloc_t noalloc;
-
- int
- nokillnote(void *_kobj, uint8_t _chn, uint8_t _note, uint8_t _vel)
- {
- printf("nokillnote\n");
- return 0;
- }
-
- int
- noopen(void *_kobj, void *_arg, int mode)
- {
- printf("noopen\n");
- return 0;
- }
-
- int
- noquery(void *_kboj)
- {
- printf("noquery\n");
- return 0;
- }
-
- int
- nostartnote(void *_kb, uint8_t _voice, uint8_t _note, uint8_t _parm)
- {
- printf("nostartnote\n");
- return 0;
- }
-
- int
- nosetinstr(void *_kb, uint8_t _chn, uint16_t _patchno)
- {
- printf("nosetinstr\n");
- return 0;
- }
-
- int
- nohwcontrol(void *_kb, uint8_t *_event)
- {
- printf("nohwcontrol\n");
- return 0;
- }
-
- int
- noaftertouch ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2)
- {
- printf("noaftertouch\n");
- return 0;
- }
-
- int
- nopanning ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2)
- {
- printf("nopanning\n");
- return 0;
- }
-
- int
- nocontroller ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2, uint16_t _x3)
- {
- printf("nocontroller\n");
- return 0;
- }
-
- int
- novolumemethod (
- void /* X */ * _kobj,
- uint8_t _x1)
- {
- printf("novolumemethod\n");
- return 0;
- }
-
- int
- nobender ( void /* X */ * _kobj, uint8_t _voice, uint16_t _bend)
- {
- printf("nobender\n");
- return 0;
- }
-
- int
- nosetupvoice ( void /* X */ * _kobj, uint8_t _voice, uint8_t _chn)
- {
-
- printf("nosetupvoice\n");
- return 0;
- }
-
- int
- nosendsysex ( void /* X */ * _kobj, void * _buf, size_t _len)
- {
- printf("nosendsysex\n");
- return 0;
- }
-
- int
- noallocvoice ( void /* X */ * _kobj, uint8_t _chn, uint8_t _note, void *_x)
- {
- printf("noallocvoice\n");
- return 0;
- }
-
- int
- nowriteraw ( void /* X */ * _kobjt, uint8_t * _buf, size_t _len)
- {
- printf("nowriteraw\n");
- return 1;
- }
-
- int
- noreset ( void /* X */ * _kobjt)
- {
-
- printf("noreset\n");
- return 0;
- }
-
- char *
- noshortname (void /* X */ * _kobjt)
- {
- printf("noshortname\n");
- return "noshortname";
- }
-
- int
- noclose ( void /* X */ * _kobjt)
- {
-
- printf("noclose\n");
- return 0;
- }
-
- int
- noinsync (void /* X */ * _kobjt)
- {
-
- printf("noinsync\n");
- return 0;
- }
-
- int
- noalloc ( void /* x */ * _kbojt, uint8_t _chn, uint8_t _note)
- {
- printf("noalloc\n");
- return 0;
- }
-}
-
-METHOD int killnote {
- void /* X */ *_kobj;
- uint8_t _chan;
- uint8_t _note;
- uint8_t _vel;
-} DEFAULT nokillnote;
-
-METHOD int startnote {
- void /* X */ *_kobj;
- uint8_t _voice;
- uint8_t _note;
- uint8_t _parm;
-} DEFAULT nostartnote;
-
-METHOD int setinstr {
- void /* X */ *_kobj;
- uint8_t _chn;
- uint16_t _patchno;
-} DEFAULT nosetinstr;
-
-METHOD int hwcontrol {
- void /* X */ *_kobj;
- uint8_t *_event;
-} DEFAULT nohwcontrol;
-
-METHOD int aftertouch {
- void /* X */ *_kobj;
- uint8_t _x1;
- uint8_t _x2;
-} DEFAULT noaftertouch;
-
-METHOD int panning {
- void /* X */ *_kobj;
- uint8_t _x1;
- uint8_t _x2;
-} DEFAULT nopanning;
-
-METHOD int controller {
- void /* X */ *_kobj;
- uint8_t _x1;
- uint8_t _x2;
- uint16_t _x3;
-} DEFAULT nocontroller;
-
-METHOD int volumemethod {
- void /* X */ *_kobj;
- uint8_t _x1;
-} DEFAULT novolumemethod;
-
-METHOD int bender {
- void /* X */ *_kobj;
- uint8_t _voice;
- uint16_t _bend;
-} DEFAULT nobender;
-
-METHOD int setupvoice {
- void /* X */ *_kobj;
- uint8_t _voice;
- uint8_t _chn;
-} DEFAULT nosetupvoice;
-
-METHOD int sendsysex {
- void /* X */ *_kobj;
- void *_buf;
- size_t _len;
-} DEFAULT nosendsysex;
-
-METHOD int allocvoice {
- void /* X */ *_kobj;
- uint8_t _chn;
- uint8_t _note;
- void *_x;
-} DEFAULT noallocvoice;
-
-METHOD int writeraw {
- void /* X */ *_kobjt;
- uint8_t *_buf;
- size_t _len;
-} DEFAULT nowriteraw;
-
-METHOD int reset {
- void /* X */ *_kobjt;
-} DEFAULT noreset;
-
-METHOD char * shortname {
- void /* X */ *_kobjt;
-} DEFAULT noshortname;
-
-METHOD int open {
- void /* X */ *_kobjt;
- void *_sythn;
- int _mode;
-} DEFAULT noopen;
-
-METHOD int close {
- void /* X */ *_kobjt;
-} DEFAULT noclose;
-
-METHOD int query {
- void /* X */ *_kobjt;
-} DEFAULT noquery;
-
-METHOD int insync {
- void /* X */ *_kobjt;
-} DEFAULT noinsync;
-
-METHOD int alloc {
- void /* x */ *_kbojt;
- uint8_t _chn;
- uint8_t _note;
-} DEFAULT noalloc;
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index 092af3298f0e..f281dff36248 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -750,8 +750,8 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
mixer_setrecsrc(m, 0); /* Set default input. */
- pdev = make_dev(&mixer_cdevsw, SND_DEV_CTL, UID_ROOT, GID_WHEEL, 0666,
- "mixer%d", unit);
+ pdev = make_dev(&mixer_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "mixer%d",
+ unit);
pdev->si_drv1 = m;
snddev->mixer_dev = pdev;
diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
index 509a35c5a038..51d0fb3bb686 100644
--- a/sys/dev/sound/pcm/sndstat.c
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -52,7 +52,6 @@
#define SS_TYPE_PCM 1
#define SS_TYPE_MIDI 2
-#define SS_TYPE_SEQUENCER 3
static d_open_t sndstat_open;
static void sndstat_close(void *);
@@ -1165,8 +1164,6 @@ sndstat_register(device_t dev, char *str)
type = SS_TYPE_PCM;
else if (!strcmp(devtype, "midi"))
type = SS_TYPE_MIDI;
- else if (!strcmp(devtype, "sequencer"))
- type = SS_TYPE_SEQUENCER;
else
return (EINVAL);
@@ -1441,8 +1438,8 @@ static void
sndstat_sysinit(void *p)
{
sx_init(&sndstat_lock, "sndstat lock");
- sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS,
- UID_ROOT, GID_WHEEL, 0644, "sndstat");
+ sndstat_dev = make_dev(&sndstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644,
+ "sndstat");
}
SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL);
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index 315452e294d1..6bd435d0ea25 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -148,14 +148,6 @@ struct snd_mixer;
#define RANGE(var, low, high) (var) = \
(((var)<(low))? (low) : ((var)>(high))? (high) : (var))
-enum {
- SND_DEV_CTL = 0, /* Control port /dev/mixer */
- SND_DEV_SEQ, /* Sequencer /dev/sequencer */
- SND_DEV_MIDIN, /* Raw midi access */
- SND_DEV_DSP, /* Digitized voice /dev/dsp */
- SND_DEV_STATUS, /* /dev/sndstat */
-};
-
#define DSP_DEFAULT_SPEED 8000
extern int snd_unit;
diff --git a/sys/dev/vmm/vmm_dev.c b/sys/dev/vmm/vmm_dev.c
index 1ffa15dd157b..819debadd1ac 100644
--- a/sys/dev/vmm/vmm_dev.c
+++ b/sys/dev/vmm/vmm_dev.c
@@ -351,6 +351,7 @@ static const struct vmmdev_ioctl vmmdev_ioctls[] = {
VMMDEV_IOCTL(VM_ACTIVATE_CPU, VMMDEV_IOCTL_LOCK_ONE_VCPU),
VMMDEV_IOCTL(VM_INJECT_EXCEPTION, VMMDEV_IOCTL_LOCK_ONE_VCPU),
VMMDEV_IOCTL(VM_STATS, VMMDEV_IOCTL_LOCK_ONE_VCPU),
+ VMMDEV_IOCTL(VM_STAT_DESC, 0),
#if defined(__amd64__) && defined(COMPAT_FREEBSD12)
VMMDEV_IOCTL(VM_ALLOC_MEMSEG_12,