xref: /openbmc/linux/arch/um/kernel/tlb.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
10d1fb0a4SAlex Dewar // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
3ba180fd4SJeff Dike  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
68192ab42SJeff Dike #include <linux/mm.h>
773395a00SAl Viro #include <linux/module.h>
83f07c014SIngo Molnar #include <linux/sched/signal.h>
93f07c014SIngo Molnar 
108192ab42SJeff Dike #include <asm/tlbflush.h>
1137185b33SAl Viro #include <as-layout.h>
1237185b33SAl Viro #include <mem_user.h>
1337185b33SAl Viro #include <os.h>
1437185b33SAl Viro #include <skas.h>
15468f6597SRichard Weinberger #include <kern_util.h>
161da177e4SLinus Torvalds 
171466abf2SJeff Dike struct host_vm_change {
181466abf2SJeff Dike 	struct host_vm_op {
191466abf2SJeff Dike 		enum { NONE, MMAP, MUNMAP, MPROTECT } type;
201466abf2SJeff Dike 		union {
211466abf2SJeff Dike 			struct {
221466abf2SJeff Dike 				unsigned long addr;
231466abf2SJeff Dike 				unsigned long len;
241466abf2SJeff Dike 				unsigned int prot;
251466abf2SJeff Dike 				int fd;
261466abf2SJeff Dike 				__u64 offset;
271466abf2SJeff Dike 			} mmap;
281466abf2SJeff Dike 			struct {
291466abf2SJeff Dike 				unsigned long addr;
301466abf2SJeff Dike 				unsigned long len;
311466abf2SJeff Dike 			} munmap;
321466abf2SJeff Dike 			struct {
331466abf2SJeff Dike 				unsigned long addr;
341466abf2SJeff Dike 				unsigned long len;
351466abf2SJeff Dike 				unsigned int prot;
361466abf2SJeff Dike 			} mprotect;
371466abf2SJeff Dike 		} u;
381466abf2SJeff Dike 	} ops[1];
39a9c52c2aSAnton Ivanov 	int userspace;
401466abf2SJeff Dike 	int index;
41a9c52c2aSAnton Ivanov 	struct mm_struct *mm;
421466abf2SJeff Dike 	void *data;
431466abf2SJeff Dike 	int force;
441466abf2SJeff Dike };
451466abf2SJeff Dike 
46a9c52c2aSAnton Ivanov #define INIT_HVC(mm, force, userspace) \
471466abf2SJeff Dike 	((struct host_vm_change) \
481466abf2SJeff Dike 	 { .ops		= { { .type = NONE } },	\
49a9c52c2aSAnton Ivanov 	   .mm		= mm, \
501466abf2SJeff Dike        	   .data	= NULL, \
51a9c52c2aSAnton Ivanov 	   .userspace	= userspace, \
521466abf2SJeff Dike 	   .index	= 0, \
531466abf2SJeff Dike 	   .force	= force })
541466abf2SJeff Dike 
report_enomem(void)5570c8205fSRichard Weinberger static void report_enomem(void)
5670c8205fSRichard Weinberger {
5770c8205fSRichard Weinberger 	printk(KERN_ERR "UML ran out of memory on the host side! "
5870c8205fSRichard Weinberger 			"This can happen due to a memory limitation or "
5970c8205fSRichard Weinberger 			"vm.max_map_count has been reached.\n");
6070c8205fSRichard Weinberger }
6170c8205fSRichard Weinberger 
do_ops(struct host_vm_change * hvc,int end,int finished)621466abf2SJeff Dike static int do_ops(struct host_vm_change *hvc, int end,
631466abf2SJeff Dike 		  int finished)
641466abf2SJeff Dike {
651466abf2SJeff Dike 	struct host_vm_op *op;
661466abf2SJeff Dike 	int i, ret = 0;
671466abf2SJeff Dike 
681466abf2SJeff Dike 	for (i = 0; i < end && !ret; i++) {
691466abf2SJeff Dike 		op = &hvc->ops[i];
701466abf2SJeff Dike 		switch (op->type) {
711466abf2SJeff Dike 		case MMAP:
72a9c52c2aSAnton Ivanov 			if (hvc->userspace)
73a9c52c2aSAnton Ivanov 				ret = map(&hvc->mm->context.id, op->u.mmap.addr,
74a9c52c2aSAnton Ivanov 					  op->u.mmap.len, op->u.mmap.prot,
75a9c52c2aSAnton Ivanov 					  op->u.mmap.fd,
76a9c52c2aSAnton Ivanov 					  op->u.mmap.offset, finished,
77a9c52c2aSAnton Ivanov 					  &hvc->data);
78a9c52c2aSAnton Ivanov 			else
79a9c52c2aSAnton Ivanov 				map_memory(op->u.mmap.addr, op->u.mmap.offset,
80a9c52c2aSAnton Ivanov 					   op->u.mmap.len, 1, 1, 1);
811466abf2SJeff Dike 			break;
821466abf2SJeff Dike 		case MUNMAP:
83a9c52c2aSAnton Ivanov 			if (hvc->userspace)
84a9c52c2aSAnton Ivanov 				ret = unmap(&hvc->mm->context.id,
85a9c52c2aSAnton Ivanov 					    op->u.munmap.addr,
86a9c52c2aSAnton Ivanov 					    op->u.munmap.len, finished,
87a9c52c2aSAnton Ivanov 					    &hvc->data);
88a9c52c2aSAnton Ivanov 			else
89a9c52c2aSAnton Ivanov 				ret = os_unmap_memory(
90a9c52c2aSAnton Ivanov 					(void *) op->u.munmap.addr,
91a9c52c2aSAnton Ivanov 						      op->u.munmap.len);
92a9c52c2aSAnton Ivanov 
931466abf2SJeff Dike 			break;
941466abf2SJeff Dike 		case MPROTECT:
95a9c52c2aSAnton Ivanov 			if (hvc->userspace)
96a9c52c2aSAnton Ivanov 				ret = protect(&hvc->mm->context.id,
97a9c52c2aSAnton Ivanov 					      op->u.mprotect.addr,
98a9c52c2aSAnton Ivanov 					      op->u.mprotect.len,
99a9c52c2aSAnton Ivanov 					      op->u.mprotect.prot,
1001466abf2SJeff Dike 					      finished, &hvc->data);
101a9c52c2aSAnton Ivanov 			else
102a9c52c2aSAnton Ivanov 				ret = os_protect_memory(
103a9c52c2aSAnton Ivanov 					(void *) op->u.mprotect.addr,
104a9c52c2aSAnton Ivanov 							op->u.mprotect.len,
105a9c52c2aSAnton Ivanov 							1, 1, 1);
1061466abf2SJeff Dike 			break;
1071466abf2SJeff Dike 		default:
1081466abf2SJeff Dike 			printk(KERN_ERR "Unknown op type %d in do_ops\n",
1091466abf2SJeff Dike 			       op->type);
11062179d47SRichard Weinberger 			BUG();
1111466abf2SJeff Dike 			break;
1121466abf2SJeff Dike 		}
1131466abf2SJeff Dike 	}
1141466abf2SJeff Dike 
11570c8205fSRichard Weinberger 	if (ret == -ENOMEM)
11670c8205fSRichard Weinberger 		report_enomem();
11770c8205fSRichard Weinberger 
1181466abf2SJeff Dike 	return ret;
1191466abf2SJeff Dike }
1201466abf2SJeff Dike 
add_mmap(unsigned long virt,unsigned long phys,unsigned long len,unsigned int prot,struct host_vm_change * hvc)121c5600490SJeff Dike static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len,
1221466abf2SJeff Dike 		    unsigned int prot, struct host_vm_change *hvc)
123c5600490SJeff Dike {
124c5600490SJeff Dike 	__u64 offset;
125c5600490SJeff Dike 	struct host_vm_op *last;
126a9c52c2aSAnton Ivanov 	int fd = -1, ret = 0;
127c5600490SJeff Dike 
128a9c52c2aSAnton Ivanov 	if (hvc->userspace)
129c5600490SJeff Dike 		fd = phys_mapping(phys, &offset);
130a9c52c2aSAnton Ivanov 	else
131a9c52c2aSAnton Ivanov 		offset = phys;
1321466abf2SJeff Dike 	if (hvc->index != 0) {
1331466abf2SJeff Dike 		last = &hvc->ops[hvc->index - 1];
134c5600490SJeff Dike 		if ((last->type == MMAP) &&
135c5600490SJeff Dike 		   (last->u.mmap.addr + last->u.mmap.len == virt) &&
13616dd07bcSJeff Dike 		   (last->u.mmap.prot == prot) && (last->u.mmap.fd == fd) &&
137c5600490SJeff Dike 		   (last->u.mmap.offset + last->u.mmap.len == offset)) {
138c5600490SJeff Dike 			last->u.mmap.len += len;
13907bf731eSBodo Stroesser 			return 0;
140c5600490SJeff Dike 		}
141c5600490SJeff Dike 	}
142c5600490SJeff Dike 
1431466abf2SJeff Dike 	if (hvc->index == ARRAY_SIZE(hvc->ops)) {
1441466abf2SJeff Dike 		ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0);
1451466abf2SJeff Dike 		hvc->index = 0;
146c5600490SJeff Dike 	}
147c5600490SJeff Dike 
1481466abf2SJeff Dike 	hvc->ops[hvc->index++] = ((struct host_vm_op)
1491466abf2SJeff Dike 				  { .type	= MMAP,
1501466abf2SJeff Dike 				    .u = { .mmap = { .addr	= virt,
151c5600490SJeff Dike 						     .len	= len,
15216dd07bcSJeff Dike 						     .prot	= prot,
153c5600490SJeff Dike 						     .fd	= fd,
154c5600490SJeff Dike 						     .offset	= offset }
155c5600490SJeff Dike 			   } });
15607bf731eSBodo Stroesser 	return ret;
157c5600490SJeff Dike }
158c5600490SJeff Dike 
add_munmap(unsigned long addr,unsigned long len,struct host_vm_change * hvc)159c5600490SJeff Dike static int add_munmap(unsigned long addr, unsigned long len,
1601466abf2SJeff Dike 		      struct host_vm_change *hvc)
161c5600490SJeff Dike {
162c5600490SJeff Dike 	struct host_vm_op *last;
16307bf731eSBodo Stroesser 	int ret = 0;
164c5600490SJeff Dike 
1651466abf2SJeff Dike 	if (hvc->index != 0) {
1661466abf2SJeff Dike 		last = &hvc->ops[hvc->index - 1];
167c5600490SJeff Dike 		if ((last->type == MUNMAP) &&
168c5600490SJeff Dike 		   (last->u.munmap.addr + last->u.mmap.len == addr)) {
169c5600490SJeff Dike 			last->u.munmap.len += len;
17007bf731eSBodo Stroesser 			return 0;
171c5600490SJeff Dike 		}
172c5600490SJeff Dike 	}
173c5600490SJeff Dike 
1741466abf2SJeff Dike 	if (hvc->index == ARRAY_SIZE(hvc->ops)) {
1751466abf2SJeff Dike 		ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0);
1761466abf2SJeff Dike 		hvc->index = 0;
177c5600490SJeff Dike 	}
178c5600490SJeff Dike 
1791466abf2SJeff Dike 	hvc->ops[hvc->index++] = ((struct host_vm_op)
1801466abf2SJeff Dike 				  { .type	= MUNMAP,
1811466abf2SJeff Dike 			     	    .u = { .munmap = { .addr	= addr,
182c5600490SJeff Dike 						       .len	= len } } });
18307bf731eSBodo Stroesser 	return ret;
184c5600490SJeff Dike }
185c5600490SJeff Dike 
add_mprotect(unsigned long addr,unsigned long len,unsigned int prot,struct host_vm_change * hvc)18616dd07bcSJeff Dike static int add_mprotect(unsigned long addr, unsigned long len,
1871466abf2SJeff Dike 			unsigned int prot, struct host_vm_change *hvc)
188c5600490SJeff Dike {
189c5600490SJeff Dike 	struct host_vm_op *last;
19007bf731eSBodo Stroesser 	int ret = 0;
191c5600490SJeff Dike 
1921466abf2SJeff Dike 	if (hvc->index != 0) {
1931466abf2SJeff Dike 		last = &hvc->ops[hvc->index - 1];
194c5600490SJeff Dike 		if ((last->type == MPROTECT) &&
195c5600490SJeff Dike 		   (last->u.mprotect.addr + last->u.mprotect.len == addr) &&
19616dd07bcSJeff Dike 		   (last->u.mprotect.prot == prot)) {
197c5600490SJeff Dike 			last->u.mprotect.len += len;
19807bf731eSBodo Stroesser 			return 0;
199c5600490SJeff Dike 		}
200c5600490SJeff Dike 	}
201c5600490SJeff Dike 
2021466abf2SJeff Dike 	if (hvc->index == ARRAY_SIZE(hvc->ops)) {
2031466abf2SJeff Dike 		ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0);
2041466abf2SJeff Dike 		hvc->index = 0;
205c5600490SJeff Dike 	}
206c5600490SJeff Dike 
2071466abf2SJeff Dike 	hvc->ops[hvc->index++] = ((struct host_vm_op)
2081466abf2SJeff Dike 				  { .type	= MPROTECT,
2091466abf2SJeff Dike 			     	    .u = { .mprotect = { .addr	= addr,
210c5600490SJeff Dike 							 .len	= len,
21116dd07bcSJeff Dike 							 .prot	= prot } } });
21207bf731eSBodo Stroesser 	return ret;
213c5600490SJeff Dike }
214c5600490SJeff Dike 
2151da177e4SLinus Torvalds #define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1))
2161da177e4SLinus Torvalds 
update_pte_range(pmd_t * pmd,unsigned long addr,unsigned long end,struct host_vm_change * hvc)2177f0536f8SJeff Dike static inline int update_pte_range(pmd_t *pmd, unsigned long addr,
2181466abf2SJeff Dike 				   unsigned long end,
2191466abf2SJeff Dike 				   struct host_vm_change *hvc)
2207f0536f8SJeff Dike {
2217f0536f8SJeff Dike 	pte_t *pte;
22216dd07bcSJeff Dike 	int r, w, x, prot, ret = 0;
2237f0536f8SJeff Dike 
2247f0536f8SJeff Dike 	pte = pte_offset_kernel(pmd, addr);
2257f0536f8SJeff Dike 	do {
2267f0536f8SJeff Dike 		r = pte_read(*pte);
2277f0536f8SJeff Dike 		w = pte_write(*pte);
2287f0536f8SJeff Dike 		x = pte_exec(*pte);
2297f0536f8SJeff Dike 		if (!pte_young(*pte)) {
2307f0536f8SJeff Dike 			r = 0;
2317f0536f8SJeff Dike 			w = 0;
2320b4e273fSJeff Dike 		} else if (!pte_dirty(*pte))
2337f0536f8SJeff Dike 			w = 0;
2340b4e273fSJeff Dike 
23516dd07bcSJeff Dike 		prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) |
23616dd07bcSJeff Dike 			(x ? UM_PROT_EXEC : 0));
2371466abf2SJeff Dike 		if (hvc->force || pte_newpage(*pte)) {
238742f3c81SAnton Ivanov 			if (pte_present(*pte)) {
239742f3c81SAnton Ivanov 				if (pte_newpage(*pte))
2407f0536f8SJeff Dike 					ret = add_mmap(addr, pte_val(*pte) & PAGE_MASK,
2411466abf2SJeff Dike 						       PAGE_SIZE, prot, hvc);
242742f3c81SAnton Ivanov 			} else
2430b4e273fSJeff Dike 				ret = add_munmap(addr, PAGE_SIZE, hvc);
2440b4e273fSJeff Dike 		} else if (pte_newprot(*pte))
2451466abf2SJeff Dike 			ret = add_mprotect(addr, PAGE_SIZE, prot, hvc);
2467f0536f8SJeff Dike 		*pte = pte_mkuptodate(*pte);
247909e90d3SJeff Dike 	} while (pte++, addr += PAGE_SIZE, ((addr < end) && !ret));
2487f0536f8SJeff Dike 	return ret;
2497f0536f8SJeff Dike }
2507f0536f8SJeff Dike 
update_pmd_range(pud_t * pud,unsigned long addr,unsigned long end,struct host_vm_change * hvc)2517f0536f8SJeff Dike static inline int update_pmd_range(pud_t *pud, unsigned long addr,
2521466abf2SJeff Dike 				   unsigned long end,
2531466abf2SJeff Dike 				   struct host_vm_change *hvc)
2547f0536f8SJeff Dike {
2557f0536f8SJeff Dike 	pmd_t *pmd;
2567f0536f8SJeff Dike 	unsigned long next;
2577f0536f8SJeff Dike 	int ret = 0;
2587f0536f8SJeff Dike 
2597f0536f8SJeff Dike 	pmd = pmd_offset(pud, addr);
2607f0536f8SJeff Dike 	do {
2617f0536f8SJeff Dike 		next = pmd_addr_end(addr, end);
2627f0536f8SJeff Dike 		if (!pmd_present(*pmd)) {
2631466abf2SJeff Dike 			if (hvc->force || pmd_newpage(*pmd)) {
2641466abf2SJeff Dike 				ret = add_munmap(addr, next - addr, hvc);
2657f0536f8SJeff Dike 				pmd_mkuptodate(*pmd);
2667f0536f8SJeff Dike 			}
2677f0536f8SJeff Dike 		}
2681466abf2SJeff Dike 		else ret = update_pte_range(pmd, addr, next, hvc);
269909e90d3SJeff Dike 	} while (pmd++, addr = next, ((addr < end) && !ret));
2707f0536f8SJeff Dike 	return ret;
2717f0536f8SJeff Dike }
2727f0536f8SJeff Dike 
update_pud_range(p4d_t * p4d,unsigned long addr,unsigned long end,struct host_vm_change * hvc)273e19f97edSMike Rapoport static inline int update_pud_range(p4d_t *p4d, unsigned long addr,
2741466abf2SJeff Dike 				   unsigned long end,
2751466abf2SJeff Dike 				   struct host_vm_change *hvc)
2767f0536f8SJeff Dike {
2777f0536f8SJeff Dike 	pud_t *pud;
2787f0536f8SJeff Dike 	unsigned long next;
2797f0536f8SJeff Dike 	int ret = 0;
2807f0536f8SJeff Dike 
281e19f97edSMike Rapoport 	pud = pud_offset(p4d, addr);
2827f0536f8SJeff Dike 	do {
2837f0536f8SJeff Dike 		next = pud_addr_end(addr, end);
2847f0536f8SJeff Dike 		if (!pud_present(*pud)) {
2851466abf2SJeff Dike 			if (hvc->force || pud_newpage(*pud)) {
2861466abf2SJeff Dike 				ret = add_munmap(addr, next - addr, hvc);
2877f0536f8SJeff Dike 				pud_mkuptodate(*pud);
2887f0536f8SJeff Dike 			}
2897f0536f8SJeff Dike 		}
2901466abf2SJeff Dike 		else ret = update_pmd_range(pud, addr, next, hvc);
291909e90d3SJeff Dike 	} while (pud++, addr = next, ((addr < end) && !ret));
2927f0536f8SJeff Dike 	return ret;
2937f0536f8SJeff Dike }
2947f0536f8SJeff Dike 
update_p4d_range(pgd_t * pgd,unsigned long addr,unsigned long end,struct host_vm_change * hvc)295e19f97edSMike Rapoport static inline int update_p4d_range(pgd_t *pgd, unsigned long addr,
296e19f97edSMike Rapoport 				   unsigned long end,
297e19f97edSMike Rapoport 				   struct host_vm_change *hvc)
298e19f97edSMike Rapoport {
299e19f97edSMike Rapoport 	p4d_t *p4d;
300e19f97edSMike Rapoport 	unsigned long next;
301e19f97edSMike Rapoport 	int ret = 0;
302e19f97edSMike Rapoport 
303e19f97edSMike Rapoport 	p4d = p4d_offset(pgd, addr);
304e19f97edSMike Rapoport 	do {
305e19f97edSMike Rapoport 		next = p4d_addr_end(addr, end);
306e19f97edSMike Rapoport 		if (!p4d_present(*p4d)) {
307e19f97edSMike Rapoport 			if (hvc->force || p4d_newpage(*p4d)) {
308e19f97edSMike Rapoport 				ret = add_munmap(addr, next - addr, hvc);
309e19f97edSMike Rapoport 				p4d_mkuptodate(*p4d);
310e19f97edSMike Rapoport 			}
311e19f97edSMike Rapoport 		} else
312e19f97edSMike Rapoport 			ret = update_pud_range(p4d, addr, next, hvc);
313e19f97edSMike Rapoport 	} while (p4d++, addr = next, ((addr < end) && !ret));
314e19f97edSMike Rapoport 	return ret;
315e19f97edSMike Rapoport }
316e19f97edSMike Rapoport 
fix_range_common(struct mm_struct * mm,unsigned long start_addr,unsigned long end_addr,int force)317*d5dbcfe7SBenjamin Berg static void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
3181466abf2SJeff Dike 			     unsigned long end_addr, int force)
3191da177e4SLinus Torvalds {
3207f0536f8SJeff Dike 	pgd_t *pgd;
3211466abf2SJeff Dike 	struct host_vm_change hvc;
3227f0536f8SJeff Dike 	unsigned long addr = start_addr, next;
323a9c52c2aSAnton Ivanov 	int ret = 0, userspace = 1;
3241da177e4SLinus Torvalds 
325a9c52c2aSAnton Ivanov 	hvc = INIT_HVC(mm, force, userspace);
3267f0536f8SJeff Dike 	pgd = pgd_offset(mm, addr);
3277f0536f8SJeff Dike 	do {
3287f0536f8SJeff Dike 		next = pgd_addr_end(addr, end_addr);
3297f0536f8SJeff Dike 		if (!pgd_present(*pgd)) {
3307f0536f8SJeff Dike 			if (force || pgd_newpage(*pgd)) {
3311466abf2SJeff Dike 				ret = add_munmap(addr, next - addr, &hvc);
3327f0536f8SJeff Dike 				pgd_mkuptodate(*pgd);
3331da177e4SLinus Torvalds 			}
334e19f97edSMike Rapoport 		} else
335e19f97edSMike Rapoport 			ret = update_p4d_range(pgd, addr, next, &hvc);
336909e90d3SJeff Dike 	} while (pgd++, addr = next, ((addr < end_addr) && !ret));
3371da177e4SLinus Torvalds 
33807bf731eSBodo Stroesser 	if (!ret)
3391466abf2SJeff Dike 		ret = do_ops(&hvc, hvc.index, 1);
34007bf731eSBodo Stroesser 
34107bf731eSBodo Stroesser 	/* This is not an else because ret is modified above */
34207bf731eSBodo Stroesser 	if (ret) {
343a7d48886SJohannes Berg 		struct mm_id *mm_idp = &current->mm->context.id;
344a7d48886SJohannes Berg 
345ba180fd4SJeff Dike 		printk(KERN_ERR "fix_range_common: failed, killing current "
346468f6597SRichard Weinberger 		       "process: %d\n", task_tgid_vnr(current));
347a7d48886SJohannes Berg 		mm_idp->kill = 1;
34807bf731eSBodo Stroesser 	}
3491da177e4SLinus Torvalds }
3501da177e4SLinus Torvalds 
flush_tlb_kernel_range_common(unsigned long start,unsigned long end)351c75d053bSAl Viro static int flush_tlb_kernel_range_common(unsigned long start, unsigned long end)
3521da177e4SLinus Torvalds {
3531da177e4SLinus Torvalds 	struct mm_struct *mm;
3541da177e4SLinus Torvalds 	pgd_t *pgd;
355e19f97edSMike Rapoport 	p4d_t *p4d;
3561da177e4SLinus Torvalds 	pud_t *pud;
3571da177e4SLinus Torvalds 	pmd_t *pmd;
3581da177e4SLinus Torvalds 	pte_t *pte;
3591da177e4SLinus Torvalds 	unsigned long addr, last;
360a9c52c2aSAnton Ivanov 	int updated = 0, err = 0, force = 0, userspace = 0;
361a9c52c2aSAnton Ivanov 	struct host_vm_change hvc;
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 	mm = &init_mm;
364a9c52c2aSAnton Ivanov 	hvc = INIT_HVC(mm, force, userspace);
3651da177e4SLinus Torvalds 	for (addr = start; addr < end;) {
3661da177e4SLinus Torvalds 		pgd = pgd_offset(mm, addr);
3671da177e4SLinus Torvalds 		if (!pgd_present(*pgd)) {
3681da177e4SLinus Torvalds 			last = ADD_ROUND(addr, PGDIR_SIZE);
3691da177e4SLinus Torvalds 			if (last > end)
3701da177e4SLinus Torvalds 				last = end;
3711da177e4SLinus Torvalds 			if (pgd_newpage(*pgd)) {
3721da177e4SLinus Torvalds 				updated = 1;
373a9c52c2aSAnton Ivanov 				err = add_munmap(addr, last - addr, &hvc);
3741da177e4SLinus Torvalds 				if (err < 0)
3751da177e4SLinus Torvalds 					panic("munmap failed, errno = %d\n",
3761da177e4SLinus Torvalds 					      -err);
3771da177e4SLinus Torvalds 			}
3781da177e4SLinus Torvalds 			addr = last;
3791da177e4SLinus Torvalds 			continue;
3801da177e4SLinus Torvalds 		}
3811da177e4SLinus Torvalds 
382e19f97edSMike Rapoport 		p4d = p4d_offset(pgd, addr);
383e19f97edSMike Rapoport 		if (!p4d_present(*p4d)) {
384e19f97edSMike Rapoport 			last = ADD_ROUND(addr, P4D_SIZE);
385e19f97edSMike Rapoport 			if (last > end)
386e19f97edSMike Rapoport 				last = end;
387e19f97edSMike Rapoport 			if (p4d_newpage(*p4d)) {
388e19f97edSMike Rapoport 				updated = 1;
389e19f97edSMike Rapoport 				err = add_munmap(addr, last - addr, &hvc);
390e19f97edSMike Rapoport 				if (err < 0)
391e19f97edSMike Rapoport 					panic("munmap failed, errno = %d\n",
392e19f97edSMike Rapoport 					      -err);
393e19f97edSMike Rapoport 			}
394e19f97edSMike Rapoport 			addr = last;
395e19f97edSMike Rapoport 			continue;
396e19f97edSMike Rapoport 		}
397e19f97edSMike Rapoport 
398e19f97edSMike Rapoport 		pud = pud_offset(p4d, addr);
3991da177e4SLinus Torvalds 		if (!pud_present(*pud)) {
4001da177e4SLinus Torvalds 			last = ADD_ROUND(addr, PUD_SIZE);
4011da177e4SLinus Torvalds 			if (last > end)
4021da177e4SLinus Torvalds 				last = end;
4031da177e4SLinus Torvalds 			if (pud_newpage(*pud)) {
4041da177e4SLinus Torvalds 				updated = 1;
405a9c52c2aSAnton Ivanov 				err = add_munmap(addr, last - addr, &hvc);
4061da177e4SLinus Torvalds 				if (err < 0)
4071da177e4SLinus Torvalds 					panic("munmap failed, errno = %d\n",
4081da177e4SLinus Torvalds 					      -err);
4091da177e4SLinus Torvalds 			}
4101da177e4SLinus Torvalds 			addr = last;
4111da177e4SLinus Torvalds 			continue;
4121da177e4SLinus Torvalds 		}
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 		pmd = pmd_offset(pud, addr);
4151da177e4SLinus Torvalds 		if (!pmd_present(*pmd)) {
4161da177e4SLinus Torvalds 			last = ADD_ROUND(addr, PMD_SIZE);
4171da177e4SLinus Torvalds 			if (last > end)
4181da177e4SLinus Torvalds 				last = end;
4191da177e4SLinus Torvalds 			if (pmd_newpage(*pmd)) {
4201da177e4SLinus Torvalds 				updated = 1;
421a9c52c2aSAnton Ivanov 				err = add_munmap(addr, last - addr, &hvc);
4221da177e4SLinus Torvalds 				if (err < 0)
4231da177e4SLinus Torvalds 					panic("munmap failed, errno = %d\n",
4241da177e4SLinus Torvalds 					      -err);
4251da177e4SLinus Torvalds 			}
4261da177e4SLinus Torvalds 			addr = last;
4271da177e4SLinus Torvalds 			continue;
4281da177e4SLinus Torvalds 		}
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 		pte = pte_offset_kernel(pmd, addr);
4311da177e4SLinus Torvalds 		if (!pte_present(*pte) || pte_newpage(*pte)) {
4321da177e4SLinus Torvalds 			updated = 1;
433a9c52c2aSAnton Ivanov 			err = add_munmap(addr, PAGE_SIZE, &hvc);
4341da177e4SLinus Torvalds 			if (err < 0)
4351da177e4SLinus Torvalds 				panic("munmap failed, errno = %d\n",
4361da177e4SLinus Torvalds 				      -err);
4371da177e4SLinus Torvalds 			if (pte_present(*pte))
438a9c52c2aSAnton Ivanov 				err = add_mmap(addr, pte_val(*pte) & PAGE_MASK,
439a9c52c2aSAnton Ivanov 					       PAGE_SIZE, 0, &hvc);
4401da177e4SLinus Torvalds 		}
4411da177e4SLinus Torvalds 		else if (pte_newprot(*pte)) {
4421da177e4SLinus Torvalds 			updated = 1;
443a9c52c2aSAnton Ivanov 			err = add_mprotect(addr, PAGE_SIZE, 0, &hvc);
4441da177e4SLinus Torvalds 		}
4451da177e4SLinus Torvalds 		addr += PAGE_SIZE;
4461da177e4SLinus Torvalds 	}
447a9c52c2aSAnton Ivanov 	if (!err)
448a9c52c2aSAnton Ivanov 		err = do_ops(&hvc, hvc.index, 1);
449a9c52c2aSAnton Ivanov 
450a9c52c2aSAnton Ivanov 	if (err < 0)
451a9c52c2aSAnton Ivanov 		panic("flush_tlb_kernel failed, errno = %d\n", err);
452ba180fd4SJeff Dike 	return updated;
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds 
flush_tlb_page(struct vm_area_struct * vma,unsigned long address)45577bf4400SJeff Dike void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
45677bf4400SJeff Dike {
45777bf4400SJeff Dike 	pgd_t *pgd;
458e19f97edSMike Rapoport 	p4d_t *p4d;
45977bf4400SJeff Dike 	pud_t *pud;
46077bf4400SJeff Dike 	pmd_t *pmd;
46177bf4400SJeff Dike 	pte_t *pte;
46277bf4400SJeff Dike 	struct mm_struct *mm = vma->vm_mm;
46377bf4400SJeff Dike 	void *flush = NULL;
46477bf4400SJeff Dike 	int r, w, x, prot, err = 0;
46577bf4400SJeff Dike 	struct mm_id *mm_id;
46677bf4400SJeff Dike 
46777bf4400SJeff Dike 	address &= PAGE_MASK;
46847da2976SJohannes Berg 
46977bf4400SJeff Dike 	pgd = pgd_offset(mm, address);
47077bf4400SJeff Dike 	if (!pgd_present(*pgd))
47177bf4400SJeff Dike 		goto kill;
47277bf4400SJeff Dike 
473e19f97edSMike Rapoport 	p4d = p4d_offset(pgd, address);
474e19f97edSMike Rapoport 	if (!p4d_present(*p4d))
475e19f97edSMike Rapoport 		goto kill;
476e19f97edSMike Rapoport 
477e19f97edSMike Rapoport 	pud = pud_offset(p4d, address);
47877bf4400SJeff Dike 	if (!pud_present(*pud))
47977bf4400SJeff Dike 		goto kill;
48077bf4400SJeff Dike 
48177bf4400SJeff Dike 	pmd = pmd_offset(pud, address);
48277bf4400SJeff Dike 	if (!pmd_present(*pmd))
48377bf4400SJeff Dike 		goto kill;
48477bf4400SJeff Dike 
48577bf4400SJeff Dike 	pte = pte_offset_kernel(pmd, address);
48677bf4400SJeff Dike 
48777bf4400SJeff Dike 	r = pte_read(*pte);
48877bf4400SJeff Dike 	w = pte_write(*pte);
48977bf4400SJeff Dike 	x = pte_exec(*pte);
49077bf4400SJeff Dike 	if (!pte_young(*pte)) {
49177bf4400SJeff Dike 		r = 0;
49277bf4400SJeff Dike 		w = 0;
49377bf4400SJeff Dike 	} else if (!pte_dirty(*pte)) {
49477bf4400SJeff Dike 		w = 0;
49577bf4400SJeff Dike 	}
49677bf4400SJeff Dike 
4976c738ffaSJeff Dike 	mm_id = &mm->context.id;
49877bf4400SJeff Dike 	prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) |
49977bf4400SJeff Dike 		(x ? UM_PROT_EXEC : 0));
50077bf4400SJeff Dike 	if (pte_newpage(*pte)) {
50177bf4400SJeff Dike 		if (pte_present(*pte)) {
50277bf4400SJeff Dike 			unsigned long long offset;
50377bf4400SJeff Dike 			int fd;
50477bf4400SJeff Dike 
50577bf4400SJeff Dike 			fd = phys_mapping(pte_val(*pte) & PAGE_MASK, &offset);
50677bf4400SJeff Dike 			err = map(mm_id, address, PAGE_SIZE, prot, fd, offset,
50777bf4400SJeff Dike 				  1, &flush);
50877bf4400SJeff Dike 		}
50977bf4400SJeff Dike 		else err = unmap(mm_id, address, PAGE_SIZE, 1, &flush);
51077bf4400SJeff Dike 	}
51177bf4400SJeff Dike 	else if (pte_newprot(*pte))
51277bf4400SJeff Dike 		err = protect(mm_id, address, PAGE_SIZE, prot, 1, &flush);
51377bf4400SJeff Dike 
51470c8205fSRichard Weinberger 	if (err) {
51570c8205fSRichard Weinberger 		if (err == -ENOMEM)
51670c8205fSRichard Weinberger 			report_enomem();
51770c8205fSRichard Weinberger 
51877bf4400SJeff Dike 		goto kill;
51970c8205fSRichard Weinberger 	}
52077bf4400SJeff Dike 
52177bf4400SJeff Dike 	*pte = pte_mkuptodate(*pte);
52277bf4400SJeff Dike 
52377bf4400SJeff Dike 	return;
52477bf4400SJeff Dike 
52577bf4400SJeff Dike kill:
526ba180fd4SJeff Dike 	printk(KERN_ERR "Failed to flush page for address 0x%lx\n", address);
5273cf5d076SEric W. Biederman 	force_sig(SIGKILL);
52877bf4400SJeff Dike }
52977bf4400SJeff Dike 
flush_tlb_all(void)5301da177e4SLinus Torvalds void flush_tlb_all(void)
5311da177e4SLinus Torvalds {
53238e3cbd9SAnton Ivanov 	/*
53338e3cbd9SAnton Ivanov 	 * Don't bother flushing if this address space is about to be
53438e3cbd9SAnton Ivanov 	 * destroyed.
53538e3cbd9SAnton Ivanov 	 */
53638e3cbd9SAnton Ivanov 	if (atomic_read(&current->mm->mm_users) == 0)
53738e3cbd9SAnton Ivanov 		return;
53838e3cbd9SAnton Ivanov 
5391da177e4SLinus Torvalds 	flush_tlb_mm(current->mm);
5401da177e4SLinus Torvalds }
5411da177e4SLinus Torvalds 
flush_tlb_kernel_range(unsigned long start,unsigned long end)5421da177e4SLinus Torvalds void flush_tlb_kernel_range(unsigned long start, unsigned long end)
5431da177e4SLinus Torvalds {
5446aa802ceSJeff Dike 	flush_tlb_kernel_range_common(start, end);
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds 
flush_tlb_kernel_vm(void)5471da177e4SLinus Torvalds void flush_tlb_kernel_vm(void)
5481da177e4SLinus Torvalds {
5496aa802ceSJeff Dike 	flush_tlb_kernel_range_common(start_vm, end_vm);
5501da177e4SLinus Torvalds }
5511da177e4SLinus Torvalds 
__flush_tlb_one(unsigned long addr)5521da177e4SLinus Torvalds void __flush_tlb_one(unsigned long addr)
5531da177e4SLinus Torvalds {
55477bf4400SJeff Dike 	flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE);
55577bf4400SJeff Dike }
55677bf4400SJeff Dike 
fix_range(struct mm_struct * mm,unsigned long start_addr,unsigned long end_addr,int force)55777bf4400SJeff Dike static void fix_range(struct mm_struct *mm, unsigned long start_addr,
55877bf4400SJeff Dike 		      unsigned long end_addr, int force)
55977bf4400SJeff Dike {
56038e3cbd9SAnton Ivanov 	/*
56138e3cbd9SAnton Ivanov 	 * Don't bother flushing if this address space is about to be
56238e3cbd9SAnton Ivanov 	 * destroyed.
56338e3cbd9SAnton Ivanov 	 */
56438e3cbd9SAnton Ivanov 	if (atomic_read(&mm->mm_users) == 0)
56538e3cbd9SAnton Ivanov 		return;
56638e3cbd9SAnton Ivanov 
5671466abf2SJeff Dike 	fix_range_common(mm, start_addr, end_addr, force);
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds 
flush_tlb_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)5701da177e4SLinus Torvalds void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
5711da177e4SLinus Torvalds 		     unsigned long end)
5721da177e4SLinus Torvalds {
57377bf4400SJeff Dike 	if (vma->vm_mm == NULL)
57477bf4400SJeff Dike 		flush_tlb_kernel_range_common(start, end);
57577bf4400SJeff Dike 	else fix_range(vma->vm_mm, start, end, 0);
5761da177e4SLinus Torvalds }
57773395a00SAl Viro EXPORT_SYMBOL(flush_tlb_range);
5781da177e4SLinus Torvalds 
flush_tlb_mm_range(struct mm_struct * mm,unsigned long start,unsigned long end)5790b4e273fSJeff Dike void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
5800b4e273fSJeff Dike 			unsigned long end)
5811da177e4SLinus Torvalds {
5820b4e273fSJeff Dike 	fix_range(mm, start, end, 0);
5830b4e273fSJeff Dike }
5840b4e273fSJeff Dike 
flush_tlb_mm(struct mm_struct * mm)5850b4e273fSJeff Dike void flush_tlb_mm(struct mm_struct *mm)
5860b4e273fSJeff Dike {
587cbd43755SMatthew Wilcox (Oracle) 	struct vm_area_struct *vma;
588cbd43755SMatthew Wilcox (Oracle) 	VMA_ITERATOR(vmi, mm, 0);
5890b4e273fSJeff Dike 
590cbd43755SMatthew Wilcox (Oracle) 	for_each_vma(vmi, vma)
5910b4e273fSJeff Dike 		fix_range(mm, vma->vm_start, vma->vm_end, 0);
5921da177e4SLinus Torvalds }
5931da177e4SLinus Torvalds 
force_flush_all(void)5941da177e4SLinus Torvalds void force_flush_all(void)
5951da177e4SLinus Torvalds {
59677bf4400SJeff Dike 	struct mm_struct *mm = current->mm;
597cbd43755SMatthew Wilcox (Oracle) 	struct vm_area_struct *vma;
598cbd43755SMatthew Wilcox (Oracle) 	VMA_ITERATOR(vmi, mm, 0);
59977bf4400SJeff Dike 
6003271e27bSJohannes Berg 	mmap_read_lock(mm);
601cbd43755SMatthew Wilcox (Oracle) 	for_each_vma(vmi, vma)
60277bf4400SJeff Dike 		fix_range(mm, vma->vm_start, vma->vm_end, 1);
6033271e27bSJohannes Berg 	mmap_read_unlock(mm);
6041da177e4SLinus Torvalds }
605