mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'x86-fred-2024-03-10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 FRED support from Thomas Gleixner:
"Support for x86 Fast Return and Event Delivery (FRED).
FRED is a replacement for IDT event delivery on x86 and addresses most
of the technical nightmares which IDT exposes:
1) Exception cause registers like CR2 need to be manually preserved
in nested exception scenarios.
2) Hardware interrupt stack switching is suboptimal for nested
exceptions as the interrupt stack mechanism rewinds the stack on
each entry which requires a massive effort in the low level entry
of #NMI code to handle this.
3) No hardware distinction between entry from kernel or from user
which makes establishing kernel context more complex than it needs
to be especially for unconditionally nestable exceptions like NMI.
4) NMI nesting caused by IRET unconditionally reenabling NMIs, which
is a problem when the perf NMI takes a fault when collecting a
stack trace.
5) Partial restore of ESP when returning to a 16-bit segment
6) Limitation of the vector space which can cause vector exhaustion
on large systems.
7) Inability to differentiate NMI sources
FRED addresses these shortcomings by:
1) An extended exception stack frame which the CPU uses to save
exception cause registers. This ensures that the meta information
for each exception is preserved on stack and avoids the extra
complexity of preserving it in software.
2) Hardware interrupt stack switching is non-rewinding if a nested
exception uses the currently interrupt stack.
3) The entry points for kernel and user context are separate and GS
BASE handling which is required to establish kernel context for
per CPU variable access is done in hardware.
4) NMIs are now nesting protected. They are only reenabled on the
return from NMI.
5) FRED guarantees full restore of ESP
6) FRED does not put a limitation on the vector space by design
because it uses a central entry points for kernel and user space
and the CPUstores the entry type (exception, trap, interrupt,
syscall) on the entry stack along with the vector number. The
entry code has to demultiplex this information, but this removes
the vector space restriction.
The first hardware implementations will still have the current
restricted vector space because lifting this limitation requires
further changes to the local APIC.
7) FRED stores the vector number and meta information on stack which
allows having more than one NMI vector in future hardware when the
required local APIC changes are in place.
The series implements the initial FRED support by:
- Reworking the existing entry and IDT handling infrastructure to
accomodate for the alternative entry mechanism.
- Expanding the stack frame to accomodate for the extra 16 bytes FRED
requires to store context and meta information
- Providing FRED specific C entry points for events which have
information pushed to the extended stack frame, e.g. #PF and #DB.
- Providing FRED specific C entry points for #NMI and #MCE
- Implementing the FRED specific ASM entry points and the C code to
demultiplex the events
- Providing detection and initialization mechanisms and the necessary
tweaks in context switching, GS BASE handling etc.
The FRED integration aims for maximum code reuse vs the existing IDT
implementation to the extent possible and the deviation in hot paths
like context switching are handled with alternatives to minimalize the
impact. The low level entry and exit paths are seperate due to the
extended stack frame and the hardware based GS BASE swichting and
therefore have no impact on IDT based systems.
It has been extensively tested on existing systems and on the FRED
simulation and as of now there are no outstanding problems"
* tag 'x86-fred-2024-03-10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (38 commits)
x86/fred: Fix init_task thread stack pointer initialization
MAINTAINERS: Add a maintainer entry for FRED
x86/fred: Fix a build warning with allmodconfig due to 'inline' failing to inline properly
x86/fred: Invoke FRED initialization code to enable FRED
x86/fred: Add FRED initialization functions
x86/syscall: Split IDT syscall setup code into idt_syscall_init()
KVM: VMX: Call fred_entry_from_kvm() for IRQ/NMI handling
x86/entry: Add fred_entry_from_kvm() for VMX to handle IRQ/NMI
x86/entry/calling: Allow PUSH_AND_CLEAR_REGS being used beyond actual entry code
x86/fred: Fixup fault on ERETU by jumping to fred_entrypoint_user
x86/fred: Let ret_from_fork_asm() jmp to asm_fred_exit_user when FRED is enabled
x86/traps: Add sysvec_install() to install a system interrupt handler
x86/fred: FRED entry/exit and dispatch code
x86/fred: Add a machine check entry stub for FRED
x86/fred: Add a NMI entry stub for FRED
x86/fred: Add a debug fault entry stub for FRED
x86/idtentry: Incorporate definitions/declarations of the FRED entries
x86/fred: Make exc_page_fault() work for FRED
x86/fred: Allow single-step trap and NMI when starting a new task
x86/fred: No ESPFIX needed when FRED is enabled
...
This commit is contained in:
@@ -1525,6 +1525,12 @@
|
||||
Warning: use of this parameter will taint the kernel
|
||||
and may cause unknown problems.
|
||||
|
||||
fred= [X86-64]
|
||||
Enable/disable Flexible Return and Event Delivery.
|
||||
Format: { on | off }
|
||||
on: enable FRED when it's present.
|
||||
off: disable FRED, the default setting.
|
||||
|
||||
ftrace=[tracer]
|
||||
[FTRACE] will set and start the specified tracer
|
||||
as early as possible in order to facilitate early
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=========================================
|
||||
Flexible Return and Event Delivery (FRED)
|
||||
=========================================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The FRED architecture defines simple new transitions that change
|
||||
privilege level (ring transitions). The FRED architecture was
|
||||
designed with the following goals:
|
||||
|
||||
1) Improve overall performance and response time by replacing event
|
||||
delivery through the interrupt descriptor table (IDT event
|
||||
delivery) and event return by the IRET instruction with lower
|
||||
latency transitions.
|
||||
|
||||
2) Improve software robustness by ensuring that event delivery
|
||||
establishes the full supervisor context and that event return
|
||||
establishes the full user context.
|
||||
|
||||
The new transitions defined by the FRED architecture are FRED event
|
||||
delivery and, for returning from events, two FRED return instructions.
|
||||
FRED event delivery can effect a transition from ring 3 to ring 0, but
|
||||
it is used also to deliver events incident to ring 0. One FRED
|
||||
instruction (ERETU) effects a return from ring 0 to ring 3, while the
|
||||
other (ERETS) returns while remaining in ring 0. Collectively, FRED
|
||||
event delivery and the FRED return instructions are FRED transitions.
|
||||
|
||||
In addition to these transitions, the FRED architecture defines a new
|
||||
instruction (LKGS) for managing the state of the GS segment register.
|
||||
The LKGS instruction can be used by 64-bit operating systems that do
|
||||
not use the new FRED transitions.
|
||||
|
||||
Furthermore, the FRED architecture is easy to extend for future CPU
|
||||
architectures.
|
||||
|
||||
Software based event dispatching
|
||||
================================
|
||||
|
||||
FRED operates differently from IDT in terms of event handling. Instead
|
||||
of directly dispatching an event to its handler based on the event
|
||||
vector, FRED requires the software to dispatch an event to its handler
|
||||
based on both the event's type and vector. Therefore, an event dispatch
|
||||
framework must be implemented to facilitate the event-to-handler
|
||||
dispatch process. The FRED event dispatch framework takes control
|
||||
once an event is delivered, and employs a two-level dispatch.
|
||||
|
||||
The first level dispatching is event type based, and the second level
|
||||
dispatching is event vector based.
|
||||
|
||||
Full supervisor/user context
|
||||
============================
|
||||
|
||||
FRED event delivery atomically save and restore full supervisor/user
|
||||
context upon event delivery and return. Thus it avoids the problem of
|
||||
transient states due to %cr2 and/or %dr6, and it is no longer needed
|
||||
to handle all the ugly corner cases caused by half baked entry states.
|
||||
|
||||
FRED allows explicit unblock of NMI with new event return instructions
|
||||
ERETS/ERETU, avoiding the mess caused by IRET which unconditionally
|
||||
unblocks NMI, e.g., when an exception happens during NMI handling.
|
||||
|
||||
FRED always restores the full value of %rsp, thus ESPFIX is no longer
|
||||
needed when FRED is enabled.
|
||||
|
||||
LKGS
|
||||
====
|
||||
|
||||
LKGS behaves like the MOV to GS instruction except that it loads the
|
||||
base address into the IA32_KERNEL_GS_BASE MSR instead of the GS
|
||||
segment’s descriptor cache. With LKGS, it ends up with avoiding
|
||||
mucking with kernel GS, i.e., an operating system can always operate
|
||||
with its own GS base address.
|
||||
|
||||
Because FRED event delivery from ring 3 and ERETU both swap the value
|
||||
of the GS base address and that of the IA32_KERNEL_GS_BASE MSR, plus
|
||||
the introduction of LKGS instruction, the SWAPGS instruction is no
|
||||
longer needed when FRED is enabled, thus is disallowed (#UD).
|
||||
|
||||
Stack levels
|
||||
============
|
||||
|
||||
4 stack levels 0~3 are introduced to replace the nonreentrant IST for
|
||||
event handling, and each stack level should be configured to use a
|
||||
dedicated stack.
|
||||
|
||||
The current stack level could be unchanged or go higher upon FRED
|
||||
event delivery. If unchanged, the CPU keeps using the current event
|
||||
stack. If higher, the CPU switches to a new event stack specified by
|
||||
the MSR of the new stack level, i.e., MSR_IA32_FRED_RSP[123].
|
||||
|
||||
Only execution of a FRED return instruction ERET[US], could lower the
|
||||
current stack level, causing the CPU to switch back to the stack it was
|
||||
on before a previous event delivery that promoted the stack level.
|
||||
@@ -15,3 +15,4 @@ x86_64 Support
|
||||
cpu-hotplug-spec
|
||||
machinecheck
|
||||
fsgs
|
||||
fred
|
||||
|
||||
+10
@@ -11157,6 +11157,16 @@ L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/wwan/iosm/
|
||||
|
||||
INTEL(R) FLEXIBLE RETURN AND EVENT DELIVERY
|
||||
M: Xin Li <xin@zytor.com>
|
||||
M: "H. Peter Anvin" <hpa@zytor.com>
|
||||
S: Supported
|
||||
F: Documentation/arch/x86/x86_64/fred.rst
|
||||
F: arch/x86/entry/entry_64_fred.S
|
||||
F: arch/x86/entry/entry_fred.c
|
||||
F: arch/x86/include/asm/fred.h
|
||||
F: arch/x86/kernel/fred.c
|
||||
|
||||
INTEL(R) TRACE HUB
|
||||
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
|
||||
S: Supported
|
||||
|
||||
@@ -496,6 +496,15 @@ config X86_CPU_RESCTRL
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config X86_FRED
|
||||
bool "Flexible Return and Event Delivery"
|
||||
depends on X86_64
|
||||
help
|
||||
When enabled, try to use Flexible Return and Event Delivery
|
||||
instead of the legacy SYSCALL/SYSENTER/IDT architecture for
|
||||
ring transitions and exception/interrupt handling if the
|
||||
system supports.
|
||||
|
||||
if X86_32
|
||||
config X86_BIGSMP
|
||||
bool "Support for big SMP systems with more than 8 CPUs"
|
||||
|
||||
@@ -18,6 +18,9 @@ obj-y += vdso/
|
||||
obj-y += vsyscall/
|
||||
|
||||
obj-$(CONFIG_PREEMPTION) += thunk_$(BITS).o
|
||||
CFLAGS_entry_fred.o += -fno-stack-protector
|
||||
CFLAGS_REMOVE_entry_fred.o += -pg $(CC_FLAGS_FTRACE)
|
||||
obj-$(CONFIG_X86_FRED) += entry_64_fred.o entry_fred.o
|
||||
|
||||
obj-$(CONFIG_IA32_EMULATION) += entry_64_compat.o syscall_32.o
|
||||
obj-$(CONFIG_X86_X32_ABI) += syscall_x32.o
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ For 32-bit we have the following conventions - kernel is built with
|
||||
* for assembly code:
|
||||
*/
|
||||
|
||||
.macro PUSH_REGS rdx=%rdx rcx=%rcx rax=%rax save_ret=0
|
||||
.macro PUSH_REGS rdx=%rdx rcx=%rcx rax=%rax save_ret=0 unwind_hint=1
|
||||
.if \save_ret
|
||||
pushq %rsi /* pt_regs->si */
|
||||
movq 8(%rsp), %rsi /* temporarily store the return address in %rsi */
|
||||
@@ -87,14 +87,17 @@ For 32-bit we have the following conventions - kernel is built with
|
||||
pushq %r13 /* pt_regs->r13 */
|
||||
pushq %r14 /* pt_regs->r14 */
|
||||
pushq %r15 /* pt_regs->r15 */
|
||||
|
||||
.if \unwind_hint
|
||||
UNWIND_HINT_REGS
|
||||
.endif
|
||||
|
||||
.if \save_ret
|
||||
pushq %rsi /* return address on top of stack */
|
||||
.endif
|
||||
.endm
|
||||
|
||||
.macro CLEAR_REGS
|
||||
.macro CLEAR_REGS clear_bp=1
|
||||
/*
|
||||
* Sanitize registers of values that a speculation attack might
|
||||
* otherwise want to exploit. The lower registers are likely clobbered
|
||||
@@ -109,7 +112,9 @@ For 32-bit we have the following conventions - kernel is built with
|
||||
xorl %r10d, %r10d /* nospec r10 */
|
||||
xorl %r11d, %r11d /* nospec r11 */
|
||||
xorl %ebx, %ebx /* nospec rbx */
|
||||
.if \clear_bp
|
||||
xorl %ebp, %ebp /* nospec rbp */
|
||||
.endif
|
||||
xorl %r12d, %r12d /* nospec r12 */
|
||||
xorl %r13d, %r13d /* nospec r13 */
|
||||
xorl %r14d, %r14d /* nospec r14 */
|
||||
@@ -117,9 +122,9 @@ For 32-bit we have the following conventions - kernel is built with
|
||||
|
||||
.endm
|
||||
|
||||
.macro PUSH_AND_CLEAR_REGS rdx=%rdx rcx=%rcx rax=%rax save_ret=0
|
||||
PUSH_REGS rdx=\rdx, rcx=\rcx, rax=\rax, save_ret=\save_ret
|
||||
CLEAR_REGS
|
||||
.macro PUSH_AND_CLEAR_REGS rdx=%rdx rcx=%rcx rax=%rax save_ret=0 clear_bp=1 unwind_hint=1
|
||||
PUSH_REGS rdx=\rdx, rcx=\rcx, rax=\rax, save_ret=\save_ret unwind_hint=\unwind_hint
|
||||
CLEAR_REGS clear_bp=\clear_bp
|
||||
.endm
|
||||
|
||||
.macro POP_REGS pop_rdi=1
|
||||
|
||||
@@ -649,10 +649,6 @@ SYM_CODE_START_LOCAL(asm_\cfunc)
|
||||
SYM_CODE_END(asm_\cfunc)
|
||||
.endm
|
||||
|
||||
.macro idtentry_sysvec vector cfunc
|
||||
idtentry \vector asm_\cfunc \cfunc has_error_code=0
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Include the defines which emit the idt entries which are shared
|
||||
* shared between 32 and 64 bit and emit the __irqentry_text_* markers
|
||||
|
||||
@@ -248,7 +248,13 @@ SYM_CODE_START(ret_from_fork_asm)
|
||||
* and unwind should work normally.
|
||||
*/
|
||||
UNWIND_HINT_REGS
|
||||
|
||||
#ifdef CONFIG_X86_FRED
|
||||
ALTERNATIVE "jmp swapgs_restore_regs_and_return_to_usermode", \
|
||||
"jmp asm_fred_exit_user", X86_FEATURE_FRED
|
||||
#else
|
||||
jmp swapgs_restore_regs_and_return_to_usermode
|
||||
#endif
|
||||
SYM_CODE_END(ret_from_fork_asm)
|
||||
.popsection
|
||||
|
||||
@@ -371,14 +377,6 @@ SYM_CODE_END(\asmsym)
|
||||
idtentry \vector asm_\cfunc \cfunc has_error_code=1
|
||||
.endm
|
||||
|
||||
/*
|
||||
* System vectors which invoke their handlers directly and are not
|
||||
* going through the regular common device interrupt handling code.
|
||||
*/
|
||||
.macro idtentry_sysvec vector cfunc
|
||||
idtentry \vector asm_\cfunc \cfunc has_error_code=0
|
||||
.endm
|
||||
|
||||
/**
|
||||
* idtentry_mce_db - Macro to generate entry stubs for #MC and #DB
|
||||
* @vector: Vector number
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* The actual FRED entry points.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <asm/asm.h>
|
||||
#include <asm/fred.h>
|
||||
#include <asm/segment.h>
|
||||
|
||||
#include "calling.h"
|
||||
|
||||
.code64
|
||||
.section .noinstr.text, "ax"
|
||||
|
||||
.macro FRED_ENTER
|
||||
UNWIND_HINT_END_OF_STACK
|
||||
ENDBR
|
||||
PUSH_AND_CLEAR_REGS
|
||||
movq %rsp, %rdi /* %rdi -> pt_regs */
|
||||
.endm
|
||||
|
||||
.macro FRED_EXIT
|
||||
UNWIND_HINT_REGS
|
||||
POP_REGS
|
||||
.endm
|
||||
|
||||
/*
|
||||
* The new RIP value that FRED event delivery establishes is
|
||||
* IA32_FRED_CONFIG & ~FFFH for events that occur in ring 3.
|
||||
* Thus the FRED ring 3 entry point must be 4K page aligned.
|
||||
*/
|
||||
.align 4096
|
||||
|
||||
SYM_CODE_START_NOALIGN(asm_fred_entrypoint_user)
|
||||
FRED_ENTER
|
||||
call fred_entry_from_user
|
||||
SYM_INNER_LABEL(asm_fred_exit_user, SYM_L_GLOBAL)
|
||||
FRED_EXIT
|
||||
1: ERETU
|
||||
|
||||
_ASM_EXTABLE_TYPE(1b, asm_fred_entrypoint_user, EX_TYPE_ERETU)
|
||||
SYM_CODE_END(asm_fred_entrypoint_user)
|
||||
|
||||
/*
|
||||
* The new RIP value that FRED event delivery establishes is
|
||||
* (IA32_FRED_CONFIG & ~FFFH) + 256 for events that occur in
|
||||
* ring 0, i.e., asm_fred_entrypoint_user + 256.
|
||||
*/
|
||||
.org asm_fred_entrypoint_user + 256, 0xcc
|
||||
SYM_CODE_START_NOALIGN(asm_fred_entrypoint_kernel)
|
||||
FRED_ENTER
|
||||
call fred_entry_from_kernel
|
||||
FRED_EXIT
|
||||
ERETS
|
||||
SYM_CODE_END(asm_fred_entrypoint_kernel)
|
||||
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
SYM_FUNC_START(asm_fred_entry_from_kvm)
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
UNWIND_HINT_SAVE
|
||||
|
||||
/*
|
||||
* Both IRQ and NMI from VMX can be handled on current task stack
|
||||
* because there is no need to protect from reentrancy and the call
|
||||
* stack leading to this helper is effectively constant and shallow
|
||||
* (relatively speaking). Do the same when FRED is active, i.e., no
|
||||
* need to check current stack level for a stack switch.
|
||||
*
|
||||
* Emulate the FRED-defined redzone and stack alignment.
|
||||
*/
|
||||
sub $(FRED_CONFIG_REDZONE_AMOUNT << 6), %rsp
|
||||
and $FRED_STACK_FRAME_RSP_MASK, %rsp
|
||||
|
||||
/*
|
||||
* Start to push a FRED stack frame, which is always 64 bytes:
|
||||
*
|
||||
* +--------+-----------------+
|
||||
* | Bytes | Usage |
|
||||
* +--------+-----------------+
|
||||
* | 63:56 | Reserved |
|
||||
* | 55:48 | Event Data |
|
||||
* | 47:40 | SS + Event Info |
|
||||
* | 39:32 | RSP |
|
||||
* | 31:24 | RFLAGS |
|
||||
* | 23:16 | CS + Aux Info |
|
||||
* | 15:8 | RIP |
|
||||
* | 7:0 | Error Code |
|
||||
* +--------+-----------------+
|
||||
*/
|
||||
push $0 /* Reserved, must be 0 */
|
||||
push $0 /* Event data, 0 for IRQ/NMI */
|
||||
push %rdi /* fred_ss handed in by the caller */
|
||||
push %rbp
|
||||
pushf
|
||||
mov $__KERNEL_CS, %rax
|
||||
push %rax
|
||||
|
||||
/*
|
||||
* Unlike the IDT event delivery, FRED _always_ pushes an error code
|
||||
* after pushing the return RIP, thus the CALL instruction CANNOT be
|
||||
* used here to push the return RIP, otherwise there is no chance to
|
||||
* push an error code before invoking the IRQ/NMI handler.
|
||||
*
|
||||
* Use LEA to get the return RIP and push it, then push an error code.
|
||||
*/
|
||||
lea 1f(%rip), %rax
|
||||
push %rax /* Return RIP */
|
||||
push $0 /* Error code, 0 for IRQ/NMI */
|
||||
|
||||
PUSH_AND_CLEAR_REGS clear_bp=0 unwind_hint=0
|
||||
movq %rsp, %rdi /* %rdi -> pt_regs */
|
||||
call __fred_entry_from_kvm /* Call the C entry point */
|
||||
POP_REGS
|
||||
ERETS
|
||||
1:
|
||||
/*
|
||||
* Objtool doesn't understand what ERETS does, this hint tells it that
|
||||
* yes, we'll reach here and with what stack state. A save/restore pair
|
||||
* isn't strictly needed, but it's the simplest form.
|
||||
*/
|
||||
UNWIND_HINT_RESTORE
|
||||
pop %rbp
|
||||
RET
|
||||
|
||||
SYM_FUNC_END(asm_fred_entry_from_kvm)
|
||||
EXPORT_SYMBOL_GPL(asm_fred_entry_from_kvm);
|
||||
#endif
|
||||
@@ -0,0 +1,294 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* The FRED specific kernel/user entry functions which are invoked from
|
||||
* assembly code and dispatch to the associated handlers.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/nospec.h>
|
||||
|
||||
#include <asm/desc.h>
|
||||
#include <asm/fred.h>
|
||||
#include <asm/idtentry.h>
|
||||
#include <asm/syscall.h>
|
||||
#include <asm/trapnr.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
/* FRED EVENT_TYPE_OTHER vector numbers */
|
||||
#define FRED_SYSCALL 1
|
||||
#define FRED_SYSENTER 2
|
||||
|
||||
static noinstr void fred_bad_type(struct pt_regs *regs, unsigned long error_code)
|
||||
{
|
||||
irqentry_state_t irq_state = irqentry_nmi_enter(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
|
||||
/* Panic on events from a high stack level */
|
||||
if (regs->fred_cs.sl > 0) {
|
||||
pr_emerg("PANIC: invalid or fatal FRED event; event type %u "
|
||||
"vector %u error 0x%lx aux 0x%lx at %04x:%016lx\n",
|
||||
regs->fred_ss.type, regs->fred_ss.vector, regs->orig_ax,
|
||||
fred_event_data(regs), regs->cs, regs->ip);
|
||||
die("invalid or fatal FRED event", regs, regs->orig_ax);
|
||||
panic("invalid or fatal FRED event");
|
||||
} else {
|
||||
unsigned long flags = oops_begin();
|
||||
int sig = SIGKILL;
|
||||
|
||||
pr_alert("BUG: invalid or fatal FRED event; event type %u "
|
||||
"vector %u error 0x%lx aux 0x%lx at %04x:%016lx\n",
|
||||
regs->fred_ss.type, regs->fred_ss.vector, regs->orig_ax,
|
||||
fred_event_data(regs), regs->cs, regs->ip);
|
||||
|
||||
if (__die("Invalid or fatal FRED event", regs, regs->orig_ax))
|
||||
sig = 0;
|
||||
|
||||
oops_end(flags, regs, sig);
|
||||
}
|
||||
|
||||
instrumentation_end();
|
||||
irqentry_nmi_exit(regs, irq_state);
|
||||
}
|
||||
|
||||
static noinstr void fred_intx(struct pt_regs *regs)
|
||||
{
|
||||
switch (regs->fred_ss.vector) {
|
||||
/* Opcode 0xcd, 0x3, NOT INT3 (opcode 0xcc) */
|
||||
case X86_TRAP_BP:
|
||||
return exc_int3(regs);
|
||||
|
||||
/* Opcode 0xcd, 0x4, NOT INTO (opcode 0xce) */
|
||||
case X86_TRAP_OF:
|
||||
return exc_overflow(regs);
|
||||
|
||||
#ifdef CONFIG_IA32_EMULATION
|
||||
/* INT80 */
|
||||
case IA32_SYSCALL_VECTOR:
|
||||
if (ia32_enabled())
|
||||
return int80_emulation(regs);
|
||||
fallthrough;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return exc_general_protection(regs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline void fred_other(struct pt_regs *regs)
|
||||
{
|
||||
/* The compiler can fold these conditions into a single test */
|
||||
if (likely(regs->fred_ss.vector == FRED_SYSCALL && regs->fred_ss.lm)) {
|
||||
regs->orig_ax = regs->ax;
|
||||
regs->ax = -ENOSYS;
|
||||
do_syscall_64(regs, regs->orig_ax);
|
||||
return;
|
||||
} else if (ia32_enabled() &&
|
||||
likely(regs->fred_ss.vector == FRED_SYSENTER && !regs->fred_ss.lm)) {
|
||||
regs->orig_ax = regs->ax;
|
||||
regs->ax = -ENOSYS;
|
||||
do_fast_syscall_32(regs);
|
||||
return;
|
||||
} else {
|
||||
exc_invalid_op(regs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define SYSVEC(_vector, _function) [_vector - FIRST_SYSTEM_VECTOR] = fred_sysvec_##_function
|
||||
|
||||
static idtentry_t sysvec_table[NR_SYSTEM_VECTORS] __ro_after_init = {
|
||||
SYSVEC(ERROR_APIC_VECTOR, error_interrupt),
|
||||
SYSVEC(SPURIOUS_APIC_VECTOR, spurious_apic_interrupt),
|
||||
SYSVEC(LOCAL_TIMER_VECTOR, apic_timer_interrupt),
|
||||
SYSVEC(X86_PLATFORM_IPI_VECTOR, x86_platform_ipi),
|
||||
|
||||
SYSVEC(RESCHEDULE_VECTOR, reschedule_ipi),
|
||||
SYSVEC(CALL_FUNCTION_SINGLE_VECTOR, call_function_single),
|
||||
SYSVEC(CALL_FUNCTION_VECTOR, call_function),
|
||||
SYSVEC(REBOOT_VECTOR, reboot),
|
||||
|
||||
SYSVEC(THRESHOLD_APIC_VECTOR, threshold),
|
||||
SYSVEC(DEFERRED_ERROR_VECTOR, deferred_error),
|
||||
SYSVEC(THERMAL_APIC_VECTOR, thermal),
|
||||
|
||||
SYSVEC(IRQ_WORK_VECTOR, irq_work),
|
||||
|
||||
SYSVEC(POSTED_INTR_VECTOR, kvm_posted_intr_ipi),
|
||||
SYSVEC(POSTED_INTR_WAKEUP_VECTOR, kvm_posted_intr_wakeup_ipi),
|
||||
SYSVEC(POSTED_INTR_NESTED_VECTOR, kvm_posted_intr_nested_ipi),
|
||||
};
|
||||
|
||||
static bool fred_setup_done __initdata;
|
||||
|
||||
void __init fred_install_sysvec(unsigned int sysvec, idtentry_t handler)
|
||||
{
|
||||
if (WARN_ON_ONCE(sysvec < FIRST_SYSTEM_VECTOR))
|
||||
return;
|
||||
|
||||
if (WARN_ON_ONCE(fred_setup_done))
|
||||
return;
|
||||
|
||||
if (!WARN_ON_ONCE(sysvec_table[sysvec - FIRST_SYSTEM_VECTOR]))
|
||||
sysvec_table[sysvec - FIRST_SYSTEM_VECTOR] = handler;
|
||||
}
|
||||
|
||||
static noinstr void fred_handle_spurious_interrupt(struct pt_regs *regs)
|
||||
{
|
||||
spurious_interrupt(regs, regs->fred_ss.vector);
|
||||
}
|
||||
|
||||
void __init fred_complete_exception_setup(void)
|
||||
{
|
||||
unsigned int vector;
|
||||
|
||||
for (vector = 0; vector < FIRST_EXTERNAL_VECTOR; vector++)
|
||||
set_bit(vector, system_vectors);
|
||||
|
||||
for (vector = 0; vector < NR_SYSTEM_VECTORS; vector++) {
|
||||
if (sysvec_table[vector])
|
||||
set_bit(vector + FIRST_SYSTEM_VECTOR, system_vectors);
|
||||
else
|
||||
sysvec_table[vector] = fred_handle_spurious_interrupt;
|
||||
}
|
||||
fred_setup_done = true;
|
||||
}
|
||||
|
||||
static noinstr void fred_extint(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int vector = regs->fred_ss.vector;
|
||||
unsigned int index = array_index_nospec(vector - FIRST_SYSTEM_VECTOR,
|
||||
NR_SYSTEM_VECTORS);
|
||||
|
||||
if (WARN_ON_ONCE(vector < FIRST_EXTERNAL_VECTOR))
|
||||
return;
|
||||
|
||||
if (likely(vector >= FIRST_SYSTEM_VECTOR)) {
|
||||
irqentry_state_t state = irqentry_enter(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
sysvec_table[index](regs);
|
||||
instrumentation_end();
|
||||
irqentry_exit(regs, state);
|
||||
} else {
|
||||
common_interrupt(regs, vector);
|
||||
}
|
||||
}
|
||||
|
||||
static noinstr void fred_hwexc(struct pt_regs *regs, unsigned long error_code)
|
||||
{
|
||||
/* Optimize for #PF. That's the only exception which matters performance wise */
|
||||
if (likely(regs->fred_ss.vector == X86_TRAP_PF))
|
||||
return exc_page_fault(regs, error_code);
|
||||
|
||||
switch (regs->fred_ss.vector) {
|
||||
case X86_TRAP_DE: return exc_divide_error(regs);
|
||||
case X86_TRAP_DB: return fred_exc_debug(regs);
|
||||
case X86_TRAP_BR: return exc_bounds(regs);
|
||||
case X86_TRAP_UD: return exc_invalid_op(regs);
|
||||
case X86_TRAP_NM: return exc_device_not_available(regs);
|
||||
case X86_TRAP_DF: return exc_double_fault(regs, error_code);
|
||||
case X86_TRAP_TS: return exc_invalid_tss(regs, error_code);
|
||||
case X86_TRAP_NP: return exc_segment_not_present(regs, error_code);
|
||||
case X86_TRAP_SS: return exc_stack_segment(regs, error_code);
|
||||
case X86_TRAP_GP: return exc_general_protection(regs, error_code);
|
||||
case X86_TRAP_MF: return exc_coprocessor_error(regs);
|
||||
case X86_TRAP_AC: return exc_alignment_check(regs, error_code);
|
||||
case X86_TRAP_XF: return exc_simd_coprocessor_error(regs);
|
||||
|
||||
#ifdef CONFIG_X86_MCE
|
||||
case X86_TRAP_MC: return fred_exc_machine_check(regs);
|
||||
#endif
|
||||
#ifdef CONFIG_INTEL_TDX_GUEST
|
||||
case X86_TRAP_VE: return exc_virtualization_exception(regs);
|
||||
#endif
|
||||
#ifdef CONFIG_X86_CET
|
||||
case X86_TRAP_CP: return exc_control_protection(regs, error_code);
|
||||
#endif
|
||||
default: return fred_bad_type(regs, error_code);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static noinstr void fred_swexc(struct pt_regs *regs, unsigned long error_code)
|
||||
{
|
||||
switch (regs->fred_ss.vector) {
|
||||
case X86_TRAP_BP: return exc_int3(regs);
|
||||
case X86_TRAP_OF: return exc_overflow(regs);
|
||||
default: return fred_bad_type(regs, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
__visible noinstr void fred_entry_from_user(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long error_code = regs->orig_ax;
|
||||
|
||||
/* Invalidate orig_ax so that syscall_get_nr() works correctly */
|
||||
regs->orig_ax = -1;
|
||||
|
||||
switch (regs->fred_ss.type) {
|
||||
case EVENT_TYPE_EXTINT:
|
||||
return fred_extint(regs);
|
||||
case EVENT_TYPE_NMI:
|
||||
if (likely(regs->fred_ss.vector == X86_TRAP_NMI))
|
||||
return fred_exc_nmi(regs);
|
||||
break;
|
||||
case EVENT_TYPE_HWEXC:
|
||||
return fred_hwexc(regs, error_code);
|
||||
case EVENT_TYPE_SWINT:
|
||||
return fred_intx(regs);
|
||||
case EVENT_TYPE_PRIV_SWEXC:
|
||||
if (likely(regs->fred_ss.vector == X86_TRAP_DB))
|
||||
return fred_exc_debug(regs);
|
||||
break;
|
||||
case EVENT_TYPE_SWEXC:
|
||||
return fred_swexc(regs, error_code);
|
||||
case EVENT_TYPE_OTHER:
|
||||
return fred_other(regs);
|
||||
default: break;
|
||||
}
|
||||
|
||||
return fred_bad_type(regs, error_code);
|
||||
}
|
||||
|
||||
__visible noinstr void fred_entry_from_kernel(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long error_code = regs->orig_ax;
|
||||
|
||||
/* Invalidate orig_ax so that syscall_get_nr() works correctly */
|
||||
regs->orig_ax = -1;
|
||||
|
||||
switch (regs->fred_ss.type) {
|
||||
case EVENT_TYPE_EXTINT:
|
||||
return fred_extint(regs);
|
||||
case EVENT_TYPE_NMI:
|
||||
if (likely(regs->fred_ss.vector == X86_TRAP_NMI))
|
||||
return fred_exc_nmi(regs);
|
||||
break;
|
||||
case EVENT_TYPE_HWEXC:
|
||||
return fred_hwexc(regs, error_code);
|
||||
case EVENT_TYPE_PRIV_SWEXC:
|
||||
if (likely(regs->fred_ss.vector == X86_TRAP_DB))
|
||||
return fred_exc_debug(regs);
|
||||
break;
|
||||
case EVENT_TYPE_SWEXC:
|
||||
return fred_swexc(regs, error_code);
|
||||
default: break;
|
||||
}
|
||||
|
||||
return fred_bad_type(regs, error_code);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
__visible noinstr void __fred_entry_from_kvm(struct pt_regs *regs)
|
||||
{
|
||||
switch (regs->fred_ss.type) {
|
||||
case EVENT_TYPE_EXTINT:
|
||||
return fred_extint(regs);
|
||||
case EVENT_TYPE_NMI:
|
||||
return fred_exc_nmi(regs);
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -76,7 +76,7 @@ static void warn_bad_vsyscall(const char *level, struct pt_regs *regs,
|
||||
if (!show_unhandled_signals)
|
||||
return;
|
||||
|
||||
printk_ratelimited("%s%s[%d] %s ip:%lx cs:%lx sp:%lx ax:%lx si:%lx di:%lx\n",
|
||||
printk_ratelimited("%s%s[%d] %s ip:%lx cs:%x sp:%lx ax:%lx si:%lx di:%lx\n",
|
||||
level, current->comm, task_pid_nr(current),
|
||||
message, regs->ip, regs->cs,
|
||||
regs->sp, regs->ax, regs->si, regs->di);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <asm/special_insns.h>
|
||||
#include <asm/preempt.h>
|
||||
#include <asm/asm.h>
|
||||
#include <asm/fred.h>
|
||||
#include <asm/gsseg.h>
|
||||
|
||||
#ifndef CONFIG_X86_CMPXCHG64
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Returns:
|
||||
* 0 - (index < size)
|
||||
*/
|
||||
static inline unsigned long array_index_mask_nospec(unsigned long index,
|
||||
static __always_inline unsigned long array_index_mask_nospec(unsigned long index,
|
||||
unsigned long size)
|
||||
{
|
||||
unsigned long mask;
|
||||
|
||||
@@ -324,7 +324,9 @@
|
||||
#define X86_FEATURE_FZRM (12*32+10) /* "" Fast zero-length REP MOVSB */
|
||||
#define X86_FEATURE_FSRS (12*32+11) /* "" Fast short REP STOSB */
|
||||
#define X86_FEATURE_FSRC (12*32+12) /* "" Fast short REP {CMPSB,SCASB} */
|
||||
#define X86_FEATURE_FRED (12*32+17) /* Flexible Return and Event Delivery */
|
||||
#define X86_FEATURE_LKGS (12*32+18) /* "" Load "kernel" (userspace) GS */
|
||||
#define X86_FEATURE_WRMSRNS (12*32+19) /* "" Non-serializing WRMSR */
|
||||
#define X86_FEATURE_AMX_FP16 (12*32+21) /* "" AMX fp16 Support */
|
||||
#define X86_FEATURE_AVX_IFMA (12*32+23) /* "" Support for VPMADD52[H,L]UQ */
|
||||
#define X86_FEATURE_LAM (12*32+26) /* Linear Address Masking */
|
||||
|
||||
@@ -402,8 +402,6 @@ static inline void set_desc_limit(struct desc_struct *desc, unsigned long limit)
|
||||
desc->limit1 = (limit >> 16) & 0xf;
|
||||
}
|
||||
|
||||
void alloc_intr_gate(unsigned int n, const void *addr);
|
||||
|
||||
static inline void init_idt_data(struct idt_data *data, unsigned int n,
|
||||
const void *addr)
|
||||
{
|
||||
|
||||
@@ -117,6 +117,12 @@
|
||||
#define DISABLE_IBT (1 << (X86_FEATURE_IBT & 31))
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_FRED
|
||||
# define DISABLE_FRED 0
|
||||
#else
|
||||
# define DISABLE_FRED (1 << (X86_FEATURE_FRED & 31))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure to add features to the correct mask
|
||||
*/
|
||||
@@ -133,7 +139,7 @@
|
||||
#define DISABLED_MASK10 0
|
||||
#define DISABLED_MASK11 (DISABLE_RETPOLINE|DISABLE_RETHUNK|DISABLE_UNRET| \
|
||||
DISABLE_CALL_DEPTH_TRACKING|DISABLE_USER_SHSTK)
|
||||
#define DISABLED_MASK12 (DISABLE_LAM)
|
||||
#define DISABLED_MASK12 (DISABLE_FRED|DISABLE_LAM)
|
||||
#define DISABLED_MASK13 0
|
||||
#define DISABLED_MASK14 0
|
||||
#define DISABLED_MASK15 0
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
#define EX_TYPE_UCOPY_LEN4 (EX_TYPE_UCOPY_LEN | EX_DATA_IMM(4))
|
||||
#define EX_TYPE_UCOPY_LEN8 (EX_TYPE_UCOPY_LEN | EX_DATA_IMM(8))
|
||||
|
||||
#define EX_TYPE_ZEROPAD 20 /* longword load with zeropad on fault */
|
||||
#define EX_TYPE_ZEROPAD 20 /* longword load with zeropad on fault */
|
||||
|
||||
#define EX_TYPE_ERETU 21
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Macros for Flexible Return and Event Delivery (FRED)
|
||||
*/
|
||||
|
||||
#ifndef ASM_X86_FRED_H
|
||||
#define ASM_X86_FRED_H
|
||||
|
||||
#include <linux/const.h>
|
||||
|
||||
#include <asm/asm.h>
|
||||
#include <asm/trapnr.h>
|
||||
|
||||
/*
|
||||
* FRED event return instruction opcodes for ERET{S,U}; supported in
|
||||
* binutils >= 2.41.
|
||||
*/
|
||||
#define ERETS _ASM_BYTES(0xf2,0x0f,0x01,0xca)
|
||||
#define ERETU _ASM_BYTES(0xf3,0x0f,0x01,0xca)
|
||||
|
||||
/*
|
||||
* RSP is aligned to a 64-byte boundary before used to push a new stack frame
|
||||
*/
|
||||
#define FRED_STACK_FRAME_RSP_MASK _AT(unsigned long, (~0x3f))
|
||||
|
||||
/*
|
||||
* Used for the return address for call emulation during code patching,
|
||||
* and measured in 64-byte cache lines.
|
||||
*/
|
||||
#define FRED_CONFIG_REDZONE_AMOUNT 1
|
||||
#define FRED_CONFIG_REDZONE (_AT(unsigned long, FRED_CONFIG_REDZONE_AMOUNT) << 6)
|
||||
#define FRED_CONFIG_INT_STKLVL(l) (_AT(unsigned long, l) << 9)
|
||||
#define FRED_CONFIG_ENTRYPOINT(p) _AT(unsigned long, (p))
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#ifdef CONFIG_X86_FRED
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
struct fred_info {
|
||||
/* Event data: CR2, DR6, ... */
|
||||
unsigned long edata;
|
||||
unsigned long resv;
|
||||
};
|
||||
|
||||
/* Full format of the FRED stack frame */
|
||||
struct fred_frame {
|
||||
struct pt_regs regs;
|
||||
struct fred_info info;
|
||||
};
|
||||
|
||||
static __always_inline struct fred_info *fred_info(struct pt_regs *regs)
|
||||
{
|
||||
return &container_of(regs, struct fred_frame, regs)->info;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long fred_event_data(struct pt_regs *regs)
|
||||
{
|
||||
return fred_info(regs)->edata;
|
||||
}
|
||||
|
||||
void asm_fred_entrypoint_user(void);
|
||||
void asm_fred_entrypoint_kernel(void);
|
||||
void asm_fred_entry_from_kvm(struct fred_ss);
|
||||
|
||||
__visible void fred_entry_from_user(struct pt_regs *regs);
|
||||
__visible void fred_entry_from_kernel(struct pt_regs *regs);
|
||||
__visible void __fred_entry_from_kvm(struct pt_regs *regs);
|
||||
|
||||
/* Can be called from noinstr code, thus __always_inline */
|
||||
static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector)
|
||||
{
|
||||
struct fred_ss ss = {
|
||||
.ss =__KERNEL_DS,
|
||||
.type = type,
|
||||
.vector = vector,
|
||||
.nmi = type == EVENT_TYPE_NMI,
|
||||
.lm = 1,
|
||||
};
|
||||
|
||||
asm_fred_entry_from_kvm(ss);
|
||||
}
|
||||
|
||||
void cpu_init_fred_exceptions(void);
|
||||
void fred_complete_exception_setup(void);
|
||||
|
||||
#else /* CONFIG_X86_FRED */
|
||||
static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { return 0; }
|
||||
static inline void cpu_init_fred_exceptions(void) { }
|
||||
static inline void fred_complete_exception_setup(void) { }
|
||||
static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { }
|
||||
#endif /* CONFIG_X86_FRED */
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#endif /* ASM_X86_FRED_H */
|
||||
@@ -69,7 +69,7 @@ extern void ia32_pick_mmap_layout(struct mm_struct *mm);
|
||||
|
||||
extern bool __ia32_enabled;
|
||||
|
||||
static inline bool ia32_enabled(void)
|
||||
static __always_inline bool ia32_enabled(void)
|
||||
{
|
||||
return __ia32_enabled;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ static inline void ia32_disable(void)
|
||||
|
||||
#else /* !CONFIG_IA32_EMULATION */
|
||||
|
||||
static inline bool ia32_enabled(void)
|
||||
static __always_inline bool ia32_enabled(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_X86_32);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user