hc
2023-12-11 6778948f9de86c3cfaf36725a7c87dcff9ba247f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mm/highmem.c -- ARM highmem support
 *
 * Author:    Nicolas Pitre
 * Created:    september 8, 2008
 * Copyright:    Marvell Semiconductors Inc.
 */
 
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <asm/fixmap.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include "mm.h"
 
static inline void set_fixmap_pte(int idx, pte_t pte)
{
   unsigned long vaddr = __fix_to_virt(idx);
   pte_t *ptep = virt_to_kpte(vaddr);
 
   set_pte_ext(ptep, pte, 0);
   local_flush_tlb_kernel_page(vaddr);
}
 
static inline pte_t get_fixmap_pte(unsigned long vaddr)
{
   pte_t *ptep = virt_to_kpte(vaddr);
 
   return *ptep;
}
 
void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
{
   unsigned int idx;
   unsigned long vaddr;
   void *kmap;
   int type;
 
#ifdef CONFIG_DEBUG_HIGHMEM
   /*
    * There is no cache coherency issue when non VIVT, so force the
    * dedicated kmap usage for better debugging purposes in that case.
    */
   if (!cache_is_vivt())
       kmap = NULL;
   else
#endif
       kmap = kmap_high_get(page);
   if (kmap)
       return kmap;
 
   type = kmap_atomic_idx_push();
 
   idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
   vaddr = __fix_to_virt(idx);
#ifdef CONFIG_DEBUG_HIGHMEM
   /*
    * With debugging enabled, kunmap_atomic forces that entry to 0.
    * Make sure it was indeed properly unmapped.
    */
   BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
#endif
   /*
    * When debugging is off, kunmap_atomic leaves the previous mapping
    * in place, so the contained TLB flush ensures the TLB is updated
    * with the new mapping.
    */
   set_fixmap_pte(idx, mk_pte(page, prot));
 
   return (void *)vaddr;
}
EXPORT_SYMBOL(kmap_atomic_high_prot);
 
void kunmap_atomic_high(void *kvaddr)
{
   unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
   int idx, type;
 
   if (kvaddr >= (void *)FIXADDR_START) {
       type = kmap_atomic_idx();
       idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
 
       if (cache_is_vivt())
           __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
#ifdef CONFIG_DEBUG_HIGHMEM
       BUG_ON(vaddr != __fix_to_virt(idx));
       set_fixmap_pte(idx, __pte(0));
#else
       (void) idx;  /* to kill a warning */
#endif
       kmap_atomic_idx_pop();
   } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
       /* this address was obtained through kmap_high_get() */
       kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
   }
}
EXPORT_SYMBOL(kunmap_atomic_high);
 
void *kmap_atomic_pfn(unsigned long pfn)
{
   unsigned long vaddr;
   int idx, type;
   struct page *page = pfn_to_page(pfn);
 
   preempt_disable();
   pagefault_disable();
   if (!PageHighMem(page))
       return page_address(page);
 
   type = kmap_atomic_idx_push();
   idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
   vaddr = __fix_to_virt(idx);
#ifdef CONFIG_DEBUG_HIGHMEM
   BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
#endif
   set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot));
 
   return (void *)vaddr;
}