📄 iommu_map.c
字号:
/* * Copyright (C) 2007 Advanced Micro Devices, Inc. * Author: Leo Duran <leo.duran@amd.com> * Author: Wei Wang <wei.wang2@amd.com> - adapted to xen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <xen/sched.h>#include <xen/hvm/iommu.h>#include <asm/amd-iommu.h>#include <asm/hvm/svm/amd-iommu-proto.h>long amd_iommu_poll_comp_wait = COMPLETION_WAIT_DEFAULT_POLLING_COUNT;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 { amd_iov_warning("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, u64 intremap_ptr, u16 domain_id, u8 sys_mgt, u8 dev_ex, u8 paging_mode){ u64 addr_hi, addr_lo; u32 entry; dte[7] = dte[6] = 0; addr_lo = intremap_ptr & DMA_32BIT_MASK; addr_hi = intremap_ptr >> 32; set_field_in_reg_u32((u32)addr_hi, 0, IOMMU_DEV_TABLE_INT_TABLE_PTR_HIGH_MASK, IOMMU_DEV_TABLE_INT_TABLE_PTR_HIGH_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_INIT_PASSTHRU_MASK, IOMMU_DEV_TABLE_INIT_PASSTHRU_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_EINT_PASSTHRU_MASK, IOMMU_DEV_TABLE_EINT_PASSTHRU_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_NMI_PASSTHRU_MASK, IOMMU_DEV_TABLE_NMI_PASSTHRU_SHIFT, &entry); /* Fixed and arbitrated interrupts remapepd */ set_field_in_reg_u32(2, entry, IOMMU_DEV_TABLE_INT_CONTROL_MASK, IOMMU_DEV_TABLE_INT_CONTROL_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_LINT0_ENABLE_MASK, IOMMU_DEV_TABLE_LINT0_ENABLE_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_LINT1_ENABLE_MASK, IOMMU_DEV_TABLE_LINT1_ENABLE_SHIFT, &entry); dte[5] = entry; set_field_in_reg_u32((u32)addr_lo >> 6, 0, IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_MASK, IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_SHIFT, &entry); /* 2048 entries */ set_field_in_reg_u32(0xB, entry, IOMMU_DEV_TABLE_INT_TABLE_LENGTH_MASK, IOMMU_DEV_TABLE_INT_TABLE_LENGTH_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_INT_VALID_MASK, IOMMU_DEV_TABLE_INT_VALID_SHIFT, &entry); set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, IOMMU_DEV_TABLE_INT_TABLE_IGN_UNMAPPED_MASK, IOMMU_DEV_TABLE_INT_TABLE_IGN_UNMAPPED_SHIFT, &entry); dte[4] = entry; set_field_in_reg_u32(sys_mgt, 0, IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_MASK, IOMMU_DEV_TABLE_SYS_MGT_MSG_ENABLE_SHIFT, &entry); set_field_in_reg_u32(dev_ex, entry,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -