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 = ¤t->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(¤t->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