[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 03 of 16] amd iommu: Add iommu emulation for hvm guest
# HG changeset patch # User Wei Wang <wei.wang2@xxxxxxx> # Date 1324569377 -3600 # Node ID 07f338ae663242ba9080f1ab84298894783da3e2 # Parent e15194f68f99a64b65046ba3d29a3f06ccdca950 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 OS. If we want passthru ATS device to hvm guests using unmodified OS, we have to expose iommu functionality to HVM 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 diff -r e15194f68f99 -r 07f338ae6632 xen/drivers/passthrough/amd/Makefile --- a/xen/drivers/passthrough/amd/Makefile Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/drivers/passthrough/amd/Makefile Thu Dec 22 16:56:17 2011 +0100 @@ -5,3 +5,4 @@ obj-y += pci_amd_iommu.o obj-bin-y += iommu_acpi.init.o obj-y += iommu_intr.o obj-y += iommu_cmd.o +obj-y += iommu_guest.o diff -r e15194f68f99 -r 07f338ae6632 xen/drivers/passthrough/amd/iommu_cmd.c --- a/xen/drivers/passthrough/amd/iommu_cmd.c Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/drivers/passthrough/amd/iommu_cmd.c Thu Dec 22 16:56:17 2011 +0100 @@ -398,3 +398,15 @@ void amd_iommu_flush_all_caches(struct a 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 e15194f68f99 -r 07f338ae6632 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 Dec 22 16:56:17 2011 +0100 @@ -0,0 +1,915 @@ +/* + * 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 = val & 0xFFFFFFFF; \ + (reg)->hi = (val >> 32) & 0xFFFFFFFF; \ + } 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) +{ + uint64_t addr_lo, addr_hi, addr64; + + addr_lo = iommu_get_addr_lo_from_reg(base_raw & DMA_32BIT_MASK); + addr_hi = iommu_get_addr_hi_from_reg(base_raw >> 32); + addr64 = (addr_hi << 32) | (addr_lo << PAGE_SHIFT); + + 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!\n",__func__); + 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 guest_iommu *iommu; + + d = (struct domain*) _d; + 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 && iommu_en_old ) + { + 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 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 & 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 = %lx\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 & 7u) * 8; + + /* mmio access is always aligned on 8-byte boundary */ + mmio = offset & (~7u); + + 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 e15194f68f99 -r 07f338ae6632 xen/drivers/passthrough/amd/iommu_map.c --- a/xen/drivers/passthrough/amd/iommu_map.c Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/drivers/passthrough/amd/iommu_map.c Thu Dec 22 16:56:17 2011 +0100 @@ -234,6 +234,53 @@ void __init iommu_dte_add_device_entry(u 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 e15194f68f99 -r 07f338ae6632 xen/drivers/passthrough/amd/pci_amd_iommu.c --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c Thu Dec 22 16:56:17 2011 +0100 @@ -260,6 +260,8 @@ static int amd_iommu_domain_init(struct hd->domain_id = d->domain_id; + guest_iommu_init(d); + return 0; } @@ -443,6 +445,7 @@ static void deallocate_iommu_page_tables 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 e15194f68f99 -r 07f338ae6632 xen/include/asm-x86/amd-iommu.h --- a/xen/include/asm-x86/amd-iommu.h Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/include/asm-x86/amd-iommu.h Thu Dec 22 16:56:17 2011 +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)) @@ -130,4 +131,55 @@ struct ivrs_mappings *get_ivrs_mappings( 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 e15194f68f99 -r 07f338ae6632 xen/include/asm-x86/hvm/svm/amd-iommu-defs.h --- a/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h Thu Dec 22 16:56:17 2011 +0100 @@ -117,6 +117,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 @@ -127,6 +134,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 @@ -155,6 +164,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 @@ -164,7 +175,6 @@ #define IOMMU_DEV_TABLE_INT_CONTROL_MASK 0x30000000 #define IOMMU_DEV_TABLE_INT_CONTROL_SHIFT 28 - /* Command Buffer */ #define IOMMU_CMD_BUFFER_BASE_LOW_OFFSET 0x08 #define IOMMU_CMD_BUFFER_BASE_HIGH_OFFSET 0x0C @@ -192,6 +202,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 */ @@ -282,6 +293,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 @@ -309,6 +342,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 @@ -342,7 +380,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 @@ -359,6 +398,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 e15194f68f99 -r 07f338ae6632 xen/include/asm-x86/hvm/svm/amd-iommu-proto.h --- a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h Thu Dec 22 16:56:17 2011 +0100 @@ -71,6 +71,8 @@ void amd_iommu_set_root_page_table( 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_resume(void); 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 e15194f68f99 -r 07f338ae6632 xen/include/xen/hvm/iommu.h --- a/xen/include/xen/hvm/iommu.h Thu Dec 22 16:56:14 2011 +0100 +++ b/xen/include/xen/hvm/iommu.h Thu Dec 22 16:56:17 2011 +0100 @@ -47,6 +47,7 @@ struct hvm_iommu { 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-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |