From 5bce1b9711201abd906e3c41e0d4c38e536d016c Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Mon, 6 Jan 2014 14:18:12 +0800 Subject: [PATCH] iommu/vt-d, trivial: refine support of 64bit guest address In Intel IOMMU driver, it calculate page table level from adjusted guest address width as 'level = (agaw - 30) / 9', which assumes (agaw -30) could be divided by 9. On the other hand, 64bit is a valid agaw and (64 - 30) can't be divided by 9, so it needs special handling. This patch enhances Intel IOMMU driver to correctly handle 64bit agaw. It's mainly for code readability because there's no hardware supporting 64bit agaw yet. Signed-off-by: Jiang Liu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0cbf1dda0730..7bddb9b32da8 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -63,6 +63,7 @@ #define DEFAULT_DOMAIN_ADDRESS_WIDTH 48 #define MAX_AGAW_WIDTH 64 +#define MAX_AGAW_PFN_WIDTH (MAX_AGAW_WIDTH - VTD_PAGE_SHIFT) #define __DOMAIN_MAX_PFN(gaw) ((((uint64_t)1) << (gaw-VTD_PAGE_SHIFT)) - 1) #define __DOMAIN_MAX_ADDR(gaw) ((((uint64_t)1) << gaw) - 1) @@ -106,12 +107,12 @@ static inline int agaw_to_level(int agaw) static inline int agaw_to_width(int agaw) { - return 30 + agaw * LEVEL_STRIDE; + return min_t(int, 30 + agaw * LEVEL_STRIDE, MAX_AGAW_WIDTH); } static inline int width_to_agaw(int width) { - return (width - 30) / LEVEL_STRIDE; + return DIV_ROUND_UP(width - 30, LEVEL_STRIDE); } static inline unsigned int level_to_offset_bits(int level) @@ -141,7 +142,7 @@ static inline unsigned long align_to_level(unsigned long pfn, int level) static inline unsigned long lvl_to_nr_pages(unsigned int lvl) { - return 1 << ((lvl - 1) * LEVEL_STRIDE); + return 1 << min_t(int, (lvl - 1) * LEVEL_STRIDE, MAX_AGAW_PFN_WIDTH); } /* VT-d pages must always be _smaller_ than MM pages. Otherwise things @@ -865,7 +866,6 @@ static int dma_pte_clear_range(struct dmar_domain *domain, int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; unsigned int large_page = 1; struct dma_pte *first_pte, *pte; - int order; BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); @@ -890,8 +890,7 @@ static int dma_pte_clear_range(struct dmar_domain *domain, } while (start_pfn && start_pfn <= last_pfn); - order = (large_page - 1) * 9; - return order; + return min_t(int, (large_page - 1) * 9, MAX_AGAW_PFN_WIDTH); } static void dma_pte_free_level(struct dmar_domain *domain, int level, -- 2.39.5