18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_SPECIAL_INSNS_H
38c2ecf20Sopenharmony_ci#define _ASM_X86_SPECIAL_INSNS_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#ifdef __KERNEL__
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/nops.h>
98c2ecf20Sopenharmony_ci#include <asm/processor-flags.h>
108c2ecf20Sopenharmony_ci#include <linux/irqflags.h>
118c2ecf20Sopenharmony_ci#include <linux/jump_label.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * The compiler should not reorder volatile asm statements with respect to each
158c2ecf20Sopenharmony_ci * other: they should execute in program order. However GCC 4.9.x and 5.x have
168c2ecf20Sopenharmony_ci * a bug (which was fixed in 8.1, 7.3 and 6.5) where they might reorder
178c2ecf20Sopenharmony_ci * volatile asm. The write functions are not affected since they have memory
188c2ecf20Sopenharmony_ci * clobbers preventing reordering. To prevent reads from being reordered with
198c2ecf20Sopenharmony_ci * respect to writes, use a dummy memory operand.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define __FORCE_ORDER "m"(*(unsigned int *)0x1000UL)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_civoid native_write_cr0(unsigned long val);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic inline unsigned long native_read_cr0(void)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	unsigned long val;
298c2ecf20Sopenharmony_ci	asm volatile("mov %%cr0,%0\n\t" : "=r" (val) : __FORCE_ORDER);
308c2ecf20Sopenharmony_ci	return val;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic __always_inline unsigned long native_read_cr2(void)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	unsigned long val;
368c2ecf20Sopenharmony_ci	asm volatile("mov %%cr2,%0\n\t" : "=r" (val) : __FORCE_ORDER);
378c2ecf20Sopenharmony_ci	return val;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic __always_inline void native_write_cr2(unsigned long val)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	asm volatile("mov %0,%%cr2": : "r" (val) : "memory");
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline unsigned long __native_read_cr3(void)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	unsigned long val;
488c2ecf20Sopenharmony_ci	asm volatile("mov %%cr3,%0\n\t" : "=r" (val) : __FORCE_ORDER);
498c2ecf20Sopenharmony_ci	return val;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic inline void native_write_cr3(unsigned long val)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	asm volatile("mov %0,%%cr3": : "r" (val) : "memory");
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic inline unsigned long native_read_cr4(void)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned long val;
608c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
618c2ecf20Sopenharmony_ci	/*
628c2ecf20Sopenharmony_ci	 * This could fault if CR4 does not exist.  Non-existent CR4
638c2ecf20Sopenharmony_ci	 * is functionally equivalent to CR4 == 0.  Keep it simple and pretend
648c2ecf20Sopenharmony_ci	 * that CR4 == 0 on CPUs that don't have CR4.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci	asm volatile("1: mov %%cr4, %0\n"
678c2ecf20Sopenharmony_ci		     "2:\n"
688c2ecf20Sopenharmony_ci		     _ASM_EXTABLE(1b, 2b)
698c2ecf20Sopenharmony_ci		     : "=r" (val) : "0" (0), __FORCE_ORDER);
708c2ecf20Sopenharmony_ci#else
718c2ecf20Sopenharmony_ci	/* CR4 always exists on x86_64. */
728c2ecf20Sopenharmony_ci	asm volatile("mov %%cr4,%0\n\t" : "=r" (val) : __FORCE_ORDER);
738c2ecf20Sopenharmony_ci#endif
748c2ecf20Sopenharmony_ci	return val;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid native_write_cr4(unsigned long val);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
808c2ecf20Sopenharmony_cistatic inline u32 rdpkru(void)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	u32 ecx = 0;
838c2ecf20Sopenharmony_ci	u32 edx, pkru;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * "rdpkru" instruction.  Places PKRU contents in to EAX,
878c2ecf20Sopenharmony_ci	 * clears EDX and requires that ecx=0.
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	asm volatile(".byte 0x0f,0x01,0xee\n\t"
908c2ecf20Sopenharmony_ci		     : "=a" (pkru), "=d" (edx)
918c2ecf20Sopenharmony_ci		     : "c" (ecx));
928c2ecf20Sopenharmony_ci	return pkru;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic inline void wrpkru(u32 pkru)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u32 ecx = 0, edx = 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/*
1008c2ecf20Sopenharmony_ci	 * "wrpkru" instruction.  Loads contents in EAX to PKRU,
1018c2ecf20Sopenharmony_ci	 * requires that ecx = edx = 0.
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	asm volatile(".byte 0x0f,0x01,0xef\n\t"
1048c2ecf20Sopenharmony_ci		     : : "a" (pkru), "c"(ecx), "d"(edx));
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic inline void __write_pkru(u32 pkru)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	/*
1108c2ecf20Sopenharmony_ci	 * WRPKRU is relatively expensive compared to RDPKRU.
1118c2ecf20Sopenharmony_ci	 * Avoid WRPKRU when it would not change the value.
1128c2ecf20Sopenharmony_ci	 */
1138c2ecf20Sopenharmony_ci	if (pkru == rdpkru())
1148c2ecf20Sopenharmony_ci		return;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	wrpkru(pkru);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#else
1208c2ecf20Sopenharmony_cistatic inline u32 rdpkru(void)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline void __write_pkru(u32 pkru)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci#endif
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline void native_wbinvd(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	asm volatile("wbinvd": : :"memory");
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciextern asmlinkage void asm_load_gs_index(unsigned int selector);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic inline void native_load_gs_index(unsigned int selector)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	unsigned long flags;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	local_irq_save(flags);
1428c2ecf20Sopenharmony_ci	asm_load_gs_index(selector);
1438c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic inline unsigned long __read_cr4(void)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return native_read_cr4();
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci#ifdef CONFIG_PARAVIRT_XXL
1528c2ecf20Sopenharmony_ci#include <asm/paravirt.h>
1538c2ecf20Sopenharmony_ci#else
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic inline unsigned long read_cr0(void)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	return native_read_cr0();
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic inline void write_cr0(unsigned long x)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	native_write_cr0(x);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic __always_inline unsigned long read_cr2(void)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	return native_read_cr2();
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic __always_inline void write_cr2(unsigned long x)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	native_write_cr2(x);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * Careful!  CR3 contains more than just an address.  You probably want
1778c2ecf20Sopenharmony_ci * read_cr3_pa() instead.
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_cistatic inline unsigned long __read_cr3(void)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	return __native_read_cr3();
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic inline void write_cr3(unsigned long x)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	native_write_cr3(x);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic inline void __write_cr4(unsigned long x)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	native_write_cr4(x);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic inline void wbinvd(void)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	native_wbinvd();
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic inline void load_gs_index(unsigned int selector)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	native_load_gs_index(selector);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci#endif
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci#endif /* CONFIG_PARAVIRT_XXL */
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic inline void clflush(volatile void *__p)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	asm volatile("clflush %0" : "+m" (*(volatile char __force *)__p));
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic inline void clflushopt(volatile void *__p)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	alternative_io(".byte " __stringify(NOP_DS_PREFIX) "; clflush %P0",
2188c2ecf20Sopenharmony_ci		       ".byte 0x66; clflush %P0",
2198c2ecf20Sopenharmony_ci		       X86_FEATURE_CLFLUSHOPT,
2208c2ecf20Sopenharmony_ci		       "+m" (*(volatile char __force *)__p));
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic inline void clwb(volatile void *__p)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	volatile struct { char x[64]; } *p = __p;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	asm volatile(ALTERNATIVE_2(
2288c2ecf20Sopenharmony_ci		".byte " __stringify(NOP_DS_PREFIX) "; clflush (%[pax])",
2298c2ecf20Sopenharmony_ci		".byte 0x66; clflush (%[pax])", /* clflushopt (%%rax) */
2308c2ecf20Sopenharmony_ci		X86_FEATURE_CLFLUSHOPT,
2318c2ecf20Sopenharmony_ci		".byte 0x66, 0x0f, 0xae, 0x30",  /* clwb (%%rax) */
2328c2ecf20Sopenharmony_ci		X86_FEATURE_CLWB)
2338c2ecf20Sopenharmony_ci		: [p] "+m" (*p)
2348c2ecf20Sopenharmony_ci		: [pax] "a" (p));
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#define nop() asm volatile ("nop")
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic inline void serialize(void)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	/* Instruction opcode for SERIALIZE; supported in binutils >= 2.35. */
2428c2ecf20Sopenharmony_ci	asm volatile(".byte 0xf, 0x1, 0xe8" ::: "memory");
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/* The dst parameter must be 64-bytes aligned */
2468c2ecf20Sopenharmony_cistatic inline void movdir64b(void *dst, const void *src)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	const struct { char _[64]; } *__src = src;
2498c2ecf20Sopenharmony_ci	struct { char _[64]; } *__dst = dst;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/*
2528c2ecf20Sopenharmony_ci	 * MOVDIR64B %(rdx), rax.
2538c2ecf20Sopenharmony_ci	 *
2548c2ecf20Sopenharmony_ci	 * Both __src and __dst must be memory constraints in order to tell the
2558c2ecf20Sopenharmony_ci	 * compiler that no other memory accesses should be reordered around
2568c2ecf20Sopenharmony_ci	 * this one.
2578c2ecf20Sopenharmony_ci	 *
2588c2ecf20Sopenharmony_ci	 * Also, both must be supplied as lvalues because this tells
2598c2ecf20Sopenharmony_ci	 * the compiler what the object is (its size) the instruction accesses.
2608c2ecf20Sopenharmony_ci	 * I.e., not the pointers but what they point to, thus the deref'ing '*'.
2618c2ecf20Sopenharmony_ci	 */
2628c2ecf20Sopenharmony_ci	asm volatile(".byte 0x66, 0x0f, 0x38, 0xf8, 0x02"
2638c2ecf20Sopenharmony_ci		     : "+m" (*__dst)
2648c2ecf20Sopenharmony_ci		     :  "m" (*__src), "a" (__dst), "d" (__src));
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/**
2688c2ecf20Sopenharmony_ci * enqcmds - Enqueue a command in supervisor (CPL0) mode
2698c2ecf20Sopenharmony_ci * @dst: destination, in MMIO space (must be 512-bit aligned)
2708c2ecf20Sopenharmony_ci * @src: 512 bits memory operand
2718c2ecf20Sopenharmony_ci *
2728c2ecf20Sopenharmony_ci * The ENQCMDS instruction allows software to write a 512-bit command to
2738c2ecf20Sopenharmony_ci * a 512-bit-aligned special MMIO region that supports the instruction.
2748c2ecf20Sopenharmony_ci * A return status is loaded into the ZF flag in the RFLAGS register.
2758c2ecf20Sopenharmony_ci * ZF = 0 equates to success, and ZF = 1 indicates retry or error.
2768c2ecf20Sopenharmony_ci *
2778c2ecf20Sopenharmony_ci * This function issues the ENQCMDS instruction to submit data from
2788c2ecf20Sopenharmony_ci * kernel space to MMIO space, in a unit of 512 bits. Order of data access
2798c2ecf20Sopenharmony_ci * is not guaranteed, nor is a memory barrier performed afterwards. It
2808c2ecf20Sopenharmony_ci * returns 0 on success and -EAGAIN on failure.
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * Warning: Do not use this helper unless your driver has checked that the
2838c2ecf20Sopenharmony_ci * ENQCMDS instruction is supported on the platform and the device accepts
2848c2ecf20Sopenharmony_ci * ENQCMDS.
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_cistatic inline int enqcmds(void __iomem *dst, const void *src)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	const struct { char _[64]; } *__src = src;
2898c2ecf20Sopenharmony_ci	struct { char _[64]; } __iomem *__dst = dst;
2908c2ecf20Sopenharmony_ci	bool zf;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * ENQCMDS %(rdx), rax
2948c2ecf20Sopenharmony_ci	 *
2958c2ecf20Sopenharmony_ci	 * See movdir64b()'s comment on operand specification.
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	asm volatile(".byte 0xf3, 0x0f, 0x38, 0xf8, 0x02, 0x66, 0x90"
2988c2ecf20Sopenharmony_ci		     CC_SET(z)
2998c2ecf20Sopenharmony_ci		     : CC_OUT(z) (zf), "+m" (*__dst)
3008c2ecf20Sopenharmony_ci		     : "m" (*__src), "a" (__dst), "d" (__src));
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Submission failure is indicated via EFLAGS.ZF=1 */
3038c2ecf20Sopenharmony_ci	if (zf)
3048c2ecf20Sopenharmony_ci		return -EAGAIN;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return 0;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci#endif /* __KERNEL__ */
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci#endif /* _ASM_X86_SPECIAL_INSNS_H */
312