Add AMD IOMMU/AMD-Vi support in bhyve for passthrough/direct assignment to VMs. To enable AMD-Vi, set hw.vmm.amdvi.enable=1.

Reviewed by:bcr
Approved by:grehan
Tested by:rgrimes
Differential Revision:https://reviews.freebsd.org/D10049
This commit is contained in:
Anish Gupta 2017-04-30 02:08:46 +00:00
parent 342b089bca
commit 07ff474a68
4 changed files with 2405 additions and 2 deletions

1509
sys/amd64/vmm/amd/amdvi_hw.c Normal file

File diff suppressed because it is too large Load Diff

395
sys/amd64/vmm/amd/amdvi_priv.h Executable file
View File

@ -0,0 +1,395 @@
/*-
* Copyright (c) 2016 Anish Gupta (anish@freebsd.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _AMDVI_PRIV_H_
#define _AMDVI_PRIV_H_
#define BIT(n) (1ULL << (n))
/* Return value of bits[n:m] where n and (n >= ) m are bit positions. */
#define REG_BITS(x, n, m) (((x) >> (m)) & \
((1 << (((n) - (m)) + 1)) - 1))
/*
* IOMMU PCI capability.
*/
#define AMDVI_PCI_CAP_IOTLB BIT(0) /* IOTLB is supported. */
#define AMDVI_PCI_CAP_HT BIT(1) /* HyperTransport tunnel support. */
#define AMDVI_PCI_CAP_NPCACHE BIT(2) /* Not present page cached. */
#define AMDVI_PCI_CAP_EFR BIT(3) /* Extended features. */
#define AMDVI_PCI_CAP_EXT BIT(4) /* Miscellaneous information reg. */
/*
* IOMMU extended features.
*/
#define AMDVI_EX_FEA_PREFSUP BIT(0) /* Prefetch command support. */
#define AMDVI_EX_FEA_PPRSUP BIT(1) /* PPR support */
#define AMDVI_EX_FEA_XTSUP BIT(2) /* Reserved */
#define AMDVI_EX_FEA_NXSUP BIT(3) /* No-execute. */
#define AMDVI_EX_FEA_GTSUP BIT(4) /* Guest translation support. */
#define AMDVI_EX_FEA_EFRW BIT(5) /* Reserved */
#define AMDVI_EX_FEA_IASUP BIT(6) /* Invalidate all command supp. */
#define AMDVI_EX_FEA_GASUP BIT(7) /* Guest APIC or AVIC support. */
#define AMDVI_EX_FEA_HESUP BIT(8) /* Hardware Error. */
#define AMDVI_EX_FEA_PCSUP BIT(9) /* Performance counters support. */
/* XXX: add more EFER bits. */
/*
* Device table entry or DTE
* NOTE: Must be 256-bits/32 bytes aligned.
*/
struct amdvi_dte {
uint32_t dt_valid:1; /* Device Table valid. */
uint32_t pt_valid:1; /* Page translation valid. */
uint8_t :7; /* Reserved[8:2] */
uint8_t pt_level:3; /* Paging level, 0 to disable. */
uint64_t pt_base:40; /* Page table root pointer. */
uint8_t :3; /* Reserved[54:52] */
uint8_t gv_valid:1; /* Revision 2, GVA to SPA. */
uint8_t gv_level:2; /* Revision 2, GLX level. */
uint8_t gv_cr3_lsb:3; /* Revision 2, GCR3[14:12] */
uint8_t read_allow:1; /* I/O read enabled. */
uint8_t write_allow:1; /* I/O write enabled. */
uint8_t :1; /* Reserved[63] */
uint16_t domain_id:16; /* Domain ID */
uint16_t gv_cr3_lsb2:16; /* Revision 2, GCR3[30:15] */
uint8_t iotlb_enable:1; /* Device support IOTLB */
uint8_t sup_second_io_fault:1; /* Suppress subsequent I/O faults. */
uint8_t sup_all_io_fault:1; /* Suppress all I/O page faults. */
uint8_t IOctl:2; /* Port I/O control. */
uint8_t iotlb_cache_disable:1; /* IOTLB cache hints. */
uint8_t snoop_disable:1; /* Snoop disable. */
uint8_t allow_ex:1; /* Allow exclusion. */
uint8_t sysmgmt:2; /* System management message.*/
uint8_t :1; /* Reserved[106] */
uint32_t gv_cr3_msb:21; /* Revision 2, GCR3[51:31] */
uint8_t intmap_valid:1; /* Interrupt map valid. */
uint8_t intmap_len:4; /* Interrupt map table length. */
uint8_t intmap_ign:1; /* Ignore unmapped interrupts. */
uint64_t intmap_base:46; /* IntMap base. */
uint8_t :4; /* Reserved[183:180] */
uint8_t init_pass:1; /* INIT pass through or PT */
uint8_t extintr_pass:1; /* External Interrupt PT */
uint8_t nmi_pass:1; /* NMI PT */
uint8_t :1; /* Reserved[187] */
uint8_t intr_ctrl:2; /* Interrupt control */
uint8_t lint0_pass:1; /* LINT0 PT */
uint8_t lint1_pass:1; /* LINT1 PT */
uint64_t :64; /* Reserved[255:192] */
} __attribute__((__packed__));
CTASSERT(sizeof(struct amdvi_dte) == 32);
/*
* IOMMU command entry.
*/
struct amdvi_cmd {
uint32_t word0;
uint32_t word1:28;
uint8_t opcode:4;
uint64_t addr;
} __attribute__((__packed__));
/* Command opcodes. */
#define AMDVI_CMP_WAIT_OPCODE 0x1 /* Completion wait. */
#define AMDVI_INVD_DTE_OPCODE 0x2 /* Invalidate device table entry. */
#define AMDVI_INVD_PAGE_OPCODE 0x3 /* Invalidate pages. */
#define AMDVI_INVD_IOTLB_OPCODE 0x4 /* Invalidate IOTLB pages. */
#define AMDVI_INVD_INTR_OPCODE 0x5 /* Invalidate Interrupt table. */
#define AMDVI_PREFETCH_PAGES_OPCODE 0x6 /* Prefetch IOMMU pages. */
#define AMDVI_COMP_PPR_OPCODE 0x7 /* Complete PPR request. */
#define AMDVI_INV_ALL_OPCODE 0x8 /* Invalidate all. */
/* Completion wait attributes. */
#define AMDVI_CMP_WAIT_STORE BIT(0) /* Write back data. */
#define AMDVI_CMP_WAIT_INTR BIT(1) /* Completion wait interrupt. */
#define AMDVI_CMP_WAIT_FLUSH BIT(2) /* Flush queue. */
/* Invalidate page. */
#define AMDVI_INVD_PAGE_S BIT(0) /* Invalidation size. */
#define AMDVI_INVD_PAGE_PDE BIT(1) /* Invalidate PDE. */
#define AMDVI_INVD_PAGE_GN_GVA BIT(2) /* GPA or GVA. */
#define AMDVI_INVD_PAGE_ALL_ADDR (0x7FFFFFFFFFFFFULL << 12)
/* Invalidate IOTLB. */
#define AMDVI_INVD_IOTLB_S BIT(0) /* Invalidation size 4k or addr */
#define AMDVI_INVD_IOTLB_GN_GVA BIT(2) /* GPA or GVA. */
#define AMDVI_INVD_IOTLB_ALL_ADDR (0x7FFFFFFFFFFFFULL << 12)
/* XXX: add more command entries. */
/*
* IOMMU event entry.
*/
struct amdvi_event {
uint16_t devid;
uint16_t pasid_hi;
uint16_t pasid_domid; /* PASID low or DomainID */
uint16_t flag:12;
uint8_t opcode:4;
uint64_t addr;
} __attribute__((__packed__));
CTASSERT(sizeof(struct amdvi_event) == 16);
/* Various event types. */
#define AMDVI_EVENT_INVALID_DTE 0x1
#define AMDVI_EVENT_PFAULT 0x2
#define AMDVI_EVENT_DTE_HW_ERROR 0x3
#define AMDVI_EVENT_PAGE_HW_ERROR 0x4
#define AMDVI_EVENT_ILLEGAL_CMD 0x5
#define AMDVI_EVENT_CMD_HW_ERROR 0x6
#define AMDVI_EVENT_IOTLB_TIMEOUT 0x7
#define AMDVI_EVENT_INVALID_DTE_REQ 0x8
#define AMDVI_EVENT_INVALID_PPR_REQ 0x9
#define AMDVI_EVENT_COUNTER_ZERO 0xA
#define AMDVI_EVENT_FLAG_MASK 0x1FF /* Mask for event flags. */
#define AMDVI_EVENT_FLAG_TYPE(x) (((x) >> 9) & 0x3)
/*
* IOMMU control block.
*/
struct amdvi_ctrl {
struct {
uint16_t size:9;
uint16_t :3;
uint64_t base:40; /* Devtable register base. */
uint16_t :12;
} dte;
struct {
uint16_t :12;
uint64_t base:40;
uint8_t :4;
uint8_t len:4;
uint8_t :4;
} cmd;
struct {
uint16_t :12;
uint64_t base:40;
uint8_t :4;
uint8_t len:4;
uint8_t :4;
} event;
uint16_t control :13;
uint64_t :51;
struct {
uint8_t enable:1;
uint8_t allow:1;
uint16_t :10;
uint64_t base:40;
uint16_t :12;
uint16_t :12;
uint64_t limit:40;
uint16_t :12;
} excl;
/*
* Revision 2 only.
*/
uint64_t ex_feature;
struct {
uint16_t :12;
uint64_t base:40;
uint8_t :4;
uint8_t len:4;
uint8_t :4;
} ppr;
uint64_t first_event;
uint64_t second_event;
uint64_t event_status;
/* Revision 2 only, end. */
uint8_t pad1[0x1FA8]; /* Padding. */
uint32_t cmd_head:19;
uint64_t :45;
uint32_t cmd_tail:19;
uint64_t :45;
uint32_t evt_head:19;
uint64_t :45;
uint32_t evt_tail:19;
uint64_t :45;
uint64_t :56;
uint8_t status:8;
uint64_t pad2;
uint8_t :4;
uint16_t ppr_head:15;
uint64_t :45;
uint8_t :4;
uint16_t ppr_tail:15;
uint64_t :45;
uint8_t pad3[0x1FC0]; /* Padding. */
/* XXX: More for rev2. */
} __attribute__((__packed__));
CTASSERT(offsetof(struct amdvi_ctrl, pad1)== 0x58);
CTASSERT(offsetof(struct amdvi_ctrl, pad2)== 0x2028);
CTASSERT(offsetof(struct amdvi_ctrl, pad3)== 0x2040);
#define AMDVI_MMIO_V1_SIZE (4 * PAGE_SIZE) /* v1 size */
/*
* AMF IOMMU v2 size including event counters
*/
#define AMDVI_MMIO_V2_SIZE (8 * PAGE_SIZE)
CTASSERT(sizeof(struct amdvi_ctrl) == 0x4000);
CTASSERT(sizeof(struct amdvi_ctrl) == AMDVI_MMIO_V1_SIZE);
/* IVHD flag */
#define IVHD_FLAG_HTT BIT(0) /* Hypertransport Tunnel. */
#define IVHD_FLAG_PPW BIT(1) /* Pass posted write. */
#define IVHD_FLAG_RPPW BIT(2) /* Response pass posted write. */
#define IVHD_FLAG_ISOC BIT(3) /* Isoc support. */
#define IVHD_FLAG_IOTLB BIT(4) /* IOTLB support. */
#define IVHD_FLAG_COH BIT(5) /* Coherent control, default 1 */
#define IVHD_FLAG_PFS BIT(6) /* Prefetch IOMMU pages. */
#define IVHD_FLAG_PPRS BIT(7) /* Peripheral page support. */
/* IVHD device entry data setting. */
#define IVHD_DEV_LINT0_PASS BIT(6) /* LINT0 interrupts. */
#define IVHD_DEV_LINT1_PASS BIT(7) /* LINT1 interrupts. */
/* Bit[5:4] for System Mgmt. Bit3 is reserved. */
#define IVHD_DEV_INIT_PASS BIT(0) /* INIT */
#define IVHD_DEV_EXTINTR_PASS BIT(1) /* ExtInt */
#define IVHD_DEV_NMI_PASS BIT(2) /* NMI */
/* IVHD 8-byte extended data settings. */
#define IVHD_DEV_EXT_ATS_DISABLE BIT(31) /* Disable ATS */
/* IOMMU control register. */
#define AMDVI_CTRL_EN BIT(0) /* IOMMU enable. */
#define AMDVI_CTRL_HTT BIT(1) /* Hypertransport tunnel enable. */
#define AMDVI_CTRL_ELOG BIT(2) /* Event log enable. */
#define AMDVI_CTRL_ELOGINT BIT(3) /* Event log interrupt. */
#define AMDVI_CTRL_COMINT BIT(4) /* Completion wait interrupt. */
#define AMDVI_CTRL_PPW BIT(8)
#define AMDVI_CTRL_RPPW BIT(9)
#define AMDVI_CTRL_COH BIT(10)
#define AMDVI_CTRL_ISOC BIT(11)
#define AMDVI_CTRL_CMD BIT(12) /* Command buffer enable. */
#define AMDVI_CTRL_PPRLOG BIT(13)
#define AMDVI_CTRL_PPRINT BIT(14)
#define AMDVI_CTRL_PPREN BIT(15)
#define AMDVI_CTRL_GTE BIT(16) /* Guest translation enable. */
#define AMDVI_CTRL_GAE BIT(17) /* Guest APIC enable. */
/* Invalidation timeout. */
#define AMDVI_CTRL_INV_NO_TO 0 /* No timeout. */
#define AMDVI_CTRL_INV_TO_1ms 1 /* 1 ms */
#define AMDVI_CTRL_INV_TO_10ms 2 /* 10 ms */
#define AMDVI_CTRL_INV_TO_100ms 3 /* 100 ms */
#define AMDVI_CTRL_INV_TO_1S 4 /* 1 second */
#define AMDVI_CTRL_INV_TO_10S 5 /* 10 second */
#define AMDVI_CTRL_INV_TO_100S 6 /* 100 second */
/*
* Max number of PCI devices.
* 256 bus x 32 slot/devices x 8 functions.
*/
#define PCI_NUM_DEV_MAX 0x10000
/* Maximum number of domains supported by IOMMU. */
#define AMDVI_MAX_DOMAIN (BIT(16) - 1)
/*
* IOMMU Page Table attributes.
*/
#define AMDVI_PT_PRESENT BIT(0)
#define AMDVI_PT_COHERENT BIT(60)
#define AMDVI_PT_READ BIT(61)
#define AMDVI_PT_WRITE BIT(62)
#define AMDVI_PT_RW (AMDVI_PT_READ | AMDVI_PT_WRITE)
#define AMDVI_PT_MASK 0xFFFFFFFFFF000UL /* Only [51:12] for PA */
#define AMDVI_PD_LEVEL_SHIFT 9
#define AMDVI_PD_SUPER(x) (((x) >> AMDVI_PD_LEVEL_SHIFT) == 7)
/*
* IOMMU Status, offset 0x2020
*/
#define AMDVI_STATUS_EV_OF BIT(0) /* Event overflow. */
#define AMDVI_STATUS_EV_INTR BIT(1) /* Event interrupt. */
/* Completion wait command completed. */
#define AMDVI_STATUS_CMP BIT(2)
#define IVRS_CTRL_RID 1 /* MMIO RID */
/* ACPI IVHD */
struct ivhd_dev_cfg {
uint32_t start_id;
uint32_t end_id;
uint8_t data; /* Device configuration. */
bool enable_ats; /* ATS enabled for the device. */
int ats_qlen; /* ATS invalidation queue depth. */
};
struct amdvi_domain {
uint64_t *ptp; /* Highest level page table */
int ptp_level; /* Level of page tables */
u_int id; /* Domain id */
SLIST_ENTRY (amdvi_domain) next;
};
/*
* AMD IOMMU softc.
*/
struct amdvi_softc {
struct amdvi_ctrl *ctrl; /* Control area. */
device_t dev; /* IOMMU device. */
bool iotlb; /* IOTLB supported by IOMMU */
struct amdvi_cmd *cmd; /* Command descriptor area. */
int cmd_max; /* Max number of commands. */
uint64_t cmp_data; /* Command completion write back. */
struct amdvi_event *event; /* Event descriptor area. */
struct resource *event_res; /* Event interrupt resource. */
void *event_tag; /* Event interrupt tag. */
int event_max; /* Max number of events. */
int event_irq;
int event_rid;
/* ACPI various flags. */
uint32_t ivhd_flag; /* ACPI IVHD flag. */
uint32_t ivhd_efr; /* ACPI v1 Reserved or v2 EFR . */
/* PCI related. */
uint16_t cap_off; /* PCI Capability offset. */
uint8_t pci_cap; /* PCI capability. */
uint64_t pci_efr; /* PCI EFR for rev2.0 */
uint16_t pci_seg; /* IOMMU PCI domain/segment. */
uint16_t pci_rid; /* PCI BDF of IOMMU */
/* Device range under this IOMMU. */
uint16_t start_dev_rid; /* First device under this IOMMU. */
uint16_t end_dev_rid; /* Last device under this IOMMU. */
/* BIOS provided device configuration for end points. */
struct ivhd_dev_cfg dev_cfg[10];
int dev_cfg_cnt;
/* Software statistics. */
uint64_t event_intr_cnt; /* Total event INTR count. */
uint64_t total_cmd; /* Total number of commands. */
};
int amdvi_setup_hw(struct amdvi_softc *softc);
int amdvi_teardown_hw(struct amdvi_softc *softc);
#endif /* _AMDVI_PRIV_H_ */

