18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_DEBUGREG_H 38c2ecf20Sopenharmony_ci#define _ASM_X86_DEBUGREG_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bug.h> 78c2ecf20Sopenharmony_ci#include <uapi/asm/debugreg.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ciDECLARE_PER_CPU(unsigned long, cpu_dr7); 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#ifndef CONFIG_PARAVIRT_XXL 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * These special macros can be used to get or set a debugging register 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci#define get_debugreg(var, register) \ 168c2ecf20Sopenharmony_ci (var) = native_get_debugreg(register) 178c2ecf20Sopenharmony_ci#define set_debugreg(value, register) \ 188c2ecf20Sopenharmony_ci native_set_debugreg(register, value) 198c2ecf20Sopenharmony_ci#endif 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic __always_inline unsigned long native_get_debugreg(int regno) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci unsigned long val = 0; /* Damn you, gcc! */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci switch (regno) { 268c2ecf20Sopenharmony_ci case 0: 278c2ecf20Sopenharmony_ci asm("mov %%db0, %0" :"=r" (val)); 288c2ecf20Sopenharmony_ci break; 298c2ecf20Sopenharmony_ci case 1: 308c2ecf20Sopenharmony_ci asm("mov %%db1, %0" :"=r" (val)); 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci case 2: 338c2ecf20Sopenharmony_ci asm("mov %%db2, %0" :"=r" (val)); 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci case 3: 368c2ecf20Sopenharmony_ci asm("mov %%db3, %0" :"=r" (val)); 378c2ecf20Sopenharmony_ci break; 388c2ecf20Sopenharmony_ci case 6: 398c2ecf20Sopenharmony_ci asm("mov %%db6, %0" :"=r" (val)); 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case 7: 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * Apply __FORCE_ORDER to DR7 reads to forbid re-ordering them 448c2ecf20Sopenharmony_ci * with other code. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * This is needed because a DR7 access can cause a #VC exception 478c2ecf20Sopenharmony_ci * when running under SEV-ES. Taking a #VC exception is not a 488c2ecf20Sopenharmony_ci * safe thing to do just anywhere in the entry code and 498c2ecf20Sopenharmony_ci * re-ordering might place the access into an unsafe location. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * This happened in the NMI handler, where the DR7 read was 528c2ecf20Sopenharmony_ci * re-ordered to happen before the call to sev_es_ist_enter(), 538c2ecf20Sopenharmony_ci * causing stack recursion. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci asm volatile("mov %%db7, %0" : "=r" (val) : __FORCE_ORDER); 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci default: 588c2ecf20Sopenharmony_ci BUG(); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci return val; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic __always_inline void native_set_debugreg(int regno, unsigned long value) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci switch (regno) { 668c2ecf20Sopenharmony_ci case 0: 678c2ecf20Sopenharmony_ci asm("mov %0, %%db0" ::"r" (value)); 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci case 1: 708c2ecf20Sopenharmony_ci asm("mov %0, %%db1" ::"r" (value)); 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci case 2: 738c2ecf20Sopenharmony_ci asm("mov %0, %%db2" ::"r" (value)); 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case 3: 768c2ecf20Sopenharmony_ci asm("mov %0, %%db3" ::"r" (value)); 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case 6: 798c2ecf20Sopenharmony_ci asm("mov %0, %%db6" ::"r" (value)); 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci case 7: 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * Apply __FORCE_ORDER to DR7 writes to forbid re-ordering them 848c2ecf20Sopenharmony_ci * with other code. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * While is didn't happen with a DR7 write (see the DR7 read 878c2ecf20Sopenharmony_ci * comment above which explains where it happened), add the 888c2ecf20Sopenharmony_ci * __FORCE_ORDER here too to avoid similar problems in the 898c2ecf20Sopenharmony_ci * future. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci asm volatile("mov %0, %%db7" ::"r" (value), __FORCE_ORDER); 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci default: 948c2ecf20Sopenharmony_ci BUG(); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline void hw_breakpoint_disable(void) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci /* Zero the control register for HW Breakpoint */ 1018c2ecf20Sopenharmony_ci set_debugreg(0UL, 7); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Zero-out the individual HW breakpoint address registers */ 1048c2ecf20Sopenharmony_ci set_debugreg(0UL, 0); 1058c2ecf20Sopenharmony_ci set_debugreg(0UL, 1); 1068c2ecf20Sopenharmony_ci set_debugreg(0UL, 2); 1078c2ecf20Sopenharmony_ci set_debugreg(0UL, 3); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic __always_inline bool hw_breakpoint_active(void) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return __this_cpu_read(cpu_dr7) & DR_GLOBAL_ENABLE_MASK; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciextern void hw_breakpoint_restore(void); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic __always_inline unsigned long local_db_save(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci unsigned long dr7; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (static_cpu_has(X86_FEATURE_HYPERVISOR) && !hw_breakpoint_active()) 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci get_debugreg(dr7, 7); 1258c2ecf20Sopenharmony_ci dr7 &= ~0x400; /* architecturally set bit */ 1268c2ecf20Sopenharmony_ci if (dr7) 1278c2ecf20Sopenharmony_ci set_debugreg(0, 7); 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Ensure the compiler doesn't lower the above statements into 1308c2ecf20Sopenharmony_ci * the critical section; disabling breakpoints late would not 1318c2ecf20Sopenharmony_ci * be good. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci barrier(); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return dr7; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic __always_inline void local_db_restore(unsigned long dr7) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * Ensure the compiler doesn't raise this statement into 1428c2ecf20Sopenharmony_ci * the critical section; enabling breakpoints early would 1438c2ecf20Sopenharmony_ci * not be good. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci barrier(); 1468c2ecf20Sopenharmony_ci if (dr7) 1478c2ecf20Sopenharmony_ci set_debugreg(dr7, 7); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_SUP_AMD 1518c2ecf20Sopenharmony_ciextern void set_dr_addr_mask(unsigned long mask, int dr); 1528c2ecf20Sopenharmony_ci#else 1538c2ecf20Sopenharmony_cistatic inline void set_dr_addr_mask(unsigned long mask, int dr) { } 1548c2ecf20Sopenharmony_ci#endif 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#endif /* _ASM_X86_DEBUGREG_H */ 157