mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-16 23:29:50 +00:00
WIFI dev has no FLR, so 'reset' in sysfs calls secondary bus reset, which cause PCI configuration mess(all FF) then passthrough failure. To fix it, this patch makes no reset before passthrough by default, until append this option. Signed-off-by: Edwin Zhai <edwin.zhai@intel.com> Acked-by: Anthony Xu <anthony.xu@intel.com>
1966 lines
52 KiB
C
1966 lines
52 KiB
C
/*-
|
|
* Copyright (c) 2011 NetApp, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/user.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <pciaccess.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "iodev.h"
|
|
#include "vmmapi.h"
|
|
#include "pciio.h"
|
|
#include "pci_core.h"
|
|
#include "acpi.h"
|
|
|
|
#ifndef _PATH_DEVPCI
|
|
#define _PATH_DEVPCI "/dev/pci"
|
|
#endif
|
|
|
|
#ifndef _PATH_DEVIO
|
|
#define _PATH_DEVIO "/dev/io"
|
|
#endif
|
|
|
|
#ifndef _PATH_MEM
|
|
#define _PATH_MEM "/dev/mem"
|
|
#endif
|
|
|
|
#ifndef PCI_COMMAND_INTX_DISABLE
|
|
#define PCI_COMMAND_INTX_DISABLE ((uint16_t)0x400)
|
|
#endif
|
|
|
|
/* Used to temporarily set mmc & mme to support only one vector for MSI,
|
|
* remove it when multiple vectors for MSI is ready.
|
|
*/
|
|
#define FORCE_MSI_SINGLE_VECTOR 1
|
|
|
|
#define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1)
|
|
#define MSIX_CAPLEN 12
|
|
|
|
#define PCI_BDF_GPU 0x00000010 /* 00:02.0 */
|
|
|
|
/* Some audio driver get topology data from ACPI NHLT table, thus need copy host
|
|
* NHLT to guest. Default audio driver doesn't require this, so make it off by
|
|
* default to avoid unexpected failure.
|
|
*/
|
|
#define AUDIO_NHLT_HACK 0
|
|
|
|
static int iofd = -1;
|
|
static int memfd = -1;
|
|
|
|
/* reference count for libpciaccess init/deinit */
|
|
static int pciaccess_ref_cnt;
|
|
static pthread_mutex_t ref_cnt_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/* Not check reset capability before assign ptdev.
|
|
* Set false by default, that is, always check.
|
|
*/
|
|
static bool no_reset = false;
|
|
|
|
struct passthru_dev {
|
|
struct pci_vdev *dev;
|
|
struct pcibar bar[PCI_BARMAX + 1];
|
|
struct {
|
|
int capoff;
|
|
int msgctrl;
|
|
int emulated;
|
|
} msi;
|
|
struct {
|
|
int capoff;
|
|
int table_size;
|
|
} msix;
|
|
bool pcie_cap;
|
|
struct pcisel sel;
|
|
int phys_pin;
|
|
uint16_t phys_bdf;
|
|
struct pci_device *phys_dev;
|
|
/* Options for passthrough device:
|
|
* need_reset - reset dev before passthrough
|
|
*/
|
|
bool need_reset;
|
|
};
|
|
|
|
void ptdev_no_reset(bool enable)
|
|
{
|
|
no_reset = enable;
|
|
}
|
|
|
|
static int
|
|
msi_caplen(int msgctrl)
|
|
{
|
|
int len;
|
|
|
|
len = 10; /* minimum length of msi capability */
|
|
|
|
if (msgctrl & PCIM_MSICTRL_64BIT)
|
|
len += 4;
|
|
|
|
/*
|
|
* Ignore the 'mask' and 'pending' bits in the MSI capability
|
|
* (msgctrl & PCIM_MSICTRL_VECTOR).
|
|
* Ignore 10 bytes in total (2-byte reserved, 4-byte mask bits,
|
|
* 4-byte pending bits).
|
|
* We'll let the guest manipulate them directly.
|
|
*/
|
|
|
|
return len;
|
|
}
|
|
|
|
static uint32_t
|
|
read_config(struct pci_device *phys_dev, long reg, int width)
|
|
{
|
|
uint32_t temp = 0;
|
|
|
|
switch (width) {
|
|
case 1:
|
|
pci_device_cfg_read_u8(phys_dev, (uint8_t *)&temp, reg);
|
|
break;
|
|
case 2:
|
|
pci_device_cfg_read_u16(phys_dev, (uint16_t *)&temp, reg);
|
|
break;
|
|
case 4:
|
|
pci_device_cfg_read_u32(phys_dev, &temp, reg);
|
|
break;
|
|
default:
|
|
warnx("%s: invalid reg width", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
|
|
static int
|
|
write_config(struct pci_device *phys_dev, long reg, int width, uint32_t data)
|
|
{
|
|
int temp = -1;
|
|
|
|
switch (width) {
|
|
case 1:
|
|
temp = pci_device_cfg_write_u8(phys_dev, data, reg);
|
|
break;
|
|
case 2:
|
|
temp = pci_device_cfg_write_u16(phys_dev, data, reg);
|
|
break;
|
|
case 4:
|
|
temp = pci_device_cfg_write_u32(phys_dev, data, reg);
|
|
break;
|
|
default:
|
|
warnx("%s: invalid reg width", __func__);
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
|
|
static int
|
|
ptdev_msi_remap(struct vmctx *ctx, struct passthru_dev *ptdev,
|
|
uint64_t addr, uint16_t msg, int maxmsgnum)
|
|
{
|
|
uint16_t msgctl;
|
|
struct acrn_vm_pci_msix_remap msi_remap;
|
|
int msi_capoff;
|
|
uint16_t pci_command, new_command;
|
|
int ret = 0;
|
|
struct pci_device *phys_dev = ptdev->phys_dev;
|
|
uint16_t virt_bdf = PCI_BDF(ptdev->dev->bus, ptdev->dev->slot,
|
|
ptdev->dev->func);
|
|
|
|
(void)maxmsgnum;
|
|
|
|
if (ptdev->msi.capoff == 0)
|
|
return -1;
|
|
|
|
msi_capoff = ptdev->msi.capoff;
|
|
|
|
/* disable MSI during configuration */
|
|
msgctl = read_config(phys_dev, msi_capoff + PCIR_MSI_CTRL, 2);
|
|
msgctl &= ~PCIM_MSICTRL_MSI_ENABLE;
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_CTRL, 2, msgctl);
|
|
|
|
msi_remap.phys_bdf = ptdev->phys_bdf;
|
|
msi_remap.virt_bdf = virt_bdf;
|
|
msi_remap.msi_data = msg;
|
|
msi_remap.msi_addr = addr;
|
|
msi_remap.msix = 0;
|
|
msi_remap.msix_entry_index = 0;
|
|
|
|
if (vm_setup_ptdev_msi(ctx, &msi_remap))
|
|
return -1;
|
|
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_ADDR, 4,
|
|
(uint32_t)msi_remap.msi_addr);
|
|
|
|
if (msgctl & PCIM_MSICTRL_64BIT) {
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_ADDR_HIGH, 4,
|
|
(uint32_t)(msi_remap.msi_addr >> 32));
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_DATA_64BIT, 2,
|
|
msi_remap.msi_data);
|
|
} else {
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_DATA, 2,
|
|
msi_remap.msi_data);
|
|
}
|
|
|
|
if (!msg) {
|
|
/* disable MSI */
|
|
msgctl &= ~PCIM_MSICTRL_MSI_ENABLE;
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_CTRL, 2, msgctl);
|
|
|
|
/* enable INTx */
|
|
pci_command = read_config(phys_dev, PCIR_COMMAND, 2);
|
|
new_command = pci_command & (~PCI_COMMAND_INTX_DISABLE);
|
|
if (new_command != pci_command)
|
|
write_config(phys_dev, PCIR_COMMAND, 2, new_command);
|
|
} else {
|
|
/* disable INTx */
|
|
pci_command = read_config(phys_dev, PCIR_COMMAND, 2);
|
|
new_command = pci_command | PCI_COMMAND_INTX_DISABLE;
|
|
if (new_command != pci_command)
|
|
write_config(phys_dev, PCIR_COMMAND, 2, new_command);
|
|
|
|
/* enalbe MSI */
|
|
msgctl |= PCIM_MSICTRL_MSI_ENABLE;
|
|
write_config(phys_dev, msi_capoff + PCIR_MSI_CTRL, 2, msgctl);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ptdev_msix_remap(struct vmctx *ctx, const struct passthru_dev *ptdev,
|
|
int index, uint64_t addr, uint32_t msg,
|
|
uint32_t vector_control)
|
|
{
|
|
struct pci_device *phys_dev = ptdev->phys_dev;
|
|
struct pci_vdev *dev = ptdev->dev;
|
|
uint16_t msgctl;
|
|
struct acrn_vm_pci_msix_remap msix_remap;
|
|
int msix_capoff;
|
|
uint16_t pci_command, new_command;
|
|
uint16_t virt_bdf = PCI_BDF(ptdev->dev->bus, ptdev->dev->slot,
|
|
ptdev->dev->func);
|
|
|
|
if (!ptdev->msix.capoff)
|
|
return -1;
|
|
|
|
msix_capoff = ptdev->msix.capoff;
|
|
|
|
/* disable MSI-X during configuration */
|
|
msgctl = read_config(phys_dev, msix_capoff + PCIR_MSIX_CTRL, 2);
|
|
msgctl &= ~PCIM_MSIXCTRL_MSIX_ENABLE;
|
|
msgctl |= PCIM_MSIXCTRL_FUNCTION_MASK;
|
|
write_config(phys_dev, msix_capoff + PCIR_MSIX_CTRL, 2, msgctl);
|
|
|
|
if (!dev->msix.enabled)
|
|
return 0;
|
|
|
|
msix_remap.phys_bdf = ptdev->phys_bdf;
|
|
msix_remap.virt_bdf = virt_bdf;
|
|
msix_remap.msi_data = msg;
|
|
msix_remap.msi_addr = addr;
|
|
msix_remap.msix = 1;
|
|
msix_remap.msix_entry_index = index;
|
|
|
|
if (vm_setup_ptdev_msi(ctx, &msix_remap))
|
|
return -1;
|
|
|
|
/* disable INTx */
|
|
pci_command = read_config(phys_dev, PCIR_COMMAND, 2);
|
|
new_command = pci_command | PCI_COMMAND_INTX_DISABLE;
|
|
if (new_command != pci_command)
|
|
write_config(phys_dev, PCIR_COMMAND, 2, new_command);
|
|
|
|
/* Enable MSI-X & unmask function */
|
|
msgctl &= ~PCIM_MSIXCTRL_FUNCTION_MASK;
|
|
msgctl |= PCIM_MSIXCTRL_MSIX_ENABLE;
|
|
write_config(phys_dev, msix_capoff + PCIR_MSIX_CTRL, 2, msgctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef FORCE_MSI_SINGLE_VECTOR
|
|
/* Temporarily set mmc & mme to 0.
|
|
* Remove it when multiple vectors for MSI ready.
|
|
*/
|
|
static inline void
|
|
clear_mmc_mme(uint32_t *val)
|
|
{
|
|
*val &= ~((uint32_t)PCIM_MSICTRL_MMC_MASK << 16);
|
|
*val &= ~((uint32_t)PCIM_MSICTRL_MME_MASK << 16);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
cfginit_cap(struct vmctx *ctx, struct passthru_dev *ptdev)
|
|
{
|
|
int i, ptr, capptr, cap, sts, caplen, table_size;
|
|
uint32_t u32;
|
|
struct pci_vdev *dev;
|
|
struct pci_device *phys_dev = ptdev->phys_dev;
|
|
uint16_t virt_bdf = PCI_BDF(ptdev->dev->bus,
|
|
ptdev->dev->slot,
|
|
ptdev->dev->func);
|
|
uint32_t pba_info;
|
|
uint32_t table_info;
|
|
uint16_t msgctrl;
|
|
|
|
dev = ptdev->dev;
|
|
|
|
/*
|
|
* Parse the capabilities and cache the location of the MSI
|
|
* and MSI-X capabilities.
|
|
*/
|
|
sts = read_config(phys_dev, PCIR_STATUS, 2);
|
|
if (sts & PCIM_STATUS_CAPPRESENT) {
|
|
ptr = read_config(phys_dev, PCIR_CAP_PTR, 1);
|
|
while (ptr != 0 && ptr != 0xff) {
|
|
cap = read_config(phys_dev, ptr + PCICAP_ID, 1);
|
|
if (cap == PCIY_MSI) {
|
|
/*
|
|
* Copy the MSI capability into the config
|
|
* space of the emulated pci device
|
|
*/
|
|
ptdev->msi.capoff = ptr;
|
|
ptdev->msi.msgctrl = read_config(phys_dev,
|
|
ptr + 2, 2);
|
|
|
|
#ifdef FORCE_MSI_SINGLE_VECTOR
|
|
/* Temporarily set mmc & mme to 0,
|
|
* which means supporting 1 vector. So that
|
|
* guest will not enable more than 1 vector.
|
|
* Remove it when multiple vectors ready.
|
|
*/
|
|
ptdev->msi.msgctrl &= ~PCIM_MSICTRL_MMC_MASK;
|
|
ptdev->msi.msgctrl &= ~PCIM_MSICTRL_MME_MASK;
|
|
#endif
|
|
|
|
ptdev->msi.emulated = 0;
|
|
caplen = msi_caplen(ptdev->msi.msgctrl);
|
|
capptr = ptr;
|
|
while (caplen > 0) {
|
|
u32 = read_config(phys_dev, capptr, 4);
|
|
|
|
#ifdef FORCE_MSI_SINGLE_VECTOR
|
|
/* Temporarily set mmc & mme to 0.
|
|
* which means supporting 1 vector.
|
|
* Remove it when multiple vectors ready
|
|
*/
|
|
if (capptr == ptdev->msi.capoff)
|
|
clear_mmc_mme(&u32);
|
|
#endif
|
|
|
|
pci_set_cfgdata32(dev, capptr, u32);
|
|
caplen -= 4;
|
|
capptr += 4;
|
|
}
|
|
} else if (cap == PCIY_MSIX) {
|
|
/*
|
|
* Copy the MSI-X capability
|
|
*/
|
|
ptdev->msix.capoff = ptr;
|
|
caplen = 12;
|
|
capptr = ptr;
|
|
while (caplen > 0) {
|
|
u32 = read_config(phys_dev, capptr, 4);
|
|
pci_set_cfgdata32(dev, capptr, u32);
|
|
caplen -= 4;
|
|
capptr += 4;
|
|
}
|
|
} else if (cap == PCIY_EXPRESS)
|
|
ptdev->pcie_cap = true;
|
|
|
|
ptr = read_config(phys_dev, ptr + PCICAP_NEXTPTR, 1);
|
|
}
|
|
}
|
|
|
|
if (ptdev->msix.capoff != 0) {
|
|
capptr = ptdev->msix.capoff;
|
|
|
|
pba_info = pci_get_cfgdata32(dev, capptr + 8);
|
|
dev->msix.pba_bar = pba_info & PCIM_MSIX_BIR_MASK;
|
|
dev->msix.pba_offset = pba_info & ~PCIM_MSIX_BIR_MASK;
|
|
|
|
table_info = pci_get_cfgdata32(dev, capptr + 4);
|
|
dev->msix.table_bar = table_info & PCIM_MSIX_BIR_MASK;
|
|
dev->msix.table_offset = table_info & ~PCIM_MSIX_BIR_MASK;
|
|
|
|
msgctrl = pci_get_cfgdata16(dev, capptr + 2);
|
|
dev->msix.table_count = MSIX_TABLE_COUNT(msgctrl);
|
|
dev->msix.pba_size = PBA_SIZE(dev->msix.table_count);
|
|
|
|
/* Allocate the emulated MSI-X table array */
|
|
table_size = dev->msix.table_count * MSIX_TABLE_ENTRY_SIZE;
|
|
dev->msix.table = calloc(1, table_size);
|
|
if (dev->msix.table == NULL) {
|
|
warnx("%s: calloc FAIL!", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/* Mask all table entries */
|
|
for (i = 0; i < dev->msix.table_count; i++) {
|
|
dev->msix.table[i].vector_control |=
|
|
PCIM_MSIX_VCTRL_MASK;
|
|
}
|
|
} else if (ptdev->msi.capoff != 0) {
|
|
struct ic_ptdev_irq ptirq;
|
|
|
|
ptirq.type = IRQ_MSI;
|
|
ptirq.virt_bdf = virt_bdf;
|
|
ptirq.phys_bdf = ptdev->phys_bdf;
|
|
/* currently, only support one vector for MSI */
|
|
ptirq.msix.vector_cnt = 1;
|
|
ptirq.msix.table_paddr = 0;
|
|
ptirq.msix.table_size = 0;
|
|
vm_set_ptdev_msix_info(ctx, &ptirq);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t
|
|
msix_table_read(struct passthru_dev *ptdev, uint64_t offset, int size)
|
|
{
|
|
struct pci_vdev *dev;
|
|
struct msix_table_entry *entry;
|
|
uint8_t *src8;
|
|
uint16_t *src16;
|
|
uint32_t *src32;
|
|
uint64_t *src64;
|
|
uint64_t data;
|
|
size_t entry_offset;
|
|
int index;
|
|
|
|
dev = ptdev->dev;
|
|
if (offset >= dev->msix.pba_offset &&
|
|
offset < dev->msix.pba_offset + dev->msix.pba_size) {
|
|
switch (size) {
|
|
case 1:
|
|
src8 = (uint8_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
data = *src8;
|
|
break;
|
|
case 2:
|
|
src16 = (uint16_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
data = *src16;
|
|
break;
|
|
case 4:
|
|
src32 = (uint32_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
data = *src32;
|
|
break;
|
|
case 8:
|
|
src64 = (uint64_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
data = *src64;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
if (offset < dev->msix.table_offset)
|
|
return -1;
|
|
|
|
offset -= dev->msix.table_offset;
|
|
index = offset / MSIX_TABLE_ENTRY_SIZE;
|
|
if (index >= dev->msix.table_count)
|
|
return -1;
|
|
|
|
entry = &dev->msix.table[index];
|
|
entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
|
|
|
|
switch (size) {
|
|
case 1:
|
|
src8 = (uint8_t *)((void *)entry + entry_offset);
|
|
data = *src8;
|
|
break;
|
|
case 2:
|
|
src16 = (uint16_t *)((void *)entry + entry_offset);
|
|
data = *src16;
|
|
break;
|
|
case 4:
|
|
src32 = (uint32_t *)((void *)entry + entry_offset);
|
|
data = *src32;
|
|
break;
|
|
case 8:
|
|
src64 = (uint64_t *)((void *)entry + entry_offset);
|
|
data = *src64;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
msix_table_write(struct vmctx *ctx, int vcpu, struct passthru_dev *ptdev,
|
|
uint64_t offset, int size, uint64_t data)
|
|
{
|
|
struct pci_vdev *dev;
|
|
struct msix_table_entry *entry;
|
|
uint8_t *dest8;
|
|
uint16_t *dest16;
|
|
uint32_t *dest32;
|
|
uint64_t *dest64;
|
|
size_t entry_offset;
|
|
uint32_t vector_control;
|
|
int index;
|
|
|
|
dev = ptdev->dev;
|
|
if (offset >= dev->msix.pba_offset &&
|
|
offset < dev->msix.pba_offset + dev->msix.pba_size) {
|
|
switch (size) {
|
|
case 1:
|
|
dest8 = (uint8_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
*dest8 = data;
|
|
break;
|
|
case 2:
|
|
dest16 = (uint16_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
*dest16 = data;
|
|
break;
|
|
case 4:
|
|
dest32 = (uint32_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
*dest32 = data;
|
|
break;
|
|
case 8:
|
|
dest64 = (uint64_t *)(dev->msix.pba_page + offset -
|
|
dev->msix.pba_page_offset);
|
|
*dest64 = data;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (offset < dev->msix.table_offset)
|
|
return;
|
|
|
|
offset -= dev->msix.table_offset;
|
|
index = offset / MSIX_TABLE_ENTRY_SIZE;
|
|
if (index >= dev->msix.table_count)
|
|
return;
|
|
|
|
entry = &dev->msix.table[index];
|
|
entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
|
|
|
|
/* Only 4 byte naturally-aligned writes are supported */
|
|
assert(size == 4);
|
|
assert(entry_offset % 4 == 0);
|
|
|
|
vector_control = entry->vector_control;
|
|
dest32 = (uint32_t *)((void *)entry + entry_offset);
|
|
*dest32 = data;
|
|
/* If MSI-X hasn't been enabled, do nothing */
|
|
if (dev->msix.enabled) {
|
|
/* If the entry is masked, don't set it up */
|
|
if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 ||
|
|
(vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
|
|
(void)ptdev_msix_remap(ctx, ptdev, index,
|
|
entry->addr, entry->msg_data,
|
|
entry->vector_control);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
init_msix_table(struct vmctx *ctx, struct passthru_dev *ptdev, uint64_t base)
|
|
{
|
|
int b, s, f;
|
|
int error, idx;
|
|
size_t len, remaining;
|
|
uint32_t table_size, table_offset;
|
|
uint32_t pba_size, pba_offset;
|
|
vm_paddr_t start;
|
|
struct pci_vdev *dev = ptdev->dev;
|
|
uint16_t virt_bdf = PCI_BDF(dev->bus, dev->slot, dev->func);
|
|
struct ic_ptdev_irq ptirq;
|
|
|
|
assert(pci_msix_table_bar(dev) >= 0 && pci_msix_pba_bar(dev) >= 0);
|
|
|
|
b = ptdev->sel.bus;
|
|
s = ptdev->sel.dev;
|
|
f = ptdev->sel.func;
|
|
|
|
/*
|
|
* If the MSI-X table BAR maps memory intended for
|
|
* other uses, it is at least assured that the table
|
|
* either resides in its own page within the region,
|
|
* or it resides in a page shared with only the PBA.
|
|
*/
|
|
table_offset = rounddown2(dev->msix.table_offset, 4096);
|
|
|
|
table_size = dev->msix.table_offset - table_offset;
|
|
table_size += dev->msix.table_count * MSIX_TABLE_ENTRY_SIZE;
|
|
table_size = roundup2(table_size, 4096);
|
|
|
|
idx = dev->msix.table_bar;
|
|
start = dev->bar[idx].addr;
|
|
remaining = dev->bar[idx].size;
|
|
|
|
if (dev->msix.pba_bar == dev->msix.table_bar) {
|
|
pba_offset = dev->msix.pba_offset;
|
|
pba_size = dev->msix.pba_size;
|
|
if (pba_offset >= table_offset + table_size ||
|
|
table_offset >= pba_offset + pba_size) {
|
|
/*
|
|
* If the PBA does not share a page with the MSI-x
|
|
* tables, no PBA emulation is required.
|
|
*/
|
|
dev->msix.pba_page = NULL;
|
|
dev->msix.pba_page_offset = 0;
|
|
} else {
|
|
/*
|
|
* The PBA overlaps with either the first or last
|
|
* page of the MSI-X table region. Map the
|
|
* appropriate page.
|
|
*/
|
|
if (pba_offset <= table_offset)
|
|
dev->msix.pba_page_offset = table_offset;
|
|
else
|
|
dev->msix.pba_page_offset = table_offset +
|
|
table_size - 4096;
|
|
dev->msix.pba_page = mmap(NULL, 4096, PROT_READ |
|
|
PROT_WRITE, MAP_SHARED, memfd, start +
|
|
dev->msix.pba_page_offset);
|
|
if (dev->msix.pba_page == MAP_FAILED) {
|
|
warn(
|
|
"Failed to map PBA page for MSI-X on %x/%x/%x",
|
|
b, s, f);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Map everything before the MSI-X table */
|
|
if (table_offset > 0) {
|
|
len = table_offset;
|
|
error = vm_map_ptdev_mmio(ctx, b, s, f, start, len, base);
|
|
if (error)
|
|
return error;
|
|
|
|
base += len;
|
|
start += len;
|
|
remaining -= len;
|
|
}
|
|
|
|
/* Handle MSI-X vectors and table:
|
|
* request to alloc vector entries of MSI-X,
|
|
* Map the MSI-X table to memory space of SOS
|
|
*/
|
|
ptirq.type = IRQ_MSIX;
|
|
ptirq.virt_bdf = virt_bdf;
|
|
ptirq.phys_bdf = ptdev->phys_bdf;
|
|
ptirq.msix.vector_cnt = dev->msix.table_count;
|
|
ptirq.msix.table_paddr = ptdev->bar[idx].addr +
|
|
dev->msix.table_offset;
|
|
ptirq.msix.table_size = table_size;
|
|
vm_set_ptdev_msix_info(ctx, &ptirq);
|
|
ptdev->msix.table_size = table_size;
|
|
|
|
/* Skip the MSI-X table */
|
|
base += table_size;
|
|
start += table_size;
|
|
remaining -= table_size;
|
|
|
|
/* Map everything beyond the end of the MSI-X table */
|
|
if (remaining > 0) {
|
|
len = remaining;
|
|
error = vm_map_ptdev_mmio(ctx, b, s, f, start, len, base);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
cfginitbar(struct vmctx *ctx, struct passthru_dev *ptdev)
|
|
{
|
|
int i, error;
|
|
struct pci_vdev *dev;
|
|
struct pci_bar_io bar;
|
|
enum pcibar_type bartype;
|
|
uint64_t base, size;
|
|
|
|
dev = ptdev->dev;
|
|
|
|
/*
|
|
* Initialize BAR registers
|
|
*/
|
|
for (i = 0; i <= PCI_BARMAX; i++) {
|
|
bzero(&bar, sizeof(bar));
|
|
bar.sel = ptdev->sel;
|
|
bar.reg = PCIR_BAR(i);
|
|
|
|
bar.base = read_config(ptdev->phys_dev, bar.reg, 4);
|
|
bar.length = ptdev->phys_dev->regions[i].size;
|
|
|
|
if (PCI_BAR_IO(bar.base)) {
|
|
bartype = PCIBAR_IO;
|
|
base = bar.base & PCIM_BAR_IO_BASE;
|
|
} else {
|
|
switch (bar.base & PCIM_BAR_MEM_TYPE) {
|
|
case PCIM_BAR_MEM_64:
|
|
bartype = PCIBAR_MEM64;
|
|
break;
|
|
default:
|
|
bartype = PCIBAR_MEM32;
|
|
break;
|
|
}
|
|
base = bar.base & PCIM_BAR_MEM_BASE;
|
|
}
|
|
size = bar.length;
|
|
|
|
if (bartype != PCIBAR_IO) {
|
|
/* note here PAGE_MASK is 0xFFFFF000 */
|
|
if (((base | size) & ~PAGE_MASK) != 0) {
|
|
warnx("passthru device %x/%x/%x BAR %d: "
|
|
"base %#lx or size %#lx not page aligned\n",
|
|
ptdev->sel.bus, ptdev->sel.dev,
|
|
ptdev->sel.func, i, base, size);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Cache information about the "real" BAR */
|
|
ptdev->bar[i].type = bartype;
|
|
ptdev->bar[i].size = size;
|
|
ptdev->bar[i].addr = base;
|
|
|
|
if (size == 0)
|
|
continue;
|
|
|
|
/* Allocate the BAR in the guest I/O or MMIO space */
|
|
error = pci_emul_alloc_pbar(dev, i, base, bartype, size);
|
|
if (error)
|
|
return -1;
|
|
|
|
/* The MSI-X table needs special handling */
|
|
if (i == pci_msix_table_bar(dev)) {
|
|
error = init_msix_table(ctx, ptdev, base);
|
|
if (error)
|
|
return -1;
|
|
} else if (bartype != PCIBAR_IO) {
|
|
/* Map the physical BAR in the guest MMIO space */
|
|
error = vm_map_ptdev_mmio(ctx, ptdev->sel.bus,
|
|
ptdev->sel.dev, ptdev->sel.func,
|
|
dev->bar[i].addr, dev->bar[i].size, base);
|
|
if (error)
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* 64-bit BAR takes up two slots so skip the next one.
|
|
*/
|
|
if (bartype == PCIBAR_MEM64) {
|
|
i++;
|
|
assert(i <= PCI_BARMAX);
|
|
ptdev->bar[i].type = PCIBAR_MEMHI64;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* return value:
|
|
* -1 : fail
|
|
* >=0: succeed
|
|
* IRQ_INTX(0): phy dev has no MSI support
|
|
* IRQ_MSI(1): phy dev has MSI support
|
|
*/
|
|
static int
|
|
cfginit(struct vmctx *ctx, struct passthru_dev *ptdev, int bus,
|
|
int slot, int func)
|
|
{
|
|
int irq_type = IRQ_MSI;
|
|
char reset_path[60];
|
|
int fd;
|
|
|
|
bzero(&ptdev->sel, sizeof(struct pcisel));
|
|
ptdev->sel.bus = bus;
|
|
ptdev->sel.dev = slot;
|
|
ptdev->sel.func = func;
|
|
|
|
if (cfginit_cap(ctx, ptdev) != 0) {
|
|
warnx("Capability check fails for PCI %x/%x/%x",
|
|
bus, slot, func);
|
|
return -1;
|
|
}
|
|
|
|
/* Check MSI or MSIX capabilities */
|
|
if (ptdev->msi.capoff == 0 && ptdev->msix.capoff == 0) {
|
|
warnx("MSI not supported for PCI %x/%x/%x",
|
|
bus, slot, func);
|
|
irq_type = IRQ_INTX;
|
|
}
|
|
|
|
/* If SOS kernel provides 'reset' entry in sysfs, related dev has some
|
|
* reset capability, e.g. FLR, or secondary bus reset. We do 2 things:
|
|
* - reset each dev before passthrough to achieve valid dev state after
|
|
* UOS reboot
|
|
* - refuse to passthrough PCIe dev without any reset capability
|
|
*/
|
|
snprintf(reset_path, 40,
|
|
"/sys/bus/pci/devices/0000:%02x:%02x.%x/reset",
|
|
bus, slot, func);
|
|
|
|
fd = open(reset_path, O_WRONLY);
|
|
if (fd >= 0) {
|
|
if (ptdev->need_reset && write(fd, "1", 1) < 0)
|
|
warnx("reset dev %x/%x/%x failed!\n",
|
|
bus, slot, func);
|
|
close(fd);
|
|
} else if (errno == ENOENT && ptdev->pcie_cap) {
|
|
warnx("No reset capability for PCIe %x/%x/%x, "
|
|
"remove it from ptdev list!!\n",
|
|
bus, slot, func);
|
|
if (!no_reset)
|
|
return -1;
|
|
}
|
|
|
|
if (cfginitbar(ctx, ptdev) != 0) {
|
|
warnx("failed to initialize BARs for PCI %x/%x/%x",
|
|
bus, slot, func);
|
|
return -1;
|
|
} else
|
|
return irq_type;
|
|
}
|
|
|
|
/*
|
|
* convert the error code of pci_system_init in libpciaccess to DM standard
|
|
*
|
|
* pci_system_init in libpciaccess:
|
|
* return zero -> success
|
|
* return positive value -> failure
|
|
*
|
|
* DM standard:
|
|
* return zero -> success
|
|
* return negative value -> failure
|
|
*/
|
|
static int
|
|
native_pci_system_init()
|
|
{
|
|
int error;
|
|
|
|
error = pci_system_init();
|
|
|
|
/*
|
|
* convert the returned error value to negative since DM only handles
|
|
* the negative error code
|
|
*/
|
|
return -error;
|
|
}
|
|
|
|
/*
|
|
* return zero on success or non-zero on failure
|
|
*/
|
|
int
|
|
pciaccess_init(void)
|
|
{
|
|
int error;
|
|
|
|
pthread_mutex_lock(&ref_cnt_mtx);
|
|
|
|
if (!pciaccess_ref_cnt) {
|
|
error = native_pci_system_init();
|
|
if (error < 0) {
|
|
warnx("libpciaccess couldn't access PCI system");
|
|
pthread_mutex_unlock(&ref_cnt_mtx);
|
|
return error;
|
|
}
|
|
}
|
|
pciaccess_ref_cnt++;
|
|
|
|
pthread_mutex_unlock(&ref_cnt_mtx);
|
|
|
|
return 0; /* success */
|
|
}
|
|
|
|
/*
|
|
* Passthrough device initialization function:
|
|
* - initialize virtual config space
|
|
* - read physical info via libpciaccess
|
|
* - issue related hypercall for passthrough
|
|
* - Do some specific actions:
|
|
* - enable NHLT for audio pt dev
|
|
* - emulate INTPIN/INTLINE
|
|
* - hide INTx link if ptdev support both MSI and INTx to force guest using
|
|
* MSI, so that mitigate ptdev GSI sharing issue.
|
|
*/
|
|
static int
|
|
passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|
{
|
|
int bus, slot, func, error;
|
|
struct passthru_dev *ptdev;
|
|
struct pci_device_iterator *iter;
|
|
struct pci_device *phys_dev;
|
|
char *opt;
|
|
bool keep_gsi = false;
|
|
bool need_reset = false;
|
|
|
|
ptdev = NULL;
|
|
error = -EINVAL;
|
|
|
|
if (opts == NULL) {
|
|
warnx("Empty passthru options\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
opt = strsep(&opts, ",");
|
|
if (sscanf(opt, "%x/%x/%x", &bus, &slot, &func) != 3) {
|
|
warnx("Invalid passthru BDF options:%s", opt);
|
|
return -EINVAL;
|
|
}
|
|
|
|
while ((opt = strsep(&opts, ",")) != NULL) {
|
|
if (!strncmp(opt, "keep_gsi", 8))
|
|
keep_gsi = true;
|
|
else if (!strncmp(opt, "reset", 5))
|
|
need_reset = true;
|
|
else
|
|
warnx("Invalid passthru options:%s", opt);
|
|
}
|
|
|
|
if (vm_assign_ptdev(ctx, bus, slot, func) != 0) {
|
|
warnx("PCI device at %x/%x/%x is not using the pt(4) driver",
|
|
bus, slot, func);
|
|
goto done;
|
|
}
|
|
|
|
ptdev = calloc(1, sizeof(struct passthru_dev));
|
|
if (ptdev == NULL) {
|
|
warnx("%s: calloc FAIL!", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ptdev->phys_bdf = PCI_BDF(bus, slot, func);
|
|
ptdev->need_reset = need_reset;
|
|
update_pt_info(ptdev->phys_bdf);
|
|
|
|
error = pciaccess_init();
|
|
if (error < 0)
|
|
return error;
|
|
|
|
error = -ENODEV;
|
|
iter = pci_slot_match_iterator_create(NULL);
|
|
while ((phys_dev = pci_device_next(iter)) != NULL) {
|
|
if (phys_dev->bus == bus && phys_dev->dev == slot &&
|
|
phys_dev->func == func) {
|
|
ptdev->phys_dev = phys_dev;
|
|
error = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (error < 0) {
|
|
warnx("No physical PCI device %x:%x.%x!", bus, slot, func);
|
|
return -ENODEV;
|
|
}
|
|
|
|
pci_device_probe(ptdev->phys_dev);
|
|
|
|
dev->arg = ptdev;
|
|
ptdev->dev = dev;
|
|
|
|
/* handle 0x3c~0x3f config space
|
|
* INTLINE/INTPIN: from emulated configuration space
|
|
* MINGNT/MAXLAT: from physical configuration space
|
|
*/
|
|
pci_set_cfgdata16(dev, PCIR_MINGNT,
|
|
read_config(ptdev->phys_dev, PCIR_MINGNT, 2));
|
|
|
|
#if AUDIO_NHLT_HACK
|
|
/* device specific handling:
|
|
* audio: enable NHLT ACPI table
|
|
*/
|
|
if (read_config(ptdev->phys_dev, PCIR_VENDOR, 2) == 0x8086 &&
|
|
read_config(ptdev->phys_dev, PCIR_DEVICE, 2) == 0x5a98)
|
|
acpi_table_enable(NHLT_ENTRY_NO);
|
|
#endif
|
|
|
|
/* initialize config space */
|
|
error = cfginit(ctx, ptdev, bus, slot, func);
|
|
if (error < 0)
|
|
goto done;
|
|
|
|
/* If ptdev support MSI/MSIX, stop here to skip virtual INTx setup.
|
|
* Forge Guest to use MSI/MSIX in this case to mitigate IRQ sharing
|
|
* issue
|
|
*/
|
|
if (error == IRQ_MSI && !keep_gsi)
|
|
return 0;
|
|
|
|
/* Allocates the virq if ptdev only support INTx */
|
|
pci_lintr_request(dev);
|
|
|
|
ptdev->phys_pin = read_config(ptdev->phys_dev, PCIR_INTLINE, 1);
|
|
|
|
if (ptdev->phys_pin == -1 || ptdev->phys_pin > 256) {
|
|
warnx("ptdev %x/%x/%x has wrong phys_pin %d, likely fail!",
|
|
bus, slot, func, ptdev->phys_pin);
|
|
goto done;
|
|
}
|
|
|
|
error = 0; /* success */
|
|
done:
|
|
if (error) {
|
|
free(ptdev);
|
|
vm_unassign_ptdev(ctx, bus, slot, func);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void
|
|
pciaccess_cleanup(void)
|
|
{
|
|
pthread_mutex_lock(&ref_cnt_mtx);
|
|
pciaccess_ref_cnt--;
|
|
if (!pciaccess_ref_cnt)
|
|
pci_system_cleanup();
|
|
pthread_mutex_unlock(&ref_cnt_mtx);
|
|
}
|
|
|
|
static void
|
|
passthru_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|
{
|
|
struct passthru_dev *ptdev;
|
|
uint8_t bus, slot, func;
|
|
uint16_t virt_bdf = PCI_BDF(dev->bus, dev->slot, dev->func);
|
|
int vector_cnt = 0;
|
|
|
|
if (!dev->arg) {
|
|
warnx("%s: passthru_dev is NULL", __func__);
|
|
return;
|
|
}
|
|
|
|
ptdev = (struct passthru_dev *) dev->arg;
|
|
pciaccess_cleanup();
|
|
bus = (ptdev->phys_bdf >> 8) & 0xff;
|
|
slot = (ptdev->phys_bdf & 0xff) >> 3;
|
|
func = ptdev->phys_bdf & 0x7;
|
|
|
|
if (ptdev->msix.capoff != 0)
|
|
vector_cnt = dev->msix.table_count;
|
|
else if (ptdev->msi.capoff != 0)
|
|
/* currently, only support one vector for MSI */
|
|
vector_cnt = 1;
|
|
|
|
printf("vm_reset_ptdev_intx:0x%x-%x, ioapic virpin=%d.\n",
|
|
virt_bdf, ptdev->phys_bdf, dev->lintr.ioapic_irq);
|
|
vm_reset_ptdev_intx_info(ctx, dev->lintr.ioapic_irq, false);
|
|
|
|
if (vector_cnt > 0) {
|
|
printf("vm_reset_ptdev_msix:0x%x-%x, vector_cnt=%d.\n",
|
|
virt_bdf, ptdev->phys_bdf, vector_cnt);
|
|
vm_reset_ptdev_msix_info(ctx, virt_bdf, vector_cnt);
|
|
if (ptdev->msix.capoff)
|
|
free(dev->msix.table);
|
|
}
|
|
|
|
free(ptdev);
|
|
vm_unassign_ptdev(ctx, bus, slot, func);
|
|
}
|
|
|
|
/* bind pin info for pass-through device */
|
|
static void
|
|
passthru_bind_irq(struct vmctx *ctx, struct pci_vdev *dev)
|
|
{
|
|
struct passthru_dev *ptdev = dev->arg;
|
|
uint16_t virt_bdf = PCI_BDF(dev->bus, dev->slot, dev->func);
|
|
|
|
/* No allocated virq indicates ptdev with MSI support, so no need to
|
|
* setup intx info as MSI is preferred over ioapic intr
|
|
*/
|
|
if (dev->lintr.pin == 0)
|
|
return;
|
|
|
|
printf("vm_set_ptdev_intx for %d:%d.%d, ",
|
|
dev->bus, dev->slot, dev->func);
|
|
printf("virt_pin=%d, phys_pin=%d, virt_bdf=0x%x, phys_bdf=0x%x.\n",
|
|
dev->lintr.ioapic_irq, ptdev->phys_pin,
|
|
virt_bdf, ptdev->phys_bdf);
|
|
|
|
vm_set_ptdev_intx_info(ctx, virt_bdf, ptdev->phys_bdf,
|
|
dev->lintr.ioapic_irq, ptdev->phys_pin, false);
|
|
}
|
|
|
|
static int
|
|
bar_access(int coff)
|
|
{
|
|
if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
msicap_access(struct passthru_dev *ptdev, int coff)
|
|
{
|
|
int caplen;
|
|
|
|
if (ptdev->msi.capoff == 0)
|
|
return 0;
|
|
|
|
caplen = msi_caplen(ptdev->msi.msgctrl);
|
|
|
|
if (coff >= ptdev->msi.capoff && coff < ptdev->msi.capoff + caplen)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
msixcap_access(struct passthru_dev *ptdev, int coff)
|
|
{
|
|
if (ptdev->msix.capoff == 0)
|
|
return 0;
|
|
|
|
return (coff >= ptdev->msix.capoff &&
|
|
coff < ptdev->msix.capoff + MSIX_CAPLEN);
|
|
}
|
|
|
|
static int
|
|
passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
|
|
int coff, int bytes, uint32_t *rv)
|
|
{
|
|
struct passthru_dev *ptdev;
|
|
|
|
ptdev = dev->arg;
|
|
|
|
/*
|
|
* PCI BARs and MSI capability is emulated.
|
|
*/
|
|
if (bar_access(coff) || msicap_access(ptdev, coff))
|
|
return -1;
|
|
|
|
/* INTLINE/INTPIN/MINGNT/MAXLAT need to be hacked */
|
|
if (coff >= PCIR_INTLINE && coff <= PCIR_MAXLAT)
|
|
return -1;
|
|
|
|
/* Everything else just read from the device's config space */
|
|
*rv = read_config(ptdev->phys_dev, coff, bytes);
|
|
|
|
/*
|
|
* return zero for graphics stolen memory since acrn does not have
|
|
* support for RMRR
|
|
*/
|
|
if ((PCI_BDF(dev->bus, dev->slot, dev->func) == PCI_BDF_GPU)
|
|
&& (coff == PCIR_GMCH_CTL)) {
|
|
*rv &= ~PCIM_GMCH_CTL_GMS;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
|
|
int coff, int bytes, uint32_t val)
|
|
{
|
|
int error, msix_table_entries, i;
|
|
struct passthru_dev *ptdev;
|
|
|
|
ptdev = dev->arg;
|
|
error = 1;
|
|
|
|
/*
|
|
* PCI BARs are emulated
|
|
*/
|
|
if (bar_access(coff))
|
|
return -1;
|
|
|
|
/* INTLINE/INTPIN/MINGNT/MAXLAT need to be hacked */
|
|
if (coff >= PCIR_INTLINE && coff <= PCIR_MAXLAT)
|
|
return -1;
|
|
|
|
/*
|
|
* MSI capability is emulated
|
|
*/
|
|
if (msicap_access(ptdev, coff)) {
|
|
msicap_cfgwrite(dev, ptdev->msi.capoff, coff, bytes, val);
|
|
|
|
if ((coff - ptdev->msi.capoff) == 2) {
|
|
|
|
/* currently not support multiple vectors for MSI */
|
|
if (dev->msi.maxmsgnum > 1)
|
|
warnx("only one vector supported for MSI");
|
|
|
|
if (val & PCIM_MSICTRL_MSI_ENABLE) {
|
|
error = ptdev_msi_remap(ctx, ptdev,
|
|
dev->msi.addr, dev->msi.msg_data,
|
|
dev->msi.maxmsgnum);
|
|
} else {
|
|
error = ptdev_msi_remap(ctx, ptdev,
|
|
dev->msi.addr, 0,
|
|
dev->msi.maxmsgnum);
|
|
}
|
|
|
|
if (error != 0)
|
|
err(1, "ptdev_msi_remap");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (msixcap_access(ptdev, coff)) {
|
|
msixcap_cfgwrite(dev, ptdev->msix.capoff, coff, bytes, val);
|
|
if (dev->msix.enabled) {
|
|
msix_table_entries = dev->msix.table_count;
|
|
for (i = 0; i < msix_table_entries; i++) {
|
|
error = ptdev_msix_remap(ctx, ptdev, i,
|
|
dev->msix.table[i].addr,
|
|
dev->msix.table[i].msg_data,
|
|
dev->msix.table[i].vector_control);
|
|
|
|
if (error)
|
|
err(1, "ptdev_msix_remap");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
write_config(ptdev->phys_dev, coff, bytes, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
passthru_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, int baridx,
|
|
uint64_t offset, int size, uint64_t value)
|
|
{
|
|
struct passthru_dev *ptdev;
|
|
struct iodev_pio_req pio;
|
|
|
|
ptdev = dev->arg;
|
|
|
|
if (baridx == pci_msix_table_bar(dev)) {
|
|
msix_table_write(ctx, vcpu, ptdev, offset, size, value);
|
|
} else {
|
|
assert(dev->bar[baridx].type == PCIBAR_IO);
|
|
bzero(&pio, sizeof(struct iodev_pio_req));
|
|
pio.access = IODEV_PIO_WRITE;
|
|
pio.port = ptdev->bar[baridx].addr + offset;
|
|
pio.width = size;
|
|
pio.val = value;
|
|
|
|
(void)ioctl(iofd, IODEV_PIO, &pio);
|
|
}
|
|
}
|
|
|
|
static uint64_t
|
|
passthru_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, int baridx,
|
|
uint64_t offset, int size)
|
|
{
|
|
struct passthru_dev *ptdev;
|
|
struct iodev_pio_req pio;
|
|
uint64_t val;
|
|
|
|
ptdev = dev->arg;
|
|
|
|
if (baridx == pci_msix_table_bar(dev)) {
|
|
val = msix_table_read(ptdev, offset, size);
|
|
} else {
|
|
assert(dev->bar[baridx].type == PCIBAR_IO);
|
|
bzero(&pio, sizeof(struct iodev_pio_req));
|
|
pio.access = IODEV_PIO_READ;
|
|
pio.port = ptdev->bar[baridx].addr + offset;
|
|
pio.width = size;
|
|
pio.val = 0;
|
|
|
|
(void)ioctl(iofd, IODEV_PIO, &pio);
|
|
|
|
val = pio.val;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
write_dsdt_xhci(struct pci_vdev *dev)
|
|
{
|
|
printf("write virt-%x:%x.%x in dsdt for XDCI @ 00:15.1\n",
|
|
dev->bus,
|
|
dev->slot,
|
|
dev->func);
|
|
|
|
dsdt_line("");
|
|
dsdt_line("Device (XDCI)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func);
|
|
dsdt_line(" Name (_DDN, \"Broxton XDCI controller\")");
|
|
dsdt_line(" Name (_STR, Unicode (\"Broxton XDCI controller\"))");
|
|
dsdt_line("}");
|
|
dsdt_line("");
|
|
}
|
|
|
|
static void
|
|
write_dsdt_hdac(struct pci_vdev *dev)
|
|
{
|
|
printf("write virt-%x:%x.%x in dsdt for HDAC @ 00:17.0\n",
|
|
dev->bus,
|
|
dev->slot,
|
|
dev->func);
|
|
|
|
/* Need prepare I2C # carefully for all passthrough devices */
|
|
dsdt_line("Device (I2C0)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func);
|
|
dsdt_line(" Name (_DDN, \"Intel(R) I2C Controller #0\")");
|
|
dsdt_line(" Name (_UID, One) // _UID: Unique ID");
|
|
dsdt_line(" Name (LINK, \"\\\\_SB.PCI0.I2C0\")");
|
|
|
|
dsdt_line(" Name (RBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Name (IC4S, 0x00061A80)");
|
|
dsdt_line(" Name (_DSD, Package (0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ToUUID (\"daffd814-6eba-4d8c-8a91-bc9bbf4aa301\")"
|
|
" ,");
|
|
dsdt_line(" Package (0x01)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Package (0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" \"clock-frequency\", ");
|
|
dsdt_line(" IC4S");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Method (FMCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x64, ");
|
|
dsdt_line(" 0xD6, ");
|
|
dsdt_line(" 0x1C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (FPCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x26, ");
|
|
dsdt_line(" 0x50, ");
|
|
dsdt_line(" 0x0C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (HSCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x05, ");
|
|
dsdt_line(" 0x18, ");
|
|
dsdt_line(" 0x0C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (SSCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x0244, ");
|
|
dsdt_line(" 0x02DA, ");
|
|
dsdt_line(" 0x1C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (_CRS, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (RBUF)");
|
|
dsdt_line(" }");
|
|
|
|
dsdt_line(" Device (HDAC)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (_HID, \"INT34C3\") // _HID: Hardware ID");
|
|
dsdt_line(" Name (_CID, \"INT34C3\") // _CID: Compatible ID");
|
|
dsdt_line(" Name (_DDN, \"Intel(R) Smart Sound Technology "
|
|
"Audio Codec\") // _DDN: DOS Device Name");
|
|
dsdt_line(" Name (_UID, One) // _UID: Unique ID");
|
|
dsdt_line(" Method (_INI, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (_CRS, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (SBFB, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" I2cSerialBusV2 (0x006C, "
|
|
"ControllerInitiated, 0x00061A80,");
|
|
dsdt_line(" AddressingMode7Bit, "
|
|
"\"\\\\_SB.PCI0.I2C0\",");
|
|
dsdt_line(" 0x00, ResourceConsumer, , Exclusive,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Name (SBFI, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (ConcatenateResTemplate (SBFB, SBFI))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (_STA, 0, NotSerialized) // _STA: Status");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x0F)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line("}");
|
|
}
|
|
|
|
static void
|
|
write_dsdt_hdas(struct pci_vdev *dev)
|
|
{
|
|
printf("write virt-%x:%x.%x in dsdt for HDAS @ 00:e.0\n",
|
|
dev->bus,
|
|
dev->slot,
|
|
dev->func);
|
|
|
|
dsdt_line("Name (ADFM, 0x2A)");
|
|
dsdt_line("Name (ADPM, Zero)");
|
|
dsdt_line("Name (AG1L, Zero)");
|
|
dsdt_line("Name (AG1H, Zero)");
|
|
dsdt_line("Name (AG2L, Zero)");
|
|
dsdt_line("Name (AG2H, Zero)");
|
|
dsdt_line("Name (AG3L, Zero)");
|
|
dsdt_line("Name (AG3H, Zero)");
|
|
dsdt_line("Method (ADBG, 1, Serialized)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Return (Zero)");
|
|
dsdt_line("}");
|
|
dsdt_line("Device (HDAS)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func);
|
|
dsdt_line(" OperationRegion (HDAR, PCI_Config, Zero, 0x0100)");
|
|
dsdt_line(" Field (HDAR, ByteAcc, NoLock, Preserve)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" VDID, 32, ");
|
|
dsdt_line(" Offset (0x48), ");
|
|
dsdt_line(" , 6, ");
|
|
dsdt_line(" MBCG, 1, ");
|
|
dsdt_line(" Offset (0x54), ");
|
|
dsdt_line(" Offset (0x55), ");
|
|
dsdt_line(" PMEE, 1, ");
|
|
dsdt_line(" , 6, ");
|
|
dsdt_line(" PMES, 1");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Name (NBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" QWordMemory (ResourceConsumer, PosDecode, MinNotFixed,"
|
|
" MaxNotFixed, NonCacheable, ReadOnly,");
|
|
dsdt_line(" 0x0000000000000000, // Granularity");
|
|
dsdt_line(" 0x00000000000F2800, // Range Minimum");
|
|
dsdt_line(" 0x00000000000F2FDE, // Range Maximum");
|
|
dsdt_line(" 0x0000000000000000, // Translation Offset");
|
|
dsdt_line(" 0x00000000000007DF, // Length");
|
|
dsdt_line(" ,, _Y06, AddressRangeACPI, TypeStatic)");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Name (_S0W, 0x03) // _S0W: S0 Device Wake State");
|
|
dsdt_line(" Method (_DSW, 3, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" PMEE = Arg0");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Name (_PRW, Package (0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x0E, ");
|
|
dsdt_line(" 0x03");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Method (_PS0, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"HD-A Ctrlr D0\")");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (_PS3, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"HD-A Ctrlr D3\")");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (_INI, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"HDAS _INI\")");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (_DSM, 4, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"HDAS _DSM\")");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"a69f886e-6ceb-4594-a41f-7b5dce24c553\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Switch (ToInteger (Arg2))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Case (Zero)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (Buffer (One)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x0F");
|
|
dsdt_line(" })");
|
|
dsdt_line(" }");
|
|
dsdt_line(" Case (One)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"_DSM Fun 1 NHLT\")");
|
|
dsdt_line(" Return (NBUF)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" Case (0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"_DSM Fun 2 FMSK\")");
|
|
dsdt_line(" Return (ADFM)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" Case (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"_DSM Fun 3 PPMS\")");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"b489c2de-0f96-42e1-8a2d-c25b5091ee49\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & One))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"e1284052-8664-4fe4-a353-3878f72704c3\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x02))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"7c708106-3aff-40fe-88be-8c999b3f7445\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x04))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"e0e018a8-3550-4b54-a8d0-a8e05d0fcba2\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x08))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"202badb5-8870-4290-b536-f2380c63f55d\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x10))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"eb3fea76-394b-495d-a14d-8425092d5cb7\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x20))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"f1c69181-329a-45f0-8eef-d8bddf81e036\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x40))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"b3573eff-6441-4a75-91f7-4281eec4597d\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x80))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"ec774fa9-28d3-424a-90e4-69f984f1eeb7\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x0100))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"f101fef0-ff5a-4ad4-8710-43592a6f7948\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x0200))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"f3578986-4400-4adf-ae7e-cd433cd3f26e\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x0400))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ToUUID ("
|
|
"\"13b5e4d7-a91a-4059-8290-605b01ccb650\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x0800))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ACCG (AG1L, AG1H)))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x20000000))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ACCG (AG2L, AG2H)))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x40000000))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg3 == ACCG (AG3L, AG3H)))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return ((ADPM & 0x80000000))");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Return (Zero)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" Default");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ADBG (\"_DSM Fun NOK\")");
|
|
dsdt_line(" Return (Buffer (One)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x00");
|
|
dsdt_line(" })");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" ADBG (\"_DSM UUID NOK\")");
|
|
dsdt_line(" Return (Buffer (One)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x00");
|
|
dsdt_line(" })");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" Method (ACCG, 2, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (GBUF, Buffer (0x10){})");
|
|
dsdt_line(" Concatenate (Arg0, Arg1, GBUF)");
|
|
dsdt_line(" Return (GBUF) /* \\_SB_.PCI0.HDAS.ACCG.GBUF */");
|
|
dsdt_line(" }");
|
|
dsdt_line("}");
|
|
}
|
|
|
|
static void
|
|
write_dsdt_ipu_i2c(struct pci_vdev *dev)
|
|
{
|
|
printf("write virt-%x:%x.%x in dsdt for ipu's i2c-bus @ 00:16.0\n",
|
|
dev->bus, dev->slot, dev->func);
|
|
|
|
/* physical I2C 0:16.0 */
|
|
dsdt_line("Device (I2C1)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func);
|
|
dsdt_line(" Name (_DDN, \"Intel(R) I2C Controller #1\")");
|
|
dsdt_line(" Name (_UID, One)");
|
|
dsdt_line(" Name (LINK, \"\\\\_SB.PCI0.I2C1\")");
|
|
dsdt_line(" Name (RBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Name (IC0S, 0x00061A80)");
|
|
dsdt_line(" Name (_DSD, Package (0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" ToUUID (\"daffd814-6eba-4d8c-8a91-bc9bbf4aa301\")"
|
|
" ,");
|
|
dsdt_line(" Package (0x01)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Package (0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" \"clock-frequency\", ");
|
|
dsdt_line(" IC0S");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line(" })");
|
|
|
|
dsdt_line(" Method (FMCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x64, ");
|
|
dsdt_line(" 0xD6, ");
|
|
dsdt_line(" 0x1C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
dsdt_line(" Method (FPCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x26, ");
|
|
dsdt_line(" 0x50, ");
|
|
dsdt_line(" 0x0C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
dsdt_line(" Method (HSCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x05, ");
|
|
dsdt_line(" 0x18, ");
|
|
dsdt_line(" 0x0C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
dsdt_line(" Method (SSCN, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (PKG, Package (0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" 0x0244, ");
|
|
dsdt_line(" 0x02DA, ");
|
|
dsdt_line(" 0x1C");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (PKG)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
dsdt_line(" Method (_CRS, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (RBUF)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
/* CAM1 */
|
|
dsdt_line(" Device (CAM1)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (_ADR, Zero) // _ADR: Address");
|
|
dsdt_line(" Name (_HID, \"ADV7481A\") // _HID: Hardware ID");
|
|
dsdt_line(" Name (_CID, \"ADV7481A\") // _CID: Compatible ID");
|
|
dsdt_line(" Name (_UID, One) // _UID: Unique ID");
|
|
|
|
dsdt_line(" Method (_CRS, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (SBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" GpioIo (Exclusive, PullDefault, 0x0000, "
|
|
"0x0000, IoRestrictionInputOnly,");
|
|
dsdt_line(" \"\\\\_SB.GPO0\", 0x00, "
|
|
"ResourceConsumer, ,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" { // Pin list");
|
|
dsdt_line(" 0x001E");
|
|
dsdt_line(" }");
|
|
dsdt_line(" I2cSerialBusV2 (0x0070, "
|
|
"ControllerInitiated, 0x00061A80,");
|
|
dsdt_line(" AddressingMode7Bit, "
|
|
"\"\\\\_SB.PCI0.I2C1\",");
|
|
dsdt_line(" 0x00, ResourceConsumer, , Exclusive,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (SBUF)");
|
|
dsdt_line(" }");
|
|
|
|
dsdt_line(" Method (_DSM, 4, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"377ba76a-f390-4aff-ab38-9b1bf33a3015\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (\"ADV7481A\")");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"ea3b7bd8-e09b-4239-ad6e-ed525f3f26ab\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x40)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"8dbe2651-70c1-4c6f-ac87-a37cb46e4af6\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0xFF)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"26257549-9271-4ca4-bb43-c4899d5a4881\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" If (Arg2 == One)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x02)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" If (Arg2 == 0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x02001000)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" If (Arg2 == 0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x02000E01)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line(" Return (Zero)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
/* CAM2 */
|
|
dsdt_line(" Device (CAM2)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (_ADR, Zero) // _ADR: Address");
|
|
dsdt_line(" Name (_HID, \"ADV7481B\") // _HID: Hardware ID");
|
|
dsdt_line(" Name (_CID, \"ADV7481B\") // _CID: Compatible ID");
|
|
dsdt_line(" Name (_UID, One) // _UID: Unique ID");
|
|
|
|
dsdt_line(" Method (_CRS, 0, Serialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (SBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" GpioIo (Exclusive, PullDefault, 0x0000, "
|
|
"0x0000, IoRestrictionInputOnly,");
|
|
dsdt_line(" \"\\\\_SB.GPO0\", 0x00, "
|
|
"ResourceConsumer, ,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" { // Pin list");
|
|
dsdt_line(" 0x001E");
|
|
dsdt_line(" }");
|
|
dsdt_line(" I2cSerialBusV2 (0x0071, "
|
|
"ControllerInitiated, 0x00061A80,");
|
|
dsdt_line(" AddressingMode7Bit, "
|
|
"\"\\\\_SB.PCI0.I2C1\",");
|
|
dsdt_line(" 0x00, ResourceConsumer, , Exclusive,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Return (SBUF)");
|
|
dsdt_line(" }");
|
|
|
|
dsdt_line(" Method (_DSM, 4, NotSerialized) ");
|
|
dsdt_line(" {");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"377ba76a-f390-4aff-ab38-9b1bf33a3015\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (\"ADV7481B\")");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"ea3b7bd8-e09b-4239-ad6e-ed525f3f26ab\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x14)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"8dbe2651-70c1-4c6f-ac87-a37cb46e4af6\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0xFF)");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
dsdt_line(" If ((Arg0 == ToUUID ("
|
|
"\"26257549-9271-4ca4-bb43-c4899d5a4881\")))");
|
|
dsdt_line(" {");
|
|
dsdt_line(" If (Arg2 == One)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x02)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" If (Arg2 == 0x02)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x02001000)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" If (Arg2 == 0x03)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (0x02000E01)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line(" Return (Zero)");
|
|
dsdt_line(" }");
|
|
dsdt_line(" }");
|
|
dsdt_line("");
|
|
|
|
dsdt_line("}");
|
|
}
|
|
|
|
static void
|
|
write_dsdt_urt1(struct pci_vdev *dev)
|
|
{
|
|
printf("write virt-%x:%x.%x in dsdt for URT1 @ 00:18.0\n",
|
|
dev->bus,
|
|
dev->slot,
|
|
dev->func);
|
|
dsdt_line("Device (URT1)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func);
|
|
dsdt_line(" Name (_DDN, \"Intel(R) HS-UART Controller #1\")");
|
|
dsdt_line(" Name (_UID, One)");
|
|
dsdt_line(" Name (RBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" })");
|
|
dsdt_line(" Method (_CRS, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Return (RBUF)");
|
|
dsdt_line(" }");
|
|
dsdt_line("}");
|
|
}
|
|
|
|
static void
|
|
passthru_write_dsdt(struct pci_vdev *dev)
|
|
{
|
|
struct passthru_dev *ptdev = (struct passthru_dev *) dev->arg;
|
|
uint32_t vendor = 0, device = 0;
|
|
|
|
vendor = read_config(ptdev->phys_dev, PCIR_VENDOR, 2);
|
|
|
|
if (vendor != 0x8086)
|
|
return;
|
|
|
|
device = read_config(ptdev->phys_dev, PCIR_DEVICE, 2);
|
|
|
|
/* Provides ACPI extra info */
|
|
if (device == 0x5aaa)
|
|
/* XDCI @ 00:15.1 to enable ADB */
|
|
write_dsdt_xhci(dev);
|
|
else if (device == 0x5ab4)
|
|
/* HDAC @ 00:17.0 as codec */
|
|
write_dsdt_hdac(dev);
|
|
else if (device == 0x5a98)
|
|
/* HDAS @ 00:e.0 */
|
|
write_dsdt_hdas(dev);
|
|
else if (device == 0x5aac)
|
|
/* i2c @ 00:16.0 for ipu */
|
|
write_dsdt_ipu_i2c(dev);
|
|
else if (device == 0x5abc)
|
|
/* URT1 @ 00:18.0 for bluetooth*/
|
|
write_dsdt_urt1(dev);
|
|
|
|
}
|
|
|
|
struct pci_vdev_ops passthru = {
|
|
.class_name = "passthru",
|
|
.vdev_init = passthru_init,
|
|
.vdev_deinit = passthru_deinit,
|
|
.vdev_cfgwrite = passthru_cfgwrite,
|
|
.vdev_cfgread = passthru_cfgread,
|
|
.vdev_barwrite = passthru_write,
|
|
.vdev_barread = passthru_read,
|
|
.vdev_phys_access = passthru_bind_irq,
|
|
.vdev_write_dsdt = passthru_write_dsdt,
|
|
};
|
|
DEFINE_PCI_DEVTYPE(passthru);
|