[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] amd iommu: Add iommu emulation for hvm guest
# HG changeset patch # User Wei Wang <wei.wang2@xxxxxxx> # Date 1326372650 -3600 # Node ID 6c104b46ef8977a8cac58f3c700f2ded6e03c557 # Parent 522d544f40318079678ddc1d0885e9e8287fb33a amd iommu: Add iommu emulation for hvm guest ATS device driver that support PASID [1] and PRI [2] capabilites needs to work with iommu driver in guest OS. We have to expose iommu functionality to HVM guest, if we want assign ATS device to it. A new hypervisor mmio handler is added to intercept iommu mmio accesses from guest. Signed-off-by: Wei Wang <wei.wang2@xxxxxxx> [1] http://www.pcisig.com/specifications/pciexpress/specifications/ECN-PASID-ATS-2011-03-31.pdf [2] http://www.pcisig.com/members/downloads/specifications/iov/ats_r1.1_26Jan09.pdf Committed-by: Jan Beulich <jbeulich@xxxxxxxx> --- diff -r 522d544f4031 -r 6c104b46ef89 xen/arch/x86/hvm/intercept.c --- a/xen/arch/x86/hvm/intercept.c Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/arch/x86/hvm/intercept.c Thu Jan 12 13:50:50 2012 +0100 @@ -38,7 +38,8 @@ &hpet_mmio_handler, &vlapic_mmio_handler, &vioapic_mmio_handler, - &msixtbl_mmio_handler + &msixtbl_mmio_handler, + &iommu_mmio_handler }; static int hvm_mmio_access(struct vcpu *v, diff -r 522d544f4031 -r 6c104b46ef89 xen/drivers/passthrough/amd/Makefile --- a/xen/drivers/passthrough/amd/Makefile Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/drivers/passthrough/amd/Makefile Thu Jan 12 13:50:50 2012 +0100 @@ -5,3 +5,4 @@ obj-bin-y += iommu_acpi.init.o obj-y += iommu_intr.o obj-y += iommu_cmd.o +obj-y += iommu_guest.o diff -r 522d544f4031 -r 6c104b46ef89 xen/drivers/passthrough/amd/iommu_cmd.c --- a/xen/drivers/passthrough/amd/iommu_cmd.c Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/drivers/passthrough/amd/iommu_cmd.c Thu Jan 12 13:50:50 2012 +0100 @@ -398,3 +398,15 @@ invalidate_iommu_all(iommu); flush_command_buffer(iommu); } + +void amd_iommu_send_guest_cmd(struct amd_iommu *iommu, u32 cmd[]) +{ + unsigned long flags; + + spin_lock_irqsave(&iommu->lock, flags); + + send_iommu_command(iommu, cmd); + flush_command_buffer(iommu); + + spin_unlock_irqrestore(&iommu->lock, flags); +} diff -r 522d544f4031 -r 6c104b46ef89 xen/drivers/passthrough/amd/iommu_guest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/drivers/passthrough/amd/iommu_guest.c Thu Jan 12 13:50:50 2012 +0100 @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2011 Advanced Micro Devices, Inc. + * Author: Wei Wang <wei.wang2@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <xen/sched.h> +#include <asm/p2m.h> +#include <asm/hvm/iommu.h> +#include <asm/amd-iommu.h> +#include <asm/hvm/svm/amd-iommu-proto.h> + + +#define IOMMU_MMIO_SIZE 0x8000 +#define IOMMU_MMIO_PAGE_NR 0x8 +#define RING_BF_LENGTH_MASK 0x0F000000 +#define RING_BF_LENGTH_SHIFT 24 + +#define PASMAX_9_bit 0x8 +#define GUEST_CR3_1_LEVEL 0x0 +#define GUEST_ADDRESS_SIZE_6_LEVEL 0x2 +#define HOST_ADDRESS_SIZE_6_LEVEL 0x2 + +#define guest_iommu_set_status(iommu, bit) \ + iommu_set_bit(&((iommu)->reg_status.lo), bit) + +#define guest_iommu_clear_status(iommu, bit) \ + iommu_clear_bit(&((iommu)->reg_status.lo), bit) + +#define reg_to_u64(reg) (((uint64_t)reg.hi << 32) | reg.lo ) +#define u64_to_reg(reg, val) \ + do \ + { \ + (reg)->lo = (u32)(val); \ + (reg)->hi = (val) >> 32; \ + } while (0) + +static unsigned int machine_bdf(struct domain *d, uint16_t guest_bdf) +{ + return guest_bdf; +} + +static uint16_t guest_bdf(struct domain *d, uint16_t machine_bdf) +{ + return machine_bdf; +} + +static inline struct guest_iommu *domain_iommu(struct domain *d) +{ + return domain_hvm_iommu(d)->g_iommu; +} + +static inline struct guest_iommu *vcpu_iommu(struct vcpu *v) +{ + return domain_hvm_iommu(v->domain)->g_iommu; +} + +static void guest_iommu_enable(struct guest_iommu *iommu) +{ + iommu->enabled = 1; +} + +static void guest_iommu_disable(struct guest_iommu *iommu) +{ + iommu->enabled = 0; +} + +static uint64_t get_guest_cr3_from_dte(dev_entry_t *dte) +{ + uint64_t gcr3_1, gcr3_2, gcr3_3; + + gcr3_1 = get_field_from_reg_u32(dte->data[1], + IOMMU_DEV_TABLE_GCR3_1_MASK, + IOMMU_DEV_TABLE_GCR3_1_SHIFT); + gcr3_2 = get_field_from_reg_u32(dte->data[2], + IOMMU_DEV_TABLE_GCR3_2_MASK, + IOMMU_DEV_TABLE_GCR3_2_SHIFT); + gcr3_3 = get_field_from_reg_u32(dte->data[3], + IOMMU_DEV_TABLE_GCR3_3_MASK, + IOMMU_DEV_TABLE_GCR3_3_SHIFT); + + return ((gcr3_3 << 31) | (gcr3_2 << 15 ) | (gcr3_1 << 12)) >> PAGE_SHIFT; +} + +static uint16_t get_domid_from_dte(dev_entry_t *dte) +{ + return get_field_from_reg_u32(dte->data[2], IOMMU_DEV_TABLE_DOMAIN_ID_MASK, + IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT); +} + +static uint16_t get_glx_from_dte(dev_entry_t *dte) +{ + return get_field_from_reg_u32(dte->data[1], IOMMU_DEV_TABLE_GLX_MASK, + IOMMU_DEV_TABLE_GLX_SHIFT); +} + +static uint16_t get_gv_from_dte(dev_entry_t *dte) +{ + return get_field_from_reg_u32(dte->data[1],IOMMU_DEV_TABLE_GV_MASK, + IOMMU_DEV_TABLE_GV_SHIFT); +} + +static unsigned int host_domid(struct domain *d, uint64_t g_domid) +{ + /* Only support one PPR device in guest for now */ + return d->domain_id; +} + +static unsigned long get_gfn_from_base_reg(uint64_t base_raw) +{ + struct mmio_reg reg; + uint64_t addr64; + + reg.lo = iommu_get_addr_lo_from_reg(base_raw & DMA_32BIT_MASK); + reg.hi = iommu_get_addr_hi_from_reg(base_raw >> 32); + addr64 = reg_to_u64(reg); + + ASSERT ( addr64 != 0 ); + + return addr64 >> PAGE_SHIFT; +} + +static void guest_iommu_deliver_msi(struct domain *d) +{ + uint8_t vector, dest, dest_mode, delivery_mode, trig_mode; + struct guest_iommu *iommu = domain_iommu(d); + + vector = iommu->msi.vector; + dest = iommu->msi.dest; + dest_mode = iommu->msi.dest_mode; + delivery_mode = iommu->msi.delivery_mode; + trig_mode = iommu->msi.trig_mode; + + vmsi_deliver(d, vector, dest, dest_mode, delivery_mode, trig_mode); +} + +static unsigned long guest_iommu_get_table_mfn(struct domain *d, + uint64_t base_raw, + unsigned int entry_size, + unsigned int pos) +{ + unsigned long idx, gfn, mfn; + p2m_type_t p2mt; + + gfn = get_gfn_from_base_reg(base_raw); + idx = (pos * entry_size) >> PAGE_SHIFT; + + mfn = mfn_x(get_gfn(d, gfn + idx, &p2mt)); + put_gfn(d, gfn); + + return mfn; +} + +static void guest_iommu_enable_dev_table(struct guest_iommu *iommu) +{ + uint32_t length_raw = get_field_from_reg_u32(iommu->dev_table.reg_base.lo, + IOMMU_DEV_TABLE_SIZE_MASK, + IOMMU_DEV_TABLE_SIZE_SHIFT); + iommu->dev_table.size = (length_raw + 1) * PAGE_SIZE; +} + +static void guest_iommu_enable_ring_buffer(struct guest_iommu *iommu, + struct guest_buffer *buffer, + uint32_t entry_size) +{ + uint32_t length_raw = get_field_from_reg_u32(buffer->reg_base.hi, + RING_BF_LENGTH_MASK, + RING_BF_LENGTH_SHIFT); + buffer->entries = 1 << length_raw; +} + +void guest_iommu_add_ppr_log(struct domain *d, u32 entry[]) +{ + uint16_t gdev_id; + unsigned long mfn, tail, head; + ppr_entry_t *log, *log_base; + struct guest_iommu *iommu; + + iommu = domain_iommu(d); + tail = iommu_get_rb_pointer(iommu->ppr_log.reg_tail.lo); + head = iommu_get_rb_pointer(iommu->ppr_log.reg_head.lo); + + if ( tail >= iommu->ppr_log.entries || head >= iommu->ppr_log.entries ) + { + AMD_IOMMU_DEBUG("Error: guest iommu ppr log overflows\n"); + guest_iommu_disable(iommu); + return; + } + + mfn = guest_iommu_get_table_mfn(d, reg_to_u64(iommu->ppr_log.reg_base), + sizeof(ppr_entry_t), tail); + ASSERT(mfn_valid(mfn)); + + log_base = map_domain_page(mfn); + log = log_base + tail % (PAGE_SIZE / sizeof(ppr_entry_t)); + + /* Convert physical device id back into virtual device id */ + gdev_id = guest_bdf(d, iommu_get_devid_from_cmd(entry[0])); + iommu_set_devid_to_cmd(&entry[0], gdev_id); + + memcpy(log, entry, sizeof(ppr_entry_t)); + + /* Now shift ppr log tail pointer */ + if ( ++tail >= iommu->ppr_log.entries ) + { + tail = 0; + guest_iommu_set_status(iommu, IOMMU_STATUS_PPR_LOG_OVERFLOW_SHIFT); + } + iommu_set_rb_pointer(&iommu->ppr_log.reg_tail.lo, tail); + unmap_domain_page(log_base); + + guest_iommu_deliver_msi(d); +} + +void guest_iommu_add_event_log(struct domain *d, u32 entry[]) +{ + uint16_t dev_id; + unsigned long mfn, tail, head; + event_entry_t *log, *log_base; + struct guest_iommu *iommu; + + iommu = domain_iommu(d); + tail = iommu_get_rb_pointer(iommu->event_log.reg_tail.lo); + head = iommu_get_rb_pointer(iommu->event_log.reg_head.lo); + + if ( tail >= iommu->event_log.entries || head >= iommu->event_log.entries ) + { + AMD_IOMMU_DEBUG("Error: guest iommu event overflows\n"); + guest_iommu_disable(iommu); + return; + } + + mfn = guest_iommu_get_table_mfn(d, reg_to_u64(iommu->event_log.reg_base), + sizeof(event_entry_t), tail); + ASSERT(mfn_valid(mfn)); + + log_base = map_domain_page(mfn); + log = log_base + tail % (PAGE_SIZE / sizeof(event_entry_t)); + + /* re-write physical device id into virtual device id */ + dev_id = guest_bdf(d, iommu_get_devid_from_cmd(entry[0])); + iommu_set_devid_to_cmd(&entry[0], dev_id); + memcpy(log, entry, sizeof(event_entry_t)); + + /* Now shift event log tail pointer */ + if ( ++tail >= iommu->event_log.entries ) + { + tail = 0; + guest_iommu_set_status(iommu, IOMMU_STATUS_EVENT_OVERFLOW_SHIFT); + } + + iommu_set_rb_pointer(&iommu->event_log.reg_tail.lo, tail); + unmap_domain_page(log_base); + + guest_iommu_deliver_msi(d); +} + +static int do_complete_ppr_request(struct domain *d, cmd_entry_t *cmd) +{ + uint16_t dev_id; + struct amd_iommu *iommu; + + dev_id = machine_bdf(d, iommu_get_devid_from_cmd(cmd->data[0])); + iommu = find_iommu_for_device(0, dev_id); + + if ( !iommu ) + { + AMD_IOMMU_DEBUG("%s: Fail to find iommu for bdf %x\n", + __func__, dev_id); + return -ENODEV; + } + + /* replace virtual device id into physical */ + iommu_set_devid_to_cmd(&cmd->data[0], dev_id); + amd_iommu_send_guest_cmd(iommu, cmd->data); + + return 0; +} + +static int do_invalidate_pages(struct domain *d, cmd_entry_t *cmd) +{ + uint16_t gdom_id, hdom_id; + struct amd_iommu *iommu = NULL; + + gdom_id = get_field_from_reg_u32(cmd->data[1], + IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_MASK, + IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_SHIFT); + + hdom_id = host_domid(d, gdom_id); + set_field_in_reg_u32(hdom_id, cmd->data[1], + IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_MASK, + IOMMU_INV_IOMMU_PAGES_DOMAIN_ID_SHIFT, &cmd->data[1]); + + for_each_amd_iommu ( iommu ) + amd_iommu_send_guest_cmd(iommu, cmd->data); + + return 0; +} + +static int do_invalidate_all(struct domain *d, cmd_entry_t *cmd) +{ + struct amd_iommu *iommu = NULL; + + for_each_amd_iommu ( iommu ) + amd_iommu_flush_all_pages(d); + + return 0; +} + +static int do_invalidate_iotlb_pages(struct domain *d, cmd_entry_t *cmd) +{ + struct amd_iommu *iommu; + uint16_t dev_id; + + dev_id = machine_bdf(d, iommu_get_devid_from_cmd(cmd->data[0])); + + iommu = find_iommu_for_device(0, dev_id); + if ( !iommu ) + { + AMD_IOMMU_DEBUG("%s: Fail to find iommu for bdf %x\n", + __func__, dev_id); + return -ENODEV; + } + + iommu_set_devid_to_cmd(&cmd->data[0], dev_id); + amd_iommu_send_guest_cmd(iommu, cmd->data); + + return 0; +} + +static int do_completion_wait(struct domain *d, cmd_entry_t *cmd) +{ + bool_t com_wait_int_en, com_wait_int, i, s; + struct guest_iommu *iommu; + unsigned long gfn; + p2m_type_t p2mt; + + iommu = domain_iommu(d); + + i = iommu_get_bit(cmd->data[0], IOMMU_COMP_WAIT_I_FLAG_SHIFT); + s = iommu_get_bit(cmd->data[0], IOMMU_COMP_WAIT_S_FLAG_SHIFT); + + if ( i ) + guest_iommu_set_status(iommu, IOMMU_STATUS_COMP_WAIT_INT_SHIFT); + + if ( s ) + { + uint64_t gaddr_lo, gaddr_hi, gaddr_64, data; + void *vaddr; + + data = (uint64_t)cmd->data[3] << 32 | cmd->data[2]; + gaddr_lo = get_field_from_reg_u32(cmd->data[0], + IOMMU_COMP_WAIT_ADDR_LOW_MASK, + IOMMU_COMP_WAIT_ADDR_LOW_SHIFT); + gaddr_hi = get_field_from_reg_u32(cmd->data[1], + IOMMU_COMP_WAIT_ADDR_HIGH_MASK, + IOMMU_COMP_WAIT_ADDR_HIGH_SHIFT); + + gaddr_64 = (gaddr_hi << 32) | (gaddr_lo << 3); + + gfn = gaddr_64 >> PAGE_SHIFT; + vaddr = map_domain_page(mfn_x(get_gfn(d, gfn ,&p2mt))); + put_gfn(d, gfn); + + write_u64_atomic((uint64_t *)(vaddr + (gaddr_64 & (PAGE_SIZE-1))), + data); + unmap_domain_page(vaddr); + } + + com_wait_int_en = iommu_get_bit(iommu->reg_ctrl.lo, + IOMMU_CONTROL_COMP_WAIT_INT_SHIFT); + com_wait_int = iommu_get_bit(iommu->reg_status.lo, + IOMMU_STATUS_COMP_WAIT_INT_SHIFT); + + if ( com_wait_int_en && com_wait_int ) + guest_iommu_deliver_msi(d); + + return 0; +} + +static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) +{ + uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id; + dev_entry_t *gdte, *mdte, *dte_base; + struct amd_iommu *iommu = NULL; + struct guest_iommu *g_iommu; + uint64_t gcr3_gfn, gcr3_mfn; + uint8_t glx, gv; + unsigned long dte_mfn, flags; + p2m_type_t p2mt; + + g_iommu = domain_iommu(d); + gbdf = iommu_get_devid_from_cmd(cmd->data[0]); + mbdf = machine_bdf(d, gbdf); + + /* Guest can only update DTEs for its passthru devices */ + if ( mbdf == 0 || gbdf == 0 ) + return 0; + + /* Sometimes guest invalidates devices from non-exists dtes */ + if ( (gbdf * sizeof(dev_entry_t)) > g_iommu->dev_table.size ) + return 0; + + dte_mfn = guest_iommu_get_table_mfn(d, + reg_to_u64(g_iommu->dev_table.reg_base), + sizeof(dev_entry_t), gbdf); + ASSERT(mfn_valid(dte_mfn)); + + dte_base = map_domain_page(dte_mfn); + + gdte = dte_base + gbdf % (PAGE_SIZE / sizeof(dev_entry_t)); + + gdom_id = get_domid_from_dte(gdte); + gcr3_gfn = get_guest_cr3_from_dte(gdte); + + /* Do not update host dte before gcr3 has been set */ + if ( gcr3_gfn == 0 ) + return 0; + + gcr3_mfn = mfn_x(get_gfn(d, gcr3_gfn, &p2mt)); + put_gfn(d, gcr3_gfn); + + ASSERT(mfn_valid(gcr3_mfn)); + + /* Read guest dte information */ + iommu = find_iommu_for_device(0, mbdf); + if ( !iommu ) + { + AMD_IOMMU_DEBUG("%s: Fail to find iommu for bdf %x!\n", + __func__, mbdf); + return -ENODEV; + } + + glx = get_glx_from_dte(gdte); + gv = get_gv_from_dte(gdte); + + unmap_domain_page(dte_base); + + /* Setup host device entry */ + hdom_id = host_domid(d, gdom_id); + req_id = get_dma_requestor_id(iommu->seg, mbdf); + mdte = iommu->dev_table.buffer + (req_id * sizeof(dev_entry_t)); + + spin_lock_irqsave(&iommu->lock, flags); + iommu_dte_set_guest_cr3((u32 *)mdte, hdom_id, + gcr3_mfn << PAGE_SHIFT, gv, glx); + + amd_iommu_flush_device(iommu, req_id); + spin_unlock_irqrestore(&iommu->lock, flags); + + return 0; +} + +static void guest_iommu_process_command(unsigned long _d) +{ + unsigned long opcode, tail, head, entries_per_page, cmd_mfn; + cmd_entry_t *cmd, *cmd_base; + struct domain *d = (struct domain *)_d; + struct guest_iommu *iommu; + + iommu = domain_iommu(d); + + if ( !iommu->enabled ) + return; + + head = iommu_get_rb_pointer(iommu->cmd_buffer.reg_head.lo); + tail = iommu_get_rb_pointer(iommu->cmd_buffer.reg_tail.lo); + + /* Tail pointer is rolled over by guest driver, value outside + * cmd_buffer_entries cause iommu disabled + */ + + if ( tail >= iommu->cmd_buffer.entries || + head >= iommu->cmd_buffer.entries ) + { + AMD_IOMMU_DEBUG("Error: guest iommu cmd buffer overflows\n"); + guest_iommu_disable(iommu); + return; + } + + entries_per_page = PAGE_SIZE / sizeof(cmd_entry_t); + + while ( head != tail ) + { + int ret = 0; + + cmd_mfn = guest_iommu_get_table_mfn(d, + reg_to_u64(iommu->cmd_buffer.reg_base), + sizeof(cmd_entry_t), head); + ASSERT(mfn_valid(cmd_mfn)); + + cmd_base = map_domain_page(cmd_mfn); + cmd = cmd_base + head % entries_per_page; + + opcode = get_field_from_reg_u32(cmd->data[1], + IOMMU_CMD_OPCODE_MASK, + IOMMU_CMD_OPCODE_SHIFT); + switch ( opcode ) + { + case IOMMU_CMD_COMPLETION_WAIT: + ret = do_completion_wait(d, cmd); + break; + case IOMMU_CMD_INVALIDATE_DEVTAB_ENTRY: + ret = do_invalidate_dte(d, cmd); + break; + case IOMMU_CMD_INVALIDATE_IOMMU_PAGES: + ret = do_invalidate_pages(d, cmd); + break; + case IOMMU_CMD_INVALIDATE_IOTLB_PAGES: + ret = do_invalidate_iotlb_pages(d, cmd); + break; + case IOMMU_CMD_INVALIDATE_INT_TABLE: + break; + case IOMMU_CMD_COMPLETE_PPR_REQUEST: + ret = do_complete_ppr_request(d, cmd); + break; + case IOMMU_CMD_INVALIDATE_IOMMU_ALL: + ret = do_invalidate_all(d, cmd); + break; + default: + AMD_IOMMU_DEBUG("CMD: Unknown command cmd_type = %lx " + "head = %ld\n", opcode, head); + break; + } + + unmap_domain_page(cmd_base); + if ( ++head >= iommu->cmd_buffer.entries ) + head = 0; + if ( ret ) + guest_iommu_disable(iommu); + } + + /* Now shift cmd buffer head pointer */ + iommu_set_rb_pointer(&iommu->cmd_buffer.reg_head.lo, head); + return; +} + +static int guest_iommu_write_ctrl(struct guest_iommu *iommu, uint64_t newctrl) +{ + bool_t cmd_en, event_en, iommu_en, ppr_en, ppr_log_en; + bool_t cmd_en_old, event_en_old, iommu_en_old; + bool_t cmd_run; + + iommu_en = iommu_get_bit(newctrl, + IOMMU_CONTROL_TRANSLATION_ENABLE_SHIFT); + iommu_en_old = iommu_get_bit(iommu->reg_ctrl.lo, + IOMMU_CONTROL_TRANSLATION_ENABLE_SHIFT); + + cmd_en = iommu_get_bit(newctrl, + IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_SHIFT); + cmd_en_old = iommu_get_bit(iommu->reg_ctrl.lo, + IOMMU_CONTROL_COMMAND_BUFFER_ENABLE_SHIFT); + cmd_run = iommu_get_bit(iommu->reg_status.lo, + IOMMU_STATUS_CMD_BUFFER_RUN_SHIFT); + event_en = iommu_get_bit(newctrl, + IOMMU_CONTROL_EVENT_LOG_ENABLE_SHIFT); + event_en_old = iommu_get_bit(iommu->reg_ctrl.lo, + IOMMU_CONTROL_EVENT_LOG_ENABLE_SHIFT); + + ppr_en = iommu_get_bit(newctrl, + IOMMU_CONTROL_PPR_ENABLE_SHIFT); + ppr_log_en = iommu_get_bit(newctrl, + IOMMU_CONTROL_PPR_LOG_ENABLE_SHIFT); + + if ( iommu_en ) + { + guest_iommu_enable(iommu); + guest_iommu_enable_dev_table(iommu); + } + + if ( iommu_en && cmd_en ) + { + guest_iommu_enable_ring_buffer(iommu, &iommu->cmd_buffer, + sizeof(cmd_entry_t)); + /* Enable iommu command processing */ + tasklet_schedule(&iommu->cmd_buffer_tasklet); + } + + if ( iommu_en && event_en ) + { + guest_iommu_enable_ring_buffer(iommu, &iommu->event_log, + sizeof(event_entry_t)); + guest_iommu_set_status(iommu, IOMMU_STATUS_EVENT_LOG_RUN_SHIFT); + guest_iommu_clear_status(iommu, IOMMU_STATUS_EVENT_OVERFLOW_SHIFT); + } + + if ( iommu_en && ppr_en && ppr_log_en ) + { + guest_iommu_enable_ring_buffer(iommu, &iommu->ppr_log, + sizeof(ppr_entry_t)); + guest_iommu_set_status(iommu, IOMMU_STATUS_PPR_LOG_RUN_SHIFT); + guest_iommu_clear_status(iommu, IOMMU_STATUS_PPR_LOG_OVERFLOW_SHIFT); + } + + if ( iommu_en && cmd_en_old && !cmd_en ) + { + /* Disable iommu command processing */ + tasklet_kill(&iommu->cmd_buffer_tasklet); + } + + if ( event_en_old && !event_en ) + guest_iommu_clear_status(iommu, IOMMU_STATUS_EVENT_LOG_RUN_SHIFT); + + if ( iommu_en_old && !iommu_en ) + guest_iommu_disable(iommu); + + u64_to_reg(&iommu->reg_ctrl, newctrl); + return 0; +} + +static uint64_t iommu_mmio_read64(struct guest_iommu *iommu, + unsigned long offset) +{ + uint64_t val; + + switch ( offset ) + { + case IOMMU_DEV_TABLE_BASE_LOW_OFFSET: + val = reg_to_u64(iommu->dev_table.reg_base); + break; + case IOMMU_CMD_BUFFER_BASE_LOW_OFFSET: + val = reg_to_u64(iommu->cmd_buffer.reg_base); + break; + case IOMMU_EVENT_LOG_BASE_LOW_OFFSET: + val = reg_to_u64(iommu->event_log.reg_base); + break; + case IOMMU_PPR_LOG_BASE_LOW_OFFSET: + val = reg_to_u64(iommu->ppr_log.reg_base); + break; + case IOMMU_CMD_BUFFER_HEAD_OFFSET: + val = reg_to_u64(iommu->cmd_buffer.reg_head); + break; + case IOMMU_CMD_BUFFER_TAIL_OFFSET: + val = reg_to_u64(iommu->cmd_buffer.reg_tail); + break; + case IOMMU_EVENT_LOG_HEAD_OFFSET: + val = reg_to_u64(iommu->event_log.reg_head); + break; + case IOMMU_EVENT_LOG_TAIL_OFFSET: + val = reg_to_u64(iommu->event_log.reg_tail); + break; + case IOMMU_PPR_LOG_HEAD_OFFSET: + val = reg_to_u64(iommu->ppr_log.reg_head); + break; + case IOMMU_PPR_LOG_TAIL_OFFSET: + val = reg_to_u64(iommu->ppr_log.reg_tail); + break; + case IOMMU_CONTROL_MMIO_OFFSET: + val = reg_to_u64(iommu->reg_ctrl); + break; + case IOMMU_STATUS_MMIO_OFFSET: + val = reg_to_u64(iommu->reg_status); + break; + case IOMMU_EXT_FEATURE_MMIO_OFFSET: + val = reg_to_u64(iommu->reg_ext_feature); + break; + + default: + AMD_IOMMU_DEBUG("Guest reads unknown mmio offset = %lx\n", offset); + val = 0; + break; + } + + return val; +} + +static int guest_iommu_mmio_read(struct vcpu *v, unsigned long addr, + unsigned long len, unsigned long *pval) +{ + struct guest_iommu *iommu = vcpu_iommu(v); + unsigned long offset; + uint64_t val; + uint32_t mmio, shift; + uint64_t mask = 0; + + offset = addr - iommu->mmio_base; + + if ( unlikely((offset & (len - 1 )) || (len > 8)) ) + { + AMD_IOMMU_DEBUG("iommu mmio read access is not aligned:" + " offset = %lx, len = %lx\n", offset, len); + return X86EMUL_UNHANDLEABLE; + } + + mask = (len == 8) ? ~0ULL : (1ULL << (len * 8)) - 1; + shift = (offset & 7u) * 8; + + /* mmio access is always aligned on 8-byte boundary */ + mmio = offset & (~7u); + + spin_lock(&iommu->lock); + val = iommu_mmio_read64(iommu, mmio); + spin_unlock(&iommu->lock); + + *pval = (val >> shift ) & mask; + + return X86EMUL_OKAY; +} + +static void guest_iommu_mmio_write64(struct guest_iommu *iommu, + unsigned long offset, uint64_t val) +{ + switch ( offset ) + { + case IOMMU_DEV_TABLE_BASE_LOW_OFFSET: + u64_to_reg(&iommu->dev_table.reg_base, val); + break; + case IOMMU_CMD_BUFFER_BASE_LOW_OFFSET: + u64_to_reg(&iommu->cmd_buffer.reg_base, val); + break; + case IOMMU_EVENT_LOG_BASE_LOW_OFFSET: + u64_to_reg(&iommu->event_log.reg_base, val); + case IOMMU_PPR_LOG_BASE_LOW_OFFSET: + u64_to_reg(&iommu->ppr_log.reg_base, val); + break; + case IOMMU_CONTROL_MMIO_OFFSET: + guest_iommu_write_ctrl(iommu, val); + break; + case IOMMU_CMD_BUFFER_HEAD_OFFSET: + u64_to_reg(&iommu->cmd_buffer.reg_head, val); + break; + case IOMMU_CMD_BUFFER_TAIL_OFFSET: + u64_to_reg(&iommu->cmd_buffer.reg_tail, val); + tasklet_schedule(&iommu->cmd_buffer_tasklet); + break; + case IOMMU_EVENT_LOG_HEAD_OFFSET: + u64_to_reg(&iommu->event_log.reg_head, val); + break; + case IOMMU_EVENT_LOG_TAIL_OFFSET: + u64_to_reg(&iommu->event_log.reg_tail, val); + break; + case IOMMU_PPR_LOG_HEAD_OFFSET: + u64_to_reg(&iommu->ppr_log.reg_head, val); + break; + case IOMMU_PPR_LOG_TAIL_OFFSET: + u64_to_reg(&iommu->ppr_log.reg_tail, val); + break; + case IOMMU_STATUS_MMIO_OFFSET: + u64_to_reg(&iommu->reg_status, val); + break; + + default: + AMD_IOMMU_DEBUG("guest writes unknown mmio offset = %lx," + " val = %" PRIx64 "\n", offset, val); + break; + } +} + +static int guest_iommu_mmio_write(struct vcpu *v, unsigned long addr, + unsigned long len, unsigned long val) +{ + struct guest_iommu *iommu = vcpu_iommu(v); + unsigned long offset; + uint64_t reg_old, mmio; + uint32_t shift; + uint64_t mask = 0; + + offset = addr - iommu->mmio_base; + + if ( unlikely((offset & (len - 1)) || (len > 8)) ) + { + AMD_IOMMU_DEBUG("iommu mmio write access is not aligned:" + " offset = %lx, len = %lx\n", offset, len); + return X86EMUL_UNHANDLEABLE; + } + + mask = (len == 8) ? ~0ULL : (1ULL << (len * 8)) - 1; + shift = (offset & 7) * 8; + + /* mmio access is always aligned on 8-byte boundary */ + mmio = offset & ~7; + + spin_lock(&iommu->lock); + + reg_old = iommu_mmio_read64(iommu, mmio); + reg_old &= ~(mask << shift); + val = reg_old | ((val & mask) << shift); + guest_iommu_mmio_write64(iommu, mmio, val); + + spin_unlock(&iommu->lock); + + return X86EMUL_OKAY; +} + +int guest_iommu_set_base(struct domain *d, uint64_t base) +{ + p2m_type_t t; + struct guest_iommu *iommu = domain_iommu(d); + + iommu->mmio_base = base; + base >>= PAGE_SHIFT; + + for ( int i = 0; i < IOMMU_MMIO_PAGE_NR; i++ ) + { + unsigned long gfn = base + i; + + get_gfn_query(d, gfn, &t); + p2m_change_type(d, gfn, t, p2m_mmio_dm); + put_gfn(d, gfn); + } + + return 0; +} + +/* Initialize mmio read only bits */ +static void guest_iommu_reg_init(struct guest_iommu *iommu) +{ + uint32_t lower, upper; + + lower = upper = 0; + /* Support prefetch */ + iommu_set_bit(&lower,IOMMU_EXT_FEATURE_PREFSUP_SHIFT); + /* Support PPR log */ + iommu_set_bit(&lower,IOMMU_EXT_FEATURE_PPRSUP_SHIFT); + /* Support guest translation */ + iommu_set_bit(&lower,IOMMU_EXT_FEATURE_GTSUP_SHIFT); + /* Support invalidate all command */ + iommu_set_bit(&lower,IOMMU_EXT_FEATURE_IASUP_SHIFT); + + /* Host translation size has 6 levels */ + set_field_in_reg_u32(HOST_ADDRESS_SIZE_6_LEVEL, lower, + IOMMU_EXT_FEATURE_HATS_MASK, + IOMMU_EXT_FEATURE_HATS_SHIFT, + &lower); + /* Guest translation size has 6 levels */ + set_field_in_reg_u32(GUEST_ADDRESS_SIZE_6_LEVEL, lower, + IOMMU_EXT_FEATURE_GATS_MASK, + IOMMU_EXT_FEATURE_GATS_SHIFT, + &lower); + /* Single level gCR3 */ + set_field_in_reg_u32(GUEST_CR3_1_LEVEL, lower, + IOMMU_EXT_FEATURE_GLXSUP_MASK, + IOMMU_EXT_FEATURE_GLXSUP_SHIFT, &lower); + /* 9 bit PASID */ + set_field_in_reg_u32(PASMAX_9_bit, upper, + IOMMU_EXT_FEATURE_PASMAX_MASK, + IOMMU_EXT_FEATURE_PASMAX_SHIFT, &upper); + + iommu->reg_ext_feature.lo = lower; + iommu->reg_ext_feature.hi = upper; +} + +/* Domain specific initialization */ +int guest_iommu_init(struct domain* d) +{ + struct guest_iommu *iommu; + struct hvm_iommu *hd = domain_hvm_iommu(d); + + if ( !is_hvm_domain(d) ) + return 0; + + iommu = xzalloc(struct guest_iommu); + if ( !iommu ) + { + AMD_IOMMU_DEBUG("Error allocating guest iommu structure.\n"); + return 1; + } + + guest_iommu_reg_init(iommu); + iommu->domain = d; + hd->g_iommu = iommu; + + tasklet_init(&iommu->cmd_buffer_tasklet, + guest_iommu_process_command, (unsigned long)d); + + spin_lock_init(&iommu->lock); + + return 0; +} + +void guest_iommu_destroy(struct domain *d) +{ + struct guest_iommu *iommu; + + if ( !is_hvm_domain(d) ) + return; + + iommu = domain_iommu(d); + + tasklet_kill(&iommu->cmd_buffer_tasklet); + xfree(iommu); + + domain_hvm_iommu(d)->g_iommu = NULL; +} + +static int guest_iommu_mmio_range(struct vcpu *v, unsigned long addr) +{ + struct guest_iommu *iommu = vcpu_iommu(v); + + return addr >= iommu->mmio_base && + addr < iommu->mmio_base + IOMMU_MMIO_SIZE; +} + +const struct hvm_mmio_handler iommu_mmio_handler = { + .check_handler = guest_iommu_mmio_range, + .read_handler = guest_iommu_mmio_read, + .write_handler = guest_iommu_mmio_write +}; diff -r 522d544f4031 -r 6c104b46ef89 xen/drivers/passthrough/amd/iommu_map.c --- a/xen/drivers/passthrough/amd/iommu_map.c Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/drivers/passthrough/amd/iommu_map.c Thu Jan 12 13:50:50 2012 +0100 @@ -234,6 +234,53 @@ dte[3] = entry; } +void iommu_dte_set_guest_cr3(u32 *dte, u16 dom_id, u64 gcr3, + int gv, unsigned int glx) +{ + u32 entry, gcr3_1, gcr3_2, gcr3_3; + + gcr3_3 = gcr3 >> 31; + gcr3_2 = (gcr3 >> 15) & 0xFFFF; + gcr3_1 = (gcr3 >> PAGE_SHIFT) & 0x7; + + /* I bit must be set when gcr3 is enabled */ + entry = dte[3]; + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_DEV_TABLE_IOTLB_SUPPORT_MASK, + IOMMU_DEV_TABLE_IOTLB_SUPPORT_SHIFT, &entry); + /* update gcr3 */ + set_field_in_reg_u32(gcr3_3, entry, + IOMMU_DEV_TABLE_GCR3_3_MASK, + IOMMU_DEV_TABLE_GCR3_3_SHIFT, &entry); + dte[3] = entry; + + set_field_in_reg_u32(dom_id, entry, + IOMMU_DEV_TABLE_DOMAIN_ID_MASK, + IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT, &entry); + /* update gcr3 */ + entry = dte[2]; + set_field_in_reg_u32(gcr3_2, entry, + IOMMU_DEV_TABLE_GCR3_2_MASK, + IOMMU_DEV_TABLE_GCR3_2_SHIFT, &entry); + dte[2] = entry; + + entry = dte[1]; + /* Enable GV bit */ + set_field_in_reg_u32(!!gv, entry, + IOMMU_DEV_TABLE_GV_MASK, + IOMMU_DEV_TABLE_GV_SHIFT, &entry); + + /* 1 level guest cr3 table */ + set_field_in_reg_u32(glx, entry, + IOMMU_DEV_TABLE_GLX_MASK, + IOMMU_DEV_TABLE_GLX_SHIFT, &entry); + /* update gcr3 */ + set_field_in_reg_u32(gcr3_1, entry, + IOMMU_DEV_TABLE_GCR3_1_MASK, + IOMMU_DEV_TABLE_GCR3_1_SHIFT, &entry); + dte[1] = entry; +} + u64 amd_iommu_get_next_table_from_pte(u32 *entry) { u64 addr_lo, addr_hi, ptr; diff -r 522d544f4031 -r 6c104b46ef89 xen/drivers/passthrough/amd/pci_amd_iommu.c --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c Thu Jan 12 13:50:50 2012 +0100 @@ -260,6 +260,8 @@ hd->domain_id = d->domain_id; + guest_iommu_init(d); + return 0; } @@ -443,6 +445,7 @@ static void amd_iommu_domain_destroy(struct domain *d) { + guest_iommu_destroy(d); deallocate_iommu_page_tables(d); amd_iommu_flush_all_pages(d); } diff -r 522d544f4031 -r 6c104b46ef89 xen/include/asm-x86/amd-iommu.h --- a/xen/include/asm-x86/amd-iommu.h Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/include/asm-x86/amd-iommu.h Thu Jan 12 13:50:50 2012 +0100 @@ -24,6 +24,7 @@ #include <xen/types.h> #include <xen/list.h> #include <xen/spinlock.h> +#include <xen/tasklet.h> #include <asm/hvm/svm/amd-iommu-defs.h> #define iommu_found() (!list_empty(&amd_iommu_head)) @@ -129,4 +130,55 @@ int iterate_ivrs_mappings(int (*)(u16 seg, struct ivrs_mappings *)); int iterate_ivrs_entries(int (*)(u16 seg, struct ivrs_mappings *)); +/* iommu tables in guest space */ +struct mmio_reg { + uint32_t lo; + uint32_t hi; +}; + +struct guest_dev_table { + struct mmio_reg reg_base; + uint32_t size; +}; + +struct guest_buffer { + struct mmio_reg reg_base; + struct mmio_reg reg_tail; + struct mmio_reg reg_head; + uint32_t entries; +}; + +struct guest_iommu_msi { + uint8_t vector; + uint8_t dest; + uint8_t dest_mode; + uint8_t delivery_mode; + uint8_t trig_mode; +}; + +/* virtual IOMMU structure */ +struct guest_iommu { + + struct domain *domain; + spinlock_t lock; + bool_t enabled; + + struct guest_dev_table dev_table; + struct guest_buffer cmd_buffer; + struct guest_buffer event_log; + struct guest_buffer ppr_log; + + struct tasklet cmd_buffer_tasklet; + + uint64_t mmio_base; /* MMIO base address */ + + /* MMIO regs */ + struct mmio_reg reg_ctrl; /* MMIO offset 0018h */ + struct mmio_reg reg_status; /* MMIO offset 2020h */ + struct mmio_reg reg_ext_feature; /* MMIO offset 0030h */ + + /* guest interrupt settings */ + struct guest_iommu_msi msi; +}; + #endif /* _ASM_X86_64_AMD_IOMMU_H */ diff -r 522d544f4031 -r 6c104b46ef89 xen/include/asm-x86/hvm/io.h --- a/xen/include/asm-x86/hvm/io.h Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/include/asm-x86/hvm/io.h Thu Jan 12 13:50:50 2012 +0100 @@ -69,8 +69,9 @@ extern const struct hvm_mmio_handler vlapic_mmio_handler; extern const struct hvm_mmio_handler vioapic_mmio_handler; extern const struct hvm_mmio_handler msixtbl_mmio_handler; +extern const struct hvm_mmio_handler iommu_mmio_handler; -#define HVM_MMIO_HANDLER_NR 4 +#define HVM_MMIO_HANDLER_NR 5 int hvm_io_intercept(ioreq_t *p, int type); void register_io_handler( diff -r 522d544f4031 -r 6c104b46ef89 xen/include/asm-x86/hvm/svm/amd-iommu-defs.h --- a/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h Thu Jan 12 13:50:50 2012 +0100 @@ -113,6 +113,13 @@ #define IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT 12 /* DeviceTable Entry[63:32] */ +#define IOMMU_DEV_TABLE_GV_SHIFT 23 +#define IOMMU_DEV_TABLE_GV_MASK 0x800000 +#define IOMMU_DEV_TABLE_GLX_SHIFT 24 +#define IOMMU_DEV_TABLE_GLX_MASK 0x3000000 +#define IOMMU_DEV_TABLE_GCR3_1_SHIFT 26 +#define IOMMU_DEV_TABLE_GCR3_1_MASK 0x1c000000 + #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 @@ -123,6 +130,8 @@ /* DeviceTable Entry[95:64] */ #define IOMMU_DEV_TABLE_DOMAIN_ID_MASK 0x0000FFFF #define IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT 0 +#define IOMMU_DEV_TABLE_GCR3_2_SHIFT 16 +#define IOMMU_DEV_TABLE_GCR3_2_MASK 0xFFFF0000 /* DeviceTable Entry[127:96] */ #define IOMMU_DEV_TABLE_IOTLB_SUPPORT_MASK 0x00000001 @@ -151,6 +160,8 @@ #define IOMMU_DEV_TABLE_INT_TABLE_IGN_UNMAPPED_SHIFT 5 #define IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_MASK 0xFFFFFFC0 #define IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_SHIFT 6 +#define IOMMU_DEV_TABLE_GCR3_3_SHIFT 11 +#define IOMMU_DEV_TABLE_GCR3_3_MASK 0xfffff800 /* DeviceTable Entry[191:160] */ #define IOMMU_DEV_TABLE_INT_TABLE_PTR_HIGH_MASK 0x000FFFFF @@ -179,6 +190,7 @@ #define IOMMU_CMD_INVALIDATE_IOMMU_PAGES 0x3 #define IOMMU_CMD_INVALIDATE_IOTLB_PAGES 0x4 #define IOMMU_CMD_INVALIDATE_INT_TABLE 0x5 +#define IOMMU_CMD_COMPLETE_PPR_REQUEST 0x7 #define IOMMU_CMD_INVALIDATE_IOMMU_ALL 0x8 /* COMPLETION_WAIT command */ @@ -265,6 +277,28 @@ #define IOMMU_EVENT_DEVICE_ID_MASK 0x0000FFFF #define IOMMU_EVENT_DEVICE_ID_SHIFT 0 +/* PPR Log */ +#define IOMMU_PPR_LOG_ENTRY_SIZE 16 +#define IOMMU_PPR_LOG_POWER_OF2_ENTRIES_PER_PAGE 8 +#define IOMMU_PPR_LOG_U32_PER_ENTRY (IOMMU_PPR_LOG_ENTRY_SIZE / 4) + +#define IOMMU_PPR_LOG_BASE_LOW_OFFSET 0x0038 +#define IOMMU_PPR_LOG_BASE_HIGH_OFFSET 0x003C +#define IOMMU_PPR_LOG_BASE_LOW_MASK 0xFFFFF000 +#define IOMMU_PPR_LOG_BASE_LOW_SHIFT 12 +#define IOMMU_PPR_LOG_BASE_HIGH_MASK 0x000FFFFF +#define IOMMU_PPR_LOG_BASE_HIGH_SHIFT 0 +#define IOMMU_PPR_LOG_LENGTH_MASK 0x0F000000 +#define IOMMU_PPR_LOG_LENGTH_SHIFT 24 +#define IOMMU_PPR_LOG_HEAD_MASK 0x0007FFF0 +#define IOMMU_PPR_LOG_HEAD_SHIFT 4 +#define IOMMU_PPR_LOG_TAIL_MASK 0x0007FFF0 +#define IOMMU_PPR_LOG_TAIL_SHIFT 4 +#define IOMMU_PPR_LOG_HEAD_OFFSET 0x2030 +#define IOMMU_PPR_LOG_TAIL_OFFSET 0x2038 +#define IOMMU_PPR_LOG_DEVICE_ID_MASK 0x0000FFFF +#define IOMMU_PPR_LOG_DEVICE_ID_SHIFT 0 + /* Control Register */ #define IOMMU_CONTROL_MMIO_OFFSET 0x18 #define IOMMU_CONTROL_TRANSLATION_ENABLE_MASK 0x00000001 @@ -292,6 +326,11 @@ #define IOMMU_CONTROL_RESTART_MASK 0x80000000 #define IOMMU_CONTROL_RESTART_SHIFT 31 +#define IOMMU_CONTROL_PPR_LOG_ENABLE_SHIFT 13 +#define IOMMU_CONTROL_PPR_INT_SHIFT 14 +#define IOMMU_CONTROL_PPR_ENABLE_SHIFT 15 +#define IOMMU_CONTROL_GT_ENABLE_SHIFT 16 + /* Exclusion Register */ #define IOMMU_EXCLUSION_BASE_LOW_OFFSET 0x20 #define IOMMU_EXCLUSION_BASE_HIGH_OFFSET 0x24 @@ -325,7 +364,8 @@ #define IOMMU_EXT_FEATURE_HATS_MASK 0x00000C00 #define IOMMU_EXT_FEATURE_GATS_SHIFT 0x12 #define IOMMU_EXT_FEATURE_GATS_MASK 0x00003000 -#define IOMMU_EXT_FEATURE_GLXSUP 0x14 +#define IOMMU_EXT_FEATURE_GLXSUP_SHIFT 0x14 +#define IOMMU_EXT_FEATURE_GLXSUP_MASK 0x0000C000 #define IOMMU_EXT_FEATURE_PASMAX_SHIFT 0x0 #define IOMMU_EXT_FEATURE_PASMAX_MASK 0x0000001F @@ -342,6 +382,9 @@ #define IOMMU_STATUS_EVENT_LOG_RUN_SHIFT 3 #define IOMMU_STATUS_CMD_BUFFER_RUN_MASK 0x00000010 #define IOMMU_STATUS_CMD_BUFFER_RUN_SHIFT 4 +#define IOMMU_STATUS_PPR_LOG_OVERFLOW_SHIFT 5 +#define IOMMU_STATUS_PPR_LOG_INT_SHIFT 6 +#define IOMMU_STATUS_PPR_LOG_RUN_SHIFT 7 /* I/O Page Table */ #define IOMMU_PAGE_TABLE_ENTRY_SIZE 8 diff -r 522d544f4031 -r 6c104b46ef89 xen/include/asm-x86/hvm/svm/amd-iommu-proto.h --- a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h Thu Jan 12 13:50:50 2012 +0100 @@ -71,6 +71,8 @@ u32 *dte, u64 root_ptr, u16 domain_id, u8 paging_mode, u8 valid); void iommu_dte_set_iotlb(u32 *dte, u8 i); void iommu_dte_add_device_entry(u32 *dte, struct ivrs_mappings *ivrs_dev); +void iommu_dte_set_guest_cr3(u32 *dte, u16 dom_id, u64 gcr3, + int gv, unsigned int glx); /* send cmd to iommu */ void amd_iommu_flush_all_pages(struct domain *d); @@ -106,6 +108,14 @@ void amd_iommu_suspend(void); void amd_iommu_crash_shutdown(void); +/* guest iommu support */ +void amd_iommu_send_guest_cmd(struct amd_iommu *iommu, u32 cmd[]); +void guest_iommu_add_ppr_log(struct domain *d, u32 entry[]); +void guest_iommu_add_event_log(struct domain *d, u32 entry[]); +int guest_iommu_init(struct domain* d); +void guest_iommu_destroy(struct domain *d); +int guest_iommu_set_base(struct domain *d, uint64_t base); + static inline u32 get_field_from_reg_u32(u32 reg_value, u32 mask, u32 shift) { u32 field; diff -r 522d544f4031 -r 6c104b46ef89 xen/include/xen/hvm/iommu.h --- a/xen/include/xen/hvm/iommu.h Thu Jan 12 13:49:34 2012 +0100 +++ b/xen/include/xen/hvm/iommu.h Thu Jan 12 13:50:50 2012 +0100 @@ -47,6 +47,7 @@ int domain_id; int paging_mode; struct page_info *root_table; + struct guest_iommu *g_iommu; /* iommu_ops */ const struct iommu_ops *platform_ops; _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |