[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] Move vtd and amd iommu code to arch-generic location.
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1203606397 0 # Node ID 591cfd37bd5409d534034c64a3356a9b492b23bb # Parent f1a107ec62b6e8829981ff05fb988047174de479 Move vtd and amd iommu code to arch-generic location. Signed-off-by: Weidong Han <weidong.han@xxxxxxxxx> --- xen/arch/x86/hvm/svm/amd_iommu/Makefile | 4 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c | 215 -- xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c | 147 - xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c | 450 ---- xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c | 578 ----- xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h | 48 xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h | 513 ----- xen/arch/x86/hvm/vmx/vtd/Makefile | 6 xen/arch/x86/hvm/vmx/vtd/dmar.c | 625 ------ xen/arch/x86/hvm/vmx/vtd/dmar.h | 104 - xen/arch/x86/hvm/vmx/vtd/extern.h | 51 xen/arch/x86/hvm/vmx/vtd/intel-iommu.c | 2178 ---------------------- xen/arch/x86/hvm/vmx/vtd/intremap.c | 340 --- xen/arch/x86/hvm/vmx/vtd/io.c | 296 -- xen/arch/x86/hvm/vmx/vtd/msi.h | 127 - xen/arch/x86/hvm/vmx/vtd/pci-direct.h | 48 xen/arch/x86/hvm/vmx/vtd/pci_regs.h | 449 ---- xen/arch/x86/hvm/vmx/vtd/qinval.c | 456 ---- xen/arch/x86/hvm/vmx/vtd/utils.c | 338 --- xen/arch/x86/hvm/vmx/vtd/vtd.h | 54 xen/arch/x86/Rules.mk | 4 xen/arch/x86/hvm/svm/Makefile | 2 xen/arch/x86/hvm/vmx/Makefile | 2 xen/drivers/Makefile | 1 xen/drivers/passthrough/Makefile | 2 xen/drivers/passthrough/amd/Makefile | 4 xen/drivers/passthrough/amd/iommu_detect.c | 215 ++ xen/drivers/passthrough/amd/iommu_init.c | 147 + xen/drivers/passthrough/amd/iommu_map.c | 450 ++++ xen/drivers/passthrough/amd/pci_amd_iommu.c | 578 +++++ xen/drivers/passthrough/pci-direct.h | 48 xen/drivers/passthrough/pci_regs.h | 530 +++++ xen/drivers/passthrough/vtd/Makefile | 6 xen/drivers/passthrough/vtd/dmar.c | 625 ++++++ xen/drivers/passthrough/vtd/dmar.h | 104 + xen/drivers/passthrough/vtd/extern.h | 51 xen/drivers/passthrough/vtd/intremap.c | 339 +++ xen/drivers/passthrough/vtd/io.c | 296 ++ xen/drivers/passthrough/vtd/iommu.c | 2178 ++++++++++++++++++++++ xen/drivers/passthrough/vtd/msi.h | 127 + xen/drivers/passthrough/vtd/qinval.c | 455 ++++ xen/drivers/passthrough/vtd/utils.c | 337 +++ xen/drivers/passthrough/vtd/vtd.h | 54 43 files changed, 6550 insertions(+), 7032 deletions(-) diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/Rules.mk --- a/xen/arch/x86/Rules.mk Thu Feb 21 14:50:27 2008 +0000 +++ b/xen/arch/x86/Rules.mk Thu Feb 21 15:06:37 2008 +0000 @@ -39,7 +39,9 @@ CFLAGS += -DCONFIG_X86_SUPERVISOR_MODE_K CFLAGS += -DCONFIG_X86_SUPERVISOR_MODE_KERNEL=1 endif -ifeq ($(XEN_TARGET_ARCH),x86_32) +x86 := y + +ifeq ($(TARGET_SUBARCH),x86_32) x86_32 := y x86_64 := n endif diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/Makefile --- a/xen/arch/x86/hvm/svm/Makefile Thu Feb 21 14:50:27 2008 +0000 +++ b/xen/arch/x86/hvm/svm/Makefile Thu Feb 21 15:06:37 2008 +0000 @@ -1,7 +1,5 @@ subdir-$(x86_32) += x86_32 subdir-$(x86_32) += x86_32 subdir-$(x86_64) += x86_64 - -subdir-y += amd_iommu obj-y += asid.o obj-y += emulate.o diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/Makefile --- a/xen/arch/x86/hvm/svm/amd_iommu/Makefile Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -obj-y += amd-iommu-detect.o -obj-y += amd-iommu-init.o -obj-y += amd-iommu-map.o -obj-y += pci-amd-iommu.o diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c --- a/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2007 Advanced Micro Devices, Inc. - * Author: Leo Duran <leo.duran@xxxxxxx> - * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <xen/config.h> -#include <xen/errno.h> -#include <asm/iommu.h> -#include <asm/amd-iommu.h> -#include <asm/hvm/svm/amd-iommu-proto.h> -#include "pci-direct.h" -#include "pci_regs.h" - -static int __init valid_bridge_bus_config(int bus, int dev, int func, - int *sec_bus, int *sub_bus) -{ - int pri_bus; - - pri_bus = read_pci_config_byte(bus, dev, func, PCI_PRIMARY_BUS); - *sec_bus = read_pci_config_byte(bus, dev, func, PCI_SECONDARY_BUS); - *sub_bus = read_pci_config_byte(bus, dev, func, PCI_SUBORDINATE_BUS); - - return ( pri_bus == bus && *sec_bus > bus && *sub_bus >= *sec_bus ); -} - -int __init get_iommu_last_downstream_bus(struct amd_iommu *iommu) -{ - int bus, dev, func; - int devfn, hdr_type; - int sec_bus, sub_bus; - int multi_func; - - bus = iommu->last_downstream_bus = iommu->root_bus; - iommu->downstream_bus_present[bus] = 1; - dev = PCI_SLOT(iommu->first_devfn); - multi_func = PCI_FUNC(iommu->first_devfn) > 0; - for ( devfn = iommu->first_devfn; devfn <= iommu->last_devfn; ++devfn ) { - /* skipping to next device#? */ - if ( dev != PCI_SLOT(devfn) ) { - dev = PCI_SLOT(devfn); - multi_func = 0; - } - func = PCI_FUNC(devfn); - - if ( !VALID_PCI_VENDOR_ID( - read_pci_config_16(bus, dev, func, PCI_VENDOR_ID)) ) - continue; - - hdr_type = read_pci_config_byte(bus, dev, func, - PCI_HEADER_TYPE); - if ( func == 0 ) - multi_func = IS_PCI_MULTI_FUNCTION(hdr_type); - - if ( (func == 0 || multi_func) && - IS_PCI_TYPE1_HEADER(hdr_type) ) { - if (!valid_bridge_bus_config(bus, dev, func, - &sec_bus, &sub_bus)) - return -ENODEV; - - if ( sub_bus > iommu->last_downstream_bus ) - iommu->last_downstream_bus = sub_bus; - do { - iommu->downstream_bus_present[sec_bus] = 1; - } while ( sec_bus++ < sub_bus ); - } - } - - return 0; -} - -int __init get_iommu_capabilities(u8 bus, u8 dev, u8 func, u8 cap_ptr, - struct amd_iommu *iommu) -{ - u32 cap_header, cap_range; - u64 mmio_bar; - -#if HACK_BIOS_SETTINGS - /* remove it when BIOS available */ - write_pci_config(bus, dev, func, - cap_ptr + PCI_CAP_MMIO_BAR_HIGH_OFFSET, 0x00000000); - write_pci_config(bus, dev, func, - cap_ptr + PCI_CAP_MMIO_BAR_LOW_OFFSET, 0x40000001); - /* remove it when BIOS available */ -#endif - - mmio_bar = (u64)read_pci_config(bus, dev, func, - cap_ptr + PCI_CAP_MMIO_BAR_HIGH_OFFSET) << 32; - mmio_bar |= read_pci_config(bus, dev, func, - cap_ptr + PCI_CAP_MMIO_BAR_LOW_OFFSET) & - PCI_CAP_MMIO_BAR_LOW_MASK; - iommu->mmio_base_phys = (unsigned long)mmio_bar; - - if ( (mmio_bar == 0) || ( (mmio_bar & 0x3FFF) != 0 ) ) { - dprintk(XENLOG_ERR , - "AMD IOMMU: Invalid MMIO_BAR = 0x%"PRIx64"\n", mmio_bar); - return -ENODEV; - } - - cap_header = read_pci_config(bus, dev, func, cap_ptr); - iommu->revision = get_field_from_reg_u32(cap_header, - PCI_CAP_REV_MASK, PCI_CAP_REV_SHIFT); - iommu->iotlb_support = get_field_from_reg_u32(cap_header, - PCI_CAP_IOTLB_MASK, PCI_CAP_IOTLB_SHIFT); - iommu->ht_tunnel_support = get_field_from_reg_u32(cap_header, - PCI_CAP_HT_TUNNEL_MASK, - PCI_CAP_HT_TUNNEL_SHIFT); - iommu->not_present_cached = get_field_from_reg_u32(cap_header, - PCI_CAP_NP_CACHE_MASK, - PCI_CAP_NP_CACHE_SHIFT); - - cap_range = read_pci_config(bus, dev, func, - cap_ptr + PCI_CAP_RANGE_OFFSET); - iommu->root_bus = get_field_from_reg_u32(cap_range, - PCI_CAP_BUS_NUMBER_MASK, - PCI_CAP_BUS_NUMBER_SHIFT); - iommu->first_devfn = get_field_from_reg_u32(cap_range, - PCI_CAP_FIRST_DEVICE_MASK, - PCI_CAP_FIRST_DEVICE_SHIFT); - iommu->last_devfn = get_field_from_reg_u32(cap_range, - PCI_CAP_LAST_DEVICE_MASK, - PCI_CAP_LAST_DEVICE_SHIFT); - - return 0; -} - -static int __init scan_caps_for_iommu(int bus, int dev, int func, - iommu_detect_callback_ptr_t iommu_detect_callback) -{ - int cap_ptr, cap_id, cap_type; - u32 cap_header; - int count, error = 0; - - count = 0; - cap_ptr = read_pci_config_byte(bus, dev, func, - PCI_CAPABILITY_LIST); - while ( cap_ptr >= PCI_MIN_CAP_OFFSET && - count < PCI_MAX_CAP_BLOCKS && !error ) { - cap_ptr &= PCI_CAP_PTR_MASK; - cap_header = read_pci_config(bus, dev, func, cap_ptr); - cap_id = get_field_from_reg_u32(cap_header, - PCI_CAP_ID_MASK, PCI_CAP_ID_SHIFT); - - if ( cap_id == PCI_CAP_ID_SECURE_DEVICE ) { - cap_type = get_field_from_reg_u32(cap_header, - PCI_CAP_TYPE_MASK, PCI_CAP_TYPE_SHIFT); - if ( cap_type == PCI_CAP_TYPE_IOMMU ) { - error = iommu_detect_callback( - bus, dev, func, cap_ptr); - } - } - - cap_ptr = get_field_from_reg_u32(cap_header, - PCI_CAP_NEXT_PTR_MASK, PCI_CAP_NEXT_PTR_SHIFT); - ++count; } - - return error; -} - -static int __init scan_functions_for_iommu(int bus, int dev, - iommu_detect_callback_ptr_t iommu_detect_callback) -{ - int func, hdr_type; - int count, error = 0; - - func = 0; - count = 1; - while ( VALID_PCI_VENDOR_ID(read_pci_config_16(bus, dev, func, - PCI_VENDOR_ID)) && !error && func < count ) { - hdr_type = read_pci_config_byte(bus, dev, func, - PCI_HEADER_TYPE); - - if ( func == 0 && IS_PCI_MULTI_FUNCTION(hdr_type) ) - count = PCI_MAX_FUNC_COUNT; - - if ( IS_PCI_TYPE0_HEADER(hdr_type) || - IS_PCI_TYPE1_HEADER(hdr_type) ) { - error = scan_caps_for_iommu(bus, dev, func, - iommu_detect_callback); - } - ++func; - } - - return error; -} - - -int __init scan_for_iommu(iommu_detect_callback_ptr_t iommu_detect_callback) -{ - int bus, dev, error = 0; - - for ( bus = 0; bus < PCI_MAX_BUS_COUNT && !error; ++bus ) { - for ( dev = 0; dev < PCI_MAX_DEV_COUNT && !error; ++dev ) { - error = scan_functions_for_iommu(bus, dev, - iommu_detect_callback); - } - } - - return error; -} - diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c --- a/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2007 Advanced Micro Devices, Inc. - * Author: Leo Duran <leo.duran@xxxxxxx> - * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <xen/config.h> -#include <xen/errno.h> -#include <asm/amd-iommu.h> -#include <asm/hvm/svm/amd-iommu-proto.h> -#include <asm-x86/fixmap.h> -#include "pci-direct.h" -#include "pci_regs.h" - -extern int nr_amd_iommus; - -int __init map_iommu_mmio_region(struct amd_iommu *iommu) -{ - unsigned long mfn; - - if ( nr_amd_iommus > MAX_AMD_IOMMUS ) { - gdprintk(XENLOG_ERR, - "IOMMU: nr_amd_iommus %d > MAX_IOMMUS\n", nr_amd_iommus); - return -ENOMEM; - } - - iommu->mmio_base = (void *) fix_to_virt(FIX_IOMMU_MMIO_BASE_0 + - nr_amd_iommus * MMIO_PAGES_PER_IOMMU); - mfn = (unsigned long)iommu->mmio_base_phys >> PAGE_SHIFT; - map_pages_to_xen((unsigned long)iommu->mmio_base, mfn, - MMIO_PAGES_PER_IOMMU, PAGE_HYPERVISOR_NOCACHE); - - memset((u8*)iommu->mmio_base, 0, IOMMU_MMIO_REGION_LENGTH); - - return 0; -} - -void __init unmap_iommu_mmio_region(struct amd_iommu *iommu) -{ - if ( iommu->mmio_base ) { - iounmap(iommu->mmio_base); - iommu->mmio_base = NULL; - } -} - -void __init register_iommu_dev_table_in_mmio_space(struct amd_iommu *iommu) -{ - u64 addr_64, addr_lo, addr_hi; - u32 entry; - - addr_64 = (u64)virt_to_maddr(iommu->dev_table.buffer); - addr_lo = addr_64 & DMA_32BIT_MASK; - addr_hi = addr_64 >> 32; - - set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, - IOMMU_DEV_TABLE_BASE_LOW_MASK, - IOMMU_DEV_TABLE_BASE_LOW_SHIFT, &entry); - set_field_in_reg_u32((iommu->dev_table.alloc_size / PAGE_SIZE) - 1, - entry, IOMMU_DEV_TABLE_SIZE_MASK, - IOMMU_DEV_TABLE_SIZE_SHIFT, &entry); - writel(entry, iommu->mmio_base + IOMMU_DEV_TABLE_BASE_LOW_OFFSET); - - set_field_in_reg_u32((u32)addr_hi, 0, - IOMMU_DEV_TABLE_BASE_HIGH_MASK, - IOMMU_DEV_TABLE_BASE_HIGH_SHIFT, &entry); - writel(entry, iommu->mmio_base + IOMMU_DEV_TABLE_BASE_HIGH_OFFSET); -} - -void __init register_iommu_cmd_buffer_in_mmio_space(struct amd_iommu *iommu) -{ - u64 addr_64, addr_lo, addr_hi; - u32 power_of2_entries; - u32 entry; - - addr_64 = (u64)virt_to_maddr(iommu->cmd_buffer.buffer); - addr_lo = addr_64 & DMA_32BIT_MASK; - addr_hi = addr_64 >> 32; - - set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, - IOMMU_CMD_BUFFER_BASE_LOW_MASK, - IOMMU_CMD_BUFFER_BASE_LOW_SHIFT, &entry); - writel(entry, iommu->mmio_base + IOMMU_CMD_BUFFER_BASE_LOW_OFFSET); - - power_of2_entries = get_order_from_bytes(iommu->cmd_buffer.alloc_size) + - IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE; - - set_field_in_reg_u32((u32)addr_hi, 0, - IOMMU_CMD_BUFFER_BASE_HIGH_MASK, - IOMMU_CMD_BUFFER_BASE_HIGH_SHIFT, &entry); - set_field_in_reg_u32(power_of2_entries, entry, - IOMMU_CMD_BUFFER_LENGTH_MASK, - IOMMU_CMD_BUFFER_LENGTH_SHIFT, &entry); - writel(entry, iommu->mmio_base+IOMMU_CMD_BUFFER_BASE_HIGH_OFFSET); -} - -static void __init set_iommu_translation_control(struct amd_iommu *iommu, - int enable) -{ - u32 entry; - - entry = readl(iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); - set_field_in_reg_u32(iommu->ht_tunnel_support ? IOMMU_CONTROL_ENABLED : - IOMMU_CONTROL_ENABLED, entry, - IOMMU_CONTROL_HT_TUNNEL_TRANSLATION_MASK, - IOMMU_CONTROL_HT_TUNNEL_TRANSLATION_SHIFT, &entry); - set_field_in_reg_u32(enable ? IOMMU_CONTROL_ENABLED : - IOMMU_CONTROL_ENABLED, entry, - IOMMU_CONTROL_TRANSLATION_ENABLE_MASK, - IOMMU_CONTROL_TRANSLATION_ENABLE_SHIFT, &entry); - writel(entry, iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); -} - -static void __init set_iommu_command_buffer_control(struct amd_iommu *iommu, - int enable) -{ - u32 entry; - - entry = readl(iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); - set_field_in_reg_u32(enable ? IOMMU_CONTROL_ENABLED : - IOMMU_CONTROL_ENABLED, entry, - IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_MASK, - IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_SHIFT, &entry); - writel(entry, iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); -} - -void __init enable_iommu(struct amd_iommu *iommu) -{ - set_iommu_command_buffer_control(iommu, IOMMU_CONTROL_ENABLED); - set_iommu_translation_control(iommu, IOMMU_CONTROL_ENABLED); - printk("AMD IOMMU %d: Enabled\n", nr_amd_iommus); -} - - diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c --- a/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,450 +0,0 @@ -/* - * Copyright (C) 2007 Advanced Micro Devices, Inc. - * Author: Leo Duran <leo.duran@xxxxxxx> - * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <xen/sched.h> -#include <asm/hvm/iommu.h> -#include <asm/amd-iommu.h> -#include <asm/hvm/svm/amd-iommu-proto.h> - -extern long amd_iommu_poll_comp_wait; - -static int queue_iommu_command(struct amd_iommu *iommu, u32 cmd[]) -{ - u32 tail, head, *cmd_buffer; - int i; - - tail = iommu->cmd_buffer_tail; - if ( ++tail == iommu->cmd_buffer.entries ) - tail = 0; - head = get_field_from_reg_u32( - readl(iommu->mmio_base+IOMMU_CMD_BUFFER_HEAD_OFFSET), - IOMMU_CMD_BUFFER_HEAD_MASK, - IOMMU_CMD_BUFFER_HEAD_SHIFT); - if ( head != tail ) - { - cmd_buffer = (u32 *)(iommu->cmd_buffer.buffer + - (iommu->cmd_buffer_tail * - IOMMU_CMD_BUFFER_ENTRY_SIZE)); - for ( i = 0; i < IOMMU_CMD_BUFFER_U32_PER_ENTRY; i++ ) - cmd_buffer[i] = cmd[i]; - - iommu->cmd_buffer_tail = tail; - return 1; - } - - return 0; -} - -static void commit_iommu_command_buffer(struct amd_iommu *iommu) -{ - u32 tail; - - set_field_in_reg_u32(iommu->cmd_buffer_tail, 0, - IOMMU_CMD_BUFFER_TAIL_MASK, - IOMMU_CMD_BUFFER_TAIL_SHIFT, &tail); - writel(tail, iommu->mmio_base+IOMMU_CMD_BUFFER_TAIL_OFFSET); -} - -int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]) -{ - if ( queue_iommu_command(iommu, cmd) ) - { - commit_iommu_command_buffer(iommu); - return 1; - } - - return 0; -} - -static void invalidate_iommu_page(struct amd_iommu *iommu, - u64 io_addr, u16 domain_id) -{ - u64 addr_lo, addr_hi; - u32 cmd[4], entry; - - addr_lo = io_addr & DMA_32BIT_MASK; - addr_hi = io_addr >> 32; - - set_field_in_reg_u32(domain_id, 0, - IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_MASK, - IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CMD_INVALIDATE_IOMMU_PAGES, entry, - IOMMU_CMD_OPCODE_MASK, IOMMU_CMD_OPCODE_SHIFT, - &entry); - cmd[1] = entry; - - set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, 0, - IOMMU_INV_IOMMU_PAGES_S_FLAG_MASK, - IOMMU_INV_IOMMU_PAGES_S_FLAG_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, entry, - IOMMU_INV_IOMMU_PAGES_PDE_FLAG_MASK, - IOMMU_INV_IOMMU_PAGES_PDE_FLAG_SHIFT, &entry); - set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, entry, - IOMMU_INV_IOMMU_PAGES_ADDR_LOW_MASK, - IOMMU_INV_IOMMU_PAGES_ADDR_LOW_SHIFT, &entry); - cmd[2] = entry; - - set_field_in_reg_u32((u32)addr_hi, 0, - IOMMU_INV_IOMMU_PAGES_ADDR_HIGH_MASK, - IOMMU_INV_IOMMU_PAGES_ADDR_HIGH_SHIFT, &entry); - cmd[3] = entry; - - cmd[0] = 0; - send_iommu_command(iommu, cmd); -} - -void flush_command_buffer(struct amd_iommu *iommu) -{ - u32 cmd[4], status; - int loop_count, comp_wait; - - /* clear 'ComWaitInt' in status register (WIC) */ - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, 0, - IOMMU_STATUS_COMP_WAIT_INT_MASK, - IOMMU_STATUS_COMP_WAIT_INT_SHIFT, &status); - writel(status, iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); - - /* send an empty COMPLETION_WAIT command to flush command buffer */ - cmd[3] = cmd[2] = 0; - set_field_in_reg_u32(IOMMU_CMD_COMPLETION_WAIT, 0, - IOMMU_CMD_OPCODE_MASK, - IOMMU_CMD_OPCODE_SHIFT, &cmd[1]); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, 0, - IOMMU_COMP_WAIT_I_FLAG_MASK, - IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]); - send_iommu_command(iommu, cmd); - - /* wait for 'ComWaitInt' to signal comp#endifletion? */ - if ( amd_iommu_poll_comp_wait ) { - loop_count = amd_iommu_poll_comp_wait; - do { - status = readl(iommu->mmio_base + - IOMMU_STATUS_MMIO_OFFSET); - comp_wait = get_field_from_reg_u32( - status, - IOMMU_STATUS_COMP_WAIT_INT_MASK, - IOMMU_STATUS_COMP_WAIT_INT_SHIFT); - --loop_count; - } while ( loop_count && !comp_wait ); - - if ( comp_wait ) - { - /* clear 'ComWaitInt' in status register (WIC) */ - status &= IOMMU_STATUS_COMP_WAIT_INT_MASK; - writel(status, iommu->mmio_base + - IOMMU_STATUS_MMIO_OFFSET); - } - else - dprintk(XENLOG_WARNING, "AMD IOMMU: Warning:" - " ComWaitInt bit did not assert!\n"); - } -} - -static void clear_page_table_entry_present(u32 *pte) -{ - set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, pte[0], - IOMMU_PTE_PRESENT_MASK, - IOMMU_PTE_PRESENT_SHIFT, &pte[0]); -} - -static void set_page_table_entry_present(u32 *pte, u64 page_addr, - int iw, int ir) -{ - u64 addr_lo, addr_hi; - u32 entry; - - addr_lo = page_addr & DMA_32BIT_MASK; - addr_hi = page_addr >> 32; - - set_field_in_reg_u32((u32)addr_hi, 0, - IOMMU_PTE_ADDR_HIGH_MASK, - IOMMU_PTE_ADDR_HIGH_SHIFT, &entry); - set_field_in_reg_u32(iw ? IOMMU_CONTROL_ENABLED : - IOMMU_CONTROL_DISABLED, entry, - IOMMU_PTE_IO_WRITE_PERMISSION_MASK, - IOMMU_PTE_IO_WRITE_PERMISSION_SHIFT, &entry); - set_field_in_reg_u32(ir ? IOMMU_CONTROL_ENABLED : - IOMMU_CONTROL_DISABLED, entry, - IOMMU_PTE_IO_READ_PERMISSION_MASK, - IOMMU_PTE_IO_READ_PERMISSION_SHIFT, &entry); - pte[1] = entry; - - set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, - IOMMU_PTE_ADDR_LOW_MASK, - IOMMU_PTE_ADDR_LOW_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_PAGING_MODE_LEVEL_0, entry, - IOMMU_PTE_NEXT_LEVEL_MASK, - IOMMU_PTE_NEXT_LEVEL_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_PTE_PRESENT_MASK, - IOMMU_PTE_PRESENT_SHIFT, &entry); - pte[0] = entry; -} - - -static void amd_iommu_set_page_directory_entry(u32 *pde, - u64 next_ptr, u8 next_level) -{ - u64 addr_lo, addr_hi; - u32 entry; - - addr_lo = next_ptr & DMA_32BIT_MASK; - addr_hi = next_ptr >> 32; - - /* enable read/write permissions,which will be enforced at the PTE */ - set_field_in_reg_u32((u32)addr_hi, 0, - IOMMU_PDE_ADDR_HIGH_MASK, - IOMMU_PDE_ADDR_HIGH_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_PDE_IO_WRITE_PERMISSION_MASK, - IOMMU_PDE_IO_WRITE_PERMISSION_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_PDE_IO_READ_PERMISSION_MASK, - IOMMU_PDE_IO_READ_PERMISSION_SHIFT, &entry); - pde[1] = entry; - - /* mark next level as 'present' */ - set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, - IOMMU_PDE_ADDR_LOW_MASK, - IOMMU_PDE_ADDR_LOW_SHIFT, &entry); - set_field_in_reg_u32(next_level, entry, - IOMMU_PDE_NEXT_LEVEL_MASK, - IOMMU_PDE_NEXT_LEVEL_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_PDE_PRESENT_MASK, - IOMMU_PDE_PRESENT_SHIFT, &entry); - pde[0] = entry; -} - -void amd_iommu_set_dev_table_entry(u32 *dte, u64 root_ptr, u16 domain_id, - u8 paging_mode) -{ - u64 addr_hi, addr_lo; - u32 entry; - - dte[6] = dte[5] = dte[4] = 0; - - set_field_in_reg_u32(IOMMU_DEV_TABLE_SYS_MGT_MSG_FORWARDED, 0, - IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_MASK, - IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_SHIFT, &entry); - dte[3] = entry; - - set_field_in_reg_u32(domain_id, 0, - IOMMU_DEV_TABLE_DOMAIN_ID_MASK, - IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT, &entry); - dte[2] = entry; - - addr_lo = root_ptr & DMA_32BIT_MASK; - addr_hi = root_ptr >> 32; - set_field_in_reg_u32((u32)addr_hi, 0, - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_MASK, - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_DEV_TABLE_IO_WRITE_PERMISSION_MASK, - IOMMU_DEV_TABLE_IO_WRITE_PERMISSION_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_DEV_TABLE_IO_READ_PERMISSION_MASK, - IOMMU_DEV_TABLE_IO_READ_PERMISSION_SHIFT, &entry); - dte[1] = entry; - - set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_MASK, - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT, &entry); - set_field_in_reg_u32(paging_mode, entry, - IOMMU_DEV_TABLE_PAGING_MODE_MASK, - IOMMU_DEV_TABLE_PAGING_MODE_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_DEV_TABLE_TRANSLATION_VALID_MASK, - IOMMU_DEV_TABLE_TRANSLATION_VALID_SHIFT, &entry); - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, - IOMMU_DEV_TABLE_VALID_MASK, - IOMMU_DEV_TABLE_VALID_SHIFT, &entry); - dte[0] = entry; -} - -void *amd_iommu_get_vptr_from_page_table_entry(u32 *entry) -{ - u64 addr_lo, addr_hi, ptr; - - addr_lo = get_field_from_reg_u32( - entry[0], - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_MASK, - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT); - - addr_hi = get_field_from_reg_u32( - entry[1], - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_MASK, - IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_SHIFT); - - ptr = (addr_hi << 32) | (addr_lo << PAGE_SHIFT); - return ptr ? maddr_to_virt((unsigned long)ptr) : NULL; -} - -static int amd_iommu_is_pte_present(u32 *entry) -{ - return (get_field_from_reg_u32(entry[0], - IOMMU_PDE_PRESENT_MASK, - IOMMU_PDE_PRESENT_SHIFT)); -} - -void invalidate_dev_table_entry(struct amd_iommu *iommu, - u16 device_id) -{ - u32 cmd[4], entry; - - cmd[3] = cmd[2] = 0; - set_field_in_reg_u32(device_id, 0, - IOMMU_INV_DEVTAB_ENTRY_DEVICE_ID_MASK, - IOMMU_INV_DEVTAB_ENTRY_DEVICE_ID_SHIFT, &entry); - cmd[0] = entry; - - set_field_in_reg_u32(IOMMU_CMD_INVALIDATE_DEVTAB_ENTRY, 0, - IOMMU_CMD_OPCODE_MASK, IOMMU_CMD_OPCODE_SHIFT, - &entry); - cmd[1] = entry; - - send_iommu_command(iommu, cmd); -} - -int amd_iommu_is_dte_page_translation_valid(u32 *entry) -{ - return (get_field_from_reg_u32(entry[0], - IOMMU_DEV_TABLE_VALID_MASK, - IOMMU_DEV_TABLE_VALID_SHIFT) && - get_field_from_reg_u32(entry[0], - IOMMU_DEV_TABLE_TRANSLATION_VALID_MASK, - IOMMU_DEV_TABLE_TRANSLATION_VALID_SHIFT)); -} - -static void *get_pte_from_page_tables(void *table, int level, - unsigned long io_pfn) -{ - unsigned long offset; - void *pde = NULL; - - BUG_ON(table == NULL); - - while ( level > 0 ) - { - offset = io_pfn >> ((PTE_PER_TABLE_SHIFT * - (level - IOMMU_PAGING_MODE_LEVEL_1))); - offset &= ~PTE_PER_TABLE_MASK; - pde = table + (offset * IOMMU_PAGE_TABLE_ENTRY_SIZE); - - if ( level == 1 ) - break; - if ( !pde ) - return NULL; - if ( !amd_iommu_is_pte_present(pde) ) - { - void *next_table = alloc_xenheap_page(); - if ( next_table == NULL ) - return NULL; - memset(next_table, 0, PAGE_SIZE); - if ( *(u64 *)pde == 0 ) - { - unsigned long next_ptr = (u64)virt_to_maddr(next_table); - amd_iommu_set_page_directory_entry( - (u32 *)pde, next_ptr, level - 1); - } - else - { - free_xenheap_page(next_table); - } - } - table = amd_iommu_get_vptr_from_page_table_entry(pde); - level--; - } - - return pde; -} - -int amd_iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn) -{ - void *pte; - unsigned long flags; - u64 maddr; - struct hvm_iommu *hd = domain_hvm_iommu(d); - int iw, ir; - - BUG_ON( !hd->root_table ); - - maddr = (u64)mfn << PAGE_SHIFT; - - iw = IOMMU_IO_WRITE_ENABLED; - ir = IOMMU_IO_READ_ENABLED; - - spin_lock_irqsave(&hd->mapping_lock, flags); - - pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn); - if ( pte == 0 ) - { - dprintk(XENLOG_ERR, - "AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", gfn); - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return -EIO; - } - - set_page_table_entry_present((u32 *)pte, maddr, iw, ir); - - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return 0; -} - -int amd_iommu_unmap_page(struct domain *d, unsigned long gfn) -{ - void *pte; - unsigned long flags; - u64 io_addr = gfn; - int requestor_id; - struct amd_iommu *iommu; - struct hvm_iommu *hd = domain_hvm_iommu(d); - - BUG_ON( !hd->root_table ); - - requestor_id = hd->domain_id; - io_addr = (u64)gfn << PAGE_SHIFT; - - spin_lock_irqsave(&hd->mapping_lock, flags); - - pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn); - if ( pte == 0 ) - { - dprintk(XENLOG_ERR, - "AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", gfn); - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return -EIO; - } - - /* mark PTE as 'page not present' */ - clear_page_table_entry_present((u32 *)pte); - spin_unlock_irqrestore(&hd->mapping_lock, flags); - - /* send INVALIDATE_IOMMU_PAGES command */ - for_each_amd_iommu(iommu) - { - spin_lock_irqsave(&iommu->lock, flags); - invalidate_iommu_page(iommu, io_addr, requestor_id); - flush_command_buffer(iommu); - spin_unlock_irqrestore(&iommu->lock, flags); - } - - return 0; -} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c --- a/xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,578 +0,0 @@ -/* - * Copyright (C) 2007 Advanced Micro Devices, Inc. - * Author: Leo Duran <leo.duran@xxxxxxx> - * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <asm/amd-iommu.h> -#include <asm/hvm/svm/amd-iommu-proto.h> -#include <xen/sched.h> -#include <asm/mm.h> -#include "pci-direct.h" -#include "pci_regs.h" - -struct list_head amd_iommu_head; -long amd_iommu_poll_comp_wait = COMPLETION_WAIT_DEFAULT_POLLING_COUNT; -static long amd_iommu_cmd_buffer_entries = IOMMU_CMD_BUFFER_DEFAULT_ENTRIES; -int nr_amd_iommus = 0; - -/* will set if amd-iommu HW is found */ -int amd_iommu_enabled = 0; - -static int enable_amd_iommu = 0; -boolean_param("enable_amd_iommu", enable_amd_iommu); - -static void deallocate_domain_page_tables(struct hvm_iommu *hd) -{ - if ( hd->root_table ) - free_xenheap_page(hd->root_table); -} - -static void deallocate_domain_resources(struct hvm_iommu *hd) -{ - deallocate_domain_page_tables(hd); -} - -static void __init init_cleanup(void) -{ - struct amd_iommu *iommu; - - for_each_amd_iommu ( iommu ) - unmap_iommu_mmio_region(iommu); -} - -static void __init deallocate_iommu_table_struct( - struct table_struct *table) -{ - if ( table->buffer ) - { - free_xenheap_pages(table->buffer, - get_order_from_bytes(table->alloc_size)); - table->buffer = NULL; - } -} - -static void __init deallocate_iommu_resources(struct amd_iommu *iommu) -{ - deallocate_iommu_table_struct(&iommu->dev_table); - deallocate_iommu_table_struct(&iommu->cmd_buffer);; -} - -static void __init detect_cleanup(void) -{ - struct amd_iommu *iommu, *next; - - list_for_each_entry_safe ( iommu, next, &amd_iommu_head, list ) - { - list_del(&iommu->list); - deallocate_iommu_resources(iommu); - xfree(iommu); - } -} - -static int requestor_id_from_bdf(int bdf) -{ - /* HACK - HACK */ - /* account for possible 'aliasing' by parent device */ - return bdf; -} - -static int __init allocate_iommu_table_struct(struct table_struct *table, - const char *name) -{ - table->buffer = (void *) alloc_xenheap_pages( - get_order_from_bytes(table->alloc_size)); - - if ( !table->buffer ) - { - dprintk(XENLOG_ERR, "AMD IOMMU: Error allocating %s\n", name); - return -ENOMEM; - } - - memset(table->buffer, 0, table->alloc_size); - - return 0; -} - -static int __init allocate_iommu_resources(struct amd_iommu *iommu) -{ - /* allocate 'device table' on a 4K boundary */ - iommu->dev_table.alloc_size = - PAGE_ALIGN(((iommu->last_downstream_bus + 1) * - IOMMU_DEV_TABLE_ENTRIES_PER_BUS) * - IOMMU_DEV_TABLE_ENTRY_SIZE); - iommu->dev_table.entries = - iommu->dev_table.alloc_size / IOMMU_DEV_TABLE_ENTRY_SIZE; - - if ( allocate_iommu_table_struct(&iommu->dev_table, - "Device Table") != 0 ) - goto error_out; - - /* allocate 'command buffer' in power of 2 increments of 4K */ - iommu->cmd_buffer_tail = 0; - iommu->cmd_buffer.alloc_size = - PAGE_SIZE << get_order_from_bytes( - PAGE_ALIGN(amd_iommu_cmd_buffer_entries * - IOMMU_CMD_BUFFER_ENTRY_SIZE)); - - iommu->cmd_buffer.entries = - iommu->cmd_buffer.alloc_size / IOMMU_CMD_BUFFER_ENTRY_SIZE; - - if ( allocate_iommu_table_struct(&iommu->cmd_buffer, - "Command Buffer") != 0 ) - goto error_out; - - return 0; - - error_out: - deallocate_iommu_resources(iommu); - return -ENOMEM; -} - -int iommu_detect_callback(u8 bus, u8 dev, u8 func, u8 cap_ptr) -{ - struct amd_iommu *iommu; - - iommu = (struct amd_iommu *) xmalloc(struct amd_iommu); - if ( !iommu ) - { - dprintk(XENLOG_ERR, "AMD IOMMU: Error allocating amd_iommu\n"); - return -ENOMEM; - } - memset(iommu, 0, sizeof(struct amd_iommu)); - spin_lock_init(&iommu->lock); - - /* get capability and topology information */ - if ( get_iommu_capabilities(bus, dev, func, cap_ptr, iommu) != 0 ) - goto error_out; - if ( get_iommu_last_downstream_bus(iommu) != 0 ) - goto error_out; - - list_add_tail(&iommu->list, &amd_iommu_head); - - /* allocate resources for this IOMMU */ - if (allocate_iommu_resources(iommu) != 0) - goto error_out; - - return 0; - - error_out: - xfree(iommu); - return -ENODEV; -} - -static int __init amd_iommu_init(void) -{ - struct amd_iommu *iommu; - unsigned long flags; - - for_each_amd_iommu ( iommu ) - { - spin_lock_irqsave(&iommu->lock, flags); - - /* register IOMMU data strucures in MMIO space */ - if ( map_iommu_mmio_region(iommu) != 0 ) - goto error_out; - register_iommu_dev_table_in_mmio_space(iommu); - register_iommu_cmd_buffer_in_mmio_space(iommu); - - /* enable IOMMU translation services */ - enable_iommu(iommu); - nr_amd_iommus++; - - spin_unlock_irqrestore(&iommu->lock, flags); - } - - amd_iommu_enabled = 1; - - return 0; - - error_out: - init_cleanup(); - return -ENODEV; -} - -struct amd_iommu *find_iommu_for_device(int bus, int devfn) -{ - struct amd_iommu *iommu; - - for_each_amd_iommu ( iommu ) - { - if ( bus == iommu->root_bus ) - { - if ( (devfn >= iommu->first_devfn) && - (devfn <= iommu->last_devfn) ) - return iommu; - } - else if ( bus <= iommu->last_downstream_bus ) - { - if ( iommu->downstream_bus_present[bus] ) - return iommu; - } - } - - return NULL; -} - -void amd_iommu_setup_domain_device( - struct domain *domain, struct amd_iommu *iommu, int requestor_id) -{ - void *dte; - u64 root_ptr; - unsigned long flags; - struct hvm_iommu *hd = domain_hvm_iommu(domain); - - BUG_ON( !hd->root_table||!hd->paging_mode ); - - root_ptr = (u64)virt_to_maddr(hd->root_table); - dte = iommu->dev_table.buffer + - (requestor_id * IOMMU_DEV_TABLE_ENTRY_SIZE); - - if ( !amd_iommu_is_dte_page_translation_valid((u32 *)dte) ) - { - spin_lock_irqsave(&iommu->lock, flags); - - amd_iommu_set_dev_table_entry( - (u32 *)dte, - root_ptr, hd->domain_id, hd->paging_mode); - invalidate_dev_table_entry(iommu, requestor_id); - flush_command_buffer(iommu); - dprintk(XENLOG_INFO, "AMD IOMMU: Set DTE req_id:%x, " - "root_ptr:%"PRIx64", domain_id:%d, paging_mode:%d\n", - requestor_id, root_ptr, hd->domain_id, hd->paging_mode); - - spin_unlock_irqrestore(&iommu->lock, flags); - } -} - -void __init amd_iommu_setup_dom0_devices(void) -{ - struct hvm_iommu *hd = domain_hvm_iommu(dom0); - struct amd_iommu *iommu; - struct pci_dev *pdev; - int bus, dev, func; - u32 l; - int req_id, bdf; - - for ( bus = 0; bus < 256; bus++ ) - { - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - l = read_pci_config(bus, dev, func, PCI_VENDOR_ID); - /* some broken boards return 0 or ~0 if a slot is empty: */ - if ( l == 0xffffffff || l == 0x00000000 || - l == 0x0000ffff || l == 0xffff0000 ) - continue; - - pdev = xmalloc(struct pci_dev); - pdev->bus = bus; - pdev->devfn = PCI_DEVFN(dev, func); - list_add_tail(&pdev->list, &hd->pdev_list); - - bdf = (bus << 8) | pdev->devfn; - req_id = requestor_id_from_bdf(bdf); - iommu = find_iommu_for_device(bus, pdev->devfn); - - if ( iommu ) - amd_iommu_setup_domain_device(dom0, iommu, req_id); - } - } - } -} - -int amd_iommu_detect(void) -{ - unsigned long i; - - if ( !enable_amd_iommu ) - { - printk("AMD IOMMU: Disabled\n"); - return 0; - } - - INIT_LIST_HEAD(&amd_iommu_head); - - if ( scan_for_iommu(iommu_detect_callback) != 0 ) - { - dprintk(XENLOG_ERR, "AMD IOMMU: Error detection\n"); - goto error_out; - } - - if ( !iommu_found() ) - { - printk("AMD IOMMU: Not found!\n"); - return 0; - } - - if ( amd_iommu_init() != 0 ) - { - dprintk(XENLOG_ERR, "AMD IOMMU: Error initialization\n"); - goto error_out; - } - - if ( iommu_domain_init(dom0) != 0 ) - goto error_out; - - /* setup 1:1 page table for dom0 */ - for ( i = 0; i < max_page; i++ ) - amd_iommu_map_page(dom0, i, i); - - amd_iommu_setup_dom0_devices(); - return 0; - - error_out: - detect_cleanup(); - return -ENODEV; - -} - -static int allocate_domain_resources(struct hvm_iommu *hd) -{ - /* allocate root table */ - unsigned long flags; - - spin_lock_irqsave(&hd->mapping_lock, flags); - if ( !hd->root_table ) - { - hd->root_table = (void *)alloc_xenheap_page(); - if ( !hd->root_table ) - goto error_out; - memset((u8*)hd->root_table, 0, PAGE_SIZE); - } - spin_unlock_irqrestore(&hd->mapping_lock, flags); - - return 0; - error_out: - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return -ENOMEM; -} - -static int get_paging_mode(unsigned long entries) -{ - int level = 1; - - BUG_ON ( !max_page ); - - if ( entries > max_page ) - entries = max_page; - - while ( entries > PTE_PER_TABLE_SIZE ) - { - entries = PTE_PER_TABLE_ALIGN(entries) >> PTE_PER_TABLE_SHIFT; - ++level; - if ( level > 6 ) - return -ENOMEM; - } - - dprintk(XENLOG_INFO, "AMD IOMMU: paging mode = %d\n", level); - - return level; -} - -int amd_iommu_domain_init(struct domain *domain) -{ - struct hvm_iommu *hd = domain_hvm_iommu(domain); - - /* allocate page directroy */ - if ( allocate_domain_resources(hd) != 0 ) - { - deallocate_domain_resources(hd); - return -ENOMEM; - } - - if ( is_hvm_domain(domain) ) - hd->paging_mode = IOMMU_PAGE_TABLE_LEVEL_4; - else - hd->paging_mode = get_paging_mode(max_page); - - hd->domain_id = domain->domain_id; - - return 0; -} - -static void amd_iommu_disable_domain_device( - struct domain *domain, struct amd_iommu *iommu, u16 requestor_id) -{ - void *dte; - unsigned long flags; - - dte = iommu->dev_table.buffer + - (requestor_id * IOMMU_DEV_TABLE_ENTRY_SIZE); - - if ( amd_iommu_is_dte_page_translation_valid((u32 *)dte) ) - { - spin_lock_irqsave(&iommu->lock, flags); - memset (dte, 0, IOMMU_DEV_TABLE_ENTRY_SIZE); - invalidate_dev_table_entry(iommu, requestor_id); - flush_command_buffer(iommu); - dprintk(XENLOG_INFO , "AMD IOMMU: disable DTE 0x%x," - " domain_id:%d, paging_mode:%d\n", - requestor_id, domain_hvm_iommu(domain)->domain_id, - domain_hvm_iommu(domain)->paging_mode); - spin_unlock_irqrestore(&iommu->lock, flags); - } -} - -extern void pdev_flr(u8 bus, u8 devfn); - -static int reassign_device( struct domain *source, struct domain *target, - u8 bus, u8 devfn) -{ - struct hvm_iommu *source_hd = domain_hvm_iommu(source); - struct hvm_iommu *target_hd = domain_hvm_iommu(target); - struct pci_dev *pdev; - struct amd_iommu *iommu; - int req_id, bdf; - unsigned long flags; - - for_each_pdev( source, pdev ) - { - if ( (pdev->bus != bus) || (pdev->devfn != devfn) ) - continue; - - pdev->bus = bus; - pdev->devfn = devfn; - - bdf = (bus << 8) | devfn; - req_id = requestor_id_from_bdf(bdf); - iommu = find_iommu_for_device(bus, devfn); - - if ( iommu ) - { - amd_iommu_disable_domain_device(source, iommu, req_id); - /* Move pci device from the source domain to target domain. */ - spin_lock_irqsave(&source_hd->iommu_list_lock, flags); - spin_lock_irqsave(&target_hd->iommu_list_lock, flags); - list_move(&pdev->list, &target_hd->pdev_list); - spin_unlock_irqrestore(&target_hd->iommu_list_lock, flags); - spin_unlock_irqrestore(&source_hd->iommu_list_lock, flags); - - amd_iommu_setup_domain_device(target, iommu, req_id); - gdprintk(XENLOG_INFO , - "AMD IOMMU: reassign %x:%x.%x domain %d -> domain %d\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - source->domain_id, target->domain_id); - } - else - { - gdprintk(XENLOG_ERR , "AMD IOMMU: fail to find iommu." - " %x:%x.%x cannot be assigned to domain %d\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn), target->domain_id); - return -ENODEV; - } - - break; - } - return 0; -} - -int amd_iommu_assign_device(struct domain *d, u8 bus, u8 devfn) -{ - pdev_flr(bus, devfn); - return reassign_device(dom0, d, bus, devfn); -} - -static void release_domain_devices(struct domain *d) -{ - struct hvm_iommu *hd = domain_hvm_iommu(d); - struct pci_dev *pdev; - - while ( !list_empty(&hd->pdev_list) ) - { - pdev = list_entry(hd->pdev_list.next, typeof(*pdev), list); - pdev_flr(pdev->bus, pdev->devfn); - gdprintk(XENLOG_INFO , - "AMD IOMMU: release devices %x:%x.%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - reassign_device(d, dom0, pdev->bus, pdev->devfn); - } -} - -static void deallocate_next_page_table(void *table, unsigned long index, - int level) -{ - unsigned long next_index; - void *next_table, *pde; - int next_level; - - pde = table + (index * IOMMU_PAGE_TABLE_ENTRY_SIZE); - next_table = amd_iommu_get_vptr_from_page_table_entry((u32 *)pde); - - if ( next_table ) - { - next_level = level - 1; - if ( next_level > 1 ) - { - next_index = 0; - do - { - deallocate_next_page_table(next_table, - next_index, next_level); - ++next_index; - } while (next_index < PTE_PER_TABLE_SIZE); - } - - free_xenheap_page(next_table); - } -} - -static void deallocate_iommu_page_tables(struct domain *d) -{ - unsigned long index; - struct hvm_iommu *hd = domain_hvm_iommu(d); - - if ( hd ->root_table ) - { - index = 0; - do - { - deallocate_next_page_table(hd->root_table, - index, hd->paging_mode); - ++index; - } while ( index < PTE_PER_TABLE_SIZE ); - - free_xenheap_page(hd ->root_table); - } - - hd ->root_table = NULL; -} - -void amd_iommu_domain_destroy(struct domain *d) -{ - if ( !amd_iommu_enabled ) - return; - - deallocate_iommu_page_tables(d); - release_domain_devices(d); -} - -void amd_iommu_return_device(struct domain *s, struct domain *t, u8 bus, u8 devfn) -{ - pdev_flr(bus, devfn); - reassign_device(s, t, bus, devfn); -} - -struct iommu_ops amd_iommu_ops = { - .init = amd_iommu_domain_init, - .assign_device = amd_iommu_assign_device, - .teardown = amd_iommu_domain_destroy, - .map_page = amd_iommu_map_page, - .unmap_page = amd_iommu_unmap_page, - .reassign_device = amd_iommu_return_device, -}; diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h --- a/xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -#ifndef ASM_PCI_DIRECT_H -#define ASM_PCI_DIRECT_H 1 - -#include <xen/types.h> -#include <asm/io.h> - -/* Direct PCI access. This is used for PCI accesses in early boot before - the PCI subsystem works. */ - -#define PDprintk(x...) - -static inline u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset) -{ - u32 v; - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - v = inl(0xcfc); - if (v != 0xffffffff) - PDprintk("%x reading 4 from %x: %x\n", slot, offset, v); - return v; -} - -static inline u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset) -{ - u8 v; - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - v = inb(0xcfc + (offset&3)); - PDprintk("%x reading 1 from %x: %x\n", slot, offset, v); - return v; -} - -static inline u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset) -{ - u16 v; - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - v = inw(0xcfc + (offset&2)); - PDprintk("%x reading 2 from %x: %x\n", slot, offset, v); - return v; -} - -static inline void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset, - u32 val) -{ - PDprintk("%x writing to %x: %x\n", slot, offset, val); - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - outl(val, 0xcfc); -} - -#endif diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h --- a/xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,513 +0,0 @@ -/* - * pci_regs.h - * - * PCI standard defines - * Copyright 1994, Drew Eckhardt - * Copyright 1997--1999 Martin Mares <mj@xxxxxx> - * - * For more information, please consult the following manuals (look at - * http://www.pcisig.com/ for how to get them): - * - * PCI BIOS Specification - * PCI Local Bus Specification - * PCI to PCI Bridge Specification - * PCI System Design Guide - * - * For hypertransport information, please consult the following manuals - * from http://www.hypertransport.org - * - * The Hypertransport I/O Link Specification - */ - -#ifndef LINUX_PCI_REGS_H -#define LINUX_PCI_REGS_H - -/* - * Under PCI, each device has 256 bytes of configuration address space, - * of which the first 64 bytes are standardized as follows: - */ -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ -#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ -#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ -#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ -#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ -#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ -#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ -#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ - -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ -#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ -#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ -#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ -#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ -#define PCI_STATUS_DEVSEL_FAST 0x000 -#define PCI_STATUS_DEVSEL_MEDIUM 0x200 -#define PCI_STATUS_DEVSEL_SLOW 0x400 -#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ -#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ -#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ -#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ -#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ - -#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ -#define PCI_REVISION_ID 0x08 /* Revision ID */ -#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ -#define PCI_CLASS_DEVICE 0x0a /* Device class */ - -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 - -#define PCI_BIST 0x0f /* 8 bits */ -#define PCI_BIST_CODE_MASK 0x0f /* Return result */ -#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ -#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ - -/* - * Base addresses specify locations in memory or I/O space. - * Decoded size can be determined by writing a value of - * 0xffffffff to the register, and reading it back. Only - * 1 bits are decoded. - */ -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ -#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ -#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ -#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ -#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) -#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) -/* bit 1 is reserved if address_space = 1 */ - -/* Header type 0 (normal devices) */ -#define PCI_CARDBUS_CIS 0x28 -#define PCI_SUBSYSTEM_VENDOR_ID 0x2c -#define PCI_SUBSYSTEM_ID 0x2e -#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 -#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) - -#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ - -/* 0x35-0x3b are reserved */ -#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ -#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ -#define PCI_MIN_GNT 0x3e /* 8 bits */ -#define PCI_MAX_LAT 0x3f /* 8 bits */ - -/* Header type 1 (PCI-to-PCI bridges) */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ -#define PCI_IO_RANGE_TYPE_16 0x00 -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL -#define PCI_MEMORY_RANGE_MASK (~0x0fUL) -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL -#define PCI_PREF_RANGE_TYPE_32 0x00 -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_RANGE_MASK (~0x0fUL) -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -/* 0x34 same as for htype 0 */ -/* 0x35-0x3b is reserved */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ -#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ -#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ -#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ -#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ -#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ -#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ - -/* Header type 2 (CardBus bridges) */ -#define PCI_CB_CAPABILITY_LIST 0x14 -/* 0x15 reserved */ -#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ -#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ -#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ -#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ -#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ -#define PCI_CB_MEMORY_BASE_0 0x1c -#define PCI_CB_MEMORY_LIMIT_0 0x20 -#define PCI_CB_MEMORY_BASE_1 0x24 -#define PCI_CB_MEMORY_LIMIT_1 0x28 -#define PCI_CB_IO_BASE_0 0x2c -#define PCI_CB_IO_BASE_0_HI 0x2e -#define PCI_CB_IO_LIMIT_0 0x30 -#define PCI_CB_IO_LIMIT_0_HI 0x32 -#define PCI_CB_IO_BASE_1 0x34 -#define PCI_CB_IO_BASE_1_HI 0x36 -#define PCI_CB_IO_LIMIT_1 0x38 -#define PCI_CB_IO_LIMIT_1_HI 0x3a -#define PCI_CB_IO_RANGE_MASK (~0x03UL) -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_CB_BRIDGE_CONTROL 0x3e -#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ -#define PCI_CB_BRIDGE_CTL_SERR 0x02 -#define PCI_CB_BRIDGE_CTL_ISA 0x04 -#define PCI_CB_BRIDGE_CTL_VGA 0x08 -#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 -#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ -#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 -#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 -#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 -#define PCI_CB_SUBSYSTEM_ID 0x42 -#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ -/* 0x48-0x7f reserved */ - -/* Capability lists */ - -#define PCI_CAP_LIST_ID 0 /* Capability ID */ -#define PCI_CAP_ID_PM 0x01 /* Power Management */ -#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ -#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ -#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ -#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ -#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ -#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ -#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ -#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific capability */ -#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ -#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ -#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ -#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ -#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ -#define PCI_CAP_SIZEOF 4 - -/* Power Management Registers */ - -#define PCI_PM_PMC 2 /* PM Capabilities Register */ -#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ -#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ -#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ -#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ -#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ -#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ -#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ -#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ -#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ -#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ -#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ -#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ -#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ -#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ -#define PCI_PM_CTRL 4 /* PM control and status register */ -#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ -#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ -#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ -#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ -#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ -#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ -#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ -#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ -#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ -#define PCI_PM_DATA_REGISTER 7 /* (??) */ -#define PCI_PM_SIZEOF 8 - -/* AGP registers */ - -#define PCI_AGP_VERSION 2 /* BCD version number */ -#define PCI_AGP_RFU 3 /* Rest of capability flags */ -#define PCI_AGP_STATUS 4 /* Status register */ -#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ -#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ -#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ -#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ -#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ -#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ -#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ -#define PCI_AGP_COMMAND 8 /* Control register */ -#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ -#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ -#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ -#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ -#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ -#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ -#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ -#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ -#define PCI_AGP_SIZEOF 12 - -/* Vital Product Data */ - -#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ -#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ -#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ -#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ - -/* Slot Identification */ - -#define PCI_SID_ESR 2 /* Expansion Slot Register */ -#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ -#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ -#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ - -/* Message Signalled Interrupts registers */ - -#define PCI_MSI_FLAGS 2 /* Various flags */ -#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ -#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ -#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ -#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ -#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ -#define PCI_MSI_RFU 3 /* Rest of capability flags */ -#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ -#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ -#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ -#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ -#define PCI_MSI_MASK_BIT 16 /* Mask bits register */ - -/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ -#define PCI_MSIX_FLAGS 2 -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_MASKALL (1 << 14) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) -#define PCI_MSIX_FLAGS_BITMASK (1 << 0) - -/* CompactPCI Hotswap Register */ - -#define PCI_CHSWP_CSR 2 /* Control and Status Register */ -#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ -#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ -#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ -#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ -#define PCI_CHSWP_PI 0x30 /* Programming Interface */ -#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ -#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ - -/* PCI-X registers */ - -#define PCI_X_CMD 2 /* Modes & Features */ -#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ -#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ -#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ -#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ -#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ -#define PCI_X_STATUS 4 /* PCI-X capabilities */ -#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ -#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ -#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ -#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ -#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ -#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ -#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ -#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ -#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ -#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ -#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ -#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ -#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ - -/* PCI Express capability registers */ - -#define PCI_EXP_FLAGS 2 /* Capabilities register */ -#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ -#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ -#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ -#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ -#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ -#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ -#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ -#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ -#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ -#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ -#define PCI_EXP_DEVCAP 4 /* Device capabilities */ -#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ -#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ -#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ -#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ -#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ -#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ -#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ -#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ -#define PCI_EXP_DEVCTL 8 /* Device Control */ -#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ -#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ -#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ -#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ -#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ -#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ -#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ -#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ -#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ -#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ -#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ -#define PCI_EXP_DEVSTA 10 /* Device Status */ -#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ -#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ -#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ -#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ -#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ -#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ -#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ -#define PCI_EXP_LNKCTL 16 /* Link Control */ -#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ -#define PCI_EXP_LNKSTA 18 /* Link Status */ -#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ -#define PCI_EXP_SLTCTL 24 /* Slot Control */ -#define PCI_EXP_SLTSTA 26 /* Slot Status */ -#define PCI_EXP_RTCTL 28 /* Root Control */ -#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ -#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ -#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ -#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ -#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ -#define PCI_EXP_RTCAP 30 /* Root Capabilities */ -#define PCI_EXP_RTSTA 32 /* Root Status */ - -/* Extended Capabilities (PCI-X 2.0 and Express) */ -#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) -#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) -#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) - -#define PCI_EXT_CAP_ID_ERR 1 -#define PCI_EXT_CAP_ID_VC 2 -#define PCI_EXT_CAP_ID_DSN 3 -#define PCI_EXT_CAP_ID_PWR 4 - -/* Advanced Error Reporting */ -#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ -#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ -#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ -#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ -#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ -#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ -#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ -#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ -#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ -#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ -#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ -#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ -#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ - /* Same bits as above */ -#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ -#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ -#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ -#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ -#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ -#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ -#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ -#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ -#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ -#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ -#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ -#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ -#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ -#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ -/* Correctable Err Reporting Enable */ -#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 -/* Non-fatal Err Reporting Enable */ -#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 -/* Fatal Err Reporting Enable */ -#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 -#define PCI_ERR_ROOT_STATUS 48 -#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ -/* Multi ERR_COR Received */ -#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 -/* ERR_FATAL/NONFATAL Recevied */ -#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 -/* Multi ERR_FATAL/NONFATAL Recevied */ -#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 -#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ -#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ -#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ -#define PCI_ERR_ROOT_COR_SRC 52 -#define PCI_ERR_ROOT_SRC 54 - -/* Virtual Channel */ -#define PCI_VC_PORT_REG1 4 -#define PCI_VC_PORT_REG2 8 -#define PCI_VC_PORT_CTRL 12 -#define PCI_VC_PORT_STATUS 14 -#define PCI_VC_RES_CAP 16 -#define PCI_VC_RES_CTRL 20 -#define PCI_VC_RES_STATUS 26 - -/* Power Budgeting */ -#define PCI_PWR_DSR 4 /* Data Select Register */ -#define PCI_PWR_DATA 8 /* Data Register */ -#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ -#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ -#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ -#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ -#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ -#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ -#define PCI_PWR_CAP 12 /* Capability */ -#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ - -/* - * Hypertransport sub capability types - * - * Unfortunately there are both 3 bit and 5 bit capability types defined - * in the HT spec, catering for that is a little messy. You probably don't - * want to use these directly, just use pci_find_ht_capability() and it - * will do the right thing for you. - */ -#define HT_3BIT_CAP_MASK 0xE0 -#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ -#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ - -#define HT_5BIT_CAP_MASK 0xF8 -#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ -#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ -#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ -#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ -#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ -#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ -#define HT_MSI_FLAGS 0x02 /* Offset to flags */ -#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ -#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ -#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ -#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ -#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ -#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ -#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ -#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ -#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ -#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ -#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ - - -#endif /* LINUX_PCI_REGS_H */ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/Makefile --- a/xen/arch/x86/hvm/vmx/Makefile Thu Feb 21 14:50:27 2008 +0000 +++ b/xen/arch/x86/hvm/vmx/Makefile Thu Feb 21 15:06:37 2008 +0000 @@ -1,5 +1,3 @@ subdir-y += vtd -subdir-y += vtd - subdir-$(x86_32) += x86_32 subdir-$(x86_64) += x86_64 diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/Makefile --- a/xen/arch/x86/hvm/vmx/vtd/Makefile Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -obj-y += intel-iommu.o -obj-y += dmar.o -obj-y += utils.o -obj-y += io.o -obj-y += qinval.o -obj-y += intremap.o diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/dmar.c --- a/xen/arch/x86/hvm/vmx/vtd/dmar.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,625 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Ashok Raj <ashok.raj@xxxxxxxxx> - * Copyright (C) Shaohua Li <shaohua.li@xxxxxxxxx> - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - adapted to xen - */ - -#include <xen/init.h> -#include <xen/bitmap.h> -#include <xen/kernel.h> -#include <xen/acpi.h> -#include <xen/mm.h> -#include <xen/xmalloc.h> -#include <asm/string.h> -#include "dmar.h" -#include "pci-direct.h" -#include "pci_regs.h" - -int vtd_enabled; -boolean_param("vtd", vtd_enabled); - -#undef PREFIX -#define PREFIX VTDPREFIX "ACPI DMAR:" -#define DEBUG - -#define MIN_SCOPE_LEN (sizeof(struct acpi_pci_path) + \ - sizeof(struct acpi_dev_scope)) - -LIST_HEAD(acpi_drhd_units); -LIST_HEAD(acpi_rmrr_units); -LIST_HEAD(acpi_atsr_units); - -u8 dmar_host_address_width; - -static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd) -{ - /* - * add INCLUDE_ALL at the tail, so scan the list will find it at - * the very end. - */ - if ( drhd->include_all ) - list_add_tail(&drhd->list, &acpi_drhd_units); - else - list_add(&drhd->list, &acpi_drhd_units); - return 0; -} - -static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr) -{ - list_add(&rmrr->list, &acpi_rmrr_units); - return 0; -} - -static int acpi_ioapic_device_match( - struct list_head *ioapic_list, unsigned int apic_id) -{ - struct acpi_ioapic_unit *ioapic; - list_for_each_entry( ioapic, ioapic_list, list ) { - if (ioapic->apic_id == apic_id) - return 1; - } - return 0; -} - -struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id) -{ - struct acpi_drhd_unit *drhd; - list_for_each_entry( drhd, &acpi_drhd_units, list ) { - if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) ) { - dprintk(XENLOG_INFO VTDPREFIX, - "ioapic_to_drhd: drhd->address = %lx\n", - drhd->address); - return drhd; - } - } - return NULL; -} - -struct iommu * ioapic_to_iommu(unsigned int apic_id) -{ - struct acpi_drhd_unit *drhd; - - list_for_each_entry( drhd, &acpi_drhd_units, list ) { - if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) ) { - dprintk(XENLOG_INFO VTDPREFIX, - "ioapic_to_iommu: drhd->address = %lx\n", - drhd->address); - return drhd->iommu; - } - } - dprintk(XENLOG_INFO VTDPREFIX, "returning NULL\n"); - return NULL; -} - -static int acpi_pci_device_match(struct pci_dev *devices, int cnt, - struct pci_dev *dev) -{ - int i; - - for ( i = 0; i < cnt; i++ ) - { - if ( (dev->bus == devices->bus) && - (dev->devfn == devices->devfn) ) - return 1; - devices++; - } - return 0; -} - -static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr) -{ - /* - * add ALL_PORTS at the tail, so scan the list will find it at - * the very end. - */ - if ( atsr->all_ports ) - list_add_tail(&atsr->list, &acpi_atsr_units); - else - list_add(&atsr->list, &acpi_atsr_units); - return 0; -} - -struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev) -{ - struct acpi_drhd_unit *drhd; - struct acpi_drhd_unit *include_all_drhd; - - include_all_drhd = NULL; - list_for_each_entry ( drhd, &acpi_drhd_units, list ) - { - if ( drhd->include_all ) - { - include_all_drhd = drhd; - continue; - } - - if ( acpi_pci_device_match(drhd->devices, - drhd->devices_cnt, dev) ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "acpi_find_matched_drhd_unit: drhd->address = %lx\n", - drhd->address); - return drhd; - } - } - - if ( include_all_drhd ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "acpi_find_matched_drhd_unit:include_all_drhd->addr = %lx\n", - include_all_drhd->address); - return include_all_drhd; - } - - return NULL; -} - -struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev) -{ - struct acpi_rmrr_unit *rmrr; - - list_for_each_entry ( rmrr, &acpi_rmrr_units, list ) - { - if ( acpi_pci_device_match(rmrr->devices, - rmrr->devices_cnt, dev) ) - return rmrr; - } - - return NULL; -} - -struct acpi_atsr_unit * acpi_find_matched_atsr_unit(struct pci_dev *dev) -{ - struct acpi_atsr_unit *atsru; - struct acpi_atsr_unit *all_ports_atsru; - - all_ports_atsru = NULL; - list_for_each_entry ( atsru, &acpi_atsr_units, list ) - { - if ( atsru->all_ports ) - all_ports_atsru = atsru; - if ( acpi_pci_device_match(atsru->devices, - atsru->devices_cnt, dev) ) - return atsru; - } - - if ( all_ports_atsru ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "acpi_find_matched_atsr_unit: all_ports_atsru\n"); - return all_ports_atsru;; - } - - return NULL; -} - -static int scope_device_count(void *start, void *end) -{ - struct acpi_dev_scope *scope; - u8 bus, sub_bus, sec_bus; - struct acpi_pci_path *path; - int depth, count = 0; - u8 dev, func; - u32 l; - - while ( start < end ) - { - scope = start; - if ( (scope->length < MIN_SCOPE_LEN) || - (scope->dev_type >= ACPI_DEV_ENTRY_COUNT) ) - { - dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope\n"); - return -EINVAL; - } - - path = (struct acpi_pci_path *)(scope + 1); - bus = scope->start_bus; - depth = (scope->length - sizeof(struct acpi_dev_scope)) - / sizeof(struct acpi_pci_path); - while ( --depth ) - { - bus = read_pci_config_byte( - bus, path->dev, path->fn, PCI_SECONDARY_BUS); - path++; - } - - if ( scope->dev_type == ACPI_DEV_ENDPOINT ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "found endpoint: bdf = %x:%x:%x\n", - bus, path->dev, path->fn); - count++; - } - else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "found bridge: bdf = %x:%x:%x\n", - bus, path->dev, path->fn); - sec_bus = read_pci_config_byte( - bus, path->dev, path->fn, PCI_SECONDARY_BUS); - sub_bus = read_pci_config_byte( - bus, path->dev, path->fn, PCI_SUBORDINATE_BUS); - - while ( sec_bus <= sub_bus ) - { - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - l = read_pci_config( - sec_bus, dev, func, PCI_VENDOR_ID); - - /* some broken boards return 0 or - * ~0 if a slot is empty - */ - if ( l == 0xffffffff || l == 0x00000000 || - l == 0x0000ffff || l == 0xffff0000 ) - break; - count++; - } - } - sec_bus++; - } - } - else if ( scope->dev_type == ACPI_DEV_IOAPIC ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "found IOAPIC: bdf = %x:%x:%x\n", - bus, path->dev, path->fn); - count++; - } - else - { - dprintk(XENLOG_INFO VTDPREFIX, - "found MSI HPET: bdf = %x:%x:%x\n", - bus, path->dev, path->fn); - count++; - } - - start += scope->length; - } - - return count; -} - -static int __init acpi_parse_dev_scope( - void *start, void *end, void *acpi_entry, int type) -{ - struct acpi_dev_scope *scope; - u8 bus, sub_bus, sec_bus; - struct acpi_pci_path *path; - struct acpi_ioapic_unit *acpi_ioapic_unit = NULL; - int depth; - struct pci_dev *pdev; - u8 dev, func; - u32 l; - - int *cnt = NULL; - struct pci_dev **devices = NULL; - struct acpi_drhd_unit *dmaru = (struct acpi_drhd_unit *) acpi_entry; - struct acpi_rmrr_unit *rmrru = (struct acpi_rmrr_unit *) acpi_entry; - struct acpi_atsr_unit *atsru = (struct acpi_atsr_unit *) acpi_entry; - - switch (type) { - case DMAR_TYPE: - cnt = &(dmaru->devices_cnt); - devices = &(dmaru->devices); - break; - case RMRR_TYPE: - cnt = &(rmrru->devices_cnt); - devices = &(rmrru->devices); - break; - case ATSR_TYPE: - cnt = &(atsru->devices_cnt); - devices = &(atsru->devices); - break; - default: - dprintk(XENLOG_ERR VTDPREFIX, "invalid vt-d acpi entry type\n"); - } - - *cnt = scope_device_count(start, end); - if ( *cnt == 0 ) - { - dprintk(XENLOG_INFO VTDPREFIX, "acpi_parse_dev_scope: no device\n"); - return 0; - } - - *devices = xmalloc_array(struct pci_dev, *cnt); - if ( !*devices ) - return -ENOMEM; - memset(*devices, 0, sizeof(struct pci_dev) * (*cnt)); - - pdev = *devices; - while ( start < end ) - { - scope = start; - path = (struct acpi_pci_path *)(scope + 1); - depth = (scope->length - sizeof(struct acpi_dev_scope)) - / sizeof(struct acpi_pci_path); - bus = scope->start_bus; - - while ( --depth ) - { - bus = read_pci_config_byte( - bus, path->dev, path->fn, PCI_SECONDARY_BUS); - path++; - } - - if ( scope->dev_type == ACPI_DEV_ENDPOINT ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "found endpoint: bdf = %x:%x:%x\n", - bus, path->dev, path->fn); - pdev->bus = bus; - pdev->devfn = PCI_DEVFN(path->dev, path->fn); - pdev++; - } - else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE ) - { - dprintk(XENLOG_INFO VTDPREFIX, - "found bridge: bus = %x dev = %x func = %x\n", - bus, path->dev, path->fn); - sec_bus = read_pci_config_byte( - bus, path->dev, path->fn, PCI_SECONDARY_BUS); - sub_bus = read_pci_config_byte( - bus, path->dev, path->fn, PCI_SUBORDINATE_BUS); - - while ( sec_bus <= sub_bus ) - { - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - l = read_pci_config( - sec_bus, dev, func, PCI_VENDOR_ID); - - /* some broken boards return 0 or - * ~0 if a slot is empty - */ - if ( l == 0xffffffff || l == 0x00000000 || - l == 0x0000ffff || l == 0xffff0000 ) - break; - - pdev->bus = sec_bus; - pdev->devfn = PCI_DEVFN(dev, func); - pdev++; - } - } - sec_bus++; - } - } - else if ( scope->dev_type == ACPI_DEV_IOAPIC ) - { - acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit); - if ( !acpi_ioapic_unit ) - return -ENOMEM; - acpi_ioapic_unit->apic_id = scope->enum_id; - acpi_ioapic_unit->ioapic.bdf.bus = bus; - acpi_ioapic_unit->ioapic.bdf.dev = path->dev; - acpi_ioapic_unit->ioapic.bdf.func = path->fn; - list_add(&acpi_ioapic_unit->list, &dmaru->ioapic_list); - dprintk(XENLOG_INFO VTDPREFIX, - "found IOAPIC: bus = %x dev = %x func = %x\n", - bus, path->dev, path->fn); - } - else - dprintk(XENLOG_INFO VTDPREFIX, - "found MSI HPET: bus = %x dev = %x func = %x\n", - bus, path->dev, path->fn); - start += scope->length; - } - - return 0; -} - -static int __init -acpi_parse_one_drhd(struct acpi_dmar_entry_header *header) -{ - struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header; - struct acpi_drhd_unit *dmaru; - int ret = 0; - static int include_all; - void *dev_scope_start, *dev_scope_end; - - dmaru = xmalloc(struct acpi_drhd_unit); - if ( !dmaru ) - return -ENOMEM; - memset(dmaru, 0, sizeof(struct acpi_drhd_unit)); - - dmaru->address = drhd->address; - dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */ - INIT_LIST_HEAD(&dmaru->ioapic_list); - dprintk(XENLOG_INFO VTDPREFIX, "dmaru->address = %lx\n", dmaru->address); - - dev_scope_start = (void *)(drhd + 1); - dev_scope_end = ((void *)drhd) + header->length; - ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end, - dmaru, DMAR_TYPE); - - if ( dmaru->include_all ) - { - dprintk(XENLOG_INFO VTDPREFIX, "found INCLUDE_ALL\n"); - /* Only allow one INCLUDE_ALL */ - if ( include_all ) - { - dprintk(XENLOG_WARNING VTDPREFIX, - "Only one INCLUDE_ALL device scope is allowed\n"); - ret = -EINVAL; - } - include_all = 1; - } - - if ( ret ) - xfree(dmaru); - else - acpi_register_drhd_unit(dmaru); - return ret; -} - -static int __init -acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header) -{ - struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header; - struct acpi_rmrr_unit *rmrru; - void *dev_scope_start, *dev_scope_end; - int ret = 0; - - rmrru = xmalloc(struct acpi_rmrr_unit); - if ( !rmrru ) - return -ENOMEM; - memset(rmrru, 0, sizeof(struct acpi_rmrr_unit)); - - rmrru->base_address = rmrr->base_address; - rmrru->end_address = rmrr->end_address; - dev_scope_start = (void *)(rmrr + 1); - dev_scope_end = ((void *)rmrr) + header->length; - ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end, - rmrru, RMRR_TYPE); - if ( ret || (rmrru->devices_cnt == 0) ) - xfree(rmrru); - else - acpi_register_rmrr_unit(rmrru); - return ret; -} - -static int __init -acpi_parse_one_atsr(struct acpi_dmar_entry_header *header) -{ - struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header; - struct acpi_atsr_unit *atsru; - int ret = 0; - static int all_ports; - void *dev_scope_start, *dev_scope_end; - - atsru = xmalloc(struct acpi_atsr_unit); - if ( !atsru ) - return -ENOMEM; - memset(atsru, 0, sizeof(struct acpi_atsr_unit)); - - atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */ - if ( !atsru->all_ports ) - { - dev_scope_start = (void *)(atsr + 1); - dev_scope_end = ((void *)atsr) + header->length; - ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end, - atsru, ATSR_TYPE); - } - else { - dprintk(XENLOG_INFO VTDPREFIX, "found ALL_PORTS\n"); - /* Only allow one ALL_PORTS */ - if ( all_ports ) - { - dprintk(XENLOG_WARNING VTDPREFIX, - "Only one ALL_PORTS device scope is allowed\n"); - ret = -EINVAL; - } - all_ports = 1; - } - - if ( ret ) - xfree(atsr); - else - acpi_register_atsr_unit(atsru); - return ret; -} - -static int __init acpi_parse_dmar(unsigned long phys_addr, - unsigned long size) -{ - struct acpi_table_dmar *dmar = NULL; - struct acpi_dmar_entry_header *entry_header; - int ret = 0; - - if ( !phys_addr || !size ) - return -EINVAL; - - dmar = (struct acpi_table_dmar *)__acpi_map_table(phys_addr, size); - if ( !dmar ) - { - dprintk(XENLOG_WARNING VTDPREFIX, "Unable to map DMAR\n"); - return -ENODEV; - } - - if ( !dmar->haw ) - { - dprintk(XENLOG_WARNING VTDPREFIX, "Zero: Invalid DMAR haw\n"); - return -EINVAL; - } - - dmar_host_address_width = dmar->haw; - dprintk(XENLOG_INFO VTDPREFIX, "Host address width %d\n", - dmar_host_address_width); - - entry_header = (struct acpi_dmar_entry_header *)(dmar + 1); - while ( ((unsigned long)entry_header) < - (((unsigned long)dmar) + size) ) - { - switch ( entry_header->type ) - { - case ACPI_DMAR_DRHD: - dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_DRHD\n"); - ret = acpi_parse_one_drhd(entry_header); - break; - case ACPI_DMAR_RMRR: - dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RMRR\n"); - ret = acpi_parse_one_rmrr(entry_header); - break; - case ACPI_DMAR_ATSR: - dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_ATSR\n"); - ret = acpi_parse_one_atsr(entry_header); - break; - default: - dprintk(XENLOG_WARNING VTDPREFIX, "Unknown DMAR structure type\n"); - ret = -EINVAL; - break; - } - if ( ret ) - break; - - entry_header = ((void *)entry_header + entry_header->length); - } - - /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */ - dmar->header.signature[0] = '\0'; - - return ret; -} - -int acpi_dmar_init(void) -{ - int rc; - - if ( !vtd_enabled ) - return -ENODEV; - - if ( (rc = vtd_hw_check()) != 0 ) - return rc; - - acpi_table_parse(ACPI_DMAR, acpi_parse_dmar); - - if ( list_empty(&acpi_drhd_units) ) - { - dprintk(XENLOG_ERR VTDPREFIX, "No DMAR devices found\n"); - vtd_enabled = 0; - return -ENODEV; - } - - printk("Intel VT-d has been enabled\n"); - - return 0; -} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/dmar.h --- a/xen/arch/x86/hvm/vmx/vtd/dmar.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Ashok Raj <ashok.raj@xxxxxxxxx> - * Copyright (C) Shaohua Li <shaohua.li@xxxxxxxxx> - */ - -#ifndef _DMAR_H_ -#define _DMAR_H_ - -#include <xen/list.h> -#include <asm/iommu.h> - -extern u8 dmar_host_address_width; - -/* This one is for interrupt remapping */ -struct acpi_ioapic_unit { - struct list_head list; - int apic_id; - union { - u16 info; - struct { - u16 func: 3, - dev: 5, - bus: 8; - }bdf; - }ioapic; -}; - -struct acpi_drhd_unit { - struct list_head list; - unsigned long address; /* register base address of the unit */ - struct pci_dev *devices; /* target devices */ - int devices_cnt; - u8 include_all:1; - struct iommu *iommu; - struct list_head ioapic_list; -}; - -struct acpi_rmrr_unit { - struct list_head list; - unsigned long base_address; - unsigned long end_address; - struct pci_dev *devices; /* target devices */ - int devices_cnt; - u8 allow_all:1; -}; - -struct acpi_atsr_unit { - struct list_head list; - struct pci_dev *devices; /* target devices */ - int devices_cnt; - u8 all_ports:1; -}; - -#define for_each_iommu(domain, iommu) \ - list_for_each_entry(iommu, \ - &(domain->arch.hvm_domain.hvm_iommu.iommu_list), list) - -#define for_each_pdev(domain, pdev) \ - list_for_each_entry(pdev, \ - &(domain->arch.hvm_domain.hvm_iommu.pdev_list), list) - -#define for_each_drhd_unit(drhd) \ - list_for_each_entry(drhd, &acpi_drhd_units, list) -#define for_each_rmrr_device(rmrr, pdev) \ - list_for_each_entry(rmrr, &acpi_rmrr_units, list) { \ - int _i; \ - for (_i = 0; _i < rmrr->devices_cnt; _i++) { \ - pdev = &(rmrr->devices[_i]); -#define end_for_each_rmrr_device(rmrr, pdev) \ - } \ - } - -struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev); -struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev); - -#define DMAR_TYPE 1 -#define RMRR_TYPE 2 -#define ATSR_TYPE 3 - -#define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */ -#define time_after(a,b) \ - (typecheck(unsigned long, a) && \ - typecheck(unsigned long, b) && \ - ((long)(b) - (long)(a) < 0)) - -int vtd_hw_check(void); -void disable_pmr(struct iommu *iommu); - -#endif // _DMAR_H_ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/extern.h --- a/xen/arch/x86/hvm/vmx/vtd/extern.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - * Copyright (C) Weidong Han <weidong.han@xxxxxxxxx> - */ - -#ifndef _VTD_EXTERN_H_ -#define _VTD_EXTERN_H_ - -#include "dmar.h" - -extern struct qi_ctrl *qi_ctrl; -extern struct ir_ctrl *ir_ctrl; - -void print_iommu_regs(struct acpi_drhd_unit *drhd); -void print_vtd_entries(struct domain *d, struct iommu *iommu, - int bus, int devfn, unsigned long gmfn); -void pdev_flr(u8 bus, u8 devfn); - -int qinval_setup(struct iommu *iommu); -int intremap_setup(struct iommu *iommu); -int queue_invalidate_context(struct iommu *iommu, - u16 did, u16 source_id, u8 function_mask, u8 granu); -int queue_invalidate_iotlb(struct iommu *iommu, - u8 granu, u8 dr, u8 dw, u16 did, u8 am, u8 ih, u64 addr); -int queue_invalidate_iec(struct iommu *iommu, - u8 granu, u8 im, u16 iidx); -int invalidate_sync(struct iommu *iommu); -int iommu_flush_iec_global(struct iommu *iommu); -int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx); -void print_iommu_regs(struct acpi_drhd_unit *drhd); -int vtd_hw_check(void); -struct iommu * ioapic_to_iommu(unsigned int apic_id); -struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id); -void clear_fault_bits(struct iommu *iommu); - -#endif // _VTD_EXTERN_H_ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/intel-iommu.c --- a/xen/arch/x86/hvm/vmx/vtd/intel-iommu.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2178 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Ashok Raj <ashok.raj@xxxxxxxxx> - * Copyright (C) Shaohua Li <shaohua.li@xxxxxxxxx> - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - adapted to xen - */ - -#include <xen/init.h> -#include <xen/irq.h> -#include <xen/spinlock.h> -#include <xen/sched.h> -#include <xen/xmalloc.h> -#include <xen/domain_page.h> -#include <asm/delay.h> -#include <asm/string.h> -#include <asm/mm.h> -#include <asm/iommu.h> -#include <asm/hvm/vmx/intel-iommu.h> -#include "dmar.h" -#include "pci-direct.h" -#include "pci_regs.h" -#include "msi.h" -#include "extern.h" - -#define domain_iommu_domid(d) ((d)->arch.hvm_domain.hvm_iommu.iommu_domid) - -static spinlock_t domid_bitmap_lock; /* protect domain id bitmap */ -static int domid_bitmap_size; /* domain id bitmap size in bit */ -static void *domid_bitmap; /* iommu domain id bitmap */ - -#define DID_FIELD_WIDTH 16 -#define DID_HIGH_OFFSET 8 -static void context_set_domain_id(struct context_entry *context, - struct domain *d) -{ - unsigned long flags; - domid_t iommu_domid = domain_iommu_domid(d); - - if ( iommu_domid == 0 ) - { - spin_lock_irqsave(&domid_bitmap_lock, flags); - iommu_domid = find_first_zero_bit(domid_bitmap, domid_bitmap_size); - set_bit(iommu_domid, domid_bitmap); - spin_unlock_irqrestore(&domid_bitmap_lock, flags); - d->arch.hvm_domain.hvm_iommu.iommu_domid = iommu_domid; - } - - context->hi &= (1 << DID_HIGH_OFFSET) - 1; - context->hi |= iommu_domid << DID_HIGH_OFFSET; -} - -static void iommu_domid_release(struct domain *d) -{ - domid_t iommu_domid = domain_iommu_domid(d); - - if ( iommu_domid != 0 ) - { - d->arch.hvm_domain.hvm_iommu.iommu_domid = 0; - clear_bit(iommu_domid, domid_bitmap); - } -} - -unsigned int x86_clflush_size; -void clflush_cache_range(void *adr, int size) -{ - int i; - for ( i = 0; i < size; i += x86_clflush_size ) - clflush(adr + i); -} - -static void __iommu_flush_cache(struct iommu *iommu, void *addr, int size) -{ - if ( !ecap_coherent(iommu->ecap) ) - clflush_cache_range(addr, size); -} - -#define iommu_flush_cache_entry(iommu, addr) \ - __iommu_flush_cache(iommu, addr, 8) -#define iommu_flush_cache_page(iommu, addr) \ - __iommu_flush_cache(iommu, addr, PAGE_SIZE_4K) - -int nr_iommus; -/* context entry handling */ -static struct context_entry * device_to_context_entry(struct iommu *iommu, - u8 bus, u8 devfn) -{ - struct root_entry *root; - struct context_entry *context; - unsigned long phy_addr; - unsigned long flags; - - spin_lock_irqsave(&iommu->lock, flags); - root = &iommu->root_entry[bus]; - if ( !root_present(*root) ) - { - phy_addr = (unsigned long) alloc_xenheap_page(); - if ( !phy_addr ) - { - spin_unlock_irqrestore(&iommu->lock, flags); - return NULL; - } - memset((void *) phy_addr, 0, PAGE_SIZE); - iommu_flush_cache_page(iommu, (void *)phy_addr); - phy_addr = virt_to_maddr((void *)phy_addr); - set_root_value(*root, phy_addr); - set_root_present(*root); - iommu_flush_cache_entry(iommu, root); - } - phy_addr = (unsigned long) get_context_addr(*root); - context = (struct context_entry *)maddr_to_virt(phy_addr); - spin_unlock_irqrestore(&iommu->lock, flags); - return &context[devfn]; -} - -static int device_context_mapped(struct iommu *iommu, u8 bus, u8 devfn) -{ - struct root_entry *root; - struct context_entry *context; - unsigned long phy_addr; - int ret; - unsigned long flags; - - spin_lock_irqsave(&iommu->lock, flags); - root = &iommu->root_entry[bus]; - if ( !root_present(*root) ) - { - ret = 0; - goto out; - } - phy_addr = get_context_addr(*root); - context = (struct context_entry *)maddr_to_virt(phy_addr); - ret = context_present(context[devfn]); - out: - spin_unlock_irqrestore(&iommu->lock, flags); - return ret; -} - -static struct page_info *addr_to_dma_page(struct domain *domain, u64 addr) -{ - struct hvm_iommu *hd = domain_hvm_iommu(domain); - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - int addr_width = agaw_to_width(hd->agaw); - struct dma_pte *parent, *pte = NULL, *pgd; - int level = agaw_to_level(hd->agaw); - int offset; - unsigned long flags; - struct page_info *pg = NULL; - u64 *vaddr = NULL; - - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - iommu = drhd->iommu; - - addr &= (((u64)1) << addr_width) - 1; - spin_lock_irqsave(&hd->mapping_lock, flags); - if ( !hd->pgd ) - { - pgd = (struct dma_pte *)alloc_xenheap_page(); - if ( !pgd ) - { - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return NULL; - } - memset(pgd, 0, PAGE_SIZE); - hd->pgd = pgd; - } - - parent = hd->pgd; - while ( level > 1 ) - { - offset = address_level_offset(addr, level); - pte = &parent[offset]; - - if ( dma_pte_addr(*pte) == 0 ) - { - pg = alloc_domheap_page(NULL); - vaddr = map_domain_page(page_to_mfn(pg)); - if ( !vaddr ) - { - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return NULL; - } - memset(vaddr, 0, PAGE_SIZE); - iommu_flush_cache_page(iommu, vaddr); - - dma_set_pte_addr(*pte, page_to_maddr(pg)); - - /* - * high level table always sets r/w, last level - * page table control read/write - */ - dma_set_pte_readable(*pte); - dma_set_pte_writable(*pte); - iommu_flush_cache_entry(iommu, pte); - } - else - { - pg = maddr_to_page(pte->val); - vaddr = map_domain_page(page_to_mfn(pg)); - if ( !vaddr ) - { - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return NULL; - } - } - - if ( parent != hd->pgd ) - unmap_domain_page(parent); - - if ( level == 2 && vaddr ) - { - unmap_domain_page(vaddr); - break; - } - - parent = (struct dma_pte *)vaddr; - vaddr = NULL; - level--; - } - - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return pg; -} - -/* return address's page at specific level */ -static struct page_info *dma_addr_level_page(struct domain *domain, - u64 addr, int level) -{ - struct hvm_iommu *hd = domain_hvm_iommu(domain); - struct dma_pte *parent, *pte = NULL; - int total = agaw_to_level(hd->agaw); - int offset; - struct page_info *pg = NULL; - - parent = hd->pgd; - while ( level <= total ) - { - offset = address_level_offset(addr, total); - pte = &parent[offset]; - if ( dma_pte_addr(*pte) == 0 ) - { - if ( parent != hd->pgd ) - unmap_domain_page(parent); - break; - } - - pg = maddr_to_page(pte->val); - if ( parent != hd->pgd ) - unmap_domain_page(parent); - - if ( level == total ) - return pg; - - parent = map_domain_page(page_to_mfn(pg)); - total--; - } - - return NULL; -} - -static void iommu_flush_write_buffer(struct iommu *iommu) -{ - u32 val; - unsigned long flag; - unsigned long start_time; - - if ( !cap_rwbf(iommu->cap) ) - return; - val = iommu->gcmd | DMA_GCMD_WBF; - - spin_lock_irqsave(&iommu->register_lock, flag); - dmar_writel(iommu->reg, DMAR_GCMD_REG, val); - - /* Make sure hardware complete it */ - start_time = jiffies; - for ( ; ; ) - { - val = dmar_readl(iommu->reg, DMAR_GSTS_REG); - if ( !(val & DMA_GSTS_WBFS) ) - break; - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - panic("DMAR hardware is malfunctional," - " please disable IOMMU\n"); - cpu_relax(); - } - spin_unlock_irqrestore(&iommu->register_lock, flag); -} - -/* return value determine if we need a write buffer flush */ -static int flush_context_reg( - void *_iommu, - u16 did, u16 source_id, u8 function_mask, u64 type, - int non_present_entry_flush) -{ - struct iommu *iommu = (struct iommu *) _iommu; - u64 val = 0; - unsigned long flag; - unsigned long start_time; - - /* - * In the non-present entry flush case, if hardware doesn't cache - * non-present entry we do nothing and if hardware cache non-present - * entry, we flush entries of domain 0 (the domain id is used to cache - * any non-present entries) - */ - if ( non_present_entry_flush ) - { - if ( !cap_caching_mode(iommu->cap) ) - return 1; - else - did = 0; - } - - /* use register invalidation */ - switch ( type ) - { - case DMA_CCMD_GLOBAL_INVL: - val = DMA_CCMD_GLOBAL_INVL; - break; - case DMA_CCMD_DOMAIN_INVL: - val = DMA_CCMD_DOMAIN_INVL|DMA_CCMD_DID(did); - break; - case DMA_CCMD_DEVICE_INVL: - val = DMA_CCMD_DEVICE_INVL|DMA_CCMD_DID(did) - |DMA_CCMD_SID(source_id)|DMA_CCMD_FM(function_mask); - break; - default: - BUG(); - } - val |= DMA_CCMD_ICC; - - spin_lock_irqsave(&iommu->register_lock, flag); - dmar_writeq(iommu->reg, DMAR_CCMD_REG, val); - - /* Make sure hardware complete it */ - start_time = jiffies; - for ( ; ; ) - { - val = dmar_readq(iommu->reg, DMAR_CCMD_REG); - if ( !(val & DMA_CCMD_ICC) ) - break; - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - panic("DMAR hardware is malfunctional, please disable IOMMU\n"); - cpu_relax(); - } - spin_unlock_irqrestore(&iommu->register_lock, flag); - /* flush context entry will implictly flush write buffer */ - return 0; -} - -static int inline iommu_flush_context_global( - struct iommu *iommu, int non_present_entry_flush) -{ - struct iommu_flush *flush = iommu_get_flush(iommu); - return flush->context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL, - non_present_entry_flush); -} - -static int inline iommu_flush_context_domain( - struct iommu *iommu, u16 did, int non_present_entry_flush) -{ - struct iommu_flush *flush = iommu_get_flush(iommu); - return flush->context(iommu, did, 0, 0, DMA_CCMD_DOMAIN_INVL, - non_present_entry_flush); -} - -static int inline iommu_flush_context_device( - struct iommu *iommu, u16 did, u16 source_id, - u8 function_mask, int non_present_entry_flush) -{ - struct iommu_flush *flush = iommu_get_flush(iommu); - return flush->context(iommu, did, source_id, function_mask, - DMA_CCMD_DEVICE_INVL, - non_present_entry_flush); -} - -/* return value determine if we need a write buffer flush */ -static int flush_iotlb_reg(void *_iommu, u16 did, - u64 addr, unsigned int size_order, u64 type, - int non_present_entry_flush) -{ - struct iommu *iommu = (struct iommu *) _iommu; - int tlb_offset = ecap_iotlb_offset(iommu->ecap); - u64 val = 0, val_iva = 0; - unsigned long flag; - unsigned long start_time; - - /* - * In the non-present entry flush case, if hardware doesn't cache - * non-present entry we do nothing and if hardware cache non-present - * entry, we flush entries of domain 0 (the domain id is used to cache - * any non-present entries) - */ - if ( non_present_entry_flush ) - { - if ( !cap_caching_mode(iommu->cap) ) - return 1; - else - did = 0; - } - - /* use register invalidation */ - switch ( type ) - { - case DMA_TLB_GLOBAL_FLUSH: - /* global flush doesn't need set IVA_REG */ - val = DMA_TLB_GLOBAL_FLUSH|DMA_TLB_IVT; - break; - case DMA_TLB_DSI_FLUSH: - val = DMA_TLB_DSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did); - break; - case DMA_TLB_PSI_FLUSH: - val = DMA_TLB_PSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did); - /* Note: always flush non-leaf currently */ - val_iva = size_order | addr; - break; - default: - BUG(); - } - /* Note: set drain read/write */ - if ( cap_read_drain(iommu->cap) ) - val |= DMA_TLB_READ_DRAIN; - if ( cap_write_drain(iommu->cap) ) - val |= DMA_TLB_WRITE_DRAIN; - - spin_lock_irqsave(&iommu->register_lock, flag); - /* Note: Only uses first TLB reg currently */ - if ( val_iva ) - dmar_writeq(iommu->reg, tlb_offset, val_iva); - dmar_writeq(iommu->reg, tlb_offset + 8, val); - - /* Make sure hardware complete it */ - start_time = jiffies; - for ( ; ; ) - { - val = dmar_readq(iommu->reg, tlb_offset + 8); - if ( !(val & DMA_TLB_IVT) ) - break; - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - panic("DMAR hardware is malfunctional, please disable IOMMU\n"); - cpu_relax(); - } - spin_unlock_irqrestore(&iommu->register_lock, flag); - - /* check IOTLB invalidation granularity */ - if ( DMA_TLB_IAIG(val) == 0 ) - printk(KERN_ERR VTDPREFIX "IOMMU: flush IOTLB failed\n"); - if ( DMA_TLB_IAIG(val) != DMA_TLB_IIRG(type) ) - printk(KERN_ERR VTDPREFIX "IOMMU: tlb flush request %x, actual %x\n", - (u32)DMA_TLB_IIRG(type), (u32)DMA_TLB_IAIG(val)); - /* flush context entry will implictly flush write buffer */ - return 0; -} - -static int inline iommu_flush_iotlb_global(struct iommu *iommu, - int non_present_entry_flush) -{ - struct iommu_flush *flush = iommu_get_flush(iommu); - return flush->iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH, - non_present_entry_flush); -} - -static int inline iommu_flush_iotlb_dsi(struct iommu *iommu, u16 did, - int non_present_entry_flush) -{ - struct iommu_flush *flush = iommu_get_flush(iommu); - return flush->iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH, - non_present_entry_flush); -} - -static int inline get_alignment(u64 base, unsigned int size) -{ - int t = 0; - u64 end; - - end = base + size - 1; - while ( base != end ) - { - t++; - base >>= 1; - end >>= 1; - } - return t; -} - -static int inline iommu_flush_iotlb_psi( - struct iommu *iommu, u16 did, - u64 addr, unsigned int pages, int non_present_entry_flush) -{ - unsigned int align; - struct iommu_flush *flush = iommu_get_flush(iommu); - - BUG_ON(addr & (~PAGE_MASK_4K)); - BUG_ON(pages == 0); - - /* Fallback to domain selective flush if no PSI support */ - if ( !cap_pgsel_inv(iommu->cap) ) - return iommu_flush_iotlb_dsi(iommu, did, - non_present_entry_flush); - - /* - * PSI requires page size is 2 ^ x, and the base address is naturally - * aligned to the size - */ - align = get_alignment(addr >> PAGE_SHIFT_4K, pages); - /* Fallback to domain selective flush if size is too big */ - if ( align > cap_max_amask_val(iommu->cap) ) - return iommu_flush_iotlb_dsi(iommu, did, - non_present_entry_flush); - - addr >>= PAGE_SHIFT_4K + align; - addr <<= PAGE_SHIFT_4K + align; - - return flush->iotlb(iommu, did, addr, align, - DMA_TLB_PSI_FLUSH, non_present_entry_flush); -} - -void iommu_flush_all(void) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - - wbinvd(); - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - iommu_flush_context_global(iommu, 0); - iommu_flush_iotlb_global(iommu, 0); - } -} - -/* clear one page's page table */ -static void dma_pte_clear_one(struct domain *domain, u64 addr) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - struct dma_pte *pte = NULL; - struct page_info *pg = NULL; - - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - - /* get last level pte */ - pg = dma_addr_level_page(domain, addr, 1); - if ( !pg ) - return; - pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg)); - pte += address_level_offset(addr, 1); - if ( pte ) - { - dma_clear_pte(*pte); - iommu_flush_cache_entry(drhd->iommu, pte); - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - if ( cap_caching_mode(iommu->cap) ) - iommu_flush_iotlb_psi(iommu, domain_iommu_domid(domain), - addr, 1, 0); - else if (cap_rwbf(iommu->cap)) - iommu_flush_write_buffer(iommu); - } - } - unmap_domain_page(pte); -} - -/* clear last level pte, a tlb flush should be followed */ -static void dma_pte_clear_range(struct domain *domain, u64 start, u64 end) -{ - struct hvm_iommu *hd = domain_hvm_iommu(domain); - int addr_width = agaw_to_width(hd->agaw); - - start &= (((u64)1) << addr_width) - 1; - end &= (((u64)1) << addr_width) - 1; - /* in case it's partial page */ - start = PAGE_ALIGN_4K(start); - end &= PAGE_MASK_4K; - - /* we don't need lock here, nobody else touches the iova range */ - while ( start < end ) - { - dma_pte_clear_one(domain, start); - start += PAGE_SIZE_4K; - } -} - -/* free page table pages. last level pte should already be cleared */ -void dma_pte_free_pagetable(struct domain *domain, u64 start, u64 end) -{ - struct acpi_drhd_unit *drhd; - struct hvm_iommu *hd = domain_hvm_iommu(domain); - struct iommu *iommu; - int addr_width = agaw_to_width(hd->agaw); - struct dma_pte *pte; - int total = agaw_to_level(hd->agaw); - int level; - u32 tmp; - struct page_info *pg = NULL; - - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - iommu = drhd->iommu; - - start &= (((u64)1) << addr_width) - 1; - end &= (((u64)1) << addr_width) - 1; - - /* we don't need lock here, nobody else touches the iova range */ - level = 2; - while ( level <= total ) - { - tmp = align_to_level(start, level); - if ( (tmp >= end) || ((tmp + level_size(level)) > end) ) - return; - - while ( tmp < end ) - { - pg = dma_addr_level_page(domain, tmp, level); - if ( !pg ) - return; - pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg)); - pte += address_level_offset(tmp, level); - dma_clear_pte(*pte); - iommu_flush_cache_entry(iommu, pte); - unmap_domain_page(pte); - free_domheap_page(pg); - - tmp += level_size(level); - } - level++; - } - - /* free pgd */ - if ( start == 0 && end == ((((u64)1) << addr_width) - 1) ) - { - free_xenheap_page((void *)hd->pgd); - hd->pgd = NULL; - } -} - -/* iommu handling */ -static int iommu_set_root_entry(struct iommu *iommu) -{ - void *addr; - u32 cmd, sts; - struct root_entry *root; - unsigned long flags; - - if ( iommu == NULL ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "iommu_set_root_entry: iommu == NULL\n"); - return -EINVAL; - } - - if ( unlikely(!iommu->root_entry) ) - { - root = (struct root_entry *)alloc_xenheap_page(); - if ( root == NULL ) - return -ENOMEM; - - memset((u8*)root, 0, PAGE_SIZE); - iommu_flush_cache_page(iommu, root); - - if ( cmpxchg((unsigned long *)&iommu->root_entry, - 0, (unsigned long)root) != 0 ) - free_xenheap_page((void *)root); - } - - addr = iommu->root_entry; - - spin_lock_irqsave(&iommu->register_lock, flags); - - dmar_writeq(iommu->reg, DMAR_RTADDR_REG, virt_to_maddr(addr)); - cmd = iommu->gcmd | DMA_GCMD_SRTP; - dmar_writel(iommu->reg, DMAR_GCMD_REG, cmd); - - /* Make sure hardware complete it */ - for ( ; ; ) - { - sts = dmar_readl(iommu->reg, DMAR_GSTS_REG); - if ( sts & DMA_GSTS_RTPS ) - break; - cpu_relax(); - } - - spin_unlock_irqrestore(&iommu->register_lock, flags); - - return 0; -} - -static int iommu_enable_translation(struct iommu *iommu) -{ - u32 sts; - unsigned long flags; - - dprintk(XENLOG_INFO VTDPREFIX, - "iommu_enable_translation: iommu->reg = %p\n", iommu->reg); - spin_lock_irqsave(&iommu->register_lock, flags); - iommu->gcmd |= DMA_GCMD_TE; - dmar_writel(iommu->reg, DMAR_GCMD_REG, iommu->gcmd); - /* Make sure hardware complete it */ - for ( ; ; ) - { - sts = dmar_readl(iommu->reg, DMAR_GSTS_REG); - if ( sts & DMA_GSTS_TES ) - break; - cpu_relax(); - } - - /* Disable PMRs when VT-d engine takes effect per spec definition */ - disable_pmr(iommu); - spin_unlock_irqrestore(&iommu->register_lock, flags); - return 0; -} - -int iommu_disable_translation(struct iommu *iommu) -{ - u32 sts; - unsigned long flags; - - spin_lock_irqsave(&iommu->register_lock, flags); - iommu->gcmd &= ~ DMA_GCMD_TE; - dmar_writel(iommu->reg, DMAR_GCMD_REG, iommu->gcmd); - - /* Make sure hardware complete it */ - for ( ; ; ) - { - sts = dmar_readl(iommu->reg, DMAR_GSTS_REG); - if ( !(sts & DMA_GSTS_TES) ) - break; - cpu_relax(); - } - spin_unlock_irqrestore(&iommu->register_lock, flags); - return 0; -} - -static struct iommu *vector_to_iommu[NR_VECTORS]; -static int iommu_page_fault_do_one(struct iommu *iommu, int type, - u8 fault_reason, u16 source_id, u32 addr) -{ - dprintk(XENLOG_WARNING VTDPREFIX, - "iommu_fault:%s: %x:%x.%x addr %x REASON %x iommu->reg = %p\n", - (type ? "DMA Read" : "DMA Write"), (source_id >> 8), - PCI_SLOT(source_id & 0xFF), PCI_FUNC(source_id & 0xFF), addr, - fault_reason, iommu->reg); - - if (fault_reason < 0x20) - print_vtd_entries(current->domain, iommu, (source_id >> 8), - (source_id & 0xff), (addr >> PAGE_SHIFT)); - - return 0; -} - -static void iommu_fault_status(u32 fault_status) -{ - if (fault_status & DMA_FSTS_PFO) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Fault Overflow\n"); - else - if (fault_status & DMA_FSTS_PPF) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Primary Pending Fault\n"); - else - if (fault_status & DMA_FSTS_AFO) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Advanced Fault Overflow\n"); - else - if (fault_status & DMA_FSTS_APF) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Advanced Pending Fault\n"); - else - if (fault_status & DMA_FSTS_IQE) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Invalidation Queue Error\n"); - else - if (fault_status & DMA_FSTS_ICE) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Invalidation Completion Error\n"); - else - if (fault_status & DMA_FSTS_ITE) - dprintk(XENLOG_ERR VTDPREFIX, - "iommu_fault_status: Invalidation Time-out Error\n"); -} - -#define PRIMARY_FAULT_REG_LEN (16) -static void iommu_page_fault(int vector, void *dev_id, - struct cpu_user_regs *regs) -{ - struct iommu *iommu = dev_id; - int reg, fault_index; - u32 fault_status; - unsigned long flags; - - dprintk(XENLOG_WARNING VTDPREFIX, - "iommu_page_fault: iommu->reg = %p\n", iommu->reg); - - spin_lock_irqsave(&iommu->register_lock, flags); - fault_status = dmar_readl(iommu->reg, DMAR_FSTS_REG); - spin_unlock_irqrestore(&iommu->register_lock, flags); - - iommu_fault_status(fault_status); - - /* FIXME: ignore advanced fault log */ - if ( !(fault_status & DMA_FSTS_PPF) ) - return; - fault_index = dma_fsts_fault_record_index(fault_status); - reg = cap_fault_reg_offset(iommu->cap); - for ( ; ; ) - { - u8 fault_reason; - u16 source_id; - u32 guest_addr, data; - int type; - - /* highest 32 bits */ - spin_lock_irqsave(&iommu->register_lock, flags); - data = dmar_readl(iommu->reg, reg + - fault_index * PRIMARY_FAULT_REG_LEN + 12); - if ( !(data & DMA_FRCD_F) ) - { - spin_unlock_irqrestore(&iommu->register_lock, flags); - break; - } - - fault_reason = dma_frcd_fault_reason(data); - type = dma_frcd_type(data); - - data = dmar_readl(iommu->reg, reg + - fault_index * PRIMARY_FAULT_REG_LEN + 8); - source_id = dma_frcd_source_id(data); - - guest_addr = dmar_readq(iommu->reg, reg + - fault_index * PRIMARY_FAULT_REG_LEN); - guest_addr = dma_frcd_page_addr(guest_addr); - /* clear the fault */ - dmar_writel(iommu->reg, reg + - fault_index * PRIMARY_FAULT_REG_LEN + 12, DMA_FRCD_F); - spin_unlock_irqrestore(&iommu->register_lock, flags); - - iommu_page_fault_do_one(iommu, type, fault_reason, - source_id, guest_addr); - - fault_index++; - if ( fault_index > cap_num_fault_regs(iommu->cap) ) - fault_index = 0; - } - - /* clear primary fault overflow */ - if ( fault_status & DMA_FSTS_PFO ) - { - spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writel(iommu->reg, DMAR_FSTS_REG, DMA_FSTS_PFO); - spin_unlock_irqrestore(&iommu->register_lock, flags); - } -} - -static void dma_msi_unmask(unsigned int vector) -{ - struct iommu *iommu = vector_to_iommu[vector]; - unsigned long flags; - - /* unmask it */ - spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writel(iommu->reg, DMAR_FECTL_REG, 0); - spin_unlock_irqrestore(&iommu->register_lock, flags); -} - -static void dma_msi_mask(unsigned int vector) -{ - unsigned long flags; - struct iommu *iommu = vector_to_iommu[vector]; - - /* mask it */ - spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writel(iommu->reg, DMAR_FECTL_REG, DMA_FECTL_IM); - spin_unlock_irqrestore(&iommu->register_lock, flags); -} - -static unsigned int dma_msi_startup(unsigned int vector) -{ - dma_msi_unmask(vector); - return 0; -} - -static void dma_msi_end(unsigned int vector) -{ - dma_msi_unmask(vector); - ack_APIC_irq(); -} - -static void dma_msi_data_init(struct iommu *iommu, int vector) -{ - u32 msi_data = 0; - unsigned long flags; - - /* Fixed, edge, assert mode. Follow MSI setting */ - msi_data |= vector & 0xff; - msi_data |= 1 << 14; - - spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writel(iommu->reg, DMAR_FEDATA_REG, msi_data); - spin_unlock_irqrestore(&iommu->register_lock, flags); -} - -static void dma_msi_addr_init(struct iommu *iommu, int phy_cpu) -{ - u64 msi_address; - unsigned long flags; - - /* Physical, dedicated cpu. Follow MSI setting */ - msi_address = (MSI_ADDRESS_HEADER << (MSI_ADDRESS_HEADER_SHIFT + 8)); - msi_address |= MSI_PHYSICAL_MODE << 2; - msi_address |= MSI_REDIRECTION_HINT_MODE << 3; - msi_address |= phy_cpu << MSI_TARGET_CPU_SHIFT; - - spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writel(iommu->reg, DMAR_FEADDR_REG, (u32)msi_address); - dmar_writel(iommu->reg, DMAR_FEUADDR_REG, (u32)(msi_address >> 32)); - spin_unlock_irqrestore(&iommu->register_lock, flags); -} - -static void dma_msi_set_affinity(unsigned int vector, cpumask_t dest) -{ - struct iommu *iommu = vector_to_iommu[vector]; - dma_msi_addr_init(iommu, cpu_physical_id(first_cpu(dest))); -} - -static struct hw_interrupt_type dma_msi_type = { - .typename = "DMA_MSI", - .startup = dma_msi_startup, - .shutdown = dma_msi_mask, - .enable = dma_msi_unmask, - .disable = dma_msi_mask, - .ack = dma_msi_mask, - .end = dma_msi_end, - .set_affinity = dma_msi_set_affinity, -}; - -int iommu_set_interrupt(struct iommu *iommu) -{ - int vector, ret; - - vector = assign_irq_vector(AUTO_ASSIGN); - vector_to_iommu[vector] = iommu; - - /* VT-d fault is a MSI, make irq == vector */ - irq_vector[vector] = vector; - vector_irq[vector] = vector; - - if ( !vector ) - { - gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: no vectors\n"); - return -EINVAL; - } - - irq_desc[vector].handler = &dma_msi_type; - ret = request_irq(vector, iommu_page_fault, 0, "dmar", iommu); - if ( ret ) - gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: can't request irq\n"); - return vector; -} - -struct iommu *iommu_alloc(void *hw_data) -{ - struct acpi_drhd_unit *drhd = (struct acpi_drhd_unit *) hw_data; - struct iommu *iommu; - struct qi_ctrl *qi_ctrl; - struct ir_ctrl *ir_ctrl; - - if ( nr_iommus > MAX_IOMMUS ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "IOMMU: nr_iommus %d > MAX_IOMMUS\n", nr_iommus); - return NULL; - } - - iommu = xmalloc(struct iommu); - if ( !iommu ) - return NULL; - memset(iommu, 0, sizeof(struct iommu)); - - set_fixmap_nocache(FIX_IOMMU_REGS_BASE_0 + nr_iommus, drhd->address); - iommu->reg = (void *) fix_to_virt(FIX_IOMMU_REGS_BASE_0 + nr_iommus); - - printk("iommu_alloc: iommu->reg = %p drhd->address = %lx\n", - iommu->reg, drhd->address); - - nr_iommus++; - - if ( !iommu->reg ) - { - printk(KERN_ERR VTDPREFIX "IOMMU: can't mapping the region\n"); - goto error; - } - - iommu->cap = dmar_readq(iommu->reg, DMAR_CAP_REG); - iommu->ecap = dmar_readq(iommu->reg, DMAR_ECAP_REG); - - printk("iommu_alloc: cap = %"PRIx64"\n",iommu->cap); - printk("iommu_alloc: ecap = %"PRIx64"\n", iommu->ecap); - - spin_lock_init(&iommu->lock); - spin_lock_init(&iommu->register_lock); - - qi_ctrl = iommu_qi_ctrl(iommu); - spin_lock_init(&qi_ctrl->qinval_lock); - spin_lock_init(&qi_ctrl->qinval_poll_lock); - - ir_ctrl = iommu_ir_ctrl(iommu); - spin_lock_init(&ir_ctrl->iremap_lock); - - drhd->iommu = iommu; - return iommu; - error: - xfree(iommu); - return NULL; -} - -static void free_iommu(struct iommu *iommu) -{ - if ( !iommu ) - return; - if ( iommu->root_entry ) - free_xenheap_page((void *)iommu->root_entry); - if ( iommu->reg ) - iounmap(iommu->reg); - free_irq(iommu->vector); - xfree(iommu); -} - -#define guestwidth_to_adjustwidth(gaw) ({ \ - int agaw, r = (gaw - 12) % 9; \ - agaw = (r == 0) ? gaw : (gaw + 9 - r); \ - if ( agaw > 64 ) \ - agaw = 64; \ - agaw; }) - -int intel_iommu_domain_init(struct domain *domain) -{ - struct hvm_iommu *hd = domain_hvm_iommu(domain); - struct iommu *iommu = NULL; - int guest_width = DEFAULT_DOMAIN_ADDRESS_WIDTH; - int adjust_width, agaw; - unsigned long sagaw; - struct acpi_drhd_unit *drhd; - - if ( !vtd_enabled || list_empty(&acpi_drhd_units) ) - return 0; - - for_each_drhd_unit ( drhd ) - iommu = drhd->iommu ? : iommu_alloc(drhd); - - /* calculate AGAW */ - if (guest_width > cap_mgaw(iommu->cap)) - guest_width = cap_mgaw(iommu->cap); - adjust_width = guestwidth_to_adjustwidth(guest_width); - agaw = width_to_agaw(adjust_width); - /* FIXME: hardware doesn't support it, choose a bigger one? */ - sagaw = cap_sagaw(iommu->cap); - if ( !test_bit(agaw, &sagaw) ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "IOMMU: hardware doesn't support the agaw\n"); - agaw = find_next_bit(&sagaw, 5, agaw); - if ( agaw >= 5 ) - return -ENODEV; - } - hd->agaw = agaw; - return 0; -} - -static int domain_context_mapping_one( - struct domain *domain, - struct iommu *iommu, - u8 bus, u8 devfn) -{ - struct hvm_iommu *hd = domain_hvm_iommu(domain); - struct context_entry *context; - unsigned long flags; - int ret = 0; - - context = device_to_context_entry(iommu, bus, devfn); - if ( !context ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "domain_context_mapping_one:context == NULL:" - "bdf = %x:%x:%x\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return -ENOMEM; - } - - if ( context_present(*context) ) - { - gdprintk(XENLOG_WARNING VTDPREFIX, - "domain_context_mapping_one:context present:bdf=%x:%x:%x\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return 0; - } - - spin_lock_irqsave(&iommu->lock, flags); - /* - * domain_id 0 is not valid on Intel's IOMMU, force domain_id to - * be 1 based as required by intel's iommu hw. - */ - context_set_domain_id(context, domain); - context_set_address_width(*context, hd->agaw); - - if ( ecap_pass_thru(iommu->ecap) ) - context_set_translation_type(*context, CONTEXT_TT_PASS_THRU); -#ifdef CONTEXT_PASSTHRU - else - { -#endif - if ( !hd->pgd ) - { - struct dma_pte *pgd = (struct dma_pte *)alloc_xenheap_page(); - if ( !pgd ) - { - spin_unlock_irqrestore(&hd->mapping_lock, flags); - return -ENOMEM; - } - memset(pgd, 0, PAGE_SIZE); - hd->pgd = pgd; - } - - context_set_address_root(*context, virt_to_maddr(hd->pgd)); - context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL); -#ifdef CONTEXT_PASSTHRU - } -#endif - - context_set_fault_enable(*context); - context_set_present(*context); - iommu_flush_cache_entry(iommu, context); - - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_mapping_one-%x:%x:%x-*context=%"PRIx64":%"PRIx64 - " hd->pgd=%p\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - context->hi, context->lo, hd->pgd); - - if ( iommu_flush_context_device(iommu, domain_iommu_domid(domain), - (((u16)bus) << 8) | devfn, - DMA_CCMD_MASK_NOBIT, 1) ) - iommu_flush_write_buffer(iommu); - else - iommu_flush_iotlb_dsi(iommu, domain_iommu_domid(domain), 0); - spin_unlock_irqrestore(&iommu->lock, flags); - return ret; -} - -static int __pci_find_next_cap(u8 bus, unsigned int devfn, u8 pos, int cap) -{ - u8 id; - int ttl = 48; - - while ( ttl-- ) - { - pos = read_pci_config_byte(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos); - if ( pos < 0x40 ) - break; - - pos &= ~3; - id = read_pci_config_byte(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - pos + PCI_CAP_LIST_ID); - - if ( id == 0xff ) - break; - if ( id == cap ) - return pos; - - pos += PCI_CAP_LIST_NEXT; - } - return 0; -} - -#define PCI_BASE_CLASS_BRIDGE 0x06 -#define PCI_CLASS_BRIDGE_PCI 0x0604 - -#define DEV_TYPE_PCIe_ENDPOINT 1 -#define DEV_TYPE_PCI_BRIDGE 2 -#define DEV_TYPE_PCI 3 - -int pdev_type(struct pci_dev *dev) -{ - u16 class_device; - u16 status; - - class_device = read_pci_config_16(dev->bus, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), PCI_CLASS_DEVICE); - if ( class_device == PCI_CLASS_BRIDGE_PCI ) - return DEV_TYPE_PCI_BRIDGE; - - status = read_pci_config_16(dev->bus, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), PCI_STATUS); - - if ( !(status & PCI_STATUS_CAP_LIST) ) - return DEV_TYPE_PCI; - - if ( __pci_find_next_cap(dev->bus, dev->devfn, - PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) ) - return DEV_TYPE_PCIe_ENDPOINT; - - return DEV_TYPE_PCI; -} - -#define MAX_BUSES 256 -struct pci_dev bus2bridge[MAX_BUSES]; - -static int domain_context_mapping( - struct domain *domain, - struct iommu *iommu, - struct pci_dev *pdev) -{ - int ret = 0; - int dev, func, sec_bus, sub_bus; - u32 type; - - type = pdev_type(pdev); - switch ( type ) - { - case DEV_TYPE_PCI_BRIDGE: - sec_bus = read_pci_config_byte( - pdev->bus, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn), PCI_SECONDARY_BUS); - - if ( bus2bridge[sec_bus].bus == 0 ) - { - bus2bridge[sec_bus].bus = pdev->bus; - bus2bridge[sec_bus].devfn = pdev->devfn; - } - - sub_bus = read_pci_config_byte( - pdev->bus, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn), PCI_SUBORDINATE_BUS); - - if ( sec_bus != sub_bus ) - gdprintk(XENLOG_WARNING VTDPREFIX, - "context_context_mapping: nested PCI bridge not " - "supported: bdf = %x:%x:%x sec_bus = %x sub_bus = %x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - sec_bus, sub_bus); - break; - case DEV_TYPE_PCIe_ENDPOINT: - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_mapping:PCIe : bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - ret = domain_context_mapping_one(domain, iommu, - (u8)(pdev->bus), (u8)(pdev->devfn)); - break; - case DEV_TYPE_PCI: - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_mapping:PCI: bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - - if ( pdev->bus == 0 ) - ret = domain_context_mapping_one( - domain, iommu, (u8)(pdev->bus), (u8)(pdev->devfn)); - else - { - if ( bus2bridge[pdev->bus].bus != 0 ) - gdprintk(XENLOG_WARNING VTDPREFIX, - "domain_context_mapping:bus2bridge" - "[%d].bus != 0\n", pdev->bus); - - ret = domain_context_mapping_one( - domain, iommu, - (u8)(bus2bridge[pdev->bus].bus), - (u8)(bus2bridge[pdev->bus].devfn)); - - /* now map everything behind the PCI bridge */ - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - ret = domain_context_mapping_one( - domain, iommu, - pdev->bus, (u8)PCI_DEVFN(dev, func)); - if ( ret ) - return ret; - } - } - } - break; - default: - gdprintk(XENLOG_ERR VTDPREFIX, - "domain_context_mapping:unknown type : bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - ret = -EINVAL; - break; - } - - return ret; -} - -static int domain_context_unmap_one( - struct domain *domain, - struct iommu *iommu, - u8 bus, u8 devfn) -{ - struct context_entry *context; - unsigned long flags; - - context = device_to_context_entry(iommu, bus, devfn); - if ( !context ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "domain_context_unmap_one-%x:%x:%x- context == NULL:return\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return -ENOMEM; - } - - if ( !context_present(*context) ) - { - gdprintk(XENLOG_WARNING VTDPREFIX, - "domain_context_unmap_one-%x:%x:%x- " - "context NOT present:return\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return 0; - } - - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_unmap_one: bdf = %x:%x:%x\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - - spin_lock_irqsave(&iommu->lock, flags); - context_clear_present(*context); - context_clear_entry(*context); - iommu_flush_cache_entry(iommu, context); - iommu_flush_context_global(iommu, 0); - iommu_flush_iotlb_global(iommu, 0); - spin_unlock_irqrestore(&iommu->lock, flags); - - return 0; -} - -static int domain_context_unmap( - struct domain *domain, - struct iommu *iommu, - struct pci_dev *pdev) -{ - int ret = 0; - int dev, func, sec_bus, sub_bus; - u32 type; - - type = pdev_type(pdev); - switch ( type ) - { - case DEV_TYPE_PCI_BRIDGE: - sec_bus = read_pci_config_byte( - pdev->bus, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn), PCI_SECONDARY_BUS); - sub_bus = read_pci_config_byte( - pdev->bus, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn), PCI_SUBORDINATE_BUS); - - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_unmap:BRIDGE:%x:%x:%x " - "sec_bus=%x sub_bus=%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn), sec_bus, sub_bus); - break; - case DEV_TYPE_PCIe_ENDPOINT: - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_unmap:PCIe : bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - ret = domain_context_unmap_one(domain, iommu, - (u8)(pdev->bus), (u8)(pdev->devfn)); - break; - case DEV_TYPE_PCI: - gdprintk(XENLOG_INFO VTDPREFIX, - "domain_context_unmap:PCI: bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - if ( pdev->bus == 0 ) - ret = domain_context_unmap_one( - domain, iommu, - (u8)(pdev->bus), (u8)(pdev->devfn)); - else - { - if ( bus2bridge[pdev->bus].bus != 0 ) - gdprintk(XENLOG_WARNING VTDPREFIX, - "domain_context_unmap:" - "bus2bridge[%d].bus != 0\n", pdev->bus); - - ret = domain_context_unmap_one(domain, iommu, - (u8)(bus2bridge[pdev->bus].bus), - (u8)(bus2bridge[pdev->bus].devfn)); - - /* Unmap everything behind the PCI bridge */ - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - ret = domain_context_unmap_one( - domain, iommu, - pdev->bus, (u8)PCI_DEVFN(dev, func)); - if ( ret ) - return ret; - } - } - } - break; - default: - gdprintk(XENLOG_ERR VTDPREFIX, - "domain_context_unmap:unknown type: bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - ret = -EINVAL; - break; - } - - return ret; -} - -void reassign_device_ownership( - struct domain *source, - struct domain *target, - u8 bus, u8 devfn) -{ - struct hvm_iommu *source_hd = domain_hvm_iommu(source); - struct hvm_iommu *target_hd = domain_hvm_iommu(target); - struct pci_dev *pdev; - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - int status; - unsigned long flags; - - gdprintk(XENLOG_INFO VTDPREFIX, - "reassign_device-%x:%x:%x- source = %d target = %d\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - source->domain_id, target->domain_id); - - pdev_flr(bus, devfn); - - for_each_pdev( source, pdev ) - { - if ( (pdev->bus != bus) || (pdev->devfn != devfn) ) - continue; - - drhd = acpi_find_matched_drhd_unit(pdev); - iommu = drhd->iommu; - domain_context_unmap(source, iommu, pdev); - - /* Move pci device from the source domain to target domain. */ - spin_lock_irqsave(&source_hd->iommu_list_lock, flags); - spin_lock_irqsave(&target_hd->iommu_list_lock, flags); - list_move(&pdev->list, &target_hd->pdev_list); - spin_unlock_irqrestore(&target_hd->iommu_list_lock, flags); - spin_unlock_irqrestore(&source_hd->iommu_list_lock, flags); - - status = domain_context_mapping(target, iommu, pdev); - if ( status != 0 ) - gdprintk(XENLOG_ERR VTDPREFIX, "domain_context_mapping failed\n"); - - break; - } -} - -void return_devices_to_dom0(struct domain *d) -{ - struct hvm_iommu *hd = domain_hvm_iommu(d); - struct pci_dev *pdev; - - while ( !list_empty(&hd->pdev_list) ) - { - pdev = list_entry(hd->pdev_list.next, typeof(*pdev), list); - dprintk(XENLOG_INFO VTDPREFIX, - "return_devices_to_dom0: bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - reassign_device_ownership(d, dom0, pdev->bus, pdev->devfn); - } - -#ifdef VTD_DEBUG - for_each_pdev ( dom0, pdev ) - dprintk(XENLOG_INFO VTDPREFIX, - "return_devices_to_dom0:%x: bdf = %x:%x:%x\n", - dom0->domain_id, pdev->bus, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); -#endif -} - -void iommu_domain_teardown(struct domain *d) -{ - if ( list_empty(&acpi_drhd_units) ) - return; - - iommu_domid_release(d); - -#if CONFIG_PAGING_LEVELS == 3 - { - struct hvm_iommu *hd = domain_hvm_iommu(d); - int level = agaw_to_level(hd->agaw); - struct dma_pte *pgd = NULL; - - switch ( level ) - { - case VTD_PAGE_TABLE_LEVEL_3: - if ( hd->pgd ) - free_xenheap_page((void *)hd->pgd); - break; - case VTD_PAGE_TABLE_LEVEL_4: - if ( hd->pgd ) - { - pgd = hd->pgd; - if ( pgd[0].val != 0 ) - free_xenheap_page((void*)maddr_to_virt( - dma_pte_addr(pgd[0]))); - free_xenheap_page((void *)hd->pgd); - } - break; - default: - gdprintk(XENLOG_ERR VTDPREFIX, - "Unsupported p2m table sharing level!\n"); - break; - } - } -#endif - return_devices_to_dom0(d); -} - -static int domain_context_mapped(struct pci_dev *pdev) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - int ret; - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - ret = device_context_mapped(iommu, pdev->bus, pdev->devfn); - if ( ret ) - return ret; - } - - return 0; -} - -int intel_iommu_map_page( - struct domain *d, unsigned long gfn, unsigned long mfn) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - struct dma_pte *pte = NULL; - struct page_info *pg = NULL; - - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - iommu = drhd->iommu; - -#ifdef CONTEXT_PASSTHRU - /* do nothing if dom0 and iommu supports pass thru */ - if ( ecap_pass_thru(iommu->ecap) && (d->domain_id == 0) ) - return 0; -#endif - - pg = addr_to_dma_page(d, (paddr_t)gfn << PAGE_SHIFT_4K); - if ( !pg ) - return -ENOMEM; - pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg)); - pte += gfn & LEVEL_MASK; - dma_set_pte_addr(*pte, (paddr_t)mfn << PAGE_SHIFT_4K); - dma_set_pte_prot(*pte, DMA_PTE_READ | DMA_PTE_WRITE); - iommu_flush_cache_entry(iommu, pte); - unmap_domain_page(pte); - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - if ( cap_caching_mode(iommu->cap) ) - iommu_flush_iotlb_psi(iommu, domain_iommu_domid(d), - (paddr_t)gfn << PAGE_SHIFT_4K, 1, 0); - else if ( cap_rwbf(iommu->cap) ) - iommu_flush_write_buffer(iommu); - } - - return 0; -} - -int intel_iommu_unmap_page(struct domain *d, unsigned long gfn) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - iommu = drhd->iommu; - -#ifdef CONTEXT_PASSTHRU - /* do nothing if dom0 and iommu supports pass thru */ - if ( ecap_pass_thru(iommu->ecap) && (d->domain_id == 0) ) - return 0; -#endif - - dma_pte_clear_one(d, (paddr_t)gfn << PAGE_SHIFT_4K); - - return 0; -} - -int iommu_page_mapping(struct domain *domain, paddr_t iova, - void *hpa, size_t size, int prot) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - unsigned long start_pfn, end_pfn; - struct dma_pte *pte = NULL; - int index; - struct page_info *pg = NULL; - - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - iommu = drhd->iommu; - if ( (prot & (DMA_PTE_READ|DMA_PTE_WRITE)) == 0 ) - return -EINVAL; - iova = (iova >> PAGE_SHIFT_4K) << PAGE_SHIFT_4K; - start_pfn = (unsigned long)(((unsigned long) hpa) >> PAGE_SHIFT_4K); - end_pfn = (unsigned long) - ((PAGE_ALIGN_4K(((unsigned long)hpa) + size)) >> PAGE_SHIFT_4K); - index = 0; - while ( start_pfn < end_pfn ) - { - pg = addr_to_dma_page(domain, iova + PAGE_SIZE_4K * index); - if ( !pg ) - return -ENOMEM; - pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg)); - pte += start_pfn & LEVEL_MASK; - dma_set_pte_addr(*pte, start_pfn << PAGE_SHIFT_4K); - dma_set_pte_prot(*pte, prot); - iommu_flush_cache_entry(iommu, pte); - unmap_domain_page(pte); - start_pfn++; - index++; - } - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - if ( cap_caching_mode(iommu->cap) ) - iommu_flush_iotlb_psi(iommu, domain_iommu_domid(domain), - iova, index, 0); - else if ( cap_rwbf(iommu->cap) ) - iommu_flush_write_buffer(iommu); - } - - return 0; -} - -int iommu_page_unmapping(struct domain *domain, paddr_t addr, size_t size) -{ - dma_pte_clear_range(domain, addr, addr + size); - - return 0; -} - -void iommu_flush(struct domain *d, unsigned long gfn, u64 *p2m_entry) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu = NULL; - struct dma_pte *pte = (struct dma_pte *) p2m_entry; - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - if ( cap_caching_mode(iommu->cap) ) - iommu_flush_iotlb_psi(iommu, domain_iommu_domid(d), - (paddr_t)gfn << PAGE_SHIFT_4K, 1, 0); - else if ( cap_rwbf(iommu->cap) ) - iommu_flush_write_buffer(iommu); - } - - iommu_flush_cache_entry(iommu, pte); -} - -static int iommu_prepare_rmrr_dev( - struct domain *d, - struct acpi_rmrr_unit *rmrr, - struct pci_dev *pdev) -{ - struct acpi_drhd_unit *drhd; - unsigned long size; - int ret; - - /* page table init */ - size = rmrr->end_address - rmrr->base_address + 1; - ret = iommu_page_mapping(d, rmrr->base_address, - (void *)rmrr->base_address, size, - DMA_PTE_READ|DMA_PTE_WRITE); - if ( ret ) - return ret; - - if ( domain_context_mapped(pdev) == 0 ) - { - drhd = acpi_find_matched_drhd_unit(pdev); - ret = domain_context_mapping(d, drhd->iommu, pdev); - if ( !ret ) - return 0; - } - - return ret; -} - -void __init setup_dom0_devices(void) -{ - struct hvm_iommu *hd = domain_hvm_iommu(dom0); - struct acpi_drhd_unit *drhd; - struct pci_dev *pdev; - int bus, dev, func, ret; - u32 l; - -#ifdef DEBUG_VTD_CONTEXT_ENTRY - for ( bus = 0; bus < 256; bus++ ) - { - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - struct context_entry *context; - struct pci_dev device; - - device.bus = bus; - device.devfn = PCI_DEVFN(dev, func); - drhd = acpi_find_matched_drhd_unit(&device); - context = device_to_context_entry(drhd->iommu, - bus, PCI_DEVFN(dev, func)); - if ( (context->lo != 0) || (context->hi != 0) ) - dprintk(XENLOG_INFO VTDPREFIX, - "setup_dom0_devices-%x:%x:%x- context not 0\n", - bus, dev, func); - } - } - } -#endif - - for ( bus = 0; bus < 256; bus++ ) - { - for ( dev = 0; dev < 32; dev++ ) - { - for ( func = 0; func < 8; func++ ) - { - l = read_pci_config(bus, dev, func, PCI_VENDOR_ID); - /* some broken boards return 0 or ~0 if a slot is empty: */ - if ( (l == 0xffffffff) || (l == 0x00000000) || - (l == 0x0000ffff) || (l == 0xffff0000) ) - continue; - pdev = xmalloc(struct pci_dev); - pdev->bus = bus; - pdev->devfn = PCI_DEVFN(dev, func); - list_add_tail(&pdev->list, &hd->pdev_list); - - drhd = acpi_find_matched_drhd_unit(pdev); - ret = domain_context_mapping(dom0, drhd->iommu, pdev); - if ( ret != 0 ) - gdprintk(XENLOG_ERR VTDPREFIX, - "domain_context_mapping failed\n"); - } - } - } - - for_each_pdev ( dom0, pdev ) - dprintk(XENLOG_INFO VTDPREFIX, - "setup_dom0_devices: bdf = %x:%x:%x\n", - pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); -} - -void clear_fault_bits(struct iommu *iommu) -{ - u64 val; - - val = dmar_readq( - iommu->reg, - cap_fault_reg_offset(dmar_readq(iommu->reg,DMAR_CAP_REG))+0x8); - dmar_writeq( - iommu->reg, - cap_fault_reg_offset(dmar_readq(iommu->reg,DMAR_CAP_REG))+8, - val); - dmar_writel(iommu->reg, DMAR_FSTS_REG, DMA_FSTS_FAULTS); -} - -static int init_vtd_hw(void) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - struct iommu_flush *flush = NULL; - int vector; - int ret; - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - ret = iommu_set_root_entry(iommu); - if ( ret ) - { - gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: set root entry failed\n"); - return -EIO; - } - - vector = iommu_set_interrupt(iommu); - dma_msi_data_init(iommu, vector); - dma_msi_addr_init(iommu, cpu_physical_id(first_cpu(cpu_online_map))); - iommu->vector = vector; - clear_fault_bits(iommu); - dmar_writel(iommu->reg, DMAR_FECTL_REG, 0); - - /* initialize flush functions */ - flush = iommu_get_flush(iommu); - flush->context = flush_context_reg; - flush->iotlb = flush_iotlb_reg; - } - return 0; -} - -static int init_vtd2_hw(void) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - if ( qinval_setup(iommu) != 0 ) - dprintk(XENLOG_ERR VTDPREFIX, - "Queued Invalidation hardware not found\n"); - - if ( intremap_setup(iommu) != 0 ) - dprintk(XENLOG_ERR VTDPREFIX, - "Interrupt Remapping hardware not found\n"); - } - return 0; -} - -static int enable_vtd_translation(void) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - if ( iommu_enable_translation(iommu) ) - return -EIO; - } - return 0; -} - -static void setup_dom0_rmrr(void) -{ - struct acpi_rmrr_unit *rmrr; - struct pci_dev *pdev; - int ret; - - for_each_rmrr_device ( rmrr, pdev ) - ret = iommu_prepare_rmrr_dev(dom0, rmrr, pdev); - if ( ret ) - gdprintk(XENLOG_ERR VTDPREFIX, - "IOMMU: mapping reserved region failed\n"); - end_for_each_rmrr_device ( rmrr, pdev ) -} - -int iommu_setup(void) -{ - struct hvm_iommu *hd = domain_hvm_iommu(dom0); - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - unsigned long i; - - if ( !vtd_enabled ) - return 0; - - spin_lock_init(&domid_bitmap_lock); - INIT_LIST_HEAD(&hd->pdev_list); - - /* setup clflush size */ - x86_clflush_size = ((cpuid_ebx(1) >> 8) & 0xff) * 8; - - /* Allocate IO page directory page for the domain. */ - drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list); - iommu = drhd->iommu; - - /* Allocate domain id bitmap, and set bit 0 as reserved */ - domid_bitmap_size = cap_ndoms(iommu->cap); - domid_bitmap = xmalloc_bytes(domid_bitmap_size / 8); - if ( domid_bitmap == NULL ) - goto error; - memset(domid_bitmap, 0, domid_bitmap_size / 8); - set_bit(0, domid_bitmap); - - /* setup 1:1 page table for dom0 */ - for ( i = 0; i < max_page; i++ ) - iommu_map_page(dom0, i, i); - - init_vtd_hw(); - setup_dom0_devices(); - setup_dom0_rmrr(); - iommu_flush_all(); - enable_vtd_translation(); - init_vtd2_hw(); - - return 0; - - error: - printk("iommu_setup() failed\n"); - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - free_iommu(iommu); - } - return -EIO; -} - -/* - * If the device isn't owned by dom0, it means it already - * has been assigned to other domain, or it's not exist. - */ -int device_assigned(u8 bus, u8 devfn) -{ - struct pci_dev *pdev; - - for_each_pdev( dom0, pdev ) - if ( (pdev->bus == bus ) && (pdev->devfn == devfn) ) - return 0; - - return 1; -} - -int intel_iommu_assign_device(struct domain *d, u8 bus, u8 devfn) -{ - struct acpi_rmrr_unit *rmrr; - struct pci_dev *pdev; - int ret = 0; - - if ( list_empty(&acpi_drhd_units) ) - return ret; - - gdprintk(XENLOG_INFO VTDPREFIX, - "assign_device: bus = %x dev = %x func = %x\n", - bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - - reassign_device_ownership(dom0, d, bus, devfn); - - /* Setup rmrr identify mapping */ - for_each_rmrr_device( rmrr, pdev ) - if ( pdev->bus == bus && pdev->devfn == devfn ) - { - ret = iommu_prepare_rmrr_dev(d, rmrr, pdev); - if ( ret ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "IOMMU: mapping reserved region failed\n"); - return ret; - } - } - end_for_each_rmrr_device(rmrr, pdev) - - return ret; -} - -void iommu_set_pgd(struct domain *d) -{ - struct hvm_iommu *hd = domain_hvm_iommu(d); - unsigned long p2m_table; - - if ( hd->pgd ) - { - gdprintk(XENLOG_INFO VTDPREFIX, - "iommu_set_pgd_1: hd->pgd = %p\n", hd->pgd); - hd->pgd = NULL; - } - p2m_table = mfn_x(pagetable_get_mfn(d->arch.phys_table)); - -#if CONFIG_PAGING_LEVELS == 3 - if ( !hd->pgd ) - { - int level = agaw_to_level(hd->agaw); - struct dma_pte *pmd = NULL; - struct dma_pte *pgd = NULL; - struct dma_pte *pte = NULL; - l3_pgentry_t *l3e; - unsigned long flags; - int i; - - spin_lock_irqsave(&hd->mapping_lock, flags); - if ( !hd->pgd ) - { - pgd = (struct dma_pte *)alloc_xenheap_page(); - if ( !pgd ) - { - spin_unlock_irqrestore(&hd->mapping_lock, flags); - gdprintk(XENLOG_ERR VTDPREFIX, - "Allocate pgd memory failed!\n"); - return; - } - memset(pgd, 0, PAGE_SIZE); - hd->pgd = pgd; - } - - l3e = map_domain_page(p2m_table); - switch ( level ) - { - case VTD_PAGE_TABLE_LEVEL_3: /* Weybridge */ - /* We only support 8 entries for the PAE L3 p2m table */ - for ( i = 0; i < 8 ; i++ ) - { - /* Don't create new L2 entry, use ones from p2m table */ - pgd[i].val = l3e[i].l3 | _PAGE_PRESENT | _PAGE_RW; - } - break; - - case VTD_PAGE_TABLE_LEVEL_4: /* Stoakley */ - /* We allocate one more page for the top vtd page table. */ - pmd = (struct dma_pte *)alloc_xenheap_page(); - if ( !pmd ) - { - unmap_domain_page(l3e); - spin_unlock_irqrestore(&hd->mapping_lock, flags); - gdprintk(XENLOG_ERR VTDPREFIX, - "Allocate pmd memory failed!\n"); - return; - } - memset((u8*)pmd, 0, PAGE_SIZE); - pte = &pgd[0]; - dma_set_pte_addr(*pte, virt_to_maddr(pmd)); - dma_set_pte_readable(*pte); - dma_set_pte_writable(*pte); - - for ( i = 0; i < 8; i++ ) - { - /* Don't create new L2 entry, use ones from p2m table */ - pmd[i].val = l3e[i].l3 | _PAGE_PRESENT | _PAGE_RW; - } - break; - default: - gdprintk(XENLOG_ERR VTDPREFIX, - "iommu_set_pgd:Unsupported p2m table sharing level!\n"); - break; - } - unmap_domain_page(l3e); - spin_unlock_irqrestore(&hd->mapping_lock, flags); - } -#elif CONFIG_PAGING_LEVELS == 4 - if ( !hd->pgd ) - { - int level = agaw_to_level(hd->agaw); - l3_pgentry_t *l3e; - mfn_t pgd_mfn; - - switch ( level ) - { - case VTD_PAGE_TABLE_LEVEL_3: - l3e = map_domain_page(p2m_table); - if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 ) - { - gdprintk(XENLOG_ERR VTDPREFIX, - "iommu_set_pgd: second level wasn't there\n"); - unmap_domain_page(l3e); - return; - } - pgd_mfn = _mfn(l3e_get_pfn(*l3e)); - unmap_domain_page(l3e); - hd->pgd = maddr_to_virt(pagetable_get_paddr( - pagetable_from_mfn(pgd_mfn))); - break; - - case VTD_PAGE_TABLE_LEVEL_4: - pgd_mfn = _mfn(p2m_table); - hd->pgd = maddr_to_virt(pagetable_get_paddr( - pagetable_from_mfn(pgd_mfn))); - break; - default: - gdprintk(XENLOG_ERR VTDPREFIX, - "iommu_set_pgd:Unsupported p2m table sharing level!\n"); - break; - } - } -#endif - gdprintk(XENLOG_INFO VTDPREFIX, - "iommu_set_pgd: hd->pgd = %p\n", hd->pgd); -} - - -u8 iommu_state[MAX_IOMMU_REGS * MAX_IOMMUS]; -int iommu_suspend(void) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - int i = 0; - - iommu_flush_all(); - - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - iommu_state[DMAR_RTADDR_REG * i] = - (u64) dmar_readq(iommu->reg, DMAR_RTADDR_REG); - iommu_state[DMAR_FECTL_REG * i] = - (u32) dmar_readl(iommu->reg, DMAR_FECTL_REG); - iommu_state[DMAR_FEDATA_REG * i] = - (u32) dmar_readl(iommu->reg, DMAR_FEDATA_REG); - iommu_state[DMAR_FEADDR_REG * i] = - (u32) dmar_readl(iommu->reg, DMAR_FEADDR_REG); - iommu_state[DMAR_FEUADDR_REG * i] = - (u32) dmar_readl(iommu->reg, DMAR_FEUADDR_REG); - iommu_state[DMAR_PLMBASE_REG * i] = - (u32) dmar_readl(iommu->reg, DMAR_PLMBASE_REG); - iommu_state[DMAR_PLMLIMIT_REG * i] = - (u32) dmar_readl(iommu->reg, DMAR_PLMLIMIT_REG); - iommu_state[DMAR_PHMBASE_REG * i] = - (u64) dmar_readq(iommu->reg, DMAR_PHMBASE_REG); - iommu_state[DMAR_PHMLIMIT_REG * i] = - (u64) dmar_readq(iommu->reg, DMAR_PHMLIMIT_REG); - i++; - } - - return 0; -} - -int iommu_resume(void) -{ - struct acpi_drhd_unit *drhd; - struct iommu *iommu; - int i = 0; - - iommu_flush_all(); - - init_vtd_hw(); - for_each_drhd_unit ( drhd ) - { - iommu = drhd->iommu; - dmar_writeq( iommu->reg, DMAR_RTADDR_REG, - (u64) iommu_state[DMAR_RTADDR_REG * i]); - dmar_writel(iommu->reg, DMAR_FECTL_REG, - (u32) iommu_state[DMAR_FECTL_REG * i]); - dmar_writel(iommu->reg, DMAR_FEDATA_REG, - (u32) iommu_state[DMAR_FEDATA_REG * i]); - dmar_writel(iommu->reg, DMAR_FEADDR_REG, - (u32) iommu_state[DMAR_FEADDR_REG * i]); - dmar_writel(iommu->reg, DMAR_FEUADDR_REG, - (u32) iommu_state[DMAR_FEUADDR_REG * i]); - dmar_writel(iommu->reg, DMAR_PLMBASE_REG, - (u32) iommu_state[DMAR_PLMBASE_REG * i]); - dmar_writel(iommu->reg, DMAR_PLMLIMIT_REG, - (u32) iommu_state[DMAR_PLMLIMIT_REG * i]); - dmar_writeq(iommu->reg, DMAR_PHMBASE_REG, - (u64) iommu_state[DMAR_PHMBASE_REG * i]); - dmar_writeq(iommu->reg, DMAR_PHMLIMIT_REG, - (u64) iommu_state[DMAR_PHMLIMIT_REG * i]); - - if ( iommu_enable_translation(iommu) ) - return -EIO; - i++; - } - return 0; -} - -struct iommu_ops intel_iommu_ops = { - .init = intel_iommu_domain_init, - .assign_device = intel_iommu_assign_device, - .teardown = iommu_domain_teardown, - .map_page = intel_iommu_map_page, - .unmap_page = intel_iommu_unmap_page, - .reassign_device = reassign_device_ownership, -}; - -/* - * Local variables: - * mode: C - * c-set-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/intremap.c --- a/xen/arch/x86/hvm/vmx/vtd/intremap.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - * Copyright (C) Xiaohui Xin <xiaohui.xin@xxxxxxxxx> - */ - -#include <xen/config.h> -#include <xen/lib.h> -#include <xen/init.h> -#include <xen/irq.h> -#include <xen/delay.h> -#include <xen/sched.h> -#include <xen/acpi.h> -#include <xen/keyhandler.h> -#include <xen/spinlock.h> -#include <asm/io.h> -#include <asm/mc146818rtc.h> -#include <asm/smp.h> -#include <asm/desc.h> -#include <mach_apic.h> -#include <io_ports.h> - -#include <xen/spinlock.h> -#include <xen/xmalloc.h> -#include <xen/domain_page.h> -#include <asm/delay.h> -#include <asm/string.h> -#include <asm/iommu.h> -#include <asm/hvm/vmx/intel-iommu.h> -#include "dmar.h" -#include "vtd.h" -#include "pci-direct.h" -#include "pci_regs.h" -#include "msi.h" -#include "extern.h" - -u16 apicid_to_bdf(int apic_id) -{ - struct acpi_drhd_unit *drhd = ioapic_to_drhd(apic_id); - struct acpi_ioapic_unit *acpi_ioapic_unit; - - list_for_each_entry ( acpi_ioapic_unit, &drhd->ioapic_list, list ) - if ( acpi_ioapic_unit->apic_id == apic_id ) - return acpi_ioapic_unit->ioapic.info; - - dprintk(XENLOG_ERR VTDPREFIX, "Didn't find the bdf for the apic_id!\n"); - return 0; -} - -static void remap_entry_to_ioapic_rte( - struct iommu *iommu, struct IO_APIC_route_entry *old_rte) -{ - struct iremap_entry *iremap_entry = NULL; - struct IO_APIC_route_remap_entry *remap_rte; - unsigned int index; - unsigned long flags; - struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); - - if ( ir_ctrl == NULL ) - { - dprintk(XENLOG_ERR VTDPREFIX, - "remap_entry_to_ioapic_rte: ir_ctl == NULL"); - return; - } - - remap_rte = (struct IO_APIC_route_remap_entry *) old_rte; - index = (remap_rte->index_15 << 15) + remap_rte->index_0_14; - - if ( index > ir_ctrl->iremap_index ) - { - dprintk(XENLOG_ERR VTDPREFIX, - "Index is larger than remap table entry size. Error!\n"); - return; - } - - spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); - - iremap_entry = &ir_ctrl->iremap[index]; - - old_rte->vector = iremap_entry->lo.vector; - old_rte->delivery_mode = iremap_entry->lo.dlm; - old_rte->dest_mode = iremap_entry->lo.dm; - old_rte->trigger = iremap_entry->lo.tm; - old_rte->__reserved_2 = 0; - old_rte->dest.logical.__reserved_1 = 0; - old_rte->dest.logical.logical_dest = iremap_entry->lo.dst; - - spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); -} - -static void ioapic_rte_to_remap_entry(struct iommu *iommu, - int apic_id, struct IO_APIC_route_entry *old_rte) -{ - struct iremap_entry *iremap_entry = NULL; - struct IO_APIC_route_remap_entry *remap_rte; - unsigned int index; - unsigned long flags; - int ret = 0; - struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); - - remap_rte = (struct IO_APIC_route_remap_entry *) old_rte; - spin_lock_irqsave(&ir_ctrl->iremap_lock, flags); - index = ir_ctrl->iremap_index; - if ( index > IREMAP_ENTRY_NR - 1 ) - { - dprintk(XENLOG_ERR VTDPREFIX, - "The interrupt number is more than 256!\n"); - goto out; - } - - iremap_entry = &(ir_ctrl->iremap[index]); - if ( *(u64 *)iremap_entry != 0 ) - dprintk(XENLOG_WARNING VTDPREFIX, - "Interrupt remapping entry is in use already!\n"); - iremap_entry->lo.fpd = 0; - iremap_entry->lo.dm = old_rte->dest_mode; - iremap_entry->lo.rh = 0; - iremap_entry->lo.tm = old_rte->trigger; - iremap_entry->lo.dlm = old_rte->delivery_mode; - iremap_entry->lo.avail = 0; - iremap_entry->lo.res_1 = 0; - iremap_entry->lo.vector = old_rte->vector; - iremap_entry->lo.res_2 = 0; - iremap_entry->lo.dst = (old_rte->dest.logical.logical_dest << 8); - iremap_entry->hi.sid = apicid_to_bdf(apic_id); - iremap_entry->hi.sq = 0; /* comparing all 16-bit of SID */ - iremap_entry->hi.svt = 1; /* turn on requestor ID verification SID/SQ */ - iremap_entry->hi.res_1 = 0; - iremap_entry->lo.p = 1; /* finally, set present bit */ - ir_ctrl->iremap_index++; - - iommu_flush_iec_index(iommu, 0, index); - ret = invalidate_sync(iommu); - - /* now construct new ioapic rte entry */ - remap_rte->vector = old_rte->vector; - remap_rte->delivery_mode = 0; /* has to be 0 for remap format */ - remap_rte->index_15 = index & 0x8000; - remap_rte->index_0_14 = index & 0x7fff; - remap_rte->delivery_status = old_rte->delivery_status; - remap_rte->polarity = old_rte->polarity; - remap_rte->irr = old_rte->irr; - remap_rte->trigger = old_rte->trigger; - remap_rte->mask = 1; - remap_rte->reserved = 0; - remap_rte->format = 1; /* indicate remap format */ -out: - spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags); - return; -} - -unsigned int -io_apic_read_remap_rte( - unsigned int apic, unsigned int reg) -{ - struct IO_APIC_route_entry old_rte = { 0 }; - struct IO_APIC_route_remap_entry *remap_rte; - int rte_upper = (reg & 1) ? 1 : 0; - struct iommu *iommu = ioapic_to_iommu(mp_ioapics[apic].mpc_apicid); - struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); - - if ( !iommu || !(ir_ctrl->iremap) ) - { - *IO_APIC_BASE(apic) = reg; - return *(IO_APIC_BASE(apic)+4); - } - - if ( rte_upper ) - reg--; - - /* read lower and upper 32-bits of rte entry */ - *IO_APIC_BASE(apic) = reg; - *(((u32 *)&old_rte) + 0) = *(IO_APIC_BASE(apic)+4); - *IO_APIC_BASE(apic) = reg + 1; - *(((u32 *)&old_rte) + 1) = *(IO_APIC_BASE(apic)+4); - - remap_rte = (struct IO_APIC_route_remap_entry *) &old_rte; - - if ( remap_rte->mask || (remap_rte->format == 0) ) - { - *IO_APIC_BASE(apic) = reg; - return *(IO_APIC_BASE(apic)+4); - } - - remap_entry_to_ioapic_rte(iommu, &old_rte); - if ( rte_upper ) - { - *IO_APIC_BASE(apic) = reg + 1; - return (*(((u32 *)&old_rte) + 1)); - } - else - { - *IO_APIC_BASE(apic) = reg; - return (*(((u32 *)&old_rte) + 0)); - } -} - -void -io_apic_write_remap_rte( - unsigned int apic, unsigned int reg, unsigned int value) -{ - struct IO_APIC_route_entry old_rte = { 0 }; - struct IO_APIC_route_remap_entry *remap_rte; - int rte_upper = (reg & 1) ? 1 : 0; - struct iommu *iommu = ioapic_to_iommu(mp_ioapics[apic].mpc_apicid); - struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu); - - if ( !iommu || !(ir_ctrl->iremap) ) - { - *IO_APIC_BASE(apic) = reg; - *(IO_APIC_BASE(apic)+4) = value; - return; - } - - if ( rte_upper ) - reg--; - - /* read both lower and upper 32-bits of rte entry */ - *IO_APIC_BASE(apic) = reg; - *(((u32 *)&old_rte) + 0) = *(IO_APIC_BASE(apic)+4); - *IO_APIC_BASE(apic) = reg + 1; - *(((u32 *)&old_rte) + 1) = *(IO_APIC_BASE(apic)+4); - - remap_rte = (struct IO_APIC_route_remap_entry *) &old_rte; - if ( remap_rte->mask || (remap_rte->format == 0) ) - { - *IO_APIC_BASE(apic) = rte_upper ? ++reg : reg; - *(IO_APIC_BASE(apic)+4) = value; - return; - } - - *(((u32 *)&old_rte) + rte_upper) = value; - ioapic_rte_to_remap_entry(iommu, mp_ioapics[apic].mpc_apicid, &old_rte); - - /* write new entry to ioapic */ - *IO_APIC_BASE(apic) = reg; - *(IO_APIC_BASE(apic)+4) = *(((int *)&old_rte)+0); - *IO_APIC_BASE(apic) = reg + 1; - *(IO_APIC_BASE(apic)+4) = *(((int *)&old_rte)+1); -} - -int intremap_setup(struct iommu *iommu) -{ - struct ir_ctrl *ir_ctrl; - unsigned long start_time; - u64 paddr; - - if ( !ecap_intr_remap(iommu->ecap) ) - return -ENODEV; - - ir_ctrl = iommu_ir_ctrl(iommu); - if ( ir_ctrl->iremap == NULL ) - { - ir_ctrl->iremap = alloc_xenheap_page(); - if ( ir_ctrl->iremap == NULL ) - { - dprintk(XENLOG_WARNING VTDPREFIX, - "Cannot allocate memory for ir_ctrl->iremap\n"); - return -ENODEV; - } - memset(ir_ctrl->iremap, 0, PAGE_SIZE); - } - - paddr = virt_to_maddr(ir_ctrl->iremap); -#if defined(ENABLED_EXTENDED_INTERRUPT_SUPPORT) - /* set extended interrupt mode bit */ - paddr |= ecap_ext_intr(iommu->ecap) ? (1 << IRTA_REG_EIMI_SHIFT) : 0; -#endif - /* size field = 256 entries per 4K page = 8 - 1 */ - paddr |= 7; - dmar_writeq(iommu->reg, DMAR_IRTA_REG, paddr); - - /* set SIRTP */ - iommu->gcmd |= DMA_GCMD_SIRTP; - dmar_writel(iommu->reg, DMAR_GCMD_REG, iommu->gcmd); - - /* Make sure hardware complete it */ - start_time = jiffies; - while ( !(dmar_readl(iommu->reg, DMAR_GSTS_REG) & DMA_GSTS_SIRTPS) ) - { - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - { - dprintk(XENLOG_ERR VTDPREFIX, - "Cannot set SIRTP field for interrupt remapping\n"); - return -ENODEV; - } - cpu_relax(); - } - - /* enable comaptiblity format interrupt pass through */ - iommu->gcmd |= DMA_GCMD_CFI; - dmar_writel(iommu->reg, DMAR_GCMD_REG, iommu->gcmd); - - start_time = jiffies; - while ( !(dmar_readl(iommu->reg, DMAR_GSTS_REG) & DMA_GSTS_CFIS) ) - { - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - { - dprintk(XENLOG_ERR VTDPREFIX, - "Cannot set CFI field for interrupt remapping\n"); - return -ENODEV; - } - cpu_relax(); - } - - /* enable interrupt remapping hardware */ - iommu->gcmd |= DMA_GCMD_IRE; - dmar_writel(iommu->reg, DMAR_GCMD_REG, iommu->gcmd); - - start_time = jiffies; - while ( !(dmar_readl(iommu->reg, DMAR_GSTS_REG) & DMA_GSTS_IRES) ) - { - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - { - dprintk(XENLOG_ERR VTDPREFIX, - "Cannot set IRE field for interrupt remapping\n"); - return -ENODEV; - } - cpu_relax(); - } - - /* After set SIRTP, we should do globally invalidate the IEC */ - iommu_flush_iec_global(iommu); - - return 0; -} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/io.c --- a/xen/arch/x86/hvm/vmx/vtd/io.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - * Copyright (C) Xiaohui Xin <xiaohui.xin@xxxxxxxxx> - */ - -#include <xen/init.h> -#include <xen/config.h> -#include <xen/init.h> -#include <xen/mm.h> -#include <xen/lib.h> -#include <xen/errno.h> -#include <xen/trace.h> -#include <xen/event.h> -#include <xen/hypercall.h> -#include <asm/current.h> -#include <asm/cpufeature.h> -#include <asm/processor.h> -#include <asm/msr.h> -#include <asm/apic.h> -#include <asm/paging.h> -#include <asm/shadow.h> -#include <asm/p2m.h> -#include <asm/hvm/hvm.h> -#include <asm/hvm/support.h> -#include <asm/hvm/vpt.h> -#include <asm/hvm/vpic.h> -#include <asm/hvm/vlapic.h> -#include <public/sched.h> -#include <xen/iocap.h> -#include <public/hvm/ioreq.h> -#include <public/domctl.h> - -static void pt_irq_time_out(void *data) -{ - struct hvm_mirq_dpci_mapping *irq_map = data; - unsigned int guest_gsi, machine_gsi = 0; - struct hvm_irq_dpci *dpci = irq_map->dom->arch.hvm_domain.irq.dpci; - struct dev_intx_gsi_link *digl; - uint32_t device, intx; - - list_for_each_entry ( digl, &irq_map->digl_list, list ) - { - guest_gsi = digl->gsi; - machine_gsi = dpci->girq[guest_gsi].machine_gsi; - device = digl->device; - intx = digl->intx; - hvm_pci_intx_deassert(irq_map->dom, device, intx); - } - - clear_bit(machine_gsi, dpci->dirq_mask); - stop_timer(&dpci->hvm_timer[irq_to_vector(machine_gsi)]); - spin_lock(&dpci->dirq_lock); - dpci->mirq[machine_gsi].pending = 0; - spin_unlock(&dpci->dirq_lock); - pirq_guest_eoi(irq_map->dom, machine_gsi); -} - -int pt_irq_create_bind_vtd( - struct domain *d, xen_domctl_bind_pt_irq_t *pt_irq_bind) -{ - struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; - uint32_t machine_gsi, guest_gsi; - uint32_t device, intx, link; - struct dev_intx_gsi_link *digl; - - if ( hvm_irq_dpci == NULL ) - { - hvm_irq_dpci = xmalloc(struct hvm_irq_dpci); - if ( hvm_irq_dpci == NULL ) - return -ENOMEM; - - memset(hvm_irq_dpci, 0, sizeof(*hvm_irq_dpci)); - spin_lock_init(&hvm_irq_dpci->dirq_lock); - for ( int i = 0; i < NR_IRQS; i++ ) - INIT_LIST_HEAD(&hvm_irq_dpci->mirq[i].digl_list); - - if ( cmpxchg((unsigned long *)&d->arch.hvm_domain.irq.dpci, - 0, (unsigned long)hvm_irq_dpci) != 0 ) - xfree(hvm_irq_dpci); - - hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; - } - - machine_gsi = pt_irq_bind->machine_irq; - device = pt_irq_bind->u.pci.device; - intx = pt_irq_bind->u.pci.intx; - guest_gsi = hvm_pci_intx_gsi(device, intx); - link = hvm_pci_intx_link(device, intx); - hvm_irq_dpci->link_cnt[link]++; - - digl = xmalloc(struct dev_intx_gsi_link); - if ( !digl ) - return -ENOMEM; - - digl->device = device; - digl->intx = intx; - digl->gsi = guest_gsi; - digl->link = link; - list_add_tail(&digl->list, - &hvm_irq_dpci->mirq[machine_gsi].digl_list); - - hvm_irq_dpci->girq[guest_gsi].valid = 1; - hvm_irq_dpci->girq[guest_gsi].device = device; - hvm_irq_dpci->girq[guest_gsi].intx = intx; - hvm_irq_dpci->girq[guest_gsi].machine_gsi = machine_gsi; - - /* Bind the same mirq once in the same domain */ - if ( !hvm_irq_dpci->mirq[machine_gsi].valid ) - { - hvm_irq_dpci->mirq[machine_gsi].valid = 1; - hvm_irq_dpci->mirq[machine_gsi].dom = d; - - init_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(machine_gsi)], - pt_irq_time_out, &hvm_irq_dpci->mirq[machine_gsi], 0); - /* Deal with gsi for legacy devices */ - pirq_guest_bind(d->vcpu[0], machine_gsi, BIND_PIRQ__WILL_SHARE); - } - - gdprintk(XENLOG_INFO VTDPREFIX, - "VT-d irq bind: m_irq = %x device = %x intx = %x\n", - machine_gsi, device, intx); - return 0; -} - -int pt_irq_destroy_bind_vtd( - struct domain *d, xen_domctl_bind_pt_irq_t *pt_irq_bind) -{ - struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; - uint32_t machine_gsi, guest_gsi; - uint32_t device, intx, link; - struct list_head *digl_list, *tmp; - struct dev_intx_gsi_link *digl; - - if ( hvm_irq_dpci == NULL ) - return 0; - - machine_gsi = pt_irq_bind->machine_irq; - device = pt_irq_bind->u.pci.device; - intx = pt_irq_bind->u.pci.intx; - guest_gsi = hvm_pci_intx_gsi(device, intx); - link = hvm_pci_intx_link(device, intx); - hvm_irq_dpci->link_cnt[link]--; - - gdprintk(XENLOG_INFO, - "pt_irq_destroy_bind_vtd: machine_gsi=%d, guest_gsi=%d, device=%d, intx=%d.\n", - machine_gsi, guest_gsi, device, intx); - memset(&hvm_irq_dpci->girq[guest_gsi], 0, sizeof(struct hvm_girq_dpci_mapping)); - - /* clear the mirq info */ - if ( hvm_irq_dpci->mirq[machine_gsi].valid ) - { - - list_for_each_safe ( digl_list, tmp, - &hvm_irq_dpci->mirq[machine_gsi].digl_list ) - { - digl = list_entry(digl_list, - struct dev_intx_gsi_link, list); - if ( digl->device == device && - digl->intx == intx && - digl->link == link && - digl->gsi == guest_gsi ) - { - list_del(&digl->list); - xfree(digl); - } - } - - if ( list_empty(&hvm_irq_dpci->mirq[machine_gsi].digl_list) ) - { - pirq_guest_unbind(d, machine_gsi); - kill_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(machine_gsi)]); - hvm_irq_dpci->mirq[machine_gsi].dom = NULL; - hvm_irq_dpci->mirq[machine_gsi].valid = 0; - } - } - - gdprintk(XENLOG_INFO, - "XEN_DOMCTL_irq_unmapping: m_irq = %x device = %x intx = %x\n", - machine_gsi, device, intx); - - return 0; -} - -int hvm_do_IRQ_dpci(struct domain *d, unsigned int mirq) -{ - struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq; - - if ( !iommu_enabled || (d == dom0) || (hvm_irq->dpci == NULL) || - !hvm_irq->dpci->mirq[mirq].valid ) - return 0; - - /* - * Set a timer here to avoid situations where the IRQ line is shared, and - * the device belonging to the pass-through guest is not yet active. In - * this case the guest may not pick up the interrupt (e.g., masked at the - * PIC) and we need to detect that. - */ - set_bit(mirq, hvm_irq->dpci->dirq_mask); - set_timer(&hvm_irq->dpci->hvm_timer[irq_to_vector(mirq)], - NOW() + PT_IRQ_TIME_OUT); - vcpu_kick(d->vcpu[0]); - - return 1; -} - -static void hvm_dpci_isairq_eoi(struct domain *d, unsigned int isairq) -{ - struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq; - struct hvm_irq_dpci *dpci = hvm_irq->dpci; - struct dev_intx_gsi_link *digl, *tmp; - int i; - - ASSERT(isairq < NR_ISAIRQS); - if ( !iommu_enabled || !dpci || - !test_bit(isairq, dpci->isairq_map) ) - return; - - /* Multiple mirq may be mapped to one isa irq */ - for ( i = 0; i < NR_IRQS; i++ ) - { - if ( !dpci->mirq[i].valid ) - continue; - - list_for_each_entry_safe ( digl, tmp, - &dpci->mirq[i].digl_list, list ) - { - if ( hvm_irq->pci_link.route[digl->link] == isairq ) - { - hvm_pci_intx_deassert(d, digl->device, digl->intx); - spin_lock(&dpci->dirq_lock); - if ( --dpci->mirq[i].pending == 0 ) - { - spin_unlock(&dpci->dirq_lock); - gdprintk(XENLOG_INFO VTDPREFIX, - "hvm_dpci_isairq_eoi:: mirq = %x\n", i); - stop_timer(&dpci->hvm_timer[irq_to_vector(i)]); - pirq_guest_eoi(d, i); - } - else - spin_unlock(&dpci->dirq_lock); - } - } - } -} - -void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi, - union vioapic_redir_entry *ent) -{ - struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; - uint32_t device, intx, machine_gsi; - - if ( !iommu_enabled || (hvm_irq_dpci == NULL) || - (guest_gsi >= NR_ISAIRQS && - !hvm_irq_dpci->girq[guest_gsi].valid) ) - return; - - if ( guest_gsi < NR_ISAIRQS ) - { - hvm_dpci_isairq_eoi(d, guest_gsi); - return; - } - - machine_gsi = hvm_irq_dpci->girq[guest_gsi].machine_gsi; - device = hvm_irq_dpci->girq[guest_gsi].device; - intx = hvm_irq_dpci->girq[guest_gsi].intx; - hvm_pci_intx_deassert(d, device, intx); - - spin_lock(&hvm_irq_dpci->dirq_lock); - if ( --hvm_irq_dpci->mirq[machine_gsi].pending == 0 ) - { - spin_unlock(&hvm_irq_dpci->dirq_lock); - - gdprintk(XENLOG_INFO VTDPREFIX, - "hvm_dpci_eoi:: mirq = %x\n", machine_gsi); - stop_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(machine_gsi)]); - if ( (ent == NULL) || !ent->fields.mask ) - pirq_guest_eoi(d, machine_gsi); - } - else - spin_unlock(&hvm_irq_dpci->dirq_lock); -} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/msi.h --- a/xen/arch/x86/hvm/vmx/vtd/msi.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2003-2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@xxxxxxxxx) - */ - -#ifndef MSI_H -#define MSI_H - -/* - * Assume the maximum number of hot plug slots supported by the system is about - * ten. The worstcase is that each of these slots is hot-added with a device, - * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which - * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined - * as below to ensure at least one message is assigned to each detected MSI/ - * MSI-X device function. - */ -#define NR_HP_RESERVED_VECTORS 20 - -extern int vector_irq[NR_VECTORS]; -extern int pci_vector_resources(int last, int nr_released); - -/* - * MSI-X Address Register - */ -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) -#define PCI_MSIX_FLAGS_BITMASK (1 << 0) - -#define PCI_MSIX_ENTRY_SIZE 16 -#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0 -#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4 -#define PCI_MSIX_ENTRY_DATA_OFFSET 8 -#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12 - -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) -#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) -#define msi_data_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) -#define msi_mask_bits_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) -#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE -#define multi_msi_capable(control) \ - (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) -#define multi_msi_enable(control, num) \ - control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); -#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) -#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) -#define msi_enable(control, num) multi_msi_enable(control, num); \ - control |= PCI_MSI_FLAGS_ENABLE - -#define msix_table_offset_reg(base) (base + 0x04) -#define msix_pba_offset_reg(base) (base + 0x08) -#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE -#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE -#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) -#define multi_msix_capable msix_table_size -#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK) -#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) -#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) - -/* - * MSI Defined Data Structures - */ -#define MSI_ADDRESS_HEADER 0xfee -#define MSI_ADDRESS_HEADER_SHIFT 12 -#define MSI_ADDRESS_HEADER_MASK 0xfff000 -#define MSI_ADDRESS_DEST_ID_MASK 0xfff0000f -#define MSI_TARGET_CPU_MASK 0xff -#define MSI_TARGET_CPU_SHIFT 12 -#define MSI_DELIVERY_MODE 0 -#define MSI_LEVEL_MODE 1 /* Edge always assert */ -#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ -#define MSI_PHYSICAL_MODE 0 -#define MSI_LOGICAL_MODE 1 -#define MSI_REDIRECTION_HINT_MODE 0 - -#define __LITTLE_ENDIAN_BITFIELD 1 - -struct msg_data { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 vector : 8; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 reserved_1 : 3; - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 reserved_2 : 16; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 reserved_2 : 16; - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 reserved_1 : 3; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 vector : 8; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif -} __attribute__ ((packed)); - -struct msg_address { - union { - struct { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 reserved_1 : 2; - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 reserved_2 : 4; - __u32 dest_id : 24; /* Destination ID */ -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 dest_id : 24; /* Destination ID */ - __u32 reserved_2 : 4; - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 reserved_1 : 2; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif - }u; - __u32 value; - }lo_address; - __u32 hi_address; -} __attribute__ ((packed)); - -#endif /* MSI_H */ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/pci-direct.h --- a/xen/arch/x86/hvm/vmx/vtd/pci-direct.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -#ifndef ASM_PCI_DIRECT_H -#define ASM_PCI_DIRECT_H 1 - -#include <xen/types.h> -#include <asm/io.h> - -/* Direct PCI access. This is used for PCI accesses in early boot before - the PCI subsystem works. */ - -#define PDprintk(x...) - -static inline u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset) -{ - u32 v; - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - v = inl(0xcfc); - if (v != 0xffffffff) - PDprintk("%x reading 4 from %x: %x\n", slot, offset, v); - return v; -} - -static inline u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset) -{ - u8 v; - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - v = inb(0xcfc + (offset&3)); - PDprintk("%x reading 1 from %x: %x\n", slot, offset, v); - return v; -} - -static inline u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset) -{ - u16 v; - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - v = inw(0xcfc + (offset&2)); - PDprintk("%x reading 2 from %x: %x\n", slot, offset, v); - return v; -} - -static inline void write_pci_config( - u8 bus, u8 slot, u8 func, u8 offset, u32 val) -{ - PDprintk("%x writing to %x: %x\n", slot, offset, val); - outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); - outl(val, 0xcfc); -} - -#endif diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/pci_regs.h --- a/xen/arch/x86/hvm/vmx/vtd/pci_regs.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,449 +0,0 @@ -/* - * pci_regs.h - * - * PCI standard defines - * Copyright 1994, Drew Eckhardt - * Copyright 1997--1999 Martin Mares <mj@xxxxxx> - * - * For more information, please consult the following manuals (look at - * http://www.pcisig.com/ for how to get them): - * - * PCI BIOS Specification - * PCI Local Bus Specification - * PCI to PCI Bridge Specification - * PCI System Design Guide - */ - -#ifndef LINUX_PCI_REGS_H -#define LINUX_PCI_REGS_H - -/* - * Under PCI, each device has 256 bytes of configuration address space, - * of which the first 64 bytes are standardized as follows: - */ -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ -#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ -#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ -#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ -#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ -#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ -#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ -#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ - -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ -#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ -#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ -#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ -#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ -#define PCI_STATUS_DEVSEL_FAST 0x000 -#define PCI_STATUS_DEVSEL_MEDIUM 0x200 -#define PCI_STATUS_DEVSEL_SLOW 0x400 -#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ -#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ -#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ -#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ -#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ - -#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ -#define PCI_REVISION_ID 0x08 /* Revision ID */ -#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ -#define PCI_CLASS_DEVICE 0x0a /* Device class */ - -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 - -#define PCI_BIST 0x0f /* 8 bits */ -#define PCI_BIST_CODE_MASK 0x0f /* Return result */ -#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ -#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ - -/* - * Base addresses specify locations in memory or I/O space. - * Decoded size can be determined by writing a value of - * 0xffffffff to the register, and reading it back. Only - * 1 bits are decoded. - */ -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ -#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ -#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ -#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ -#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) -#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) -/* bit 1 is reserved if address_space = 1 */ - -/* Header type 0 (normal devices) */ -#define PCI_CARDBUS_CIS 0x28 -#define PCI_SUBSYSTEM_VENDOR_ID 0x2c -#define PCI_SUBSYSTEM_ID 0x2e -#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 -#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) - -#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ - -/* 0x35-0x3b are reserved */ -#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ -#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ -#define PCI_MIN_GNT 0x3e /* 8 bits */ -#define PCI_MAX_LAT 0x3f /* 8 bits */ - -/* Header type 1 (PCI-to-PCI bridges) */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ -#define PCI_IO_RANGE_TYPE_16 0x00 -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL -#define PCI_MEMORY_RANGE_MASK (~0x0fUL) -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL -#define PCI_PREF_RANGE_TYPE_32 0x00 -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_RANGE_MASK (~0x0fUL) -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -/* 0x34 same as for htype 0 */ -/* 0x35-0x3b is reserved */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ -#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ -#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ -#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ -#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ -#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ -#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ - -/* Header type 2 (CardBus bridges) */ -#define PCI_CB_CAPABILITY_LIST 0x14 -/* 0x15 reserved */ -#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ -#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ -#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ -#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ -#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ -#define PCI_CB_MEMORY_BASE_0 0x1c -#define PCI_CB_MEMORY_LIMIT_0 0x20 -#define PCI_CB_MEMORY_BASE_1 0x24 -#define PCI_CB_MEMORY_LIMIT_1 0x28 -#define PCI_CB_IO_BASE_0 0x2c -#define PCI_CB_IO_BASE_0_HI 0x2e -#define PCI_CB_IO_LIMIT_0 0x30 -#define PCI_CB_IO_LIMIT_0_HI 0x32 -#define PCI_CB_IO_BASE_1 0x34 -#define PCI_CB_IO_BASE_1_HI 0x36 -#define PCI_CB_IO_LIMIT_1 0x38 -#define PCI_CB_IO_LIMIT_1_HI 0x3a -#define PCI_CB_IO_RANGE_MASK (~0x03UL) -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_CB_BRIDGE_CONTROL 0x3e -#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ -#define PCI_CB_BRIDGE_CTL_SERR 0x02 -#define PCI_CB_BRIDGE_CTL_ISA 0x04 -#define PCI_CB_BRIDGE_CTL_VGA 0x08 -#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 -#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ -#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 -#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 -#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 -#define PCI_CB_SUBSYSTEM_ID 0x42 -#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ -/* 0x48-0x7f reserved */ - -/* Capability lists */ - -#define PCI_CAP_LIST_ID 0 /* Capability ID */ -#define PCI_CAP_ID_PM 0x01 /* Power Management */ -#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ -#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ -#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ -#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ -#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ -#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ -#define PCI_CAP_ID_HT_IRQCONF 0x08 /* HyperTransport IRQ Configuration */ -#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ -#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ -#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ -#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ -#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ -#define PCI_CAP_SIZEOF 4 - -/* Power Management Registers */ - -#define PCI_PM_PMC 2 /* PM Capabilities Register */ -#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ -#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ -#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ -#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ -#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ -#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ -#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ -#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ -#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ -#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ -#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ -#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ -#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ -#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ -#define PCI_PM_CTRL 4 /* PM control and status register */ -#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ -#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ -#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ -#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ -#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ -#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ -#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ -#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ -#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ -#define PCI_PM_DATA_REGISTER 7 /* (??) */ -#define PCI_PM_SIZEOF 8 - -/* AGP registers */ - -#define PCI_AGP_VERSION 2 /* BCD version number */ -#define PCI_AGP_RFU 3 /* Rest of capability flags */ -#define PCI_AGP_STATUS 4 /* Status register */ -#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ -#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ -#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ -#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ -#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ -#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ -#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ -#define PCI_AGP_COMMAND 8 /* Control register */ -#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ -#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ -#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ -#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ -#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ -#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ -#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ -#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ -#define PCI_AGP_SIZEOF 12 - -/* Vital Product Data */ - -#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ -#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ -#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ -#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ - -/* Slot Identification */ - -#define PCI_SID_ESR 2 /* Expansion Slot Register */ -#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ -#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ -#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ - -/* Message Signalled Interrupts registers */ - -#define PCI_MSI_FLAGS 2 /* Various flags */ -#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ -#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ -#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ -#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ -#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ -#define PCI_MSI_RFU 3 /* Rest of capability flags */ -#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ -#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ -#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ -#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ -#define PCI_MSI_MASK_BIT 16 /* Mask bits register */ - -/* CompactPCI Hotswap Register */ - -#define PCI_CHSWP_CSR 2 /* Control and Status Register */ -#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ -#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ -#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ -#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ -#define PCI_CHSWP_PI 0x30 /* Programming Interface */ -#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ -#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ - -/* PCI-X registers */ - -#define PCI_X_CMD 2 /* Modes & Features */ -#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ -#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ -#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ -#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ -#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ -#define PCI_X_STATUS 4 /* PCI-X capabilities */ -#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ -#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ -#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ -#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ -#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ -#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ -#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ -#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ -#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ -#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ -#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ -#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ -#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ - -/* PCI Express capability registers */ - -#define PCI_EXP_FLAGS 2 /* Capabilities register */ -#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ -#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ -#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ -#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ -#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ -#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ -#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ -#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ -#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ -#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ -#define PCI_EXP_DEVCAP 4 /* Device capabilities */ -#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ -#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ -#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ -#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ -#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ -#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ -#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ -#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ -#define PCI_EXP_DEVCTL 8 /* Device Control */ -#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ -#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ -#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ -#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ -#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ -#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ -#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ -#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ -#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ -#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ -#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ -#define PCI_EXP_DEVSTA 10 /* Device Status */ -#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ -#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ -#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ -#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ -#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ -#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ -#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ -#define PCI_EXP_LNKCTL 16 /* Link Control */ -#define PCI_EXP_LNKSTA 18 /* Link Status */ -#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ -#define PCI_EXP_SLTCTL 24 /* Slot Control */ -#define PCI_EXP_SLTSTA 26 /* Slot Status */ -#define PCI_EXP_RTCTL 28 /* Root Control */ -#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ -#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ -#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ -#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ -#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ -#define PCI_EXP_RTCAP 30 /* Root Capabilities */ -#define PCI_EXP_RTSTA 32 /* Root Status */ - -/* Extended Capabilities (PCI-X 2.0 and Express) */ -#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) -#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) -#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) - -#define PCI_EXT_CAP_ID_ERR 1 -#define PCI_EXT_CAP_ID_VC 2 -#define PCI_EXT_CAP_ID_DSN 3 -#define PCI_EXT_CAP_ID_PWR 4 - -/* Advanced Error Reporting */ -#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ -#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ -#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ -#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ -#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ -#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ -#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ -#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ -#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ -#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ -#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ -#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ -#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ - /* Same bits as above */ -#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ -#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ -#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ -#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ -#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ -#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ -#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ -#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ -#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ -#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ -#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ -#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ -#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ -#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ -#define PCI_ERR_ROOT_STATUS 48 -#define PCI_ERR_ROOT_COR_SRC 52 -#define PCI_ERR_ROOT_SRC 54 - -/* Virtual Channel */ -#define PCI_VC_PORT_REG1 4 -#define PCI_VC_PORT_REG2 8 -#define PCI_VC_PORT_CTRL 12 -#define PCI_VC_PORT_STATUS 14 -#define PCI_VC_RES_CAP 16 -#define PCI_VC_RES_CTRL 20 -#define PCI_VC_RES_STATUS 26 - -/* Power Budgeting */ -#define PCI_PWR_DSR 4 /* Data Select Register */ -#define PCI_PWR_DATA 8 /* Data Register */ -#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ -#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ -#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ -#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ -#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ -#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ -#define PCI_PWR_CAP 12 /* Capability */ -#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ - -#endif /* LINUX_PCI_REGS_H */ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/qinval.c --- a/xen/arch/x86/hvm/vmx/vtd/qinval.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - * Copyright (C) Xiaohui Xin <xiaohui.xin@xxxxxxxxx> - */ - - -#include <xen/init.h> -#include <xen/irq.h> -#include <xen/spinlock.h> -#include <xen/sched.h> -#include <xen/xmalloc.h> -#include <xen/domain_page.h> -#include <asm/delay.h> -#include <asm/string.h> -#include <asm/iommu.h> -#include <asm/hvm/vmx/intel-iommu.h> -#include "dmar.h" -#include "vtd.h" -#include "pci-direct.h" -#include "pci_regs.h" -#include "msi.h" -#include "extern.h" - -static void print_qi_regs(struct iommu *iommu) -{ - u64 val; - - val = dmar_readq(iommu->reg, DMAR_IQA_REG); - printk("DMAR_IAQ_REG = %"PRIx64"\n", val); - - val = dmar_readq(iommu->reg, DMAR_IQH_REG); - printk("DMAR_IAH_REG = %"PRIx64"\n", val); - - val = dmar_readq(iommu->reg, DMAR_IQT_REG); - printk("DMAR_IAT_REG = %"PRIx64"\n", val); -} - -static int qinval_next_index(struct iommu *iommu) -{ - u64 val; - val = dmar_readq(iommu->reg, DMAR_IQT_REG); - return (val >> 4); -} - -static int qinval_update_qtail(struct iommu *iommu, int index) -{ - u64 val; - - /* Need an ASSERT to insure that we have got register lock */ - val = (index < (QINVAL_ENTRY_NR-1)) ? (index + 1) : 0; - dmar_writeq(iommu->reg, DMAR_IQT_REG, (val << 4)); - return 0; -} - -static int gen_cc_inv_dsc(struct iommu *iommu, int index, - u16 did, u16 source_id, u8 function_mask, u8 granu) -{ - u64 *ptr64; - unsigned long flags; - struct qinval_entry * qinval_entry = NULL; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - spin_lock_irqsave(&qi_ctrl->qinval_lock, flags); - qinval_entry = &qi_ctrl->qinval[index]; - qinval_entry->q.cc_inv_dsc.lo.type = TYPE_INVAL_CONTEXT; - qinval_entry->q.cc_inv_dsc.lo.granu = granu; - qinval_entry->q.cc_inv_dsc.lo.res_1 = 0; - qinval_entry->q.cc_inv_dsc.lo.did = did; - qinval_entry->q.cc_inv_dsc.lo.sid = source_id; - qinval_entry->q.cc_inv_dsc.lo.fm = function_mask; - qinval_entry->q.cc_inv_dsc.lo.res_2 = 0; - qinval_entry->q.cc_inv_dsc.hi.res = 0; - spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags); - - ptr64 = (u64 *)qinval_entry; - return 0; -} - -int queue_invalidate_context(struct iommu *iommu, - u16 did, u16 source_id, u8 function_mask, u8 granu) -{ - int ret = -1; - unsigned long flags; - int index = -1; - - spin_lock_irqsave(&iommu->register_lock, flags); - index = qinval_next_index(iommu); - if (index == -1) - return -EBUSY; - ret = gen_cc_inv_dsc(iommu, index, did, source_id, - function_mask, granu); - ret |= qinval_update_qtail(iommu, index); - spin_unlock_irqrestore(&iommu->register_lock, flags); - return ret; -} - -static int gen_iotlb_inv_dsc(struct iommu *iommu, int index, - u8 granu, u8 dr, u8 dw, u16 did, u8 am, u8 ih, u64 addr) -{ - unsigned long flags; - struct qinval_entry * qinval_entry = NULL; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - if ( index == -1 ) - return -1; - spin_lock_irqsave(&qi_ctrl->qinval_lock, flags); - - qinval_entry = &qi_ctrl->qinval[index]; - qinval_entry->q.iotlb_inv_dsc.lo.type = TYPE_INVAL_IOTLB; - qinval_entry->q.iotlb_inv_dsc.lo.granu = granu; - qinval_entry->q.iotlb_inv_dsc.lo.dr = 0; - qinval_entry->q.iotlb_inv_dsc.lo.dw = 0; - qinval_entry->q.iotlb_inv_dsc.lo.res_1 = 0; - qinval_entry->q.iotlb_inv_dsc.lo.did = did; - qinval_entry->q.iotlb_inv_dsc.lo.res_2 = 0; - - qinval_entry->q.iotlb_inv_dsc.hi.am = am; - qinval_entry->q.iotlb_inv_dsc.hi.ih = ih; - qinval_entry->q.iotlb_inv_dsc.hi.res_1 = 0; - qinval_entry->q.iotlb_inv_dsc.hi.addr = addr; - - spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags); - return 0; -} - -int queue_invalidate_iotlb(struct iommu *iommu, - u8 granu, u8 dr, u8 dw, u16 did, u8 am, u8 ih, u64 addr) -{ - int ret = -1; - unsigned long flags; - int index = -1; - - spin_lock_irqsave(&iommu->register_lock, flags); - - index = qinval_next_index(iommu); - ret = gen_iotlb_inv_dsc(iommu, index, granu, dr, dw, did, - am, ih, addr); - ret |= qinval_update_qtail(iommu, index); - spin_unlock_irqrestore(&iommu->register_lock, flags); - return ret; -} - -static int gen_wait_dsc(struct iommu *iommu, int index, - u8 iflag, u8 sw, u8 fn, u32 sdata, volatile u32 *saddr) -{ - u64 *ptr64; - unsigned long flags; - struct qinval_entry * qinval_entry = NULL; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - if ( index == -1 ) - return -1; - spin_lock_irqsave(&qi_ctrl->qinval_lock, flags); - qinval_entry = &qi_ctrl->qinval[index]; - qinval_entry->q.inv_wait_dsc.lo.type = TYPE_INVAL_WAIT; - qinval_entry->q.inv_wait_dsc.lo.iflag = iflag; - qinval_entry->q.inv_wait_dsc.lo.sw = sw; - qinval_entry->q.inv_wait_dsc.lo.fn = fn; - qinval_entry->q.inv_wait_dsc.lo.res_1 = 0; - qinval_entry->q.inv_wait_dsc.lo.sdata = sdata; - qinval_entry->q.inv_wait_dsc.hi.res_1 = 0; - qinval_entry->q.inv_wait_dsc.hi.saddr = virt_to_maddr(saddr) >> 2; - spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags); - ptr64 = (u64 *)qinval_entry; - return 0; -} - -static int queue_invalidate_wait(struct iommu *iommu, - u8 iflag, u8 sw, u8 fn, u32 sdata, volatile u32 *saddr) -{ - unsigned long flags; - unsigned long start_time; - int index = -1; - int ret = -1; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - spin_lock_irqsave(&qi_ctrl->qinval_poll_lock, flags); - spin_lock_irqsave(&iommu->register_lock, flags); - index = qinval_next_index(iommu); - if (*saddr == 1) - *saddr = 0; - ret = gen_wait_dsc(iommu, index, iflag, sw, fn, sdata, saddr); - ret |= qinval_update_qtail(iommu, index); - spin_unlock_irqrestore(&iommu->register_lock, flags); - - /* Now we don't support interrupt method */ - if ( sw ) - { - /* In case all wait descriptor writes to same addr with same data */ - start_time = jiffies; - while ( *saddr != 1 ) { - if (time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT)) { - print_qi_regs(iommu); - panic("queue invalidate wait descriptor was not executed\n"); - } - cpu_relax(); - } - } - spin_unlock_irqrestore(&qi_ctrl->qinval_poll_lock, flags); - return ret; -} - -int invalidate_sync(struct iommu *iommu) -{ - int ret = -1; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - if (qi_ctrl->qinval) - { - ret = queue_invalidate_wait(iommu, - 0, 1, 1, 1, &qi_ctrl->qinval_poll_status); - return ret; - } - return 0; -} - -static int gen_dev_iotlb_inv_dsc(struct iommu *iommu, int index, - u32 max_invs_pend, u16 sid, u16 size, u64 addr) -{ - unsigned long flags; - struct qinval_entry * qinval_entry = NULL; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - if ( index == -1 ) - return -1; - spin_lock_irqsave(&qi_ctrl->qinval_lock, flags); - - qinval_entry = &qi_ctrl->qinval[index]; - qinval_entry->q.dev_iotlb_inv_dsc.lo.type = TYPE_INVAL_DEVICE_IOTLB; - qinval_entry->q.dev_iotlb_inv_dsc.lo.res_1 = 0; - qinval_entry->q.dev_iotlb_inv_dsc.lo.max_invs_pend = max_invs_pend; - qinval_entry->q.dev_iotlb_inv_dsc.lo.res_2 = 0; - qinval_entry->q.dev_iotlb_inv_dsc.lo.sid = sid; - qinval_entry->q.dev_iotlb_inv_dsc.lo.res_3 = 0; - - qinval_entry->q.dev_iotlb_inv_dsc.hi.size = size; - qinval_entry->q.dev_iotlb_inv_dsc.hi.addr = addr; - - spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags); - return 0; -} - -int queue_invalidate_device_iotlb(struct iommu *iommu, - u32 max_invs_pend, u16 sid, u16 size, u64 addr) -{ - int ret = -1; - unsigned long flags; - int index = -1; - - spin_lock_irqsave(&iommu->register_lock, flags); - index = qinval_next_index(iommu); - ret = gen_dev_iotlb_inv_dsc(iommu, index, max_invs_pend, - sid, size, addr); - ret |= qinval_update_qtail(iommu, index); - spin_unlock_irqrestore(&iommu->register_lock, flags); - return ret; -} - -static int gen_iec_inv_dsc(struct iommu *iommu, int index, - u8 granu, u8 im, u16 iidx) -{ - unsigned long flags; - struct qinval_entry * qinval_entry = NULL; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - if ( index == -1 ) - return -1; - spin_lock_irqsave(&qi_ctrl->qinval_lock, flags); - - qinval_entry = &qi_ctrl->qinval[index]; - qinval_entry->q.iec_inv_dsc.lo.type = TYPE_INVAL_IEC; - qinval_entry->q.iec_inv_dsc.lo.granu = granu; - qinval_entry->q.iec_inv_dsc.lo.res_1 = 0; - qinval_entry->q.iec_inv_dsc.lo.im = im; - qinval_entry->q.iec_inv_dsc.lo.iidx = iidx; - qinval_entry->q.iec_inv_dsc.lo.res_2 = 0; - qinval_entry->q.iec_inv_dsc.hi.res = 0; - - spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags); - return 0; -} - -int queue_invalidate_iec(struct iommu *iommu, u8 granu, u8 im, u16 iidx) -{ - int ret; - unsigned long flags; - int index = -1; - - spin_lock_irqsave(&iommu->register_lock, flags); - index = qinval_next_index(iommu); - ret = gen_iec_inv_dsc(iommu, index, granu, im, iidx); - ret |= qinval_update_qtail(iommu, index); - spin_unlock_irqrestore(&iommu->register_lock, flags); - return ret; -} - -u64 iec_cap; -int __iommu_flush_iec(struct iommu *iommu, u8 granu, u8 im, u16 iidx) -{ - int ret; - ret = queue_invalidate_iec(iommu, granu, im, iidx); - ret |= invalidate_sync(iommu); - - /* - * reading vt-d architecture register will ensure - * draining happens in implementation independent way. - */ - iec_cap = dmar_readq(iommu->reg, DMAR_CAP_REG); - return ret; -} - -int iommu_flush_iec_global(struct iommu *iommu) -{ - return __iommu_flush_iec(iommu, IEC_GLOBAL_INVL, 0, 0); -} - -int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx) -{ - return __iommu_flush_iec(iommu, IEC_INDEX_INVL, im, iidx); -} - -static int flush_context_qi( - void *_iommu, u16 did, u16 sid, u8 fm, u64 type, - int non_present_entry_flush) -{ - int ret = 0; - struct iommu *iommu = (struct iommu *)_iommu; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - /* - * In the non-present entry flush case, if hardware doesn't cache - * non-present entry we do nothing and if hardware cache non-present - * entry, we flush entries of domain 0 (the domain id is used to cache - * any non-present entries) - */ - if ( non_present_entry_flush ) - { - if ( !cap_caching_mode(iommu->cap) ) - return 1; - else - did = 0; - } - - if (qi_ctrl->qinval) - { - ret = queue_invalidate_context(iommu, did, sid, fm, - type >> DMA_CCMD_INVL_GRANU_OFFSET); - ret |= invalidate_sync(iommu); - } - return ret; -} - -static int flush_iotlb_qi( - void *_iommu, u16 did, - u64 addr, unsigned int size_order, u64 type, - int non_present_entry_flush) -{ - u8 dr = 0, dw = 0; - int ret = 0; - struct iommu *iommu = (struct iommu *)_iommu; - struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu); - - /* - * In the non-present entry flush case, if hardware doesn't cache - * non-present entry we do nothing and if hardware cache non-present - * entry, we flush entries of domain 0 (the domain id is used to cache - * any non-present entries) - */ - if ( non_present_entry_flush ) - { - if ( !cap_caching_mode(iommu->cap) ) - return 1; - else - did = 0; - } - - if (qi_ctrl->qinval) { - /* use queued invalidation */ - if (cap_write_drain(iommu->cap)) - dw = 1; - if (cap_read_drain(iommu->cap)) - dr = 1; - /* Need to conside the ih bit later */ - ret = queue_invalidate_iotlb(iommu, - (type >> DMA_TLB_FLUSH_GRANU_OFFSET), dr, - dw, did, (u8)size_order, 0, addr); - ret |= invalidate_sync(iommu); - } - return ret; -} - -int qinval_setup(struct iommu *iommu) -{ - unsigned long start_time; - u64 paddr; - u32 status = 0; - struct qi_ctrl *qi_ctrl; - struct iommu_flush *flush; - - qi_ctrl = iommu_qi_ctrl(iommu); - flush = iommu_get_flush(iommu); - - if ( !ecap_queued_inval(iommu->ecap) ) - return -ENODEV; - - if (qi_ctrl->qinval == NULL) { - qi_ctrl->qinval = alloc_xenheap_page(); - if (qi_ctrl->qinval == NULL) - panic("Cannot allocate memory for qi_ctrl->qinval\n"); - memset((u8*)qi_ctrl->qinval, 0, PAGE_SIZE_4K); - flush->context = flush_context_qi; - flush->iotlb = flush_iotlb_qi; - } - paddr = virt_to_maddr(qi_ctrl->qinval); - - /* Setup Invalidation Queue Address(IQA) register with the - * address of the page we just allocated. QS field at - * bits[2:0] to indicate size of queue is one 4KB page. - * That's 256 entries. Queued Head (IQH) and Queue Tail (IQT) - * registers are automatically reset to 0 with write - * to IQA register. - */ - dmar_writeq(iommu->reg, DMAR_IQA_REG, paddr); - - /* enable queued invalidation hardware */ - iommu->gcmd |= DMA_GCMD_QIE; - dmar_writel(iommu->reg, DMAR_GCMD_REG, iommu->gcmd); - - /* Make sure hardware complete it */ - start_time = jiffies; - while (1) { - status = dmar_readl(iommu->reg, DMAR_GSTS_REG); - if (status & DMA_GSTS_QIES) - break; - if (time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT)) - panic("Cannot set QIE field for queue invalidation\n"); - cpu_relax(); - } - status = 0; - return status; -} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/utils.c --- a/xen/arch/x86/hvm/vmx/vtd/utils.c Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,338 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - */ - -#include <xen/init.h> -#include <xen/bitmap.h> -#include <xen/irq.h> -#include <xen/spinlock.h> -#include <xen/sched.h> -#include <xen/delay.h> -#include <asm/iommu.h> -#include <asm/hvm/vmx/intel-iommu.h> -#include "dmar.h" -#include "pci-direct.h" -#include "pci_regs.h" -#include "msi.h" - -#include <xen/mm.h> -#include <xen/xmalloc.h> -#include <xen/inttypes.h> - -#define INTEL 0x8086 -#define SEABURG 0x4000 -#define C_STEP 2 - -int vtd_hw_check(void) -{ - u16 vendor, device; - u8 revision, stepping; - - vendor = read_pci_config_16(0, 0, 0, PCI_VENDOR_ID); - device = read_pci_config_16(0, 0, 0, PCI_DEVICE_ID); - revision = read_pci_config_byte(0, 0, 0, PCI_REVISION_ID); - stepping = revision & 0xf; - - if ( (vendor == INTEL) && (device == SEABURG) ) - { - if ( stepping < C_STEP ) - { - dprintk(XENLOG_WARNING VTDPREFIX, - "*** VT-d disabled - pre C0-step Seaburg found\n"); - dprintk(XENLOG_WARNING VTDPREFIX, - "*** vendor = %x device = %x revision = %x\n", - vendor, device, revision); - vtd_enabled = 0; - return -ENODEV; - } - } - return 0; -} - -/* Disable vt-d protected memory registers. */ -void disable_pmr(struct iommu *iommu) -{ - unsigned long start_time; - unsigned int val; - - val = dmar_readl(iommu->reg, DMAR_PMEN_REG); - if ( !(val & DMA_PMEN_PRS) ) - return; - - dmar_writel(iommu->reg, DMAR_PMEN_REG, val & ~DMA_PMEN_EPM); - start_time = jiffies; - - for ( ; ; ) - { - val = dmar_readl(iommu->reg, DMAR_PMEN_REG); - if ( (val & DMA_PMEN_PRS) == 0 ) - break; - - if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) ) - panic("Disable PMRs timeout\n"); - - cpu_relax(); - } - - dprintk(XENLOG_INFO VTDPREFIX, - "Disabled protected memory registers\n"); -} - -static u8 find_cap_offset(u8 bus, u8 dev, u8 func, u8 cap) -{ - u8 id; - int max_cap = 48; - u8 pos = PCI_CAPABILITY_LIST; - u16 status; - - status = read_pci_config_16(bus, dev, func, PCI_STATUS); - if ( (status & PCI_STATUS_CAP_LIST) == 0 ) - return 0; - - while ( max_cap-- ) - { - pos = read_pci_config_byte(bus, dev, func, pos); - if ( pos < 0x40 ) - break; - - pos &= ~3; - id = read_pci_config_byte(bus, dev, func, pos + PCI_CAP_LIST_ID); - - if ( id == 0xff ) - break; - else if ( id == cap ) - return pos; - - pos += PCI_CAP_LIST_NEXT; - } - - return 0; -} - -#define PCI_D3hot (3) -#define PCI_CONFIG_DWORD_SIZE (64) -#define PCI_EXP_DEVCAP_FLR (1 << 28) -#define PCI_EXP_DEVCTL_FLR (1 << 15) - -void pdev_flr(u8 bus, u8 devfn) -{ - u8 pos; - u32 dev_cap, dev_status, pm_ctl; - int flr = 0; - u8 dev = PCI_SLOT(devfn); - u8 func = PCI_FUNC(devfn); - - pos = find_cap_offset(bus, dev, func, PCI_CAP_ID_EXP); - if ( pos != 0 ) - { - dev_cap = read_pci_config(bus, dev, func, pos + PCI_EXP_DEVCAP); - if ( dev_cap & PCI_EXP_DEVCAP_FLR ) - { - write_pci_config(bus, dev, func, - pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR); - do { - dev_status = read_pci_config(bus, dev, func, - pos + PCI_EXP_DEVSTA); - } while ( dev_status & PCI_EXP_DEVSTA_TRPND ); - - flr = 1; - } - } - - /* If this device doesn't support function level reset, - * program device from D0 t0 D3hot, and then return to D0 - * to implement function level reset - */ - if ( flr == 0 ) - { - pos = find_cap_offset(bus, dev, func, PCI_CAP_ID_PM); - if ( pos != 0 ) - { - int i; - u32 config[PCI_CONFIG_DWORD_SIZE]; - for ( i = 0; i < PCI_CONFIG_DWORD_SIZE; i++ ) - config[i] = read_pci_config(bus, dev, func, i*4); - - /* Enter D3hot without soft reset */ - pm_ctl = read_pci_config(bus, dev, func, pos + PCI_PM_CTRL); - pm_ctl |= PCI_PM_CTRL_NO_SOFT_RESET; - pm_ctl &= ~PCI_PM_CTRL_STATE_MASK; - pm_ctl |= PCI_D3hot; - write_pci_config(bus, dev, func, pos + PCI_PM_CTRL, pm_ctl); - mdelay(10); - - /* From D3hot to D0 */ - write_pci_config(bus, dev, func, pos + PCI_PM_CTRL, 0); - mdelay(10); - - /* Write saved configurations to device */ - for ( i = 0; i < PCI_CONFIG_DWORD_SIZE; i++ ) - write_pci_config(bus, dev, func, i*4, config[i]); - - flr = 1; - } - } -} - -void print_iommu_regs(struct acpi_drhd_unit *drhd) -{ - struct iommu *iommu = drhd->iommu; - - printk("---- print_iommu_regs ----\n"); - printk("print_iommu_regs: drhd->address = %lx\n", drhd->address); - printk("print_iommu_regs: DMAR_VER_REG = %x\n", - dmar_readl(iommu->reg,DMAR_VER_REG)); - printk("print_iommu_regs: DMAR_CAP_REG = %"PRIx64"\n", - dmar_readq(iommu->reg,DMAR_CAP_REG)); - printk("print_iommu_regs: n_fault_reg = %"PRIx64"\n", - cap_num_fault_regs(dmar_readq(iommu->reg, DMAR_CAP_REG))); - printk("print_iommu_regs: fault_recording_offset_l = %"PRIx64"\n", - cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG))); - printk("print_iommu_regs: fault_recording_offset_h = %"PRIx64"\n", - cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG)) + 8); - printk("print_iommu_regs: fault_recording_reg_l = %"PRIx64"\n", - dmar_readq(iommu->reg, - cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG)))); - printk("print_iommu_regs: fault_recording_reg_h = %"PRIx64"\n", - dmar_readq(iommu->reg, - cap_fault_reg_offset(dmar_readq(iommu->reg, DMAR_CAP_REG)) + 8)); - printk("print_iommu_regs: DMAR_ECAP_REG = %"PRIx64"\n", - dmar_readq(iommu->reg,DMAR_ECAP_REG)); - printk("print_iommu_regs: DMAR_GCMD_REG = %x\n", - dmar_readl(iommu->reg,DMAR_GCMD_REG)); - printk("print_iommu_regs: DMAR_GSTS_REG = %x\n", - dmar_readl(iommu->reg,DMAR_GSTS_REG)); - printk("print_iommu_regs: DMAR_RTADDR_REG = %"PRIx64"\n", - dmar_readq(iommu->reg,DMAR_RTADDR_REG)); - printk("print_iommu_regs: DMAR_CCMD_REG = %"PRIx64"\n", - dmar_readq(iommu->reg,DMAR_CCMD_REG)); - printk("print_iommu_regs: DMAR_FSTS_REG = %x\n", - dmar_readl(iommu->reg,DMAR_FSTS_REG)); - printk("print_iommu_regs: DMAR_FECTL_REG = %x\n", - dmar_readl(iommu->reg,DMAR_FECTL_REG)); - printk("print_iommu_regs: DMAR_FEDATA_REG = %x\n", - dmar_readl(iommu->reg,DMAR_FEDATA_REG)); - printk("print_iommu_regs: DMAR_FEADDR_REG = %x\n", - dmar_readl(iommu->reg,DMAR_FEADDR_REG)); - printk("print_iommu_regs: DMAR_FEUADDR_REG = %x\n", - dmar_readl(iommu->reg,DMAR_FEUADDR_REG)); -} - -u32 get_level_index(unsigned long gmfn, int level) -{ - while ( --level ) - gmfn = gmfn >> LEVEL_STRIDE; - - return gmfn & LEVEL_MASK; -} - -void print_vtd_entries( - struct domain *d, - struct iommu *iommu, - int bus, int devfn, - unsigned long gmfn) -{ - struct hvm_iommu *hd = domain_hvm_iommu(d); - struct acpi_drhd_unit *drhd; - struct context_entry *ctxt_entry; - struct root_entry *root_entry; - struct dma_pte pte; - u64 *l; - u32 l_index; - u32 i = 0; - int level = agaw_to_level(hd->agaw); - - printk("print_vtd_entries: domain_id = %x bdf = %x:%x:%x gmfn = %lx\n", - d->domain_id, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), gmfn); - - if ( hd->pgd == NULL ) - { - printk(" hg->pgd == NULL\n"); - return; - } - printk(" d->pgd = %p virt_to_maddr(hd->pgd) = %lx\n", - hd->pgd, virt_to_maddr(hd->pgd)); - - for_each_drhd_unit ( drhd ) - { - printk("---- print_vtd_entries %d ----\n", i++); - - root_entry = iommu->root_entry; - if ( root_entry == NULL ) - { - printk(" root_entry == NULL\n"); - continue; - } - - printk(" root_entry = %p\n", root_entry); - printk(" root_entry[%x] = %"PRIx64"\n", bus, root_entry[bus].val); - if ( !root_present(root_entry[bus]) ) - { - printk(" root_entry[%x] not present\n", bus); - continue; - } - - ctxt_entry = - maddr_to_virt((root_entry[bus].val >> PAGE_SHIFT) << PAGE_SHIFT); - if ( ctxt_entry == NULL ) - { - printk(" ctxt_entry == NULL\n"); - continue; - } - - printk(" context = %p\n", ctxt_entry); - printk(" context[%x] = %"PRIx64" %"PRIx64"\n", - devfn, ctxt_entry[devfn].hi, ctxt_entry[devfn].lo); - if ( !context_present(ctxt_entry[devfn]) ) - { - printk(" ctxt_entry[%x] not present\n", devfn); - continue; - } - - if ( level != VTD_PAGE_TABLE_LEVEL_3 && - level != VTD_PAGE_TABLE_LEVEL_4) - { - printk("Unsupported VTD page table level (%d)!\n", level); - continue; - } - - l = maddr_to_virt(ctxt_entry[devfn].lo); - do - { - l = (u64*)(((unsigned long)l >> PAGE_SHIFT_4K) << PAGE_SHIFT_4K); - printk(" l%d = %p\n", level, l); - if ( l == NULL ) - { - printk(" l%d == NULL\n", level); - break; - } - l_index = get_level_index(gmfn, level); - printk(" l%d_index = %x\n", level, l_index); - printk(" l%d[%x] = %"PRIx64"\n", level, l_index, l[l_index]); - - pte.val = l[l_index]; - if ( !dma_pte_present(pte) ) - { - printk(" l%d[%x] not present\n", level, l_index); - break; - } - - l = maddr_to_virt(l[l_index]); - } while ( --level ); - } -} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/arch/x86/hvm/vmx/vtd/vtd.h --- a/xen/arch/x86/hvm/vmx/vtd/vtd.h Thu Feb 21 14:50:27 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2006, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - * Copyright (C) Weidong Han <weidong.han@xxxxxxxxx> - */ - -#ifndef _VTD_H_ -#define _VTD_H_ - -#include <xen/list.h> -#include <asm/iommu.h> - -#define VTDPREFIX "[VT-D]" - -#define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */ -#define time_after(a,b) \ - (typecheck(unsigned long, a) && \ - typecheck(unsigned long, b) && \ - ((long)(b) - (long)(a) < 0)) - -struct IO_APIC_route_remap_entry { - union { - u64 val; - struct { - u64 vector:8, - delivery_mode:3, - index_15:1, - delivery_status:1, - polarity:1, - irr:1, - trigger:1, - mask:1, - reserved:31, - format:1, - index_0_14:15; - }; - }; -}; - -#endif // _VTD_H_ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/Makefile --- a/xen/drivers/Makefile Thu Feb 21 14:50:27 2008 +0000 +++ b/xen/drivers/Makefile Thu Feb 21 15:06:37 2008 +0000 @@ -1,3 +1,4 @@ subdir-y += char subdir-y += char +subdir-$(x86) += passthrough subdir-$(HAS_ACPI) += acpi subdir-$(HAS_VGA) += video diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/Makefile Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,2 @@ +subdir-$(x86) += vtd +subdir-$(x86) += amd diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/amd/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd/Makefile Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,4 @@ +obj-y += iommu_detect.o +obj-y += iommu_init.o +obj-y += iommu_map.o +obj-y += pci_amd_iommu.o diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/amd/iommu_detect.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd/iommu_detect.c Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran <leo.duran@xxxxxxx> + * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <xen/config.h> +#include <xen/errno.h> +#include <asm/iommu.h> +#include <asm/amd-iommu.h> +#include <asm/hvm/svm/amd-iommu-proto.h> +#include "../pci-direct.h" +#include "../pci_regs.h" + +static int __init valid_bridge_bus_config(int bus, int dev, int func, + int *sec_bus, int *sub_bus) +{ + int pri_bus; + + pri_bus = read_pci_config_byte(bus, dev, func, PCI_PRIMARY_BUS); + *sec_bus = read_pci_config_byte(bus, dev, func, PCI_SECONDARY_BUS); + *sub_bus = read_pci_config_byte(bus, dev, func, PCI_SUBORDINATE_BUS); + + return ( pri_bus == bus && *sec_bus > bus && *sub_bus >= *sec_bus ); +} + +int __init get_iommu_last_downstream_bus(struct amd_iommu *iommu) +{ + int bus, dev, func; + int devfn, hdr_type; + int sec_bus, sub_bus; + int multi_func; + + bus = iommu->last_downstream_bus = iommu->root_bus; + iommu->downstream_bus_present[bus] = 1; + dev = PCI_SLOT(iommu->first_devfn); + multi_func = PCI_FUNC(iommu->first_devfn) > 0; + for ( devfn = iommu->first_devfn; devfn <= iommu->last_devfn; ++devfn ) { + /* skipping to next device#? */ + if ( dev != PCI_SLOT(devfn) ) { + dev = PCI_SLOT(devfn); + multi_func = 0; + } + func = PCI_FUNC(devfn); + + if ( !VALID_PCI_VENDOR_ID( + read_pci_config_16(bus, dev, func, PCI_VENDOR_ID)) ) + continue; + + hdr_type = read_pci_config_byte(bus, dev, func, + PCI_HEADER_TYPE); + if ( func == 0 ) + multi_func = IS_PCI_MULTI_FUNCTION(hdr_type); + + if ( (func == 0 || multi_func) && + IS_PCI_TYPE1_HEADER(hdr_type) ) { + if (!valid_bridge_bus_config(bus, dev, func, + &sec_bus, &sub_bus)) + return -ENODEV; + + if ( sub_bus > iommu->last_downstream_bus ) + iommu->last_downstream_bus = sub_bus; + do { + iommu->downstream_bus_present[sec_bus] = 1; + } while ( sec_bus++ < sub_bus ); + } + } + + return 0; +} + +int __init get_iommu_capabilities(u8 bus, u8 dev, u8 func, u8 cap_ptr, + struct amd_iommu *iommu) +{ + u32 cap_header, cap_range; + u64 mmio_bar; + +#if HACK_BIOS_SETTINGS + /* remove it when BIOS available */ + write_pci_config(bus, dev, func, + cap_ptr + PCI_CAP_MMIO_BAR_HIGH_OFFSET, 0x00000000); + write_pci_config(bus, dev, func, + cap_ptr + PCI_CAP_MMIO_BAR_LOW_OFFSET, 0x40000001); + /* remove it when BIOS available */ +#endif + + mmio_bar = (u64)read_pci_config(bus, dev, func, + cap_ptr + PCI_CAP_MMIO_BAR_HIGH_OFFSET) << 32; + mmio_bar |= read_pci_config(bus, dev, func, + cap_ptr + PCI_CAP_MMIO_BAR_LOW_OFFSET) & + PCI_CAP_MMIO_BAR_LOW_MASK; + iommu->mmio_base_phys = (unsigned long)mmio_bar; + + if ( (mmio_bar == 0) || ( (mmio_bar & 0x3FFF) != 0 ) ) { + dprintk(XENLOG_ERR , + "AMD IOMMU: Invalid MMIO_BAR = 0x%"PRIx64"\n", mmio_bar); + return -ENODEV; + } + + cap_header = read_pci_config(bus, dev, func, cap_ptr); + iommu->revision = get_field_from_reg_u32(cap_header, + PCI_CAP_REV_MASK, PCI_CAP_REV_SHIFT); + iommu->iotlb_support = get_field_from_reg_u32(cap_header, + PCI_CAP_IOTLB_MASK, PCI_CAP_IOTLB_SHIFT); + iommu->ht_tunnel_support = get_field_from_reg_u32(cap_header, + PCI_CAP_HT_TUNNEL_MASK, + PCI_CAP_HT_TUNNEL_SHIFT); + iommu->not_present_cached = get_field_from_reg_u32(cap_header, + PCI_CAP_NP_CACHE_MASK, + PCI_CAP_NP_CACHE_SHIFT); + + cap_range = read_pci_config(bus, dev, func, + cap_ptr + PCI_CAP_RANGE_OFFSET); + iommu->root_bus = get_field_from_reg_u32(cap_range, + PCI_CAP_BUS_NUMBER_MASK, + PCI_CAP_BUS_NUMBER_SHIFT); + iommu->first_devfn = get_field_from_reg_u32(cap_range, + PCI_CAP_FIRST_DEVICE_MASK, + PCI_CAP_FIRST_DEVICE_SHIFT); + iommu->last_devfn = get_field_from_reg_u32(cap_range, + PCI_CAP_LAST_DEVICE_MASK, + PCI_CAP_LAST_DEVICE_SHIFT); + + return 0; +} + +static int __init scan_caps_for_iommu(int bus, int dev, int func, + iommu_detect_callback_ptr_t iommu_detect_callback) +{ + int cap_ptr, cap_id, cap_type; + u32 cap_header; + int count, error = 0; + + count = 0; + cap_ptr = read_pci_config_byte(bus, dev, func, + PCI_CAPABILITY_LIST); + while ( cap_ptr >= PCI_MIN_CAP_OFFSET && + count < PCI_MAX_CAP_BLOCKS && !error ) { + cap_ptr &= PCI_CAP_PTR_MASK; + cap_header = read_pci_config(bus, dev, func, cap_ptr); + cap_id = get_field_from_reg_u32(cap_header, + PCI_CAP_ID_MASK, PCI_CAP_ID_SHIFT); + + if ( cap_id == PCI_CAP_ID_SECURE_DEVICE ) { + cap_type = get_field_from_reg_u32(cap_header, + PCI_CAP_TYPE_MASK, PCI_CAP_TYPE_SHIFT); + if ( cap_type == PCI_CAP_TYPE_IOMMU ) { + error = iommu_detect_callback( + bus, dev, func, cap_ptr); + } + } + + cap_ptr = get_field_from_reg_u32(cap_header, + PCI_CAP_NEXT_PTR_MASK, PCI_CAP_NEXT_PTR_SHIFT); + ++count; } + + return error; +} + +static int __init scan_functions_for_iommu(int bus, int dev, + iommu_detect_callback_ptr_t iommu_detect_callback) +{ + int func, hdr_type; + int count, error = 0; + + func = 0; + count = 1; + while ( VALID_PCI_VENDOR_ID(read_pci_config_16(bus, dev, func, + PCI_VENDOR_ID)) && !error && func < count ) { + hdr_type = read_pci_config_byte(bus, dev, func, + PCI_HEADER_TYPE); + + if ( func == 0 && IS_PCI_MULTI_FUNCTION(hdr_type) ) + count = PCI_MAX_FUNC_COUNT; + + if ( IS_PCI_TYPE0_HEADER(hdr_type) || + IS_PCI_TYPE1_HEADER(hdr_type) ) { + error = scan_caps_for_iommu(bus, dev, func, + iommu_detect_callback); + } + ++func; + } + + return error; +} + + +int __init scan_for_iommu(iommu_detect_callback_ptr_t iommu_detect_callback) +{ + int bus, dev, error = 0; + + for ( bus = 0; bus < PCI_MAX_BUS_COUNT && !error; ++bus ) { + for ( dev = 0; dev < PCI_MAX_DEV_COUNT && !error; ++dev ) { + error = scan_functions_for_iommu(bus, dev, + iommu_detect_callback); + } + } + + return error; +} + diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/amd/iommu_init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd/iommu_init.c Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran <leo.duran@xxxxxxx> + * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <xen/config.h> +#include <xen/errno.h> +#include <asm/amd-iommu.h> +#include <asm/hvm/svm/amd-iommu-proto.h> +#include <asm-x86/fixmap.h> +#include "../pci-direct.h" +#include "../pci_regs.h" + +extern int nr_amd_iommus; + +int __init map_iommu_mmio_region(struct amd_iommu *iommu) +{ + unsigned long mfn; + + if ( nr_amd_iommus > MAX_AMD_IOMMUS ) { + gdprintk(XENLOG_ERR, + "IOMMU: nr_amd_iommus %d > MAX_IOMMUS\n", nr_amd_iommus); + return -ENOMEM; + } + + iommu->mmio_base = (void *) fix_to_virt(FIX_IOMMU_MMIO_BASE_0 + + nr_amd_iommus * MMIO_PAGES_PER_IOMMU); + mfn = (unsigned long)iommu->mmio_base_phys >> PAGE_SHIFT; + map_pages_to_xen((unsigned long)iommu->mmio_base, mfn, + MMIO_PAGES_PER_IOMMU, PAGE_HYPERVISOR_NOCACHE); + + memset((u8*)iommu->mmio_base, 0, IOMMU_MMIO_REGION_LENGTH); + + return 0; +} + +void __init unmap_iommu_mmio_region(struct amd_iommu *iommu) +{ + if ( iommu->mmio_base ) { + iounmap(iommu->mmio_base); + iommu->mmio_base = NULL; + } +} + +void __init register_iommu_dev_table_in_mmio_space(struct amd_iommu *iommu) +{ + u64 addr_64, addr_lo, addr_hi; + u32 entry; + + addr_64 = (u64)virt_to_maddr(iommu->dev_table.buffer); + addr_lo = addr_64 & DMA_32BIT_MASK; + addr_hi = addr_64 >> 32; + + set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, + IOMMU_DEV_TABLE_BASE_LOW_MASK, + IOMMU_DEV_TABLE_BASE_LOW_SHIFT, &entry); + set_field_in_reg_u32((iommu->dev_table.alloc_size / PAGE_SIZE) - 1, + entry, IOMMU_DEV_TABLE_SIZE_MASK, + IOMMU_DEV_TABLE_SIZE_SHIFT, &entry); + writel(entry, iommu->mmio_base + IOMMU_DEV_TABLE_BASE_LOW_OFFSET); + + set_field_in_reg_u32((u32)addr_hi, 0, + IOMMU_DEV_TABLE_BASE_HIGH_MASK, + IOMMU_DEV_TABLE_BASE_HIGH_SHIFT, &entry); + writel(entry, iommu->mmio_base + IOMMU_DEV_TABLE_BASE_HIGH_OFFSET); +} + +void __init register_iommu_cmd_buffer_in_mmio_space(struct amd_iommu *iommu) +{ + u64 addr_64, addr_lo, addr_hi; + u32 power_of2_entries; + u32 entry; + + addr_64 = (u64)virt_to_maddr(iommu->cmd_buffer.buffer); + addr_lo = addr_64 & DMA_32BIT_MASK; + addr_hi = addr_64 >> 32; + + set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, + IOMMU_CMD_BUFFER_BASE_LOW_MASK, + IOMMU_CMD_BUFFER_BASE_LOW_SHIFT, &entry); + writel(entry, iommu->mmio_base + IOMMU_CMD_BUFFER_BASE_LOW_OFFSET); + + power_of2_entries = get_order_from_bytes(iommu->cmd_buffer.alloc_size) + + IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE; + + set_field_in_reg_u32((u32)addr_hi, 0, + IOMMU_CMD_BUFFER_BASE_HIGH_MASK, + IOMMU_CMD_BUFFER_BASE_HIGH_SHIFT, &entry); + set_field_in_reg_u32(power_of2_entries, entry, + IOMMU_CMD_BUFFER_LENGTH_MASK, + IOMMU_CMD_BUFFER_LENGTH_SHIFT, &entry); + writel(entry, iommu->mmio_base+IOMMU_CMD_BUFFER_BASE_HIGH_OFFSET); +} + +static void __init set_iommu_translation_control(struct amd_iommu *iommu, + int enable) +{ + u32 entry; + + entry = readl(iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); + set_field_in_reg_u32(iommu->ht_tunnel_support ? IOMMU_CONTROL_ENABLED : + IOMMU_CONTROL_ENABLED, entry, + IOMMU_CONTROL_HT_TUNNEL_TRANSLATION_MASK, + IOMMU_CONTROL_HT_TUNNEL_TRANSLATION_SHIFT, &entry); + set_field_in_reg_u32(enable ? IOMMU_CONTROL_ENABLED : + IOMMU_CONTROL_ENABLED, entry, + IOMMU_CONTROL_TRANSLATION_ENABLE_MASK, + IOMMU_CONTROL_TRANSLATION_ENABLE_SHIFT, &entry); + writel(entry, iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); +} + +static void __init set_iommu_command_buffer_control(struct amd_iommu *iommu, + int enable) +{ + u32 entry; + + entry = readl(iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); + set_field_in_reg_u32(enable ? IOMMU_CONTROL_ENABLED : + IOMMU_CONTROL_ENABLED, entry, + IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_MASK, + IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_SHIFT, &entry); + writel(entry, iommu->mmio_base+IOMMU_CONTROL_MMIO_OFFSET); +} + +void __init enable_iommu(struct amd_iommu *iommu) +{ + set_iommu_command_buffer_control(iommu, IOMMU_CONTROL_ENABLED); + set_iommu_translation_control(iommu, IOMMU_CONTROL_ENABLED); + printk("AMD IOMMU %d: Enabled\n", nr_amd_iommus); +} + + diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/amd/iommu_map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd/iommu_map.c Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran <leo.duran@xxxxxxx> + * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <xen/sched.h> +#include <asm/hvm/iommu.h> +#include <asm/amd-iommu.h> +#include <asm/hvm/svm/amd-iommu-proto.h> + +extern long amd_iommu_poll_comp_wait; + +static int queue_iommu_command(struct amd_iommu *iommu, u32 cmd[]) +{ + u32 tail, head, *cmd_buffer; + int i; + + tail = iommu->cmd_buffer_tail; + if ( ++tail == iommu->cmd_buffer.entries ) + tail = 0; + head = get_field_from_reg_u32( + readl(iommu->mmio_base+IOMMU_CMD_BUFFER_HEAD_OFFSET), + IOMMU_CMD_BUFFER_HEAD_MASK, + IOMMU_CMD_BUFFER_HEAD_SHIFT); + if ( head != tail ) + { + cmd_buffer = (u32 *)(iommu->cmd_buffer.buffer + + (iommu->cmd_buffer_tail * + IOMMU_CMD_BUFFER_ENTRY_SIZE)); + for ( i = 0; i < IOMMU_CMD_BUFFER_U32_PER_ENTRY; i++ ) + cmd_buffer[i] = cmd[i]; + + iommu->cmd_buffer_tail = tail; + return 1; + } + + return 0; +} + +static void commit_iommu_command_buffer(struct amd_iommu *iommu) +{ + u32 tail; + + set_field_in_reg_u32(iommu->cmd_buffer_tail, 0, + IOMMU_CMD_BUFFER_TAIL_MASK, + IOMMU_CMD_BUFFER_TAIL_SHIFT, &tail); + writel(tail, iommu->mmio_base+IOMMU_CMD_BUFFER_TAIL_OFFSET); +} + +int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]) +{ + if ( queue_iommu_command(iommu, cmd) ) + { + commit_iommu_command_buffer(iommu); + return 1; + } + + return 0; +} + +static void invalidate_iommu_page(struct amd_iommu *iommu, + u64 io_addr, u16 domain_id) +{ + u64 addr_lo, addr_hi; + u32 cmd[4], entry; + + addr_lo = io_addr & DMA_32BIT_MASK; + addr_hi = io_addr >> 32; + + set_field_in_reg_u32(domain_id, 0, + IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_MASK, + IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CMD_INVALIDATE_IOMMU_PAGES, entry, + IOMMU_CMD_OPCODE_MASK, IOMMU_CMD_OPCODE_SHIFT, + &entry); + cmd[1] = entry; + + set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, 0, + IOMMU_INV_IOMMU_PAGES_S_FLAG_MASK, + IOMMU_INV_IOMMU_PAGES_S_FLAG_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, entry, + IOMMU_INV_IOMMU_PAGES_PDE_FLAG_MASK, + IOMMU_INV_IOMMU_PAGES_PDE_FLAG_SHIFT, &entry); + set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, entry, + IOMMU_INV_IOMMU_PAGES_ADDR_LOW_MASK, + IOMMU_INV_IOMMU_PAGES_ADDR_LOW_SHIFT, &entry); + cmd[2] = entry; + + set_field_in_reg_u32((u32)addr_hi, 0, + IOMMU_INV_IOMMU_PAGES_ADDR_HIGH_MASK, + IOMMU_INV_IOMMU_PAGES_ADDR_HIGH_SHIFT, &entry); + cmd[3] = entry; + + cmd[0] = 0; + send_iommu_command(iommu, cmd); +} + +void flush_command_buffer(struct amd_iommu *iommu) +{ + u32 cmd[4], status; + int loop_count, comp_wait; + + /* clear 'ComWaitInt' in status register (WIC) */ + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, 0, + IOMMU_STATUS_COMP_WAIT_INT_MASK, + IOMMU_STATUS_COMP_WAIT_INT_SHIFT, &status); + writel(status, iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); + + /* send an empty COMPLETION_WAIT command to flush command buffer */ + cmd[3] = cmd[2] = 0; + set_field_in_reg_u32(IOMMU_CMD_COMPLETION_WAIT, 0, + IOMMU_CMD_OPCODE_MASK, + IOMMU_CMD_OPCODE_SHIFT, &cmd[1]); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, 0, + IOMMU_COMP_WAIT_I_FLAG_MASK, + IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]); + send_iommu_command(iommu, cmd); + + /* wait for 'ComWaitInt' to signal comp#endifletion? */ + if ( amd_iommu_poll_comp_wait ) { + loop_count = amd_iommu_poll_comp_wait; + do { + status = readl(iommu->mmio_base + + IOMMU_STATUS_MMIO_OFFSET); + comp_wait = get_field_from_reg_u32( + status, + IOMMU_STATUS_COMP_WAIT_INT_MASK, + IOMMU_STATUS_COMP_WAIT_INT_SHIFT); + --loop_count; + } while ( loop_count && !comp_wait ); + + if ( comp_wait ) + { + /* clear 'ComWaitInt' in status register (WIC) */ + status &= IOMMU_STATUS_COMP_WAIT_INT_MASK; + writel(status, iommu->mmio_base + + IOMMU_STATUS_MMIO_OFFSET); + } + else + dprintk(XENLOG_WARNING, "AMD IOMMU: Warning:" + " ComWaitInt bit did not assert!\n"); + } +} + +static void clear_page_table_entry_present(u32 *pte) +{ + set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, pte[0], + IOMMU_PTE_PRESENT_MASK, + IOMMU_PTE_PRESENT_SHIFT, &pte[0]); +} + +static void set_page_table_entry_present(u32 *pte, u64 page_addr, + int iw, int ir) +{ + u64 addr_lo, addr_hi; + u32 entry; + + addr_lo = page_addr & DMA_32BIT_MASK; + addr_hi = page_addr >> 32; + + set_field_in_reg_u32((u32)addr_hi, 0, + IOMMU_PTE_ADDR_HIGH_MASK, + IOMMU_PTE_ADDR_HIGH_SHIFT, &entry); + set_field_in_reg_u32(iw ? IOMMU_CONTROL_ENABLED : + IOMMU_CONTROL_DISABLED, entry, + IOMMU_PTE_IO_WRITE_PERMISSION_MASK, + IOMMU_PTE_IO_WRITE_PERMISSION_SHIFT, &entry); + set_field_in_reg_u32(ir ? IOMMU_CONTROL_ENABLED : + IOMMU_CONTROL_DISABLED, entry, + IOMMU_PTE_IO_READ_PERMISSION_MASK, + IOMMU_PTE_IO_READ_PERMISSION_SHIFT, &entry); + pte[1] = entry; + + set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, + IOMMU_PTE_ADDR_LOW_MASK, + IOMMU_PTE_ADDR_LOW_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_PAGING_MODE_LEVEL_0, entry, + IOMMU_PTE_NEXT_LEVEL_MASK, + IOMMU_PTE_NEXT_LEVEL_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_PTE_PRESENT_MASK, + IOMMU_PTE_PRESENT_SHIFT, &entry); + pte[0] = entry; +} + + +static void amd_iommu_set_page_directory_entry(u32 *pde, + u64 next_ptr, u8 next_level) +{ + u64 addr_lo, addr_hi; + u32 entry; + + addr_lo = next_ptr & DMA_32BIT_MASK; + addr_hi = next_ptr >> 32; + + /* enable read/write permissions,which will be enforced at the PTE */ + set_field_in_reg_u32((u32)addr_hi, 0, + IOMMU_PDE_ADDR_HIGH_MASK, + IOMMU_PDE_ADDR_HIGH_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_PDE_IO_WRITE_PERMISSION_MASK, + IOMMU_PDE_IO_WRITE_PERMISSION_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_PDE_IO_READ_PERMISSION_MASK, + IOMMU_PDE_IO_READ_PERMISSION_SHIFT, &entry); + pde[1] = entry; + + /* mark next level as 'present' */ + set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, + IOMMU_PDE_ADDR_LOW_MASK, + IOMMU_PDE_ADDR_LOW_SHIFT, &entry); + set_field_in_reg_u32(next_level, entry, + IOMMU_PDE_NEXT_LEVEL_MASK, + IOMMU_PDE_NEXT_LEVEL_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_PDE_PRESENT_MASK, + IOMMU_PDE_PRESENT_SHIFT, &entry); + pde[0] = entry; +} + +void amd_iommu_set_dev_table_entry(u32 *dte, u64 root_ptr, u16 domain_id, + u8 paging_mode) +{ + u64 addr_hi, addr_lo; + u32 entry; + + dte[6] = dte[5] = dte[4] = 0; + + set_field_in_reg_u32(IOMMU_DEV_TABLE_SYS_MGT_MSG_FORWARDED, 0, + IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_MASK, + IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_SHIFT, &entry); + dte[3] = entry; + + set_field_in_reg_u32(domain_id, 0, + IOMMU_DEV_TABLE_DOMAIN_ID_MASK, + IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT, &entry); + dte[2] = entry; + + addr_lo = root_ptr & DMA_32BIT_MASK; + addr_hi = root_ptr >> 32; + set_field_in_reg_u32((u32)addr_hi, 0, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_MASK, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_DEV_TABLE_IO_WRITE_PERMISSION_MASK, + IOMMU_DEV_TABLE_IO_WRITE_PERMISSION_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_DEV_TABLE_IO_READ_PERMISSION_MASK, + IOMMU_DEV_TABLE_IO_READ_PERMISSION_SHIFT, &entry); + dte[1] = entry; + + set_field_in_reg_u32((u32)addr_lo >> PAGE_SHIFT, 0, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_MASK, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT, &entry); + set_field_in_reg_u32(paging_mode, entry, + IOMMU_DEV_TABLE_PAGING_MODE_MASK, + IOMMU_DEV_TABLE_PAGING_MODE_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_DEV_TABLE_TRANSLATION_VALID_MASK, + IOMMU_DEV_TABLE_TRANSLATION_VALID_SHIFT, &entry); + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_DEV_TABLE_VALID_MASK, + IOMMU_DEV_TABLE_VALID_SHIFT, &entry); + dte[0] = entry; +} + +void *amd_iommu_get_vptr_from_page_table_entry(u32 *entry) +{ + u64 addr_lo, addr_hi, ptr; + + addr_lo = get_field_from_reg_u32( + entry[0], + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_MASK, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT); + + addr_hi = get_field_from_reg_u32( + entry[1], + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_MASK, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_SHIFT); + + ptr = (addr_hi << 32) | (addr_lo << PAGE_SHIFT); + return ptr ? maddr_to_virt((unsigned long)ptr) : NULL; +} + +static int amd_iommu_is_pte_present(u32 *entry) +{ + return (get_field_from_reg_u32(entry[0], + IOMMU_PDE_PRESENT_MASK, + IOMMU_PDE_PRESENT_SHIFT)); +} + +void invalidate_dev_table_entry(struct amd_iommu *iommu, + u16 device_id) +{ + u32 cmd[4], entry; + + cmd[3] = cmd[2] = 0; + set_field_in_reg_u32(device_id, 0, + IOMMU_INV_DEVTAB_ENTRY_DEVICE_ID_MASK, + IOMMU_INV_DEVTAB_ENTRY_DEVICE_ID_SHIFT, &entry); + cmd[0] = entry; + + set_field_in_reg_u32(IOMMU_CMD_INVALIDATE_DEVTAB_ENTRY, 0, + IOMMU_CMD_OPCODE_MASK, IOMMU_CMD_OPCODE_SHIFT, + &entry); + cmd[1] = entry; + + send_iommu_command(iommu, cmd); +} + +int amd_iommu_is_dte_page_translation_valid(u32 *entry) +{ + return (get_field_from_reg_u32(entry[0], + IOMMU_DEV_TABLE_VALID_MASK, + IOMMU_DEV_TABLE_VALID_SHIFT) && + get_field_from_reg_u32(entry[0], + IOMMU_DEV_TABLE_TRANSLATION_VALID_MASK, + IOMMU_DEV_TABLE_TRANSLATION_VALID_SHIFT)); +} + +static void *get_pte_from_page_tables(void *table, int level, + unsigned long io_pfn) +{ + unsigned long offset; + void *pde = NULL; + + BUG_ON(table == NULL); + + while ( level > 0 ) + { + offset = io_pfn >> ((PTE_PER_TABLE_SHIFT * + (level - IOMMU_PAGING_MODE_LEVEL_1))); + offset &= ~PTE_PER_TABLE_MASK; + pde = table + (offset * IOMMU_PAGE_TABLE_ENTRY_SIZE); + + if ( level == 1 ) + break; + if ( !pde ) + return NULL; + if ( !amd_iommu_is_pte_present(pde) ) + { + void *next_table = alloc_xenheap_page(); + if ( next_table == NULL ) + return NULL; + memset(next_table, 0, PAGE_SIZE); + if ( *(u64 *)pde == 0 ) + { + unsigned long next_ptr = (u64)virt_to_maddr(next_table); + amd_iommu_set_page_directory_entry( + (u32 *)pde, next_ptr, level - 1); + } + else + { + free_xenheap_page(next_table); + } + } + table = amd_iommu_get_vptr_from_page_table_entry(pde); + level--; + } + + return pde; +} + +int amd_iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn) +{ + void *pte; + unsigned long flags; + u64 maddr; + struct hvm_iommu *hd = domain_hvm_iommu(d); + int iw, ir; + + BUG_ON( !hd->root_table ); + + maddr = (u64)mfn << PAGE_SHIFT; + + iw = IOMMU_IO_WRITE_ENABLED; + ir = IOMMU_IO_READ_ENABLED; + + spin_lock_irqsave(&hd->mapping_lock, flags); + + pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn); + if ( pte == 0 ) + { + dprintk(XENLOG_ERR, + "AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", gfn); + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return -EIO; + } + + set_page_table_entry_present((u32 *)pte, maddr, iw, ir); + + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return 0; +} + +int amd_iommu_unmap_page(struct domain *d, unsigned long gfn) +{ + void *pte; + unsigned long flags; + u64 io_addr = gfn; + int requestor_id; + struct amd_iommu *iommu; + struct hvm_iommu *hd = domain_hvm_iommu(d); + + BUG_ON( !hd->root_table ); + + requestor_id = hd->domain_id; + io_addr = (u64)gfn << PAGE_SHIFT; + + spin_lock_irqsave(&hd->mapping_lock, flags); + + pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn); + if ( pte == 0 ) + { + dprintk(XENLOG_ERR, + "AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", gfn); + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return -EIO; + } + + /* mark PTE as 'page not present' */ + clear_page_table_entry_present((u32 *)pte); + spin_unlock_irqrestore(&hd->mapping_lock, flags); + + /* send INVALIDATE_IOMMU_PAGES command */ + for_each_amd_iommu(iommu) + { + spin_lock_irqsave(&iommu->lock, flags); + invalidate_iommu_page(iommu, io_addr, requestor_id); + flush_command_buffer(iommu); + spin_unlock_irqrestore(&iommu->lock, flags); + } + + return 0; +} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/amd/pci_amd_iommu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran <leo.duran@xxxxxxx> + * Author: Wei Wang <wei.wang2@xxxxxxx> - adapted to xen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <asm/amd-iommu.h> +#include <asm/hvm/svm/amd-iommu-proto.h> +#include <xen/sched.h> +#include <asm/mm.h> +#include "../pci-direct.h" +#include "../pci_regs.h" + +struct list_head amd_iommu_head; +long amd_iommu_poll_comp_wait = COMPLETION_WAIT_DEFAULT_POLLING_COUNT; +static long amd_iommu_cmd_buffer_entries = IOMMU_CMD_BUFFER_DEFAULT_ENTRIES; +int nr_amd_iommus = 0; + +/* will set if amd-iommu HW is found */ +int amd_iommu_enabled = 0; + +static int enable_amd_iommu = 0; +boolean_param("enable_amd_iommu", enable_amd_iommu); + +static void deallocate_domain_page_tables(struct hvm_iommu *hd) +{ + if ( hd->root_table ) + free_xenheap_page(hd->root_table); +} + +static void deallocate_domain_resources(struct hvm_iommu *hd) +{ + deallocate_domain_page_tables(hd); +} + +static void __init init_cleanup(void) +{ + struct amd_iommu *iommu; + + for_each_amd_iommu ( iommu ) + unmap_iommu_mmio_region(iommu); +} + +static void __init deallocate_iommu_table_struct( + struct table_struct *table) +{ + if ( table->buffer ) + { + free_xenheap_pages(table->buffer, + get_order_from_bytes(table->alloc_size)); + table->buffer = NULL; + } +} + +static void __init deallocate_iommu_resources(struct amd_iommu *iommu) +{ + deallocate_iommu_table_struct(&iommu->dev_table); + deallocate_iommu_table_struct(&iommu->cmd_buffer);; +} + +static void __init detect_cleanup(void) +{ + struct amd_iommu *iommu, *next; + + list_for_each_entry_safe ( iommu, next, &amd_iommu_head, list ) + { + list_del(&iommu->list); + deallocate_iommu_resources(iommu); + xfree(iommu); + } +} + +static int requestor_id_from_bdf(int bdf) +{ + /* HACK - HACK */ + /* account for possible 'aliasing' by parent device */ + return bdf; +} + +static int __init allocate_iommu_table_struct(struct table_struct *table, + const char *name) +{ + table->buffer = (void *) alloc_xenheap_pages( + get_order_from_bytes(table->alloc_size)); + + if ( !table->buffer ) + { + dprintk(XENLOG_ERR, "AMD IOMMU: Error allocating %s\n", name); + return -ENOMEM; + } + + memset(table->buffer, 0, table->alloc_size); + + return 0; +} + +static int __init allocate_iommu_resources(struct amd_iommu *iommu) +{ + /* allocate 'device table' on a 4K boundary */ + iommu->dev_table.alloc_size = + PAGE_ALIGN(((iommu->last_downstream_bus + 1) * + IOMMU_DEV_TABLE_ENTRIES_PER_BUS) * + IOMMU_DEV_TABLE_ENTRY_SIZE); + iommu->dev_table.entries = + iommu->dev_table.alloc_size / IOMMU_DEV_TABLE_ENTRY_SIZE; + + if ( allocate_iommu_table_struct(&iommu->dev_table, + "Device Table") != 0 ) + goto error_out; + + /* allocate 'command buffer' in power of 2 increments of 4K */ + iommu->cmd_buffer_tail = 0; + iommu->cmd_buffer.alloc_size = + PAGE_SIZE << get_order_from_bytes( + PAGE_ALIGN(amd_iommu_cmd_buffer_entries * + IOMMU_CMD_BUFFER_ENTRY_SIZE)); + + iommu->cmd_buffer.entries = + iommu->cmd_buffer.alloc_size / IOMMU_CMD_BUFFER_ENTRY_SIZE; + + if ( allocate_iommu_table_struct(&iommu->cmd_buffer, + "Command Buffer") != 0 ) + goto error_out; + + return 0; + + error_out: + deallocate_iommu_resources(iommu); + return -ENOMEM; +} + +int iommu_detect_callback(u8 bus, u8 dev, u8 func, u8 cap_ptr) +{ + struct amd_iommu *iommu; + + iommu = (struct amd_iommu *) xmalloc(struct amd_iommu); + if ( !iommu ) + { + dprintk(XENLOG_ERR, "AMD IOMMU: Error allocating amd_iommu\n"); + return -ENOMEM; + } + memset(iommu, 0, sizeof(struct amd_iommu)); + spin_lock_init(&iommu->lock); + + /* get capability and topology information */ + if ( get_iommu_capabilities(bus, dev, func, cap_ptr, iommu) != 0 ) + goto error_out; + if ( get_iommu_last_downstream_bus(iommu) != 0 ) + goto error_out; + + list_add_tail(&iommu->list, &amd_iommu_head); + + /* allocate resources for this IOMMU */ + if (allocate_iommu_resources(iommu) != 0) + goto error_out; + + return 0; + + error_out: + xfree(iommu); + return -ENODEV; +} + +static int __init amd_iommu_init(void) +{ + struct amd_iommu *iommu; + unsigned long flags; + + for_each_amd_iommu ( iommu ) + { + spin_lock_irqsave(&iommu->lock, flags); + + /* register IOMMU data strucures in MMIO space */ + if ( map_iommu_mmio_region(iommu) != 0 ) + goto error_out; + register_iommu_dev_table_in_mmio_space(iommu); + register_iommu_cmd_buffer_in_mmio_space(iommu); + + /* enable IOMMU translation services */ + enable_iommu(iommu); + nr_amd_iommus++; + + spin_unlock_irqrestore(&iommu->lock, flags); + } + + amd_iommu_enabled = 1; + + return 0; + + error_out: + init_cleanup(); + return -ENODEV; +} + +struct amd_iommu *find_iommu_for_device(int bus, int devfn) +{ + struct amd_iommu *iommu; + + for_each_amd_iommu ( iommu ) + { + if ( bus == iommu->root_bus ) + { + if ( (devfn >= iommu->first_devfn) && + (devfn <= iommu->last_devfn) ) + return iommu; + } + else if ( bus <= iommu->last_downstream_bus ) + { + if ( iommu->downstream_bus_present[bus] ) + return iommu; + } + } + + return NULL; +} + +void amd_iommu_setup_domain_device( + struct domain *domain, struct amd_iommu *iommu, int requestor_id) +{ + void *dte; + u64 root_ptr; + unsigned long flags; + struct hvm_iommu *hd = domain_hvm_iommu(domain); + + BUG_ON( !hd->root_table||!hd->paging_mode ); + + root_ptr = (u64)virt_to_maddr(hd->root_table); + dte = iommu->dev_table.buffer + + (requestor_id * IOMMU_DEV_TABLE_ENTRY_SIZE); + + if ( !amd_iommu_is_dte_page_translation_valid((u32 *)dte) ) + { + spin_lock_irqsave(&iommu->lock, flags); + + amd_iommu_set_dev_table_entry( + (u32 *)dte, + root_ptr, hd->domain_id, hd->paging_mode); + invalidate_dev_table_entry(iommu, requestor_id); + flush_command_buffer(iommu); + dprintk(XENLOG_INFO, "AMD IOMMU: Set DTE req_id:%x, " + "root_ptr:%"PRIx64", domain_id:%d, paging_mode:%d\n", + requestor_id, root_ptr, hd->domain_id, hd->paging_mode); + + spin_unlock_irqrestore(&iommu->lock, flags); + } +} + +void __init amd_iommu_setup_dom0_devices(void) +{ + struct hvm_iommu *hd = domain_hvm_iommu(dom0); + struct amd_iommu *iommu; + struct pci_dev *pdev; + int bus, dev, func; + u32 l; + int req_id, bdf; + + for ( bus = 0; bus < 256; bus++ ) + { + for ( dev = 0; dev < 32; dev++ ) + { + for ( func = 0; func < 8; func++ ) + { + l = read_pci_config(bus, dev, func, PCI_VENDOR_ID); + /* some broken boards return 0 or ~0 if a slot is empty: */ + if ( l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000 ) + continue; + + pdev = xmalloc(struct pci_dev); + pdev->bus = bus; + pdev->devfn = PCI_DEVFN(dev, func); + list_add_tail(&pdev->list, &hd->pdev_list); + + bdf = (bus << 8) | pdev->devfn; + req_id = requestor_id_from_bdf(bdf); + iommu = find_iommu_for_device(bus, pdev->devfn); + + if ( iommu ) + amd_iommu_setup_domain_device(dom0, iommu, req_id); + } + } + } +} + +int amd_iommu_detect(void) +{ + unsigned long i; + + if ( !enable_amd_iommu ) + { + printk("AMD IOMMU: Disabled\n"); + return 0; + } + + INIT_LIST_HEAD(&amd_iommu_head); + + if ( scan_for_iommu(iommu_detect_callback) != 0 ) + { + dprintk(XENLOG_ERR, "AMD IOMMU: Error detection\n"); + goto error_out; + } + + if ( !iommu_found() ) + { + printk("AMD IOMMU: Not found!\n"); + return 0; + } + + if ( amd_iommu_init() != 0 ) + { + dprintk(XENLOG_ERR, "AMD IOMMU: Error initialization\n"); + goto error_out; + } + + if ( iommu_domain_init(dom0) != 0 ) + goto error_out; + + /* setup 1:1 page table for dom0 */ + for ( i = 0; i < max_page; i++ ) + amd_iommu_map_page(dom0, i, i); + + amd_iommu_setup_dom0_devices(); + return 0; + + error_out: + detect_cleanup(); + return -ENODEV; + +} + +static int allocate_domain_resources(struct hvm_iommu *hd) +{ + /* allocate root table */ + unsigned long flags; + + spin_lock_irqsave(&hd->mapping_lock, flags); + if ( !hd->root_table ) + { + hd->root_table = (void *)alloc_xenheap_page(); + if ( !hd->root_table ) + goto error_out; + memset((u8*)hd->root_table, 0, PAGE_SIZE); + } + spin_unlock_irqrestore(&hd->mapping_lock, flags); + + return 0; + error_out: + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return -ENOMEM; +} + +static int get_paging_mode(unsigned long entries) +{ + int level = 1; + + BUG_ON ( !max_page ); + + if ( entries > max_page ) + entries = max_page; + + while ( entries > PTE_PER_TABLE_SIZE ) + { + entries = PTE_PER_TABLE_ALIGN(entries) >> PTE_PER_TABLE_SHIFT; + ++level; + if ( level > 6 ) + return -ENOMEM; + } + + dprintk(XENLOG_INFO, "AMD IOMMU: paging mode = %d\n", level); + + return level; +} + +int amd_iommu_domain_init(struct domain *domain) +{ + struct hvm_iommu *hd = domain_hvm_iommu(domain); + + /* allocate page directroy */ + if ( allocate_domain_resources(hd) != 0 ) + { + deallocate_domain_resources(hd); + return -ENOMEM; + } + + if ( is_hvm_domain(domain) ) + hd->paging_mode = IOMMU_PAGE_TABLE_LEVEL_4; + else + hd->paging_mode = get_paging_mode(max_page); + + hd->domain_id = domain->domain_id; + + return 0; +} + +static void amd_iommu_disable_domain_device( + struct domain *domain, struct amd_iommu *iommu, u16 requestor_id) +{ + void *dte; + unsigned long flags; + + dte = iommu->dev_table.buffer + + (requestor_id * IOMMU_DEV_TABLE_ENTRY_SIZE); + + if ( amd_iommu_is_dte_page_translation_valid((u32 *)dte) ) + { + spin_lock_irqsave(&iommu->lock, flags); + memset (dte, 0, IOMMU_DEV_TABLE_ENTRY_SIZE); + invalidate_dev_table_entry(iommu, requestor_id); + flush_command_buffer(iommu); + dprintk(XENLOG_INFO , "AMD IOMMU: disable DTE 0x%x," + " domain_id:%d, paging_mode:%d\n", + requestor_id, domain_hvm_iommu(domain)->domain_id, + domain_hvm_iommu(domain)->paging_mode); + spin_unlock_irqrestore(&iommu->lock, flags); + } +} + +extern void pdev_flr(u8 bus, u8 devfn); + +static int reassign_device( struct domain *source, struct domain *target, + u8 bus, u8 devfn) +{ + struct hvm_iommu *source_hd = domain_hvm_iommu(source); + struct hvm_iommu *target_hd = domain_hvm_iommu(target); + struct pci_dev *pdev; + struct amd_iommu *iommu; + int req_id, bdf; + unsigned long flags; + + for_each_pdev( source, pdev ) + { + if ( (pdev->bus != bus) || (pdev->devfn != devfn) ) + continue; + + pdev->bus = bus; + pdev->devfn = devfn; + + bdf = (bus << 8) | devfn; + req_id = requestor_id_from_bdf(bdf); + iommu = find_iommu_for_device(bus, devfn); + + if ( iommu ) + { + amd_iommu_disable_domain_device(source, iommu, req_id); + /* Move pci device from the source domain to target domain. */ + spin_lock_irqsave(&source_hd->iommu_list_lock, flags); + spin_lock_irqsave(&target_hd->iommu_list_lock, flags); + list_move(&pdev->list, &target_hd->pdev_list); + spin_unlock_irqrestore(&target_hd->iommu_list_lock, flags); + spin_unlock_irqrestore(&source_hd->iommu_list_lock, flags); + + amd_iommu_setup_domain_device(target, iommu, req_id); + gdprintk(XENLOG_INFO , + "AMD IOMMU: reassign %x:%x.%x domain %d -> domain %d\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + source->domain_id, target->domain_id); + } + else + { + gdprintk(XENLOG_ERR , "AMD IOMMU: fail to find iommu." + " %x:%x.%x cannot be assigned to domain %d\n", + bus, PCI_SLOT(devfn), PCI_FUNC(devfn), target->domain_id); + return -ENODEV; + } + + break; + } + return 0; +} + +int amd_iommu_assign_device(struct domain *d, u8 bus, u8 devfn) +{ + pdev_flr(bus, devfn); + return reassign_device(dom0, d, bus, devfn); +} + +static void release_domain_devices(struct domain *d) +{ + struct hvm_iommu *hd = domain_hvm_iommu(d); + struct pci_dev *pdev; + + while ( !list_empty(&hd->pdev_list) ) + { + pdev = list_entry(hd->pdev_list.next, typeof(*pdev), list); + pdev_flr(pdev->bus, pdev->devfn); + gdprintk(XENLOG_INFO , + "AMD IOMMU: release devices %x:%x.%x\n", + pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + reassign_device(d, dom0, pdev->bus, pdev->devfn); + } +} + +static void deallocate_next_page_table(void *table, unsigned long index, + int level) +{ + unsigned long next_index; + void *next_table, *pde; + int next_level; + + pde = table + (index * IOMMU_PAGE_TABLE_ENTRY_SIZE); + next_table = amd_iommu_get_vptr_from_page_table_entry((u32 *)pde); + + if ( next_table ) + { + next_level = level - 1; + if ( next_level > 1 ) + { + next_index = 0; + do + { + deallocate_next_page_table(next_table, + next_index, next_level); + ++next_index; + } while (next_index < PTE_PER_TABLE_SIZE); + } + + free_xenheap_page(next_table); + } +} + +static void deallocate_iommu_page_tables(struct domain *d) +{ + unsigned long index; + struct hvm_iommu *hd = domain_hvm_iommu(d); + + if ( hd ->root_table ) + { + index = 0; + do + { + deallocate_next_page_table(hd->root_table, + index, hd->paging_mode); + ++index; + } while ( index < PTE_PER_TABLE_SIZE ); + + free_xenheap_page(hd ->root_table); + } + + hd ->root_table = NULL; +} + +void amd_iommu_domain_destroy(struct domain *d) +{ + if ( !amd_iommu_enabled ) + return; + + deallocate_iommu_page_tables(d); + release_domain_devices(d); +} + +void amd_iommu_return_device(struct domain *s, struct domain *t, u8 bus, u8 devfn) +{ + pdev_flr(bus, devfn); + reassign_device(s, t, bus, devfn); +} + +struct iommu_ops amd_iommu_ops = { + .init = amd_iommu_domain_init, + .assign_device = amd_iommu_assign_device, + .teardown = amd_iommu_domain_destroy, + .map_page = amd_iommu_map_page, + .unmap_page = amd_iommu_unmap_page, + .reassign_device = amd_iommu_return_device, +}; diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/pci-direct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/pci-direct.h Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,48 @@ +#ifndef ASM_PCI_DIRECT_H +#define ASM_PCI_DIRECT_H 1 + +#include <xen/types.h> +#include <asm/io.h> + +/* Direct PCI access. This is used for PCI accesses in early boot before + the PCI subsystem works. */ + +#define PDprintk(x...) + +static inline u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset) +{ + u32 v; + outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); + v = inl(0xcfc); + if (v != 0xffffffff) + PDprintk("%x reading 4 from %x: %x\n", slot, offset, v); + return v; +} + +static inline u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset) +{ + u8 v; + outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); + v = inb(0xcfc + (offset&3)); + PDprintk("%x reading 1 from %x: %x\n", slot, offset, v); + return v; +} + +static inline u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset) +{ + u16 v; + outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); + v = inw(0xcfc + (offset&2)); + PDprintk("%x reading 2 from %x: %x\n", slot, offset, v); + return v; +} + +static inline void write_pci_config( + u8 bus, u8 slot, u8 func, u8 offset, u32 val) +{ + PDprintk("%x writing to %x: %x\n", slot, offset, val); + outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8); + outl(val, 0xcfc); +} + +#endif diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/pci_regs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/pci_regs.h Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,530 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares <mj@xxxxxx> + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_BIT 16 /* Mask bits register */ + +/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_FLAGS_BITMASK (1 << 0) + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_COR_SRC 52 +#define PCI_ERR_ROOT_SRC 54 + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + + +#endif /* LINUX_PCI_REGS_H */ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/vtd/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/vtd/Makefile Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,6 @@ +obj-y += iommu.o +obj-y += dmar.o +obj-y += utils.o +obj-y += io.o +obj-y += qinval.o +obj-y += intremap.o diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/vtd/dmar.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/vtd/dmar.c Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,625 @@ +/* + * Copyright (c) 2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Copyright (C) Ashok Raj <ashok.raj@xxxxxxxxx> + * Copyright (C) Shaohua Li <shaohua.li@xxxxxxxxx> + * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> - adapted to xen + */ + +#include <xen/init.h> +#include <xen/bitmap.h> +#include <xen/kernel.h> +#include <xen/acpi.h> +#include <xen/mm.h> +#include <xen/xmalloc.h> +#include <asm/string.h> +#include "dmar.h" +#include "../pci-direct.h" +#include "../pci_regs.h" + +int vtd_enabled; +boolean_param("vtd", vtd_enabled); + +#undef PREFIX +#define PREFIX VTDPREFIX "ACPI DMAR:" +#define DEBUG + +#define MIN_SCOPE_LEN (sizeof(struct acpi_pci_path) + \ + sizeof(struct acpi_dev_scope)) + +LIST_HEAD(acpi_drhd_units); +LIST_HEAD(acpi_rmrr_units); +LIST_HEAD(acpi_atsr_units); + +u8 dmar_host_address_width; + +static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd) +{ + /* + * add INCLUDE_ALL at the tail, so scan the list will find it at + * the very end. + */ + if ( drhd->include_all ) + list_add_tail(&drhd->list, &acpi_drhd_units); + else + list_add(&drhd->list, &acpi_drhd_units); + return 0; +} + +static int __init acpi_register_rmrr_unit(struct acpi_rmrr_unit *rmrr) +{ + list_add(&rmrr->list, &acpi_rmrr_units); + return 0; +} + +static int acpi_ioapic_device_match( + struct list_head *ioapic_list, unsigned int apic_id) +{ + struct acpi_ioapic_unit *ioapic; + list_for_each_entry( ioapic, ioapic_list, list ) { + if (ioapic->apic_id == apic_id) + return 1; + } + return 0; +} + +struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id) +{ + struct acpi_drhd_unit *drhd; + list_for_each_entry( drhd, &acpi_drhd_units, list ) { + if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) ) { + dprintk(XENLOG_INFO VTDPREFIX, + "ioapic_to_drhd: drhd->address = %lx\n", + drhd->address); + return drhd; + } + } + return NULL; +} + +struct iommu * ioapic_to_iommu(unsigned int apic_id) +{ + struct acpi_drhd_unit *drhd; + + list_for_each_entry( drhd, &acpi_drhd_units, list ) { + if ( acpi_ioapic_device_match(&drhd->ioapic_list, apic_id) ) { + dprintk(XENLOG_INFO VTDPREFIX, + "ioapic_to_iommu: drhd->address = %lx\n", + drhd->address); + return drhd->iommu; + } + } + dprintk(XENLOG_INFO VTDPREFIX, "returning NULL\n"); + return NULL; +} + +static int acpi_pci_device_match(struct pci_dev *devices, int cnt, + struct pci_dev *dev) +{ + int i; + + for ( i = 0; i < cnt; i++ ) + { + if ( (dev->bus == devices->bus) && + (dev->devfn == devices->devfn) ) + return 1; + devices++; + } + return 0; +} + +static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr) +{ + /* + * add ALL_PORTS at the tail, so scan the list will find it at + * the very end. + */ + if ( atsr->all_ports ) + list_add_tail(&atsr->list, &acpi_atsr_units); + else + list_add(&atsr->list, &acpi_atsr_units); + return 0; +} + +struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev) +{ + struct acpi_drhd_unit *drhd; + struct acpi_drhd_unit *include_all_drhd; + + include_all_drhd = NULL; + list_for_each_entry ( drhd, &acpi_drhd_units, list ) + { + if ( drhd->include_all ) + { + include_all_drhd = drhd; + continue; + } + + if ( acpi_pci_device_match(drhd->devices, + drhd->devices_cnt, dev) ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "acpi_find_matched_drhd_unit: drhd->address = %lx\n", + drhd->address); + return drhd; + } + } + + if ( include_all_drhd ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "acpi_find_matched_drhd_unit:include_all_drhd->addr = %lx\n", + include_all_drhd->address); + return include_all_drhd; + } + + return NULL; +} + +struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev) +{ + struct acpi_rmrr_unit *rmrr; + + list_for_each_entry ( rmrr, &acpi_rmrr_units, list ) + { + if ( acpi_pci_device_match(rmrr->devices, + rmrr->devices_cnt, dev) ) + return rmrr; + } + + return NULL; +} + +struct acpi_atsr_unit * acpi_find_matched_atsr_unit(struct pci_dev *dev) +{ + struct acpi_atsr_unit *atsru; + struct acpi_atsr_unit *all_ports_atsru; + + all_ports_atsru = NULL; + list_for_each_entry ( atsru, &acpi_atsr_units, list ) + { + if ( atsru->all_ports ) + all_ports_atsru = atsru; + if ( acpi_pci_device_match(atsru->devices, + atsru->devices_cnt, dev) ) + return atsru; + } + + if ( all_ports_atsru ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "acpi_find_matched_atsr_unit: all_ports_atsru\n"); + return all_ports_atsru;; + } + + return NULL; +} + +static int scope_device_count(void *start, void *end) +{ + struct acpi_dev_scope *scope; + u8 bus, sub_bus, sec_bus; + struct acpi_pci_path *path; + int depth, count = 0; + u8 dev, func; + u32 l; + + while ( start < end ) + { + scope = start; + if ( (scope->length < MIN_SCOPE_LEN) || + (scope->dev_type >= ACPI_DEV_ENTRY_COUNT) ) + { + dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope\n"); + return -EINVAL; + } + + path = (struct acpi_pci_path *)(scope + 1); + bus = scope->start_bus; + depth = (scope->length - sizeof(struct acpi_dev_scope)) + / sizeof(struct acpi_pci_path); + while ( --depth ) + { + bus = read_pci_config_byte( + bus, path->dev, path->fn, PCI_SECONDARY_BUS); + path++; + } + + if ( scope->dev_type == ACPI_DEV_ENDPOINT ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "found endpoint: bdf = %x:%x:%x\n", + bus, path->dev, path->fn); + count++; + } + else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "found bridge: bdf = %x:%x:%x\n", + bus, path->dev, path->fn); + sec_bus = read_pci_config_byte( + bus, path->dev, path->fn, PCI_SECONDARY_BUS); + sub_bus = read_pci_config_byte( + bus, path->dev, path->fn, PCI_SUBORDINATE_BUS); + + while ( sec_bus <= sub_bus ) + { + for ( dev = 0; dev < 32; dev++ ) + { + for ( func = 0; func < 8; func++ ) + { + l = read_pci_config( + sec_bus, dev, func, PCI_VENDOR_ID); + + /* some broken boards return 0 or + * ~0 if a slot is empty + */ + if ( l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000 ) + break; + count++; + } + } + sec_bus++; + } + } + else if ( scope->dev_type == ACPI_DEV_IOAPIC ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "found IOAPIC: bdf = %x:%x:%x\n", + bus, path->dev, path->fn); + count++; + } + else + { + dprintk(XENLOG_INFO VTDPREFIX, + "found MSI HPET: bdf = %x:%x:%x\n", + bus, path->dev, path->fn); + count++; + } + + start += scope->length; + } + + return count; +} + +static int __init acpi_parse_dev_scope( + void *start, void *end, void *acpi_entry, int type) +{ + struct acpi_dev_scope *scope; + u8 bus, sub_bus, sec_bus; + struct acpi_pci_path *path; + struct acpi_ioapic_unit *acpi_ioapic_unit = NULL; + int depth; + struct pci_dev *pdev; + u8 dev, func; + u32 l; + + int *cnt = NULL; + struct pci_dev **devices = NULL; + struct acpi_drhd_unit *dmaru = (struct acpi_drhd_unit *) acpi_entry; + struct acpi_rmrr_unit *rmrru = (struct acpi_rmrr_unit *) acpi_entry; + struct acpi_atsr_unit *atsru = (struct acpi_atsr_unit *) acpi_entry; + + switch (type) { + case DMAR_TYPE: + cnt = &(dmaru->devices_cnt); + devices = &(dmaru->devices); + break; + case RMRR_TYPE: + cnt = &(rmrru->devices_cnt); + devices = &(rmrru->devices); + break; + case ATSR_TYPE: + cnt = &(atsru->devices_cnt); + devices = &(atsru->devices); + break; + default: + dprintk(XENLOG_ERR VTDPREFIX, "invalid vt-d acpi entry type\n"); + } + + *cnt = scope_device_count(start, end); + if ( *cnt == 0 ) + { + dprintk(XENLOG_INFO VTDPREFIX, "acpi_parse_dev_scope: no device\n"); + return 0; + } + + *devices = xmalloc_array(struct pci_dev, *cnt); + if ( !*devices ) + return -ENOMEM; + memset(*devices, 0, sizeof(struct pci_dev) * (*cnt)); + + pdev = *devices; + while ( start < end ) + { + scope = start; + path = (struct acpi_pci_path *)(scope + 1); + depth = (scope->length - sizeof(struct acpi_dev_scope)) + / sizeof(struct acpi_pci_path); + bus = scope->start_bus; + + while ( --depth ) + { + bus = read_pci_config_byte( + bus, path->dev, path->fn, PCI_SECONDARY_BUS); + path++; + } + + if ( scope->dev_type == ACPI_DEV_ENDPOINT ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "found endpoint: bdf = %x:%x:%x\n", + bus, path->dev, path->fn); + pdev->bus = bus; + pdev->devfn = PCI_DEVFN(path->dev, path->fn); + pdev++; + } + else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE ) + { + dprintk(XENLOG_INFO VTDPREFIX, + "found bridge: bus = %x dev = %x func = %x\n", + bus, path->dev, path->fn); + sec_bus = read_pci_config_byte( + bus, path->dev, path->fn, PCI_SECONDARY_BUS); + sub_bus = read_pci_config_byte( + bus, path->dev, path->fn, PCI_SUBORDINATE_BUS); + + while ( sec_bus <= sub_bus ) + { + for ( dev = 0; dev < 32; dev++ ) + { + for ( func = 0; func < 8; func++ ) + { + l = read_pci_config( + sec_bus, dev, func, PCI_VENDOR_ID); + + /* some broken boards return 0 or + * ~0 if a slot is empty + */ + if ( l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000 ) + break; + + pdev->bus = sec_bus; + pdev->devfn = PCI_DEVFN(dev, func); + pdev++; + } + } + sec_bus++; + } + } + else if ( scope->dev_type == ACPI_DEV_IOAPIC ) + { + acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit); + if ( !acpi_ioapic_unit ) + return -ENOMEM; + acpi_ioapic_unit->apic_id = scope->enum_id; + acpi_ioapic_unit->ioapic.bdf.bus = bus; + acpi_ioapic_unit->ioapic.bdf.dev = path->dev; + acpi_ioapic_unit->ioapic.bdf.func = path->fn; + list_add(&acpi_ioapic_unit->list, &dmaru->ioapic_list); + dprintk(XENLOG_INFO VTDPREFIX, + "found IOAPIC: bus = %x dev = %x func = %x\n", + bus, path->dev, path->fn); + } + else + dprintk(XENLOG_INFO VTDPREFIX, + "found MSI HPET: bus = %x dev = %x func = %x\n", + bus, path->dev, path->fn); + start += scope->length; + } + + return 0; +} + +static int __init +acpi_parse_one_drhd(struct acpi_dmar_entry_header *header) +{ + struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header; + struct acpi_drhd_unit *dmaru; + int ret = 0; + static int include_all; + void *dev_scope_start, *dev_scope_end; + + dmaru = xmalloc(struct acpi_drhd_unit); + if ( !dmaru ) + return -ENOMEM; + memset(dmaru, 0, sizeof(struct acpi_drhd_unit)); + + dmaru->address = drhd->address; + dmaru->include_all = drhd->flags & 1; /* BIT0: INCLUDE_ALL */ + INIT_LIST_HEAD(&dmaru->ioapic_list); + dprintk(XENLOG_INFO VTDPREFIX, "dmaru->address = %lx\n", dmaru->address); + + dev_scope_start = (void *)(drhd + 1); + dev_scope_end = ((void *)drhd) + header->length; + ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end, + dmaru, DMAR_TYPE); + + if ( dmaru->include_all ) + { + dprintk(XENLOG_INFO VTDPREFIX, "found INCLUDE_ALL\n"); + /* Only allow one INCLUDE_ALL */ + if ( include_all ) + { + dprintk(XENLOG_WARNING VTDPREFIX, + "Only one INCLUDE_ALL device scope is allowed\n"); + ret = -EINVAL; + } + include_all = 1; + } + + if ( ret ) + xfree(dmaru); + else + acpi_register_drhd_unit(dmaru); + return ret; +} + +static int __init +acpi_parse_one_rmrr(struct acpi_dmar_entry_header *header) +{ + struct acpi_table_rmrr *rmrr = (struct acpi_table_rmrr *)header; + struct acpi_rmrr_unit *rmrru; + void *dev_scope_start, *dev_scope_end; + int ret = 0; + + rmrru = xmalloc(struct acpi_rmrr_unit); + if ( !rmrru ) + return -ENOMEM; + memset(rmrru, 0, sizeof(struct acpi_rmrr_unit)); + + rmrru->base_address = rmrr->base_address; + rmrru->end_address = rmrr->end_address; + dev_scope_start = (void *)(rmrr + 1); + dev_scope_end = ((void *)rmrr) + header->length; + ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end, + rmrru, RMRR_TYPE); + if ( ret || (rmrru->devices_cnt == 0) ) + xfree(rmrru); + else + acpi_register_rmrr_unit(rmrru); + return ret; +} + +static int __init +acpi_parse_one_atsr(struct acpi_dmar_entry_header *header) +{ + struct acpi_table_atsr *atsr = (struct acpi_table_atsr *)header; + struct acpi_atsr_unit *atsru; + int ret = 0; + static int all_ports; + void *dev_scope_start, *dev_scope_end; + + atsru = xmalloc(struct acpi_atsr_unit); + if ( !atsru ) + return -ENOMEM; + memset(atsru, 0, sizeof(struct acpi_atsr_unit)); + + atsru->all_ports = atsr->flags & 1; /* BIT0: ALL_PORTS */ + if ( !atsru->all_ports ) + { + dev_scope_start = (void *)(atsr + 1); + dev_scope_end = ((void *)atsr) + header->length; + ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end, + atsru, ATSR_TYPE); + } + else { + dprintk(XENLOG_INFO VTDPREFIX, "found ALL_PORTS\n"); + /* Only allow one ALL_PORTS */ + if ( all_ports ) + { + dprintk(XENLOG_WARNING VTDPREFIX, + "Only one ALL_PORTS device scope is allowed\n"); + ret = -EINVAL; + } + all_ports = 1; + } + + if ( ret ) + xfree(atsr); + else + acpi_register_atsr_unit(atsru); + return ret; +} + +static int __init acpi_parse_dmar(unsigned long phys_addr, + unsigned long size) +{ + struct acpi_table_dmar *dmar = NULL; + struct acpi_dmar_entry_header *entry_header; + int ret = 0; + + if ( !phys_addr || !size ) + return -EINVAL; + + dmar = (struct acpi_table_dmar *)__acpi_map_table(phys_addr, size); + if ( !dmar ) + { + dprintk(XENLOG_WARNING VTDPREFIX, "Unable to map DMAR\n"); + return -ENODEV; + } + + if ( !dmar->haw ) + { + dprintk(XENLOG_WARNING VTDPREFIX, "Zero: Invalid DMAR haw\n"); + return -EINVAL; + } + + dmar_host_address_width = dmar->haw; + dprintk(XENLOG_INFO VTDPREFIX, "Host address width %d\n", + dmar_host_address_width); + + entry_header = (struct acpi_dmar_entry_header *)(dmar + 1); + while ( ((unsigned long)entry_header) < + (((unsigned long)dmar) + size) ) + { + switch ( entry_header->type ) + { + case ACPI_DMAR_DRHD: + dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_DRHD\n"); + ret = acpi_parse_one_drhd(entry_header); + break; + case ACPI_DMAR_RMRR: + dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RMRR\n"); + ret = acpi_parse_one_rmrr(entry_header); + break; + case ACPI_DMAR_ATSR: + dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_ATSR\n"); + ret = acpi_parse_one_atsr(entry_header); + break; + default: + dprintk(XENLOG_WARNING VTDPREFIX, "Unknown DMAR structure type\n"); + ret = -EINVAL; + break; + } + if ( ret ) + break; + + entry_header = ((void *)entry_header + entry_header->length); + } + + /* Zap APCI DMAR signature to prevent dom0 using vt-d HW. */ + dmar->header.signature[0] = '\0'; + + return ret; +} + +int acpi_dmar_init(void) +{ + int rc; + + if ( !vtd_enabled ) + return -ENODEV; + + if ( (rc = vtd_hw_check()) != 0 ) + return rc; + + acpi_table_parse(ACPI_DMAR, acpi_parse_dmar); + + if ( list_empty(&acpi_drhd_units) ) + { + dprintk(XENLOG_ERR VTDPREFIX, "No DMAR devices found\n"); + vtd_enabled = 0; + return -ENODEV; + } + + printk("Intel VT-d has been enabled\n"); + + return 0; +} diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/vtd/dmar.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/vtd/dmar.h Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Copyright (C) Ashok Raj <ashok.raj@xxxxxxxxx> + * Copyright (C) Shaohua Li <shaohua.li@xxxxxxxxx> + */ + +#ifndef _DMAR_H_ +#define _DMAR_H_ + +#include <xen/list.h> +#include <asm/iommu.h> + +extern u8 dmar_host_address_width; + +/* This one is for interrupt remapping */ +struct acpi_ioapic_unit { + struct list_head list; + int apic_id; + union { + u16 info; + struct { + u16 func: 3, + dev: 5, + bus: 8; + }bdf; + }ioapic; +}; + +struct acpi_drhd_unit { + struct list_head list; + unsigned long address; /* register base address of the unit */ + struct pci_dev *devices; /* target devices */ + int devices_cnt; + u8 include_all:1; + struct iommu *iommu; + struct list_head ioapic_list; +}; + +struct acpi_rmrr_unit { + struct list_head list; + unsigned long base_address; + unsigned long end_address; + struct pci_dev *devices; /* target devices */ + int devices_cnt; + u8 allow_all:1; +}; + +struct acpi_atsr_unit { + struct list_head list; + struct pci_dev *devices; /* target devices */ + int devices_cnt; + u8 all_ports:1; +}; + +#define for_each_iommu(domain, iommu) \ + list_for_each_entry(iommu, \ + &(domain->arch.hvm_domain.hvm_iommu.iommu_list), list) + +#define for_each_pdev(domain, pdev) \ + list_for_each_entry(pdev, \ + &(domain->arch.hvm_domain.hvm_iommu.pdev_list), list) + +#define for_each_drhd_unit(drhd) \ + list_for_each_entry(drhd, &acpi_drhd_units, list) +#define for_each_rmrr_device(rmrr, pdev) \ + list_for_each_entry(rmrr, &acpi_rmrr_units, list) { \ + int _i; \ + for (_i = 0; _i < rmrr->devices_cnt; _i++) { \ + pdev = &(rmrr->devices[_i]); +#define end_for_each_rmrr_device(rmrr, pdev) \ + } \ + } + +struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev); +struct acpi_rmrr_unit * acpi_find_matched_rmrr_unit(struct pci_dev *dev); + +#define DMAR_TYPE 1 +#define RMRR_TYPE 2 +#define ATSR_TYPE 3 + +#define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */ +#define time_after(a,b) \ + (typecheck(unsigned long, a) && \ + typecheck(unsigned long, b) && \ + ((long)(b) - (long)(a) < 0)) + +int vtd_hw_check(void); +void disable_pmr(struct iommu *iommu); + +#endif // _DMAR_H_ diff -r f1a107ec62b6 -r 591cfd37bd54 xen/drivers/passthrough/vtd/extern.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/vtd/extern.h Thu Feb 21 15:06:37 2008 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Copyright (C) Allen Kay <allen.m.kay@xxxxxxxxx> + * Copyright (C) Weidong Han <weidong.han@xxxxxxxxx> + */ + +#ifndef _VTD_EXTERN_H_ +#define _VTD_EXTERN_H_ + +#include "dmar.h" + +extern struct qi_ctrl *qi_ctrl; +extern struct ir_ctrl *ir_ctrl; + +void print_iommu_regs(struct acpi_drhd_unit *drhd); +void print_vtd_entries(struct domain *d, struct iommu *iommu, + int bus, int devfn, unsigned long gmfn); +void pdev_flr(u8 bus, u8 devfn); + +int qinval_setup(struct iommu *iommu); +int intremap_setup(struct iommu *iommu); _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |