diff -r 8848d9e07584 xen/arch/x86/hvm/svm/Makefile --- a/xen/arch/x86/hvm/svm/Makefile Mon Feb 18 21:26:57 2008 +0000 +++ b/xen/arch/x86/hvm/svm/Makefile Tue Feb 19 10:17:12 2008 +0800 @@ -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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/Makefile --- a/xen/arch/x86/hvm/svm/amd_iommu/Makefile Mon Feb 18 21:26:57 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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c --- a/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c Mon Feb 18 21:26:57 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 - * Author: Wei Wang - 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 -#include -#include -#include -#include -#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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c --- a/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c Mon Feb 18 21:26:57 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 - * Author: Wei Wang - 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 -#include -#include -#include -#include -#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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c --- a/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c Mon Feb 18 21:26:57 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 - * Author: Wei Wang - 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 -#include -#include -#include - -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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c --- a/xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c Mon Feb 18 21:26:57 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 - * Author: Wei Wang - 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 -#include -#include -#include -#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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h --- a/xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h Mon Feb 18 21:26:57 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 -#include - -/* 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 8848d9e07584 xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h --- a/xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h Mon Feb 18 21:26:57 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 - * - * 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 8848d9e07584 xen/drivers/passthrough/amd_iommu/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd_iommu/Makefile Mon Feb 18 15:20:55 2008 +0800 @@ -0,0 +1,4 @@ +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 8848d9e07584 xen/drivers/passthrough/amd_iommu/amd-iommu-detect.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd_iommu/amd-iommu-detect.c Wed Feb 20 10:09:13 2008 +0800 @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran + * Author: Wei Wang - 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 +#include +#include +#include +#include +#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 8848d9e07584 xen/drivers/passthrough/amd_iommu/amd-iommu-init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd_iommu/amd-iommu-init.c Tue Feb 19 11:00:44 2008 +0800 @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran + * Author: Wei Wang - 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 +#include +#include +#include +#include +#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 8848d9e07584 xen/drivers/passthrough/amd_iommu/amd-iommu-map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd_iommu/amd-iommu-map.c Wed Feb 20 10:08:44 2008 +0800 @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran + * Author: Wei Wang - 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 +#include +#include +#include + +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 8848d9e07584 xen/drivers/passthrough/amd_iommu/pci-amd-iommu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd_iommu/pci-amd-iommu.c Tue Feb 19 11:01:28 2008 +0800 @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * Author: Leo Duran + * Author: Wei Wang - 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 +#include +#include +#include +#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, +};