#include <sys/asm_linkage.h>
#include <sys/asm_misc.h>
#include <sys/regset.h>
#include <sys/privregs.h>
#include <sys/psw.h>
#include <sys/machbrand.h>
#include <sys/param.h>
#include <sys/segments.h>
#include <sys/pcb.h>
#include <sys/trap.h>
#include <sys/ftrace.h>
#include <sys/traptrace.h>
#include <sys/clock.h>
#include <sys/model.h>
#include <sys/panic.h>
#if defined(__xpv)
#include <sys/hypervisor.h>
#endif
#include "assym.h"
.data
DGDEF3(kpti_enable, 8, 8)
.fill 1, 8, 1
#if DEBUG
.data
_bad_ts_panic_msg:
.string "kpti_trampolines.s: tr_iret_user but CR0.TS set"
#endif
.section ".text";
.align MMU_PAGESIZE
.global kpti_tramp_start
kpti_tramp_start:
nop
.global kpti_safe_cr3
kpti_safe_cr3:
.quad 0
SET_SIZE(kpti_safe_cr3)
.global kpti_kbase
kpti_kbase:
.quad KERNELBASE
SET_SIZE(kpti_kbase)
#define SET_KERNEL_CR3(spillreg) \
mov %cr3, spillreg; \
mov spillreg, %gs:CPU_KPTI_TR_CR3; \
mov %gs:CPU_KPTI_KCR3, spillreg; \
cmp $0, spillreg; \
je 2f; \
mov spillreg, %cr3; \
2:
#if DEBUG
#define SET_USER_CR3(spillreg) \
mov %cr3, spillreg; \
mov spillreg, %gs:CPU_KPTI_TR_CR3; \
mov %gs:CPU_KPTI_UCR3, spillreg; \
mov spillreg, %cr3
#else
#define SET_USER_CR3(spillreg) \
mov %gs:CPU_KPTI_UCR3, spillreg; \
mov spillreg, %cr3
#endif
#define PIVOT_KPTI_STK(spillreg) \
mov %rsp, spillreg; \
mov %gs:CPU_KPTI_RET_RSP, %rsp; \
pushq T_FRAMERET_SS(spillreg); \
pushq T_FRAMERET_RSP(spillreg); \
pushq T_FRAMERET_RFLAGS(spillreg); \
pushq T_FRAMERET_CS(spillreg); \
pushq T_FRAMERET_RIP(spillreg)
#define INTERRUPT_TRAMPOLINE_P(errpush) \
pushq %r13; \
pushq %r14; \
subq $KPTI_R14, %rsp; \
\
mov %cr3, %r14; \
mov %r14, KPTI_TR_CR3(%rsp); \
\
cmpw $KCS_SEL, KPTI_CS(%rsp); \
je 3f; \
1: \
\
mov KPTI_KCR3(%rsp), %r14; \
cmp $0, %r14; \
je 2f; \
mov %r14, %cr3; \
2: \
\
mov %rsp, %r13; \
and $(~(MMU_PAGESIZE - 1)), %r13; \
subq $CPU_KPTI_START, %r13; \
\
mov CPU_THREAD(%r13), %r14; \
mov T_STACK(%r14), %r14; \
addq $REGSIZE+MINFRAME, %r14; \
jmp 4f; \
3: \
\
\
mov kpti_kbase, %r14; \
cmp %r14, KPTI_RSP(%rsp); \
jb 1b; \
\
mov KPTI_RSP(%rsp), %r14; \
and $(~0xf), %r14; \
4: \
mov %rsp, %r13; \
\
mov %r14, %rsp; \
pushq KPTI_SS(%r13); \
pushq KPTI_RSP(%r13); \
pushq KPTI_RFLAGS(%r13); \
pushq KPTI_CS(%r13); \
pushq KPTI_RIP(%r13); \
errpush; \
mov KPTI_R14(%r13), %r14; \
mov KPTI_R13(%r13), %r13
#define INTERRUPT_TRAMPOLINE_NOERR \
INTERRUPT_TRAMPOLINE_P()
#define INTERRUPT_TRAMPOLINE \
INTERRUPT_TRAMPOLINE_P(pushq KPTI_ERR(%r13))
#define DBG_INTERRUPT_TRAMPOLINE_P(errpush) \
pushq %r13; \
pushq %r14; \
subq $KPTI_R14, %rsp; \
\
cmpq $0, KPTI_FLAG(%rsp); \
je 1f; \
\
int $8; \
1: \
movq $1, KPTI_FLAG(%rsp); \
\
mov %cr3, %r14; \
mov %r14, KPTI_TR_CR3(%rsp); \
\
cmpw $KCS_SEL, KPTI_CS(%rsp); \
je 4f; \
2: \
\
mov KPTI_KCR3(%rsp), %r14; \
cmp $0, %r14; \
je 3f; \
mov %r14, %cr3; \
3: \
\
mov %rsp, %r13; \
and $(~(MMU_PAGESIZE - 1)), %r13; \
subq $CPU_KPTI_START, %r13; \
\
mov CPU_THREAD(%r13), %r14; \
mov T_STACK(%r14), %r14; \
addq $REGSIZE+MINFRAME, %r14; \
jmp 6f; \
4: \
\
\
\
mov kpti_kbase, %r14; \
cmp %r14, KPTI_RSP(%rsp); \
jb 2b; \
\
\
mov %rsp, %r13; \
and $(~(MMU_PAGESIZE - 1)), %r13; \
mov KPTI_RSP(%rsp), %r14; \
and $(~(MMU_PAGESIZE - 1)), %r14; \
cmp %r13, %r14; \
je 2b; \
\
leaq kpti_tramp_start, %r14; \
cmp %r14, KPTI_RIP(%rsp); \
jb 5f; \
leaq kpti_tramp_end, %r14; \
cmp %r14, KPTI_RIP(%rsp); \
ja 5f; \
\
\
mov KPTI_KCR3(%rsp), %r14; \
mov %r14, %cr3; \
5: \
\
mov KPTI_RSP(%rsp), %r14; \
and $(~0xf), %r14; \
6: \
mov %rsp, %r13; \
\
mov %r14, %rsp; \
pushq KPTI_SS(%r13); \
pushq KPTI_RSP(%r13); \
pushq KPTI_RFLAGS(%r13); \
pushq KPTI_CS(%r13); \
pushq KPTI_RIP(%r13); \
errpush; \
mov KPTI_R14(%r13), %r14; \
movq $0, KPTI_FLAG(%r13); \
mov KPTI_R13(%r13), %r13
#define DBG_INTERRUPT_TRAMPOLINE_NOERR \
DBG_INTERRUPT_TRAMPOLINE_P()
#define DBG_INTERRUPT_TRAMPOLINE \
DBG_INTERRUPT_TRAMPOLINE_P(pushq KPTI_ERR(%r13))
.global tr_sysc_ret_start
tr_sysc_ret_start:
ENTRY_NP(tr_sysretq)
cmpq $1, kpti_enable
jne 1f
mov %r13, %gs:CPU_KPTI_R13
SET_USER_CR3(%r13)
mov %gs:CPU_KPTI_R13, %r13
movq $0, %gs:CPU_KPTI_R13
movq $0, %gs:CPU_KPTI_R14
1:
swapgs
sysretq
SET_SIZE(tr_sysretq)
ENTRY_NP(tr_sysretl)
cmpq $1, kpti_enable
jne 1f
mov %r13, %gs:CPU_KPTI_R13
SET_USER_CR3(%r13)
mov %gs:CPU_KPTI_R13, %r13
movq $0, %gs:CPU_KPTI_R13
movq $0, %gs:CPU_KPTI_R14
1:
SWAPGS
SYSRETL
SET_SIZE(tr_sysretl)
ENTRY_NP(tr_sysexit)
pushfq
cmpq $1, kpti_enable
jne 1f
popfq
mov %r13, %gs:CPU_KPTI_R13
SET_USER_CR3(%r13)
mov %gs:CPU_KPTI_R13, %r13
movq $0, %gs:CPU_KPTI_R13
movq $0, %gs:CPU_KPTI_R14
jmp 2f
1:
popfq
2:
swapgs
sti
SYSEXITL
SET_SIZE(tr_sysexit)
.global tr_sysc_ret_end
tr_sysc_ret_end:
#if DEBUG
#define MK_SYSCALL_TRAMPOLINE(isr) \
ENTRY_NP(tr_##isr); \
swapgs; \
mov %r13, %gs:CPU_KPTI_R13; \
mov %cr3, %r13; \
mov %r13, %gs:CPU_KPTI_TR_CR3; \
mov %gs:CPU_KPTI_KCR3, %r13; \
mov %r13, %cr3; \
mov %gs:CPU_KPTI_R13, %r13; \
swapgs; \
jmp isr; \
SET_SIZE(tr_##isr)
#else
#define MK_SYSCALL_TRAMPOLINE(isr) \
ENTRY_NP(tr_##isr); \
swapgs; \
mov %r13, %gs:CPU_KPTI_R13; \
mov %gs:CPU_KPTI_KCR3, %r13; \
mov %r13, %cr3; \
mov %gs:CPU_KPTI_R13, %r13; \
swapgs; \
jmp isr; \
SET_SIZE(tr_##isr)
#endif
MK_SYSCALL_TRAMPOLINE(sys_syscall)
MK_SYSCALL_TRAMPOLINE(sys_syscall32)
MK_SYSCALL_TRAMPOLINE(brand_sys_syscall)
MK_SYSCALL_TRAMPOLINE(brand_sys_syscall32)
ENTRY_NP(tr_sys_sysenter)
swapgs
mov %r13, %gs:CPU_KPTI_R13
#if DEBUG
mov %cr3, %r13
mov %r13, %gs:CPU_KPTI_TR_CR3
#endif
mov %gs:CPU_KPTI_KCR3, %r13
mov %r13, %cr3
mov %gs:CPU_KPTI_R13, %r13
jmp _sys_sysenter_post_swapgs
SET_SIZE(tr_sys_sysenter)
ENTRY_NP(tr_brand_sys_sysenter)
swapgs
mov %r13, %gs:CPU_KPTI_R13
#if DEBUG
mov %cr3, %r13
mov %r13, %gs:CPU_KPTI_TR_CR3
#endif
mov %gs:CPU_KPTI_KCR3, %r13
mov %r13, %cr3
mov %gs:CPU_KPTI_R13, %r13
jmp _brand_sys_sysenter_post_swapgs
SET_SIZE(tr_brand_sys_sysenter)
#define MK_SYSCALL_INT_TRAMPOLINE(isr) \
ENTRY_NP(tr_##isr); \
swapgs; \
mov %r13, %gs:CPU_KPTI_R13; \
SET_KERNEL_CR3(%r13); \
mov %gs:CPU_THREAD, %r13; \
mov T_STACK(%r13), %r13; \
addq $REGSIZE+MINFRAME, %r13; \
mov %r13, %rsp; \
pushq %gs:CPU_KPTI_SS; \
pushq %gs:CPU_KPTI_RSP; \
pushq %gs:CPU_KPTI_RFLAGS; \
pushq %gs:CPU_KPTI_CS; \
pushq %gs:CPU_KPTI_RIP; \
mov %gs:CPU_KPTI_R13, %r13; \
swapgs; \
jmp isr; \
SET_SIZE(tr_##isr)
MK_SYSCALL_INT_TRAMPOLINE(brand_sys_syscall_int)
MK_SYSCALL_INT_TRAMPOLINE(sys_syscall_int)
.global tr_intr_ret_start
tr_intr_ret_start:
ENTRY_NP(tr_iret_auto)
cmpq $1, kpti_enable
jne tr_iret_kernel
cmpw $KCS_SEL, T_FRAMERET_CS(%rsp)
je tr_iret_kernel
jmp tr_iret_user
SET_SIZE(tr_iret_auto)
ENTRY_NP(tr_iret_kernel)
iretq
SET_SIZE(tr_iret_kernel)
ENTRY_NP(tr_iret_user)
#if DEBUG
pushq %rax
mov %cr0, %rax
testq $CR0_TS, %rax
jz 1f
swapgs
popq %rax
leaq _bad_ts_panic_msg(%rip), %rdi
xorl %eax, %eax
pushq %rbp
movq %rsp, %rbp
call panic
1:
popq %rax
#endif
cmpq $1, kpti_enable
jne 1f
swapgs
lfence
mov %r13, %gs:CPU_KPTI_R13
PIVOT_KPTI_STK(%r13)
SET_USER_CR3(%r13)
mov %gs:CPU_KPTI_R13, %r13
movq $0, %gs:CPU_KPTI_R13
movq $0, %gs:CPU_KPTI_R14
swapgs
1:
iretq
SET_SIZE(tr_iret_user)
ENTRY_NP(tr_iret_kdi)
movq %r14, KPTI_R14(%r13)
movq %rsp, %r14
leaq KPTI_TOP(%r13), %rsp
pushq T_FRAMERET_SS(%r14)
pushq T_FRAMERET_RSP(%r14)
pushq T_FRAMERET_RFLAGS(%r14)
pushq T_FRAMERET_CS(%r14)
pushq T_FRAMERET_RIP(%r14)
movq KPTI_TR_CR3(%r13), %r14
movq %r14, %cr3
movq KPTI_R14(%r13), %r14
movq KPTI_R13(%r13), %r13
iretq
SET_SIZE(tr_iret_kdi)
.global tr_intr_ret_end
tr_intr_ret_end:
#define MK_INTR_TRAMPOLINE(isr) \
ENTRY_NP(tr_##isr); \
INTERRUPT_TRAMPOLINE; \
jmp isr; \
SET_SIZE(tr_##isr)
#define MK_INTR_TRAMPOLINE_NOERR(isr) \
ENTRY_NP(tr_##isr); \
push $0; \
INTERRUPT_TRAMPOLINE_NOERR; \
jmp isr; \
SET_SIZE(tr_##isr)
#define MK_DBG_INTR_TRAMPOLINE(isr) \
ENTRY_NP(tr_##isr); \
DBG_INTERRUPT_TRAMPOLINE; \
jmp isr; \
SET_SIZE(tr_##isr)
#define MK_DBG_INTR_TRAMPOLINE_NOERR(isr) \
ENTRY_NP(tr_##isr); \
push $0; \
DBG_INTERRUPT_TRAMPOLINE_NOERR; \
jmp isr; \
SET_SIZE(tr_##isr)
MK_INTR_TRAMPOLINE_NOERR(div0trap)
MK_DBG_INTR_TRAMPOLINE_NOERR(dbgtrap)
MK_DBG_INTR_TRAMPOLINE_NOERR(brktrap)
MK_INTR_TRAMPOLINE_NOERR(ovflotrap)
MK_INTR_TRAMPOLINE_NOERR(boundstrap)
MK_INTR_TRAMPOLINE_NOERR(invoptrap)
MK_INTR_TRAMPOLINE_NOERR(ndptrap)
MK_INTR_TRAMPOLINE(invtsstrap)
MK_DBG_INTR_TRAMPOLINE(segnptrap)
MK_DBG_INTR_TRAMPOLINE(stktrap)
MK_DBG_INTR_TRAMPOLINE(gptrap)
MK_DBG_INTR_TRAMPOLINE(pftrap)
MK_INTR_TRAMPOLINE_NOERR(resvtrap)
MK_INTR_TRAMPOLINE_NOERR(ndperr)
MK_INTR_TRAMPOLINE(achktrap)
MK_INTR_TRAMPOLINE_NOERR(xmtrap)
MK_INTR_TRAMPOLINE_NOERR(invaltrap)
MK_INTR_TRAMPOLINE_NOERR(fasttrap)
MK_INTR_TRAMPOLINE_NOERR(dtrace_ret)
ENTRY_NP(tr_nmiint)
pushq %r13
mov kpti_safe_cr3, %r13
mov %r13, %cr3
popq %r13
jmp nmiint
SET_SIZE(tr_nmiint)
#if !defined(__xpv)
ENTRY_NP(tr_syserrtrap)
cmpq $0, (%rsp)
je 1f
pushq $0
1:
pushq %r13
mov kpti_safe_cr3, %r13
mov %r13, %cr3
popq %r13
jmp syserrtrap
SET_SIZE(tr_syserrtrap)
#endif
ENTRY_NP(tr_mcetrap)
pushq %r13
mov kpti_safe_cr3, %r13
mov %r13, %cr3
popq %r13
jmp mcetrap
SET_SIZE(tr_mcetrap)
#define MKIVCT(n) \
ENTRY_NP(tr_ivct##n) \
push $0; \
INTERRUPT_TRAMPOLINE; \
push $n - 0x20; \
jmp cmnint; \
SET_SIZE(tr_ivct##n)
MKIVCT(32); MKIVCT(33); MKIVCT(34); MKIVCT(35);
MKIVCT(36); MKIVCT(37); MKIVCT(38); MKIVCT(39);
MKIVCT(40); MKIVCT(41); MKIVCT(42); MKIVCT(43);
MKIVCT(44); MKIVCT(45); MKIVCT(46); MKIVCT(47);
MKIVCT(48); MKIVCT(49); MKIVCT(50); MKIVCT(51);
MKIVCT(52); MKIVCT(53); MKIVCT(54); MKIVCT(55);
MKIVCT(56); MKIVCT(57); MKIVCT(58); MKIVCT(59);
MKIVCT(60); MKIVCT(61); MKIVCT(62); MKIVCT(63);
MKIVCT(64); MKIVCT(65); MKIVCT(66); MKIVCT(67);
MKIVCT(68); MKIVCT(69); MKIVCT(70); MKIVCT(71);
MKIVCT(72); MKIVCT(73); MKIVCT(74); MKIVCT(75);
MKIVCT(76); MKIVCT(77); MKIVCT(78); MKIVCT(79);
MKIVCT(80); MKIVCT(81); MKIVCT(82); MKIVCT(83);
MKIVCT(84); MKIVCT(85); MKIVCT(86); MKIVCT(87);
MKIVCT(88); MKIVCT(89); MKIVCT(90); MKIVCT(91);
MKIVCT(92); MKIVCT(93); MKIVCT(94); MKIVCT(95);
MKIVCT(96); MKIVCT(97); MKIVCT(98); MKIVCT(99);
MKIVCT(100); MKIVCT(101); MKIVCT(102); MKIVCT(103);
MKIVCT(104); MKIVCT(105); MKIVCT(106); MKIVCT(107);
MKIVCT(108); MKIVCT(109); MKIVCT(110); MKIVCT(111);
MKIVCT(112); MKIVCT(113); MKIVCT(114); MKIVCT(115);
MKIVCT(116); MKIVCT(117); MKIVCT(118); MKIVCT(119);
MKIVCT(120); MKIVCT(121); MKIVCT(122); MKIVCT(123);
MKIVCT(124); MKIVCT(125); MKIVCT(126); MKIVCT(127);
MKIVCT(128); MKIVCT(129); MKIVCT(130); MKIVCT(131);
MKIVCT(132); MKIVCT(133); MKIVCT(134); MKIVCT(135);
MKIVCT(136); MKIVCT(137); MKIVCT(138); MKIVCT(139);
MKIVCT(140); MKIVCT(141); MKIVCT(142); MKIVCT(143);
MKIVCT(144); MKIVCT(145); MKIVCT(146); MKIVCT(147);
MKIVCT(148); MKIVCT(149); MKIVCT(150); MKIVCT(151);
MKIVCT(152); MKIVCT(153); MKIVCT(154); MKIVCT(155);
MKIVCT(156); MKIVCT(157); MKIVCT(158); MKIVCT(159);
MKIVCT(160); MKIVCT(161); MKIVCT(162); MKIVCT(163);
MKIVCT(164); MKIVCT(165); MKIVCT(166); MKIVCT(167);
MKIVCT(168); MKIVCT(169); MKIVCT(170); MKIVCT(171);
MKIVCT(172); MKIVCT(173); MKIVCT(174); MKIVCT(175);
MKIVCT(176); MKIVCT(177); MKIVCT(178); MKIVCT(179);
MKIVCT(180); MKIVCT(181); MKIVCT(182); MKIVCT(183);
MKIVCT(184); MKIVCT(185); MKIVCT(186); MKIVCT(187);
MKIVCT(188); MKIVCT(189); MKIVCT(190); MKIVCT(191);
MKIVCT(192); MKIVCT(193); MKIVCT(194); MKIVCT(195);
MKIVCT(196); MKIVCT(197); MKIVCT(198); MKIVCT(199);
MKIVCT(200); MKIVCT(201); MKIVCT(202); MKIVCT(203);
MKIVCT(204); MKIVCT(205); MKIVCT(206); MKIVCT(207);
MKIVCT(208); MKIVCT(209); MKIVCT(210); MKIVCT(211);
MKIVCT(212); MKIVCT(213); MKIVCT(214); MKIVCT(215);
MKIVCT(216); MKIVCT(217); MKIVCT(218); MKIVCT(219);
MKIVCT(220); MKIVCT(221); MKIVCT(222); MKIVCT(223);
MKIVCT(224); MKIVCT(225); MKIVCT(226); MKIVCT(227);
MKIVCT(228); MKIVCT(229); MKIVCT(230); MKIVCT(231);
MKIVCT(232); MKIVCT(233); MKIVCT(234); MKIVCT(235);
MKIVCT(236); MKIVCT(237); MKIVCT(238); MKIVCT(239);
MKIVCT(240); MKIVCT(241); MKIVCT(242); MKIVCT(243);
MKIVCT(244); MKIVCT(245); MKIVCT(246); MKIVCT(247);
MKIVCT(248); MKIVCT(249); MKIVCT(250); MKIVCT(251);
MKIVCT(252); MKIVCT(253); MKIVCT(254); MKIVCT(255);
ENTRY_NP(tr_mmu_flush_user_range)
push %rbx
mov %cr3, %rax
movq $CR3_NOINVL_BIT, %rbx
orq %rbx, %rax
mov %rcx, %cr3
add %rdi, %rsi
.align ASM_ENTRY_ALIGN
1:
invlpg (%rdi)
add %rdx, %rdi
cmp %rsi, %rdi
jb 1b
mov %rax, %cr3
pop %rbx
retq
SET_SIZE(tr_mmu_flush_user_range)
.align MMU_PAGESIZE
.global kpti_tramp_end
kpti_tramp_end:
nop