162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_X86_DEBUGREG_H
362306a36Sopenharmony_ci#define _ASM_X86_DEBUGREG_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/bug.h>
662306a36Sopenharmony_ci#include <linux/percpu.h>
762306a36Sopenharmony_ci#include <uapi/asm/debugreg.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ciDECLARE_PER_CPU(unsigned long, cpu_dr7);
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#ifndef CONFIG_PARAVIRT_XXL
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * These special macros can be used to get or set a debugging register
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci#define get_debugreg(var, register)				\
1662306a36Sopenharmony_ci	(var) = native_get_debugreg(register)
1762306a36Sopenharmony_ci#define set_debugreg(value, register)				\
1862306a36Sopenharmony_ci	native_set_debugreg(register, value)
1962306a36Sopenharmony_ci#endif
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic __always_inline unsigned long native_get_debugreg(int regno)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	unsigned long val = 0;	/* Damn you, gcc! */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	switch (regno) {
2662306a36Sopenharmony_ci	case 0:
2762306a36Sopenharmony_ci		asm("mov %%db0, %0" :"=r" (val));
2862306a36Sopenharmony_ci		break;
2962306a36Sopenharmony_ci	case 1:
3062306a36Sopenharmony_ci		asm("mov %%db1, %0" :"=r" (val));
3162306a36Sopenharmony_ci		break;
3262306a36Sopenharmony_ci	case 2:
3362306a36Sopenharmony_ci		asm("mov %%db2, %0" :"=r" (val));
3462306a36Sopenharmony_ci		break;
3562306a36Sopenharmony_ci	case 3:
3662306a36Sopenharmony_ci		asm("mov %%db3, %0" :"=r" (val));
3762306a36Sopenharmony_ci		break;
3862306a36Sopenharmony_ci	case 6:
3962306a36Sopenharmony_ci		asm("mov %%db6, %0" :"=r" (val));
4062306a36Sopenharmony_ci		break;
4162306a36Sopenharmony_ci	case 7:
4262306a36Sopenharmony_ci		/*
4362306a36Sopenharmony_ci		 * Apply __FORCE_ORDER to DR7 reads to forbid re-ordering them
4462306a36Sopenharmony_ci		 * with other code.
4562306a36Sopenharmony_ci		 *
4662306a36Sopenharmony_ci		 * This is needed because a DR7 access can cause a #VC exception
4762306a36Sopenharmony_ci		 * when running under SEV-ES. Taking a #VC exception is not a
4862306a36Sopenharmony_ci		 * safe thing to do just anywhere in the entry code and
4962306a36Sopenharmony_ci		 * re-ordering might place the access into an unsafe location.
5062306a36Sopenharmony_ci		 *
5162306a36Sopenharmony_ci		 * This happened in the NMI handler, where the DR7 read was
5262306a36Sopenharmony_ci		 * re-ordered to happen before the call to sev_es_ist_enter(),
5362306a36Sopenharmony_ci		 * causing stack recursion.
5462306a36Sopenharmony_ci		 */
5562306a36Sopenharmony_ci		asm volatile("mov %%db7, %0" : "=r" (val) : __FORCE_ORDER);
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	default:
5862306a36Sopenharmony_ci		BUG();
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	return val;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic __always_inline void native_set_debugreg(int regno, unsigned long value)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	switch (regno) {
6662306a36Sopenharmony_ci	case 0:
6762306a36Sopenharmony_ci		asm("mov %0, %%db0"	::"r" (value));
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	case 1:
7062306a36Sopenharmony_ci		asm("mov %0, %%db1"	::"r" (value));
7162306a36Sopenharmony_ci		break;
7262306a36Sopenharmony_ci	case 2:
7362306a36Sopenharmony_ci		asm("mov %0, %%db2"	::"r" (value));
7462306a36Sopenharmony_ci		break;
7562306a36Sopenharmony_ci	case 3:
7662306a36Sopenharmony_ci		asm("mov %0, %%db3"	::"r" (value));
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	case 6:
7962306a36Sopenharmony_ci		asm("mov %0, %%db6"	::"r" (value));
8062306a36Sopenharmony_ci		break;
8162306a36Sopenharmony_ci	case 7:
8262306a36Sopenharmony_ci		/*
8362306a36Sopenharmony_ci		 * Apply __FORCE_ORDER to DR7 writes to forbid re-ordering them
8462306a36Sopenharmony_ci		 * with other code.
8562306a36Sopenharmony_ci		 *
8662306a36Sopenharmony_ci		 * While is didn't happen with a DR7 write (see the DR7 read
8762306a36Sopenharmony_ci		 * comment above which explains where it happened), add the
8862306a36Sopenharmony_ci		 * __FORCE_ORDER here too to avoid similar problems in the
8962306a36Sopenharmony_ci		 * future.
9062306a36Sopenharmony_ci		 */
9162306a36Sopenharmony_ci		asm volatile("mov %0, %%db7"	::"r" (value), __FORCE_ORDER);
9262306a36Sopenharmony_ci		break;
9362306a36Sopenharmony_ci	default:
9462306a36Sopenharmony_ci		BUG();
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic inline void hw_breakpoint_disable(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	/* Zero the control register for HW Breakpoint */
10162306a36Sopenharmony_ci	set_debugreg(0UL, 7);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Zero-out the individual HW breakpoint address registers */
10462306a36Sopenharmony_ci	set_debugreg(0UL, 0);
10562306a36Sopenharmony_ci	set_debugreg(0UL, 1);
10662306a36Sopenharmony_ci	set_debugreg(0UL, 2);
10762306a36Sopenharmony_ci	set_debugreg(0UL, 3);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic __always_inline bool hw_breakpoint_active(void)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	return __this_cpu_read(cpu_dr7) & DR_GLOBAL_ENABLE_MASK;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciextern void hw_breakpoint_restore(void);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic __always_inline unsigned long local_db_save(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	unsigned long dr7;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (static_cpu_has(X86_FEATURE_HYPERVISOR) && !hw_breakpoint_active())
12262306a36Sopenharmony_ci		return 0;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	get_debugreg(dr7, 7);
12562306a36Sopenharmony_ci	dr7 &= ~0x400; /* architecturally set bit */
12662306a36Sopenharmony_ci	if (dr7)
12762306a36Sopenharmony_ci		set_debugreg(0, 7);
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * Ensure the compiler doesn't lower the above statements into
13062306a36Sopenharmony_ci	 * the critical section; disabling breakpoints late would not
13162306a36Sopenharmony_ci	 * be good.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	barrier();
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return dr7;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic __always_inline void local_db_restore(unsigned long dr7)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * Ensure the compiler doesn't raise this statement into
14262306a36Sopenharmony_ci	 * the critical section; enabling breakpoints early would
14362306a36Sopenharmony_ci	 * not be good.
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci	barrier();
14662306a36Sopenharmony_ci	if (dr7)
14762306a36Sopenharmony_ci		set_debugreg(dr7, 7);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#ifdef CONFIG_CPU_SUP_AMD
15162306a36Sopenharmony_ciextern void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr);
15262306a36Sopenharmony_ciextern unsigned long amd_get_dr_addr_mask(unsigned int dr);
15362306a36Sopenharmony_ci#else
15462306a36Sopenharmony_cistatic inline void amd_set_dr_addr_mask(unsigned long mask, unsigned int dr) { }
15562306a36Sopenharmony_cistatic inline unsigned long amd_get_dr_addr_mask(unsigned int dr)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci#endif
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#endif /* _ASM_X86_DEBUGREG_H */
162