Skip to content

Commit 5105e76

Browse files
xinli-intelbp3tk0v
authored andcommitted
x86/fred: Fixup fault on ERETU by jumping to fred_entrypoint_user
If the stack frame contains an invalid user context (e.g. due to invalid SS, a non-canonical RIP, etc.) the ERETU instruction will trap (#SS or #GP). From a Linux point of view, this really should be considered a user space failure, so use the standard fault fixup mechanism to intercept the fault, fix up the exception frame, and redirect execution to fred_entrypoint_user. The end result is that it appears just as if the hardware had taken the exception immediately after completing the transition to user space. Suggested-by: H. Peter Anvin (Intel) <hpa@zytor.com> Signed-off-by: Xin Li <xin3.li@intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Tested-by: Shan Kang <shan.kang@intel.com> Link: https://lore.kernel.org/r/20231205105030.8698-30-xin3.li@intel.com
1 parent 51ef2a4 commit 5105e76

3 files changed

Lines changed: 85 additions & 2 deletions

File tree

arch/x86/entry/entry_64_fred.S

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* The actual FRED entry points.
44
*/
55

6+
#include <asm/asm.h>
67
#include <asm/fred.h>
78

89
#include "calling.h"
@@ -34,7 +35,9 @@ SYM_CODE_START_NOALIGN(asm_fred_entrypoint_user)
3435
call fred_entry_from_user
3536
SYM_INNER_LABEL(asm_fred_exit_user, SYM_L_GLOBAL)
3637
FRED_EXIT
37-
ERETU
38+
1: ERETU
39+
40+
_ASM_EXTABLE_TYPE(1b, asm_fred_entrypoint_user, EX_TYPE_ERETU)
3841
SYM_CODE_END(asm_fred_entrypoint_user)
3942

4043
/*

arch/x86/include/asm/extable_fixup_types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
#define EX_TYPE_UCOPY_LEN4 (EX_TYPE_UCOPY_LEN | EX_DATA_IMM(4))
6565
#define EX_TYPE_UCOPY_LEN8 (EX_TYPE_UCOPY_LEN | EX_DATA_IMM(8))
6666

67-
#define EX_TYPE_ZEROPAD 20 /* longword load with zeropad on fault */
67+
#define EX_TYPE_ZEROPAD 20 /* longword load with zeropad on fault */
68+
69+
#define EX_TYPE_ERETU 21
6870

6971
#endif

arch/x86/mm/extable.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <xen/xen.h>
77

88
#include <asm/fpu/api.h>
9+
#include <asm/fred.h>
910
#include <asm/sev.h>
1011
#include <asm/traps.h>
1112
#include <asm/kdebug.h>
@@ -223,6 +224,79 @@ static bool ex_handler_ucopy_len(const struct exception_table_entry *fixup,
223224
return ex_handler_uaccess(fixup, regs, trapnr, fault_address);
224225
}
225226

227+
#ifdef CONFIG_X86_FRED
228+
static bool ex_handler_eretu(const struct exception_table_entry *fixup,
229+
struct pt_regs *regs, unsigned long error_code)
230+
{
231+
struct pt_regs *uregs = (struct pt_regs *)(regs->sp - offsetof(struct pt_regs, orig_ax));
232+
unsigned short ss = uregs->ss;
233+
unsigned short cs = uregs->cs;
234+
235+
/*
236+
* Move the NMI bit from the invalid stack frame, which caused ERETU
237+
* to fault, to the fault handler's stack frame, thus to unblock NMI
238+
* with the fault handler's ERETS instruction ASAP if NMI is blocked.
239+
*/
240+
regs->fred_ss.nmi = uregs->fred_ss.nmi;
241+
242+
/*
243+
* Sync event information to uregs, i.e., the ERETU return frame, but
244+
* is it safe to write to the ERETU return frame which is just above
245+
* current event stack frame?
246+
*
247+
* The RSP used by FRED to push a stack frame is not the value in %rsp,
248+
* it is calculated from %rsp with the following 2 steps:
249+
* 1) RSP = %rsp - (IA32_FRED_CONFIG & 0x1c0) // Reserve N*64 bytes
250+
* 2) RSP = RSP & ~0x3f // Align to a 64-byte cache line
251+
* when an event delivery doesn't trigger a stack level change.
252+
*
253+
* Here is an example with N*64 (N=1) bytes reserved:
254+
*
255+
* 64-byte cache line ==> ______________
256+
* |___Reserved___|
257+
* |__Event_data__|
258+
* |_____SS_______|
259+
* |_____RSP______|
260+
* |_____FLAGS____|
261+
* |_____CS_______|
262+
* |_____IP_______|
263+
* 64-byte cache line ==> |__Error_code__| <== ERETU return frame
264+
* |______________|
265+
* |______________|
266+
* |______________|
267+
* |______________|
268+
* |______________|
269+
* |______________|
270+
* |______________|
271+
* 64-byte cache line ==> |______________| <== RSP after step 1) and 2)
272+
* |___Reserved___|
273+
* |__Event_data__|
274+
* |_____SS_______|
275+
* |_____RSP______|
276+
* |_____FLAGS____|
277+
* |_____CS_______|
278+
* |_____IP_______|
279+
* 64-byte cache line ==> |__Error_code__| <== ERETS return frame
280+
*
281+
* Thus a new FRED stack frame will always be pushed below a previous
282+
* FRED stack frame ((N*64) bytes may be reserved between), and it is
283+
* safe to write to a previous FRED stack frame as they never overlap.
284+
*/
285+
fred_info(uregs)->edata = fred_event_data(regs);
286+
uregs->ssx = regs->ssx;
287+
uregs->fred_ss.ss = ss;
288+
/* The NMI bit was moved away above */
289+
uregs->fred_ss.nmi = 0;
290+
uregs->csx = regs->csx;
291+
uregs->fred_cs.sl = 0;
292+
uregs->fred_cs.wfe = 0;
293+
uregs->cs = cs;
294+
uregs->orig_ax = error_code;
295+
296+
return ex_handler_default(fixup, regs);
297+
}
298+
#endif
299+
226300
int ex_get_fixup_type(unsigned long ip)
227301
{
228302
const struct exception_table_entry *e = search_exception_tables(ip);
@@ -300,6 +374,10 @@ int fixup_exception(struct pt_regs *regs, int trapnr, unsigned long error_code,
300374
return ex_handler_ucopy_len(e, regs, trapnr, fault_addr, reg, imm);
301375
case EX_TYPE_ZEROPAD:
302376
return ex_handler_zeropad(e, regs, fault_addr);
377+
#ifdef CONFIG_X86_FRED
378+
case EX_TYPE_ERETU:
379+
return ex_handler_eretu(e, regs, error_code);
380+
#endif
303381
}
304382
BUG();
305383
}

0 commit comments

Comments
 (0)