diff -r be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/Makefile Fri Sep 21 14:37:47 2007 +0200 @@ -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 be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-detect.c Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,211 @@ +/* + * 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 "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; + + /* 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 */ + + 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%lx\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 be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-init.c Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,145 @@ +/* + * 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 "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 be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/amd-iommu-map.c Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,419 @@ +/* + * 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; + + BUG_ON( !iommu || !cmd ); + + 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; + + BUG_ON( !iommu ); + + 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[]) +{ + BUG_ON( !iommu || !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); +} + +static 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: %s(): Warning:" + " ComWaitInt bit did not assert!\n", + __FUNCTION__); + } +} + +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; +} + +static 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)); +} + +static void *get_pte_from_page_tables(void *table, int level, + unsigned long io_pfn) +{ + unsigned long offset; + void *pde = 0; + + BUG_ON( !table ); + + while ( level > 0 ) + { + void *next_table = 0; + unsigned long next_ptr; + 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) ) { + next_table = alloc_xenheap_page(); + if ( next_table == NULL ) + return NULL; + memset(next_table, 0, PAGE_SIZE); + if ( *(u64*)(pde) == 0 ) { + 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 ) { + set_page_table_entry_present((u32 *)pte, maddr, iw, ir); + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return 0; + } else { + dprintk(XENLOG_ERR, + "%s() AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", + __FUNCTION__, gfn); + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return -EIO; + } +} + +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 ) { + /* 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; + } else { + dprintk(XENLOG_ERR, + "%s() AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", + __FUNCTION__, gfn); + spin_unlock_irqrestore(&hd->mapping_lock, flags); + return -EIO; + } +} diff -r be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/pci-amd-iommu.c Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,389 @@ +/* + * 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; + + dprintk(XENLOG_ERR, "AMD IOMMU: %s()\n", __FUNCTION__); + + 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; + + dprintk(XENLOG_ERR, "AMD IOMMU: %s()\n", __FUNCTION__); + + for_each_amd_iommu(iommu) { + 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); + + spin_lock_irqsave(&iommu->lock, flags); + + amd_iommu_set_dev_table_entry((u32 *)dte, + root_ptr, hd->domain_id, hd->paging_mode); + + dprintk(XENLOG_INFO, "AMD IOMMU: Set DTE req_id:%x, " + "root_ptr:%lx, 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 ( amd_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 */ + hd->root_table = (void *)alloc_xenheap_page(); + if ( !hd->root_table ) + return -ENOMEM; + memset((u8*)hd->root_table, 0, PAGE_SIZE); + + return 0; +} + +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); + + spin_lock_init(&hd->mapping_lock); + spin_lock_init(&hd->iommu_list_lock); + INIT_LIST_HEAD(&hd->pdev_list); + + /* allocate page directroy */ + if ( allocate_domain_resources(hd) != 0 ) { + dprintk(XENLOG_ERR, "AMD IOMMU: %s()\n", __FUNCTION__); + goto error_out; + } + + 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; + +error_out: + deallocate_domain_resources(hd); + return -ENOMEM; +} + + diff -r be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/pci-direct.h Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,48 @@ +#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 be5f27ff147b xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hvm/svm/amd_iommu/pci_regs.h Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,513 @@ +/* + * 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 be5f27ff147b xen/include/asm-x86/amd-iommu.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/include/asm-x86/amd-iommu.h Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,70 @@ +/* + * 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 + */ +#ifndef _ASM_X86_64_AMD_IOMMU_H +#define _ASM_X86_64_AMD_IOMMU_H + +#include +#include +#include +#include +#include + +#define iommu_found() (!list_empty(&amd_iommu_head)) + +extern int amd_iommu_enabled; +extern struct list_head amd_iommu_head; + +extern int __init amd_iommu_detect(void); + +struct table_struct { + void *buffer; + unsigned long entries; + unsigned long alloc_size; +}; + +struct amd_iommu { + struct list_head list; + spinlock_t lock; /* protect iommu */ + + int iotlb_support; + int ht_tunnel_support; + int not_present_cached; + u8 revision; + + u8 root_bus; + u8 first_devfn; + u8 last_devfn; + + int last_downstream_bus; + int downstream_bus_present[PCI_MAX_BUS_COUNT]; + + void *mmio_base; + unsigned long mmio_base_phys; + + struct table_struct dev_table; + struct table_struct cmd_buffer; + u32 cmd_buffer_tail; + + int exclusion_enabled; + unsigned long exclusion_base; + unsigned long exclusion_limit; +}; + +#endif /* _ASM_X86_64_AMD_IOMMU_H */ diff -r be5f27ff147b xen/include/asm-x86/hvm/svm/amd-iommu-defs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,419 @@ +/* + * 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 + */ + +#ifndef _ASM_X86_64_AMD_IOMMU_DEFS_H +#define _ASM_X86_64_AMD_IOMMU_DEFS_H + +/* Reserve some non-mapped pages to handle error conditions. + * 'bad_dma_address' will point to these reserved pages, and + * the mapping funtions will return 'bad_dma_address' if there + * are not enough page table entries available. + */ +#define IOMMU_RESERVED_BASE_ADDR 0 +#define IOMMU_RESERVED_PAGES 32 + +/* IOMMU ComWaitInt polling after issuing a COMPLETION_WAIT command */ +#define COMPLETION_WAIT_DEFAULT_POLLING_COUNT 10 + +/* IOMMU Command Buffer entries: in power of 2 increments, minimum of 256 */ +#define IOMMU_CMD_BUFFER_DEFAULT_ENTRIES 512 + +#define BITMAP_ENTRIES_PER_BYTE 8 + +#define PTE_PER_TABLE_SHIFT 9 +#define PTE_PER_TABLE_SIZE (1 << PTE_PER_TABLE_SHIFT) +#define PTE_PER_TABLE_MASK (~(PTE_PER_TABLE_SIZE - 1)) +#define PTE_PER_TABLE_ALIGN(entries) \ + (((entries) + PTE_PER_TABLE_SIZE - 1) & PTE_PER_TABLE_MASK) +#define PTE_PER_TABLE_ALLOC(entries) \ + PAGE_SIZE * (PTE_PER_TABLE_ALIGN(entries) >> PTE_PER_TABLE_SHIFT) + +/* 0-based aperture order (represents virtual address space for DMA mappings */ +#define APERTURE_ORDER_FOR_32B_APERTURE 0 +#define APERTURE_ORDER_FOR_64MB_APERTURE 1 +#define APERTURE_ORDER_FOR_128MB_APERTURE 2 +#define APERTURE_ORDER_FOR_256MB_APERTURE 3 +#define APERTURE_ORDER_FOR_512MB_APERTURE 4 +#define APERTURE_ORDER_FOR_1GB_APERTURE 5 +#define APERTURE_ORDER_FOR_MAX_APERTURE APERTURE_ORDER_FOR_1GB_APERTURE + +/* The minimum 32MB aperture requires 2**13 level-1 page table entries */ +#define SHIFT_FOR_MIN_APERTURE 13 +#define PAGES_FROM_APERTURE_ORDER(order) \ + ((1 << (order)) << SHIFT_FOR_MIN_APERTURE) +#define ORDER_FROM_APERTURE_PAGES(pages) \ + get_order(((pages) * PAGE_SIZE) >> SHIFT_FOR_MIN_APERTURE) + +/* + * PCI config-space + */ +#define VALID_PCI_VENDOR_ID(id) (((id) != 0) && ((id) != 0xFFFF)) +#define IS_PCI_MULTI_FUNCTION(hdr) ((hdr) & 0x80) +#define IS_PCI_TYPE0_HEADER(hdr) (((hdr) & 0x7f) == 0) +#define IS_PCI_TYPE1_HEADER(hdr) (((hdr) & 0x7f) == 1) + +#define PCI_MAX_BUS_COUNT 256 +#define PCI_MAX_DEV_COUNT 32 +#define PCI_MAX_FUNC_COUNT 8 +#define PCI_MIN_DEVFN 0 +#define PCI_MAX_DEVFN 0xFF + +/* + * Capability blocks are 4-byte aligned, and must start at >= offset 0x40, + * for a max of 48 possible cap_blocks (256 - 0x40 = 192; 192 / 4 = 48) + * The lower 2 bits of each pointer are reserved, and must be masked off. + */ +#define PCI_MIN_CAP_OFFSET 0x40 +#define PCI_MAX_CAP_BLOCKS 48 +#define PCI_CAP_PTR_MASK 0xFC + +/* IOMMU Capability */ +#define PCI_CAP_ID_MASK 0x000000FF +#define PCI_CAP_ID_SHIFT 0 +#define PCI_CAP_NEXT_PTR_MASK 0x0000FF00 +#define PCI_CAP_NEXT_PTR_SHIFT 8 +#define PCI_CAP_TYPE_MASK 0x00070000 +#define PCI_CAP_TYPE_SHIFT 16 +#define PCI_CAP_REV_MASK 0x00F80000 +#define PCI_CAP_REV_SHIFT 19 +#define PCI_CAP_IOTLB_MASK 0x01000000 +#define PCI_CAP_IOTLB_SHIFT 24 +#define PCI_CAP_HT_TUNNEL_MASK 0x02000000 +#define PCI_CAP_HT_TUNNEL_SHIFT 25 +#define PCI_CAP_NP_CACHE_MASK 0x04000000 +#define PCI_CAP_NP_CACHE_SHIFT 26 +#define PCI_CAP_RESET_MASK 0x80000000 +#define PCI_CAP_RESET_SHIFT 31 + +#define PCI_CAP_ID_SECURE_DEVICE 0x0F +#define PCI_CAP_TYPE_IOMMU 0x3 + +#define PCI_CAP_MMIO_BAR_LOW_OFFSET 0x04 +#define PCI_CAP_MMIO_BAR_HIGH_OFFSET 0x08 +#define PCI_CAP_MMIO_BAR_LOW_MASK 0xFFFFC000 +#define IOMMU_MMIO_REGION_LENGTH 0x4000 + +#define PCI_CAP_RANGE_OFFSET 0x0C +#define PCI_CAP_BUS_NUMBER_MASK 0x0000FF00 +#define PCI_CAP_BUS_NUMBER_SHIFT 8 +#define PCI_CAP_FIRST_DEVICE_MASK 0x00FF0000 +#define PCI_CAP_FIRST_DEVICE_SHIFT 16 +#define PCI_CAP_LAST_DEVICE_MASK 0xFF000000 +#define PCI_CAP_LAST_DEVICE_SHIFT 24 + +/* Device Table */ +#define IOMMU_DEV_TABLE_BASE_LOW_OFFSET 0x00 +#define IOMMU_DEV_TABLE_BASE_HIGH_OFFSET 0x04 +#define IOMMU_DEV_TABLE_BASE_LOW_MASK 0xFFFFF000 +#define IOMMU_DEV_TABLE_BASE_LOW_SHIFT 12 +#define IOMMU_DEV_TABLE_BASE_HIGH_MASK 0x000FFFFF +#define IOMMU_DEV_TABLE_BASE_HIGH_SHIFT 0 +#define IOMMU_DEV_TABLE_SIZE_MASK 0x000001FF +#define IOMMU_DEV_TABLE_SIZE_SHIFT 0 + +#define IOMMU_DEV_TABLE_ENTRIES_PER_BUS 256 +#define IOMMU_DEV_TABLE_ENTRY_SIZE 32 +#define IOMMU_DEV_TABLE_U32_PER_ENTRY (IOMMU_DEV_TABLE_ENTRY_SIZE / 4) + +#define IOMMU_DEV_TABLE_SYS_MGT_DMA_ABORTED 0x0 +#define IOMMU_DEV_TABLE_SYS_MGT_MSG_FORWARDED 0x1 +#define IOMMU_DEV_TABLE_SYS_MGT_INT_FORWARDED 0x2 +#define IOMMU_DEV_TABLE_SYS_MGT_DMA_FORWARDED 0x3 + +#define IOMMU_DEV_TABLE_IO_CONTROL_ABORTED 0x0 +#define IOMMU_DEV_TABLE_IO_CONTROL_FORWARDED 0x1 +#define IOMMU_DEV_TABLE_IO_CONTROL_TRANSLATED 0x2 + +#define IOMMU_DEV_TABLE_INT_CONTROL_ABORTED 0x0 +#define IOMMU_DEV_TABLE_INT_CONTROL_FORWARDED 0x1 +#define IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED 0x2 + +/* DeviceTable Entry[31:0] */ +#define IOMMU_DEV_TABLE_VALID_MASK 0x00000001 +#define IOMMU_DEV_TABLE_VALID_SHIFT 0 +#define IOMMU_DEV_TABLE_TRANSLATION_VALID_MASK 0x00000002 +#define IOMMU_DEV_TABLE_TRANSLATION_VALID_SHIFT 1 +#define IOMMU_DEV_TABLE_PAGING_MODE_MASK 0x00000E00 +#define IOMMU_DEV_TABLE_PAGING_MODE_SHIFT 9 +#define IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_MASK 0xFFFFF000 +#define IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT 12 + +/* DeviceTable Entry[63:32] */ +#define IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_MASK 0x000FFFFF +#define IOMMU_DEV_TABLE_PAGE_TABLE_PTR_HIGH_SHIFT 0 +#define IOMMU_DEV_TABLE_IO_READ_PERMISSION_MASK 0x20000000 +#define IOMMU_DEV_TABLE_IO_READ_PERMISSION_SHIFT 29 +#define IOMMU_DEV_TABLE_IO_WRITE_PERMISSION_MASK 0x40000000 +#define IOMMU_DEV_TABLE_IO_WRITE_PERMISSION_SHIFT 30 + +/* DeviceTable Entry[95:64] */ +#define IOMMU_DEV_TABLE_DOMAIN_ID_MASK 0x0000FFFF +#define IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT 0 + +/* DeviceTable Entry[127:96] */ +#define IOMMU_DEV_TABLE_IOTLB_SUPPORT_MASK 0x00000001 +#define IOMMU_DEV_TABLE_IOTLB_SUPPORT_SHIFT 0 +#define IOMMU_DEV_TABLE_SUPRESS_LOGGED_PAGES_MASK 0x00000002 +#define IOMMU_DEV_TABLE_SUPRESS_LOGGED_PAGES_SHIFT 1 +#define IOMMU_DEV_TABLE_SUPRESS_ALL_PAGES_MASK 0x00000004 +#define IOMMU_DEV_TABLE_SUPRESS_ALL_PAGES_SHIFT 2 +#define IOMMU_DEV_TABLE_IO_CONTROL_MASK 0x00000018 +#define IOMMU_DEV_TABLE_IO_CONTROL_SHIFT 3 +#define IOMMU_DEV_TABLE_IOTLB_CACHE_HINT_MASK 0x00000020 +#define IOMMU_DEV_TABLE_IOTLB_CACHE_HINT_SHIFT 5 +#define IOMMU_DEV_TABLE_SNOOP_DISABLE_MASK 0x00000040 +#define IOMMU_DEV_TABLE_SNOOP_DISABLE_SHIFT 6 +#define IOMMU_DEV_TABLE_ALLOW_EXCLUSION_MASK 0x00000080 +#define IOMMU_DEV_TABLE_ALLOW_EXCLUSION_SHIFT 7 +#define IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_MASK 0x00000300 +#define IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_SHIFT 8 + +/* DeviceTable Entry[159:128] */ +#define IOMMU_DEV_TABLE_INT_VALID_MASK 0x00000001 +#define IOMMU_DEV_TABLE_INT_VALID_SHIFT 0 +#define IOMMU_DEV_TABLE_INT_TABLE_LENGTH_MASK 0x0000001E +#define IOMMU_DEV_TABLE_INT_TABLE_LENGTH_SHIFT 1 +#define IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_MASK 0xFFFFFFC0 +#define IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_SHIFT 6 + +/* DeviceTable Entry[191:160] */ +#define IOMMU_DEV_TABLE_INT_TABLE_PTR_HIGH_MASK 0x000FFFFF +#define IOMMU_DEV_TABLE_INT_TABLE_PTR_HIGH_SHIFT 0 +#define IOMMU_DEV_TABLE_INIT_PASSTHRU_MASK 0x01000000 +#define IOMMU_DEV_TABLE_INIT_PASSTHRU_SHIFT 24 +#define IOMMU_DEV_TABLE_EINT_PASSTHRU_MASK 0x02000000 +#define IOMMU_DEV_TABLE_EINT_PASSTHRU_SHIFT 25 +#define IOMMU_DEV_TABLE_NMI_PASSTHRU_MASK 0x04000000 +#define IOMMU_DEV_TABLE_NMI_PASSTHRU_SHIFT 26 +#define IOMMU_DEV_TABLE_INT_CONTROL_MASK 0x30000000 +#define IOMMU_DEV_TABLE_INT_CONTROL_SHIFT 28 +#define IOMMU_DEV_TABLE_LINT0_ENABLE_MASK 0x40000000 +#define IOMMU_DEV_TABLE_LINT0_ENABLE_SHIFT 30 +#define IOMMU_DEV_TABLE_LINT1_ENABLE_MASK 0x80000000 +#define IOMMU_DEV_TABLE_LINT1_ENABLE_SHIFT 31 + +/* Command Buffer */ +#define IOMMU_CMD_BUFFER_BASE_LOW_OFFSET 0x08 +#define IOMMU_CMD_BUFFER_BASE_HIGH_OFFSET 0x0C +#define IOMMU_CMD_BUFFER_HEAD_OFFSET 0x2000 +#define IOMMU_CMD_BUFFER_TAIL_OFFSET 0x2008 +#define IOMMU_CMD_BUFFER_BASE_LOW_MASK 0xFFFFF000 +#define IOMMU_CMD_BUFFER_BASE_LOW_SHIFT 12 +#define IOMMU_CMD_BUFFER_BASE_HIGH_MASK 0x000FFFFF +#define IOMMU_CMD_BUFFER_BASE_HIGH_SHIFT 0 +#define IOMMU_CMD_BUFFER_LENGTH_MASK 0x0F000000 +#define IOMMU_CMD_BUFFER_LENGTH_SHIFT 24 +#define IOMMU_CMD_BUFFER_HEAD_MASK 0x0007FFF0 +#define IOMMU_CMD_BUFFER_HEAD_SHIFT 4 +#define IOMMU_CMD_BUFFER_TAIL_MASK 0x0007FFF0 +#define IOMMU_CMD_BUFFER_TAIL_SHIFT 4 + +#define IOMMU_CMD_BUFFER_ENTRY_SIZE 16 +#define IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE 8 +#define IOMMU_CMD_BUFFER_U32_PER_ENTRY (IOMMU_CMD_BUFFER_ENTRY_SIZE / 4) + +#define IOMMU_CMD_OPCODE_MASK 0xF0000000 +#define IOMMU_CMD_OPCODE_SHIFT 28 +#define IOMMU_CMD_COMPLETION_WAIT 0x1 +#define IOMMU_CMD_INVALIDATE_DEVTAB_ENTRY 0x2 +#define IOMMU_CMD_INVALIDATE_IOMMU_PAGES 0x3 +#define IOMMU_CMD_INVALIDATE_IOTLB_PAGES 0x4 +#define IOMMU_CMD_INVALIDATE_INT_TABLE 0x5 + +/* COMPLETION_WAIT command */ +#define IOMMU_COMP_WAIT_DATA_BUFFER_SIZE 8 +#define IOMMU_COMP_WAIT_DATA_BUFFER_ALIGNMENT 8 +#define IOMMU_COMP_WAIT_S_FLAG_MASK 0x00000001 +#define IOMMU_COMP_WAIT_S_FLAG_SHIFT 0 +#define IOMMU_COMP_WAIT_I_FLAG_MASK 0x00000002 +#define IOMMU_COMP_WAIT_I_FLAG_SHIFT 1 +#define IOMMU_COMP_WAIT_F_FLAG_MASK 0x00000004 +#define IOMMU_COMP_WAIT_F_FLAG_SHIFT 2 +#define IOMMU_COMP_WAIT_ADDR_LOW_MASK 0xFFFFFFF8 +#define IOMMU_COMP_WAIT_ADDR_LOW_SHIFT 3 +#define IOMMU_COMP_WAIT_ADDR_HIGH_MASK 0x000FFFFF +#define IOMMU_COMP_WAIT_ADDR_HIGH_SHIFT 0 + +/* INVALIDATE_IOMMU_PAGES command */ +#define IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_MASK 0x0000FFFF +#define IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_SHIFT 0 +#define IOMMU_INV_IOMMU_PAGES_S_FLAG_MASK 0x00000001 +#define IOMMU_INV_IOMMU_PAGES_S_FLAG_SHIFT 0 +#define IOMMU_INV_IOMMU_PAGES_PDE_FLAG_MASK 0x00000002 +#define IOMMU_INV_IOMMU_PAGES_PDE_FLAG_SHIFT 1 +#define IOMMU_INV_IOMMU_PAGES_ADDR_LOW_MASK 0xFFFFF000 +#define IOMMU_INV_IOMMU_PAGES_ADDR_LOW_SHIFT 12 +#define IOMMU_INV_IOMMU_PAGES_ADDR_HIGH_MASK 0xFFFFFFFF +#define IOMMU_INV_IOMMU_PAGES_ADDR_HIGH_SHIFT 0 + +/* Event Log */ +#define IOMMU_EVENT_LOG_BASE_LOW_OFFSET 0x10 +#define IOMMU_EVENT_LOG_BASE_HIGH_OFFSET 0x14 +#define IOMMU_EVENT_LOG_HEAD_OFFSET 0x2010 +#define IOMMU_EVENT_LOG_TAIL_OFFSET 0x2018 +#define IOMMU_EVENT_LOG_BASE_LOW_MASK 0xFFFFF000 +#define IOMMU_EVENT_LOG_BASE_LOW_SHIFT 12 +#define IOMMU_EVENT_LOG_BASE_HIGH_MASK 0x000FFFFF +#define IOMMU_EVENT_LOG_BASE_HIGH_SHIFT 0 +#define IOMMU_EVENT_LOG_LENGTH_MASK 0x0F000000 +#define IOMMU_EVENT_LOG_LENGTH_SHIFT 24 +#define IOMMU_EVENT_LOG_HEAD_MASK 0x0007FFF0 +#define IOMMU_EVENT_LOG_HEAD_SHIFT 4 +#define IOMMU_EVENT_LOG_TAIL_MASK 0x0007FFF0 +#define IOMMU_EVENT_LOG_TAIL_SHIFT 4 + +#define IOMMU_EVENT_LOG_ENTRY_SIZE 16 +#define IOMMU_EVENT_LOG_POWER_OF2_ENTRIES_PER_PAGE 8 +#define IOMMU_EVENT_LOG_U32_PER_ENTRY (IOMMU_EVENT_LOG_ENTRY_SIZE / 4) + +#define IOMMU_EVENT_CODE_MASK 0xF0000000 +#define IOMMU_EVENT_CODE_SHIFT 28 +#define IOMMU_EVENT_ILLEGAL_DEV_TABLE_ENTRY 0x1 +#define IOMMU_EVENT_IO_PAGE_FALT 0x2 +#define IOMMU_EVENT_DEV_TABLE_HW_ERROR 0x3 +#define IOMMU_EVENT_PAGE_TABLE_HW_ERROR 0x4 +#define IOMMU_EVENT_ILLEGAL_COMMAND_ERROR 0x5 +#define IOMMU_EVENT_COMMAND_HW_ERROR 0x6 +#define IOMMU_EVENT_IOTLB_INV_TIMEOUT 0x7 +#define IOMMU_EVENT_INVALID_DEV_REQUEST 0x8 + +/* Control Register */ +#define IOMMU_CONTROL_MMIO_OFFSET 0x18 +#define IOMMU_CONTROL_TRANSLATION_ENABLE_MASK 0x00000001 +#define IOMMU_CONTROL_TRANSLATION_ENABLE_SHIFT 0 +#define IOMMU_CONTROL_HT_TUNNEL_TRANSLATION_MASK 0x00000002 +#define IOMMU_CONTROL_HT_TUNNEL_TRANSLATION_SHIFT 1 +#define IOMMU_CONTROL_EVENT_LOG_ENABLE_MASK 0x00000004 +#define IOMMU_CONTROL_EVENT_LOG_ENABLE_SHIFT 2 +#define IOMMU_CONTROL_EVENT_LOG_INT_MASK 0x00000008 +#define IOMMU_CONTROL_EVENT_LOG_INT_SHIFT 3 +#define IOMMU_CONTROL_COMP_WAIT_INT_MASK 0x00000010 +#define IOMMU_CONTROL_COMP_WAIT_INT_SHIFT 4 +#define IOMMU_CONTROL_TRANSLATION_CHECK_DISABLE_MASK 0x00000020 +#define IOMMU_CONTROL_TRANSLATION_CHECK_DISABLE_SHIFT 5 +#define IOMMU_CONTROL_INVALIDATION_TIMEOUT_MASK 0x000000C0 +#define IOMMU_CONTROL_INVALIDATION_TIMEOUT_SHIFT 6 +#define IOMMU_CONTROL_PASS_POSTED_WRITE_MASK 0x00000100 +#define IOMMU_CONTROL_PASS_POSTED_WRITE_SHIFT 8 +#define IOMMU_CONTROL_RESP_PASS_POSTED_WRITE_MASK 0x00000200 +#define IOMMU_CONTROL_RESP_PASS_POSTED_WRITE_SHIFT 9 +#define IOMMU_CONTROL_COHERENT_MASK 0x00000400 +#define IOMMU_CONTROL_COHERENT_SHIFT 10 +#define IOMMU_CONTROL_ISOCHRONOUS_MASK 0x00000800 +#define IOMMU_CONTROL_ISOCHRONOUS_SHIFT 11 +#define IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_MASK 0x00001000 +#define IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_SHIFT 12 +#define IOMMU_CONTROL_RESTART_MASK 0x80000000 +#define IOMMU_CONTROL_RESTART_SHIFT 31 + +/* Exclusion Register */ +#define IOMMU_EXCLUSION_BASE_LOW_OFFSET 0x20 +#define IOMMU_EXCLUSION_BASE_HIGH_OFFSET 0x24 +#define IOMMU_EXCLUSION_LIMIT_LOW_OFFSET 0x28 +#define IOMMU_EXCLUSION_LIMIT_HIGH_OFFSET 0x2C +#define IOMMU_EXCLUSION_BASE_LOW_MASK 0xFFFFF000 +#define IOMMU_EXCLUSION_BASE_LOW_SHIFT 12 +#define IOMMU_EXCLUSION_BASE_HIGH_MASK 0xFFFFFFFF +#define IOMMU_EXCLUSION_BASE_HIGH_SHIFT 0 +#define IOMMU_EXCLUSION_RANGE_ENABLE_MASK 0x00000001 +#define IOMMU_EXCLUSION_RANGE_ENABLE_SHIFT 0 +#define IOMMU_EXCLUSION_ALLOW_ALL_MASK 0x00000002 +#define IOMMU_EXCLUSION_ALLOW_ALL_SHIFT 1 +#define IOMMU_EXCLUSION_LIMIT_LOW_MASK 0xFFFFF000 +#define IOMMU_EXCLUSION_LIMIT_LOW_SHIFT 12 +#define IOMMU_EXCLUSION_LIMIT_HIGH_MASK 0xFFFFFFFF +#define IOMMU_EXCLUSION_LIMIT_HIGH_SHIFT 0 + +/* Status Register*/ +#define IOMMU_STATUS_MMIO_OFFSET 0x2020 +#define IOMMU_STATUS_EVENT_OVERFLOW_MASK 0x00000001 +#define IOMMU_STATUS_EVENT_OVERFLOW_SHIFT 0 +#define IOMMU_STATUS_EVENT_LOG_INT_MASK 0x00000002 +#define IOMMU_STATUS_EVENT_LOG_INT_SHIFT 1 +#define IOMMU_STATUS_COMP_WAIT_INT_MASK 0x00000004 +#define IOMMU_STATUS_COMP_WAIT_INT_SHIFT 2 +#define IOMMU_STATUS_EVENT_LOG_RUN_MASK 0x00000008 +#define IOMMU_STATUS_EVENT_LOG_RUN_SHIFT 3 +#define IOMMU_STATUS_CMD_BUFFER_RUN_MASK 0x00000010 +#define IOMMU_STATUS_CMD_BUFFER_RUN_SHIFT 4 + +/* I/O Page Table */ +#define IOMMU_PAGE_TABLE_ENTRY_SIZE 8 +#define IOMMU_PAGE_TABLE_U32_PER_ENTRY (IOMMU_PAGE_TABLE_ENTRY_SIZE / 4) +#define IOMMU_PAGE_TABLE_ALIGNMENT 4096 + +#define IOMMU_PTE_PRESENT_MASK 0x00000001 +#define IOMMU_PTE_PRESENT_SHIFT 0 +#define IOMMU_PTE_NEXT_LEVEL_MASK 0x00000E00 +#define IOMMU_PTE_NEXT_LEVEL_SHIFT 9 +#define IOMMU_PTE_ADDR_LOW_MASK 0xFFFFF000 +#define IOMMU_PTE_ADDR_LOW_SHIFT 12 +#define IOMMU_PTE_ADDR_HIGH_MASK 0x000FFFFF +#define IOMMU_PTE_ADDR_HIGH_SHIFT 0 +#define IOMMU_PTE_U_MASK 0x08000000 +#define IOMMU_PTE_U_SHIFT 7 +#define IOMMU_PTE_FC_MASK 0x10000000 +#define IOMMU_PTE_FC_SHIFT 28 +#define IOMMU_PTE_IO_READ_PERMISSION_MASK 0x20000000 +#define IOMMU_PTE_IO_READ_PERMISSION_SHIFT 29 +#define IOMMU_PTE_IO_WRITE_PERMISSION_MASK 0x40000000 +#define IOMMU_PTE_IO_WRITE_PERMISSION_SHIFT 30 + +/* I/O Page Directory */ +#define IOMMU_PAGE_DIRECTORY_ENTRY_SIZE 8 +#define IOMMU_PAGE_DIRECTORY_ALIGNMENT 4096 +#define IOMMU_PDE_PRESENT_MASK 0x00000001 +#define IOMMU_PDE_PRESENT_SHIFT 0 +#define IOMMU_PDE_NEXT_LEVEL_MASK 0x00000E00 +#define IOMMU_PDE_NEXT_LEVEL_SHIFT 9 +#define IOMMU_PDE_ADDR_LOW_MASK 0xFFFFF000 +#define IOMMU_PDE_ADDR_LOW_SHIFT 12 +#define IOMMU_PDE_ADDR_HIGH_MASK 0x000FFFFF +#define IOMMU_PDE_ADDR_HIGH_SHIFT 0 +#define IOMMU_PDE_IO_READ_PERMISSION_MASK 0x20000000 +#define IOMMU_PDE_IO_READ_PERMISSION_SHIFT 29 +#define IOMMU_PDE_IO_WRITE_PERMISSION_MASK 0x40000000 +#define IOMMU_PDE_IO_WRITE_PERMISSION_SHIFT 30 + +/* Paging modes */ +#define IOMMU_PAGING_MODE_DISABLED 0x0 +#define IOMMU_PAGING_MODE_LEVEL_0 0x0 +#define IOMMU_PAGING_MODE_LEVEL_1 0x1 +#define IOMMU_PAGING_MODE_LEVEL_2 0x2 +#define IOMMU_PAGING_MODE_LEVEL_3 0x3 +#define IOMMU_PAGING_MODE_LEVEL_4 0x4 +#define IOMMU_PAGING_MODE_LEVEL_5 0x5 +#define IOMMU_PAGING_MODE_LEVEL_6 0x6 +#define IOMMU_PAGING_MODE_LEVEL_7 0x7 + +/* Flags */ +#define IOMMU_CONTROL_DISABLED 0 +#define IOMMU_CONTROL_ENABLED 1 + +#define MMIO_PAGES_PER_IOMMU (IOMMU_MMIO_REGION_LENGTH / PAGE_SIZE_4K) +#define IOMMU_PAGES (MMIO_PAGES_PER_IOMMU * MAX_AMD_IOMMUS) +#define DEFAULT_DOMAIN_ADDRESS_WIDTH 48 +#define MAX_AMD_IOMMUS 32 +#define IOMMU_PAGE_TABLE_LEVEL_3 3 +#define IOMMU_PAGE_TABLE_LEVEL_4 4 +#define IOMMU_IO_WRITE_ENABLED 1 +#define IOMMU_IO_READ_ENABLED 1 + +#endif /* _ASM_X86_64_AMD_IOMMU_DEFS_H */ diff -r be5f27ff147b xen/include/asm-x86/hvm/svm/amd-iommu-proto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h Fri Sep 21 14:37:47 2007 +0200 @@ -0,0 +1,88 @@ +/* + * 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 + */ + +#ifndef _ASM_X86_64_AMD_IOMMU_PROTO_H +#define _ASM_X86_64_AMD_IOMMU_PROTO_H + +#include + +#define for_each_amd_iommu(amd_iommu) \ + list_for_each_entry(amd_iommu, \ + &amd_iommu_head, list) + +#define DMA_32BIT_MASK 0x00000000ffffffffULL +#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) +#define PAGE_SHIFT_4K (12) +#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K) +#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K) + +typedef int (*iommu_detect_callback_ptr_t)(u8 bus, u8 dev, u8 func, u8 cap_ptr); + +/* amd-iommu-detect functions */ +int __init scan_for_iommu(iommu_detect_callback_ptr_t iommu_detect_callback); +int __init get_iommu_capabilities(u8 bus, u8 dev, u8 func, u8 cap_ptr, + struct amd_iommu *iommu); +int __init get_iommu_last_downstream_bus(struct amd_iommu *iommu); + +/* amd-iommu-init functions */ +int __init map_iommu_mmio_region(struct amd_iommu *iommu); +void __init unmap_iommu_mmio_region(struct amd_iommu *iommu); +void __init register_iommu_dev_table_in_mmio_space(struct amd_iommu *iommu); +void __init register_iommu_cmd_buffer_in_mmio_space(struct amd_iommu *iommu); +void __init enable_iommu(struct amd_iommu *iommu); + +/* mapping functions */ +int amd_iommu_map_page(struct domain *d, unsigned long gfn, + unsigned long mfn); +int amd_iommu_unmap_page(struct domain *d, unsigned long gfn); + +/* device table functions */ +void amd_iommu_set_dev_table_entry(u32 *dte, + u64 root_ptr, u16 domain_id, u8 paging_mode); + +/* send cmd to iommu */ +int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]); + +/* iommu domain funtions */ +int amd_iommu_domain_init(struct domain *domain); +void amd_iommu_setup_domain_device(struct domain *domain, + struct amd_iommu *iommu, int requestor_id); + +/* find iommu for bdf */ +struct amd_iommu *find_iommu_for_device(int bus, int devfn); + +static inline u32 get_field_from_reg_u32(u32 reg_value, u32 mask, u32 shift) +{ + u32 field; + field = (reg_value & mask) >> shift; + return field; +} + +static inline u32 set_field_in_reg_u32(u32 field, u32 reg_value, + u32 mask, u32 shift, u32 *reg) +{ + reg_value &= ~mask; + reg_value |= (field << shift) & mask; + if (reg) + *reg = reg_value; + return reg_value; +} + +#endif /* _ASM_X86_64_AMD_IOMMU_PROTO_H */