summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Zeller <mike@mikezeller.net>2020-03-11 16:55:43 -0400
committerGitHub <noreply@github.com>2020-03-11 16:55:43 -0400
commit4327ac76d19a07f330bc609add63aaa6bb51db89 (patch)
treec901ef8fa7580dcebfb11316f6bb49d498bd40a8
parent1de02da27664d38cedeccf227bd4ae92d32619d9 (diff)
downloadillumos-joyent-4327ac76d19a07f330bc609add63aaa6bb51db89.tar.gz
OS-8117 bhyve upstream sync 2019 Sept (#263)release-20200312
Reviewed by: Dan McDonald <danmcd@kebe.com> Reviewed by: John Levon <john.levon@joyent.com> Reviewed by: Patrick Mooney <pmooney@oxide.computer> Approved by: John Levon <john.levon@joyent.com>
-rw-r--r--usr/src/cmd/bhyve/Makefile10
-rw-r--r--usr/src/cmd/bhyve/audio.c285
-rw-r--r--usr/src/cmd/bhyve/audio.h88
-rw-r--r--usr/src/cmd/bhyve/gdb.c13
-rw-r--r--usr/src/cmd/bhyve/hda_codec.c952
-rw-r--r--usr/src/cmd/bhyve/hda_reg.h1369
-rw-r--r--usr/src/cmd/bhyve/hdac_reg.h271
-rw-r--r--usr/src/cmd/bhyve/mevent.c2
-rw-r--r--usr/src/cmd/bhyve/net_backends.c807
-rw-r--r--usr/src/cmd/bhyve/net_backends.h89
-rw-r--r--usr/src/cmd/bhyve/net_utils.c89
-rw-r--r--usr/src/cmd/bhyve/net_utils.h39
-rw-r--r--usr/src/cmd/bhyve/pci_e82545.c47
-rw-r--r--usr/src/cmd/bhyve/pci_emul.c96
-rw-r--r--usr/src/cmd/bhyve/pci_emul.h1
-rw-r--r--usr/src/cmd/bhyve/pci_fbuf.c17
-rw-r--r--usr/src/cmd/bhyve/pci_hda.c1331
-rw-r--r--usr/src/cmd/bhyve/pci_hda.h92
-rw-r--r--usr/src/cmd/bhyve/pci_nvme.c108
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_console.c9
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_net.c132
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_scsi.c10
-rw-r--r--usr/src/cmd/bhyve/pci_xhci.c28
-rw-r--r--usr/src/cmd/bhyve/rfb.c30
-rw-r--r--usr/src/cmd/bhyve/uart_emul.c7
-rw-r--r--usr/src/cmd/bhyve/virtio.c3
-rw-r--r--usr/src/cmd/bhyve/virtio.h21
-rw-r--r--usr/src/compat/freebsd/net/ieee_oui.h85
-rw-r--r--usr/src/compat/freebsd/sys/param.h2
-rw-r--r--usr/src/compat/freebsd/sys/pcpu.h4
-rw-r--r--usr/src/lib/brand/bhyve/zone/boot.c2
-rw-r--r--usr/src/uts/i86pc/io/vmm/README.sync32
-rw-r--r--usr/src/uts/i86pc/io/vmm/amd/svm.c48
-rw-r--r--usr/src/uts/i86pc/io/vmm/intel/vmx.c46
-rw-r--r--usr/src/uts/i86pc/io/vmm/intel/vmx_msr.c21
-rw-r--r--usr/src/uts/i86pc/io/vmm/intel/vtd.c123
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vatpit.c11
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vlapic.c5
-rw-r--r--usr/src/uts/i86pc/io/vmm/vm/vm_page.h6
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm.c32
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_host.h7
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c95
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_lapic.c14
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_lapic.h2
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_sol_vm.c28
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_util.c20
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_util.h6
-rw-r--r--usr/src/uts/i86pc/sys/vmm.h39
48 files changed, 6161 insertions, 413 deletions
diff --git a/usr/src/cmd/bhyve/Makefile b/usr/src/cmd/bhyve/Makefile
index 3e02ce4a62..f611af835f 100644
--- a/usr/src/cmd/bhyve/Makefile
+++ b/usr/src/cmd/bhyve/Makefile
@@ -44,6 +44,7 @@ SRCS = acpi.c \
mem.c \
mevent.c \
mptbl.c \
+ net_utils.c \
pci_ahci.c \
pci_e82545.c \
pci_emul.c \
@@ -86,6 +87,15 @@ SRCS = acpi.c \
#ctl_scsi_all.c \
#pci_virtio_scsi.c \
+# The audio backend in FreeBSD is different than the one found in audio_oss.h
+ #audio.c \
+ #hda_codec.c \
+ #pci_hda.c \
+
+# The bhyve generic net-backend stuff has been ignored by us at the moment
+# because SmartOS users prefer to use viona for its superior network perf.
+ #net_backends.c \
+
OBJS = $(SRCS:.c=.o)
diff --git a/usr/src/cmd/bhyve/audio.c b/usr/src/cmd/bhyve/audio.c
new file mode 100644
index 0000000000..15e370284e
--- /dev/null
+++ b/usr/src/cmd/bhyve/audio.c
@@ -0,0 +1,285 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
+ * 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 ``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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#include <capsicum_helpers.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <err.h>
+#include <sysexits.h>
+
+#include "audio.h"
+#include "pci_hda.h"
+
+/*
+ * Audio Player internal data structures
+ */
+
+struct audio {
+ int fd;
+ uint8_t dir;
+ uint8_t inited;
+ char dev_name[64];
+};
+
+/*
+ * Audio Player module function definitions
+ */
+
+/*
+ * audio_init - initialize an instance of audio player
+ * @dev_name - the backend sound device used to play / capture
+ * @dir - dir = 1 for write mode, dir = 0 for read mode
+ */
+struct audio *
+audio_init(const char *dev_name, uint8_t dir)
+{
+ struct audio *aud = NULL;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ cap_ioctl_t cmds[] = {
+ SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_CHANNELS,
+ SNDCTL_DSP_SPEED,
+#ifdef DEBUG_HDA
+ SNDCTL_DSP_GETOSPACE, SNDCTL_DSP_GETISPACE,
+#endif
+ };
+#endif
+
+ assert(dev_name);
+
+ aud = calloc(1, sizeof(*aud));
+ if (!aud)
+ return NULL;
+
+ if (strlen(dev_name) < sizeof(aud->dev_name))
+ memcpy(aud->dev_name, dev_name, strlen(dev_name) + 1);
+ else {
+ DPRINTF("dev_name too big\n");
+ free(aud);
+ return NULL;
+ }
+
+ aud->dir = dir;
+
+ aud->fd = open(aud->dev_name, aud->dir ? O_WRONLY : O_RDONLY, 0);
+ if (aud->fd == -1) {
+ DPRINTF("Failed to open dev: %s, errno: %d\n",
+ aud->dev_name, errno);
+ free(aud);
+ return (NULL);
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE);
+ if (caph_rights_limit(aud->fd, &rights) == -1)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (caph_ioctls_limit(aud->fd, cmds, nitems(cmds)) == -1)
+ errx(EX_OSERR, "Unable to limit ioctl rights for sandbox");
+#endif
+
+ return aud;
+}
+
+/*
+ * audio_set_params - reset the sound device and set the audio params
+ * @aud - the audio player to be configured
+ * @params - the audio parameters to be set
+ */
+int
+audio_set_params(struct audio *aud, struct audio_params *params)
+{
+ int audio_fd;
+ int format, channels, rate;
+ int err;
+#if DEBUG_HDA == 1
+ audio_buf_info info;
+#endif
+
+ assert(aud);
+ assert(params);
+
+ if ((audio_fd = aud->fd) < 0) {
+ DPRINTF("Incorrect audio device descriptor for %s\n",
+ aud->dev_name);
+ return (-1);
+ }
+
+ /* Reset the device if it was previously opened */
+ if (aud->inited) {
+ err = ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
+ if (err == -1) {
+ DPRINTF("Failed to reset fd: %d, errno: %d\n",
+ aud->fd, errno);
+ return (-1);
+ }
+ } else
+ aud->inited = 1;
+
+ /* Set the Format (Bits per Sample) */
+ format = params->format;
+ err = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
+ if (err == -1) {
+ DPRINTF("Fail to set fmt: 0x%x errno: %d\n",
+ params->format, errno);
+ return -1;
+ }
+
+ /* The device does not support the requested audio format */
+ if (format != params->format) {
+ DPRINTF("Mismatch format: 0x%x params->format: 0x%x\n",
+ format, params->format);
+ return -1;
+ }
+
+ /* Set the Number of Channels */
+ channels = params->channels;
+ err = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels);
+ if (err == -1) {
+ DPRINTF("Fail to set channels: %d errno: %d\n",
+ params->channels, errno);
+ return -1;
+ }
+
+ /* The device does not support the requested no. of channels */
+ if (channels != params->channels) {
+ DPRINTF("Mismatch channels: %d params->channels: %d\n",
+ channels, params->channels);
+ return -1;
+ }
+
+ /* Set the Sample Rate / Speed */
+ rate = params->rate;
+ err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
+ if (err == -1) {
+ DPRINTF("Fail to set speed: %d errno: %d\n",
+ params->rate, errno);
+ return -1;
+ }
+
+ /* The device does not support the requested rate / speed */
+ if (rate != params->rate) {
+ DPRINTF("Mismatch rate: %d params->rate: %d\n",
+ rate, params->rate);
+ return -1;
+ }
+
+#if DEBUG_HDA == 1
+ err = ioctl(audio_fd, aud->dir ? SNDCTL_DSP_GETOSPACE :
+ SNDCTL_DSP_GETISPACE, &info);
+ if (err == -1) {
+ DPRINTF("Fail to get audio buf info errno: %d\n", errno);
+ return -1;
+ }
+ DPRINTF("fragstotal: 0x%x fragsize: 0x%x\n",
+ info.fragstotal, info.fragsize);
+#endif
+ return 0;
+}
+
+/*
+ * audio_playback - plays samples to the sound device using blocking operations
+ * @aud - the audio player used to play the samples
+ * @buf - the buffer containing the samples
+ * @count - the number of bytes in buffer
+ */
+int
+audio_playback(struct audio *aud, const void *buf, size_t count)
+{
+ int audio_fd = -1;
+ ssize_t len = 0, total = 0;
+
+ assert(aud);
+ assert(aud->dir);
+ assert(buf);
+
+ audio_fd = aud->fd;
+ assert(audio_fd != -1);
+
+ total = 0;
+ while (total < count) {
+ len = write(audio_fd, buf + total, count - total);
+ if (len == -1) {
+ DPRINTF("Fail to write to fd: %d, errno: %d\n",
+ audio_fd, errno);
+ return -1;
+ }
+
+ total += len;
+ }
+
+ return 0;
+}
+
+/*
+ * audio_record - records samples from the sound device using
+ * blocking operations.
+ * @aud - the audio player used to capture the samples
+ * @buf - the buffer to receive the samples
+ * @count - the number of bytes to capture in buffer
+ * Returns -1 on error and 0 on success
+ */
+int
+audio_record(struct audio *aud, void *buf, size_t count)
+{
+ int audio_fd = -1;
+ ssize_t len = 0, total = 0;
+
+ assert(aud);
+ assert(!aud->dir);
+ assert(buf);
+
+ audio_fd = aud->fd;
+ assert(audio_fd != -1);
+
+ total = 0;
+ while (total < count) {
+ len = read(audio_fd, buf + total, count - total);
+ if (len == -1) {
+ DPRINTF("Fail to write to fd: %d, errno: %d\n",
+ audio_fd, errno);
+ return -1;
+ }
+
+ total += len;
+ }
+
+ return 0;
+}
diff --git a/usr/src/cmd/bhyve/audio.h b/usr/src/cmd/bhyve/audio.h
new file mode 100644
index 0000000000..2b559a43e5
--- /dev/null
+++ b/usr/src/cmd/bhyve/audio.h
@@ -0,0 +1,88 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
+ * 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 ``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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AUDIO_EMUL_H_
+#define _AUDIO_EMUL_H_
+
+#include <sys/types.h>
+#include <sys/soundcard.h>
+
+/*
+ * Audio Player data structures
+ */
+
+struct audio;
+
+struct audio_params {
+ int channels;
+ int format;
+ int rate;
+};
+
+/*
+ * Audio Player API
+ */
+
+/*
+ * audio_init - initialize an instance of audio player
+ * @dev_name - the backend sound device used to play / capture
+ * @dir - dir = 1 for write mode, dir = 0 for read mode
+ * Returns NULL on error and the address of the audio player instance
+ */
+struct audio *audio_init(const char *dev_name, uint8_t dir);
+
+/*
+ * audio_set_params - reset the sound device and set the audio params
+ * @aud - the audio player to be configured
+ * @params - the audio parameters to be set
+ * Returns -1 on error and 0 on success
+ */
+int audio_set_params(struct audio *aud, struct audio_params *params);
+
+/*
+ * audio_playback - plays samples to the sound device using blocking operations
+ * @aud - the audio player used to play the samples
+ * @buf - the buffer containing the samples
+ * @count - the number of bytes in buffer
+ * Returns -1 on error and 0 on success
+ */
+int audio_playback(struct audio *aud, const void *buf, size_t count);
+
+/*
+ * audio_record - records samples from the sound device using blocking
+ * operations.
+ * @aud - the audio player used to capture the samples
+ * @buf - the buffer to receive the samples
+ * @count - the number of bytes to capture in buffer
+ * Returns -1 on error and 0 on success
+ */
+int audio_record(struct audio *aud, void *buf, size_t count);
+
+#endif /* _AUDIO_EMUL_H_ */
diff --git a/usr/src/cmd/bhyve/gdb.c b/usr/src/cmd/bhyve/gdb.c
index 71cb780544..06809860c6 100644
--- a/usr/src/cmd/bhyve/gdb.c
+++ b/usr/src/cmd/bhyve/gdb.c
@@ -32,6 +32,11 @@ __FBSDID("$FreeBSD$");
#ifndef WITHOUT_CAPSICUM
#include <sys/capsicum.h>
#endif
+#ifdef __FreeBSD__
+#include <sys/endian.h>
+#else
+#include <endian.h>
+#endif
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
@@ -969,14 +974,10 @@ gdb_write_mem(const uint8_t *data, size_t len)
val = parse_byte(data);
} else if (gpa & 2 || todo == 2) {
bytes = 2;
- val = parse_byte(data) |
- (parse_byte(data + 2) << 8);
+ val = be16toh(parse_integer(data, 4));
} else {
bytes = 4;
- val = parse_byte(data) |
- (parse_byte(data + 2) << 8) |
- (parse_byte(data + 4) << 16) |
- (parse_byte(data + 6) << 24);
+ val = be32toh(parse_integer(data, 8));
}
error = write_mem(ctx, cur_vcpu, gpa, val,
bytes);
diff --git a/usr/src/cmd/bhyve/hda_codec.c b/usr/src/cmd/bhyve/hda_codec.c
new file mode 100644
index 0000000000..82f5fb1eed
--- /dev/null
+++ b/usr/src/cmd/bhyve/hda_codec.c
@@ -0,0 +1,952 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
+ * 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 ``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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <pthread.h>
+#include <pthread_np.h>
+#include <unistd.h>
+
+#include "pci_hda.h"
+#include "audio.h"
+
+/*
+ * HDA Codec defines
+ */
+#define INTEL_VENDORID 0x8086
+
+#define HDA_CODEC_SUBSYSTEM_ID ((INTEL_VENDORID << 16) | 0x01)
+#define HDA_CODEC_ROOT_NID 0x00
+#define HDA_CODEC_FG_NID 0x01
+#define HDA_CODEC_AUDIO_OUTPUT_NID 0x02
+#define HDA_CODEC_PIN_OUTPUT_NID 0x03
+#define HDA_CODEC_AUDIO_INPUT_NID 0x04
+#define HDA_CODEC_PIN_INPUT_NID 0x05
+
+#define HDA_CODEC_STREAMS_COUNT 0x02
+#define HDA_CODEC_STREAM_OUTPUT 0x00
+#define HDA_CODEC_STREAM_INPUT 0x01
+
+#define HDA_CODEC_PARAMS_COUNT 0x14
+#define HDA_CODEC_CONN_LIST_COUNT 0x01
+#define HDA_CODEC_RESPONSE_EX_UNSOL 0x10
+#define HDA_CODEC_RESPONSE_EX_SOL 0x00
+#define HDA_CODEC_AMP_NUMSTEPS 0x4a
+
+#define HDA_CODEC_SUPP_STREAM_FORMATS_PCM \
+ (1 << HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT)
+
+#define HDA_CODEC_FMT_BASE_MASK (0x01 << 14)
+
+#define HDA_CODEC_FMT_MULT_MASK (0x07 << 11)
+#define HDA_CODEC_FMT_MULT_2 (0x01 << 11)
+#define HDA_CODEC_FMT_MULT_3 (0x02 << 11)
+#define HDA_CODEC_FMT_MULT_4 (0x03 << 11)
+
+#define HDA_CODEC_FMT_DIV_MASK 0x07
+#define HDA_CODEC_FMT_DIV_SHIFT 8
+
+#define HDA_CODEC_FMT_BITS_MASK (0x07 << 4)
+#define HDA_CODEC_FMT_BITS_8 (0x00 << 4)
+#define HDA_CODEC_FMT_BITS_16 (0x01 << 4)
+#define HDA_CODEC_FMT_BITS_24 (0x03 << 4)
+#define HDA_CODEC_FMT_BITS_32 (0x04 << 4)
+
+#define HDA_CODEC_FMT_CHAN_MASK (0x0f << 0)
+
+#define HDA_CODEC_AUDIO_WCAP_OUTPUT \
+ (0x00 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_INPUT \
+ (0x01 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_PIN \
+ (0x04 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_CONN_LIST \
+ (1 << HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_FORMAT_OVR \
+ (1 << HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_AMP_OVR \
+ (1 << HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_OUT_AMP \
+ (1 << HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_IN_AMP \
+ (1 << HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT)
+#define HDA_CODEC_AUDIO_WCAP_STEREO \
+ (1 << HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT)
+
+#define HDA_CODEC_PIN_CAP_OUTPUT \
+ (1 << HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT)
+#define HDA_CODEC_PIN_CAP_INPUT \
+ (1 << HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT)
+#define HDA_CODEC_PIN_CAP_PRESENCE_DETECT \
+ (1 << HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT)
+
+#define HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP \
+ (1 << HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT)
+#define HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE \
+ (0x03 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT)
+#define HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS \
+ (HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT)
+#define HDA_CODEC_OUTPUT_AMP_CAP_OFFSET \
+ (HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT)
+
+#define HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE 0x80
+#define HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK 0x7f
+
+#define HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED (1 << 31)
+#define HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE \
+ (1 << HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT)
+#define HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE \
+ (1 << HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT)
+
+#define HDA_CONFIG_DEFAULTCONF_COLOR_BLACK \
+ (0x01 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_COLOR_RED \
+ (0x05 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
+
+#define HDA_CODEC_BUF_SIZE HDA_FIFO_SIZE
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+
+/*
+ * HDA Audio Context data structures
+ */
+
+typedef void (*transfer_func_t)(void *arg);
+typedef int (*setup_func_t)(void *arg);
+
+struct hda_audio_ctxt {
+ char name[64];
+ uint8_t run;
+ uint8_t started;
+ void *priv;
+ pthread_t tid;
+ pthread_mutex_t mtx;
+ pthread_cond_t cond;
+ setup_func_t do_setup;
+ transfer_func_t do_transfer;
+};
+
+/*
+ * HDA Audio Context module function declarations
+ */
+
+static void *hda_audio_ctxt_thr(void *arg);
+static int hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname,
+ transfer_func_t do_transfer, setup_func_t do_setup, void *priv);
+static int hda_audio_ctxt_start(struct hda_audio_ctxt *actx);
+static int hda_audio_ctxt_stop(struct hda_audio_ctxt *actx);
+
+/*
+ * HDA Codec data structures
+ */
+
+struct hda_codec_softc;
+
+typedef uint32_t (*verb_func_t)(struct hda_codec_softc *sc, uint16_t verb,
+ uint16_t payload);
+
+struct hda_codec_stream {
+ uint8_t buf[HDA_CODEC_BUF_SIZE];
+ uint8_t channel;
+ uint16_t fmt;
+ uint8_t stream;
+
+ uint8_t left_gain;
+ uint8_t right_gain;
+ uint8_t left_mute;
+ uint8_t right_mute;
+
+ struct audio *aud;
+ struct hda_audio_ctxt actx;
+};
+
+struct hda_codec_softc {
+ uint32_t no_nodes;
+ uint32_t subsystem_id;
+ const uint32_t (*get_parameters)[HDA_CODEC_PARAMS_COUNT];
+ const uint8_t (*conn_list)[HDA_CODEC_CONN_LIST_COUNT];
+ const uint32_t *conf_default;
+ const uint8_t *pin_ctrl_default;
+ const verb_func_t *verb_handlers;
+
+ struct hda_codec_inst *hci;
+ struct hda_codec_stream streams[HDA_CODEC_STREAMS_COUNT];
+};
+
+/*
+ * HDA Codec module function declarations
+ */
+static int hda_codec_init(struct hda_codec_inst *hci, const char *play,
+ const char *rec, const char *opts);
+static int hda_codec_reset(struct hda_codec_inst *hci);
+static int hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data);
+static int hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
+ uint8_t stream, uint8_t dir);
+
+static int hda_codec_parse_format(uint16_t fmt, struct audio_params *params);
+
+static uint32_t hda_codec_audio_output_nid(struct hda_codec_softc *sc,
+ uint16_t verb, uint16_t payload);
+static void hda_codec_audio_output_do_transfer(void *arg);
+static int hda_codec_audio_output_do_setup(void *arg);
+static uint32_t hda_codec_audio_input_nid(struct hda_codec_softc *sc,
+ uint16_t verb, uint16_t payload);
+static void hda_codec_audio_input_do_transfer(void *arg);
+static int hda_codec_audio_input_do_setup(void *arg);
+
+static uint32_t hda_codec_audio_inout_nid(struct hda_codec_stream *st,
+ uint16_t verb, uint16_t payload);
+
+/*
+ * HDA Codec global data
+ */
+
+#define HDA_CODEC_ROOT_DESC \
+ [HDA_CODEC_ROOT_NID] = { \
+ [HDA_PARAM_VENDOR_ID] = INTEL_VENDORID, \
+ [HDA_PARAM_REVISION_ID] = 0xffff, \
+ /* 1 Subnode, StartNid = 1 */ \
+ [HDA_PARAM_SUB_NODE_COUNT] = 0x00010001, \
+ }, \
+
+#define HDA_CODEC_FG_COMMON_DESC \
+ [HDA_PARAM_FCT_GRP_TYPE] = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO,\
+ /* B8 - B32, 8.0 - 192.0kHz */ \
+ [HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x1f << 16) | 0x7ff, \
+ [HDA_PARAM_SUPP_STREAM_FORMATS] = HDA_CODEC_SUPP_STREAM_FORMATS_PCM,\
+ [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \
+ [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \
+ [HDA_PARAM_GPIO_COUNT] = 0x00, \
+
+#define HDA_CODEC_FG_OUTPUT_DESC \
+ [HDA_CODEC_FG_NID] = { \
+ /* 2 Subnodes, StartNid = 2 */ \
+ [HDA_PARAM_SUB_NODE_COUNT] = 0x00020002, \
+ HDA_CODEC_FG_COMMON_DESC \
+ }, \
+
+#define HDA_CODEC_FG_INPUT_DESC \
+ [HDA_CODEC_FG_NID] = { \
+ /* 2 Subnodes, StartNid = 4 */ \
+ [HDA_PARAM_SUB_NODE_COUNT] = 0x00040002, \
+ HDA_CODEC_FG_COMMON_DESC \
+ }, \
+
+#define HDA_CODEC_FG_DUPLEX_DESC \
+ [HDA_CODEC_FG_NID] = { \
+ /* 4 Subnodes, StartNid = 2 */ \
+ [HDA_PARAM_SUB_NODE_COUNT] = 0x00020004, \
+ HDA_CODEC_FG_COMMON_DESC \
+ }, \
+
+#define HDA_CODEC_OUTPUT_DESC \
+ [HDA_CODEC_AUDIO_OUTPUT_NID] = { \
+ [HDA_PARAM_AUDIO_WIDGET_CAP] = \
+ HDA_CODEC_AUDIO_WCAP_OUTPUT | \
+ HDA_CODEC_AUDIO_WCAP_FORMAT_OVR | \
+ HDA_CODEC_AUDIO_WCAP_AMP_OVR | \
+ HDA_CODEC_AUDIO_WCAP_OUT_AMP | \
+ HDA_CODEC_AUDIO_WCAP_STEREO, \
+ /* B16, 16.0 - 192.0kHz */ \
+ [HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc, \
+ [HDA_PARAM_SUPP_STREAM_FORMATS] = \
+ HDA_CODEC_SUPP_STREAM_FORMATS_PCM, \
+ [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \
+ [HDA_PARAM_CONN_LIST_LENGTH] = 0x00, \
+ [HDA_PARAM_OUTPUT_AMP_CAP] = \
+ HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP | \
+ HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE | \
+ HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS | \
+ HDA_CODEC_OUTPUT_AMP_CAP_OFFSET, \
+ }, \
+ [HDA_CODEC_PIN_OUTPUT_NID] = { \
+ [HDA_PARAM_AUDIO_WIDGET_CAP] = \
+ HDA_CODEC_AUDIO_WCAP_PIN | \
+ HDA_CODEC_AUDIO_WCAP_CONN_LIST | \
+ HDA_CODEC_AUDIO_WCAP_STEREO, \
+ [HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_OUTPUT | \
+ HDA_CODEC_PIN_CAP_PRESENCE_DETECT,\
+ [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \
+ [HDA_PARAM_CONN_LIST_LENGTH] = 0x01, \
+ [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \
+ }, \
+
+#define HDA_CODEC_INPUT_DESC \
+ [HDA_CODEC_AUDIO_INPUT_NID] = { \
+ [HDA_PARAM_AUDIO_WIDGET_CAP] = \
+ HDA_CODEC_AUDIO_WCAP_INPUT | \
+ HDA_CODEC_AUDIO_WCAP_CONN_LIST | \
+ HDA_CODEC_AUDIO_WCAP_FORMAT_OVR | \
+ HDA_CODEC_AUDIO_WCAP_AMP_OVR | \
+ HDA_CODEC_AUDIO_WCAP_IN_AMP | \
+ HDA_CODEC_AUDIO_WCAP_STEREO, \
+ /* B16, 16.0 - 192.0kHz */ \
+ [HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc, \
+ [HDA_PARAM_SUPP_STREAM_FORMATS] = \
+ HDA_CODEC_SUPP_STREAM_FORMATS_PCM, \
+ [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \
+ [HDA_PARAM_CONN_LIST_LENGTH] = 0x01, \
+ [HDA_PARAM_INPUT_AMP_CAP] = \
+ HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP | \
+ HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE | \
+ HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS | \
+ HDA_CODEC_OUTPUT_AMP_CAP_OFFSET, \
+ }, \
+ [HDA_CODEC_PIN_INPUT_NID] = { \
+ [HDA_PARAM_AUDIO_WIDGET_CAP] = \
+ HDA_CODEC_AUDIO_WCAP_PIN | \
+ HDA_CODEC_AUDIO_WCAP_STEREO, \
+ [HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_INPUT | \
+ HDA_CODEC_PIN_CAP_PRESENCE_DETECT, \
+ [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \
+ [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \
+ }, \
+
+static const uint32_t
+hda_codec_output_parameters[][HDA_CODEC_PARAMS_COUNT] = {
+ HDA_CODEC_ROOT_DESC
+ HDA_CODEC_FG_OUTPUT_DESC
+ HDA_CODEC_OUTPUT_DESC
+};
+
+static const uint32_t
+hda_codec_input_parameters[][HDA_CODEC_PARAMS_COUNT] = {
+ HDA_CODEC_ROOT_DESC
+ HDA_CODEC_FG_INPUT_DESC
+ HDA_CODEC_INPUT_DESC
+};
+
+static const uint32_t
+hda_codec_duplex_parameters[][HDA_CODEC_PARAMS_COUNT] = {
+ HDA_CODEC_ROOT_DESC
+ HDA_CODEC_FG_DUPLEX_DESC
+ HDA_CODEC_OUTPUT_DESC
+ HDA_CODEC_INPUT_DESC
+};
+
+#define HDA_CODEC_NODES_COUNT (ARRAY_SIZE(hda_codec_duplex_parameters))
+
+static const uint8_t
+hda_codec_conn_list[HDA_CODEC_NODES_COUNT][HDA_CODEC_CONN_LIST_COUNT] = {
+ [HDA_CODEC_PIN_OUTPUT_NID] = {HDA_CODEC_AUDIO_OUTPUT_NID},
+ [HDA_CODEC_AUDIO_INPUT_NID] = {HDA_CODEC_PIN_INPUT_NID},
+};
+
+static const uint32_t
+hda_codec_conf_default[HDA_CODEC_NODES_COUNT] = {
+ [HDA_CODEC_PIN_OUTPUT_NID] = \
+ HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK |
+ HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT |
+ HDA_CONFIG_DEFAULTCONF_COLOR_BLACK |
+ (0x01 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT),
+ [HDA_CODEC_PIN_INPUT_NID] = HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK |
+ HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN |
+ HDA_CONFIG_DEFAULTCONF_COLOR_RED |
+ (0x02 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT),
+};
+
+static const uint8_t
+hda_codec_pin_ctrl_default[HDA_CODEC_NODES_COUNT] = {
+ [HDA_CODEC_PIN_OUTPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE,
+ [HDA_CODEC_PIN_INPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE,
+};
+
+static const
+verb_func_t hda_codec_verb_handlers[HDA_CODEC_NODES_COUNT] = {
+ [HDA_CODEC_AUDIO_OUTPUT_NID] = hda_codec_audio_output_nid,
+ [HDA_CODEC_AUDIO_INPUT_NID] = hda_codec_audio_input_nid,
+};
+
+/*
+ * HDA Codec module function definitions
+ */
+
+static int
+hda_codec_init(struct hda_codec_inst *hci, const char *play,
+ const char *rec, const char *opts)
+{
+ struct hda_codec_softc *sc = NULL;
+ struct hda_codec_stream *st = NULL;
+ int err;
+
+ if (!(play || rec))
+ return (-1);
+
+ DPRINTF("cad: 0x%x opts: %s\n", hci->cad, opts);
+
+ sc = calloc(1, sizeof(*sc));
+ if (!sc)
+ return (-1);
+
+ if (play && rec)
+ sc->get_parameters = hda_codec_duplex_parameters;
+ else {
+ if (play)
+ sc->get_parameters = hda_codec_output_parameters;
+ else
+ sc->get_parameters = hda_codec_input_parameters;
+ }
+ sc->subsystem_id = HDA_CODEC_SUBSYSTEM_ID;
+ sc->no_nodes = HDA_CODEC_NODES_COUNT;
+ sc->conn_list = hda_codec_conn_list;
+ sc->conf_default = hda_codec_conf_default;
+ sc->pin_ctrl_default = hda_codec_pin_ctrl_default;
+ sc->verb_handlers = hda_codec_verb_handlers;
+ DPRINTF("HDA Codec nodes: %d\n", sc->no_nodes);
+
+ /*
+ * Initialize the Audio Output stream
+ */
+ if (play) {
+ st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
+
+ err = hda_audio_ctxt_init(&st->actx, "hda-audio-output",
+ hda_codec_audio_output_do_transfer,
+ hda_codec_audio_output_do_setup, sc);
+ assert(!err);
+
+ st->aud = audio_init(play, 1);
+ if (!st->aud) {
+ DPRINTF("Fail to init the output audio player\n");
+ return (-1);
+ }
+ }
+
+ /*
+ * Initialize the Audio Input stream
+ */
+ if (rec) {
+ st = &sc->streams[HDA_CODEC_STREAM_INPUT];
+
+ err = hda_audio_ctxt_init(&st->actx, "hda-audio-input",
+ hda_codec_audio_input_do_transfer,
+ hda_codec_audio_input_do_setup, sc);
+ assert(!err);
+
+ st->aud = audio_init(rec, 0);
+ if (!st->aud) {
+ DPRINTF("Fail to init the input audio player\n");
+ return (-1);
+ }
+ }
+
+ sc->hci = hci;
+ hci->priv = sc;
+
+ return (0);
+}
+
+static int
+hda_codec_reset(struct hda_codec_inst *hci)
+{
+ struct hda_ops *hops = NULL;
+ struct hda_codec_softc *sc = NULL;
+ struct hda_codec_stream *st = NULL;
+ int i;
+
+ assert(hci);
+
+ hops = hci->hops;
+ assert(hops);
+
+ sc = (struct hda_codec_softc *)hci->priv;
+ assert(sc);
+
+ for (i = 0; i < HDA_CODEC_STREAMS_COUNT; i++) {
+ st = &sc->streams[i];
+ st->left_gain = HDA_CODEC_AMP_NUMSTEPS;
+ st->right_gain = HDA_CODEC_AMP_NUMSTEPS;
+ st->left_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
+ st->right_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
+ }
+
+ DPRINTF("cad: 0x%x\n", hci->cad);
+
+ if (!hops->signal) {
+ DPRINTF("The controller ops does not implement \
+ the signal function\n");
+ return (-1);
+ }
+
+ return (hops->signal(hci));
+}
+
+static int
+hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data)
+{
+ struct hda_codec_softc *sc = NULL;
+ struct hda_ops *hops = NULL;
+ uint8_t cad = 0, nid = 0;
+ uint16_t verb = 0, payload = 0;
+ uint32_t res = 0;
+
+ /* 4 bits */
+ cad = (cmd_data >> HDA_CMD_CAD_SHIFT) & 0x0f;
+ /* 8 bits */
+ nid = (cmd_data >> HDA_CMD_NID_SHIFT) & 0xff;
+
+ if ((cmd_data & 0x70000) == 0x70000) {
+ /* 12 bits */
+ verb = (cmd_data >> HDA_CMD_VERB_12BIT_SHIFT) & 0x0fff;
+ /* 8 bits */
+ payload = cmd_data & 0xff;
+ } else {
+ /* 4 bits */
+ verb = (cmd_data >> HDA_CMD_VERB_4BIT_SHIFT) & 0x0f;
+ /* 16 bits */
+ payload = cmd_data & 0xffff;
+ }
+
+ assert(cad == hci->cad);
+ assert(hci);
+
+ hops = hci->hops;
+ assert(hops);
+
+ sc = (struct hda_codec_softc *)hci->priv;
+ assert(sc);
+
+ assert(nid < sc->no_nodes);
+
+ if (!hops->response) {
+ DPRINTF("The controller ops does not implement \
+ the response function\n");
+ return (-1);
+ }
+
+ switch (verb) {
+ case HDA_CMD_VERB_GET_PARAMETER:
+ res = sc->get_parameters[nid][payload];
+ break;
+ case HDA_CMD_VERB_GET_CONN_LIST_ENTRY:
+ res = sc->conn_list[nid][0];
+ break;
+ case HDA_CMD_VERB_GET_PIN_WIDGET_CTRL:
+ res = sc->pin_ctrl_default[nid];
+ break;
+ case HDA_CMD_VERB_GET_PIN_SENSE:
+ res = HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED;
+ break;
+ case HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT:
+ res = sc->conf_default[nid];
+ break;
+ case HDA_CMD_VERB_GET_SUBSYSTEM_ID:
+ res = sc->subsystem_id;
+ break;
+ default:
+ assert(sc->verb_handlers);
+ if (sc->verb_handlers[nid])
+ res = sc->verb_handlers[nid](sc, verb, payload);
+ else
+ DPRINTF("Unknown VERB: 0x%x\n", verb);
+ break;
+ }
+
+ DPRINTF("cad: 0x%x nid: 0x%x verb: 0x%x payload: 0x%x response: 0x%x\n",
+ cad, nid, verb, payload, res);
+
+ return (hops->response(hci, res, HDA_CODEC_RESPONSE_EX_SOL));
+}
+
+static int
+hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
+ uint8_t stream, uint8_t dir)
+{
+ struct hda_codec_softc *sc = NULL;
+ struct hda_codec_stream *st = NULL;
+ struct hda_audio_ctxt *actx = NULL;
+ int i;
+ int err;
+
+ assert(hci);
+ assert(stream);
+
+ sc = (struct hda_codec_softc *)hci->priv;
+ assert(sc);
+
+ i = dir ? HDA_CODEC_STREAM_OUTPUT : HDA_CODEC_STREAM_INPUT;
+ st = &sc->streams[i];
+
+ DPRINTF("run: %d, stream: 0x%x, st->stream: 0x%x dir: %d\n",
+ run, stream, st->stream, dir);
+
+ if (stream != st->stream) {
+ DPRINTF("Stream not found\n");
+ return (0);
+ }
+
+ actx = &st->actx;
+
+ if (run)
+ err = hda_audio_ctxt_start(actx);
+ else
+ err = hda_audio_ctxt_stop(actx);
+
+ return (err);
+}
+
+static int
+hda_codec_parse_format(uint16_t fmt, struct audio_params *params)
+{
+ uint8_t div = 0;
+
+ assert(params);
+
+ /* Compute the Sample Rate */
+ params->rate = (fmt & HDA_CODEC_FMT_BASE_MASK) ? 44100 : 48000;
+
+ switch (fmt & HDA_CODEC_FMT_MULT_MASK) {
+ case HDA_CODEC_FMT_MULT_2:
+ params->rate *= 2;
+ break;
+ case HDA_CODEC_FMT_MULT_3:
+ params->rate *= 3;
+ break;
+ case HDA_CODEC_FMT_MULT_4:
+ params->rate *= 4;
+ break;
+ }
+
+ div = (fmt >> HDA_CODEC_FMT_DIV_SHIFT) & HDA_CODEC_FMT_DIV_MASK;
+ params->rate /= (div + 1);
+
+ /* Compute the Bits per Sample */
+ switch (fmt & HDA_CODEC_FMT_BITS_MASK) {
+ case HDA_CODEC_FMT_BITS_8:
+ params->format = AFMT_U8;
+ break;
+ case HDA_CODEC_FMT_BITS_16:
+ params->format = AFMT_S16_LE;
+ break;
+ case HDA_CODEC_FMT_BITS_24:
+ params->format = AFMT_S24_LE;
+ break;
+ case HDA_CODEC_FMT_BITS_32:
+ params->format = AFMT_S32_LE;
+ break;
+ default:
+ DPRINTF("Unknown format bits: 0x%x\n",
+ fmt & HDA_CODEC_FMT_BITS_MASK);
+ return (-1);
+ }
+
+ /* Compute the Number of Channels */
+ params->channels = (fmt & HDA_CODEC_FMT_CHAN_MASK) + 1;
+
+ return (0);
+}
+
+static uint32_t
+hda_codec_audio_output_nid(struct hda_codec_softc *sc, uint16_t verb,
+ uint16_t payload)
+{
+ struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
+ int res;
+
+ res = hda_codec_audio_inout_nid(st, verb, payload);
+
+ return (res);
+}
+
+static void
+hda_codec_audio_output_do_transfer(void *arg)
+{
+ struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
+ struct hda_codec_inst *hci = NULL;
+ struct hda_ops *hops = NULL;
+ struct hda_codec_stream *st = NULL;
+ struct audio *aud = NULL;
+ int err;
+
+ hci = sc->hci;
+ assert(hci);
+
+ hops = hci->hops;
+ assert(hops);
+
+ st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
+ aud = st->aud;
+
+ err = hops->transfer(hci, st->stream, 1, st->buf, sizeof(st->buf));
+ if (err)
+ return;
+
+ err = audio_playback(aud, st->buf, sizeof(st->buf));
+ assert(!err);
+}
+
+static int
+hda_codec_audio_output_do_setup(void *arg)
+{
+ struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
+ struct hda_codec_stream *st = NULL;
+ struct audio *aud = NULL;
+ struct audio_params params;
+ int err;
+
+ st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
+ aud = st->aud;
+
+ err = hda_codec_parse_format(st->fmt, &params);
+ if (err)
+ return (-1);
+
+ DPRINTF("rate: %d, channels: %d, format: 0x%x\n",
+ params.rate, params.channels, params.format);
+
+ return (audio_set_params(aud, &params));
+}
+
+static uint32_t
+hda_codec_audio_input_nid(struct hda_codec_softc *sc, uint16_t verb,
+ uint16_t payload)
+{
+ struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_INPUT];
+ int res;
+
+ res = hda_codec_audio_inout_nid(st, verb, payload);
+
+ return (res);
+}
+
+static void
+hda_codec_audio_input_do_transfer(void *arg)
+{
+ struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
+ struct hda_codec_inst *hci = NULL;
+ struct hda_ops *hops = NULL;
+ struct hda_codec_stream *st = NULL;
+ struct audio *aud = NULL;
+ int err;
+
+ hci = sc->hci;
+ assert(hci);
+
+ hops = hci->hops;
+ assert(hops);
+
+ st = &sc->streams[HDA_CODEC_STREAM_INPUT];
+ aud = st->aud;
+
+ err = audio_record(aud, st->buf, sizeof(st->buf));
+ assert(!err);
+
+ hops->transfer(hci, st->stream, 0, st->buf, sizeof(st->buf));
+}
+
+static int
+hda_codec_audio_input_do_setup(void *arg)
+{
+ struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
+ struct hda_codec_stream *st = NULL;
+ struct audio *aud = NULL;
+ struct audio_params params;
+ int err;
+
+ st = &sc->streams[HDA_CODEC_STREAM_INPUT];
+ aud = st->aud;
+
+ err = hda_codec_parse_format(st->fmt, &params);
+ if (err)
+ return (-1);
+
+ DPRINTF("rate: %d, channels: %d, format: 0x%x\n",
+ params.rate, params.channels, params.format);
+
+ return (audio_set_params(aud, &params));
+}
+
+static uint32_t
+hda_codec_audio_inout_nid(struct hda_codec_stream *st, uint16_t verb,
+ uint16_t payload)
+{
+ uint32_t res = 0;
+ uint8_t mute = 0;
+ uint8_t gain = 0;
+
+ DPRINTF("%s verb: 0x%x, payload, 0x%x\n", st->actx.name, verb, payload);
+
+ switch (verb) {
+ case HDA_CMD_VERB_GET_CONV_FMT:
+ res = st->fmt;
+ break;
+ case HDA_CMD_VERB_SET_CONV_FMT:
+ st->fmt = payload;
+ break;
+ case HDA_CMD_VERB_GET_AMP_GAIN_MUTE:
+ if (payload & HDA_CMD_GET_AMP_GAIN_MUTE_LEFT) {
+ res = st->left_gain | st->left_mute;
+ DPRINTF("GET_AMP_GAIN_MUTE_LEFT: 0x%x\n", res);
+ } else {
+ res = st->right_gain | st->right_mute;
+ DPRINTF("GET_AMP_GAIN_MUTE_RIGHT: 0x%x\n", res);
+ }
+ break;
+ case HDA_CMD_VERB_SET_AMP_GAIN_MUTE:
+ mute = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
+ gain = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK;
+
+ if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_LEFT) {
+ st->left_mute = mute;
+ st->left_gain = gain;
+ DPRINTF("SET_AMP_GAIN_MUTE_LEFT: \
+ mute: 0x%x gain: 0x%x\n", mute, gain);
+ }
+
+ if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT) {
+ st->right_mute = mute;
+ st->right_gain = gain;
+ DPRINTF("SET_AMP_GAIN_MUTE_RIGHT: \
+ mute: 0x%x gain: 0x%x\n", mute, gain);
+ }
+ break;
+ case HDA_CMD_VERB_GET_CONV_STREAM_CHAN:
+ res = (st->stream << 4) | st->channel;
+ break;
+ case HDA_CMD_VERB_SET_CONV_STREAM_CHAN:
+ st->channel = payload & 0x0f;
+ st->stream = (payload >> 4) & 0x0f;
+ DPRINTF("st->channel: 0x%x st->stream: 0x%x\n",
+ st->channel, st->stream);
+ if (!st->stream)
+ hda_audio_ctxt_stop(&st->actx);
+ break;
+ default:
+ DPRINTF("Unknown VERB: 0x%x\n", verb);
+ break;
+ }
+
+ return (res);
+}
+
+struct hda_codec_class hda_codec = {
+ .name = "hda_codec",
+ .init = hda_codec_init,
+ .reset = hda_codec_reset,
+ .command = hda_codec_command,
+ .notify = hda_codec_notify,
+};
+
+HDA_EMUL_SET(hda_codec);
+
+
+/*
+ * HDA Audio Context module function definitions
+ */
+
+static void *
+hda_audio_ctxt_thr(void *arg)
+{
+ struct hda_audio_ctxt *actx = arg;
+
+ DPRINTF("Start Thread: %s\n", actx->name);
+
+ pthread_mutex_lock(&actx->mtx);
+ while (1) {
+ while (!actx->run)
+ pthread_cond_wait(&actx->cond, &actx->mtx);
+
+ actx->do_transfer(actx->priv);
+ }
+ pthread_mutex_unlock(&actx->mtx);
+
+ pthread_exit(NULL);
+ return (NULL);
+}
+
+static int
+hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname,
+ transfer_func_t do_transfer, setup_func_t do_setup, void *priv)
+{
+ int err;
+
+ assert(actx);
+ assert(tname);
+ assert(do_transfer);
+ assert(do_setup);
+ assert(priv);
+
+ memset(actx, 0, sizeof(*actx));
+
+ actx->run = 0;
+ actx->do_transfer = do_transfer;
+ actx->do_setup = do_setup;
+ actx->priv = priv;
+ if (strlen(tname) < sizeof(actx->name))
+ memcpy(actx->name, tname, strlen(tname) + 1);
+ else
+ strcpy(actx->name, "unknown");
+
+ err = pthread_mutex_init(&actx->mtx, NULL);
+ assert(!err);
+
+ err = pthread_cond_init(&actx->cond, NULL);
+ assert(!err);
+
+ err = pthread_create(&actx->tid, NULL, hda_audio_ctxt_thr, actx);
+ assert(!err);
+
+ pthread_set_name_np(actx->tid, tname);
+
+ actx->started = 1;
+
+ return (0);
+}
+
+static int
+hda_audio_ctxt_start(struct hda_audio_ctxt *actx)
+{
+ int err = 0;
+
+ assert(actx);
+ assert(actx->started);
+
+ /* The stream is supposed to be stopped */
+ if (actx->run)
+ return (-1);
+
+ pthread_mutex_lock(&actx->mtx);
+ err = (* actx->do_setup)(actx->priv);
+ if (!err) {
+ actx->run = 1;
+ pthread_cond_signal(&actx->cond);
+ }
+ pthread_mutex_unlock(&actx->mtx);
+
+ return (err);
+}
+
+static int
+hda_audio_ctxt_stop(struct hda_audio_ctxt *actx)
+{
+ actx->run = 0;
+ return (0);
+}
diff --git a/usr/src/cmd/bhyve/hda_reg.h b/usr/src/cmd/bhyve/hda_reg.h
new file mode 100644
index 0000000000..b3034bf9f4
--- /dev/null
+++ b/usr/src/cmd/bhyve/hda_reg.h
@@ -0,0 +1,1369 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _HDA_REG_H_
+#define _HDA_REG_H_
+
+/****************************************************************************
+ * HDA Device Verbs
+ ****************************************************************************/
+
+/* HDA Command */
+#define HDA_CMD_VERB_MASK 0x000fffff
+#define HDA_CMD_VERB_SHIFT 0
+#define HDA_CMD_NID_MASK 0x0ff00000
+#define HDA_CMD_NID_SHIFT 20
+#define HDA_CMD_CAD_MASK 0xf0000000
+#define HDA_CMD_CAD_SHIFT 28
+
+#define HDA_CMD_VERB_4BIT_SHIFT 16
+#define HDA_CMD_VERB_12BIT_SHIFT 8
+
+#define HDA_CMD_VERB_4BIT(verb, payload) \
+ (((verb) << HDA_CMD_VERB_4BIT_SHIFT) | (payload))
+#define HDA_CMD_4BIT(cad, nid, verb, payload) \
+ (((cad) << HDA_CMD_CAD_SHIFT) | \
+ ((nid) << HDA_CMD_NID_SHIFT) | \
+ (HDA_CMD_VERB_4BIT((verb), (payload))))
+
+#define HDA_CMD_VERB_12BIT(verb, payload) \
+ (((verb) << HDA_CMD_VERB_12BIT_SHIFT) | (payload))
+#define HDA_CMD_12BIT(cad, nid, verb, payload) \
+ (((cad) << HDA_CMD_CAD_SHIFT) | \
+ ((nid) << HDA_CMD_NID_SHIFT) | \
+ (HDA_CMD_VERB_12BIT((verb), (payload))))
+
+/* Get Parameter */
+#define HDA_CMD_VERB_GET_PARAMETER 0xf00
+
+#define HDA_CMD_GET_PARAMETER(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_PARAMETER, (payload)))
+
+/* Connection Select Control */
+#define HDA_CMD_VERB_GET_CONN_SELECT_CONTROL 0xf01
+#define HDA_CMD_VERB_SET_CONN_SELECT_CONTROL 0x701
+
+#define HDA_CMD_GET_CONN_SELECT_CONTROL(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_CONN_SELECT_CONTROL, 0x0))
+#define HDA_CMD_SET_CONNECTION_SELECT_CONTROL(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONN_SELECT_CONTROL, (payload)))
+
+/* Connection List Entry */
+#define HDA_CMD_VERB_GET_CONN_LIST_ENTRY 0xf02
+
+#define HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_CONN_LIST_ENTRY, (payload)))
+
+#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_SHORT 1
+#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_LONG 2
+
+/* Processing State */
+#define HDA_CMD_VERB_GET_PROCESSING_STATE 0xf03
+#define HDA_CMD_VERB_SET_PROCESSING_STATE 0x703
+
+#define HDA_CMD_GET_PROCESSING_STATE(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_PROCESSING_STATE, 0x0))
+#define HDA_CMD_SET_PROCESSING_STATE(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_PROCESSING_STATE, (payload)))
+
+#define HDA_CMD_GET_PROCESSING_STATE_STATE_OFF 0x00
+#define HDA_CMD_GET_PROCESSING_STATE_STATE_ON 0x01
+#define HDA_CMD_GET_PROCESSING_STATE_STATE_BENIGN 0x02
+
+/* Coefficient Index */
+#define HDA_CMD_VERB_GET_COEFF_INDEX 0xd
+#define HDA_CMD_VERB_SET_COEFF_INDEX 0x5
+
+#define HDA_CMD_GET_COEFF_INDEX(cad, nid) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_COEFF_INDEX, 0x0))
+#define HDA_CMD_SET_COEFF_INDEX(cad, nid, payload) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_COEFF_INDEX, (payload)))
+
+/* Processing Coefficient */
+#define HDA_CMD_VERB_GET_PROCESSING_COEFF 0xc
+#define HDA_CMD_VERB_SET_PROCESSING_COEFF 0x4
+
+#define HDA_CMD_GET_PROCESSING_COEFF(cad, nid) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_PROCESSING_COEFF, 0x0))
+#define HDA_CMD_SET_PROCESSING_COEFF(cad, nid, payload) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_PROCESSING_COEFF, (payload)))
+
+/* Amplifier Gain/Mute */
+#define HDA_CMD_VERB_GET_AMP_GAIN_MUTE 0xb
+#define HDA_CMD_VERB_SET_AMP_GAIN_MUTE 0x3
+
+#define HDA_CMD_GET_AMP_GAIN_MUTE(cad, nid, payload) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_AMP_GAIN_MUTE, (payload)))
+#define HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, payload) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_AMP_GAIN_MUTE, (payload)))
+
+#define HDA_CMD_GET_AMP_GAIN_MUTE_INPUT 0x0000
+#define HDA_CMD_GET_AMP_GAIN_MUTE_OUTPUT 0x8000
+#define HDA_CMD_GET_AMP_GAIN_MUTE_RIGHT 0x0000
+#define HDA_CMD_GET_AMP_GAIN_MUTE_LEFT 0x2000
+
+#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK 0x00000008
+#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT 7
+#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK 0x00000007
+#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT 0
+
+#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE(rsp) \
+ (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK) >> \
+ HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT)
+#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN(rsp) \
+ (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK) >> \
+ HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT)
+
+#define HDA_CMD_SET_AMP_GAIN_MUTE_OUTPUT 0x8000
+#define HDA_CMD_SET_AMP_GAIN_MUTE_INPUT 0x4000
+#define HDA_CMD_SET_AMP_GAIN_MUTE_LEFT 0x2000
+#define HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT 0x1000
+#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK 0x0f00
+#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT 8
+#define HDA_CMD_SET_AMP_GAIN_MUTE_MUTE 0x0080
+#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK 0x0007
+#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT 0
+
+#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX(index) \
+ (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT) & \
+ HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK)
+#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN(index) \
+ (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT) & \
+ HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK)
+
+/* Converter format */
+#define HDA_CMD_VERB_GET_CONV_FMT 0xa
+#define HDA_CMD_VERB_SET_CONV_FMT 0x2
+
+#define HDA_CMD_GET_CONV_FMT(cad, nid) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_CONV_FMT, 0x0))
+#define HDA_CMD_SET_CONV_FMT(cad, nid, payload) \
+ (HDA_CMD_4BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONV_FMT, (payload)))
+
+/* Digital Converter Control */
+#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT1 0xf0d
+#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT2 0xf0e
+#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1 0x70d
+#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2 0x70e
+
+#define HDA_CMD_GET_DIGITAL_CONV_FMT(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_DIGITAL_CONV_FMT1, 0x0))
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1, (payload)))
+#define HDA_CMD_SET_DIGITAL_CONV_FMT2(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2, (payload)))
+
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK 0x7f00
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT 8
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK 0x0080
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT 7
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK 0x0040
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT 6
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK 0x0020
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT 5
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK 0x0010
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT 4
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK 0x0008
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT 3
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK 0x0004
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT 2
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK 0x0002
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT 1
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK 0x0001
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT 0
+
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_L(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_V(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT)
+#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN(rsp) \
+ (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK) >> \
+ HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT)
+
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_L 0x80
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRO 0x40
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO 0x20
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_COPY 0x10
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRE 0x08
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_VCFG 0x04
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_V 0x02
+#define HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN 0x01
+
+/* Power State */
+#define HDA_CMD_VERB_GET_POWER_STATE 0xf05
+#define HDA_CMD_VERB_SET_POWER_STATE 0x705
+
+#define HDA_CMD_GET_POWER_STATE(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_POWER_STATE, 0x0))
+#define HDA_CMD_SET_POWER_STATE(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_POWER_STATE, (payload)))
+
+#define HDA_CMD_POWER_STATE_D0 0x00
+#define HDA_CMD_POWER_STATE_D1 0x01
+#define HDA_CMD_POWER_STATE_D2 0x02
+#define HDA_CMD_POWER_STATE_D3 0x03
+
+#define HDA_CMD_POWER_STATE_ACT_MASK 0x000000f0
+#define HDA_CMD_POWER_STATE_ACT_SHIFT 4
+#define HDA_CMD_POWER_STATE_SET_MASK 0x0000000f
+#define HDA_CMD_POWER_STATE_SET_SHIFT 0
+
+#define HDA_CMD_GET_POWER_STATE_ACT(rsp) \
+ (((rsp) & HDA_CMD_POWER_STATE_ACT_MASK) >> \
+ HDA_CMD_POWER_STATE_ACT_SHIFT)
+#define HDA_CMD_GET_POWER_STATE_SET(rsp) \
+ (((rsp) & HDA_CMD_POWER_STATE_SET_MASK) >> \
+ HDA_CMD_POWER_STATE_SET_SHIFT)
+
+#define HDA_CMD_SET_POWER_STATE_ACT(ps) \
+ (((ps) << HDA_CMD_POWER_STATE_ACT_SHIFT) & \
+ HDA_CMD_POWER_STATE_ACT_MASK)
+#define HDA_CMD_SET_POWER_STATE_SET(ps) \
+ (((ps) << HDA_CMD_POWER_STATE_SET_SHIFT) & \
+ HDA_CMD_POWER_STATE_ACT_MASK)
+
+/* Converter Stream, Channel */
+#define HDA_CMD_VERB_GET_CONV_STREAM_CHAN 0xf06
+#define HDA_CMD_VERB_SET_CONV_STREAM_CHAN 0x706
+
+#define HDA_CMD_GET_CONV_STREAM_CHAN(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_CONV_STREAM_CHAN, 0x0))
+#define HDA_CMD_SET_CONV_STREAM_CHAN(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONV_STREAM_CHAN, (payload)))
+
+#define HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK 0x000000f0
+#define HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT 4
+#define HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK 0x0000000f
+#define HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT 0
+
+#define HDA_CMD_GET_CONV_STREAM_CHAN_STREAM(rsp) \
+ (((rsp) & HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) >> \
+ HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT)
+#define HDA_CMD_GET_CONV_STREAM_CHAN_CHAN(rsp) \
+ (((rsp) & HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) >> \
+ HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT)
+
+#define HDA_CMD_SET_CONV_STREAM_CHAN_STREAM(param) \
+ (((param) << HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) & \
+ HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK)
+#define HDA_CMD_SET_CONV_STREAM_CHAN_CHAN(param) \
+ (((param) << HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) & \
+ HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK)
+
+/* Input Converter SDI Select */
+#define HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT 0xf04
+#define HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT 0x704
+
+#define HDA_CMD_GET_INPUT_CONVERTER_SDI_SELECT(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT, 0x0))
+#define HDA_CMD_SET_INPUT_CONVERTER_SDI_SELECT(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT, (payload)))
+
+/* Pin Widget Control */
+#define HDA_CMD_VERB_GET_PIN_WIDGET_CTRL 0xf07
+#define HDA_CMD_VERB_SET_PIN_WIDGET_CTRL 0x707
+
+#define HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_PIN_WIDGET_CTRL, 0x0))
+#define HDA_CMD_SET_PIN_WIDGET_CTRL(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_PIN_WIDGET_CTRL, (payload)))
+
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK 0x00000080
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT 7
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK 0x00000040
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT 6
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK 0x00000020
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT 5
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x00000007
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0
+
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE(rsp) \
+ (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK) >> \
+ HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT)
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE(rsp) \
+ (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK) >> \
+ HDA_GET_CMD_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT)
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE(rsp) \
+ (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK) >> \
+ HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT)
+#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE(rsp) \
+ (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) >> \
+ HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT)
+
+#define HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE 0x80
+#define HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE 0x40
+#define HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE 0x20
+#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x07
+#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0
+
+#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(param) \
+ (((param) << HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) & \
+ HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK)
+
+#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_HIZ 0
+#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50 1
+#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_GROUND 2
+#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80 4
+#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100 5
+
+/* Unsolicited Response */
+#define HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE 0xf08
+#define HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE 0x708
+
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE, 0x0))
+#define HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE, (payload)))
+
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK 0x00000080
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT 7
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK 0x0000001f
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT 0
+
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE(rsp) \
+ (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK) >> \
+ HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT)
+#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG(rsp) \
+ (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK) >> \
+ HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT)
+
+#define HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE 0x80
+#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK 0x3f
+#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT 0
+
+#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG(param) \
+ (((param) << HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT) & \
+ HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK)
+
+/* Pin Sense */
+#define HDA_CMD_VERB_GET_PIN_SENSE 0xf09
+#define HDA_CMD_VERB_SET_PIN_SENSE 0x709
+
+#define HDA_CMD_GET_PIN_SENSE(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_PIN_SENSE, 0x0))
+#define HDA_CMD_SET_PIN_SENSE(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_PIN_SENSE, (payload)))
+
+#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT 0x80000000
+#define HDA_CMD_GET_PIN_SENSE_ELD_VALID 0x40000000
+#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK 0x7fffffff
+#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT 0
+
+#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE(rsp) \
+ (((rsp) & HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK) >> \
+ HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT)
+
+#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_INVALID 0x7fffffff
+
+#define HDA_CMD_SET_PIN_SENSE_LEFT_CHANNEL 0x00
+#define HDA_CMD_SET_PIN_SENSE_RIGHT_CHANNEL 0x01
+
+/* EAPD/BTL Enable */
+#define HDA_CMD_VERB_GET_EAPD_BTL_ENABLE 0xf0c
+#define HDA_CMD_VERB_SET_EAPD_BTL_ENABLE 0x70c
+
+#define HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_EAPD_BTL_ENABLE, 0x0))
+#define HDA_CMD_SET_EAPD_BTL_ENABLE(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_EAPD_BTL_ENABLE, (payload)))
+
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK 0x00000004
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT 2
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK 0x00000002
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT 1
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK 0x00000001
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT 0
+
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP(rsp) \
+ (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK) >> \
+ HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT)
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD(rsp) \
+ (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK) >> \
+ HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT)
+#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL(rsp) \
+ (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK) >> \
+ HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT)
+
+#define HDA_CMD_SET_EAPD_BTL_ENABLE_LR_SWAP 0x04
+#define HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD 0x02
+#define HDA_CMD_SET_EAPD_BTL_ENABLE_BTL 0x01
+
+/* GPI Data */
+#define HDA_CMD_VERB_GET_GPI_DATA 0xf10
+#define HDA_CMD_VERB_SET_GPI_DATA 0x710
+
+#define HDA_CMD_GET_GPI_DATA(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPI_DATA, 0x0))
+#define HDA_CMD_SET_GPI_DATA(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPI_DATA, (payload)))
+
+/* GPI Wake Enable Mask */
+#define HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK 0xf11
+#define HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK 0x711
+
+#define HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK, 0x0))
+#define HDA_CMD_SET_GPI_WAKE_ENABLE_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK, (payload)))
+
+/* GPI Unsolicited Enable Mask */
+#define HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK 0xf12
+#define HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK 0x712
+
+#define HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK, 0x0))
+#define HDA_CMD_SET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK, (payload)))
+
+/* GPI Sticky Mask */
+#define HDA_CMD_VERB_GET_GPI_STICKY_MASK 0xf13
+#define HDA_CMD_VERB_SET_GPI_STICKY_MASK 0x713
+
+#define HDA_CMD_GET_GPI_STICKY_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPI_STICKY_MASK, 0x0))
+#define HDA_CMD_SET_GPI_STICKY_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPI_STICKY_MASK, (payload)))
+
+/* GPO Data */
+#define HDA_CMD_VERB_GET_GPO_DATA 0xf14
+#define HDA_CMD_VERB_SET_GPO_DATA 0x714
+
+#define HDA_CMD_GET_GPO_DATA(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPO_DATA, 0x0))
+#define HDA_CMD_SET_GPO_DATA(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPO_DATA, (payload)))
+
+/* GPIO Data */
+#define HDA_CMD_VERB_GET_GPIO_DATA 0xf15
+#define HDA_CMD_VERB_SET_GPIO_DATA 0x715
+
+#define HDA_CMD_GET_GPIO_DATA(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPIO_DATA, 0x0))
+#define HDA_CMD_SET_GPIO_DATA(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPIO_DATA, (payload)))
+
+/* GPIO Enable Mask */
+#define HDA_CMD_VERB_GET_GPIO_ENABLE_MASK 0xf16
+#define HDA_CMD_VERB_SET_GPIO_ENABLE_MASK 0x716
+
+#define HDA_CMD_GET_GPIO_ENABLE_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPIO_ENABLE_MASK, 0x0))
+#define HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPIO_ENABLE_MASK, (payload)))
+
+/* GPIO Direction */
+#define HDA_CMD_VERB_GET_GPIO_DIRECTION 0xf17
+#define HDA_CMD_VERB_SET_GPIO_DIRECTION 0x717
+
+#define HDA_CMD_GET_GPIO_DIRECTION(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPIO_DIRECTION, 0x0))
+#define HDA_CMD_SET_GPIO_DIRECTION(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPIO_DIRECTION, (payload)))
+
+/* GPIO Wake Enable Mask */
+#define HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK 0xf18
+#define HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK 0x718
+
+#define HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK, 0x0))
+#define HDA_CMD_SET_GPIO_WAKE_ENABLE_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK, (payload)))
+
+/* GPIO Unsolicited Enable Mask */
+#define HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK 0xf19
+#define HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK 0x719
+
+#define HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK, 0x0))
+#define HDA_CMD_SET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK, (payload)))
+
+/* GPIO_STICKY_MASK */
+#define HDA_CMD_VERB_GET_GPIO_STICKY_MASK 0xf1a
+#define HDA_CMD_VERB_SET_GPIO_STICKY_MASK 0x71a
+
+#define HDA_CMD_GET_GPIO_STICKY_MASK(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_GPIO_STICKY_MASK, 0x0))
+#define HDA_CMD_SET_GPIO_STICKY_MASK(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_GPIO_STICKY_MASK, (payload)))
+
+/* Beep Generation */
+#define HDA_CMD_VERB_GET_BEEP_GENERATION 0xf0a
+#define HDA_CMD_VERB_SET_BEEP_GENERATION 0x70a
+
+#define HDA_CMD_GET_BEEP_GENERATION(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_BEEP_GENERATION, 0x0))
+#define HDA_CMD_SET_BEEP_GENERATION(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_BEEP_GENERATION, (payload)))
+
+/* Volume Knob */
+#define HDA_CMD_VERB_GET_VOLUME_KNOB 0xf0f
+#define HDA_CMD_VERB_SET_VOLUME_KNOB 0x70f
+
+#define HDA_CMD_GET_VOLUME_KNOB(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_VOLUME_KNOB, 0x0))
+#define HDA_CMD_SET_VOLUME_KNOB(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_VOLUME_KNOB, (payload)))
+
+/* Subsystem ID */
+#define HDA_CMD_VERB_GET_SUBSYSTEM_ID 0xf20
+#define HDA_CMD_VERB_SET_SUSBYSTEM_ID1 0x720
+#define HDA_CMD_VERB_SET_SUBSYSTEM_ID2 0x721
+#define HDA_CMD_VERB_SET_SUBSYSTEM_ID3 0x722
+#define HDA_CMD_VERB_SET_SUBSYSTEM_ID4 0x723
+
+#define HDA_CMD_GET_SUBSYSTEM_ID(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_SUBSYSTEM_ID, 0x0))
+#define HDA_CMD_SET_SUBSYSTEM_ID1(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_SUSBYSTEM_ID1, (payload)))
+#define HDA_CMD_SET_SUBSYSTEM_ID2(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_SUSBYSTEM_ID2, (payload)))
+#define HDA_CMD_SET_SUBSYSTEM_ID3(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_SUSBYSTEM_ID3, (payload)))
+#define HDA_CMD_SET_SUBSYSTEM_ID4(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_SUSBYSTEM_ID4, (payload)))
+
+/* Configuration Default */
+#define HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT 0xf1c
+#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1 0x71c
+#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2 0x71d
+#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3 0x71e
+#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4 0x71f
+
+#define HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT, 0x0))
+#define HDA_CMD_SET_CONFIGURATION_DEFAULT1(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1, (payload)))
+#define HDA_CMD_SET_CONFIGURATION_DEFAULT2(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2, (payload)))
+#define HDA_CMD_SET_CONFIGURATION_DEFAULT3(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3, (payload)))
+#define HDA_CMD_SET_CONFIGURATION_DEFAULT4(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4, (payload)))
+
+/* Stripe Control */
+#define HDA_CMD_VERB_GET_STRIPE_CONTROL 0xf24
+#define HDA_CMD_VERB_SET_STRIPE_CONTROL 0x724
+
+#define HDA_CMD_GET_STRIPE_CONTROL(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_STRIPE_CONTROL, 0x0))
+#define HDA_CMD_SET_STRIPE_CONTROL(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_STRIPE_CONTROL, (payload)))
+
+/* Channel Count Control */
+#define HDA_CMD_VERB_GET_CONV_CHAN_COUNT 0xf2d
+#define HDA_CMD_VERB_SET_CONV_CHAN_COUNT 0x72d
+
+#define HDA_CMD_GET_CONV_CHAN_COUNT(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_CONV_CHAN_COUNT, 0x0))
+#define HDA_CMD_SET_CONV_CHAN_COUNT(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_CONV_CHAN_COUNT, (payload)))
+
+#define HDA_CMD_VERB_GET_HDMI_DIP_SIZE 0xf2e
+
+#define HDA_CMD_GET_HDMI_DIP_SIZE(cad, nid, arg) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_HDMI_DIP_SIZE, (arg)))
+
+#define HDA_CMD_VERB_GET_HDMI_ELDD 0xf2f
+
+#define HDA_CMD_GET_HDMI_ELDD(cad, nid, off) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_HDMI_ELDD, (off)))
+
+#define HDA_CMD_VERB_GET_HDMI_DIP_INDEX 0xf30
+#define HDA_CMD_VERB_SET_HDMI_DIP_INDEX 0x730
+
+#define HDA_CMD_GET_HDMI_DIP_INDEX(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_HDMI_DIP_INDEX, 0x0))
+#define HDA_CMD_SET_HDMI_DIP_INDEX(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_HDMI_DIP_INDEX, (payload)))
+
+#define HDA_CMD_VERB_GET_HDMI_DIP_DATA 0xf31
+#define HDA_CMD_VERB_SET_HDMI_DIP_DATA 0x731
+
+#define HDA_CMD_GET_HDMI_DIP_DATA(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_HDMI_DIP_DATA, 0x0))
+#define HDA_CMD_SET_HDMI_DIP_DATA(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_HDMI_DIP_DATA, (payload)))
+
+#define HDA_CMD_VERB_GET_HDMI_DIP_XMIT 0xf32
+#define HDA_CMD_VERB_SET_HDMI_DIP_XMIT 0x732
+
+#define HDA_CMD_GET_HDMI_DIP_XMIT(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_HDMI_DIP_XMIT, 0x0))
+#define HDA_CMD_SET_HDMI_DIP_XMIT(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_HDMI_DIP_XMIT, (payload)))
+
+#define HDA_CMD_VERB_GET_HDMI_CP_CTRL 0xf33
+#define HDA_CMD_VERB_SET_HDMI_CP_CTRL 0x733
+
+#define HDA_CMD_VERB_GET_HDMI_CHAN_SLOT 0xf34
+#define HDA_CMD_VERB_SET_HDMI_CHAN_SLOT 0x734
+
+#define HDA_CMD_GET_HDMI_CHAN_SLOT(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_GET_HDMI_CHAN_SLOT, 0x0))
+#define HDA_CMD_SET_HDMI_CHAN_SLOT(cad, nid, payload) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_SET_HDMI_CHAN_SLOT, (payload)))
+
+#define HDA_HDMI_CODING_TYPE_REF_STREAM_HEADER 0
+#define HDA_HDMI_CODING_TYPE_LPCM 1
+#define HDA_HDMI_CODING_TYPE_AC3 2
+#define HDA_HDMI_CODING_TYPE_MPEG1 3
+#define HDA_HDMI_CODING_TYPE_MP3 4
+#define HDA_HDMI_CODING_TYPE_MPEG2 5
+#define HDA_HDMI_CODING_TYPE_AACLC 6
+#define HDA_HDMI_CODING_TYPE_DTS 7
+#define HDA_HDMI_CODING_TYPE_ATRAC 8
+#define HDA_HDMI_CODING_TYPE_SACD 9
+#define HDA_HDMI_CODING_TYPE_EAC3 10
+#define HDA_HDMI_CODING_TYPE_DTS_HD 11
+#define HDA_HDMI_CODING_TYPE_MLP 12
+#define HDA_HDMI_CODING_TYPE_DST 13
+#define HDA_HDMI_CODING_TYPE_WMAPRO 14
+#define HDA_HDMI_CODING_TYPE_REF_CTX 15
+
+/* Function Reset */
+#define HDA_CMD_VERB_FUNCTION_RESET 0x7ff
+
+#define HDA_CMD_FUNCTION_RESET(cad, nid) \
+ (HDA_CMD_12BIT((cad), (nid), \
+ HDA_CMD_VERB_FUNCTION_RESET, 0x0))
+
+
+/****************************************************************************
+ * HDA Device Parameters
+ ****************************************************************************/
+
+/* Vendor ID */
+#define HDA_PARAM_VENDOR_ID 0x00
+
+#define HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK 0xffff0000
+#define HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT 16
+#define HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK 0x0000ffff
+#define HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT 0
+
+#define HDA_PARAM_VENDOR_ID_VENDOR_ID(param) \
+ (((param) & HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK) >> \
+ HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT)
+#define HDA_PARAM_VENDOR_ID_DEVICE_ID(param) \
+ (((param) & HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK) >> \
+ HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT)
+
+/* Revision ID */
+#define HDA_PARAM_REVISION_ID 0x02
+
+#define HDA_PARAM_REVISION_ID_MAJREV_MASK 0x00f00000
+#define HDA_PARAM_REVISION_ID_MAJREV_SHIFT 20
+#define HDA_PARAM_REVISION_ID_MINREV_MASK 0x000f0000
+#define HDA_PARAM_REVISION_ID_MINREV_SHIFT 16
+#define HDA_PARAM_REVISION_ID_REVISION_ID_MASK 0x0000ff00
+#define HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT 8
+#define HDA_PARAM_REVISION_ID_STEPPING_ID_MASK 0x000000ff
+#define HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT 0
+
+#define HDA_PARAM_REVISION_ID_MAJREV(param) \
+ (((param) & HDA_PARAM_REVISION_ID_MAJREV_MASK) >> \
+ HDA_PARAM_REVISION_ID_MAJREV_SHIFT)
+#define HDA_PARAM_REVISION_ID_MINREV(param) \
+ (((param) & HDA_PARAM_REVISION_ID_MINREV_MASK) >> \
+ HDA_PARAM_REVISION_ID_MINREV_SHIFT)
+#define HDA_PARAM_REVISION_ID_REVISION_ID(param) \
+ (((param) & HDA_PARAM_REVISION_ID_REVISION_ID_MASK) >> \
+ HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT)
+#define HDA_PARAM_REVISION_ID_STEPPING_ID(param) \
+ (((param) & HDA_PARAM_REVISION_ID_STEPPING_ID_MASK) >> \
+ HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT)
+
+/* Subordinate Node Cound */
+#define HDA_PARAM_SUB_NODE_COUNT 0x04
+
+#define HDA_PARAM_SUB_NODE_COUNT_START_MASK 0x00ff0000
+#define HDA_PARAM_SUB_NODE_COUNT_START_SHIFT 16
+#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK 0x000000ff
+#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT 0
+
+#define HDA_PARAM_SUB_NODE_COUNT_START(param) \
+ (((param) & HDA_PARAM_SUB_NODE_COUNT_START_MASK) >> \
+ HDA_PARAM_SUB_NODE_COUNT_START_SHIFT)
+#define HDA_PARAM_SUB_NODE_COUNT_TOTAL(param) \
+ (((param) & HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK) >> \
+ HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT)
+
+/* Function Group Type */
+#define HDA_PARAM_FCT_GRP_TYPE 0x05
+
+#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK 0x00000100
+#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_SHIFT 8
+#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK 0x000000ff
+#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT 0
+
+#define HDA_PARAM_FCT_GRP_TYPE_UNSOL(param) \
+ (((param) & HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK) >> \
+ HDA_PARAM_FCT_GROUP_TYPE_UNSOL_SHIFT)
+#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(param) \
+ (((param) & HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK) >> \
+ HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT)
+
+#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO 0x01
+#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM 0x02
+
+/* Audio Function Group Capabilities */
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP 0x08
+
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK 0x00010000
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT 16
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK 0x00000f00
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT 8
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK 0x0000000f
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT 0
+
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN(param) \
+ (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK) >> \
+ HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT)
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY(param) \
+ (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK) >> \
+ HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT)
+#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY(param) \
+ (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK) >> \
+ HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT)
+
+/* Audio Widget Capabilities */
+#define HDA_PARAM_AUDIO_WIDGET_CAP 0x09
+
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK 0x00f00000
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT 20
+#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK 0x000f0000
+#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT 16
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_MASK 0x0000e000
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_SHIFT 13
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CP_MASK 0x00001000
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CP_SHIFT 12
+#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK 0x00000800
+#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT 11
+#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK 0x00000400
+#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT 10
+#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK 0x00000200
+#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT 9
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK 0x00000100
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT 8
+#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK 0x00000080
+#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT 7
+#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK 0x00000040
+#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT 6
+#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK 0x00000020
+#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT 5
+#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK 0x00000010
+#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT 4
+#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK 0x00000008
+#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT 3
+#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK 0x00000004
+#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT 2
+#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK 0x00000002
+#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT 1
+#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK 0x00000001
+#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT 0
+
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CC(param) \
+ ((((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_MASK) >> \
+ (HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_SHIFT - 1)) | \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT))
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CP(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CP_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_CP_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT)
+#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(param) \
+ (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK) >> \
+ HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT)
+
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT 0x0
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT 0x1
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER 0x2
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR 0x3
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX 0x4
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET 0x5
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET 0x6
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET 0x7
+#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET 0xf
+
+/* Supported PCM Size, Rates */
+
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE 0x0a
+
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK 0x00100000
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT 20
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK 0x00080000
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT 19
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK 0x00040000
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT 18
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK 0x00020000
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT 17
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK 0x00010000
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT 16
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000001
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 0
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000002
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 1
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000004
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 2
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000008
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 3
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000010
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 4
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000020
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 5
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000040
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 6
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000080
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 7
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000100
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 8
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x00000200
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 9
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x00000400
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 10
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x00000800
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 11
+
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT)
+#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(param) \
+ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK) >> \
+ HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT)
+
+/* Supported Stream Formats */
+#define HDA_PARAM_SUPP_STREAM_FORMATS 0x0b
+
+#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK 0x00000004
+#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT 2
+#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK 0x00000002
+#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT 1
+#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK 0x00000001
+#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT 0
+
+#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3(param) \
+ (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK) >> \
+ HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT)
+#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(param) \
+ (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK) >> \
+ HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT)
+#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM(param) \
+ (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK) >> \
+ HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT)
+
+/* Pin Capabilities */
+#define HDA_PARAM_PIN_CAP 0x0c
+
+#define HDA_PARAM_PIN_CAP_HBR_MASK 0x08000000
+#define HDA_PARAM_PIN_CAP_HBR_SHIFT 27
+#define HDA_PARAM_PIN_CAP_DP_MASK 0x01000000
+#define HDA_PARAM_PIN_CAP_DP_SHIFT 24
+#define HDA_PARAM_PIN_CAP_EAPD_CAP_MASK 0x00010000
+#define HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT 16
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_MASK 0x0000ff00
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT 8
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK 0x00002000
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT 13
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK 0x00001000
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT 12
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK 0x00000400
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT 10
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK 0x00000200
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT 9
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK 0x00000100
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT 8
+#define HDA_PARAM_PIN_CAP_HDMI_MASK 0x00000080
+#define HDA_PARAM_PIN_CAP_HDMI_SHIFT 7
+#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK 0x00000040
+#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT 6
+#define HDA_PARAM_PIN_CAP_INPUT_CAP_MASK 0x00000020
+#define HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT 5
+#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK 0x00000010
+#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT 4
+#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK 0x00000008
+#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT 3
+#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK 0x00000004
+#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT 2
+#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK 0x00000002
+#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT 1
+#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK 0x00000001
+#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT 0
+
+#define HDA_PARAM_PIN_CAP_HBR(param) \
+ (((param) & HDA_PARAM_PIN_CAP_HBR_MASK) >> \
+ HDA_PARAM_PIN_CAP_HBR_SHIFT)
+#define HDA_PARAM_PIN_CAP_DP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_DP_MASK) >> \
+ HDA_PARAM_PIN_CAP_DP_SHIFT)
+#define HDA_PARAM_PIN_CAP_EAPD_CAP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_EAPD_CAP_MASK) >> \
+ HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT)
+#define HDA_PARAM_PIN_CAP_VREF_CTRL(param) \
+ (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_MASK) >> \
+ HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT)
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_100(param) \
+ (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK) >> \
+ HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT)
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_80(param) \
+ (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK) >> \
+ HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT)
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(param) \
+ (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK) >> \
+ HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT)
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_50(param) \
+ (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK) >> \
+ HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT)
+#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(param) \
+ (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK) >> \
+ HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT)
+#define HDA_PARAM_PIN_CAP_HDMI(param) \
+ (((param) & HDA_PARAM_PIN_CAP_HDMI_MASK) >> \
+ HDA_PARAM_PIN_CAP_HDMI_SHIFT)
+#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(param) \
+ (((param) & HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK) >> \
+ HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT)
+#define HDA_PARAM_PIN_CAP_INPUT_CAP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_INPUT_CAP_MASK) >> \
+ HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT)
+#define HDA_PARAM_PIN_CAP_OUTPUT_CAP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK) >> \
+ HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT)
+#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK) >> \
+ HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT)
+#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) >> \
+ HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT)
+#define HDA_PARAM_PIN_CAP_TRIGGER_REQD(param) \
+ (((param) & HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK) >> \
+ HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT)
+#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(param) \
+ (((param) & HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK) >> \
+ HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT)
+
+/* Input Amplifier Capabilities */
+#define HDA_PARAM_INPUT_AMP_CAP 0x0d
+
+#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000
+#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT 31
+#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000
+#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT 16
+#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00
+#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT 8
+#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK 0x0000007f
+#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT 0
+
+#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP(param) \
+ (((param) & HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK) >> \
+ HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT)
+#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE(param) \
+ (((param) & HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK) >> \
+ HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT)
+#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS(param) \
+ (((param) & HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK) >> \
+ HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT)
+#define HDA_PARAM_INPUT_AMP_CAP_OFFSET(param) \
+ (((param) & HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK) >> \
+ HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT)
+
+/* Output Amplifier Capabilities */
+#define HDA_PARAM_OUTPUT_AMP_CAP 0x12
+
+#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000
+#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT 31
+#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000
+#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT 16
+#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00
+#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT 8
+#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK 0x0000007f
+#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT 0
+
+#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(param) \
+ (((param) & HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK) >> \
+ HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT)
+#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(param) \
+ (((param) & HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK) >> \
+ HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT)
+#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(param) \
+ (((param) & HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK) >> \
+ HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT)
+#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(param) \
+ (((param) & HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK) >> \
+ HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT)
+
+/* Connection List Length */
+#define HDA_PARAM_CONN_LIST_LENGTH 0x0e
+
+#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK 0x00000080
+#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT 7
+#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK 0x0000007f
+#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT 0
+
+#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(param) \
+ (((param) & HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK) >> \
+ HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT)
+#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(param) \
+ (((param) & HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK) >> \
+ HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT)
+
+/* Supported Power States */
+#define HDA_PARAM_SUPP_POWER_STATES 0x0f
+
+#define HDA_PARAM_SUPP_POWER_STATES_D3_MASK 0x00000008
+#define HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT 3
+#define HDA_PARAM_SUPP_POWER_STATES_D2_MASK 0x00000004
+#define HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT 2
+#define HDA_PARAM_SUPP_POWER_STATES_D1_MASK 0x00000002
+#define HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT 1
+#define HDA_PARAM_SUPP_POWER_STATES_D0_MASK 0x00000001
+#define HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT 0
+
+#define HDA_PARAM_SUPP_POWER_STATES_D3(param) \
+ (((param) & HDA_PARAM_SUPP_POWER_STATES_D3_MASK) >> \
+ HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT)
+#define HDA_PARAM_SUPP_POWER_STATES_D2(param) \
+ (((param) & HDA_PARAM_SUPP_POWER_STATES_D2_MASK) >> \
+ HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT)
+#define HDA_PARAM_SUPP_POWER_STATES_D1(param) \
+ (((param) & HDA_PARAM_SUPP_POWER_STATES_D1_MASK) >> \
+ HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT)
+#define HDA_PARAM_SUPP_POWER_STATES_D0(param) \
+ (((param) & HDA_PARAM_SUPP_POWER_STATES_D0_MASK) >> \
+ HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT)
+
+/* Processing Capabilities */
+#define HDA_PARAM_PROCESSING_CAP 0x10
+
+#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK 0x0000ff00
+#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT 8
+#define HDA_PARAM_PROCESSING_CAP_BENIGN_MASK 0x00000001
+#define HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT 0
+
+#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF(param) \
+ (((param) & HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK) >> \
+ HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT)
+#define HDA_PARAM_PROCESSING_CAP_BENIGN(param) \
+ (((param) & HDA_PARAM_PROCESSING_CAP_BENIGN_MASK) >> \
+ HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT)
+
+/* GPIO Count */
+#define HDA_PARAM_GPIO_COUNT 0x11
+
+#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK 0x80000000
+#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT 31
+#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK 0x40000000
+#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT 30
+#define HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK 0x00ff0000
+#define HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT 16
+#define HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK 0x0000ff00
+#define HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT 8
+#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK 0x000000ff
+#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT 0
+
+#define HDA_PARAM_GPIO_COUNT_GPI_WAKE(param) \
+ (((param) & HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK) >> \
+ HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT)
+#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL(param) \
+ (((param) & HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK) >> \
+ HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT)
+#define HDA_PARAM_GPIO_COUNT_NUM_GPI(param) \
+ (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK) >> \
+ HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT)
+#define HDA_PARAM_GPIO_COUNT_NUM_GPO(param) \
+ (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK) >> \
+ HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT)
+#define HDA_PARAM_GPIO_COUNT_NUM_GPIO(param) \
+ (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK) >> \
+ HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT)
+
+/* Volume Knob Capabilities */
+#define HDA_PARAM_VOLUME_KNOB_CAP 0x13
+
+#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK 0x00000080
+#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT 7
+#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK 0x0000007f
+#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT 0
+
+#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA(param) \
+ (((param) & HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK) >> \
+ HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT)
+#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS(param) \
+ (((param) & HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK) >> \
+ HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT)
+
+
+#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK 0x0000000f
+#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT 0
+#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK 0x000000f0
+#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT 4
+#define HDA_CONFIG_DEFAULTCONF_MISC_MASK 0x00000f00
+#define HDA_CONFIG_DEFAULTCONF_MISC_SHIFT 8
+#define HDA_CONFIG_DEFAULTCONF_COLOR_MASK 0x0000f000
+#define HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT 12
+#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK 0x000f0000
+#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT 16
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_MASK 0x00f00000
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT 20
+#define HDA_CONFIG_DEFAULTCONF_LOCATION_MASK 0x3f000000
+#define HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT 24
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK 0xc0000000
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT 30
+
+#define HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_MISC(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_MISC_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_MISC_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_COLOR(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_LOCATION(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_LOCATION_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT)
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf) \
+ (((conf) & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> \
+ HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT)
+
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK (0<<30)
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE (1<<30)
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED (2<<30)
+#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH (3<<30)
+
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT (0<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER (1<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT (2<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_CD (3<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT (4<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT (5<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE (6<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET (7<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN (8<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_AUX (9<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN (10<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY (11<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN (12<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN (13<<20)
+#define HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER (15<<20)
+
+#endif /* _HDA_REG_H_ */
diff --git a/usr/src/cmd/bhyve/hdac_reg.h b/usr/src/cmd/bhyve/hdac_reg.h
new file mode 100644
index 0000000000..35272e5135
--- /dev/null
+++ b/usr/src/cmd/bhyve/hdac_reg.h
@@ -0,0 +1,271 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _HDAC_REG_H_
+#define _HDAC_REG_H_
+
+/****************************************************************************
+ * HDA Controller Register Set
+ ****************************************************************************/
+#define HDAC_GCAP 0x00 /* 2 - Global Capabilities*/
+#define HDAC_VMIN 0x02 /* 1 - Minor Version */
+#define HDAC_VMAJ 0x03 /* 1 - Major Version */
+#define HDAC_OUTPAY 0x04 /* 2 - Output Payload Capability */
+#define HDAC_INPAY 0x06 /* 2 - Input Payload Capability */
+#define HDAC_GCTL 0x08 /* 4 - Global Control */
+#define HDAC_WAKEEN 0x0c /* 2 - Wake Enable */
+#define HDAC_STATESTS 0x0e /* 2 - State Change Status */
+#define HDAC_GSTS 0x10 /* 2 - Global Status */
+#define HDAC_OUTSTRMPAY 0x18 /* 2 - Output Stream Payload Capability */
+#define HDAC_INSTRMPAY 0x1a /* 2 - Input Stream Payload Capability */
+#define HDAC_INTCTL 0x20 /* 4 - Interrupt Control */
+#define HDAC_INTSTS 0x24 /* 4 - Interrupt Status */
+#define HDAC_WALCLK 0x30 /* 4 - Wall Clock Counter */
+#define HDAC_SSYNC 0x38 /* 4 - Stream Synchronization */
+#define HDAC_CORBLBASE 0x40 /* 4 - CORB Lower Base Address */
+#define HDAC_CORBUBASE 0x44 /* 4 - CORB Upper Base Address */
+#define HDAC_CORBWP 0x48 /* 2 - CORB Write Pointer */
+#define HDAC_CORBRP 0x4a /* 2 - CORB Read Pointer */
+#define HDAC_CORBCTL 0x4c /* 1 - CORB Control */
+#define HDAC_CORBSTS 0x4d /* 1 - CORB Status */
+#define HDAC_CORBSIZE 0x4e /* 1 - CORB Size */
+#define HDAC_RIRBLBASE 0x50 /* 4 - RIRB Lower Base Address */
+#define HDAC_RIRBUBASE 0x54 /* 4 - RIRB Upper Base Address */
+#define HDAC_RIRBWP 0x58 /* 2 - RIRB Write Pointer */
+#define HDAC_RINTCNT 0x5a /* 2 - Response Interrupt Count */
+#define HDAC_RIRBCTL 0x5c /* 1 - RIRB Control */
+#define HDAC_RIRBSTS 0x5d /* 1 - RIRB Status */
+#define HDAC_RIRBSIZE 0x5e /* 1 - RIRB Size */
+#define HDAC_ICOI 0x60 /* 4 - Immediate Command Output Interface */
+#define HDAC_ICII 0x64 /* 4 - Immediate Command Input Interface */
+#define HDAC_ICIS 0x68 /* 2 - Immediate Command Status */
+#define HDAC_DPIBLBASE 0x70 /* 4 - DMA Position Buffer Lower Base */
+#define HDAC_DPIBUBASE 0x74 /* 4 - DMA Position Buffer Upper Base */
+#define HDAC_SDCTL0 0x80 /* 3 - Stream Descriptor Control */
+#define HDAC_SDCTL1 0x81 /* 3 - Stream Descriptor Control */
+#define HDAC_SDCTL2 0x82 /* 3 - Stream Descriptor Control */
+#define HDAC_SDSTS 0x83 /* 1 - Stream Descriptor Status */
+#define HDAC_SDLPIB 0x84 /* 4 - Link Position in Buffer */
+#define HDAC_SDCBL 0x88 /* 4 - Cyclic Buffer Length */
+#define HDAC_SDLVI 0x8C /* 2 - Last Valid Index */
+#define HDAC_SDFIFOS 0x90 /* 2 - FIFOS */
+#define HDAC_SDFMT 0x92 /* 2 - fmt */
+#define HDAC_SDBDPL 0x98 /* 4 - Buffer Descriptor Pointer Lower Base */
+#define HDAC_SDBDPU 0x9C /* 4 - Buffer Descriptor Pointer Upper Base */
+
+#define _HDAC_ISDOFFSET(n, iss, oss) (0x80 + ((n) * 0x20))
+#define _HDAC_ISDCTL(n, iss, oss) (0x00 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDSTS(n, iss, oss) (0x03 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDPICB(n, iss, oss) (0x04 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDCBL(n, iss, oss) (0x08 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDLVI(n, iss, oss) (0x0c + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDFIFOD(n, iss, oss) (0x10 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDFMT(n, iss, oss) (0x12 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDBDPL(n, iss, oss) (0x18 + _HDAC_ISDOFFSET(n, iss, oss))
+#define _HDAC_ISDBDPU(n, iss, oss) (0x1c + _HDAC_ISDOFFSET(n, iss, oss))
+
+#define _HDAC_OSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((n) * 0x20))
+#define _HDAC_OSDCTL(n, iss, oss) (0x00 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDSTS(n, iss, oss) (0x03 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDPICB(n, iss, oss) (0x04 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDCBL(n, iss, oss) (0x08 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDLVI(n, iss, oss) (0x0c + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDFIFOD(n, iss, oss) (0x10 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDFMT(n, iss, oss) (0x12 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDBDPL(n, iss, oss) (0x18 + _HDAC_OSDOFFSET(n, iss, oss))
+#define _HDAC_OSDBDPU(n, iss, oss) (0x1c + _HDAC_OSDOFFSET(n, iss, oss))
+
+#define _HDAC_BSDOFFSET(n, iss, oss) \
+ (0x80 + ((iss) * 0x20) + ((oss) * 0x20) + ((n) * 0x20))
+#define _HDAC_BSDCTL(n, iss, oss) (0x00 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDSTS(n, iss, oss) (0x03 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDPICB(n, iss, oss) (0x04 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDCBL(n, iss, oss) (0x08 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDLVI(n, iss, oss) (0x0c + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDFIFOD(n, iss, oss) (0x10 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDFMT(n, iss, oss) (0x12 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDBDPL(n, iss, oss) (0x18 + _HDAC_BSDOFFSET(n, iss, oss))
+#define _HDAC_BSDBDBU(n, iss, oss) (0x1c + _HDAC_BSDOFFSET(n, iss, oss))
+
+/****************************************************************************
+ * HDA Controller Register Fields
+ ****************************************************************************/
+
+/* GCAP - Global Capabilities */
+#define HDAC_GCAP_64OK 0x0001
+#define HDAC_GCAP_NSDO_MASK 0x0006
+#define HDAC_GCAP_NSDO_SHIFT 1
+#define HDAC_GCAP_BSS_MASK 0x00f8
+#define HDAC_GCAP_BSS_SHIFT 3
+#define HDAC_GCAP_ISS_MASK 0x0f00
+#define HDAC_GCAP_ISS_SHIFT 8
+#define HDAC_GCAP_OSS_MASK 0xf000
+#define HDAC_GCAP_OSS_SHIFT 12
+
+#define HDAC_GCAP_NSDO_1SDO 0x00
+#define HDAC_GCAP_NSDO_2SDO 0x02
+#define HDAC_GCAP_NSDO_4SDO 0x04
+
+#define HDAC_GCAP_BSS(gcap) \
+ (((gcap) & HDAC_GCAP_BSS_MASK) >> HDAC_GCAP_BSS_SHIFT)
+#define HDAC_GCAP_ISS(gcap) \
+ (((gcap) & HDAC_GCAP_ISS_MASK) >> HDAC_GCAP_ISS_SHIFT)
+#define HDAC_GCAP_OSS(gcap) \
+ (((gcap) & HDAC_GCAP_OSS_MASK) >> HDAC_GCAP_OSS_SHIFT)
+#define HDAC_GCAP_NSDO(gcap) \
+ (((gcap) & HDAC_GCAP_NSDO_MASK) >> HDAC_GCAP_NSDO_SHIFT)
+
+/* GCTL - Global Control */
+#define HDAC_GCTL_CRST 0x00000001
+#define HDAC_GCTL_FCNTRL 0x00000002
+#define HDAC_GCTL_UNSOL 0x00000100
+
+/* WAKEEN - Wake Enable */
+#define HDAC_WAKEEN_SDIWEN_MASK 0x7fff
+#define HDAC_WAKEEN_SDIWEN_SHIFT 0
+
+/* STATESTS - State Change Status */
+#define HDAC_STATESTS_SDIWAKE_MASK 0x7fff
+#define HDAC_STATESTS_SDIWAKE_SHIFT 0
+
+#define HDAC_STATESTS_SDIWAKE(statests, n) \
+ (((((statests) & HDAC_STATESTS_SDIWAKE_MASK) >> \
+ HDAC_STATESTS_SDIWAKE_SHIFT) >> (n)) & 0x0001)
+
+/* GSTS - Global Status */
+#define HDAC_GSTS_FSTS 0x0002
+
+/* INTCTL - Interrut Control */
+#define HDAC_INTCTL_SIE_MASK 0x3fffffff
+#define HDAC_INTCTL_SIE_SHIFT 0
+#define HDAC_INTCTL_CIE 0x40000000
+#define HDAC_INTCTL_GIE 0x80000000
+
+/* INTSTS - Interrupt Status */
+#define HDAC_INTSTS_SIS_MASK 0x3fffffff
+#define HDAC_INTSTS_SIS_SHIFT 0
+#define HDAC_INTSTS_CIS 0x40000000
+#define HDAC_INTSTS_GIS 0x80000000
+
+/* SSYNC - Stream Synchronization */
+#define HDAC_SSYNC_SSYNC_MASK 0x3fffffff
+#define HDAC_SSYNC_SSYNC_SHIFT 0
+
+/* CORBWP - CORB Write Pointer */
+#define HDAC_CORBWP_CORBWP_MASK 0x00ff
+#define HDAC_CORBWP_CORBWP_SHIFT 0
+
+/* CORBRP - CORB Read Pointer */
+#define HDAC_CORBRP_CORBRP_MASK 0x00ff
+#define HDAC_CORBRP_CORBRP_SHIFT 0
+#define HDAC_CORBRP_CORBRPRST 0x8000
+
+/* CORBCTL - CORB Control */
+#define HDAC_CORBCTL_CMEIE 0x01
+#define HDAC_CORBCTL_CORBRUN 0x02
+
+/* CORBSTS - CORB Status */
+#define HDAC_CORBSTS_CMEI 0x01
+
+/* CORBSIZE - CORB Size */
+#define HDAC_CORBSIZE_CORBSIZE_MASK 0x03
+#define HDAC_CORBSIZE_CORBSIZE_SHIFT 0
+#define HDAC_CORBSIZE_CORBSZCAP_MASK 0xf0
+#define HDAC_CORBSIZE_CORBSZCAP_SHIFT 4
+
+#define HDAC_CORBSIZE_CORBSIZE_2 0x00
+#define HDAC_CORBSIZE_CORBSIZE_16 0x01
+#define HDAC_CORBSIZE_CORBSIZE_256 0x02
+
+#define HDAC_CORBSIZE_CORBSZCAP_2 0x10
+#define HDAC_CORBSIZE_CORBSZCAP_16 0x20
+#define HDAC_CORBSIZE_CORBSZCAP_256 0x40
+
+#define HDAC_CORBSIZE_CORBSIZE(corbsize) \
+ (((corbsize) & HDAC_CORBSIZE_CORBSIZE_MASK) >> HDAC_CORBSIZE_CORBSIZE_SHIFT)
+
+/* RIRBWP - RIRB Write Pointer */
+#define HDAC_RIRBWP_RIRBWP_MASK 0x00ff
+#define HDAC_RIRBWP_RIRBWP_SHIFT 0
+#define HDAC_RIRBWP_RIRBWPRST 0x8000
+
+/* RINTCTN - Response Interrupt Count */
+#define HDAC_RINTCNT_MASK 0x00ff
+#define HDAC_RINTCNT_SHIFT 0
+
+/* RIRBCTL - RIRB Control */
+#define HDAC_RIRBCTL_RINTCTL 0x01
+#define HDAC_RIRBCTL_RIRBDMAEN 0x02
+#define HDAC_RIRBCTL_RIRBOIC 0x04
+
+/* RIRBSTS - RIRB Status */
+#define HDAC_RIRBSTS_RINTFL 0x01
+#define HDAC_RIRBSTS_RIRBOIS 0x04
+
+/* RIRBSIZE - RIRB Size */
+#define HDAC_RIRBSIZE_RIRBSIZE_MASK 0x03
+#define HDAC_RIRBSIZE_RIRBSIZE_SHIFT 0
+#define HDAC_RIRBSIZE_RIRBSZCAP_MASK 0xf0
+#define HDAC_RIRBSIZE_RIRBSZCAP_SHIFT 4
+
+#define HDAC_RIRBSIZE_RIRBSIZE_2 0x00
+#define HDAC_RIRBSIZE_RIRBSIZE_16 0x01
+#define HDAC_RIRBSIZE_RIRBSIZE_256 0x02
+
+#define HDAC_RIRBSIZE_RIRBSZCAP_2 0x10
+#define HDAC_RIRBSIZE_RIRBSZCAP_16 0x20
+#define HDAC_RIRBSIZE_RIRBSZCAP_256 0x40
+
+#define HDAC_RIRBSIZE_RIRBSIZE(rirbsize) \
+ (((rirbsize) & HDAC_RIRBSIZE_RIRBSIZE_MASK) >> HDAC_RIRBSIZE_RIRBSIZE_SHIFT)
+
+/* DPLBASE - DMA Position Lower Base Address */
+#define HDAC_DPLBASE_DPLBASE_MASK 0xffffff80
+#define HDAC_DPLBASE_DPLBASE_SHIFT 7
+#define HDAC_DPLBASE_DPLBASE_DMAPBE 0x00000001
+
+/* SDCTL - Stream Descriptor Control */
+#define HDAC_SDCTL_SRST 0x000001
+#define HDAC_SDCTL_RUN 0x000002
+#define HDAC_SDCTL_IOCE 0x000004
+#define HDAC_SDCTL_FEIE 0x000008
+#define HDAC_SDCTL_DEIE 0x000010
+#define HDAC_SDCTL2_STRIPE_MASK 0x03
+#define HDAC_SDCTL2_STRIPE_SHIFT 0
+#define HDAC_SDCTL2_TP 0x04
+#define HDAC_SDCTL2_DIR 0x08
+#define HDAC_SDCTL2_STRM_MASK 0xf0
+#define HDAC_SDCTL2_STRM_SHIFT 4
+
+#define HDAC_SDSTS_DESE (1 << 4)
+#define HDAC_SDSTS_FIFOE (1 << 3)
+#define HDAC_SDSTS_BCIS (1 << 2)
+
+#endif /* _HDAC_REG_H_ */
diff --git a/usr/src/cmd/bhyve/mevent.c b/usr/src/cmd/bhyve/mevent.c
index a258fd3047..d604039e1b 100644
--- a/usr/src/cmd/bhyve/mevent.c
+++ b/usr/src/cmd/bhyve/mevent.c
@@ -139,7 +139,7 @@ mevent_pipe_read(int fd, enum ev_type type, void *param)
static void
mevent_notify(void)
{
- char c;
+ char c = '\0';
/*
* If calling from outside the i/o thread, write a byte on the
diff --git a/usr/src/cmd/bhyve/net_backends.c b/usr/src/cmd/bhyve/net_backends.c
new file mode 100644
index 0000000000..88afaca4b1
--- /dev/null
+++ b/usr/src/cmd/bhyve/net_backends.c
@@ -0,0 +1,807 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Vincenzo Maffione <vmaffione@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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This file implements multiple network backends (tap, netmap, ...),
+ * to be used by network frontends such as virtio-net and e1000.
+ * The API to access the backend (e.g. send/receive packets, negotiate
+ * features) is exported by net_backends.h.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h> /* u_short etc */
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <net/netmap.h>
+#include <net/netmap_virt.h>
+#define NETMAP_WITH_LIBS
+#include <net/netmap_user.h>
+
+#ifndef WITHOUT_CAPSICUM
+#include <capsicum_helpers.h>
+#endif
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <assert.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <poll.h>
+#include <assert.h>
+
+
+#include "iov.h"
+#include "mevent.h"
+#include "net_backends.h"
+
+#include <sys/linker_set.h>
+
+/*
+ * Each network backend registers a set of function pointers that are
+ * used to implement the net backends API.
+ * This might need to be exposed if we implement backends in separate files.
+ */
+struct net_backend {
+ const char *prefix; /* prefix matching this backend */
+
+ /*
+ * Routines used to initialize and cleanup the resources needed
+ * by a backend. The cleanup function is used internally,
+ * and should not be called by the frontend.
+ */
+ int (*init)(struct net_backend *be, const char *devname,
+ net_be_rxeof_t cb, void *param);
+ void (*cleanup)(struct net_backend *be);
+
+ /*
+ * Called to serve a guest transmit request. The scatter-gather
+ * vector provided by the caller has 'iovcnt' elements and contains
+ * the packet to send.
+ */
+ ssize_t (*send)(struct net_backend *be, struct iovec *iov, int iovcnt);
+
+ /*
+ * Called to receive a packet from the backend. When the function
+ * returns a positive value 'len', the scatter-gather vector
+ * provided by the caller contains a packet with such length.
+ * The function returns 0 if the backend doesn't have a new packet to
+ * receive.
+ */
+ ssize_t (*recv)(struct net_backend *be, struct iovec *iov, int iovcnt);
+
+ /*
+ * Ask the backend for the virtio-net features it is able to
+ * support. Possible features are TSO, UFO and checksum offloading
+ * in both rx and tx direction and for both IPv4 and IPv6.
+ */
+ uint64_t (*get_cap)(struct net_backend *be);
+
+ /*
+ * Tell the backend to enable/disable the specified virtio-net
+ * features (capabilities).
+ */
+ int (*set_cap)(struct net_backend *be, uint64_t features,
+ unsigned int vnet_hdr_len);
+
+ struct pci_vtnet_softc *sc;
+ int fd;
+
+ /*
+ * Length of the virtio-net header used by the backend and the
+ * frontend, respectively. A zero value means that the header
+ * is not used.
+ */
+ unsigned int be_vnet_hdr_len;
+ unsigned int fe_vnet_hdr_len;
+
+ /* Size of backend-specific private data. */
+ size_t priv_size;
+
+ /* Room for backend-specific data. */
+ char opaque[0];
+};
+
+SET_DECLARE(net_backend_set, struct net_backend);
+
+#define VNET_HDR_LEN sizeof(struct virtio_net_rxhdr)
+
+#define WPRINTF(params) printf params
+
+/*
+ * The tap backend
+ */
+
+struct tap_priv {
+ struct mevent *mevp;
+};
+
+static void
+tap_cleanup(struct net_backend *be)
+{
+ struct tap_priv *priv = (struct tap_priv *)be->opaque;
+
+ if (priv->mevp) {
+ mevent_delete(priv->mevp);
+ }
+ if (be->fd != -1) {
+ close(be->fd);
+ be->fd = -1;
+ }
+}
+
+static int
+tap_init(struct net_backend *be, const char *devname,
+ net_be_rxeof_t cb, void *param)
+{
+ struct tap_priv *priv = (struct tap_priv *)be->opaque;
+ char tbuf[80];
+ int opt = 1;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
+
+ if (cb == NULL) {
+ WPRINTF(("TAP backend requires non-NULL callback\n"));
+ return (-1);
+ }
+
+ strcpy(tbuf, "/dev/");
+ strlcat(tbuf, devname, sizeof(tbuf));
+
+ be->fd = open(tbuf, O_RDWR);
+ if (be->fd == -1) {
+ WPRINTF(("open of tap device %s failed\n", tbuf));
+ goto error;
+ }
+
+ /*
+ * Set non-blocking and register for read
+ * notifications with the event loop
+ */
+ if (ioctl(be->fd, FIONBIO, &opt) < 0) {
+ WPRINTF(("tap device O_NONBLOCK failed\n"));
+ goto error;
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (caph_rights_limit(be->fd, &rights) == -1)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ priv->mevp = mevent_add(be->fd, EVF_READ, cb, param);
+ if (priv->mevp == NULL) {
+ WPRINTF(("Could not register event\n"));
+ goto error;
+ }
+
+ return (0);
+
+error:
+ tap_cleanup(be);
+ return (-1);
+}
+
+/*
+ * Called to send a buffer chain out to the tap device
+ */
+static ssize_t
+tap_send(struct net_backend *be, struct iovec *iov, int iovcnt)
+{
+ return (writev(be->fd, iov, iovcnt));
+}
+
+static ssize_t
+tap_recv(struct net_backend *be, struct iovec *iov, int iovcnt)
+{
+ ssize_t ret;
+
+ /* Should never be called without a valid tap fd */
+ assert(be->fd != -1);
+
+ ret = readv(be->fd, iov, iovcnt);
+
+ if (ret < 0 && errno == EWOULDBLOCK) {
+ return (0);
+ }
+
+ return (ret);
+}
+
+static uint64_t
+tap_get_cap(struct net_backend *be)
+{
+
+ return (0); /* no capabilities for now */
+}
+
+static int
+tap_set_cap(struct net_backend *be, uint64_t features,
+ unsigned vnet_hdr_len)
+{
+
+ return ((features || vnet_hdr_len) ? -1 : 0);
+}
+
+static struct net_backend tap_backend = {
+ .prefix = "tap",
+ .priv_size = sizeof(struct tap_priv),
+ .init = tap_init,
+ .cleanup = tap_cleanup,
+ .send = tap_send,
+ .recv = tap_recv,
+ .get_cap = tap_get_cap,
+ .set_cap = tap_set_cap,
+};
+
+/* A clone of the tap backend, with a different prefix. */
+static struct net_backend vmnet_backend = {
+ .prefix = "vmnet",
+ .priv_size = sizeof(struct tap_priv),
+ .init = tap_init,
+ .cleanup = tap_cleanup,
+ .send = tap_send,
+ .recv = tap_recv,
+ .get_cap = tap_get_cap,
+ .set_cap = tap_set_cap,
+};
+
+DATA_SET(net_backend_set, tap_backend);
+DATA_SET(net_backend_set, vmnet_backend);
+
+/*
+ * The netmap backend
+ */
+
+/* The virtio-net features supported by netmap. */
+#define NETMAP_FEATURES (VIRTIO_NET_F_CSUM | VIRTIO_NET_F_HOST_TSO4 | \
+ VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_UFO | \
+ VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | \
+ VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_UFO)
+
+struct netmap_priv {
+ char ifname[IFNAMSIZ];
+ struct nm_desc *nmd;
+ uint16_t memid;
+ struct netmap_ring *rx;
+ struct netmap_ring *tx;
+ struct mevent *mevp;
+ net_be_rxeof_t cb;
+ void *cb_param;
+};
+
+static void
+nmreq_init(struct nmreq *req, char *ifname)
+{
+
+ memset(req, 0, sizeof(*req));
+ strlcpy(req->nr_name, ifname, sizeof(req->nr_name));
+ req->nr_version = NETMAP_API;
+}
+
+static int
+netmap_set_vnet_hdr_len(struct net_backend *be, int vnet_hdr_len)
+{
+ int err;
+ struct nmreq req;
+ struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
+
+ nmreq_init(&req, priv->ifname);
+ req.nr_cmd = NETMAP_BDG_VNET_HDR;
+ req.nr_arg1 = vnet_hdr_len;
+ err = ioctl(be->fd, NIOCREGIF, &req);
+ if (err) {
+ WPRINTF(("Unable to set vnet header length %d\n",
+ vnet_hdr_len));
+ return (err);
+ }
+
+ be->be_vnet_hdr_len = vnet_hdr_len;
+
+ return (0);
+}
+
+static int
+netmap_has_vnet_hdr_len(struct net_backend *be, unsigned vnet_hdr_len)
+{
+ int prev_hdr_len = be->be_vnet_hdr_len;
+ int ret;
+
+ if (vnet_hdr_len == prev_hdr_len) {
+ return (1);
+ }
+
+ ret = netmap_set_vnet_hdr_len(be, vnet_hdr_len);
+ if (ret) {
+ return (0);
+ }
+
+ netmap_set_vnet_hdr_len(be, prev_hdr_len);
+
+ return (1);
+}
+
+static uint64_t
+netmap_get_cap(struct net_backend *be)
+{
+
+ return (netmap_has_vnet_hdr_len(be, VNET_HDR_LEN) ?
+ NETMAP_FEATURES : 0);
+}
+
+static int
+netmap_set_cap(struct net_backend *be, uint64_t features,
+ unsigned vnet_hdr_len)
+{
+
+ return (netmap_set_vnet_hdr_len(be, vnet_hdr_len));
+}
+
+static int
+netmap_init(struct net_backend *be, const char *devname,
+ net_be_rxeof_t cb, void *param)
+{
+ struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
+
+ strlcpy(priv->ifname, devname, sizeof(priv->ifname));
+ priv->ifname[sizeof(priv->ifname) - 1] = '\0';
+
+ priv->nmd = nm_open(priv->ifname, NULL, NETMAP_NO_TX_POLL, NULL);
+ if (priv->nmd == NULL) {
+ WPRINTF(("Unable to nm_open(): interface '%s', errno (%s)\n",
+ devname, strerror(errno)));
+ free(priv);
+ return (-1);
+ }
+
+ priv->memid = priv->nmd->req.nr_arg2;
+ priv->tx = NETMAP_TXRING(priv->nmd->nifp, 0);
+ priv->rx = NETMAP_RXRING(priv->nmd->nifp, 0);
+ priv->cb = cb;
+ priv->cb_param = param;
+ be->fd = priv->nmd->fd;
+
+ priv->mevp = mevent_add(be->fd, EVF_READ, cb, param);
+ if (priv->mevp == NULL) {
+ WPRINTF(("Could not register event\n"));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+netmap_cleanup(struct net_backend *be)
+{
+ struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
+
+ if (priv->mevp) {
+ mevent_delete(priv->mevp);
+ }
+ if (priv->nmd) {
+ nm_close(priv->nmd);
+ }
+ be->fd = -1;
+}
+
+static ssize_t
+netmap_send(struct net_backend *be, struct iovec *iov,
+ int iovcnt)
+{
+ struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
+ struct netmap_ring *ring;
+ ssize_t totlen = 0;
+ int nm_buf_size;
+ int nm_buf_len;
+ uint32_t head;
+ void *nm_buf;
+ int j;
+
+ ring = priv->tx;
+ head = ring->head;
+ if (head == ring->tail) {
+ WPRINTF(("No space, drop %zu bytes\n", count_iov(iov, iovcnt)));
+ goto txsync;
+ }
+ nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);
+ nm_buf_size = ring->nr_buf_size;
+ nm_buf_len = 0;
+
+ for (j = 0; j < iovcnt; j++) {
+ int iov_frag_size = iov[j].iov_len;
+ void *iov_frag_buf = iov[j].iov_base;
+
+ totlen += iov_frag_size;
+
+ /*
+ * Split each iovec fragment over more netmap slots, if
+ * necessary.
+ */
+ for (;;) {
+ int copylen;
+
+ copylen = iov_frag_size < nm_buf_size ? iov_frag_size : nm_buf_size;
+ memcpy(nm_buf, iov_frag_buf, copylen);
+
+ iov_frag_buf += copylen;
+ iov_frag_size -= copylen;
+ nm_buf += copylen;
+ nm_buf_size -= copylen;
+ nm_buf_len += copylen;
+
+ if (iov_frag_size == 0) {
+ break;
+ }
+
+ ring->slot[head].len = nm_buf_len;
+ ring->slot[head].flags = NS_MOREFRAG;
+ head = nm_ring_next(ring, head);
+ if (head == ring->tail) {
+ /*
+ * We ran out of netmap slots while
+ * splitting the iovec fragments.
+ */
+ WPRINTF(("No space, drop %zu bytes\n",
+ count_iov(iov, iovcnt)));
+ goto txsync;
+ }
+ nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);
+ nm_buf_size = ring->nr_buf_size;
+ nm_buf_len = 0;
+ }
+ }
+
+ /* Complete the last slot, which must not have NS_MOREFRAG set. */
+ ring->slot[head].len = nm_buf_len;
+ ring->slot[head].flags = 0;
+ head = nm_ring_next(ring, head);
+
+ /* Now update ring->head and ring->cur. */
+ ring->head = ring->cur = head;
+txsync:
+ ioctl(be->fd, NIOCTXSYNC, NULL);
+
+ return (totlen);
+}
+
+static ssize_t
+netmap_recv(struct net_backend *be, struct iovec *iov, int iovcnt)
+{
+ struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
+ struct netmap_slot *slot = NULL;
+ struct netmap_ring *ring;
+ void *iov_frag_buf;
+ int iov_frag_size;
+ ssize_t totlen = 0;
+ uint32_t head;
+
+ assert(iovcnt);
+
+ ring = priv->rx;
+ head = ring->head;
+ iov_frag_buf = iov->iov_base;
+ iov_frag_size = iov->iov_len;
+
+ do {
+ int nm_buf_len;
+ void *nm_buf;
+
+ if (head == ring->tail) {
+ return (0);
+ }
+
+ slot = ring->slot + head;
+ nm_buf = NETMAP_BUF(ring, slot->buf_idx);
+ nm_buf_len = slot->len;
+
+ for (;;) {
+ int copylen = nm_buf_len < iov_frag_size ?
+ nm_buf_len : iov_frag_size;
+
+ memcpy(iov_frag_buf, nm_buf, copylen);
+ nm_buf += copylen;
+ nm_buf_len -= copylen;
+ iov_frag_buf += copylen;
+ iov_frag_size -= copylen;
+ totlen += copylen;
+
+ if (nm_buf_len == 0) {
+ break;
+ }
+
+ iov++;
+ iovcnt--;
+ if (iovcnt == 0) {
+ /* No space to receive. */
+ WPRINTF(("Short iov, drop %zd bytes\n",
+ totlen));
+ return (-ENOSPC);
+ }
+ iov_frag_buf = iov->iov_base;
+ iov_frag_size = iov->iov_len;
+ }
+
+ head = nm_ring_next(ring, head);
+
+ } while (slot->flags & NS_MOREFRAG);
+
+ /* Release slots to netmap. */
+ ring->head = ring->cur = head;
+
+ return (totlen);
+}
+
+static struct net_backend netmap_backend = {
+ .prefix = "netmap",
+ .priv_size = sizeof(struct netmap_priv),
+ .init = netmap_init,
+ .cleanup = netmap_cleanup,
+ .send = netmap_send,
+ .recv = netmap_recv,
+ .get_cap = netmap_get_cap,
+ .set_cap = netmap_set_cap,
+};
+
+/* A clone of the netmap backend, with a different prefix. */
+static struct net_backend vale_backend = {
+ .prefix = "vale",
+ .priv_size = sizeof(struct netmap_priv),
+ .init = netmap_init,
+ .cleanup = netmap_cleanup,
+ .send = netmap_send,
+ .recv = netmap_recv,
+ .get_cap = netmap_get_cap,
+ .set_cap = netmap_set_cap,
+};
+
+DATA_SET(net_backend_set, netmap_backend);
+DATA_SET(net_backend_set, vale_backend);
+
+/*
+ * Initialize a backend and attach to the frontend.
+ * This is called during frontend initialization.
+ * @pbe is a pointer to the backend to be initialized
+ * @devname is the backend-name as supplied on the command line,
+ * e.g. -s 2:0,frontend-name,backend-name[,other-args]
+ * @cb is the receive callback supplied by the frontend,
+ * and it is invoked in the event loop when a receive
+ * event is generated in the hypervisor,
+ * @param is a pointer to the frontend, and normally used as
+ * the argument for the callback.
+ */
+int
+netbe_init(struct net_backend **ret, const char *devname, net_be_rxeof_t cb,
+ void *param)
+{
+ struct net_backend **pbe, *nbe, *tbe = NULL;
+ int err;
+
+ /*
+ * Find the network backend that matches the user-provided
+ * device name. net_backend_set is built using a linker set.
+ */
+ SET_FOREACH(pbe, net_backend_set) {
+ if (strncmp(devname, (*pbe)->prefix,
+ strlen((*pbe)->prefix)) == 0) {
+ tbe = *pbe;
+ assert(tbe->init != NULL);
+ assert(tbe->cleanup != NULL);
+ assert(tbe->send != NULL);
+ assert(tbe->recv != NULL);
+ assert(tbe->get_cap != NULL);
+ assert(tbe->set_cap != NULL);
+ break;
+ }
+ }
+
+ *ret = NULL;
+ if (tbe == NULL)
+ return (EINVAL);
+ nbe = calloc(1, sizeof(*nbe) + tbe->priv_size);
+ *nbe = *tbe; /* copy the template */
+ nbe->fd = -1;
+ nbe->sc = param;
+ nbe->be_vnet_hdr_len = 0;
+ nbe->fe_vnet_hdr_len = 0;
+
+ /* Initialize the backend. */
+ err = nbe->init(nbe, devname, cb, param);
+ if (err) {
+ free(nbe);
+ return (err);
+ }
+
+ *ret = nbe;
+
+ return (0);
+}
+
+void
+netbe_cleanup(struct net_backend *be)
+{
+
+ if (be != NULL) {
+ be->cleanup(be);
+ free(be);
+ }
+}
+
+uint64_t
+netbe_get_cap(struct net_backend *be)
+{
+
+ assert(be != NULL);
+ return (be->get_cap(be));
+}
+
+int
+netbe_set_cap(struct net_backend *be, uint64_t features,
+ unsigned vnet_hdr_len)
+{
+ int ret;
+
+ assert(be != NULL);
+
+ /* There are only three valid lengths, i.e., 0, 10 and 12. */
+ if (vnet_hdr_len && vnet_hdr_len != VNET_HDR_LEN
+ && vnet_hdr_len != (VNET_HDR_LEN - sizeof(uint16_t)))
+ return (-1);
+
+ be->fe_vnet_hdr_len = vnet_hdr_len;
+
+ ret = be->set_cap(be, features, vnet_hdr_len);
+ assert(be->be_vnet_hdr_len == 0 ||
+ be->be_vnet_hdr_len == be->fe_vnet_hdr_len);
+
+ return (ret);
+}
+
+static __inline struct iovec *
+iov_trim(struct iovec *iov, int *iovcnt, unsigned int tlen)
+{
+ struct iovec *riov;
+
+ /* XXX short-cut: assume first segment is >= tlen */
+ assert(iov[0].iov_len >= tlen);
+
+ iov[0].iov_len -= tlen;
+ if (iov[0].iov_len == 0) {
+ assert(*iovcnt > 1);
+ *iovcnt -= 1;
+ riov = &iov[1];
+ } else {
+ iov[0].iov_base = (void *)((uintptr_t)iov[0].iov_base + tlen);
+ riov = &iov[0];
+ }
+
+ return (riov);
+}
+
+ssize_t
+netbe_send(struct net_backend *be, struct iovec *iov, int iovcnt)
+{
+
+ assert(be != NULL);
+ if (be->be_vnet_hdr_len != be->fe_vnet_hdr_len) {
+ /*
+ * The frontend uses a virtio-net header, but the backend
+ * does not. We ignore it (as it must be all zeroes) and
+ * strip it.
+ */
+ assert(be->be_vnet_hdr_len == 0);
+ iov = iov_trim(iov, &iovcnt, be->fe_vnet_hdr_len);
+ }
+
+ return (be->send(be, iov, iovcnt));
+}
+
+/*
+ * Try to read a packet from the backend, without blocking.
+ * If no packets are available, return 0. In case of success, return
+ * the length of the packet just read. Return -1 in case of errors.
+ */
+ssize_t
+netbe_recv(struct net_backend *be, struct iovec *iov, int iovcnt)
+{
+ /* Length of prepended virtio-net header. */
+ unsigned int hlen = be->fe_vnet_hdr_len;
+ int ret;
+
+ assert(be != NULL);
+
+ if (hlen && hlen != be->be_vnet_hdr_len) {
+ /*
+ * The frontend uses a virtio-net header, but the backend
+ * does not. We need to prepend a zeroed header.
+ */
+ struct virtio_net_rxhdr *vh;
+
+ assert(be->be_vnet_hdr_len == 0);
+
+ /*
+ * Get a pointer to the rx header, and use the
+ * data immediately following it for the packet buffer.
+ */
+ vh = iov[0].iov_base;
+ iov = iov_trim(iov, &iovcnt, hlen);
+
+ /*
+ * The only valid field in the rx packet header is the
+ * number of buffers if merged rx bufs were negotiated.
+ */
+ memset(vh, 0, hlen);
+ if (hlen == VNET_HDR_LEN) {
+ vh->vrh_bufs = 1;
+ }
+ }
+
+ ret = be->recv(be, iov, iovcnt);
+ if (ret > 0) {
+ ret += hlen;
+ }
+
+ return (ret);
+}
+
+/*
+ * Read a packet from the backend and discard it.
+ * Returns the size of the discarded packet or zero if no packet was available.
+ * A negative error code is returned in case of read error.
+ */
+ssize_t
+netbe_rx_discard(struct net_backend *be)
+{
+ /*
+ * MP note: the dummybuf is only used to discard frames,
+ * so there is no need for it to be per-vtnet or locked.
+ * We only make it large enough for TSO-sized segment.
+ */
+ static uint8_t dummybuf[65536 + 64];
+ struct iovec iov;
+
+ iov.iov_base = dummybuf;
+ iov.iov_len = sizeof(dummybuf);
+
+ return netbe_recv(be, &iov, 1);
+}
+
diff --git a/usr/src/cmd/bhyve/net_backends.h b/usr/src/cmd/bhyve/net_backends.h
new file mode 100644
index 0000000000..bba39db59b
--- /dev/null
+++ b/usr/src/cmd/bhyve/net_backends.h
@@ -0,0 +1,89 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Vincenzo Maffione <vmaffione@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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __NET_BACKENDS_H__
+#define __NET_BACKENDS_H__
+
+#include <stdint.h>
+
+/* Opaque type representing a network backend. */
+typedef struct net_backend net_backend_t;
+
+/* Interface between network frontends and the network backends. */
+typedef void (*net_be_rxeof_t)(int, enum ev_type, void *param);
+int netbe_init(net_backend_t **be, const char *devname, net_be_rxeof_t cb,
+ void *param);
+void netbe_cleanup(net_backend_t *be);
+uint64_t netbe_get_cap(net_backend_t *be);
+int netbe_set_cap(net_backend_t *be, uint64_t cap,
+ unsigned vnet_hdr_len);
+ssize_t netbe_send(net_backend_t *be, struct iovec *iov, int iovcnt);
+ssize_t netbe_recv(net_backend_t *be, struct iovec *iov, int iovcnt);
+ssize_t netbe_rx_discard(net_backend_t *be);
+
+
+/*
+ * Network device capabilities taken from the VirtIO standard.
+ * Despite the name, these capabilities can be used by different frontents
+ * (virtio-net, ptnet) and supported by different backends (netmap, tap, ...).
+ */
+#define VIRTIO_NET_F_CSUM (1 << 0) /* host handles partial cksum */
+#define VIRTIO_NET_F_GUEST_CSUM (1 << 1) /* guest handles partial cksum */
+#define VIRTIO_NET_F_MAC (1 << 5) /* host supplies MAC */
+#define VIRTIO_NET_F_GSO_DEPREC (1 << 6) /* deprecated: host handles GSO */
+#define VIRTIO_NET_F_GUEST_TSO4 (1 << 7) /* guest can rcv TSOv4 */
+#define VIRTIO_NET_F_GUEST_TSO6 (1 << 8) /* guest can rcv TSOv6 */
+#define VIRTIO_NET_F_GUEST_ECN (1 << 9) /* guest can rcv TSO with ECN */
+#define VIRTIO_NET_F_GUEST_UFO (1 << 10) /* guest can rcv UFO */
+#define VIRTIO_NET_F_HOST_TSO4 (1 << 11) /* host can rcv TSOv4 */
+#define VIRTIO_NET_F_HOST_TSO6 (1 << 12) /* host can rcv TSOv6 */
+#define VIRTIO_NET_F_HOST_ECN (1 << 13) /* host can rcv TSO with ECN */
+#define VIRTIO_NET_F_HOST_UFO (1 << 14) /* host can rcv UFO */
+#define VIRTIO_NET_F_MRG_RXBUF (1 << 15) /* host can merge RX buffers */
+#define VIRTIO_NET_F_STATUS (1 << 16) /* config status field available */
+#define VIRTIO_NET_F_CTRL_VQ (1 << 17) /* control channel available */
+#define VIRTIO_NET_F_CTRL_RX (1 << 18) /* control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_VLAN (1 << 19) /* control channel VLAN filtering */
+#define VIRTIO_NET_F_GUEST_ANNOUNCE \
+ (1 << 21) /* guest can send gratuitous pkts */
+
+/*
+ * Fixed network header size
+ */
+struct virtio_net_rxhdr {
+ uint8_t vrh_flags;
+ uint8_t vrh_gso_type;
+ uint16_t vrh_hdr_len;
+ uint16_t vrh_gso_size;
+ uint16_t vrh_csum_start;
+ uint16_t vrh_csum_offset;
+ uint16_t vrh_bufs;
+} __packed;
+
+#endif /* __NET_BACKENDS_H__ */
diff --git a/usr/src/cmd/bhyve/net_utils.c b/usr/src/cmd/bhyve/net_utils.c
new file mode 100644
index 0000000000..a7ae4d2eef
--- /dev/null
+++ b/usr/src/cmd/bhyve/net_utils.c
@@ -0,0 +1,89 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2011 NetApp, Inc.
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <net/ethernet.h>
+
+#include <errno.h>
+#include <md5.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bhyverun.h"
+#include "net_utils.h"
+
+int
+net_parsemac(char *mac_str, uint8_t *mac_addr)
+{
+ struct ether_addr *ea;
+ char *tmpstr;
+ char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
+
+ tmpstr = strsep(&mac_str,"=");
+
+ if ((mac_str != NULL) && (!strcmp(tmpstr,"mac"))) {
+ ea = ether_aton(mac_str);
+
+ if (ea == NULL || ETHER_IS_MULTICAST(ea->octet) ||
+ memcmp(ea->octet, zero_addr, ETHER_ADDR_LEN) == 0) {
+ fprintf(stderr, "Invalid MAC %s\n", mac_str);
+ return (EINVAL);
+ } else
+ memcpy(mac_addr, ea->octet, ETHER_ADDR_LEN);
+ }
+
+ return (0);
+}
+
+void
+net_genmac(struct pci_devinst *pi, uint8_t *macaddr)
+{
+ /*
+ * The default MAC address is the standard NetApp OUI of 00-a0-98,
+ * followed by an MD5 of the PCI slot/func number and dev name
+ */
+ MD5_CTX mdctx;
+ unsigned char digest[16];
+ char nstr[80];
+
+ snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
+ pi->pi_func, vmname);
+
+ MD5Init(&mdctx);
+ MD5Update(&mdctx, nstr, (unsigned int)strlen(nstr));
+ MD5Final(digest, &mdctx);
+
+ macaddr[0] = 0x00;
+ macaddr[1] = 0xa0;
+ macaddr[2] = 0x98;
+ macaddr[3] = digest[0];
+ macaddr[4] = digest[1];
+ macaddr[5] = digest[2];
+}
diff --git a/usr/src/cmd/bhyve/net_utils.h b/usr/src/cmd/bhyve/net_utils.h
new file mode 100644
index 0000000000..3c83519931
--- /dev/null
+++ b/usr/src/cmd/bhyve/net_utils.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Vincenzo Maffione <v.maffione@gmail.com>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET_UTILS_H_
+#define _NET_UTILS_H_
+
+#include <stdint.h>
+#include "pci_emul.h"
+
+void net_genmac(struct pci_devinst *pi, uint8_t *macaddr);
+int net_parsemac(char *mac_str, uint8_t *mac_addr);
+
+#endif /* _NET_UTILS_H_ */
diff --git a/usr/src/cmd/bhyve/pci_e82545.c b/usr/src/cmd/bhyve/pci_e82545.c
index e211b5cf9c..62a647e43e 100644
--- a/usr/src/cmd/bhyve/pci_e82545.c
+++ b/usr/src/cmd/bhyve/pci_e82545.c
@@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$");
#include "bhyverun.h"
#include "pci_emul.h"
#include "mevent.h"
+#include "net_utils.h"
/* Hardware/register definitions XXX: move some to common code. */
#define E82545_VENDOR_ID_INTEL 0x8086
@@ -2286,38 +2287,16 @@ e82545_open_tap(struct e82545_softc *sc, char *opts)
}
static int
-e82545_parsemac(char *mac_str, uint8_t *mac_addr)
-{
- struct ether_addr *ea;
- char *tmpstr;
- char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
-
- tmpstr = strsep(&mac_str,"=");
- if ((mac_str != NULL) && (!strcmp(tmpstr,"mac"))) {
- ea = ether_aton(mac_str);
- if (ea == NULL || ETHER_IS_MULTICAST(ea->octet) ||
- memcmp(ea->octet, zero_addr, ETHER_ADDR_LEN) == 0) {
- fprintf(stderr, "Invalid MAC %s\n", mac_str);
- return (1);
- } else
- memcpy(mac_addr, ea->octet, ETHER_ADDR_LEN);
- }
- return (0);
-}
-
-static int
e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
{
- DPRINTF("Loading with options: %s\r\n", opts);
-
- MD5_CTX mdctx;
- unsigned char digest[16];
char nstr[80];
struct e82545_softc *sc;
char *devname;
char *vtopts;
int mac_provided;
+ DPRINTF("Loading with options: %s\r\n", opts);
+
/* Setup our softc */
sc = calloc(1, sizeof(*sc));
@@ -2367,7 +2346,7 @@ e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
(void) strsep(&vtopts, ",");
if (vtopts != NULL) {
- err = e82545_parsemac(vtopts, sc->esc_mac.octet);
+ err = net_parsemac(vtopts, sc->esc_mac.octet);
if (err != 0) {
free(devname);
return (err);
@@ -2382,24 +2361,8 @@ e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
free(devname);
}
- /*
- * The default MAC address is the standard NetApp OUI of 00-a0-98,
- * followed by an MD5 of the PCI slot/func number and dev name
- */
if (!mac_provided) {
- snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
- pi->pi_func, vmname);
-
- MD5Init(&mdctx);
- MD5Update(&mdctx, nstr, strlen(nstr));
- MD5Final(digest, &mdctx);
-
- sc->esc_mac.octet[0] = 0x00;
- sc->esc_mac.octet[1] = 0xa0;
- sc->esc_mac.octet[2] = 0x98;
- sc->esc_mac.octet[3] = digest[0];
- sc->esc_mac.octet[4] = digest[1];
- sc->esc_mac.octet[5] = digest[2];
+ net_genmac(pi, sc->esc_mac.octet);
}
/* H/w initiated reset */
diff --git a/usr/src/cmd/bhyve/pci_emul.c b/usr/src/cmd/bhyve/pci_emul.c
index a71cc528aa..771cf4e77e 100644
--- a/usr/src/cmd/bhyve/pci_emul.c
+++ b/usr/src/cmd/bhyve/pci_emul.c
@@ -600,6 +600,7 @@ pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
uint64_t *baseptr = NULL;
uint64_t limit = 0, lobits = 0;
uint64_t addr, mask, bar;
+ uint16_t cmd, enbit;
int error;
assert(idx >= 0 && idx <= PCI_BARMAX);
@@ -619,13 +620,14 @@ pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
switch (type) {
case PCIBAR_NONE:
baseptr = NULL;
- addr = mask = lobits = 0;
+ addr = mask = lobits = enbit = 0;
break;
case PCIBAR_IO:
baseptr = &pci_emul_iobase;
limit = PCI_EMUL_IOLIMIT;
mask = PCIM_BAR_IO_BASE;
lobits = PCIM_BAR_IO_SPACE;
+ enbit = PCIM_CMD_PORTEN;
break;
case PCIBAR_MEM64:
/*
@@ -647,19 +649,20 @@ pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
mask = PCIM_BAR_MEM_BASE;
lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
PCIM_BAR_MEM_PREFETCH;
- break;
} else {
baseptr = &pci_emul_membase32;
limit = PCI_EMUL_MEMLIMIT32;
mask = PCIM_BAR_MEM_BASE;
lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
}
+ enbit = PCIM_CMD_MEMEN;
break;
case PCIBAR_MEM32:
baseptr = &pci_emul_membase32;
limit = PCI_EMUL_MEMLIMIT32;
mask = PCIM_BAR_MEM_BASE;
lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
+ enbit = PCIM_CMD_MEMEN;
break;
default:
printf("pci_emul_alloc_base: invalid bar type %d\n", type);
@@ -690,6 +693,9 @@ pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
}
+ cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
+ if ((cmd & enbit) != enbit)
+ pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
register_bar(pdi, idx);
return (0);
@@ -775,8 +781,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
- pci_set_cfgdata8(pdi, PCIR_COMMAND,
- PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
+ pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN);
err = (*pde->pe_init)(ctx, pdi, fi->fi_param);
if (err == 0)
@@ -966,15 +971,23 @@ pci_emul_add_pciecap(struct pci_devinst *pi, int type)
int err;
struct pciecap pciecap;
- if (type != PCIEM_TYPE_ROOT_PORT)
- return (-1);
-
bzero(&pciecap, sizeof(pciecap));
+ /*
+ * Use the integrated endpoint type for endpoints on a root complex bus.
+ *
+ * NB: bhyve currently only supports a single PCI bus that is the root
+ * complex bus, so all endpoints are integrated.
+ */
+ if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0))
+ type = PCIEM_TYPE_ROOT_INT_EP;
+
pciecap.capid = PCIY_EXPRESS;
- pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT;
- pciecap.link_capabilities = 0x411; /* gen1, x1 */
- pciecap.link_status = 0x11; /* gen1, x1 */
+ pciecap.pcie_capabilities = PCIECAP_VERSION | type;
+ if (type != PCIEM_TYPE_ROOT_INT_EP) {
+ pciecap.link_capabilities = 0x411; /* gen1, x1 */
+ pciecap.link_status = 0x11; /* gen1, x1 */
+ }
err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
return (err);
@@ -1697,31 +1710,18 @@ pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv)
}
}
-static void
-pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
+/*
+ * Update device state in response to changes to the PCI command
+ * register.
+ */
+void
+pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old)
{
- int i, rshift;
- uint32_t cmd, cmd2, changed, old, readonly;
-
- cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */
-
- /*
- * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
- *
- * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
- * 'write 1 to clear'. However these bits are not set to '1' by
- * any device emulation so it is simpler to treat them as readonly.
- */
- rshift = (coff & 0x3) * 8;
- readonly = 0xFFFFF880 >> rshift;
-
- old = CFGREAD(pi, coff, bytes);
- new &= ~readonly;
- new |= (old & readonly);
- CFGWRITE(pi, coff, new, bytes); /* update config */
+ int i;
+ uint16_t changed, new;
- cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */
- changed = cmd ^ cmd2;
+ new = pci_get_cfgdata16(pi, PCIR_COMMAND);
+ changed = old ^ new;
/*
* If the MMIO or I/O address space decoding has changed then
@@ -1735,7 +1735,7 @@ pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
case PCIBAR_IO:
/* I/O address space decoding changed? */
if (changed & PCIM_CMD_PORTEN) {
- if (porten(pi))
+ if (new & PCIM_CMD_PORTEN)
register_bar(pi, i);
else
unregister_bar(pi, i);
@@ -1745,7 +1745,7 @@ pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
case PCIBAR_MEM64:
/* MMIO address space decoding changed? */
if (changed & PCIM_CMD_MEMEN) {
- if (memen(pi))
+ if (new & PCIM_CMD_MEMEN)
register_bar(pi, i);
else
unregister_bar(pi, i);
@@ -1764,6 +1764,32 @@ pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
}
static void
+pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
+{
+ int rshift;
+ uint32_t cmd, old, readonly;
+
+ cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */
+
+ /*
+ * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
+ *
+ * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
+ * 'write 1 to clear'. However these bits are not set to '1' by
+ * any device emulation so it is simpler to treat them as readonly.
+ */
+ rshift = (coff & 0x3) * 8;
+ readonly = 0xFFFFF880 >> rshift;
+
+ old = CFGREAD(pi, coff, bytes);
+ new &= ~readonly;
+ new |= (old & readonly);
+ CFGWRITE(pi, coff, new, bytes); /* update config */
+
+ pci_emul_cmd_changed(pi, cmd);
+}
+
+static void
pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
int coff, int bytes, uint32_t *eax)
{
diff --git a/usr/src/cmd/bhyve/pci_emul.h b/usr/src/cmd/bhyve/pci_emul.h
index 0053caed99..51de897543 100644
--- a/usr/src/cmd/bhyve/pci_emul.h
+++ b/usr/src/cmd/bhyve/pci_emul.h
@@ -230,6 +230,7 @@ int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx,
uint64_t hostbase, enum pcibar_type type, uint64_t size);
int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum);
int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type);
+void pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old);
void pci_generate_msi(struct pci_devinst *pi, int msgnum);
void pci_generate_msix(struct pci_devinst *pi, int msgnum);
void pci_lintr_assert(struct pci_devinst *pi);
diff --git a/usr/src/cmd/bhyve/pci_fbuf.c b/usr/src/cmd/bhyve/pci_fbuf.c
index 8d24dde9da..1b2eb03b9b 100644
--- a/usr/src/cmd/bhyve/pci_fbuf.c
+++ b/usr/src/cmd/bhyve/pci_fbuf.c
@@ -229,15 +229,13 @@ pci_fbuf_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
static int
pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
{
- char *uopts, *xopts, *config;
+ char *uopts, *uoptsbak, *xopts, *config;
char *tmpstr;
int ret;
ret = 0;
- uopts = strdup(opts);
- for (xopts = strtok(uopts, ",");
- xopts != NULL;
- xopts = strtok(NULL, ",")) {
+ uoptsbak = uopts = strdup(opts);
+ while ((xopts = strsep(&uopts, ",")) != NULL) {
if (strcmp(xopts, "wait") == 0) {
sc->rfb_wait = 1;
continue;
@@ -264,7 +262,7 @@ pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
if (config) {
if (tmpstr[0] == '[')
tmpstr++;
- sc->rfb_host = tmpstr;
+ sc->rfb_host = strdup(tmpstr);
if (config[0] == ':')
config++;
else {
@@ -280,12 +278,12 @@ pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
sc->rfb_port = atoi(tmpstr);
else {
sc->rfb_port = atoi(config);
- sc->rfb_host = tmpstr;
+ sc->rfb_host = strdup(tmpstr);
}
}
#ifndef __FreeBSD__
} else if (!strcmp(xopts, "unix")) {
- sc->rfb_unix = config;
+ sc->rfb_unix = strdup(config);
#endif
} else if (!strcmp(xopts, "vga")) {
if (!strcmp(config, "off")) {
@@ -318,7 +316,7 @@ pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
} else if (sc->memregs.height == 0)
sc->memregs.height = 1080;
} else if (!strcmp(xopts, "password")) {
- sc->rfb_password = config;
+ sc->rfb_password = strdup(config);
} else {
pci_fbuf_usage(xopts);
ret = -1;
@@ -327,6 +325,7 @@ pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
}
done:
+ free(uoptsbak);
return (ret);
}
diff --git a/usr/src/cmd/bhyve/pci_hda.c b/usr/src/cmd/bhyve/pci_hda.c
new file mode 100644
index 0000000000..e0324f46a9
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_hda.c
@@ -0,0 +1,1331 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
+ * 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 ``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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <time.h>
+
+#include "pci_hda.h"
+#include "bhyverun.h"
+#include "pci_emul.h"
+#include "hdac_reg.h"
+
+/*
+ * HDA defines
+ */
+#define PCIR_HDCTL 0x40
+#define INTEL_VENDORID 0x8086
+#define HDA_INTEL_82801G 0x27d8
+
+#define HDA_IOSS_NO 0x08
+#define HDA_OSS_NO 0x04
+#define HDA_ISS_NO 0x04
+#define HDA_CODEC_MAX 0x0f
+#define HDA_LAST_OFFSET \
+ (0x2084 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20))
+#define HDA_SET_REG_TABLE_SZ \
+ (0x80 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20))
+#define HDA_CORB_ENTRY_LEN 0x04
+#define HDA_RIRB_ENTRY_LEN 0x08
+#define HDA_BDL_ENTRY_LEN 0x10
+#define HDA_DMA_PIB_ENTRY_LEN 0x08
+#define HDA_STREAM_TAGS_CNT 0x10
+#define HDA_STREAM_REGS_BASE 0x80
+#define HDA_STREAM_REGS_LEN 0x20
+
+#define HDA_DMA_ACCESS_LEN (sizeof(uint32_t))
+#define HDA_BDL_MAX_LEN 0x0100
+
+#define HDAC_SDSTS_FIFORDY (1 << 5)
+
+#define HDA_RIRBSTS_IRQ_MASK (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS)
+#define HDA_STATESTS_IRQ_MASK ((1 << HDA_CODEC_MAX) - 1)
+#define HDA_SDSTS_IRQ_MASK \
+ (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS)
+
+/*
+ * HDA data structures
+ */
+
+struct hda_softc;
+
+typedef void (*hda_set_reg_handler)(struct hda_softc *sc, uint32_t offset,
+ uint32_t old);
+
+struct hda_bdle {
+ uint32_t addrl;
+ uint32_t addrh;
+ uint32_t len;
+ uint32_t ioc;
+} __packed;
+
+struct hda_bdle_desc {
+ void *addr;
+ uint8_t ioc;
+ uint32_t len;
+};
+
+struct hda_codec_cmd_ctl {
+ char *name;
+ void *dma_vaddr;
+ uint8_t run;
+ uint16_t rp;
+ uint16_t size;
+ uint16_t wp;
+};
+
+struct hda_stream_desc {
+ uint8_t dir;
+ uint8_t run;
+ uint8_t stream;
+
+ /* bp is the no. of bytes transferred in the current bdle */
+ uint32_t bp;
+ /* be is the no. of bdles transferred in the bdl */
+ uint32_t be;
+
+ uint32_t bdl_cnt;
+ struct hda_bdle_desc bdl[HDA_BDL_MAX_LEN];
+};
+
+struct hda_softc {
+ struct pci_devinst *pci_dev;
+ uint32_t regs[HDA_LAST_OFFSET];
+
+ uint8_t lintr;
+ uint8_t rirb_cnt;
+ uint64_t wall_clock_start;
+
+ struct hda_codec_cmd_ctl corb;
+ struct hda_codec_cmd_ctl rirb;
+
+ uint8_t codecs_no;
+ struct hda_codec_inst *codecs[HDA_CODEC_MAX];
+
+ /* Base Address of the DMA Position Buffer */
+ void *dma_pib_vaddr;
+
+ struct hda_stream_desc streams[HDA_IOSS_NO];
+ /* 2 tables for output and input */
+ uint8_t stream_map[2][HDA_STREAM_TAGS_CNT];
+};
+
+/*
+ * HDA module function declarations
+ */
+static inline void hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset,
+ uint32_t value);
+static inline uint32_t hda_get_reg_by_offset(struct hda_softc *sc,
+ uint32_t offset);
+static inline void hda_set_field_by_offset(struct hda_softc *sc,
+ uint32_t offset, uint32_t mask, uint32_t value);
+
+static uint8_t hda_parse_config(const char *opts, const char *key, char *val);
+static struct hda_softc *hda_init(const char *opts);
+static void hda_update_intr(struct hda_softc *sc);
+static void hda_response_interrupt(struct hda_softc *sc);
+static int hda_codec_constructor(struct hda_softc *sc,
+ struct hda_codec_class *codec, const char *play, const char *rec,
+ const char *opts);
+static struct hda_codec_class *hda_find_codec_class(const char *name);
+
+static int hda_send_command(struct hda_softc *sc, uint32_t verb);
+static int hda_notify_codecs(struct hda_softc *sc, uint8_t run,
+ uint8_t stream, uint8_t dir);
+static void hda_reset(struct hda_softc *sc);
+static void hda_reset_regs(struct hda_softc *sc);
+static void hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind);
+static int hda_stream_start(struct hda_softc *sc, uint8_t stream_ind);
+static int hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind);
+static uint32_t hda_read(struct hda_softc *sc, uint32_t offset);
+static int hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size,
+ uint32_t value);
+
+static inline void hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p);
+static int hda_corb_start(struct hda_softc *sc);
+static int hda_corb_run(struct hda_softc *sc);
+static int hda_rirb_start(struct hda_softc *sc);
+
+static void *hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr,
+ size_t len);
+static void hda_dma_st_dword(void *dma_vaddr, uint32_t data);
+static uint32_t hda_dma_ld_dword(void *dma_vaddr);
+
+static inline uint8_t hda_get_stream_by_offsets(uint32_t offset,
+ uint8_t reg_offset);
+static inline uint32_t hda_get_offset_stream(uint8_t stream_ind);
+
+static void hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
+static void hda_set_statests(struct hda_softc *sc, uint32_t offset,
+ uint32_t old);
+static void hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old);
+static void hda_set_corbctl(struct hda_softc *sc, uint32_t offset,
+ uint32_t old);
+static void hda_set_rirbctl(struct hda_softc *sc, uint32_t offset,
+ uint32_t old);
+static void hda_set_rirbsts(struct hda_softc *sc, uint32_t offset,
+ uint32_t old);
+static void hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset,
+ uint32_t old);
+static void hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
+static void hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old);
+static void hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old);
+
+static int hda_signal_state_change(struct hda_codec_inst *hci);
+static int hda_response(struct hda_codec_inst *hci, uint32_t response,
+ uint8_t unsol);
+static int hda_transfer(struct hda_codec_inst *hci, uint8_t stream,
+ uint8_t dir, void *buf, size_t count);
+
+static void hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib);
+static uint64_t hda_get_clock_ns(void);
+
+/*
+ * PCI HDA function declarations
+ */
+static int pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts);
+static void pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value);
+static uint64_t pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size);
+/*
+ * HDA global data
+ */
+
+static const hda_set_reg_handler hda_set_reg_table[] = {
+ [HDAC_GCTL] = hda_set_gctl,
+ [HDAC_STATESTS] = hda_set_statests,
+ [HDAC_CORBWP] = hda_set_corbwp,
+ [HDAC_CORBCTL] = hda_set_corbctl,
+ [HDAC_RIRBCTL] = hda_set_rirbctl,
+ [HDAC_RIRBSTS] = hda_set_rirbsts,
+ [HDAC_DPIBLBASE] = hda_set_dpiblbase,
+
+#define HDAC_ISTREAM(n, iss, oss) \
+ [_HDAC_ISDCTL(n, iss, oss)] = hda_set_sdctl, \
+ [_HDAC_ISDCTL(n, iss, oss) + 2] = hda_set_sdctl2, \
+ [_HDAC_ISDSTS(n, iss, oss)] = hda_set_sdsts, \
+
+#define HDAC_OSTREAM(n, iss, oss) \
+ [_HDAC_OSDCTL(n, iss, oss)] = hda_set_sdctl, \
+ [_HDAC_OSDCTL(n, iss, oss) + 2] = hda_set_sdctl2, \
+ [_HDAC_OSDSTS(n, iss, oss)] = hda_set_sdsts, \
+
+ HDAC_ISTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
+ HDAC_ISTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
+ HDAC_ISTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
+ HDAC_ISTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
+
+ HDAC_OSTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
+ HDAC_OSTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
+ HDAC_OSTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
+ HDAC_OSTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
+
+ [HDA_SET_REG_TABLE_SZ] = NULL,
+};
+
+static const uint16_t hda_corb_sizes[] = {
+ [HDAC_CORBSIZE_CORBSIZE_2] = 2,
+ [HDAC_CORBSIZE_CORBSIZE_16] = 16,
+ [HDAC_CORBSIZE_CORBSIZE_256] = 256,
+ [HDAC_CORBSIZE_CORBSIZE_MASK] = 0,
+};
+
+static const uint16_t hda_rirb_sizes[] = {
+ [HDAC_RIRBSIZE_RIRBSIZE_2] = 2,
+ [HDAC_RIRBSIZE_RIRBSIZE_16] = 16,
+ [HDAC_RIRBSIZE_RIRBSIZE_256] = 256,
+ [HDAC_RIRBSIZE_RIRBSIZE_MASK] = 0,
+};
+
+static struct hda_ops hops = {
+ .signal = hda_signal_state_change,
+ .response = hda_response,
+ .transfer = hda_transfer,
+};
+
+struct pci_devemu pci_de_hda = {
+ .pe_emu = "hda",
+ .pe_init = pci_hda_init,
+ .pe_barwrite = pci_hda_write,
+ .pe_barread = pci_hda_read
+};
+
+PCI_EMUL_SET(pci_de_hda);
+
+SET_DECLARE(hda_codec_class_set, struct hda_codec_class);
+
+#if DEBUG_HDA == 1
+FILE *dbg;
+#endif
+
+/*
+ * HDA module function definitions
+ */
+
+static inline void
+hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t value)
+{
+ assert(offset < HDA_LAST_OFFSET);
+ sc->regs[offset] = value;
+}
+
+static inline uint32_t
+hda_get_reg_by_offset(struct hda_softc *sc, uint32_t offset)
+{
+ assert(offset < HDA_LAST_OFFSET);
+ return sc->regs[offset];
+}
+
+static inline void
+hda_set_field_by_offset(struct hda_softc *sc, uint32_t offset,
+ uint32_t mask, uint32_t value)
+{
+ uint32_t reg_value = 0;
+
+ reg_value = hda_get_reg_by_offset(sc, offset);
+
+ reg_value &= ~mask;
+ reg_value |= (value & mask);
+
+ hda_set_reg_by_offset(sc, offset, reg_value);
+}
+
+static uint8_t
+hda_parse_config(const char *opts, const char *key, char *val)
+{
+ char buf[64];
+ char *s = buf;
+ char *tmp = NULL;
+ size_t len;
+ int i;
+
+ if (!opts)
+ return (0);
+
+ len = strlen(opts);
+ if (len >= sizeof(buf)) {
+ DPRINTF("Opts too big\n");
+ return (0);
+ }
+
+ DPRINTF("opts: %s\n", opts);
+
+ strcpy(buf, opts);
+
+ for (i = 0; i < len; i++)
+ if (buf[i] == ',') {
+ buf[i] = 0;
+ tmp = buf + i + 1;
+ break;
+ }
+
+ if (!memcmp(s, key, strlen(key))) {
+ strncpy(val, s + strlen(key), 64);
+ return (1);
+ }
+
+ if (!tmp)
+ return (0);
+
+ s = tmp;
+ if (!memcmp(s, key, strlen(key))) {
+ strncpy(val, s + strlen(key), 64);
+ return (1);
+ }
+
+ return (0);
+}
+
+static struct hda_softc *
+hda_init(const char *opts)
+{
+ struct hda_softc *sc = NULL;
+ struct hda_codec_class *codec = NULL;
+ char play[64];
+ char rec[64];
+ int err, p, r;
+
+#if DEBUG_HDA == 1
+ dbg = fopen("/tmp/bhyve_hda.log", "w+");
+#endif
+
+ DPRINTF("opts: %s\n", opts);
+
+ sc = calloc(1, sizeof(*sc));
+ if (!sc)
+ return (NULL);
+
+ hda_reset_regs(sc);
+
+ /*
+ * TODO search all the codecs declared in opts
+ * For now we play with one single codec
+ */
+ codec = hda_find_codec_class("hda_codec");
+ if (codec) {
+ p = hda_parse_config(opts, "play=", play);
+ r = hda_parse_config(opts, "rec=", rec);
+ DPRINTF("play: %s rec: %s\n", play, rec);
+ if (p | r) {
+ err = hda_codec_constructor(sc, codec, p ? \
+ play : NULL, r ? rec : NULL, NULL);
+ assert(!err);
+ }
+ }
+
+ return (sc);
+}
+
+static void
+hda_update_intr(struct hda_softc *sc)
+{
+ struct pci_devinst *pi = sc->pci_dev;
+ uint32_t intctl = hda_get_reg_by_offset(sc, HDAC_INTCTL);
+ uint32_t intsts = 0;
+ uint32_t sdsts = 0;
+ uint32_t rirbsts = 0;
+ uint32_t wakeen = 0;
+ uint32_t statests = 0;
+ uint32_t off = 0;
+ int i;
+
+ /* update the CIS bits */
+ rirbsts = hda_get_reg_by_offset(sc, HDAC_RIRBSTS);
+ if (rirbsts & (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS))
+ intsts |= HDAC_INTSTS_CIS;
+
+ wakeen = hda_get_reg_by_offset(sc, HDAC_WAKEEN);
+ statests = hda_get_reg_by_offset(sc, HDAC_STATESTS);
+ if (statests & wakeen)
+ intsts |= HDAC_INTSTS_CIS;
+
+ /* update the SIS bits */
+ for (i = 0; i < HDA_IOSS_NO; i++) {
+ off = hda_get_offset_stream(i);
+ sdsts = hda_get_reg_by_offset(sc, off + HDAC_SDSTS);
+ if (sdsts & HDAC_SDSTS_BCIS)
+ intsts |= (1 << i);
+ }
+
+ /* update the GIS bit */
+ if (intsts)
+ intsts |= HDAC_INTSTS_GIS;
+
+ hda_set_reg_by_offset(sc, HDAC_INTSTS, intsts);
+
+ if ((intctl & HDAC_INTCTL_GIE) && ((intsts & \
+ ~HDAC_INTSTS_GIS) & intctl)) {
+ if (!sc->lintr) {
+ pci_lintr_assert(pi);
+ sc->lintr = 1;
+ }
+ } else {
+ if (sc->lintr) {
+ pci_lintr_deassert(pi);
+ sc->lintr = 0;
+ }
+ }
+}
+
+static void
+hda_response_interrupt(struct hda_softc *sc)
+{
+ uint8_t rirbctl = hda_get_reg_by_offset(sc, HDAC_RIRBCTL);
+
+ if ((rirbctl & HDAC_RIRBCTL_RINTCTL) && sc->rirb_cnt) {
+ sc->rirb_cnt = 0;
+ hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL,
+ HDAC_RIRBSTS_RINTFL);
+ hda_update_intr(sc);
+ }
+}
+
+static int
+hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec,
+ const char *play, const char *rec, const char *opts)
+{
+ struct hda_codec_inst *hci = NULL;
+
+ if (sc->codecs_no >= HDA_CODEC_MAX)
+ return (-1);
+
+ hci = calloc(1, sizeof(struct hda_codec_inst));
+ if (!hci)
+ return (-1);
+
+ hci->hda = sc;
+ hci->hops = &hops;
+ hci->cad = sc->codecs_no;
+ hci->codec = codec;
+
+ sc->codecs[sc->codecs_no++] = hci;
+
+ if (!codec->init) {
+ DPRINTF("This codec does not implement the init function\n");
+ return (-1);
+ }
+
+ return (codec->init(hci, play, rec, opts));
+}
+
+static struct hda_codec_class *
+hda_find_codec_class(const char *name)
+{
+ struct hda_codec_class **pdpp = NULL, *pdp = NULL;
+
+ SET_FOREACH(pdpp, hda_codec_class_set) {
+ pdp = *pdpp;
+ if (!strcmp(pdp->name, name)) {
+ return (pdp);
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+hda_send_command(struct hda_softc *sc, uint32_t verb)
+{
+ struct hda_codec_inst *hci = NULL;
+ struct hda_codec_class *codec = NULL;
+ uint8_t cad = (verb >> HDA_CMD_CAD_SHIFT) & 0x0f;
+
+ hci = sc->codecs[cad];
+ if (!hci)
+ return (-1);
+
+ DPRINTF("cad: 0x%x verb: 0x%x\n", cad, verb);
+
+ codec = hci->codec;
+ assert(codec);
+
+ if (!codec->command) {
+ DPRINTF("This codec does not implement the command function\n");
+ return (-1);
+ }
+
+ return (codec->command(hci, verb));
+}
+
+static int
+hda_notify_codecs(struct hda_softc *sc, uint8_t run, uint8_t stream,
+ uint8_t dir)
+{
+ struct hda_codec_inst *hci = NULL;
+ struct hda_codec_class *codec = NULL;
+ int err;
+ int i;
+
+ /* Notify each codec */
+ for (i = 0; i < sc->codecs_no; i++) {
+ hci = sc->codecs[i];
+ assert(hci);
+
+ codec = hci->codec;
+ assert(codec);
+
+ if (codec->notify) {
+ err = codec->notify(hci, run, stream, dir);
+ if (!err)
+ break;
+ }
+ }
+
+ return (i == sc->codecs_no ? (-1) : 0);
+}
+
+static void
+hda_reset(struct hda_softc *sc)
+{
+ int i;
+ struct hda_codec_inst *hci = NULL;
+ struct hda_codec_class *codec = NULL;
+
+ hda_reset_regs(sc);
+
+ /* Reset each codec */
+ for (i = 0; i < sc->codecs_no; i++) {
+ hci = sc->codecs[i];
+ assert(hci);
+
+ codec = hci->codec;
+ assert(codec);
+
+ if (codec->reset)
+ codec->reset(hci);
+ }
+
+ sc->wall_clock_start = hda_get_clock_ns();
+}
+
+static void
+hda_reset_regs(struct hda_softc *sc)
+{
+ uint32_t off = 0;
+ uint8_t i;
+
+ DPRINTF("Reset the HDA controller registers ...\n");
+
+ memset(sc->regs, 0, sizeof(sc->regs));
+
+ hda_set_reg_by_offset(sc, HDAC_GCAP,
+ HDAC_GCAP_64OK |
+ (HDA_ISS_NO << HDAC_GCAP_ISS_SHIFT) |
+ (HDA_OSS_NO << HDAC_GCAP_OSS_SHIFT));
+ hda_set_reg_by_offset(sc, HDAC_VMAJ, 0x01);
+ hda_set_reg_by_offset(sc, HDAC_OUTPAY, 0x3c);
+ hda_set_reg_by_offset(sc, HDAC_INPAY, 0x1d);
+ hda_set_reg_by_offset(sc, HDAC_CORBSIZE,
+ HDAC_CORBSIZE_CORBSZCAP_256 | HDAC_CORBSIZE_CORBSIZE_256);
+ hda_set_reg_by_offset(sc, HDAC_RIRBSIZE,
+ HDAC_RIRBSIZE_RIRBSZCAP_256 | HDAC_RIRBSIZE_RIRBSIZE_256);
+
+ for (i = 0; i < HDA_IOSS_NO; i++) {
+ off = hda_get_offset_stream(i);
+ hda_set_reg_by_offset(sc, off + HDAC_SDFIFOS, HDA_FIFO_SIZE);
+ }
+}
+
+static void
+hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind)
+{
+ struct hda_stream_desc *st = &sc->streams[stream_ind];
+ uint32_t off = hda_get_offset_stream(stream_ind);
+
+ DPRINTF("Reset the HDA stream: 0x%x\n", stream_ind);
+
+ /* Reset the Stream Descriptor registers */
+ memset(sc->regs + HDA_STREAM_REGS_BASE + off, 0, HDA_STREAM_REGS_LEN);
+
+ /* Reset the Stream Descriptor */
+ memset(st, 0, sizeof(*st));
+
+ hda_set_field_by_offset(sc, off + HDAC_SDSTS,
+ HDAC_SDSTS_FIFORDY, HDAC_SDSTS_FIFORDY);
+ hda_set_field_by_offset(sc, off + HDAC_SDCTL0,
+ HDAC_SDCTL_SRST, HDAC_SDCTL_SRST);
+}
+
+static int
+hda_stream_start(struct hda_softc *sc, uint8_t stream_ind)
+{
+ struct hda_stream_desc *st = &sc->streams[stream_ind];
+ struct hda_bdle_desc *bdle_desc = NULL;
+ struct hda_bdle *bdle = NULL;
+ uint32_t lvi = 0;
+ uint32_t bdl_cnt = 0;
+ uint64_t bdpl = 0;
+ uint64_t bdpu = 0;
+ uint64_t bdl_paddr = 0;
+ void *bdl_vaddr = NULL;
+ uint32_t bdle_sz = 0;
+ uint64_t bdle_addrl = 0;
+ uint64_t bdle_addrh = 0;
+ uint64_t bdle_paddr = 0;
+ void *bdle_vaddr = NULL;
+ uint32_t off = hda_get_offset_stream(stream_ind);
+ uint32_t sdctl = 0;
+ uint8_t strm = 0;
+ uint8_t dir = 0;
+ int i;
+
+ assert(!st->run);
+
+ lvi = hda_get_reg_by_offset(sc, off + HDAC_SDLVI);
+ bdpl = hda_get_reg_by_offset(sc, off + HDAC_SDBDPL);
+ bdpu = hda_get_reg_by_offset(sc, off + HDAC_SDBDPU);
+
+ bdl_cnt = lvi + 1;
+ assert(bdl_cnt <= HDA_BDL_MAX_LEN);
+
+ bdl_paddr = bdpl | (bdpu << 32);
+ bdl_vaddr = hda_dma_get_vaddr(sc, bdl_paddr,
+ HDA_BDL_ENTRY_LEN * bdl_cnt);
+ if (!bdl_vaddr) {
+ DPRINTF("Fail to get the guest virtual address\n");
+ return (-1);
+ }
+
+ DPRINTF("stream: 0x%x bdl_cnt: 0x%x bdl_paddr: 0x%lx\n",
+ stream_ind, bdl_cnt, bdl_paddr);
+
+ st->bdl_cnt = bdl_cnt;
+
+ bdle = (struct hda_bdle *)bdl_vaddr;
+ for (i = 0; i < bdl_cnt; i++, bdle++) {
+ bdle_sz = bdle->len;
+ assert(!(bdle_sz % HDA_DMA_ACCESS_LEN));
+
+ bdle_addrl = bdle->addrl;
+ bdle_addrh = bdle->addrh;
+
+ bdle_paddr = bdle_addrl | (bdle_addrh << 32);
+ bdle_vaddr = hda_dma_get_vaddr(sc, bdle_paddr, bdle_sz);
+ if (!bdle_vaddr) {
+ DPRINTF("Fail to get the guest virtual address\n");
+ return (-1);
+ }
+
+ bdle_desc = &st->bdl[i];
+ bdle_desc->addr = bdle_vaddr;
+ bdle_desc->len = bdle_sz;
+ bdle_desc->ioc = bdle->ioc;
+
+ DPRINTF("bdle: 0x%x bdle_sz: 0x%x\n", i, bdle_sz);
+ }
+
+ sdctl = hda_get_reg_by_offset(sc, off + HDAC_SDCTL0);
+ strm = (sdctl >> 20) & 0x0f;
+ dir = stream_ind >= HDA_ISS_NO;
+
+ DPRINTF("strm: 0x%x, dir: 0x%x\n", strm, dir);
+
+ sc->stream_map[dir][strm] = stream_ind;
+ st->stream = strm;
+ st->dir = dir;
+ st->bp = 0;
+ st->be = 0;
+
+ hda_set_pib(sc, stream_ind, 0);
+
+ st->run = 1;
+
+ hda_notify_codecs(sc, 1, strm, dir);
+
+ return (0);
+}
+
+static int
+hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind)
+{
+ struct hda_stream_desc *st = &sc->streams[stream_ind];
+ uint8_t strm = st->stream;
+ uint8_t dir = st->dir;
+
+ DPRINTF("stream: 0x%x, strm: 0x%x, dir: 0x%x\n", stream_ind, strm, dir);
+
+ st->run = 0;
+
+ hda_notify_codecs(sc, 0, strm, dir);
+
+ return (0);
+}
+
+static uint32_t
+hda_read(struct hda_softc *sc, uint32_t offset)
+{
+ if (offset == HDAC_WALCLK)
+ return (24 * (hda_get_clock_ns() - \
+ sc->wall_clock_start) / 1000);
+
+ return (hda_get_reg_by_offset(sc, offset));
+}
+
+static int
+hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size, uint32_t value)
+{
+ uint32_t old = hda_get_reg_by_offset(sc, offset);
+ uint32_t masks[] = {0x00000000, 0x000000ff, 0x0000ffff,
+ 0x00ffffff, 0xffffffff};
+ hda_set_reg_handler set_reg_handler = hda_set_reg_table[offset];
+
+ hda_set_field_by_offset(sc, offset, masks[size], value);
+
+ if (set_reg_handler)
+ set_reg_handler(sc, offset, old);
+
+ return (0);
+}
+
+static inline void
+hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p)
+{
+#if DEBUG_HDA == 1
+ char *name = p->name;
+#endif
+ DPRINTF("%s size: %d\n", name, p->size);
+ DPRINTF("%s dma_vaddr: %p\n", name, p->dma_vaddr);
+ DPRINTF("%s wp: 0x%x\n", name, p->wp);
+ DPRINTF("%s rp: 0x%x\n", name, p->rp);
+}
+
+static int
+hda_corb_start(struct hda_softc *sc)
+{
+ struct hda_codec_cmd_ctl *corb = &sc->corb;
+ uint8_t corbsize = 0;
+ uint64_t corblbase = 0;
+ uint64_t corbubase = 0;
+ uint64_t corbpaddr = 0;
+
+ corb->name = "CORB";
+
+ corbsize = hda_get_reg_by_offset(sc, HDAC_CORBSIZE) & \
+ HDAC_CORBSIZE_CORBSIZE_MASK;
+ corb->size = hda_corb_sizes[corbsize];
+
+ if (!corb->size) {
+ DPRINTF("Invalid corb size\n");
+ return (-1);
+ }
+
+ corblbase = hda_get_reg_by_offset(sc, HDAC_CORBLBASE);
+ corbubase = hda_get_reg_by_offset(sc, HDAC_CORBUBASE);
+
+ corbpaddr = corblbase | (corbubase << 32);
+ DPRINTF("CORB dma_paddr: %p\n", (void *)corbpaddr);
+
+ corb->dma_vaddr = hda_dma_get_vaddr(sc, corbpaddr,
+ HDA_CORB_ENTRY_LEN * corb->size);
+ if (!corb->dma_vaddr) {
+ DPRINTF("Fail to get the guest virtual address\n");
+ return (-1);
+ }
+
+ corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
+ corb->rp = hda_get_reg_by_offset(sc, HDAC_CORBRP);
+
+ corb->run = 1;
+
+ hda_print_cmd_ctl_data(corb);
+
+ return (0);
+}
+
+static int
+hda_corb_run(struct hda_softc *sc)
+{
+ struct hda_codec_cmd_ctl *corb = &sc->corb;
+ uint32_t verb = 0;
+ int err;
+
+ corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
+
+ while (corb->rp != corb->wp && corb->run) {
+ corb->rp++;
+ corb->rp %= corb->size;
+
+ verb = hda_dma_ld_dword(corb->dma_vaddr + \
+ HDA_CORB_ENTRY_LEN * corb->rp);
+
+ err = hda_send_command(sc, verb);
+ assert(!err);
+ }
+
+ hda_set_reg_by_offset(sc, HDAC_CORBRP, corb->rp);
+
+ if (corb->run)
+ hda_response_interrupt(sc);
+
+ return (0);
+}
+
+static int
+hda_rirb_start(struct hda_softc *sc)
+{
+ struct hda_codec_cmd_ctl *rirb = &sc->rirb;
+ uint8_t rirbsize = 0;
+ uint64_t rirblbase = 0;
+ uint64_t rirbubase = 0;
+ uint64_t rirbpaddr = 0;
+
+ rirb->name = "RIRB";
+
+ rirbsize = hda_get_reg_by_offset(sc, HDAC_RIRBSIZE) & \
+ HDAC_RIRBSIZE_RIRBSIZE_MASK;
+ rirb->size = hda_rirb_sizes[rirbsize];
+
+ if (!rirb->size) {
+ DPRINTF("Invalid rirb size\n");
+ return (-1);
+ }
+
+ rirblbase = hda_get_reg_by_offset(sc, HDAC_RIRBLBASE);
+ rirbubase = hda_get_reg_by_offset(sc, HDAC_RIRBUBASE);
+
+ rirbpaddr = rirblbase | (rirbubase << 32);
+ DPRINTF("RIRB dma_paddr: %p\n", (void *)rirbpaddr);
+
+ rirb->dma_vaddr = hda_dma_get_vaddr(sc, rirbpaddr,
+ HDA_RIRB_ENTRY_LEN * rirb->size);
+ if (!rirb->dma_vaddr) {
+ DPRINTF("Fail to get the guest virtual address\n");
+ return (-1);
+ }
+
+ rirb->wp = hda_get_reg_by_offset(sc, HDAC_RIRBWP);
+ rirb->rp = 0x0000;
+
+ rirb->run = 1;
+
+ hda_print_cmd_ctl_data(rirb);
+
+ return (0);
+}
+
+static void *
+hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr, size_t len)
+{
+ struct pci_devinst *pi = sc->pci_dev;
+
+ assert(pi);
+
+ return (paddr_guest2host(pi->pi_vmctx, (uintptr_t)dma_paddr, len));
+}
+
+static void
+hda_dma_st_dword(void *dma_vaddr, uint32_t data)
+{
+ *(uint32_t*)dma_vaddr = data;
+}
+
+static uint32_t
+hda_dma_ld_dword(void *dma_vaddr)
+{
+ return (*(uint32_t*)dma_vaddr);
+}
+
+static inline uint8_t
+hda_get_stream_by_offsets(uint32_t offset, uint8_t reg_offset)
+{
+ uint8_t stream_ind = (offset - reg_offset) >> 5;
+
+ assert(stream_ind < HDA_IOSS_NO);
+
+ return (stream_ind);
+}
+
+static inline uint32_t
+hda_get_offset_stream(uint8_t stream_ind)
+{
+ return (stream_ind << 5);
+}
+
+static void
+hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+
+ if (!(value & HDAC_GCTL_CRST)) {
+ hda_reset(sc);
+ }
+}
+
+static void
+hda_set_statests(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+
+ hda_set_reg_by_offset(sc, offset, old);
+
+ /* clear the corresponding bits written by the software (guest) */
+ hda_set_field_by_offset(sc, offset, value & HDA_STATESTS_IRQ_MASK, 0);
+
+ hda_update_intr(sc);
+}
+
+static void
+hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ hda_corb_run(sc);
+}
+
+static void
+hda_set_corbctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+ int err;
+ struct hda_codec_cmd_ctl *corb = NULL;
+
+ if (value & HDAC_CORBCTL_CORBRUN) {
+ if (!(old & HDAC_CORBCTL_CORBRUN)) {
+ err = hda_corb_start(sc);
+ assert(!err);
+ }
+ } else {
+ corb = &sc->corb;
+ memset(corb, 0, sizeof(*corb));
+ }
+
+ hda_corb_run(sc);
+}
+
+static void
+hda_set_rirbctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+ int err;
+ struct hda_codec_cmd_ctl *rirb = NULL;
+
+ if (value & HDAC_RIRBCTL_RIRBDMAEN) {
+ err = hda_rirb_start(sc);
+ assert(!err);
+ } else {
+ rirb = &sc->rirb;
+ memset(rirb, 0, sizeof(*rirb));
+ }
+}
+
+static void
+hda_set_rirbsts(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+
+ hda_set_reg_by_offset(sc, offset, old);
+
+ /* clear the corresponding bits written by the software (guest) */
+ hda_set_field_by_offset(sc, offset, value & HDA_RIRBSTS_IRQ_MASK, 0);
+
+ hda_update_intr(sc);
+}
+
+static void
+hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+ uint64_t dpiblbase = 0;
+ uint64_t dpibubase = 0;
+ uint64_t dpibpaddr = 0;
+
+ if ((value & HDAC_DPLBASE_DPLBASE_DMAPBE) != (old & \
+ HDAC_DPLBASE_DPLBASE_DMAPBE)) {
+ if (value & HDAC_DPLBASE_DPLBASE_DMAPBE) {
+ dpiblbase = value & HDAC_DPLBASE_DPLBASE_MASK;
+ dpibubase = hda_get_reg_by_offset(sc, HDAC_DPIBUBASE);
+
+ dpibpaddr = dpiblbase | (dpibubase << 32);
+ DPRINTF("DMA Position In Buffer dma_paddr: %p\n",
+ (void *)dpibpaddr);
+
+ sc->dma_pib_vaddr = hda_dma_get_vaddr(sc, dpibpaddr,
+ HDA_DMA_PIB_ENTRY_LEN * HDA_IOSS_NO);
+ if (!sc->dma_pib_vaddr) {
+ DPRINTF("Fail to get the guest \
+ virtual address\n");
+ assert(0);
+ }
+ } else {
+ DPRINTF("DMA Position In Buffer Reset\n");
+ sc->dma_pib_vaddr = NULL;
+ }
+ }
+}
+
+static void
+hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint8_t stream_ind = hda_get_stream_by_offsets(offset, HDAC_SDCTL0);
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+ int err;
+
+ DPRINTF("stream_ind: 0x%x old: 0x%x value: 0x%x\n",
+ stream_ind, old, value);
+
+ if (value & HDAC_SDCTL_SRST) {
+ hda_stream_reset(sc, stream_ind);
+ }
+
+ if ((value & HDAC_SDCTL_RUN) != (old & HDAC_SDCTL_RUN)) {
+ if (value & HDAC_SDCTL_RUN) {
+ err = hda_stream_start(sc, stream_ind);
+ assert(!err);
+ } else {
+ err = hda_stream_stop(sc, stream_ind);
+ assert(!err);
+ }
+ }
+}
+
+static void
+hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+
+ hda_set_field_by_offset(sc, offset - 2, 0x00ff0000, value << 16);
+}
+
+static void
+hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old)
+{
+ uint32_t value = hda_get_reg_by_offset(sc, offset);
+
+ hda_set_reg_by_offset(sc, offset, old);
+
+ /* clear the corresponding bits written by the software (guest) */
+ hda_set_field_by_offset(sc, offset, value & HDA_SDSTS_IRQ_MASK, 0);
+
+ hda_update_intr(sc);
+}
+
+static int
+hda_signal_state_change(struct hda_codec_inst *hci)
+{
+ struct hda_softc *sc = NULL;
+ uint32_t sdiwake = 0;
+
+ assert(hci);
+ assert(hci->hda);
+
+ DPRINTF("cad: 0x%x\n", hci->cad);
+
+ sc = hci->hda;
+ sdiwake = 1 << hci->cad;
+
+ hda_set_field_by_offset(sc, HDAC_STATESTS, sdiwake, sdiwake);
+ hda_update_intr(sc);
+
+ return (0);
+}
+
+static int
+hda_response(struct hda_codec_inst *hci, uint32_t response, uint8_t unsol)
+{
+ struct hda_softc *sc = NULL;
+ struct hda_codec_cmd_ctl *rirb = NULL;
+ uint32_t response_ex = 0;
+ uint8_t rintcnt = 0;
+
+ assert(hci);
+ assert(hci->cad <= HDA_CODEC_MAX);
+
+ response_ex = hci->cad | unsol;
+
+ sc = hci->hda;
+ assert(sc);
+
+ rirb = &sc->rirb;
+
+ if (rirb->run) {
+ rirb->wp++;
+ rirb->wp %= rirb->size;
+
+ hda_dma_st_dword(rirb->dma_vaddr + HDA_RIRB_ENTRY_LEN * \
+ rirb->wp, response);
+ hda_dma_st_dword(rirb->dma_vaddr + HDA_RIRB_ENTRY_LEN * \
+ rirb->wp + 0x04, response_ex);
+
+ hda_set_reg_by_offset(sc, HDAC_RIRBWP, rirb->wp);
+
+ sc->rirb_cnt++;
+ }
+
+ rintcnt = hda_get_reg_by_offset(sc, HDAC_RINTCNT);
+ if (sc->rirb_cnt == rintcnt)
+ hda_response_interrupt(sc);
+
+ return (0);
+}
+
+static int
+hda_transfer(struct hda_codec_inst *hci, uint8_t stream, uint8_t dir,
+ void *buf, size_t count)
+{
+ struct hda_softc *sc = NULL;
+ struct hda_stream_desc *st = NULL;
+ struct hda_bdle_desc *bdl = NULL;
+ struct hda_bdle_desc *bdle_desc = NULL;
+ uint8_t stream_ind = 0;
+ uint32_t lpib = 0;
+ uint32_t off = 0;
+ size_t left = 0;
+ uint8_t irq = 0;
+
+ assert(hci);
+ assert(hci->hda);
+ assert(buf);
+ assert(!(count % HDA_DMA_ACCESS_LEN));
+
+ if (!stream) {
+ DPRINTF("Invalid stream\n");
+ return (-1);
+ }
+
+ sc = hci->hda;
+
+ assert(stream < HDA_STREAM_TAGS_CNT);
+ stream_ind = sc->stream_map[dir][stream];
+
+ if (!dir)
+ assert(stream_ind < HDA_ISS_NO);
+ else
+ assert(stream_ind >= HDA_ISS_NO && stream_ind < HDA_IOSS_NO);
+
+ st = &sc->streams[stream_ind];
+ if (!st->run) {
+ DPRINTF("Stream 0x%x stopped\n", stream);
+ return (-1);
+ }
+
+ assert(st->stream == stream);
+
+ off = hda_get_offset_stream(stream_ind);
+
+ lpib = hda_get_reg_by_offset(sc, off + HDAC_SDLPIB);
+
+ bdl = st->bdl;
+
+ assert(st->be < st->bdl_cnt);
+ assert(st->bp < bdl[st->be].len);
+
+ left = count;
+ while (left) {
+ bdle_desc = &bdl[st->be];
+
+ if (dir)
+ *(uint32_t *)buf = \
+ hda_dma_ld_dword(bdle_desc->addr + st->bp);
+ else
+ hda_dma_st_dword(bdle_desc->addr + st->bp,
+ *(uint32_t *)buf);
+
+ buf += HDA_DMA_ACCESS_LEN;
+ st->bp += HDA_DMA_ACCESS_LEN;
+ lpib += HDA_DMA_ACCESS_LEN;
+ left -= HDA_DMA_ACCESS_LEN;
+
+ if (st->bp == bdle_desc->len) {
+ st->bp = 0;
+ if (bdle_desc->ioc)
+ irq = 1;
+ st->be++;
+ if (st->be == st->bdl_cnt) {
+ st->be = 0;
+ lpib = 0;
+ }
+ bdle_desc = &bdl[st->be];
+ }
+ }
+
+ hda_set_pib(sc, stream_ind, lpib);
+
+ if (irq) {
+ hda_set_field_by_offset(sc, off + HDAC_SDSTS,
+ HDAC_SDSTS_BCIS, HDAC_SDSTS_BCIS);
+ hda_update_intr(sc);
+ }
+
+ return (0);
+}
+
+static void
+hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib)
+{
+ uint32_t off = hda_get_offset_stream(stream_ind);
+
+ hda_set_reg_by_offset(sc, off + HDAC_SDLPIB, pib);
+ /* LPIB Alias */
+ hda_set_reg_by_offset(sc, 0x2000 + off + HDAC_SDLPIB, pib);
+ if (sc->dma_pib_vaddr)
+ *(uint32_t *)(sc->dma_pib_vaddr + stream_ind * \
+ HDA_DMA_PIB_ENTRY_LEN) = pib;
+}
+
+static uint64_t hda_get_clock_ns(void)
+{
+ struct timespec ts;
+ int err;
+
+ err = clock_gettime(CLOCK_MONOTONIC, &ts);
+ assert(!err);
+
+ return (ts.tv_sec * 1000000000LL + ts.tv_nsec);
+}
+
+/*
+ * PCI HDA function definitions
+ */
+static int
+pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ struct hda_softc *sc = NULL;
+
+ assert(ctx != NULL);
+ assert(pi != NULL);
+
+ pci_set_cfgdata16(pi, PCIR_VENDOR, INTEL_VENDORID);
+ pci_set_cfgdata16(pi, PCIR_DEVICE, HDA_INTEL_82801G);
+
+ pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_MULTIMEDIA_HDA);
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_MULTIMEDIA);
+
+ /* select the Intel HDA mode */
+ pci_set_cfgdata8(pi, PCIR_HDCTL, 0x01);
+
+ /* allocate one BAR register for the Memory address offsets */
+ pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, HDA_LAST_OFFSET);
+
+ /* allocate an IRQ pin for our slot */
+ pci_lintr_request(pi);
+
+ sc = hda_init(opts);
+ if (!sc)
+ return (-1);
+
+ sc->pci_dev = pi;
+ pi->pi_arg = sc;
+
+ return (0);
+}
+
+static void
+pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+ struct hda_softc *sc = pi->pi_arg;
+ int err;
+
+ assert(sc);
+ assert(baridx == 0);
+ assert(size <= 4);
+
+ DPRINTF("offset: 0x%lx value: 0x%lx\n", offset, value);
+
+ err = hda_write(sc, offset, size, value);
+ assert(!err);
+}
+
+static uint64_t
+pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size)
+{
+ struct hda_softc *sc = pi->pi_arg;
+ uint64_t value = 0;
+
+ assert(sc);
+ assert(baridx == 0);
+ assert(size <= 4);
+
+ value = hda_read(sc, offset);
+
+ DPRINTF("offset: 0x%lx value: 0x%lx\n", offset, value);
+
+ return (value);
+}
diff --git a/usr/src/cmd/bhyve/pci_hda.h b/usr/src/cmd/bhyve/pci_hda.h
new file mode 100644
index 0000000000..8ed050cc8f
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_hda.h
@@ -0,0 +1,92 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
+ * 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 ``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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _HDA_EMUL_H_
+#define _HDA_EMUL_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+
+#include "hda_reg.h"
+
+/*
+ * HDA Debug Log
+ */
+#define DEBUG_HDA 1
+#if DEBUG_HDA == 1
+extern FILE *dbg;
+#define DPRINTF(fmt, arg...) \
+do {fprintf(dbg, "%s-%d: " fmt, __func__, __LINE__, ##arg); \
+fflush(dbg); } while (0)
+#else
+#define DPRINTF(fmt, arg...)
+#endif
+
+#define HDA_FIFO_SIZE 0x100
+
+struct hda_softc;
+struct hda_codec_class;
+
+struct hda_codec_inst {
+ uint8_t cad;
+ struct hda_codec_class *codec;
+ struct hda_softc *hda;
+ struct hda_ops *hops;
+ void *priv;
+};
+
+struct hda_codec_class {
+ char *name;
+ int (*init)(struct hda_codec_inst *hci, const char *play,
+ const char *rec, const char *opts);
+ int (*reset)(struct hda_codec_inst *hci);
+ int (*command)(struct hda_codec_inst *hci, uint32_t cmd_data);
+ int (*notify)(struct hda_codec_inst *hci, uint8_t run, uint8_t stream,
+ uint8_t dir);
+};
+
+struct hda_ops {
+ int (*signal)(struct hda_codec_inst *hci);
+ int (*response)(struct hda_codec_inst *hci, uint32_t response,
+ uint8_t unsol);
+ int (*transfer)(struct hda_codec_inst *hci, uint8_t stream,
+ uint8_t dir, void *buf, size_t count);
+};
+
+#define HDA_EMUL_SET(x) DATA_SET(hda_codec_class_set, x);
+
+#endif /* _HDA_EMUL_H_ */
diff --git a/usr/src/cmd/bhyve/pci_nvme.c b/usr/src/cmd/bhyve/pci_nvme.c
index a56c1d6959..3e6e469ed1 100644
--- a/usr/src/cmd/bhyve/pci_nvme.c
+++ b/usr/src/cmd/bhyve/pci_nvme.c
@@ -4,6 +4,9 @@
* Copyright (c) 2017 Shunsuke Mie
* Copyright (c) 2018 Leon Dang
*
+ * Function crc16 Copyright (c) 2017, Fedor Uporov
+ * Obtained from function ext2_crc16() in sys/fs/ext2fs/ext2_csum.c
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -30,7 +33,7 @@
* bhyve PCIe-NVMe device emulation.
*
* options:
- * -s <n>,nvme,devpath,maxq=#,qsz=#,ioslots=#,sectsz=#,ser=A-Z
+ * -s <n>,nvme,devpath,maxq=#,qsz=#,ioslots=#,sectsz=#,ser=A-Z,eui64=#
*
* accepted devpath:
* /dev/blockdev
@@ -42,6 +45,7 @@
* ioslots = max number of concurrent io requests
* sectsz = sector size (defaults to blockif sector size)
* ser = serial number (20-chars max)
+ * eui64 = IEEE Extended Unique Identifier (8 byte value)
*
*/
@@ -54,6 +58,10 @@
__FBSDID("$FreeBSD$");
#include <sys/types.h>
+#include <net/ieee_oui.h>
+#ifndef __FreeBSD__
+#include <endian.h>
+#endif
#include <assert.h>
#include <pthread.h>
@@ -94,6 +102,10 @@ static int nvme_debug = 0;
#define NVME_PRP2_ITEMS (PAGE_SIZE/sizeof(uint64_t))
#define NVME_MAX_BLOCKIOVS 512
+/* This is a synthetic status code to indicate there is no status */
+#define NVME_NO_STATUS 0xffff
+#define NVME_COMPLETION_VALID(c) ((c).status != NVME_NO_STATUS)
+
/* helpers */
/* Convert a zero-based value into a one-based value */
@@ -164,6 +176,7 @@ struct pci_nvme_blockstore {
uint64_t size;
uint32_t sectsz;
uint32_t sectsz_bits;
+ uint64_t eui64;
};
struct pci_nvme_ioreq {
@@ -352,12 +365,61 @@ pci_nvme_init_ctrldata(struct pci_nvme_softc *sc)
cd->power_state[0].mp = 10;
}
-static void
-pci_nvme_init_nsdata(struct pci_nvme_softc *sc)
+/*
+ * Calculate the CRC-16 of the given buffer
+ * See copyright attribution at top of file
+ */
+static uint16_t
+crc16(uint16_t crc, const void *buffer, unsigned int len)
{
- struct nvme_namespace_data *nd;
+ const unsigned char *cp = buffer;
+ /* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */
+ static uint16_t const crc16_table[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+ };
+
+ while (len--)
+ crc = (((crc >> 8) & 0xffU) ^
+ crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU;
+ return crc;
+}
- nd = &sc->nsdata;
+static void
+pci_nvme_init_nsdata(struct pci_nvme_softc *sc,
+ struct nvme_namespace_data *nd, uint32_t nsid,
+ uint64_t eui64)
+{
nd->nsze = sc->nvstore.size / sc->nvstore.sectsz;
nd->ncap = nd->nsze;
@@ -365,10 +427,25 @@ pci_nvme_init_nsdata(struct pci_nvme_softc *sc)
/* Get LBA and backstore information from backing store */
nd->nlbaf = 0; /* NLBAF is a 0's based value (i.e. 1 LBA Format) */
+ nd->flbas = 0;
+
+ /* Create an EUI-64 if user did not provide one */
+ if (eui64 == 0) {
+ char *data = NULL;
+
+ asprintf(&data, "%s%u%u%u", vmname, sc->nsc_pi->pi_bus,
+ sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func);
+
+ if (data != NULL) {
+ eui64 = OUI_FREEBSD_NVME_LOW | crc16(0, data, strlen(data));
+ free(data);
+ }
+ eui64 = (eui64 << 16) | (nsid & 0xffff);
+ }
+ be64enc(nd->eui64, eui64);
+
/* LBA data-sz = 2^lbads */
nd->lbaf[0] = sc->nvstore.sectsz_bits << NVME_NS_DATA_LBAF_LBADS_SHIFT;
-
- nd->flbas = 0;
}
static void
@@ -982,6 +1059,7 @@ pci_nvme_handle_admin_cmd(struct pci_nvme_softc* sc, uint64_t value)
while (sqhead != atomic_load_acq_short(&sq->tail)) {
cmd = &(sq->qbase)[sqhead];
+ compl.cdw0 = 0;
compl.status = 0;
switch (cmd->opc) {
@@ -1026,14 +1104,16 @@ pci_nvme_handle_admin_cmd(struct pci_nvme_softc* sc, uint64_t value)
/* XXX dont care, unhandled for now
do_intr |= nvme_opc_async_event_req(sc, cmd, &compl);
*/
+ compl.status = NVME_NO_STATUS;
break;
default:
WPRINTF(("0x%x command is not implemented\r\n",
cmd->opc));
+ pci_nvme_status_genc(&compl.status, NVME_SC_INVALID_OPCODE);
+ do_intr |= 1;
}
- /* for now skip async event generation */
- if (cmd->opc != NVME_OPC_ASYNC_EVENT_REQUEST) {
+ if (NVME_COMPLETION_VALID(compl)) {
struct nvme_completion *cp;
int phase;
@@ -1820,6 +1900,8 @@ pci_nvme_parse_opts(struct pci_nvme_softc *sc, char *opts)
free(uopt);
return (-1);
}
+ } else if (!strcmp("eui64", xopts)) {
+ sc->nvstore.eui64 = htobe64(strtoull(config, NULL, 0));
} else if (optidx == 0) {
snprintf(bident, sizeof(bident), "%d:%d",
sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func);
@@ -1929,12 +2011,18 @@ pci_nvme_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
goto done;
}
+ error = pci_emul_add_pciecap(pi, PCIEM_TYPE_ROOT_INT_EP);
+ if (error) {
+ WPRINTF(("%s pci add Express capability failed\r\n", __func__));
+ goto done;
+ }
+
pthread_mutex_init(&sc->mtx, NULL);
sem_init(&sc->iosemlock, 0, sc->ioslots);
pci_nvme_reset(sc);
pci_nvme_init_ctrldata(sc);
- pci_nvme_init_nsdata(sc);
+ pci_nvme_init_nsdata(sc, &sc->nsdata, 1, sc->nvstore.eui64);
pci_nvme_init_logpages(sc);
pci_lintr_request(pi);
diff --git a/usr/src/cmd/bhyve/pci_virtio_console.c b/usr/src/cmd/bhyve/pci_virtio_console.c
index 90437662df..f7038ff40f 100644
--- a/usr/src/cmd/bhyve/pci_virtio_console.c
+++ b/usr/src/cmd/bhyve/pci_virtio_console.c
@@ -375,8 +375,11 @@ out:
if (fd != -1)
close(fd);
- if (error != 0 && s != -1)
- close(s);
+ if (error != 0) {
+ if (s != -1)
+ close(s);
+ free(sock);
+ }
return (error);
}
@@ -630,7 +633,7 @@ pci_vtcon_notify_rx(void *vsc, struct vqueue_info *vq)
if (!port->vsp_rx_ready) {
port->vsp_rx_ready = 1;
- vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
+ vq_kick_disable(vq);
}
}
diff --git a/usr/src/cmd/bhyve/pci_virtio_net.c b/usr/src/cmd/bhyve/pci_virtio_net.c
index aa188a3e59..73f8aa0d6b 100644
--- a/usr/src/cmd/bhyve/pci_virtio_net.c
+++ b/usr/src/cmd/bhyve/pci_virtio_net.c
@@ -52,7 +52,6 @@ __FBSDID("$FreeBSD$");
#include <sys/select.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
-#include <machine/atomic.h>
#include <net/ethernet.h>
#ifdef __FreeBSD__
#ifndef NETMAP_WITH_LIBS
@@ -89,6 +88,7 @@ __FBSDID("$FreeBSD$");
#include "mevent.h"
#endif
#include "virtio.h"
+#include "net_utils.h"
#define VTNET_RINGSZ 1024
@@ -176,14 +176,13 @@ struct pci_vtnet_softc {
struct nm_desc *vsc_nmd;
int vsc_rx_ready;
- volatile int resetting; /* set and checked outside lock */
+ int resetting; /* protected by tx_mtx */
uint64_t vsc_features; /* negotiated features */
struct virtio_net_config vsc_config;
pthread_mutex_t rx_mtx;
- int rx_in_progress;
int rx_vhdrlen;
int rx_merge; /* merged rx bufs in use */
@@ -215,62 +214,39 @@ static struct virtio_consts vtnet_vi_consts = {
VTNET_S_HOSTCAPS, /* our capabilities */
};
-/*
- * If the transmit thread is active then stall until it is done.
- */
static void
-pci_vtnet_txwait(struct pci_vtnet_softc *sc)
+pci_vtnet_reset(void *vsc)
{
+ struct pci_vtnet_softc *sc = vsc;
+
+ DPRINTF(("vtnet: device reset requested !\n"));
+
+ /* Acquire the RX lock to block RX processing. */
+ pthread_mutex_lock(&sc->rx_mtx);
+ /* Set sc->resetting and give a chance to the TX thread to stop. */
pthread_mutex_lock(&sc->tx_mtx);
+ sc->resetting = 1;
while (sc->tx_in_progress) {
pthread_mutex_unlock(&sc->tx_mtx);
usleep(10000);
pthread_mutex_lock(&sc->tx_mtx);
}
- pthread_mutex_unlock(&sc->tx_mtx);
-}
-
-/*
- * If the receive thread is active then stall until it is done.
- */
-static void
-pci_vtnet_rxwait(struct pci_vtnet_softc *sc)
-{
-
- pthread_mutex_lock(&sc->rx_mtx);
- while (sc->rx_in_progress) {
- pthread_mutex_unlock(&sc->rx_mtx);
- usleep(10000);
- pthread_mutex_lock(&sc->rx_mtx);
- }
- pthread_mutex_unlock(&sc->rx_mtx);
-}
-
-static void
-pci_vtnet_reset(void *vsc)
-{
- struct pci_vtnet_softc *sc = vsc;
-
- DPRINTF(("vtnet: device reset requested !\n"));
-
- sc->resetting = 1;
-
- /*
- * Wait for the transmit and receive threads to finish their
- * processing.
- */
- pci_vtnet_txwait(sc);
- pci_vtnet_rxwait(sc);
sc->vsc_rx_ready = 0;
sc->rx_merge = 1;
sc->rx_vhdrlen = sizeof(struct virtio_net_rxhdr);
- /* now reset rings, MSI-X vectors, and negotiated capabilities */
+ /*
+ * Now reset rings, MSI-X vectors, and negotiated capabilities.
+ * Do that with the TX lock held, since we need to reset
+ * sc->resetting.
+ */
vi_reset_dev(&sc->vsc_vs);
sc->resetting = 0;
+ pthread_mutex_unlock(&sc->tx_mtx);
+ pthread_mutex_unlock(&sc->rx_mtx);
}
/*
@@ -370,9 +346,9 @@ pci_vtnet_tap_rx(struct pci_vtnet_softc *sc)
/*
* But, will be called when the rx ring hasn't yet
- * been set up or the guest is resetting the device.
+ * been set up.
*/
- if (!sc->vsc_rx_ready || sc->resetting) {
+ if (!sc->vsc_rx_ready) {
#ifdef __FreeBSD__
/*
* Drop the packet and try later.
@@ -580,9 +556,9 @@ pci_vtnet_netmap_rx(struct pci_vtnet_softc *sc)
/*
* But, will be called when the rx ring hasn't yet
- * been set up or the guest is resetting the device.
+ * been set up.
*/
- if (!sc->vsc_rx_ready || sc->resetting) {
+ if (!sc->vsc_rx_ready) {
/*
* Drop the packet and try later.
*/
@@ -661,9 +637,7 @@ pci_vtnet_rx_callback(int fd, enum ev_type type, void *param)
struct pci_vtnet_softc *sc = param;
pthread_mutex_lock(&sc->rx_mtx);
- sc->rx_in_progress = 1;
sc->pci_vtnet_rx(sc);
- sc->rx_in_progress = 0;
pthread_mutex_unlock(&sc->rx_mtx);
}
@@ -685,9 +659,7 @@ pci_vtnet_poll_thread(void *param)
continue;
}
pthread_mutex_lock(&sc->vsc_mtx);
- sc->rx_in_progress = 1;
pci_vtnet_tap_rx(sc);
- sc->rx_in_progress = 0;
pthread_mutex_unlock(&sc->vsc_mtx);
}
@@ -705,7 +677,7 @@ pci_vtnet_ping_rxq(void *vsc, struct vqueue_info *vq)
*/
if (sc->vsc_rx_ready == 0) {
sc->vsc_rx_ready = 1;
- vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
+ vq_kick_disable(vq);
}
}
@@ -751,7 +723,7 @@ pci_vtnet_ping_txq(void *vsc, struct vqueue_info *vq)
/* Signal the tx thread for processing */
pthread_mutex_lock(&sc->tx_mtx);
- vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
+ vq_kick_disable(vq);
if (sc->tx_in_progress == 0)
pthread_cond_signal(&sc->tx_cond);
pthread_mutex_unlock(&sc->tx_mtx);
@@ -780,8 +752,7 @@ pci_vtnet_tx_thread(void *param)
for (;;) {
/* note - tx mutex is locked here */
while (sc->resetting || !vq_has_descs(vq)) {
- vq->vq_used->vu_flags &= ~VRING_USED_F_NO_NOTIFY;
- mb();
+ vq_kick_enable(vq);
if (!sc->resetting && vq_has_descs(vq))
break;
@@ -789,7 +760,7 @@ pci_vtnet_tx_thread(void *param)
error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx);
assert(error == 0);
}
- vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
+ vq_kick_disable(vq);
sc->tx_in_progress = 1;
pthread_mutex_unlock(&sc->tx_mtx);
@@ -821,31 +792,6 @@ pci_vtnet_ping_ctlq(void *vsc, struct vqueue_info *vq)
}
#endif /* __FreeBSD__ */
-#ifdef __FreeBSD__
-static int
-pci_vtnet_parsemac(char *mac_str, uint8_t *mac_addr)
-{
- struct ether_addr *ea;
- char *tmpstr;
- char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
-
- tmpstr = strsep(&mac_str,"=");
-
- if ((mac_str != NULL) && (!strcmp(tmpstr,"mac"))) {
- ea = ether_aton(mac_str);
-
- if (ea == NULL || ETHER_IS_MULTICAST(ea->octet) ||
- memcmp(ea->octet, zero_addr, ETHER_ADDR_LEN) == 0) {
- fprintf(stderr, "Invalid MAC %s\n", mac_str);
- return (EINVAL);
- } else
- memcpy(mac_addr, ea->octet, ETHER_ADDR_LEN);
- }
-
- return (0);
-}
-#endif /* __FreeBSD__ */
-
static void
pci_vtnet_tap_setup(struct pci_vtnet_softc *sc, char *devname)
{
@@ -968,11 +914,6 @@ pci_vtnet_netmap_setup(struct pci_vtnet_softc *sc, char *ifname)
static int
pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
{
-#ifdef __FreeBSD__
- MD5_CTX mdctx;
- unsigned char digest[16];
- char nstr[80];
-#endif
char tname[MAXCOMLEN + 1];
struct pci_vtnet_softc *sc;
const char *env_msi;
@@ -1027,7 +968,7 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
#ifdef __FreBSD__
if (vtopts != NULL) {
- err = pci_vtnet_parsemac(vtopts, sc->vsc_config.mac);
+ err = net_parsemac(vtopts, sc->vsc_config.mac);
if (err != 0) {
free(devname);
return (err);
@@ -1048,24 +989,8 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
}
#ifdef __FreeBSD__
- /*
- * The default MAC address is the standard NetApp OUI of 00-a0-98,
- * followed by an MD5 of the PCI slot/func number and dev name
- */
if (!mac_provided) {
- snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
- pi->pi_func, vmname);
-
- MD5Init(&mdctx);
- MD5Update(&mdctx, nstr, strlen(nstr));
- MD5Final(digest, &mdctx);
-
- sc->vsc_config.mac[0] = 0x00;
- sc->vsc_config.mac[1] = 0xa0;
- sc->vsc_config.mac[2] = 0x98;
- sc->vsc_config.mac[3] = digest[0];
- sc->vsc_config.mac[4] = digest[1];
- sc->vsc_config.mac[5] = digest[2];
+ net_genmac(pi, sc->vsc_config.mac);
}
#endif
@@ -1095,7 +1020,6 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
sc->rx_merge = 1;
sc->rx_vhdrlen = sizeof(struct virtio_net_rxhdr);
- sc->rx_in_progress = 0;
pthread_mutex_init(&sc->rx_mtx, NULL);
/*
diff --git a/usr/src/cmd/bhyve/pci_virtio_scsi.c b/usr/src/cmd/bhyve/pci_virtio_scsi.c
index 38e7d918a0..632f920293 100644
--- a/usr/src/cmd/bhyve/pci_virtio_scsi.c
+++ b/usr/src/cmd/bhyve/pci_virtio_scsi.c
@@ -309,7 +309,8 @@ pci_vtscsi_reset(void *vsc)
/* initialize config structure */
sc->vss_config = (struct pci_vtscsi_config){
.num_queues = VTSCSI_REQUESTQ,
- .seg_max = VTSCSI_MAXSEG,
+ /* Leave room for the request and the response. */
+ .seg_max = VTSCSI_MAXSEG - 2,
.max_sectors = 2,
.cmd_per_lun = 1,
.event_info_size = sizeof(struct pci_vtscsi_event),
@@ -464,7 +465,7 @@ pci_vtscsi_request_handle(struct pci_vtscsi_queue *q, struct iovec *iov_in,
int data_niov_in, data_niov_out;
void *ext_data_ptr = NULL;
uint32_t ext_data_len = 0, ext_sg_entries = 0;
- int err;
+ int err, nxferred;
seek_iov(iov_in, niov_in, data_iov_in, &data_niov_in,
VTSCSI_IN_HEADER_LEN(sc));
@@ -543,10 +544,11 @@ pci_vtscsi_request_handle(struct pci_vtscsi_queue *q, struct iovec *iov_in,
}
buf_to_iov(cmd_wr, VTSCSI_OUT_HEADER_LEN(sc), iov_out, niov_out, 0);
+ nxferred = VTSCSI_OUT_HEADER_LEN(sc) + io->scsiio.ext_data_filled;
free(cmd_rd);
free(cmd_wr);
ctl_scsi_free_io(io);
- return (VTSCSI_OUT_HEADER_LEN(sc) + io->scsiio.ext_data_filled);
+ return (nxferred);
}
static void
@@ -581,7 +583,7 @@ static void
pci_vtscsi_eventq_notify(void *vsc, struct vqueue_info *vq)
{
- vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
+ vq_kick_disable(vq);
}
static void
diff --git a/usr/src/cmd/bhyve/pci_xhci.c b/usr/src/cmd/bhyve/pci_xhci.c
index 29d56ec32c..324c706c47 100644
--- a/usr/src/cmd/bhyve/pci_xhci.c
+++ b/usr/src/cmd/bhyve/pci_xhci.c
@@ -1911,6 +1911,11 @@ pci_xhci_device_doorbell(struct pci_xhci_softc *sc, uint32_t slot,
return;
}
+ if (epid == 0 || epid >= XHCI_MAX_ENDPOINTS) {
+ DPRINTF(("pci_xhci: invalid endpoint %u\r\n", epid));
+ return;
+ }
+
dev = XHCI_SLOTDEV_PTR(sc, slot);
devep = &dev->eps[epid];
dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
@@ -1942,6 +1947,23 @@ pci_xhci_device_doorbell(struct pci_xhci_softc *sc, uint32_t slot,
/* get next trb work item */
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) {
+ struct xhci_stream_ctx *sctx;
+
+ /*
+ * Stream IDs of 0, 65535 (any stream), and 65534
+ * (prime) are invalid.
+ */
+ if (streamid == 0 || streamid == 65534 || streamid == 65535) {
+ DPRINTF(("pci_xhci: invalid stream %u\r\n", streamid));
+ return;
+ }
+
+ sctx = NULL;
+ pci_xhci_find_stream(sc, ep_ctx, streamid, &sctx);
+ if (sctx == NULL) {
+ DPRINTF(("pci_xhci: invalid stream %u\r\n", streamid));
+ return;
+ }
sctx_tr = &devep->ep_sctx_trbs[streamid];
ringaddr = sctx_tr->ringaddr;
ccs = sctx_tr->ccs;
@@ -1950,6 +1972,10 @@ pci_xhci_device_doorbell(struct pci_xhci_softc *sc, uint32_t slot,
streamid, ep_ctx->qwEpCtx2 & XHCI_TRB_3_CYCLE_BIT,
trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT));
} else {
+ if (streamid != 0) {
+ DPRINTF(("pci_xhci: invalid stream %u\r\n", streamid));
+ return;
+ }
ringaddr = devep->ep_ringaddr;
ccs = devep->ep_ccs;
trb = devep->ep_tr;
@@ -2561,7 +2587,7 @@ pci_xhci_dev_intr(struct usb_hci *hci, int epctx)
struct pci_xhci_softc *sc;
struct pci_xhci_portregs *p;
struct xhci_endp_ctx *ep_ctx;
- int error;
+ int error = 0;
int dir_in;
int epid;
diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c
index f9cc9ed9c3..36795e3690 100644
--- a/usr/src/cmd/bhyve/rfb.c
+++ b/usr/src/cmd/bhyve/rfb.c
@@ -278,8 +278,10 @@ rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd)
rc->enc_raw_ok = true;
break;
case RFB_ENCODING_ZLIB:
- rc->enc_zlib_ok = true;
- deflateInit(&rc->zstream, Z_BEST_SPEED);
+ if (!rc->enc_zlib_ok) {
+ deflateInit(&rc->zstream, Z_BEST_SPEED);
+ rc->enc_zlib_ok = true;
+ }
break;
case RFB_ENCODING_RESIZE:
rc->enc_resize_ok = true;
@@ -979,7 +981,7 @@ rfb_init(char *hostname, int port, int wait, char *password)
int e;
char servname[6];
struct rfb_softc *rc;
- struct addrinfo *ai;
+ struct addrinfo *ai = NULL;
struct addrinfo hints;
int on = 1;
#ifndef WITHOUT_CAPSICUM
@@ -994,6 +996,7 @@ rfb_init(char *hostname, int port, int wait, char *password)
sizeof(uint32_t));
rc->crc_width = RFB_MAX_WIDTH;
rc->crc_height = RFB_MAX_HEIGHT;
+ rc->sfd = -1;
rc->password = password;
@@ -1013,28 +1016,25 @@ rfb_init(char *hostname, int port, int wait, char *password)
if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(e));
- return(-1);
+ goto error;
}
rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0);
if (rc->sfd < 0) {
perror("socket");
- freeaddrinfo(ai);
- return (-1);
+ goto error;
}
setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(rc->sfd, ai->ai_addr, ai->ai_addrlen) < 0) {
perror("bind");
- freeaddrinfo(ai);
- return (-1);
+ goto error;
}
if (listen(rc->sfd, 1) < 0) {
perror("listen");
- freeaddrinfo(ai);
- return (-1);
+ goto error;
}
#ifndef WITHOUT_CAPSICUM
@@ -1063,6 +1063,16 @@ rfb_init(char *hostname, int port, int wait, char *password)
freeaddrinfo(ai);
return (0);
+
+ error:
+ if (ai != NULL)
+ freeaddrinfo(ai);
+ if (rc->sfd != -1)
+ close(rc->sfd);
+ free(rc->crc);
+ free(rc->crc_tmp);
+ free(rc);
+ return (-1);
}
#ifndef __FreeBSD__
diff --git a/usr/src/cmd/bhyve/uart_emul.c b/usr/src/cmd/bhyve/uart_emul.c
index c0fff61d00..fc448152ad 100644
--- a/usr/src/cmd/bhyve/uart_emul.c
+++ b/usr/src/cmd/bhyve/uart_emul.c
@@ -923,9 +923,14 @@ uart_tty_backend(struct uart_softc *sc, const char *opts)
int fd;
fd = open(opts, O_RDWR | O_NONBLOCK);
- if (fd < 0 || !isatty(fd))
+ if (fd < 0)
return (-1);
+ if (!isatty(fd)) {
+ close(fd);
+ return (-1);
+ }
+
sc->tty.rfd = sc->tty.wfd = fd;
sc->tty.opened = true;
diff --git a/usr/src/cmd/bhyve/virtio.c b/usr/src/cmd/bhyve/virtio.c
index 47a3ed29ba..2d78b016c6 100644
--- a/usr/src/cmd/bhyve/virtio.c
+++ b/usr/src/cmd/bhyve/virtio.c
@@ -428,7 +428,8 @@ vq_relchain(struct vqueue_info *vq, uint16_t idx, uint32_t iolen)
/*
* Ensure the used descriptor is visible before updating the index.
- * This is necessary on ISAs with memory ordering less strict than x86.
+ * This is necessary on ISAs with memory ordering less strict than x86
+ * (and even on x86 to act as a compiler barrier).
*/
atomic_thread_fence_rel();
vuh->vu_idx = uidx;
diff --git a/usr/src/cmd/bhyve/virtio.h b/usr/src/cmd/bhyve/virtio.h
index a2c3362ec2..521bfac681 100644
--- a/usr/src/cmd/bhyve/virtio.h
+++ b/usr/src/cmd/bhyve/virtio.h
@@ -32,6 +32,7 @@
#define _VIRTIO_H_
#include <pthread_np.h>
+#include <machine/atomic.h>
/*
* These are derived from several virtio specifications.
@@ -463,6 +464,26 @@ vq_interrupt(struct virtio_softc *vs, struct vqueue_info *vq)
}
}
+static inline void
+vq_kick_enable(struct vqueue_info *vq)
+{
+
+ vq->vq_used->vu_flags &= ~VRING_USED_F_NO_NOTIFY;
+ /*
+ * Full memory barrier to make sure the store to vu_flags
+ * happens before the load from va_idx, which results from
+ * a subsequent call to vq_has_descs().
+ */
+ atomic_thread_fence_seq_cst();
+}
+
+static inline void
+vq_kick_disable(struct vqueue_info *vq)
+{
+
+ vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
+}
+
struct iovec;
void vi_softc_linkup(struct virtio_softc *vs, struct virtio_consts *vc,
void *dev_softc, struct pci_devinst *pi,
diff --git a/usr/src/compat/freebsd/net/ieee_oui.h b/usr/src/compat/freebsd/net/ieee_oui.h
new file mode 100644
index 0000000000..068328d833
--- /dev/null
+++ b/usr/src/compat/freebsd/net/ieee_oui.h
@@ -0,0 +1,85 @@
+/* -
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ * Author: George V. Neville-Neil
+ *
+ */
+
+/* Organizationally Unique Identifier assigned by IEEE 14 Nov 2013 */
+#define OUI_FREEBSD_BASE 0x589cfc000000
+#define OUI_FREEBSD(nic) (OUI_FREEBSD_BASE | (nic))
+
+/*
+ * OUIs are most often used to uniquely identify network interfaces
+ * and occupy the first 3 bytes of both destination and source MAC
+ * addresses. The following allocations exist so that various
+ * software systems associated with FreeBSD can have unique IDs in the
+ * absence of hardware. The use of OUIs for this purpose is not fully
+ * fleshed out but is now in common use in virtualization technology.
+ *
+ * Allocations from this range are expected to be made using COMMON
+ * SENSE by developers. Do NOT take a large range just because
+ * they're currently wide open. Take the smallest useful range for
+ * your system. We have (2^24 - 2) available addresses (see Reserved
+ * Values below) but that is far from infinite.
+ *
+ * In the event of a conflict arbitration of allocation in this file
+ * is subject to core@ approval.
+ *
+ * Applications are differentiated based on the high order bit(s) of
+ * the remaining three bytes. Our first allocation has all 0s, the
+ * next allocation has the highest bit set. Allocating in this way
+ * gives us 254 allocations of 64K addresses. Address blocks can be
+ * concatenated if necessary.
+ *
+ * Reserved Values: 0x000000 and 0xffffff are reserved and MUST NOT BE
+ * allocated for any reason.
+ */
+
+/* Allocate 20 bits to bhyve */
+#define OUI_FREEBSD_BHYVE_LOW OUI_FREEBSD(0x000001)
+#define OUI_FREEBSD_BHYVE_HIGH OUI_FREEBSD(0x0fffff)
+
+/*
+ * Allocate 16 bits for a pool to give to various interfaces that need a
+ * generated address, but don't quite need to slice off a whole section of
+ * the OUI (e.g. cloned interfaces, one-off NICs of various vendors).
+ *
+ * ether_gen_addr should be used to generate an address from this pool.
+ */
+#define OUI_FREEBSD_GENERATED_MASK 0x10ffff
+#define OUI_FREEBSD_GENERATED_LOW OUI_FREEBSD(0x100000)
+#define OUI_FREEBSD_GENERATED_HIGH OUI_FREEBSD(OUI_FREEBSD_GENERATED_MASK)
+
+/* Allocate 16 bits for emulated NVMe devices */
+#define OUI_FREEBSD_NVME_MASK 0x20ffff
+#define OUI_FREEBSD_NVME_LOW OUI_FREEBSD(0x200000)
+#define OUI_FREEBSD_NVME_HIGH OUI_FREEBSD(OUI_FREEBSD_NVME_MASK)
diff --git a/usr/src/compat/freebsd/sys/param.h b/usr/src/compat/freebsd/sys/param.h
index b125f9014f..5ba21a2809 100644
--- a/usr/src/compat/freebsd/sys/param.h
+++ b/usr/src/compat/freebsd/sys/param.h
@@ -23,7 +23,7 @@
#define MAXPHYS (56 * 1024)
#endif
#define MAXHOSTNAMELEN 256
-#define SPECNAMELEN 63
+#define SPECNAMELEN 255
#ifdef _KERNEL
#include <sys/time.h>
diff --git a/usr/src/compat/freebsd/sys/pcpu.h b/usr/src/compat/freebsd/sys/pcpu.h
index f29c9c5018..1bad53c159 100644
--- a/usr/src/compat/freebsd/sys/pcpu.h
+++ b/usr/src/compat/freebsd/sys/pcpu.h
@@ -16,6 +16,8 @@
#ifndef _COMPAT_FREEBSD_SYS_PCPU_H_
#define _COMPAT_FREEBSD_SYS_PCPU_H_
-#define curcpu (CPU->cpu_id)
+#define curcpu (CPU->cpu_id)
+
+#define get_pcpu() CPU
#endif /* _COMPAT_FREEBSD_SYS_PCPU_H_ */
diff --git a/usr/src/lib/brand/bhyve/zone/boot.c b/usr/src/lib/brand/bhyve/zone/boot.c
index ceada201ac..23bbb88a34 100644
--- a/usr/src/lib/brand/bhyve/zone/boot.c
+++ b/usr/src/lib/brand/bhyve/zone/boot.c
@@ -703,7 +703,7 @@ fail:
static int
add_vmname(int *argc, char **argv)
{
- char buf[32]; /* VM_MAX_NAMELEN */
+ char buf[229]; /* VM_MAX_NAMELEN */
char *val = getenv("_ZONECFG_did");
if (val == NULL || val[0] == '\0') {
diff --git a/usr/src/uts/i86pc/io/vmm/README.sync b/usr/src/uts/i86pc/io/vmm/README.sync
index 1cddfd829e..e8aeaaffcf 100644
--- a/usr/src/uts/i86pc/io/vmm/README.sync
+++ b/usr/src/uts/i86pc/io/vmm/README.sync
@@ -1,18 +1,30 @@
The bhyve kernel module and its associated userland consumers have been updated
to the latest upstream FreeBSD sources as of:
+commit 37e8a0e0058c226e6bd0ed5c3a07ee15b1146122
+Author: mav <mav@FreeBSD.org>
+Date: Mon Sep 23 17:53:47 2019 +0000
-commit 3b9cb80b242682690203709aaff4eafae41c138f
-Author: jhb <jhb@FreeBSD.org>
-Date: Mon Jun 3 23:17:35 2019 +0000
+ Make nvme(4) driver some more NUMA aware.
- Emulate the AMD MSR_LS_CFG MSR used for various Ryzen errata.
+ - For each queue pair precalculate CPU and domain it is bound to.
+ If queue pairs are not per-CPU, then use the domain of the device.
+ - Allocate most of queue pair memory from the domain it is bound to.
+ - Bind callouts to the same CPUs as queue pair to avoid migrations.
+ - Do not assign queue pairs to each SMT thread. It just wasted
+ resources and increased lock congestions.
+ - Remove fixed multiplier of CPUs per queue pair, spread them even.
+ This allows to use more queue pairs in some hardware configurations.
+ - If queue pair serves multiple CPUs, bind different NVMe devices to
+ different CPUs.
- Writes are ignored and reads always return zero.
+ MFC after: 1 month
+ Sponsored by: iXsystems, Inc.
- Submitted by: José Albornoz <jojo@eljojo.net> (write-only version)
- Reviewed by: Patrick Mooney, cem
- MFC after: 2 weeks
- Differential Revision: https://reviews.freebsd.org/D19506
+Which corresponds to SVN revision: 352630
-Which corresponds to SVN revision: 348592
+
+NOTE:
+This sync ignores commit c8edafdabc27533d9c51eddc2896e772c16d965c.
+There are big changes to the virtio net devices that we haven't synced up yet
+because SmartOS relies heavily on viona instead.
diff --git a/usr/src/uts/i86pc/io/vmm/amd/svm.c b/usr/src/uts/i86pc/io/vmm/amd/svm.c
index 25dc3a63fa..4c5e3112c0 100644
--- a/usr/src/uts/i86pc/io/vmm/amd/svm.c
+++ b/usr/src/uts/i86pc/io/vmm/amd/svm.c
@@ -111,11 +111,6 @@ SYSCTL_INT(_hw_vmm_svm, OID_AUTO, vmcb_clean, CTLFLAG_RDTUN, &vmcb_clean,
static MALLOC_DEFINE(M_SVM, "svm", "svm");
static MALLOC_DEFINE(M_SVM_VLAPIC, "svm-vlapic", "svm-vlapic");
-#ifdef __FreeBSD__
-/* Per-CPU context area. */
-extern struct pcpu __pcpu[];
-#endif
-
static uint32_t svm_feature = ~0U; /* AMD SVM features. */
SYSCTL_UINT(_hw_vmm_svm, OID_AUTO, features, CTLFLAG_RDTUN, &svm_feature, 0,
"SVM features advertised by CPUID.8000000AH:EDX");
@@ -2162,11 +2157,7 @@ svm_vmrun(void *arg, int vcpu, register_t rip, pmap_t pmap,
/* Launch Virtual Machine. */
VCPU_CTR1(vm, vcpu, "Resume execution at %#lx", state->rip);
svm_dr_enter_guest(gctx);
-#ifdef __FreeBSD__
- svm_launch(vmcb_pa, gctx, &__pcpu[curcpu]);
-#else
- svm_launch(vmcb_pa, gctx, CPU);
-#endif
+ svm_launch(vmcb_pa, gctx, get_pcpu());
svm_dr_leave_guest(gctx);
CPU_CLR_ATOMIC(curcpu, &pmap->pm_active);
@@ -2422,25 +2413,24 @@ svm_restorectx(void *arg, int vcpu)
#endif /* __FreeBSD__ */
struct vmm_ops vmm_ops_amd = {
- svm_init,
- svm_cleanup,
- svm_restore,
- svm_vminit,
- svm_vmrun,
- svm_vmcleanup,
- svm_getreg,
- svm_setreg,
- vmcb_getdesc,
- vmcb_setdesc,
- svm_getcap,
- svm_setcap,
- svm_npt_alloc,
- svm_npt_free,
- svm_vlapic_init,
- svm_vlapic_cleanup,
-
+ .init = svm_init,
+ .cleanup = svm_cleanup,
+ .resume = svm_restore,
+ .vminit = svm_vminit,
+ .vmrun = svm_vmrun,
+ .vmcleanup = svm_vmcleanup,
+ .vmgetreg = svm_getreg,
+ .vmsetreg = svm_setreg,
+ .vmgetdesc = vmcb_getdesc,
+ .vmsetdesc = vmcb_setdesc,
+ .vmgetcap = svm_getcap,
+ .vmsetcap = svm_setcap,
+ .vmspace_alloc = svm_npt_alloc,
+ .vmspace_free = svm_npt_free,
+ .vlapic_init = svm_vlapic_init,
+ .vlapic_cleanup = svm_vlapic_cleanup,
#ifndef __FreeBSD__
- svm_savectx,
- svm_restorectx,
+ .vmsavectx = svm_savectx,
+ .vmrestorectx = svm_restorectx,
#endif
};
diff --git a/usr/src/uts/i86pc/io/vmm/intel/vmx.c b/usr/src/uts/i86pc/io/vmm/intel/vmx.c
index 131615d576..83e137973e 100644
--- a/usr/src/uts/i86pc/io/vmm/intel/vmx.c
+++ b/usr/src/uts/i86pc/io/vmm/intel/vmx.c
@@ -2355,20 +2355,20 @@ ept_fault_type(uint64_t ept_qual)
return (fault_type);
}
-static boolean_t
+static bool
ept_emulation_fault(uint64_t ept_qual)
{
int read, write;
/* EPT fault on an instruction fetch doesn't make sense here */
if (ept_qual & EPT_VIOLATION_INST_FETCH)
- return (FALSE);
+ return (false);
/* EPT fault must be a read fault or a write fault */
read = ept_qual & EPT_VIOLATION_DATA_READ ? 1 : 0;
write = ept_qual & EPT_VIOLATION_DATA_WRITE ? 1 : 0;
if ((read | write) == 0)
- return (FALSE);
+ return (false);
/*
* The EPT violation must have been caused by accessing a
@@ -2377,10 +2377,10 @@ ept_emulation_fault(uint64_t ept_qual)
*/
if ((ept_qual & EPT_VIOLATION_GLA_VALID) == 0 ||
(ept_qual & EPT_VIOLATION_XLAT_VALID) == 0) {
- return (FALSE);
+ return (false);
}
- return (TRUE);
+ return (true);
}
static __inline int
@@ -4283,26 +4283,26 @@ vmx_restorectx(void *arg, int vcpu)
#endif /* __FreeBSD__ */
struct vmm_ops vmm_ops_intel = {
- vmx_init,
- vmx_cleanup,
- vmx_restore,
- vmx_vminit,
- vmx_run,
- vmx_vmcleanup,
- vmx_getreg,
- vmx_setreg,
- vmx_getdesc,
- vmx_setdesc,
- vmx_getcap,
- vmx_setcap,
- ept_vmspace_alloc,
- ept_vmspace_free,
- vmx_vlapic_init,
- vmx_vlapic_cleanup,
+ .init = vmx_init,
+ .cleanup = vmx_cleanup,
+ .resume = vmx_restore,
+ .vminit = vmx_vminit,
+ .vmrun = vmx_run,
+ .vmcleanup = vmx_vmcleanup,
+ .vmgetreg = vmx_getreg,
+ .vmsetreg = vmx_setreg,
+ .vmgetdesc = vmx_getdesc,
+ .vmsetdesc = vmx_setdesc,
+ .vmgetcap = vmx_getcap,
+ .vmsetcap = vmx_setcap,
+ .vmspace_alloc = ept_vmspace_alloc,
+ .vmspace_free = ept_vmspace_free,
+ .vlapic_init = vmx_vlapic_init,
+ .vlapic_cleanup = vmx_vlapic_cleanup,
#ifndef __FreeBSD__
- vmx_savectx,
- vmx_restorectx,
+ .vmsavectx = vmx_savectx,
+ .vmrestorectx = vmx_restorectx,
#endif
};
diff --git a/usr/src/uts/i86pc/io/vmm/intel/vmx_msr.c b/usr/src/uts/i86pc/io/vmm/intel/vmx_msr.c
index 4a1a2cd358..9121e46b40 100644
--- a/usr/src/uts/i86pc/io/vmm/intel/vmx_msr.c
+++ b/usr/src/uts/i86pc/io/vmm/intel/vmx_msr.c
@@ -48,24 +48,18 @@ __FBSDID("$FreeBSD$");
#include "vmx.h"
#include "vmx_msr.h"
-static boolean_t
+static bool
vmx_ctl_allows_one_setting(uint64_t msr_val, int bitpos)
{
- if (msr_val & (1UL << (bitpos + 32)))
- return (TRUE);
- else
- return (FALSE);
+ return ((msr_val & (1UL << (bitpos + 32))) != 0);
}
-static boolean_t
+static bool
vmx_ctl_allows_zero_setting(uint64_t msr_val, int bitpos)
{
- if ((msr_val & (1UL << bitpos)) == 0)
- return (TRUE);
- else
- return (FALSE);
+ return ((msr_val & (1UL << bitpos)) == 0);
}
uint32_t
@@ -92,16 +86,13 @@ vmx_set_ctlreg(int ctl_reg, int true_ctl_reg, uint32_t ones_mask,
{
int i;
uint64_t val, trueval;
- boolean_t true_ctls_avail, one_allowed, zero_allowed;
+ bool true_ctls_avail, one_allowed, zero_allowed;
/* We cannot ask the same bit to be set to both '1' and '0' */
if ((ones_mask ^ zeros_mask) != (ones_mask | zeros_mask))
return (EINVAL);
- if (rdmsr(MSR_VMX_BASIC) & (1UL << 55))
- true_ctls_avail = TRUE;
- else
- true_ctls_avail = FALSE;
+ true_ctls_avail = (rdmsr(MSR_VMX_BASIC) & (1UL << 55)) != 0;
val = rdmsr(ctl_reg);
if (true_ctls_avail)
diff --git a/usr/src/uts/i86pc/io/vmm/intel/vtd.c b/usr/src/uts/i86pc/io/vmm/intel/vtd.c
index 902080e34c..41c2c5b2f8 100644
--- a/usr/src/uts/i86pc/io/vmm/intel/vtd.c
+++ b/usr/src/uts/i86pc/io/vmm/intel/vtd.c
@@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$");
* Architecture Spec, September 2008.
*/
+#define VTD_DRHD_INCLUDE_PCI_ALL(Flags) (((Flags) >> 0) & 0x1)
+
/* Section 10.4 "Register Descriptions" */
struct vtdmap {
volatile uint32_t version;
@@ -118,10 +120,11 @@ struct domain {
static SLIST_HEAD(, domain) domhead;
#define DRHD_MAX_UNITS 8
-static int drhd_num;
-static struct vtdmap *vtdmaps[DRHD_MAX_UNITS];
-static int max_domains;
-typedef int (*drhd_ident_func_t)(void);
+static ACPI_DMAR_HARDWARE_UNIT *drhds[DRHD_MAX_UNITS];
+static int drhd_num;
+static struct vtdmap *vtdmaps[DRHD_MAX_UNITS];
+static int max_domains;
+typedef int (*drhd_ident_func_t)(void);
#ifndef __FreeBSD__
static dev_info_t *vtddips[DRHD_MAX_UNITS];
#endif
@@ -180,6 +183,69 @@ domain_id(void)
return (id);
}
+static struct vtdmap *
+vtd_device_scope(uint16_t rid)
+{
+ int i, remaining, pathremaining;
+ char *end, *pathend;
+ struct vtdmap *vtdmap;
+ ACPI_DMAR_HARDWARE_UNIT *drhd;
+ ACPI_DMAR_DEVICE_SCOPE *device_scope;
+ ACPI_DMAR_PCI_PATH *path;
+
+ for (i = 0; i < drhd_num; i++) {
+ drhd = drhds[i];
+
+ if (VTD_DRHD_INCLUDE_PCI_ALL(drhd->Flags)) {
+ /*
+ * From Intel VT-d arch spec, version 3.0:
+ * If a DRHD structure with INCLUDE_PCI_ALL flag Set is reported
+ * for a Segment, it must be enumerated by BIOS after all other
+ * DRHD structures for the same Segment.
+ */
+ vtdmap = vtdmaps[i];
+ return(vtdmap);
+ }
+
+ end = (char *)drhd + drhd->Header.Length;
+ remaining = drhd->Header.Length - sizeof(ACPI_DMAR_HARDWARE_UNIT);
+ while (remaining > sizeof(ACPI_DMAR_DEVICE_SCOPE)) {
+ device_scope = (ACPI_DMAR_DEVICE_SCOPE *)(end - remaining);
+ remaining -= device_scope->Length;
+
+ switch (device_scope->EntryType){
+ /* 0x01 and 0x02 are PCI device entries */
+ case 0x01:
+ case 0x02:
+ break;
+ default:
+ continue;
+ }
+
+ if (PCI_RID2BUS(rid) != device_scope->Bus)
+ continue;
+
+ pathend = (char *)device_scope + device_scope->Length;
+ pathremaining = device_scope->Length - sizeof(ACPI_DMAR_DEVICE_SCOPE);
+ while (pathremaining >= sizeof(ACPI_DMAR_PCI_PATH)) {
+ path = (ACPI_DMAR_PCI_PATH *)(pathend - pathremaining);
+ pathremaining -= sizeof(ACPI_DMAR_PCI_PATH);
+
+ if (PCI_RID2SLOT(rid) != path->Device)
+ continue;
+ if (PCI_RID2FUNC(rid) != path->Function)
+ continue;
+
+ vtdmap = vtdmaps[i];
+ return (vtdmap);
+ }
+ }
+ }
+
+ /* No matching scope */
+ return (NULL);
+}
+
static void
vtd_wbflush(struct vtdmap *vtdmap)
{
@@ -285,7 +351,7 @@ extern dev_info_t *vtd_get_dip(ACPI_DMAR_HARDWARE_UNIT *, int);
static int
vtd_init(void)
{
- int i, units, remaining;
+ int i, units, remaining, tmp;
struct vtdmap *vtdmap;
vm_paddr_t ctx_paddr;
char *end;
@@ -342,16 +408,16 @@ vtd_init(void)
break;
drhd = (ACPI_DMAR_HARDWARE_UNIT *)hdr;
+ drhds[units] = drhd;
#ifdef __FreeBSD__
- vtdmaps[units++] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address);
+ vtdmaps[units] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address);
#else
vtddips[units] = vtd_get_dip(drhd, units);
vtdmaps[units] = (struct vtdmap *)vtd_map(vtddips[units]);
if (vtdmaps[units] == NULL)
goto fail;
- units++;
#endif
- if (units >= DRHD_MAX_UNITS)
+ if (++units >= DRHD_MAX_UNITS)
break;
remaining -= hdr->Length;
}
@@ -363,12 +429,18 @@ vtd_init(void)
skip_dmar:
#endif
drhd_num = units;
- vtdmap = vtdmaps[0];
- if (VTD_CAP_CM(vtdmap->cap) != 0)
- panic("vtd_init: invalid caching mode");
+ max_domains = 64 * 1024; /* maximum valid value */
+ for (i = 0; i < drhd_num; i++){
+ vtdmap = vtdmaps[i];
+
+ if (VTD_CAP_CM(vtdmap->cap) != 0)
+ panic("vtd_init: invalid caching mode");
- max_domains = vtd_max_domains(vtdmap);
+ /* take most compatible (minimum) value */
+ if ((tmp = vtd_max_domains(vtdmap)) < max_domains)
+ max_domains = tmp;
+ }
/*
* Set up the root-table to point to the context-entry tables
@@ -459,7 +531,6 @@ vtd_add_device(void *arg, uint16_t rid)
struct vtdmap *vtdmap;
uint8_t bus;
- vtdmap = vtdmaps[0];
bus = PCI_RID2BUS(rid);
ctxp = ctx_tables[bus];
pt_paddr = vtophys(dom->ptp);
@@ -471,6 +542,10 @@ vtd_add_device(void *arg, uint16_t rid)
(uint16_t)(ctxp[idx + 1] >> 8));
}
+ if ((vtdmap = vtd_device_scope(rid)) == NULL)
+ panic("vtd_add_device: device %x is not in scope for "
+ "any DMA remapping unit", rid);
+
/*
* Order is important. The 'present' bit is set only after all fields
* of the context pointer are initialized.
@@ -654,8 +729,6 @@ vtd_create_domain(vm_paddr_t maxaddr)
if (drhd_num <= 0)
panic("vtd_create_domain: no dma remapping hardware available");
- vtdmap = vtdmaps[0];
-
/*
* Calculate AGAW.
* Section 3.4.2 "Adjusted Guest Address Width", Architecture Spec.
@@ -680,7 +753,14 @@ vtd_create_domain(vm_paddr_t maxaddr)
pt_levels = 2;
sagaw = 30;
addrwidth = 0;
- tmp = VTD_CAP_SAGAW(vtdmap->cap);
+
+ tmp = ~0;
+ for (i = 0; i < drhd_num; i++) {
+ vtdmap = vtdmaps[i];
+ /* take most compatible value */
+ tmp &= VTD_CAP_SAGAW(vtdmap->cap);
+ }
+
for (i = 0; i < 5; i++) {
if ((tmp & (1 << i)) != 0 && sagaw >= agaw)
break;
@@ -692,8 +772,8 @@ vtd_create_domain(vm_paddr_t maxaddr)
}
if (i >= 5) {
- panic("vtd_create_domain: SAGAW 0x%lx does not support AGAW %d",
- VTD_CAP_SAGAW(vtdmap->cap), agaw);
+ panic("vtd_create_domain: SAGAW 0x%x does not support AGAW %d",
+ tmp, agaw);
}
dom = malloc(sizeof(struct domain), M_VTD, M_ZERO | M_WAITOK);
@@ -721,7 +801,12 @@ vtd_create_domain(vm_paddr_t maxaddr)
* There is not any code to deal with the demotion at the moment
* so we disable superpage mappings altogether.
*/
- dom->spsmask = VTD_CAP_SPS(vtdmap->cap);
+ dom->spsmask = ~0;
+ for (i = 0; i < drhd_num; i++) {
+ vtdmap = vtdmaps[i];
+ /* take most compatible value */
+ dom->spsmask &= VTD_CAP_SPS(vtdmap->cap);
+ }
#endif
#else
/*
diff --git a/usr/src/uts/i86pc/io/vmm/io/vatpit.c b/usr/src/uts/i86pc/io/vmm/io/vatpit.c
index 9b3e7376d5..03f63798e7 100644
--- a/usr/src/uts/i86pc/io/vmm/io/vatpit.c
+++ b/usr/src/uts/i86pc/io/vmm/io/vatpit.c
@@ -3,6 +3,7 @@
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2011 NetApp, Inc.
* All rights reserved.
+ * Copyright (c) 2018 Joyent, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -180,20 +181,20 @@ static void
pit_timer_start_cntr0(struct vatpit *vatpit)
{
struct channel *c;
+ struct bintime now, delta;
+ sbintime_t precision;
c = &vatpit->channel[0];
if (c->initial != 0) {
- sbintime_t precision;
- struct bintime now, delta;
-
delta.sec = 0;
delta.frac = vatpit->freq_bt.frac * c->initial;
bintime_add(&c->callout_bt, &delta);
precision = bttosbt(delta) >> tc_precexp;
/*
- * Reset 'callout_bt' if the time that the callout was supposed
- * to fire is more than 'c->initial' ticks in the past.
+ * Reset 'callout_bt' if the time that the callout
+ * was supposed to fire is more than 'c->initial'
+ * ticks in the past.
*/
binuptime(&now);
if (bintime_cmp(&c->callout_bt, &now, <)) {
diff --git a/usr/src/uts/i86pc/io/vmm/io/vlapic.c b/usr/src/uts/i86pc/io/vmm/io/vlapic.c
index 4e58249c8d..687e0e6a8e 100644
--- a/usr/src/uts/i86pc/io/vmm/io/vlapic.c
+++ b/usr/src/uts/i86pc/io/vmm/io/vlapic.c
@@ -339,7 +339,7 @@ vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset)
return (&lapic->lvt_cmci);
case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
i = (offset - APIC_OFFSET_TIMER_LVT) >> 2;
- return ((&lapic->lvt_timer) + i);;
+ return ((&lapic->lvt_timer) + i);
default:
panic("vlapic_get_lvt: invalid LVT\n");
}
@@ -854,7 +854,8 @@ vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys,
*/
CPU_ZERO(dmask);
vcpuid = vm_apicid2vcpuid(vm, dest);
- if (vcpuid < vm_get_maxcpus(vm))
+ amask = vm_active_cpus(vm);
+ if (vcpuid < vm_get_maxcpus(vm) && CPU_ISSET(vcpuid, &amask))
CPU_SET(vcpuid, dmask);
} else {
/*
diff --git a/usr/src/uts/i86pc/io/vmm/vm/vm_page.h b/usr/src/uts/i86pc/io/vmm/vm/vm_page.h
index 4559fe6d4c..65f3319c97 100644
--- a/usr/src/uts/i86pc/io/vmm/vm/vm_page.h
+++ b/usr/src/uts/i86pc/io/vmm/vm/vm_page.h
@@ -19,9 +19,9 @@
#include "vm_glue.h"
-void vm_page_lock(vm_page_t);
-void vm_page_unhold(vm_page_t);
-void vm_page_unlock(vm_page_t);
+#define PQ_ACTIVE 1
+
+void vm_page_unwire(vm_page_t , uint8_t);
#define VM_PAGE_TO_PHYS(page) (mmu_ptob((uintptr_t)((page)->vmp_pfn)))
diff --git a/usr/src/uts/i86pc/io/vmm/vmm.c b/usr/src/uts/i86pc/io/vmm/vmm.c
index 47a5f26cb7..2238536121 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm.c
+++ b/usr/src/uts/i86pc/io/vmm/vmm.c
@@ -1008,7 +1008,7 @@ vmm_sysmem_maxaddr(struct vm *vm)
}
static void
-vm_iommu_modify(struct vm *vm, boolean_t map)
+vm_iommu_modify(struct vm *vm, bool map)
{
int i, sz;
vm_paddr_t gpa, hpa;
@@ -1083,8 +1083,8 @@ vm_iommu_modify(struct vm *vm, boolean_t map)
#endif
}
-#define vm_iommu_unmap(vm) vm_iommu_modify((vm), FALSE)
-#define vm_iommu_map(vm) vm_iommu_modify((vm), TRUE)
+#define vm_iommu_unmap(vm) vm_iommu_modify((vm), false)
+#define vm_iommu_map(vm) vm_iommu_modify((vm), true)
#ifdef __FreeBSD__
int
@@ -1193,9 +1193,7 @@ vm_gpa_release(void *cookie)
{
vm_page_t m = cookie;
- vm_page_lock(m);
- vm_page_unhold(m);
- vm_page_unlock(m);
+ vm_page_unwire(m, PQ_ACTIVE);
}
int
@@ -1234,20 +1232,20 @@ vm_set_register(struct vm *vm, int vcpuid, int reg, uint64_t val)
return (0);
}
-static boolean_t
+static bool
is_descriptor_table(int reg)
{
switch (reg) {
case VM_REG_GUEST_IDTR:
case VM_REG_GUEST_GDTR:
- return (TRUE);
+ return (true);
default:
- return (FALSE);
+ return (false);
}
}
-static boolean_t
+static bool
is_segment_register(int reg)
{
@@ -1260,9 +1258,9 @@ is_segment_register(int reg)
case VM_REG_GUEST_GS:
case VM_REG_GUEST_TR:
case VM_REG_GUEST_LDTR:
- return (TRUE);
+ return (true);
default:
- return (FALSE);
+ return (false);
}
}
@@ -2622,12 +2620,12 @@ vm_hpet(struct vm *vm)
}
#ifdef __FreeBSD__
-boolean_t
+bool
vmm_is_pptdev(int bus, int slot, int func)
{
- int found, i, n;
- int b, s, f;
+ int b, f, i, n, s;
char *val, *cp, *cp2;
+ bool found;
/*
* XXX
@@ -2641,7 +2639,7 @@ vmm_is_pptdev(int bus, int slot, int func)
const char *names[] = { "pptdevs", "pptdevs2", "pptdevs3", NULL };
/* set pptdevs="1/2/3 4/5/6 7/8/9 10/11/12" */
- found = 0;
+ found = false;
for (i = 0; names[i] != NULL && !found; i++) {
cp = val = kern_getenv(names[i]);
while (cp != NULL && *cp != '\0') {
@@ -2650,7 +2648,7 @@ vmm_is_pptdev(int bus, int slot, int func)
n = sscanf(cp, "%d/%d/%d", &b, &s, &f);
if (n == 3 && bus == b && slot == s && func == f) {
- found = 1;
+ found = true;
break;
}
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_host.h b/usr/src/uts/i86pc/io/vmm/vmm_host.h
index f12047819d..e0ea1ec927 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_host.h
+++ b/usr/src/uts/i86pc/io/vmm/vmm_host.h
@@ -100,17 +100,12 @@ vmm_get_host_gdtrbase(void)
#endif
}
-#ifdef __FreeBSD__
-struct pcpu;
-extern struct pcpu __pcpu[];
-#endif
-
static __inline uint64_t
vmm_get_host_gsbase(void)
{
#ifdef __FreeBSD__
- return ((uint64_t)&__pcpu[curcpu]);
+ return ((uint64_t)get_pcpu());
#else
return (rdmsr(MSR_GSBASE));
#endif
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c b/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c
index ea96cd8db0..4a4fb07eba 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c
+++ b/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c
@@ -91,6 +91,7 @@ enum {
VIE_OP_TYPE_BITTEST,
VIE_OP_TYPE_TWOB_GRP15,
VIE_OP_TYPE_ADD,
+ VIE_OP_TYPE_TEST,
VIE_OP_TYPE_LAST
};
@@ -235,6 +236,12 @@ static const struct vie_op one_byte_opcodes[256] = {
.op_byte = 0x8F,
.op_type = VIE_OP_TYPE_POP,
},
+ [0xF7] = {
+ /* XXX Group 3 extended opcode - not just TEST */
+ .op_byte = 0xF7,
+ .op_type = VIE_OP_TYPE_TEST,
+ .op_flags = VIE_OP_F_IMM,
+ },
[0xFF] = {
/* XXX Group 5 extended opcode - not just PUSH */
.op_byte = 0xFF,
@@ -465,6 +472,41 @@ getaddflags(int opsize, uint64_t x, uint64_t y)
return (getaddflags64(x, y));
}
+/*
+ * Return the status flags that would result from doing (x & y).
+ */
+#define GETANDFLAGS(sz) \
+static u_long \
+getandflags##sz(uint##sz##_t x, uint##sz##_t y) \
+{ \
+ u_long rflags; \
+ \
+ __asm __volatile("and %2,%1; pushfq; popq %0" : \
+ "=r" (rflags), "+r" (x) : "m" (y)); \
+ return (rflags); \
+} struct __hack
+
+GETANDFLAGS(8);
+GETANDFLAGS(16);
+GETANDFLAGS(32);
+GETANDFLAGS(64);
+
+static u_long
+getandflags(int opsize, uint64_t x, uint64_t y)
+{
+ KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
+ ("getandflags: invalid operand size %d", opsize));
+
+ if (opsize == 1)
+ return (getandflags8(x, y));
+ else if (opsize == 2)
+ return (getandflags16(x, y));
+ else if (opsize == 4)
+ return (getandflags32(x, y));
+ else
+ return (getandflags64(x, y));
+}
+
static int
emulate_mov(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
@@ -1234,6 +1276,55 @@ emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
}
static int
+emulate_test(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
+ mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
+{
+ int error, size;
+ uint64_t op1, rflags, rflags2;
+
+ size = vie->opsize;
+ error = EINVAL;
+
+ switch (vie->op.op_byte) {
+ case 0xF7:
+ /*
+ * F7 /0 test r/m16, imm16
+ * F7 /0 test r/m32, imm32
+ * REX.W + F7 /0 test r/m64, imm32 sign-extended to 64
+ *
+ * Test mem (ModRM:r/m) with immediate and set status
+ * flags according to the results. The comparison is
+ * performed by anding the immediate from the first
+ * operand and then setting the status flags.
+ */
+ if ((vie->reg & 7) != 0)
+ return (EINVAL);
+
+ error = memread(vm, vcpuid, gpa, &op1, size, arg);
+ if (error)
+ return (error);
+
+ rflags2 = getandflags(size, op1, vie->immediate);
+ break;
+ default:
+ return (EINVAL);
+ }
+ error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
+ if (error)
+ return (error);
+
+ /*
+ * OF and CF are cleared; the SF, ZF and PF flags are set according
+ * to the result; AF is undefined.
+ */
+ rflags &= ~RFLAGS_STATUS_BITS;
+ rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
+
+ error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
+ return (error);
+}
+
+static int
emulate_add(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
{
@@ -1658,6 +1749,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
error = emulate_add(vm, vcpuid, gpa, vie, memread,
memwrite, memarg);
break;
+ case VIE_OP_TYPE_TEST:
+ error = emulate_test(vm, vcpuid, gpa, vie,
+ memread, memwrite, memarg);
+ break;
default:
error = EINVAL;
break;
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_lapic.c b/usr/src/uts/i86pc/io/vmm/vmm_lapic.c
index 43b2bebe97..57e4cfddf3 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_lapic.c
+++ b/usr/src/uts/i86pc/io/vmm/vmm_lapic.c
@@ -149,13 +149,10 @@ lapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg)
return (0);
}
-static boolean_t
+static bool
x2apic_msr(u_int msr)
{
- if (msr >= 0x800 && msr <= 0xBFF)
- return (TRUE);
- else
- return (FALSE);
+ return (msr >= 0x800 && msr <= 0xBFF);
}
static u_int
@@ -165,14 +162,11 @@ x2apic_msr_to_regoff(u_int msr)
return ((msr - 0x800) << 4);
}
-boolean_t
+bool
lapic_msr(u_int msr)
{
- if (x2apic_msr(msr) || (msr == MSR_APICBASE))
- return (TRUE);
- else
- return (FALSE);
+ return (x2apic_msr(msr) || msr == MSR_APICBASE);
}
int
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_lapic.h b/usr/src/uts/i86pc/io/vmm/vmm_lapic.h
index da3b0ff660..58508ad70b 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_lapic.h
+++ b/usr/src/uts/i86pc/io/vmm/vmm_lapic.h
@@ -45,7 +45,7 @@
struct vm;
-boolean_t lapic_msr(u_int num);
+bool lapic_msr(u_int num);
int lapic_rdmsr(struct vm *vm, int cpu, u_int msr, uint64_t *rval,
bool *retu);
int lapic_wrmsr(struct vm *vm, int cpu, u_int msr, uint64_t wval,
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_vm.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_vm.c
index 66a67d9529..ddae4202b7 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_sol_vm.c
+++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_vm.c
@@ -982,35 +982,19 @@ vm_segmap_space(struct vmspace *vms, off_t off, struct as *as, caddr_t *addrp,
}
void
-vm_page_lock(vm_page_t vmp)
+vm_page_unwire(vm_page_t vmp, uint8_t nqueue __unused)
{
ASSERT(!MUTEX_HELD(&vmp->vmp_lock));
-
mutex_enter(&vmp->vmp_lock);
-}
-
-void
-vm_page_unlock(vm_page_t vmp)
-{
- boolean_t purge = (vmp->vmp_pfn == PFN_INVALID);
-
- ASSERT(MUTEX_HELD(&vmp->vmp_lock));
-
- mutex_exit(&vmp->vmp_lock);
- if (purge) {
- mutex_destroy(&vmp->vmp_lock);
- kmem_free(vmp, sizeof (*vmp));
- }
-}
-
-void
-vm_page_unhold(vm_page_t vmp)
-{
- ASSERT(MUTEX_HELD(&vmp->vmp_lock));
VERIFY(vmp->vmp_pfn != PFN_INVALID);
vm_object_deallocate(vmp->vmp_obj_held);
vmp->vmp_obj_held = NULL;
vmp->vmp_pfn = PFN_INVALID;
+
+ mutex_exit(&vmp->vmp_lock);
+
+ mutex_destroy(&vmp->vmp_lock);
+ kmem_free(vmp, sizeof (*vmp));
}
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_util.c b/usr/src/uts/i86pc/io/vmm/vmm_util.c
index 3eadfe57e5..b8acff9bbc 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_util.c
+++ b/usr/src/uts/i86pc/io/vmm/vmm_util.c
@@ -50,26 +50,20 @@ __FBSDID("$FreeBSD$");
#include "vmm_util.h"
-boolean_t
+bool
vmm_is_intel(void)
{
- if (strcmp(cpu_vendor, "GenuineIntel") == 0)
- return (TRUE);
- else
- return (FALSE);
+ return (strcmp(cpu_vendor, "GenuineIntel") == 0);
}
-boolean_t
+bool
vmm_is_amd(void)
{
- if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
- return (TRUE);
- else
- return (FALSE);
+ return (strcmp(cpu_vendor, "AuthenticAMD") == 0);
}
-boolean_t
+bool
vmm_supports_1G_pages(void)
{
unsigned int regs[4];
@@ -82,9 +76,9 @@ vmm_supports_1G_pages(void)
if (cpu_exthigh >= 0x80000001) {
do_cpuid(0x80000001, regs);
if (regs[3] & (1 << 26))
- return (TRUE);
+ return (true);
}
- return (FALSE);
+ return (false);
}
#ifdef __FreeBSD__
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_util.h b/usr/src/uts/i86pc/io/vmm/vmm_util.h
index fc7e7364c7..8c65e7e3a6 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_util.h
+++ b/usr/src/uts/i86pc/io/vmm/vmm_util.h
@@ -33,9 +33,9 @@
struct trapframe;
-boolean_t vmm_is_intel(void);
-boolean_t vmm_is_amd(void);
-boolean_t vmm_supports_1G_pages(void);
+bool vmm_is_intel(void);
+bool vmm_is_amd(void);
+bool vmm_supports_1G_pages(void);
void dump_trapframe(struct trapframe *tf);
diff --git a/usr/src/uts/i86pc/sys/vmm.h b/usr/src/uts/i86pc/sys/vmm.h
index ac8f14b042..0bbc219b7f 100644
--- a/usr/src/uts/i86pc/sys/vmm.h
+++ b/usr/src/uts/i86pc/sys/vmm.h
@@ -127,10 +127,39 @@ enum x2apic_state {
#define VM_INTINFO_HWEXCEPTION (3 << 8)
#define VM_INTINFO_SWINTR (4 << 8)
-
-#define VM_MAX_NAMELEN 32
+#ifndef __FreeBSD__
+/*
+ * illumos doesn't have a limitation based on SPECNAMELEN like FreeBSD does.
+ * Instead of picking an arbitrary value we will just rely on the same
+ * calculation that's made below. If this calculation ever changes we need to
+ * update the the VM_MAX_NAMELEN mapping in the bhyve brand's boot.c file.
+ */
+#else
+/*
+ * The VM name has to fit into the pathname length constraints of devfs,
+ * governed primarily by SPECNAMELEN. The length is the total number of
+ * characters in the full path, relative to the mount point and not
+ * including any leading '/' characters.
+ * A prefix and a suffix are added to the name specified by the user.
+ * The prefix is usually "vmm/" or "vmm.io/", but can be a few characters
+ * longer for future use.
+ * The suffix is a string that identifies a bootrom image or some similar
+ * image that is attached to the VM. A separator character gets added to
+ * the suffix automatically when generating the full path, so it must be
+ * accounted for, reducing the effective length by 1.
+ * The effective length of a VM name is 229 bytes for FreeBSD 13 and 37
+ * bytes for FreeBSD 12. A minimum length is set for safety and supports
+ * a SPECNAMELEN as small as 32 on old systems.
+ */
+#endif
+#define VM_MAX_PREFIXLEN 10
+#define VM_MAX_SUFFIXLEN 15
+#define VM_MIN_NAMELEN 6
+#define VM_MAX_NAMELEN \
+ (SPECNAMELEN - VM_MAX_PREFIXLEN - VM_MAX_SUFFIXLEN - 1)
#ifdef _KERNEL
+CTASSERT(VM_MAX_NAMELEN >= VM_MIN_NAMELEN);
struct vm;
struct vm_exception;
@@ -309,12 +338,12 @@ vcpu_reqidle(struct vm_eventinfo *info)
int vcpu_debugged(struct vm *vm, int vcpuid);
/*
- * Return 1 if device indicated by bus/slot/func is supposed to be a
+ * Return true if device indicated by bus/slot/func is supposed to be a
* pci passthrough device.
*
- * Return 0 otherwise.
+ * Return false otherwise.
*/
-int vmm_is_pptdev(int bus, int slot, int func);
+bool vmm_is_pptdev(int bus, int slot, int func);
void *vm_iommu_domain(struct vm *vm);