648
return PTE_LARGE_PAGE_SIZE(pte);
649
}
650
651
+/*
652
+ * Validate DTE fields and extract permissions and top level data required to
653
+ * initiate the page table walk.
654
+ *
655
+ * On success, returns 0 and stores:
656
+ * - top_level: highest page-table level encoded in DTE[Mode]
657
+ * - dte_perms: effective permissions from the DTE
658
+ *
659
+ * On failure, returns -AMDVI_FR_PT_ROOT_INV. This includes cases where:
660
+ * - DTE permissions disallow read AND write
661
+ * - DTE[Mode] is invalid for translation
662
+ * - IOVA exceeds the address width supported by DTE[Mode]
663
+ * In all such cases a page walk must be aborted.
664
+ */
665
+static uint64_t amdvi_get_top_pt_level_and_perms(hwaddr address, uint64_t dte,
666
+ uint8_t *top_level,
667
+ IOMMUAccessFlags *dte_perms)
668
+{
669
+ *dte_perms = amdvi_get_perms(dte);
670
+ if (*dte_perms == IOMMU_NONE) {
671
+ return -AMDVI_FR_PT_ROOT_INV;
672
+ }
673
+
674
+ /* Verifying a valid mode is encoded in DTE */
675
+ *top_level = get_pte_translation_mode(dte);
676
+
677
+ /*
678
+ * Page Table Root pointer is only valid for GPA->SPA translation on
679
+ * supported modes.
680
+ */
681
+ if (*top_level == 0 || *top_level > 6) {
682
+ return -AMDVI_FR_PT_ROOT_INV;
683
+ }
684
+
685
+ /*
686
+ * If IOVA is larger than the max supported by the highest pgtable level,
687
+ * there is nothing to do.
688
+ */
689
+ if (address > PT_LEVEL_MAX_ADDR(*top_level)) {
690
+ /* IOVA too large for the current DTE */
691
+ return -AMDVI_FR_PT_ROOT_INV;
692
+ }
693
+
694
+ return 0;
695
+}
696
+
697
/*
698
* Helper function to fetch a PTE using AMD v1 pgtable format.
699
* On successful page walk, returns 0 and pte parameter points to a valid PTE.
708
static uint64_t fetch_pte(AMDVIAddressSpace *as, hwaddr address, uint64_t dte,
709
uint64_t *pte, hwaddr *page_size)
710
{
665
- IOMMUAccessFlags perms = amdvi_get_perms(dte);
666
-
667
- uint8_t level, mode;
711
uint64_t pte_addr;
712
+ uint8_t pt_level, next_pt_level;
713
+ IOMMUAccessFlags perms;
714
+ int ret;
715
670
- *pte = dte;
716
*page_size = 0;
717
673
- if (perms == IOMMU_NONE) {
674
- return -AMDVI_FR_PT_ROOT_INV;
675
- }
676
-
718
/*
678
- * The Linux kernel driver initializes the default mode to 3, corresponding
679
- * to a 39-bit GPA space, where each entry in the pagetable translates to a
680
- * 1GB (2^30) page size.
719
+ * Verify the DTE is properly configured before page walk, and extract
720
+ * top pagetable level and permissions.
721
*/
682
- level = mode = get_pte_translation_mode(dte);
683
- assert(mode > 0 && mode < 7);
722
+ ret = amdvi_get_top_pt_level_and_perms(address, dte, &pt_level, &perms);
723
+ if (ret < 0) {
724
+ return ret;
725
+ }
726
727
/*
686
- * If IOVA is larger than the max supported by the current pgtable level,
687
- * there is nothing to do.
728
+ * Retrieve the top pagetable entry by following the DTE Page Table Root
729
+ * Pointer and indexing the top level table using the IOVA from the request.
730
*/
689
- if (address > PT_LEVEL_MAX_ADDR(mode - 1)) {
690
- /* IOVA too large for the current DTE */
731
+ pte_addr = NEXT_PTE_ADDR(dte, pt_level, address);
732
+ *pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn);
733
+
734
+ if (*pte == (uint64_t)-1) {
735
+ /*
736
+ * A returned PTE of -1 here indicates a failure to read the top level
737
+ * page table from guest memory. A page walk is not possible and page
738
+ * size must be returned as 0.
739
+ */
740
return -AMDVI_FR_PT_ROOT_INV;
741
}
742
694
- do {
695
- level -= 1;
743
+ /*
744
+ * Calculate page size for the top level page table entry.
745
+ * This ensures correct results for a single level Page Table setup.
746
+ */
747
+ *page_size = PTE_LEVEL_PAGE_SIZE(pt_level);
748
697
- /* Update the page_size */
698
- *page_size = PTE_LEVEL_PAGE_SIZE(level);
749
+ /*
750
+ * The root page table entry and its level have been determined. Begin the
751
+ * page walk.
752
+ */
753
+ while (pt_level > 0) {
754
755
/* Permission bits are ANDed at every level, including the DTE */
756
perms &= amdvi_get_perms(*pte);
763
return 0;
764
}
765
766
+ next_pt_level = PTE_NEXT_LEVEL(*pte);
767
+
768
/* Large or Leaf PTE found */
712
- if (PTE_NEXT_LEVEL(*pte) == 7 || PTE_NEXT_LEVEL(*pte) == 0) {
769
+ if (next_pt_level == 0 || next_pt_level == 7) {
770
/* Leaf PTE found */
771
break;
772
}
773
774
+ pt_level = next_pt_level;
775
+
776
/*
718
- * Index the pgtable using the IOVA bits corresponding to current level
719
- * and walk down to the lower level.
777
+ * The current entry is a Page Directory Entry. Descend to the lower
778
+ * page table level encoded in current pte, and index the new table
779
+ * using the appropriate IOVA bits to retrieve the new entry.
780
*/
721
- pte_addr = NEXT_PTE_ADDR(*pte, level, address);
781
+ *page_size = PTE_LEVEL_PAGE_SIZE(pt_level);
782
+
783
+ pte_addr = NEXT_PTE_ADDR(*pte, pt_level, address);
784
*pte = amdvi_get_pte_entry(as->iommu_state, pte_addr, as->devfn);
785
786
if (*pte == (uint64_t)-1) {
725
- /*
726
- * A returned PTE of -1 indicates a failure to read the page table
727
- * entry from guest memory.
728
- */
729
- if (level == mode - 1) {
730
- /* Failure to retrieve the Page Table from Root Pointer */
731
- *page_size = 0;
732
- return -AMDVI_FR_PT_ROOT_INV;
733
- } else {
734
- /* Failure to read PTE. Page walk skips a page_size chunk */
735
- return -AMDVI_FR_PT_ENTRY_INV;
736
- }
787
+ /* Failure to read PTE. Page walk skips a page_size chunk */
788
+ return -AMDVI_FR_PT_ENTRY_INV;
789
}
738
- } while (level > 0);
790
+ }
791
+
792
+ assert(PTE_NEXT_LEVEL(*pte) == 0 || PTE_NEXT_LEVEL(*pte) == 7);
793
740
- assert(PTE_NEXT_LEVEL(*pte) == 0 || PTE_NEXT_LEVEL(*pte) == 7 ||
741
- level == 0);
794
/*
795
* Page walk ends when Next Level field on PTE shows that either a leaf PTE
796
* or a series of large PTEs have been reached. In the latter case, even if