blob: 812114b6ad8d8a57db0399a8d40a2224f0f4566e [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Corey Tabaka
// Copyright (c) 2015 Intel Corporation
// Copyright (c) 2016 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <arch/asm_macros.h>
#include <arch/code-patches/case-id.h>
#include <arch/regs.h>
#include <arch/x86/descriptor.h>
#include <arch/x86/mp.h>
#include <lib/arch/asm.h>
#include <lib/arch/x86/msr.h>
#include <lib/code-patching/asm.h>
// This is called 256 times in a row, with isr.current set to the iteration
// count starting at 0. This is inside the definition for the ISR table,
// below (equivalent to being inside an .object ... .end_object pair,
// though those macros are not actually used because they don't allow
// nesting .function ... .end_function inside). So the ambient section
// state is building up the table, and the macro defines a function using
// pushsection/popsection before adding its pointer to the table.
.macro isr.dispatch.define name
// Make the whole set of macro-generated functions be cache-line aligned
// collectively.
.pushsection .text.isr.dispatch, "ax", %progbits
.ifeq isr.current
.balign 64
.endif
.function \name, cfi=custom, nosection=nosection
// Set CFI for an interrupt frame.
.cfi_signal_frame
isr.current.has_error = isr.current == 8 || (isr.current >= 10 && isr.current <= 14) || isr.current == 17
.cfi_def_cfa %rsp, (8 * (5 + isr.current.has_error))
.cfi_offset %rip, -(5 * 8)
// Mark each reg as having the same value as from the "calling" frame.
// This is the default state for callee-saved registers, but for
// completeness sake we do this for all of them.
ALL_CFI_SAME_VALUE
// Clear the AC flag to prevent ring 0 from performing data accesses to
// ring 3 if SMAP is available. If it was set, it will get restored by
// iretd. DO NOT REMOVE THIS CLAC, code in idt.c assumes it is here.
// It MUST be the first instruction of this function.
clac
.if !isr.current.has_error
// Fill in the error code not pushed by the hardware.
push_value $0
.endif
// Fill in the interrupt number.
push_value $isr.current
jmp interrupt_common
.end_function
.popsection
// Now we're back in the section building up the table.
.quad \name
.endm
.macro isr.dispatch.define.next
// The name is generated with an arbitrary number and is not actually
// meaningful at all. But there's no way to get isr.current into a
// symbol name here.
isr.dispatch.define isr.dispatch.\@.is.not.the.isr.number
isr.current = isr.current + 1
.endm
// This defines the table with pointers to all the macro-generated functions.
// This does the same as .object, but .function can't be nested inside .object.
.pushsection .rodata.isr_table, "a", %progbits
.balign 8
.label _isr_table, global, object
isr.current = 0
.rept 256
isr.dispatch.define.next
.endr
.size _isr_table, . - _isr_table
.popsection
// This is the real function all the macro-generated functions tail-call into.
.function interrupt_common, global, align=64, cfi=custom
// Set CFI for an interrupt frame, with all the words pushed by the hardware
// and the macro-generated functions together.
.cfi_signal_frame
.cfi_def_cfa %rsp, 7 * 8
.cfi_offset %rip, -(5 * 8)
// Mark each reg as having the same value as from the "calling" frame.
// This is the default state for callee-saved registers, but for completeness
// sake we do this for all of them.
ALL_CFI_SAME_VALUE
// Clear the direction flag. Without this, uses of string
// instructions, e.g. REP MOVS in memcpy() or inlined by the compiler,
// can go wrong and copy in the wrong direction, since this code may
// assume that the direction flag is unset.
cld
// Later on we're going to calculate the percpu pointer using dead reckoning
// from %rsp so keep track of what all has been pushed onto the stack.
//
// At this point the stack contains 7 qwords. 5 or 6 pushed by the interrupt
// and 2 or 1 pushed by the stub.
// Save general purpose registers.
push_reg %r15
push_reg %r14
push_reg %r13
push_reg %r12
push_reg %r11
push_reg %r10
push_reg %r9
push_reg %r8
push_reg %rax
push_reg %rcx
push_reg %rdx
push_reg %rbx
push_reg %rbp
push_reg %rsi
push_reg %rdi
// At this point the stack contains a complete iframe_t.
movq %rsp, %rdi // Pass the iframe in %rdi.
// There are two main paths through this function. One path is for NMIs. The
// other is for all other interrupts (non_nmi). Both share a common return
// path.
//
// TODO(maniscalco): Refactor this function to eliminate the compare and branch
// without duplicating logic that's common to the NMI and non-NMI paths.
// Was this an NMI?
cmpq $2, X86_IFRAME_OFFSET_VECTOR(%rsp)
je .Lnmi
// Check to see if we came from user space by testing the CPL in the
// %cs selector that was saved on the stack automatically. Check for != 0.
testb $3, X86_IFRAME_OFFSET_CS(%rsp)
jz 1f
// Swap %gs.base to kernel space.
swapgs
1:
// Mitigates the swapgs bug. See <arch/code-patches/case-id.h>.
.code_patching.start CASE_ID_SWAPGS_MITIGATION
lfence
.code_patching.end
call x86_exception_handler
// A label to assist gdb's backtracing through kernel exceptions.
// When gdb sees this as the return address it knows it can fetch
// iframe_t from $rsp. See scripts/zircon.elf-gdb.py.
.label interrupt_common_iframe_set_up_for_debugger
// Label exists only so a test can verify the code patching result.
.label interrupt_non_nmi_maybe_mds_buff_overwrite, global
// Mitigates MDS/TAA bugs. See <arch/code-patches/case-id.h>
.code_patching.start CASE_ID_MDS_TAA_MITIGATION
call mds_buff_overwrite
.code_patching.end
// Check if we're returning to user space as per before.
testb $3, X86_IFRAME_OFFSET_CS(%rsp)
jz 1f
// Swap %gs.base back to user space.
swapgs
1:
// Mitigates the swapgs bug. See <arch/code-patches/case-id.h>.
.code_patching.start CASE_ID_SWAPGS_MITIGATION
lfence
.code_patching.end
.Lcommon_return:
// Restore general purpose registers.
pop_reg %rdi
pop_reg %rsi
pop_reg %rbp
pop_reg %rbx
pop_reg %rdx
pop_reg %rcx
pop_reg %rax
pop_reg %r8
pop_reg %r9
pop_reg %r10
pop_reg %r11
pop_reg %r12
pop_reg %r13
pop_reg %r14
pop_reg %r15
// Drop vector number and error code.
add_to_sp 16
iretq
.Lnmi:
// We took an NMI. The NMI may have interrupted another interrupt handler
// before that handler was able to setup the kernel's %gs.base. We can't
// be sure of the state of %gs.base so we'll have to recover it. Start by
// saving the current value in a callee-saved register so we can later restore
// it.
// Save it in %rbx.
//
// TODO(maniscalco): If/when NMIs become performance critical (e.g. used for
// profiling) consider using rdgsbase/wrgsbase when available instead of the
// MSR.
rdmsr64 MSR_IA32_GS_BASE
mov %rax, %rbx
// Compute the %rsp-relative offset of the stack base.
.Lstack_base_from_rsp = INTERRUPT_STACK_SIZE - X86_IFRAME_SIZE
// The NMI stack is embedded in the percpu struct.
.Lpercpu_from_rsp = .Lstack_base_from_rsp + PERCPU_INTERRUPT_STACKS_NMI_OFFSET
mov %rsp, %rax
sub $.Lpercpu_from_rsp, %rax
// Set %gs.base to point at the percpu struct.
wrmsr64 MSR_IA32_GS_BASE
call x86_nmi_handler
// Label exists only so a test can verify the code patching result.
.label interrupt_nmi_maybe_mds_buff_overwrite, global
// Mitigates MDS/TAA bugs. See <arch/code-patches/case-id.h>
.code_patching.start CASE_ID_MDS_TAA_MITIGATION
call mds_buff_overwrite
.code_patching.end
// Restore the saved %gs.base.
mov %rbx, %rax
wrmsr64 MSR_IA32_GS_BASE
jmp .Lcommon_return
.end_function
// Call external interrupt handler manually without actually issuing interrupt.
//
// For external interrupts CPU doesn't store error code on stack so we use
// 0. We additionally use CODE_64_SELECTOR as CS, 0 as SS, RFLAGS value and
// current stack.
.function x86_call_external_interrupt_handler, global
// Save current RFLAGS value.
pushfq
.cfi_adjust_cfa_offset 8
pop_value %r10
// Save current RSP value.
movq %rsp, %r11
// Calculate exit address.
leaq .Lexit(%rip), %rax
// Prepare interrupt stack frame in the form interrupt_common expects to see.
sub_from_sp 0x38
movq %rdi, 0x00(%rsp) // %rdi holds vector number
movq $0, 0x08(%rsp) // error code
movq %rax, 0x10(%rsp) // RIP (return address)
movq $CODE_64_SELECTOR, 0x18(%rsp) // CS
movq %r10, 0x20(%rsp) // RFLAGS
movq %r11, 0x28(%rsp) // RSP
movq $0, 0x30(%rsp) // SS
// We can actually avoid this jump if we put this code above
// interrupt_common and just fall through, but benefits of doing this are
// not obvious so for now for the sake of clarity keep this jump.
jmp interrupt_common
.Lexit:
ret
.end_function