mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-14 22:34:26 +00:00
Remove the use of assert() in passthrough driver. Tracked-On: #3252 Signed-off-by: Binbin Wu <binbin.wu@intel.com> Reviewed-by: Shuo A Liu <shuo.a.liu@intel.com>
1800 lines
49 KiB
C
1800 lines
49 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 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 drivers get topology data from ACPI NHLT table.
|
|
* For such drivers, we need to copy the host NHLT table to make it
|
|
* available to the Guest OS. Most audio drivers don't need this by
|
|
* default, when that's the case setting this macro to 0 will avoid
|
|
* unexpected failures.
|
|
* The cAVS audio needs this however, so we enable this feature.
|
|
*/
|
|
#define AUDIO_NHLT_HACK 1
|
|
|
|
extern uint64_t audio_nhlt_len;
|
|
|
|
/* 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 mmio_map {
|
|
uint64_t gpa;
|
|
uint64_t hpa;
|
|
size_t size;
|
|
};
|
|
|
|
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; /* page aligned size */
|
|
void *table_pages;
|
|
int table_offset; /* page aligned */
|
|
bool ptirq_allocated;
|
|
} 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;
|
|
/* The memory pages, which not overlap with MSI-X table will be
|
|
* passed-through to guest, two potential ranges, before and after MSI-X
|
|
* Table if any.
|
|
*/
|
|
struct mmio_map msix_bar_mmio[2];
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
#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 ptr, capptr, cap, sts, caplen;
|
|
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);
|
|
}
|
|
}
|
|
|
|
dev->msix.table_bar = -1;
|
|
dev->msix.pba_bar = -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);
|
|
} 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)
|
|
{
|
|
uint8_t *src8;
|
|
uint16_t *src16;
|
|
uint32_t *src32;
|
|
uint64_t *src64;
|
|
uint64_t data;
|
|
|
|
switch (size) {
|
|
case 1:
|
|
src8 = (uint8_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
data = *src8;
|
|
break;
|
|
case 2:
|
|
src16 = (uint16_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
data = *src16;
|
|
break;
|
|
case 4:
|
|
src32 = (uint32_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
data = *src32;
|
|
break;
|
|
case 8:
|
|
src64 = (uint64_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
data = *src64;
|
|
break;
|
|
default:
|
|
return -1;
|
|
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
msix_table_write(struct passthru_dev *ptdev, uint64_t offset, int size, uint64_t data)
|
|
{
|
|
uint8_t *dest8;
|
|
uint16_t *dest16;
|
|
uint32_t *dest32;
|
|
uint64_t *dest64;
|
|
|
|
switch (size) {
|
|
case 1:
|
|
dest8 = (uint8_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
*dest8 = data;
|
|
break;
|
|
case 2:
|
|
dest16 = (uint16_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
*dest16 = data;
|
|
break;
|
|
case 4:
|
|
dest32 = (uint32_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
*dest32 = data;
|
|
break;
|
|
case 8:
|
|
dest64 = (uint64_t *)(ptdev->msix.table_pages + offset - ptdev->msix.table_offset);
|
|
*dest64 = data;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline int ptdev_msix_table_bar(struct passthru_dev *ptdev)
|
|
{
|
|
return ptdev->dev->msix.table_bar;
|
|
}
|
|
|
|
static inline int ptdev_msix_pba_bar(struct passthru_dev *ptdev)
|
|
{
|
|
return ptdev->dev->msix.pba_bar;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
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;
|
|
|
|
/* 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) {
|
|
warnx(
|
|
"Failed to map MSI-X BAR passthru pages on %x/%x/%x",
|
|
b,s,f);
|
|
return error;
|
|
}
|
|
/* save mapping info, which need to be unmapped when deinit */
|
|
ptdev->msix_bar_mmio[0].gpa = start;
|
|
ptdev->msix_bar_mmio[0].hpa = base;
|
|
ptdev->msix_bar_mmio[0].size = len;
|
|
|
|
base += len;
|
|
start += len;
|
|
remaining -= len;
|
|
}
|
|
|
|
/* remap real msix table (page-aligned) to user space */
|
|
error = pci_device_map_range(ptdev->phys_dev, base, table_size,
|
|
PCI_DEV_MAP_FLAG_WRITABLE, &ptdev->msix.table_pages);
|
|
if (error) {
|
|
warnx("Failed to map MSI-X table pages on %x/%x/%x", b,s,f);
|
|
return error;
|
|
}
|
|
ptdev->msix.table_offset = table_offset;
|
|
ptdev->msix.table_size = table_size;
|
|
|
|
/* Handle MSI-X vectors:
|
|
* request to alloc vector entries of MSI-X.
|
|
* Set table_paddr/table_size to 0 to skip ioremap in sos kernel.
|
|
*/
|
|
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 = 0;
|
|
ptirq.msix.table_size = 0;
|
|
error = vm_set_ptdev_msix_info(ctx, &ptirq);
|
|
if (error) {
|
|
warnx("Failed to alloc ptirq entry on %x/%x/%x", b,s,f);
|
|
return error;
|
|
}
|
|
ptdev->msix.ptirq_allocated = true;
|
|
|
|
|
|
/* 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) {
|
|
warnx(
|
|
"Failed to map MSI-X BAR passthru pages on %x/%x/%x",
|
|
b,s,f);
|
|
return error;
|
|
}
|
|
/* save mapping info, which need to be unmapped when deinit */
|
|
ptdev->msix_bar_mmio[1].gpa = start;
|
|
ptdev->msix_bar_mmio[1].hpa = base;
|
|
ptdev->msix_bar_mmio[1].size = len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
deinit_msix_table(struct vmctx *ctx, struct passthru_dev *ptdev)
|
|
{
|
|
struct pci_vdev *dev = ptdev->dev;
|
|
uint16_t virt_bdf = PCI_BDF(dev->bus, dev->slot, dev->func);
|
|
int vector_cnt = dev->msix.table_count;
|
|
|
|
if (ptdev->msix.ptirq_allocated) {
|
|
printf("ptdev reset msix: 0x%x-%x, vector_cnt=%d.\n",
|
|
virt_bdf, ptdev->phys_bdf, vector_cnt);
|
|
vm_reset_ptdev_msix_info(ctx, virt_bdf, ptdev->phys_bdf, vector_cnt);
|
|
ptdev->msix.ptirq_allocated = false;
|
|
}
|
|
|
|
if (ptdev->msix.table_pages) {
|
|
pci_device_unmap_range(ptdev->phys_dev, ptdev->msix.table_pages, ptdev->msix.table_size);
|
|
ptdev->msix.table_pages = NULL;
|
|
}
|
|
|
|
/* We passthrough the pages not overlap with MSI-X table to guest,
|
|
* need to unmap them when deinit.
|
|
*/
|
|
for (int i = 0; i < 2; i++) {
|
|
if(ptdev->msix_bar_mmio[i].size != 0) {
|
|
if (vm_unmap_ptdev_mmio(ctx, ptdev->sel.bus,
|
|
ptdev->sel.dev, ptdev->sel.func,
|
|
ptdev->msix_bar_mmio[i].gpa,
|
|
ptdev->msix_bar_mmio[i].size,
|
|
ptdev->msix_bar_mmio[i].hpa)) {
|
|
warnx("Failed to unmap MSI-X BAR pt pages.");
|
|
}
|
|
ptdev->msix_bar_mmio[i].size = 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 & ~PAGE_MASK) != 0) {
|
|
warnx("passthru device %x/%x/%x BAR %d: "
|
|
"base %#lx not page aligned\n",
|
|
ptdev->sel.bus, ptdev->sel.dev,
|
|
ptdev->sel.func, i, base);
|
|
return -1;
|
|
}
|
|
/* roundup to PAGE_SIZE for bar size */
|
|
if ((size & ~PAGE_MASK) != 0) {
|
|
warnx("passthru device %x/%x/%x BAR %d: "
|
|
"size[%lx] is expanded to page aligned [%lx]\n",
|
|
ptdev->sel.bus, ptdev->sel.dev,
|
|
ptdev->sel.func, i, size, roundup2(size, PAGE_SIZE));
|
|
size = roundup2(size, PAGE_SIZE);
|
|
}
|
|
|
|
}
|
|
|
|
/* 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 == ptdev_msix_table_bar(ptdev)) {
|
|
error = init_msix_table(ctx, ptdev, base);
|
|
if (error) {
|
|
deinit_msix_table(ctx, ptdev);
|
|
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++;
|
|
if (i > PCI_BARMAX) {
|
|
warnx("BAR count out of range\n");
|
|
return -1;
|
|
}
|
|
|
|
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 (parse_bdf(opt, &bus, &slot, &func, 16) != 0) {
|
|
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__);
|
|
error = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
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)
|
|
goto done;
|
|
|
|
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;
|
|
}
|
|
}
|
|
pci_iterator_destroy(iter);
|
|
|
|
if (error < 0) {
|
|
warnx("No physical PCI device %x:%x.%x!", bus, slot, func);
|
|
goto done;
|
|
}
|
|
|
|
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) {
|
|
if (ptdev != NULL) {
|
|
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 i;
|
|
|
|
if (!dev->arg) {
|
|
warnx("%s: passthru_dev is NULL", __func__);
|
|
return;
|
|
}
|
|
|
|
ptdev = (struct passthru_dev *) dev->arg;
|
|
bus = (ptdev->phys_bdf >> 8) & 0xff;
|
|
slot = (ptdev->phys_bdf & 0xff) >> 3;
|
|
func = ptdev->phys_bdf & 0x7;
|
|
|
|
if (ptdev->msix.capoff != 0)
|
|
deinit_msix_table(ctx, ptdev);
|
|
else if(ptdev->msi.capoff != 0) {
|
|
/* Currently only support 1 vector */
|
|
vm_reset_ptdev_msix_info(ctx, virt_bdf, ptdev->phys_bdf, 1);
|
|
}
|
|
|
|
printf("vm_reset_ptdev_intx:0x%x-%x, ioapic virpin=%d.\n",
|
|
virt_bdf, ptdev->phys_bdf, dev->lintr.ioapic_irq);
|
|
|
|
if (dev->lintr.pin != 0) {
|
|
vm_reset_ptdev_intx_info(ctx, virt_bdf, ptdev->phys_bdf, dev->lintr.ioapic_irq, false);
|
|
}
|
|
|
|
/* unmap the physical BAR in guest MMIO space */
|
|
for (i = 0; i <= PCI_BARMAX; i++) {
|
|
|
|
if (ptdev->bar[i].size == 0 ||
|
|
i == ptdev_msix_table_bar(ptdev) ||
|
|
ptdev->bar[i].type == PCIBAR_IO)
|
|
continue;
|
|
|
|
vm_unmap_ptdev_mmio(ctx, ptdev->sel.bus,
|
|
ptdev->sel.dev, ptdev->sel.func,
|
|
dev->bar[i].addr, ptdev->bar[i].size,
|
|
ptdev->bar[i].addr);
|
|
}
|
|
|
|
pciaccess_cleanup();
|
|
free(ptdev);
|
|
vm_unassign_ptdev(ctx, bus, slot, func);
|
|
}
|
|
|
|
static void
|
|
passthru_update_bar_map(struct vmctx *ctx, struct pci_vdev *dev,
|
|
int idx, uint64_t orig_addr)
|
|
{
|
|
struct passthru_dev *ptdev;
|
|
|
|
if (!dev->arg) {
|
|
warnx("%s: passthru_dev is NULL", __func__);
|
|
return;
|
|
}
|
|
|
|
ptdev = (struct passthru_dev *)dev->arg;
|
|
|
|
if (ptdev->bar[idx].size == 0 ||
|
|
idx == ptdev_msix_table_bar(ptdev) ||
|
|
ptdev->bar[idx].type == PCIBAR_IO)
|
|
return;
|
|
|
|
if (dev->bar[idx].addr + dev->bar[idx].size > PCI_EMUL_MEMLIMIT64 ||
|
|
orig_addr + dev->bar[idx].size > PCI_EMUL_MEMLIMIT64)
|
|
return;
|
|
|
|
vm_unmap_ptdev_mmio(ctx, ptdev->sel.bus,
|
|
ptdev->sel.dev, ptdev->sel.func,
|
|
orig_addr, ptdev->bar[idx].size,
|
|
ptdev->bar[idx].addr);
|
|
|
|
vm_map_ptdev_mmio(ctx, ptdev->sel.bus,
|
|
ptdev->sel.dev, ptdev->sel.func,
|
|
dev->bar[idx].addr, ptdev->bar[idx].size,
|
|
ptdev->bar[idx].addr);
|
|
}
|
|
|
|
/* 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
|
|
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))
|
|
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)
|
|
{
|
|
struct passthru_dev *ptdev;
|
|
|
|
ptdev = dev->arg;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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;
|
|
|
|
ptdev = dev->arg;
|
|
|
|
if (baridx == ptdev_msix_table_bar(ptdev)) {
|
|
msix_table_write(ptdev, offset, size, value);
|
|
} else {
|
|
/* TODO: Add support for IO BAR of PTDev */
|
|
warnx("Passthru: PIO write not supported, ignored\n");
|
|
}
|
|
}
|
|
|
|
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;
|
|
uint64_t val;
|
|
|
|
ptdev = dev->arg;
|
|
|
|
if (baridx == ptdev_msix_table_bar(ptdev)) {
|
|
val = msix_table_read(ptdev, offset, size);
|
|
} else {
|
|
/* TODO: Add support for IO BAR of PTDev */
|
|
warnx("Passthru: PIO read not supported\n");
|
|
val = (uint64_t)(-1);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
write_dsdt_xdci(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(" 0x%08X, // Range Maximum",
|
|
0xF2800 + audio_nhlt_len -1);
|
|
dsdt_line(" 0x0000000000000000, // Translation Offset");
|
|
dsdt_line(" 0x%08X, // Length", audio_nhlt_len);
|
|
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
|
|
write_dsdt_sdc(struct pci_vdev *dev)
|
|
{
|
|
printf("write SDC-%x:%x.%x in dsdt for SDC @ 00:1b.0\n",
|
|
dev->bus,
|
|
dev->slot,
|
|
dev->func);
|
|
dsdt_line("Device (SDC)");
|
|
dsdt_line("{");
|
|
dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func);
|
|
dsdt_line(" Name (_DDN, \"Intel(R) SD Card Controller\")");
|
|
dsdt_line(" Name (_UID, One)");
|
|
dsdt_line(" Method (_CRS, 0, NotSerialized)");
|
|
dsdt_line(" {");
|
|
dsdt_line(" Name (RBUF, ResourceTemplate ()");
|
|
dsdt_line(" {");
|
|
dsdt_line(" GpioInt (Edge, ActiveBoth, SharedAndWake, "
|
|
"PullNone, 0, ");
|
|
dsdt_line(" \"\\\\_SB_.PCI0.AGPI\", 0, ResourceConsumer, ,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" { // Pin list");
|
|
dsdt_line(" 0");
|
|
dsdt_line(" }");
|
|
dsdt_line(" GpioIo (Exclusive, PullDefault, 0x0000, "
|
|
"0x0000, IoRestrictionInputOnly,");
|
|
dsdt_line(" \"\\\\_SB._PCI0.AGPI\", 0x00, "
|
|
"ResourceConsumer, ,");
|
|
dsdt_line(" )");
|
|
dsdt_line(" { // Pin list");
|
|
dsdt_line(" 0");
|
|
dsdt_line(" }");
|
|
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_xdci(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);
|
|
else if (device == 0x5aca)
|
|
/* SDC @ 00:1b.0 */
|
|
write_dsdt_sdc(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_update_bar_map = passthru_update_bar_map,
|
|
.vdev_write_dsdt = passthru_write_dsdt,
|
|
};
|
|
DEFINE_PCI_DEVTYPE(passthru);
|