Files
visor/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S
T
2023-08-08 12:33:59 -07:00

199 lines
7.0 KiB
ArmAsm

// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sysmsg_offsets.h"
#include "sysmsg_offsets_amd64.h"
// Helper macros:
////////////////////////////////////////
// prepare_enter_syshandler does the following:
// - saves all registers that are restorable onto the thread_context struct.
// - loads the address of the thread_context struct into %rcx.
.macro prepare_enter_syshandler
// Syshandler clobbers rflags (load_thread_context_addr does so for example).
// Therefore save it as the first thing we do.
pushfq
// load_thread_context_addr overwrites %rcx.
push %rcx
movq %gs:offsetof_sysmsg_context, %rcx
// Registers listed in order as written in ptregs:
movq %r15, offsetof_thread_context_ptregs_r15(%rcx)
movq %r14, offsetof_thread_context_ptregs_r14(%rcx)
movq %r13, offsetof_thread_context_ptregs_r13(%rcx)
movq %r12, offsetof_thread_context_ptregs_r12(%rcx)
movq %rbp, offsetof_thread_context_ptregs_rbp(%rcx)
movq %rbx, offsetof_thread_context_ptregs_rbx(%rcx)
movq %r11, offsetof_thread_context_ptregs_r11(%rcx)
movq %r10, offsetof_thread_context_ptregs_r10(%rcx)
movq %r9, offsetof_thread_context_ptregs_r9(%rcx)
movq %r8, offsetof_thread_context_ptregs_r8(%rcx)
movq %rax, offsetof_thread_context_ptregs_rax(%rcx)
pop %r15
movq %r15, offsetof_thread_context_ptregs_rcx(%rcx)
movq %rdx, offsetof_thread_context_ptregs_rdx(%rcx)
movq %rsi, offsetof_thread_context_ptregs_rsi(%rcx)
movq %rdi, offsetof_thread_context_ptregs_rdi(%rcx)
movq %rax, offsetof_thread_context_ptregs_orig_rax(%rcx)
movw %cs, offsetof_thread_context_ptregs_cs(%rcx)
movw %ss, offsetof_thread_context_ptregs_ss(%rcx)
// Don't bother save/restoring ds/es on amd64
// movw %ds, offsetof_thread_context_ptregs_ds(%rcx)
// movw %es, offsetof_thread_context_ptregs_es(%rcx)
movw %fs, offsetof_thread_context_ptregs_fs(%rcx)
movw %gs, offsetof_thread_context_ptregs_gs(%rcx)
pop %rax
movq %rax, offsetof_thread_context_ptregs_eflags(%rcx)
movq %gs:offsetof_sysmsg_app_stack, %r8
movq %r8, offsetof_thread_context_ptregs_rsp(%rcx)
movq %gs:offsetof_sysmsg_ret_addr, %r9
movq %r9, offsetof_thread_context_ptregs_rip(%rcx)
.endm
// prepare_exit_syshandler assumes that:
// - the memory address of the thread_context is loaded in %rcx.
// prepare_exit_syshandler does the following:
// - sets sysmsg->ret_addr
// - restores all registers that were saved inside the thread_context struct except for
// %rsp and rflags.
// - %rcx will be restored as well, and will no longer contain the memory address to the
// thread context.
// - puts user %rsp and rflags onto the syshandler stack (in that order). rflags cannot
// be restored at this point because syshandler will clobber it before it exits.
.macro prepare_exit_syshandler
movq offsetof_thread_context_ptregs_rsp(%rcx), %rax
push %rax
movq offsetof_thread_context_ptregs_eflags(%rcx), %rbx
push %rbx
// set sysmsg->ret_addr
movq offsetof_thread_context_ptregs_rip(%rcx), %r9
movq %r9, %gs:offsetof_sysmsg_ret_addr
// Restore segments. Because restoring segments is slow, restore them only if necessary.
movw %fs, %dx
cmpw %dx, offsetof_thread_context_ptregs_fs(%rcx)
je restored_fs
movw offsetof_thread_context_ptregs_fs(%rcx), %fs
restored_fs:
movw %gs, %si
cmpw %si, offsetof_thread_context_ptregs_gs(%rcx)
je restored_gs
movw offsetof_thread_context_ptregs_gs(%rcx), %gs
restored_gs:
// Restore other GP registers
movq offsetof_thread_context_ptregs_r15(%rcx), %r15
movq offsetof_thread_context_ptregs_r14(%rcx), %r14
movq offsetof_thread_context_ptregs_r13(%rcx), %r13
movq offsetof_thread_context_ptregs_r12(%rcx), %r12
movq offsetof_thread_context_ptregs_rbp(%rcx), %rbp
movq offsetof_thread_context_ptregs_rbx(%rcx), %rbx
movq offsetof_thread_context_ptregs_r11(%rcx), %r11
movq offsetof_thread_context_ptregs_r10(%rcx), %r10
movq offsetof_thread_context_ptregs_r9(%rcx), %r9
movq offsetof_thread_context_ptregs_r8(%rcx), %r8
movq offsetof_thread_context_ptregs_rax(%rcx), %rax
// %rcx restored last
movq offsetof_thread_context_ptregs_rdx(%rcx), %rdx
movq offsetof_thread_context_ptregs_rsi(%rcx), %rsi
movq offsetof_thread_context_ptregs_rdi(%rcx), %rdi
movq offsetof_thread_context_ptregs_rcx(%rcx), %rcx
.endm
// save_fpstate saves the current fpstate onto thread_context.fpstate.
// It assumes that:
// - the memory address of the thread_context is loaded in %rcx.
.macro save_fpstate
lea offsetof_thread_context_fpstate(%rcx), %rdi
movl $0xffffffff, %eax
movl $0xffffffff, %edx
movl __export_arch_state+offsetof_arch_state_xsave_mode(%rip), %esi
cmpl $XSAVE_MODE_XSAVEOPT, %esi
jl use_xsave
xsaveopt (%rdi)
jmp fpu_saved
use_xsave:
cmpl $XSAVE_MODE_XSAVE, %esi
jl use_fxsave
xsave (%rdi)
jmp fpu_saved
use_fxsave:
fxsave (%rdi)
fpu_saved:
.endm
// restore_fpstate restores the fpstate previously saved onto thread_context.fpstate.
// It assumes that:
// - the memory address of the thread_context is loaded in %rcx.
.macro restore_fpstate
// We only need to restore fpstate if we were signalled that it changed (syshandler
// does not modify fpstate).
cmpl $0, offsetof_thread_context_fpstate_changed(%rcx)
je fpu_restored
lea offsetof_thread_context_fpstate(%rcx), %rdi
mov __export_arch_state+offsetof_arch_state_xsave_mode(%rip), %eax
cmpl $XSAVE_MODE_FXSAVE, %eax
jz use_fxrstor
use_xrstor:
movl $0xffffffff, %eax
movl $0xffffffff, %edx
xrstor (%rdi)
jmp fpu_restored
use_fxrstor:
fxrstor (%rdi)
fpu_restored:
.endm
// Syshandler:
////////////////////////////////////////
.globl __export_syshandler;
.type __export_syshandler, @function;
.align 4, 0x00;
__export_syshandler:
// The start of this function is in a usertrap trampoline:
// mov sysmsg.ThreadStatePrep, %gs:offset(msg.State)
// mov %rsp,%gs:0x20 // msg.AppStack
// mov %gs:0x18,%rsp // msg.SyshandlerStack
// movabs $ret_addr, %rax
// mov %rax,%gs:0x8 // msg.RetAddr
// mov sysno,%eax
// jmpq *%gs:0x10 // msg.Syshandler
prepare_enter_syshandler
save_fpstate
callq __syshandler
.globl asm_restore_state;
.type asm_restore_state, @function;
asm_restore_state:
// thread_context may have changed, therefore we reload it into %rcx anew.
movq %gs:offsetof_sysmsg_context, %rcx
restore_fpstate
prepare_exit_syshandler
// Now syshandler is exiting for good; restore user rflags and %rsp.
popfq
movq 0(%rsp), %rsp
jmp *%gs:offsetof_sysmsg_ret_addr // msg->ret_addr
.size __export_syshandler, . - __export_syshandler