Hi David,
On 07.09.2015 17:46, Valentine Sinitsyn wrote:
...snip...
+/* TODO : Mark addresses as Accessed and Dirty */
+static void amd_iommu_do_translate(AMDIOMMUAddressSpace *as, hwaddr
addr, bool is_write, IOMMUTLBEntry *ret)
+{
+ AMDIOMMUState *s = as->iommu_state;
+
+ int present;
+ dma_addr_t pte_addr;
+ uint64_t entry[4], pte, pte_perms;
+ unsigned level;
+ unsigned perms;
+
+ if(!amd_iommu_get_dte(s, as->devfn, entry)){
+ goto no_translation;
+ }
+
+ pte = entry[0];
+
+ /*
+ * It's okay to check for either read or write permissions
+ * even for memory maps, since we don't support R/W maps.
+ */
+ perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ;
+
+ if(!(level = get_pte_translation_mode(pte))){
+ goto no_translation;
+ }
+
+ while(level > 0){
+ /*
+ * check permissions: the bitwise
+ * implication perms -> entry_perms must be true. Pages must
be present
+ * and permissions on all levels must be similar
+ */
+ pte_perms = amd_iommu_get_perms(pte);
+ present = pte & 1;
+ if(!present || perms != (perms & pte_perms)){
+ amd_iommu_page_fault(s, as->devfn, entry[1] &
DEV_DOMID_ID_MASK, addr, present,
+ !!(perms & IOMMU_PERM_WRITE));
+ return;
+ }
+
+ /* go to the next lower level */
+ pte_addr = pte & DEV_PT_ROOT_MASK;
+ pte_addr += ((addr >> ( 9 * level)) & 0xff8);
Does this work for six level page tables? The highest level has
different bit size there IIRC.
+ pte = ldq_phys(&address_space_memory, pte_addr);
+ level = (pte >> DEV_MODE_RSHIFT) & DEV_MODE_MASK;
+ }
+
+ ret->iova = addr & IOMMU_PAGE_MASK_4K;
+ ret->translated_addr = (pte & DEV_PT_ROOT_MASK) &
IOMMU_PAGE_MASK_4K;
+ ret->addr_mask = ~IOMMU_PAGE_MASK_4K;
+ ret->perm = IOMMU_RW;
+ return;
+
+no_translation:
+ ret->iova = addr;
+ ret->translated_addr = addr & IOMMU_PAGE_MASK_4K;
+ ret->addr_mask = ~IOMMU_PAGE_MASK_4K;
+ ret->perm = IOMMU_RW;
+ return;
Are you sure these transactions needs to be passed through rather than
target-aborted?
FYI: They are indeed passed-through; see Table 9 in the spec.
Valentine