498
sys/amd64/vmm/amd/ivrs_drv.c Executable file
View File

@ -0,0 +1,498 @@
/*-
* Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <machine/vmparam.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <contrib/dev/acpica/include/accommon.h>
#include <dev/acpica/acpivar.h>
#include "io/iommu.h"
#include "amdvi_priv.h"
device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */
int ivhd_count; /* Number of IVHD or AMD-Vi devices. */
extern int amdvi_ptp_level; /* Page table levels. */
typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER * ptr, void *arg);
/*
* Iterate IVRS table for IVHD and IVMD device type.
*/
static void
ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
{
ACPI_TABLE_IVRS *ivrs;
ACPI_IVRS_HEADER *ivrs_hdr, *end;
ACPI_STATUS status;
status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
if (ACPI_FAILURE(status))
return;
if (ivrs->Header.Length == 0) {
return;
}
ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
while (ivrs_hdr < end) {
switch (ivrs_hdr->Type) {
case ACPI_IVRS_TYPE_HARDWARE: /* Legacy */
case 0x11:
case 0x40: /* ACPI HID */
if (!iter(ivrs_hdr, arg))
return;
break;
case ACPI_IVRS_TYPE_MEMORY1:
case ACPI_IVRS_TYPE_MEMORY2:
case ACPI_IVRS_TYPE_MEMORY3:
if (!iter(ivrs_hdr, arg))
return;
break;
default:
printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
}
ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
ivrs_hdr->Length);
if (ivrs_hdr->Length < 0) {
printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n", ivrs_hdr->Length);
break;
}
}
}
static int
ivrs_is_ivhd(UINT8 type)
{
if ((type == ACPI_IVRS_TYPE_HARDWARE) || (type == 0x11) || (type == 0x40))
return (1);
return (0);
}
/* Count the number of AMD-Vi devices in the system. */
static int
ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
{
if (ivrs_is_ivhd(ivrs_he->Type))
ivhd_count++;
return (1);
}
struct find_ivrs_hdr_args {
int i;
ACPI_IVRS_HEADER *ptr;
};
static int
ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
{
struct find_ivrs_hdr_args *fi;
fi = (struct find_ivrs_hdr_args *)args;
if (ivrs_is_ivhd(ivrs_hdr->Type)) {
if (fi->i == 0) {
fi->ptr = ivrs_hdr;
return (0);
}
fi->i--;
}
return (1);
}
static ACPI_IVRS_HARDWARE *
ivhd_find_by_index(int idx)
{
struct find_ivrs_hdr_args fi;
fi.i = idx;
fi.ptr = NULL;
ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
return ((ACPI_IVRS_HARDWARE *)fi.ptr);
}
static void
ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
uint32_t end_id, uint8_t cfg, bool ats)
{
struct ivhd_dev_cfg *dev_cfg;
/* If device doesn't have special data, don't add it. */
if (!cfg)
return;
dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
dev_cfg->start_id = start_id;
dev_cfg->end_id = end_id;
dev_cfg->data = cfg;
dev_cfg->enable_ats = ats;
}
/*
* Record device attributes as suggested by BIOS.
*/
static int
ivhd_dev_parse(ACPI_IVRS_HARDWARE * ivhd, struct amdvi_softc *softc)
{
ACPI_IVRS_DE_HEADER *de, *end;
int range_start_id = 0, range_end_id = 0;
uint32_t *extended;
uint8_t all_data = 0, range_data = 0;
bool range_enable_ats = false, enable_ats;
softc->start_dev_rid = ~0;
softc->end_dev_rid = 0;
de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
sizeof(ACPI_IVRS_HARDWARE));
end = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
ivhd->Header.Length);
while (de < (ACPI_IVRS_DE_HEADER *) end) {
softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
switch (de->Type) {
case ACPI_IVRS_TYPE_ALL:
all_data = de->DataSetting;
break;
case ACPI_IVRS_TYPE_SELECT:
case ACPI_IVRS_TYPE_ALIAS_SELECT:
case ACPI_IVRS_TYPE_EXT_SELECT:
enable_ats = false;
if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
extended = (uint32_t *)(de + 1);
enable_ats =
(*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
false : true;
}
ivhd_dev_add_entry(softc, de->Id, de->Id,
de->DataSetting | all_data, enable_ats);
break;
case ACPI_IVRS_TYPE_START:
case ACPI_IVRS_TYPE_ALIAS_START:
case ACPI_IVRS_TYPE_EXT_START:
range_start_id = de->Id;
range_data = de->DataSetting;
if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
extended = (uint32_t *)(de + 1);
range_enable_ats =
(*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
false : true;
}
break;
case ACPI_IVRS_TYPE_END:
range_end_id = de->Id;
ivhd_dev_add_entry(softc, range_start_id, range_end_id,
range_data | all_data, range_enable_ats);
range_start_id = range_end_id = 0;
range_data = 0;
all_data = 0;
break;
case ACPI_IVRS_TYPE_PAD4:
break;
case ACPI_IVRS_TYPE_SPECIAL:
/* HPET or IOAPIC */
break;
default:
if ((de->Type < 5) ||
(de->Type >= ACPI_IVRS_TYPE_PAD8))
device_printf(softc->dev,
"Unknown dev entry:0x%x\n", de->Type);
}
if (softc->dev_cfg_cnt >
(sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
device_printf(softc->dev,
"WARN Too many device entries.\n");
return (EINVAL);
}
de++;
}
KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
("Device end[0x%x] < start[0x%x.\n",
softc->end_dev_rid, softc->start_dev_rid));
return (0);
}
static void
ivhd_identify(driver_t *driver, device_t parent)
{
ACPI_TABLE_IVRS *ivrs;
ACPI_IVRS_HARDWARE *ivhd;
ACPI_STATUS status;
uint32_t info;
int i, count = 0;
if (acpi_disabled("ivhd"))
return;
status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
if (ACPI_FAILURE(status))
return;
if (ivrs->Header.Length == 0) {
return;
}
info = ivrs->Info;
printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n",
REG_BITS(info, 21, 15), REG_BITS(info, 14, 8),
REG_BITS(info, 7, 5), REG_BITS(info, 22, 22),
"\020\001HtAtsResv");
ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
if (!ivhd_count)
return;
ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
M_WAITOK | M_ZERO);
for (i = 0; i < ivhd_count; i++) {
ivhd = ivhd_find_by_index(i);
if (ivhd == NULL) {
printf("Can't find IVHD entry%d\n", i);
continue;
}
ivhd_devs[i] = BUS_ADD_CHILD(parent, 1, "ivhd", i);
/*
* XXX: In case device was not destroyed before, add will fail.
* locate the old device instance.
*/
if (ivhd_devs[i] == NULL) {
ivhd_devs[i] = device_find_child(parent, "ivhd", i);
if (ivhd_devs[i] == NULL) {
printf("AMD-Vi: cant find AMD-Vi dev%d\n", i);
break;
}
}
count++;
}
/*
* Update device count in case failed to attach.
*/
ivhd_count = count;
}
static int
ivhd_probe(device_t dev)
{
if (acpi_get_handle(dev) != NULL)
return (ENXIO);
device_set_desc(dev, "AMD-Vi/IOMMU or ivhd");
return (BUS_PROBE_NOWILDCARD);
}
static int
ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
{
device_t dev;
int max_ptp_level;
dev = softc->dev;
device_printf(dev, "Flag:%b\n", softc->ivhd_flag,
"\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup"
"\006Coherent\007PreFSup\008PPRSup");
/*
* If no extended feature[EFR], its rev1 with maximum paging level as 7.
*/
max_ptp_level = 7;
if (softc->ivhd_efr) {
device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d "
"MsiNumPr = %d PNBanks= %d PNCounters= %d\n"
"max PASID = %d EFR: %b \n",
REG_BITS(softc->ivhd_efr, 31, 30),
REG_BITS(softc->ivhd_efr, 29, 28),
REG_BITS(softc->ivhd_efr, 4, 3),
REG_BITS(softc->ivhd_efr, 27, 23),
REG_BITS(softc->ivhd_efr, 22, 17),
REG_BITS(softc->ivhd_efr, 16, 13),
REG_BITS(softc->ivhd_efr, 12, 8),
softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup"
"\006GASup\007HESup\008PPRSup");
max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4;
}
/* Make sure device support minimum page level as requested by user. */
if (max_ptp_level < amdvi_ptp_level) {
device_printf(dev, "Insufficient PTP level:%d\n",
max_ptp_level);
return (EINVAL);
}
device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n",
max_ptp_level, amdvi_ptp_level);
device_printf(softc->dev, "device supported range "
"[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid);
return (0);
}
static int
ivhd_attach(device_t dev)
{
ACPI_IVRS_HARDWARE *ivhd;
struct amdvi_softc *softc;
int status, unit;
unit = device_get_unit(dev);
/* Make sure its same device for which attach is called. */
if (ivhd_devs[unit] != dev)
panic("Not same device old %p new %p", ivhd_devs[unit], dev);
softc = device_get_softc(dev);
softc->dev = dev;
ivhd = ivhd_find_by_index(unit);
if (ivhd == NULL)
return (EINVAL);
softc->pci_seg = ivhd->PciSegmentGroup;
softc->pci_rid = ivhd->Header.DeviceId;
softc->ivhd_flag = ivhd->Header.Flags;
softc->ivhd_efr = ivhd->Reserved;
/*
* PCI capability has more capabilities that are not part of IVRS.
*/
softc->cap_off = ivhd->CapabilityOffset;
#ifdef notyet
/* IVHD Info bit[4:0] is event MSI/X number. */
softc->event_msix = ivhd->Info & 0x1F;
#endif
softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
status = ivhd_dev_parse(ivhd, softc);
if (status != 0) {
device_printf(dev,
"endpoint device parsing error=%d\n", status);
}
status = ivhd_print_cap(softc, ivhd);
if (status != 0) {
return (status);
}
status = amdvi_setup_hw(softc);
if (status != 0) {
device_printf(dev, "couldn't be initialised, error=%d\n",
status);
return (status);
}
return (0);
}
static int
ivhd_detach(device_t dev)
{
struct amdvi_softc *softc;
softc = device_get_softc(dev);
amdvi_teardown_hw(softc);
/*
* XXX: delete the device.
* don't allow detach, return EBUSY.
*/
return (0);
}
static int
ivhd_suspend(device_t dev)
{
return (0);
}
static int
ivhd_resume(device_t dev)
{
return (0);
}
static device_method_t ivhd_methods[] = {
DEVMETHOD(device_identify, ivhd_identify),
DEVMETHOD(device_probe, ivhd_probe),
DEVMETHOD(device_attach, ivhd_attach),
DEVMETHOD(device_detach, ivhd_detach),
DEVMETHOD(device_suspend, ivhd_suspend),
DEVMETHOD(device_resume, ivhd_resume),
DEVMETHOD_END
};
static driver_t ivhd_driver = {
"ivhd",
ivhd_methods,
sizeof(struct amdvi_softc),
};
static devclass_t ivhd_devclass;
/*
* Load this module at the end after PCI re-probing to configure interrupt.
*/
DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
SI_ORDER_ANY);
MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
MODULE_DEPEND(ivhd, pci, 1, 1, 1);

View File

@ -2,7 +2,7 @@
KMOD= vmm
SRCS= opt_acpi.h opt_ddb.h device_if.h bus_if.h pci_if.h
SRCS= opt_acpi.h opt_ddb.h device_if.h bus_if.h pci_if.h pcib_if.h acpi_if.h
SRCS+= vmx_assym.h svm_assym.h
DPSRCS= vmx_genassym.c svm_genassym.c
@ -51,7 +51,8 @@ SRCS+= vmcb.c \
svm.c \
svm_support.S \
npt.c \
amdv.c \
ivrs_drv.c \
amdvi_hw.c \
svm_msr.c
CLEANFILES= vmx_assym.h vmx_genassym.o svm_assym.h svm_genassym.o