aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kvm
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2013-03-04 21:43:17 -0500
committerChristoffer Dall <cdall@cs.columbia.edu>2013-03-06 18:59:20 -0500
commit000d399625b4b302935508f2fc9ce93ff1bd1ba4 (patch)
treef1df1043a1250219f1215bd4c9f7ae03d7fef576 /arch/arm/kvm
parent6190920a35068a621570cca7f07f3c4477615176 (diff)
ARM: KVM: sanitize freeing of HYP page tables
Instead of trying to free everything from PAGE_OFFSET to the top of memory, use the virt_addr_valid macro to check the upper limit. Also do the same for the vmalloc region where the IO mappings are allocated. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Christoffer Dall <cdall@cs.columbia.edu>
Diffstat (limited to 'arch/arm/kvm')
-rw-r--r--arch/arm/kvm/mmu.c44
1 files changed, 26 insertions, 18 deletions
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 0bf2c8551f75..2f12e4056408 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -85,34 +85,42 @@ static void free_ptes(pmd_t *pmd, unsigned long addr)
85 } 85 }
86} 86}
87 87
88static void free_hyp_pgd_entry(unsigned long addr)
89{
90 pgd_t *pgd;
91 pud_t *pud;
92 pmd_t *pmd;
93 unsigned long hyp_addr = KERN_TO_HYP(addr);
94
95 pgd = hyp_pgd + pgd_index(hyp_addr);
96 pud = pud_offset(pgd, hyp_addr);
97
98 if (pud_none(*pud))
99 return;
100 BUG_ON(pud_bad(*pud));
101
102 pmd = pmd_offset(pud, hyp_addr);
103 free_ptes(pmd, addr);
104 pmd_free(NULL, pmd);
105 pud_clear(pud);
106}
107
88/** 108/**
89 * free_hyp_pmds - free a Hyp-mode level-2 tables and child level-3 tables 109 * free_hyp_pmds - free a Hyp-mode level-2 tables and child level-3 tables
90 * 110 *
91 * Assumes this is a page table used strictly in Hyp-mode and therefore contains 111 * Assumes this is a page table used strictly in Hyp-mode and therefore contains
92 * only mappings in the kernel memory area, which is above PAGE_OFFSET. 112 * either mappings in the kernel memory area (above PAGE_OFFSET), or
113 * device mappings in the vmalloc range (from VMALLOC_START to VMALLOC_END).
93 */ 114 */
94void free_hyp_pmds(void) 115void free_hyp_pmds(void)
95{ 116{
96 pgd_t *pgd;
97 pud_t *pud;
98 pmd_t *pmd;
99 unsigned long addr; 117 unsigned long addr;
100 118
101 mutex_lock(&kvm_hyp_pgd_mutex); 119 mutex_lock(&kvm_hyp_pgd_mutex);
102 for (addr = PAGE_OFFSET; addr != 0; addr += PGDIR_SIZE) { 120 for (addr = PAGE_OFFSET; virt_addr_valid(addr); addr += PGDIR_SIZE)
103 unsigned long hyp_addr = KERN_TO_HYP(addr); 121 free_hyp_pgd_entry(addr);
104 pgd = hyp_pgd + pgd_index(hyp_addr); 122 for (addr = VMALLOC_START; is_vmalloc_addr((void*)addr); addr += PGDIR_SIZE)
105 pud = pud_offset(pgd, hyp_addr); 123 free_hyp_pgd_entry(addr);
106
107 if (pud_none(*pud))
108 continue;
109 BUG_ON(pud_bad(*pud));
110
111 pmd = pmd_offset(pud, hyp_addr);
112 free_ptes(pmd, addr);
113 pmd_free(NULL, pmd);
114 pud_clear(pud);
115 }
116 mutex_unlock(&kvm_hyp_pgd_mutex); 124 mutex_unlock(&kvm_hyp_pgd_mutex);
117} 125}
118 126