| /* SPDX-License-Identifier: GPL-2.0 */ |
| #ifndef _ASM_X86_PAGE_64_H |
| #define _ASM_X86_PAGE_64_H |
| |
| #include <asm/page_64_types.h> |
| |
| #ifndef __ASSEMBLER__ |
| #include <asm/cpufeatures.h> |
| #include <asm/alternative.h> |
| |
| #include <linux/kmsan-checks.h> |
| #include <linux/mmdebug.h> |
| |
| /* duplicated to the one in bootmem.h */ |
| extern unsigned long max_pfn; |
| extern unsigned long phys_base; |
| |
| extern unsigned long page_offset_base; |
| extern unsigned long vmalloc_base; |
| extern unsigned long vmemmap_base; |
| extern unsigned long direct_map_physmem_end; |
| |
| static __always_inline unsigned long __phys_addr_nodebug(unsigned long x) |
| { |
| unsigned long y = x - __START_KERNEL_map; |
| |
| /* use the carry flag to determine if x was < __START_KERNEL_map */ |
| x = y + ((x > y) ? phys_base : (__START_KERNEL_map - PAGE_OFFSET)); |
| |
| return x; |
| } |
| |
| #ifdef CONFIG_DEBUG_VIRTUAL |
| extern unsigned long __phys_addr(unsigned long); |
| #else |
| #define __phys_addr(x) __phys_addr_nodebug(x) |
| #endif |
| |
| static inline unsigned long __phys_addr_symbol(unsigned long x) |
| { |
| unsigned long y = x - __START_KERNEL_map; |
| |
| /* only check upper bounds since lower bounds will trigger carry */ |
| VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE); |
| |
| return y + phys_base; |
| } |
| |
| #define __phys_reloc_hide(x) (x) |
| |
| void __clear_pages_unrolled(void *page); |
| KCFI_REFERENCE(__clear_pages_unrolled); |
| |
| /** |
| * clear_pages() - clear a page range using a kernel virtual address. |
| * @addr: start address of kernel page range |
| * @npages: number of pages |
| * |
| * Switch between three implementations of page clearing based on CPU |
| * capabilities: |
| * |
| * - __clear_pages_unrolled(): the oldest, slowest and universally |
| * supported method. Zeroes via 8-byte MOV instructions unrolled 8x |
| * to write a 64-byte cacheline in each loop iteration. |
| * |
| * - "REP; STOSQ": really old CPUs had crummy REP implementations. |
| * Vendor CPU setup code sets 'REP_GOOD' on CPUs where REP can be |
| * trusted. The instruction writes 8-byte per REP iteration but |
| * CPUs can internally batch these together and do larger writes. |
| * |
| * - "REP; STOSB": used on CPUs with "enhanced REP MOVSB/STOSB", |
| * which enumerate 'ERMS' and provide an implementation which |
| * unlike "REP; STOSQ" above wasn't overly picky about alignment. |
| * The instruction writes 1-byte per REP iteration with CPUs |
| * internally batching these together into larger writes and is |
| * generally fastest of the three. |
| * |
| * Note that when running as a guest, features exposed by the CPU |
| * might be mediated by the hypervisor. So, the STOSQ variant might |
| * be in active use on some systems even when the hardware enumerates |
| * ERMS. |
| * |
| * Does absolutely no exception handling. |
| */ |
| static inline void clear_pages(void *addr, unsigned int npages) |
| { |
| u64 len = npages * PAGE_SIZE; |
| /* |
| * Clean up KMSAN metadata for the pages being cleared. The assembly call |
| * below clobbers @addr, so perform unpoisoning before it. |
| */ |
| kmsan_unpoison_memory(addr, len); |
| |
| /* |
| * The inline asm embeds a CALL instruction and usually that is a no-no |
| * due to the compiler not knowing that and thus being unable to track |
| * callee-clobbered registers. |
| * |
| * In this case that is fine because the registers clobbered by |
| * __clear_pages_unrolled() are part of the inline asm register |
| * specification. |
| */ |
| asm volatile(ALTERNATIVE_2("call __clear_pages_unrolled", |
| "shrq $3, %%rcx; rep stosq", X86_FEATURE_REP_GOOD, |
| "rep stosb", X86_FEATURE_ERMS) |
| : "+c" (len), "+D" (addr), ASM_CALL_CONSTRAINT |
| : "a" (0) |
| : "cc", "memory"); |
| } |
| #define clear_pages clear_pages |
| |
| static inline void clear_page(void *addr) |
| { |
| clear_pages(addr, 1); |
| } |
| |
| void copy_page(void *to, void *from); |
| KCFI_REFERENCE(copy_page); |
| |
| /* |
| * User space process size. This is the first address outside the user range. |
| * There are a few constraints that determine this: |
| * |
| * On Intel CPUs, if a SYSCALL instruction is at the highest canonical |
| * address, then that syscall will enter the kernel with a |
| * non-canonical return address, and SYSRET will explode dangerously. |
| * We avoid this particular problem by preventing anything |
| * from being mapped at the maximum canonical address. |
| * |
| * On AMD CPUs in the Ryzen family, there's a nasty bug in which the |
| * CPUs malfunction if they execute code from the highest canonical page. |
| * They'll speculate right off the end of the canonical space, and |
| * bad things happen. This is worked around in the same way as the |
| * Intel problem. |
| * |
| * With page table isolation enabled, we map the LDT in ... [stay tuned] |
| */ |
| static __always_inline unsigned long task_size_max(void) |
| { |
| unsigned long ret; |
| |
| alternative_io("movq %[small],%0","movq %[large],%0", |
| X86_FEATURE_LA57, |
| "=r" (ret), |
| [small] "i" ((1ul << 47)-PAGE_SIZE), |
| [large] "i" ((1ul << 56)-PAGE_SIZE)); |
| |
| return ret; |
| } |
| |
| #endif /* !__ASSEMBLER__ */ |
| |
| #ifdef CONFIG_X86_VSYSCALL_EMULATION |
| # define __HAVE_ARCH_GATE_AREA 1 |
| #endif |
| |
| #endif /* _ASM_X86_PAGE_64_H */ |