This repository has been archived by the owner on Jul 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
0005-x86-mm-cpa-Allow-range-check-for-static-protections.patch
189 lines (171 loc) · 6.94 KB
/
0005-x86-mm-cpa-Allow-range-check-for-static-protections.patch
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Thomas Gleixner <[email protected]>
Date: Mon, 17 Sep 2018 16:29:10 +0200
Subject: [PATCH] x86/mm/cpa: Allow range check for static protections
Checking static protections only page by page is slow especially for huge
pages. To allow quick checks over a complete range, add the ability to do
that.
Make the checks inclusive so the ranges can be directly used for debug output
later.
No functional change.
Signed-off-by: Thomas Gleixner <[email protected]>
Reviewed-by: Dave Hansen <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Bin Yang <[email protected]>
Cc: Mark Gross <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
Cc: Zhang Ning <[email protected]>
Signed-off-by: Lili Li <[email protected]>
---
arch/x86/mm/pageattr.c | 69 +++++++++++++++++++++++++++---------------
1 file changed, 44 insertions(+), 25 deletions(-)
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 44964ceaea96..ddd26536429c 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -286,22 +286,29 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache,
}
}
+static bool overlaps(unsigned long r1_start, unsigned long r1_end,
+ unsigned long r2_start, unsigned long r2_end)
+{
+ return (r1_start <= r2_end && r1_end >= r2_start) ||
+ (r2_start <= r1_end && r2_end >= r1_start);
+}
+
#ifdef CONFIG_PCI_BIOS
/*
* The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS
* based config access (CONFIG_PCI_GOBIOS) support.
*/
#define BIOS_PFN PFN_DOWN(BIOS_BEGIN)
-#define BIOS_PFN_END PFN_DOWN(BIOS_END)
+#define BIOS_PFN_END PFN_DOWN(BIOS_END - 1)
-static pgprotval_t protect_pci_bios(unsigned long pfn)
+static pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn)
{
- if (pcibios_enabled && within(pfn, BIOS_PFN, BIOS_PFN_END))
+ if (pcibios_enabled && overlaps(spfn, epfn, BIOS_PFN, BIOS_PFN_END))
return _PAGE_NX;
return 0;
}
#else
-static pgprotval_t protect_pci_bios(unsigned long pfn)
+static pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn)
{
return 0;
}
@@ -312,12 +319,17 @@ static pgprotval_t protect_pci_bios(unsigned long pfn)
* aliases. This also includes __ro_after_init, so do not enforce until
* kernel_set_to_readonly is true.
*/
-static pgprotval_t protect_rodata(unsigned long pfn)
+static pgprotval_t protect_rodata(unsigned long spfn, unsigned long epfn)
{
- unsigned long start_pfn = __pa_symbol(__start_rodata) >> PAGE_SHIFT;
- unsigned long end_pfn = __pa_symbol(__end_rodata) >> PAGE_SHIFT;
+ unsigned long epfn_ro, spfn_ro = PFN_DOWN(__pa_symbol(__start_rodata));
+
+ /*
+ * Note: __end_rodata is at page aligned and not inclusive, so
+ * subtract 1 to get the last enforced PFN in the rodata area.
+ */
+ epfn_ro = PFN_DOWN(__pa_symbol(__end_rodata)) - 1;
- if (kernel_set_to_readonly && within(pfn, start_pfn, end_pfn))
+ if (kernel_set_to_readonly && overlaps(spfn, epfn, spfn_ro, epfn_ro))
return _PAGE_RW;
return 0;
}
@@ -330,9 +342,12 @@ static pgprotval_t protect_rodata(unsigned long pfn)
*
* This does not cover __inittext since that is gone after boot.
*/
-static pgprotval_t protect_kernel_text(unsigned long address)
+static pgprotval_t protect_kernel_text(unsigned long start, unsigned long end)
{
- if (within(address, (unsigned long)_text, (unsigned long)_etext))
+ unsigned long t_end = (unsigned long)_etext - 1;
+ unsigned long t_start = (unsigned long)_text;
+
+ if (overlaps(start, end, t_start, t_end))
return _PAGE_NX;
return 0;
}
@@ -347,13 +362,14 @@ static pgprotval_t protect_kernel_text(unsigned long address)
* This will preserve the large page mappings for kernel text/data at no
* extra cost.
*/
-static pgprotval_t protect_kernel_text_ro(unsigned long address)
+static pgprotval_t protect_kernel_text_ro(unsigned long start,
+ unsigned long end)
{
- unsigned long end = (unsigned long)__end_rodata_hpage_align;
- unsigned long start = (unsigned long)_text;
+ unsigned long t_end = (unsigned long)__end_rodata_hpage_align - 1;
+ unsigned long t_start = (unsigned long)_text;
unsigned int level;
- if (!kernel_set_to_readonly || !within(address, start, end))
+ if (!kernel_set_to_readonly || !overlaps(start, end, t_start, t_end))
return 0;
/*
* Don't enforce the !RW mapping for the kernel text mapping, if
@@ -367,12 +383,13 @@ static pgprotval_t protect_kernel_text_ro(unsigned long address)
* so the protections for kernel text and identity mappings have to
* be the same.
*/
- if (lookup_address(address, &level) && (level != PG_LEVEL_4K))
+ if (lookup_address(start, &level) && (level != PG_LEVEL_4K))
return _PAGE_RW;
return 0;
}
#else
-static pgprotval_t protect_kernel_text_ro(unsigned long address)
+static pgprotval_t protect_kernel_text_ro(unsigned long start,
+ unsigned long end)
{
return 0;
}
@@ -384,18 +401,20 @@ static pgprotval_t protect_kernel_text_ro(unsigned long address)
* right (again, ioremap() on BIOS memory is not uncommon) so this function
* checks and fixes these known static required protection bits.
*/
-static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
- unsigned long pfn)
+static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
+ unsigned long pfn, unsigned long npg)
{
pgprotval_t forbidden;
+ unsigned long end;
/* Operate on the virtual address */
- forbidden = protect_kernel_text(address);
- forbidden |= protect_kernel_text_ro(address);
+ end = start + npg * PAGE_SIZE - 1;
+ forbidden = protect_kernel_text(start, end);
+ forbidden |= protect_kernel_text_ro(start, end);
/* Check the PFN directly */
- forbidden |= protect_pci_bios(pfn);
- forbidden |= protect_rodata(pfn);
+ forbidden |= protect_pci_bios(pfn, pfn + npg - 1);
+ forbidden |= protect_rodata(pfn, pfn + npg - 1);
return __pgprot(pgprot_val(prot) & ~forbidden);
}
@@ -667,10 +686,10 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
* in it results in a different pgprot than the first one of the
* requested range. If yes, then the page needs to be split.
*/
- new_prot = static_protections(req_prot, address, pfn);
+ new_prot = static_protections(req_prot, address, pfn, 1);
pfn = old_pfn;
for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) {
- pgprot_t chk_prot = static_protections(req_prot, addr, pfn);
+ pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1);
if (pgprot_val(chk_prot) != pgprot_val(new_prot))
return 1;
@@ -1280,7 +1299,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr);
pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
- new_prot = static_protections(new_prot, address, pfn);
+ new_prot = static_protections(new_prot, address, pfn, 1);
new_prot = pgprot_clear_protnone_bits(new_prot);
--
https://clearlinux.org