diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-03-31 15:26:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-03-31 15:26:05 -0400 |
commit | 7cc3afdf43ffb703db831292f3816d909fd44767 (patch) | |
tree | 9c9f4b9c1a880b4f8ec4fc44ef80b6568142756b /arch/x86/mm | |
parent | ad8946fbf913fff8671cbaee776d23c3d07173fa (diff) | |
parent | 204b0a1a4b92612c957a042df1a3be0e9cc79391 (diff) |
Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 EFI changes from Ingo Molnar:
"The main changes:
- Add debug code to the dump EFI pagetable - Borislav Petkov
- Make 1:1 runtime mapping robust when booting on machines with lots
of memory - Borislav Petkov
- Move the EFI facilities bits out of 'x86_efi_facility' and into
efi.flags which is the standard architecture independent place to
keep EFI state, by Matt Fleming.
- Add 'EFI mixed mode' support: this allows 64-bit kernels to be
booted from 32-bit firmware. This needs a bootloader that supports
the 'EFI handover protocol'. By Matt Fleming"
* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (31 commits)
x86, efi: Abstract x86 efi_early calls
x86/efi: Restore 'attr' argument to query_variable_info()
x86/efi: Rip out phys_efi_get_time()
x86/efi: Preserve segment registers in mixed mode
x86/boot: Fix non-EFI build
x86, tools: Fix up compiler warnings
x86/efi: Re-disable interrupts after calling firmware services
x86/boot: Don't overwrite cr4 when enabling PAE
x86/efi: Wire up CONFIG_EFI_MIXED
x86/efi: Add mixed runtime services support
x86/efi: Firmware agnostic handover entry points
x86/efi: Split the boot stub into 32/64 code paths
x86/efi: Add early thunk code to go from 64-bit to 32-bit
x86/efi: Build our own EFI services pointer table
efi: Add separate 32-bit/64-bit definitions
x86/efi: Delete dead code when checking for non-native
x86/mm/pageattr: Always dump the right page table in an oops
x86, tools: Consolidate #ifdef code
x86/boot: Cleanup header.S by removing some #ifdefs
efi: Use NULL instead of 0 for pointer
...
Diffstat (limited to 'arch/x86/mm')
-rw-r--r-- | arch/x86/mm/dump_pagetables.c | 84 | ||||
-rw-r--r-- | arch/x86/mm/fault.c | 7 | ||||
-rw-r--r-- | arch/x86/mm/pageattr.c | 56 |
3 files changed, 99 insertions, 48 deletions
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c index 0002a3a33081..20621d753d5f 100644 --- a/arch/x86/mm/dump_pagetables.c +++ b/arch/x86/mm/dump_pagetables.c | |||
@@ -30,6 +30,7 @@ struct pg_state { | |||
30 | unsigned long start_address; | 30 | unsigned long start_address; |
31 | unsigned long current_address; | 31 | unsigned long current_address; |
32 | const struct addr_marker *marker; | 32 | const struct addr_marker *marker; |
33 | bool to_dmesg; | ||
33 | }; | 34 | }; |
34 | 35 | ||
35 | struct addr_marker { | 36 | struct addr_marker { |
@@ -88,10 +89,28 @@ static struct addr_marker address_markers[] = { | |||
88 | #define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT) | 89 | #define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT) |
89 | #define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT) | 90 | #define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT) |
90 | 91 | ||
92 | #define pt_dump_seq_printf(m, to_dmesg, fmt, args...) \ | ||
93 | ({ \ | ||
94 | if (to_dmesg) \ | ||
95 | printk(KERN_INFO fmt, ##args); \ | ||
96 | else \ | ||
97 | if (m) \ | ||
98 | seq_printf(m, fmt, ##args); \ | ||
99 | }) | ||
100 | |||
101 | #define pt_dump_cont_printf(m, to_dmesg, fmt, args...) \ | ||
102 | ({ \ | ||
103 | if (to_dmesg) \ | ||
104 | printk(KERN_CONT fmt, ##args); \ | ||
105 | else \ | ||
106 | if (m) \ | ||
107 | seq_printf(m, fmt, ##args); \ | ||
108 | }) | ||
109 | |||
91 | /* | 110 | /* |
92 | * Print a readable form of a pgprot_t to the seq_file | 111 | * Print a readable form of a pgprot_t to the seq_file |
93 | */ | 112 | */ |
94 | static void printk_prot(struct seq_file *m, pgprot_t prot, int level) | 113 | static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg) |
95 | { | 114 | { |
96 | pgprotval_t pr = pgprot_val(prot); | 115 | pgprotval_t pr = pgprot_val(prot); |
97 | static const char * const level_name[] = | 116 | static const char * const level_name[] = |
@@ -99,47 +118,47 @@ static void printk_prot(struct seq_file *m, pgprot_t prot, int level) | |||
99 | 118 | ||
100 | if (!pgprot_val(prot)) { | 119 | if (!pgprot_val(prot)) { |
101 | /* Not present */ | 120 | /* Not present */ |
102 | seq_printf(m, " "); | 121 | pt_dump_cont_printf(m, dmsg, " "); |
103 | } else { | 122 | } else { |
104 | if (pr & _PAGE_USER) | 123 | if (pr & _PAGE_USER) |
105 | seq_printf(m, "USR "); | 124 | pt_dump_cont_printf(m, dmsg, "USR "); |
106 | else | 125 | else |
107 | seq_printf(m, " "); | 126 | pt_dump_cont_printf(m, dmsg, " "); |
108 | if (pr & _PAGE_RW) | 127 | if (pr & _PAGE_RW) |
109 | seq_printf(m, "RW "); | 128 | pt_dump_cont_printf(m, dmsg, "RW "); |
110 | else | 129 | else |
111 | seq_printf(m, "ro "); | 130 | pt_dump_cont_printf(m, dmsg, "ro "); |
112 | if (pr & _PAGE_PWT) | 131 | if (pr & _PAGE_PWT) |
113 | seq_printf(m, "PWT "); | 132 | pt_dump_cont_printf(m, dmsg, "PWT "); |
114 | else | 133 | else |
115 | seq_printf(m, " "); | 134 | pt_dump_cont_printf(m, dmsg, " "); |
116 | if (pr & _PAGE_PCD) | 135 | if (pr & _PAGE_PCD) |
117 | seq_printf(m, "PCD "); | 136 | pt_dump_cont_printf(m, dmsg, "PCD "); |
118 | else | 137 | else |
119 | seq_printf(m, " "); | 138 | pt_dump_cont_printf(m, dmsg, " "); |
120 | 139 | ||
121 | /* Bit 9 has a different meaning on level 3 vs 4 */ | 140 | /* Bit 9 has a different meaning on level 3 vs 4 */ |
122 | if (level <= 3) { | 141 | if (level <= 3) { |
123 | if (pr & _PAGE_PSE) | 142 | if (pr & _PAGE_PSE) |
124 | seq_printf(m, "PSE "); | 143 | pt_dump_cont_printf(m, dmsg, "PSE "); |
125 | else | 144 | else |
126 | seq_printf(m, " "); | 145 | pt_dump_cont_printf(m, dmsg, " "); |
127 | } else { | 146 | } else { |
128 | if (pr & _PAGE_PAT) | 147 | if (pr & _PAGE_PAT) |
129 | seq_printf(m, "pat "); | 148 | pt_dump_cont_printf(m, dmsg, "pat "); |
130 | else | 149 | else |
131 | seq_printf(m, " "); | 150 | pt_dump_cont_printf(m, dmsg, " "); |
132 | } | 151 | } |
133 | if (pr & _PAGE_GLOBAL) | 152 | if (pr & _PAGE_GLOBAL) |
134 | seq_printf(m, "GLB "); | 153 | pt_dump_cont_printf(m, dmsg, "GLB "); |
135 | else | 154 | else |
136 | seq_printf(m, " "); | 155 | pt_dump_cont_printf(m, dmsg, " "); |
137 | if (pr & _PAGE_NX) | 156 | if (pr & _PAGE_NX) |
138 | seq_printf(m, "NX "); | 157 | pt_dump_cont_printf(m, dmsg, "NX "); |
139 | else | 158 | else |
140 | seq_printf(m, "x "); | 159 | pt_dump_cont_printf(m, dmsg, "x "); |
141 | } | 160 | } |
142 | seq_printf(m, "%s\n", level_name[level]); | 161 | pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]); |
143 | } | 162 | } |
144 | 163 | ||
145 | /* | 164 | /* |
@@ -178,7 +197,8 @@ static void note_page(struct seq_file *m, struct pg_state *st, | |||
178 | st->current_prot = new_prot; | 197 | st->current_prot = new_prot; |
179 | st->level = level; | 198 | st->level = level; |
180 | st->marker = address_markers; | 199 | st->marker = address_markers; |
181 | seq_printf(m, "---[ %s ]---\n", st->marker->name); | 200 | pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n", |
201 | st->marker->name); | ||
182 | } else if (prot != cur || level != st->level || | 202 | } else if (prot != cur || level != st->level || |
183 | st->current_address >= st->marker[1].start_address) { | 203 | st->current_address >= st->marker[1].start_address) { |
184 | const char *unit = units; | 204 | const char *unit = units; |
@@ -188,17 +208,17 @@ static void note_page(struct seq_file *m, struct pg_state *st, | |||
188 | /* | 208 | /* |
189 | * Now print the actual finished series | 209 | * Now print the actual finished series |
190 | */ | 210 | */ |
191 | seq_printf(m, "0x%0*lx-0x%0*lx ", | 211 | pt_dump_seq_printf(m, st->to_dmesg, "0x%0*lx-0x%0*lx ", |
192 | width, st->start_address, | 212 | width, st->start_address, |
193 | width, st->current_address); | 213 | width, st->current_address); |
194 | 214 | ||
195 | delta = (st->current_address - st->start_address) >> 10; | 215 | delta = (st->current_address - st->start_address) >> 10; |
196 | while (!(delta & 1023) && unit[1]) { | 216 | while (!(delta & 1023) && unit[1]) { |
197 | delta >>= 10; | 217 | delta >>= 10; |
198 | unit++; | 218 | unit++; |
199 | } | 219 | } |
200 | seq_printf(m, "%9lu%c ", delta, *unit); | 220 | pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ", delta, *unit); |
201 | printk_prot(m, st->current_prot, st->level); | 221 | printk_prot(m, st->current_prot, st->level, st->to_dmesg); |
202 | 222 | ||
203 | /* | 223 | /* |
204 | * We print markers for special areas of address space, | 224 | * We print markers for special areas of address space, |
@@ -207,7 +227,8 @@ static void note_page(struct seq_file *m, struct pg_state *st, | |||
207 | */ | 227 | */ |
208 | if (st->current_address >= st->marker[1].start_address) { | 228 | if (st->current_address >= st->marker[1].start_address) { |
209 | st->marker++; | 229 | st->marker++; |
210 | seq_printf(m, "---[ %s ]---\n", st->marker->name); | 230 | pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n", |
231 | st->marker->name); | ||
211 | } | 232 | } |
212 | 233 | ||
213 | st->start_address = st->current_address; | 234 | st->start_address = st->current_address; |
@@ -296,7 +317,7 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr, | |||
296 | #define pgd_none(a) pud_none(__pud(pgd_val(a))) | 317 | #define pgd_none(a) pud_none(__pud(pgd_val(a))) |
297 | #endif | 318 | #endif |
298 | 319 | ||
299 | static void walk_pgd_level(struct seq_file *m) | 320 | void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd) |
300 | { | 321 | { |
301 | #ifdef CONFIG_X86_64 | 322 | #ifdef CONFIG_X86_64 |
302 | pgd_t *start = (pgd_t *) &init_level4_pgt; | 323 | pgd_t *start = (pgd_t *) &init_level4_pgt; |
@@ -304,9 +325,12 @@ static void walk_pgd_level(struct seq_file *m) | |||
304 | pgd_t *start = swapper_pg_dir; | 325 | pgd_t *start = swapper_pg_dir; |
305 | #endif | 326 | #endif |
306 | int i; | 327 | int i; |
307 | struct pg_state st; | 328 | struct pg_state st = {}; |
308 | 329 | ||
309 | memset(&st, 0, sizeof(st)); | 330 | if (pgd) { |
331 | start = pgd; | ||
332 | st.to_dmesg = true; | ||
333 | } | ||
310 | 334 | ||
311 | for (i = 0; i < PTRS_PER_PGD; i++) { | 335 | for (i = 0; i < PTRS_PER_PGD; i++) { |
312 | st.current_address = normalize_addr(i * PGD_LEVEL_MULT); | 336 | st.current_address = normalize_addr(i * PGD_LEVEL_MULT); |
@@ -331,7 +355,7 @@ static void walk_pgd_level(struct seq_file *m) | |||
331 | 355 | ||
332 | static int ptdump_show(struct seq_file *m, void *v) | 356 | static int ptdump_show(struct seq_file *m, void *v) |
333 | { | 357 | { |
334 | walk_pgd_level(m); | 358 | ptdump_walk_pgd_level(m, NULL); |
335 | return 0; | 359 | return 0; |
336 | } | 360 | } |
337 | 361 | ||
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index a10c8c792161..8e5722992677 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -584,8 +584,13 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code, | |||
584 | 584 | ||
585 | if (error_code & PF_INSTR) { | 585 | if (error_code & PF_INSTR) { |
586 | unsigned int level; | 586 | unsigned int level; |
587 | pgd_t *pgd; | ||
588 | pte_t *pte; | ||
587 | 589 | ||
588 | pte_t *pte = lookup_address(address, &level); | 590 | pgd = __va(read_cr3() & PHYSICAL_PAGE_MASK); |
591 | pgd += pgd_index(address); | ||
592 | |||
593 | pte = lookup_address_in_pgd(pgd, address, &level); | ||
589 | 594 | ||
590 | if (pte && pte_present(*pte) && !pte_exec(*pte)) | 595 | if (pte && pte_present(*pte) && !pte_exec(*pte)) |
591 | printk(nx_warning, from_kuid(&init_user_ns, current_uid())); | 596 | printk(nx_warning, from_kuid(&init_user_ns, current_uid())); |
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index cf125b301b69..ae242a7c11c7 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -323,8 +323,12 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, | |||
323 | return prot; | 323 | return prot; |
324 | } | 324 | } |
325 | 325 | ||
326 | static pte_t *__lookup_address_in_pgd(pgd_t *pgd, unsigned long address, | 326 | /* |
327 | unsigned int *level) | 327 | * Lookup the page table entry for a virtual address in a specific pgd. |
328 | * Return a pointer to the entry and the level of the mapping. | ||
329 | */ | ||
330 | pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address, | ||
331 | unsigned int *level) | ||
328 | { | 332 | { |
329 | pud_t *pud; | 333 | pud_t *pud; |
330 | pmd_t *pmd; | 334 | pmd_t *pmd; |
@@ -365,7 +369,7 @@ static pte_t *__lookup_address_in_pgd(pgd_t *pgd, unsigned long address, | |||
365 | */ | 369 | */ |
366 | pte_t *lookup_address(unsigned long address, unsigned int *level) | 370 | pte_t *lookup_address(unsigned long address, unsigned int *level) |
367 | { | 371 | { |
368 | return __lookup_address_in_pgd(pgd_offset_k(address), address, level); | 372 | return lookup_address_in_pgd(pgd_offset_k(address), address, level); |
369 | } | 373 | } |
370 | EXPORT_SYMBOL_GPL(lookup_address); | 374 | EXPORT_SYMBOL_GPL(lookup_address); |
371 | 375 | ||
@@ -373,7 +377,7 @@ static pte_t *_lookup_address_cpa(struct cpa_data *cpa, unsigned long address, | |||
373 | unsigned int *level) | 377 | unsigned int *level) |
374 | { | 378 | { |
375 | if (cpa->pgd) | 379 | if (cpa->pgd) |
376 | return __lookup_address_in_pgd(cpa->pgd + pgd_index(address), | 380 | return lookup_address_in_pgd(cpa->pgd + pgd_index(address), |
377 | address, level); | 381 | address, level); |
378 | 382 | ||
379 | return lookup_address(address, level); | 383 | return lookup_address(address, level); |
@@ -692,6 +696,18 @@ static bool try_to_free_pmd_page(pmd_t *pmd) | |||
692 | return true; | 696 | return true; |
693 | } | 697 | } |
694 | 698 | ||
699 | static bool try_to_free_pud_page(pud_t *pud) | ||
700 | { | ||
701 | int i; | ||
702 | |||
703 | for (i = 0; i < PTRS_PER_PUD; i++) | ||
704 | if (!pud_none(pud[i])) | ||
705 | return false; | ||
706 | |||
707 | free_page((unsigned long)pud); | ||
708 | return true; | ||
709 | } | ||
710 | |||
695 | static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end) | 711 | static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end) |
696 | { | 712 | { |
697 | pte_t *pte = pte_offset_kernel(pmd, start); | 713 | pte_t *pte = pte_offset_kernel(pmd, start); |
@@ -805,6 +821,16 @@ static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | |||
805 | */ | 821 | */ |
806 | } | 822 | } |
807 | 823 | ||
824 | static void unmap_pgd_range(pgd_t *root, unsigned long addr, unsigned long end) | ||
825 | { | ||
826 | pgd_t *pgd_entry = root + pgd_index(addr); | ||
827 | |||
828 | unmap_pud_range(pgd_entry, addr, end); | ||
829 | |||
830 | if (try_to_free_pud_page((pud_t *)pgd_page_vaddr(*pgd_entry))) | ||
831 | pgd_clear(pgd_entry); | ||
832 | } | ||
833 | |||
808 | static int alloc_pte_page(pmd_t *pmd) | 834 | static int alloc_pte_page(pmd_t *pmd) |
809 | { | 835 | { |
810 | pte_t *pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_NOTRACK); | 836 | pte_t *pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_NOTRACK); |
@@ -999,9 +1025,8 @@ static int populate_pud(struct cpa_data *cpa, unsigned long start, pgd_t *pgd, | |||
999 | static int populate_pgd(struct cpa_data *cpa, unsigned long addr) | 1025 | static int populate_pgd(struct cpa_data *cpa, unsigned long addr) |
1000 | { | 1026 | { |
1001 | pgprot_t pgprot = __pgprot(_KERNPG_TABLE); | 1027 | pgprot_t pgprot = __pgprot(_KERNPG_TABLE); |
1002 | bool allocd_pgd = false; | ||
1003 | pgd_t *pgd_entry; | ||
1004 | pud_t *pud = NULL; /* shut up gcc */ | 1028 | pud_t *pud = NULL; /* shut up gcc */ |
1029 | pgd_t *pgd_entry; | ||
1005 | int ret; | 1030 | int ret; |
1006 | 1031 | ||
1007 | pgd_entry = cpa->pgd + pgd_index(addr); | 1032 | pgd_entry = cpa->pgd + pgd_index(addr); |
@@ -1015,7 +1040,6 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr) | |||
1015 | return -1; | 1040 | return -1; |
1016 | 1041 | ||
1017 | set_pgd(pgd_entry, __pgd(__pa(pud) | _KERNPG_TABLE)); | 1042 | set_pgd(pgd_entry, __pgd(__pa(pud) | _KERNPG_TABLE)); |
1018 | allocd_pgd = true; | ||
1019 | } | 1043 | } |
1020 | 1044 | ||
1021 | pgprot_val(pgprot) &= ~pgprot_val(cpa->mask_clr); | 1045 | pgprot_val(pgprot) &= ~pgprot_val(cpa->mask_clr); |
@@ -1023,19 +1047,11 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr) | |||
1023 | 1047 | ||
1024 | ret = populate_pud(cpa, addr, pgd_entry, pgprot); | 1048 | ret = populate_pud(cpa, addr, pgd_entry, pgprot); |
1025 | if (ret < 0) { | 1049 | if (ret < 0) { |
1026 | unmap_pud_range(pgd_entry, addr, | 1050 | unmap_pgd_range(cpa->pgd, addr, |
1027 | addr + (cpa->numpages << PAGE_SHIFT)); | 1051 | addr + (cpa->numpages << PAGE_SHIFT)); |
1028 | |||
1029 | if (allocd_pgd) { | ||
1030 | /* | ||
1031 | * If I allocated this PUD page, I can just as well | ||
1032 | * free it in this error path. | ||
1033 | */ | ||
1034 | pgd_clear(pgd_entry); | ||
1035 | free_page((unsigned long)pud); | ||
1036 | } | ||
1037 | return ret; | 1052 | return ret; |
1038 | } | 1053 | } |
1054 | |||
1039 | cpa->numpages = ret; | 1055 | cpa->numpages = ret; |
1040 | return 0; | 1056 | return 0; |
1041 | } | 1057 | } |
@@ -1861,6 +1877,12 @@ out: | |||
1861 | return retval; | 1877 | return retval; |
1862 | } | 1878 | } |
1863 | 1879 | ||
1880 | void kernel_unmap_pages_in_pgd(pgd_t *root, unsigned long address, | ||
1881 | unsigned numpages) | ||
1882 | { | ||
1883 | unmap_pgd_range(root, address, address + (numpages << PAGE_SHIFT)); | ||
1884 | } | ||
1885 | |||
1864 | /* | 1886 | /* |
1865 | * The testcases use internal knowledge of the implementation that shouldn't | 1887 | * The testcases use internal knowledge of the implementation that shouldn't |
1866 | * be exposed to the rest of the kernel. Include these directly here. | 1888 | * be exposed to the rest of the kernel. Include these directly here. |