mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
199 lines
7.0 KiB
ArmAsm
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
|