91 lines
3.6 KiB
ArmAsm
91 lines
3.6 KiB
ArmAsm
/* VM-Exit assembly entry.
|
|
*
|
|
* On VM-Exit, CPU loads host state from VMCS:
|
|
* - host CR0/CR3/CR4, segment registers
|
|
* - host RSP = VMCS_HOST_RSP (top of g_host_stack)
|
|
* - host RIP = VMCS_HOST_RIP (this function)
|
|
*
|
|
* VMCS does NOT save guest GPRs (RAX-R15). They remain in the CPU registers
|
|
* and must be saved explicitly if the C dispatcher wants to read or modify
|
|
* them (e.g. for VMCALL hypercall arguments and return values).
|
|
*
|
|
* This function:
|
|
* 1. Saves all 16 guest GPRs into g_guest_regs
|
|
* 2. Calls vmm_vmexit_entry() (C dispatcher, returns void)
|
|
* - Internally calls vmexit_dispatch(); sets g_guest_terminated if !resume
|
|
* 3. Restores all 16 guest GPRs from g_guest_regs (C may have modified rax)
|
|
* 4. Checks g_guest_terminated:
|
|
* - 0: executes vmresume (GPRs = guest values, possibly modified by C)
|
|
* If vmresume fails, falls through to terminate
|
|
* - 1: restores saved host RSP, jmps to saved return RIP
|
|
*
|
|
* The saved RSP/RIP point into the vmlaunch inline asm block (in vmm_self_test
|
|
* or linux_launch). After the jmp, C flow continues with failed=0.
|
|
*/
|
|
|
|
.section .text
|
|
.global vmx_vm_exit_handler
|
|
vmx_vm_exit_handler:
|
|
/* Step 1: Save all 16 guest GPRs to g_guest_regs.
|
|
* GPRs at this point hold guest values (VMCS doesn't save them). */
|
|
movq %rax, g_guest_regs + 0(%rip)
|
|
movq %rbx, g_guest_regs + 8(%rip)
|
|
movq %rcx, g_guest_regs + 16(%rip)
|
|
movq %rdx, g_guest_regs + 24(%rip)
|
|
movq %rsi, g_guest_regs + 32(%rip)
|
|
movq %rdi, g_guest_regs + 40(%rip)
|
|
movq %rbp, g_guest_regs + 48(%rip)
|
|
movq %r8, g_guest_regs + 56(%rip)
|
|
movq %r9, g_guest_regs + 64(%rip)
|
|
movq %r10, g_guest_regs + 72(%rip)
|
|
movq %r11, g_guest_regs + 80(%rip)
|
|
movq %r12, g_guest_regs + 88(%rip)
|
|
movq %r13, g_guest_regs + 96(%rip)
|
|
movq %r14, g_guest_regs + 104(%rip)
|
|
movq %r15, g_guest_regs + 112(%rip)
|
|
|
|
/* Step 2: Align stack to 16 bytes and call C dispatcher.
|
|
* The host RSP (from VMCS_HOST_RSP) is 16-byte aligned minus 8
|
|
* (for the implicit return address), so we sub 8 for alignment. */
|
|
subq $8, %rsp
|
|
call vmm_vmexit_entry
|
|
addq $8, %rsp
|
|
|
|
/* Step 3: Restore all 16 guest GPRs from g_guest_regs.
|
|
* C handlers may have modified fields (e.g. rax = hypercall return value). */
|
|
movq g_guest_regs + 0(%rip), %rax
|
|
movq g_guest_regs + 8(%rip), %rbx
|
|
movq g_guest_regs + 16(%rip), %rcx
|
|
movq g_guest_regs + 24(%rip), %rdx
|
|
movq g_guest_regs + 32(%rip), %rsi
|
|
movq g_guest_regs + 40(%rip), %rdi
|
|
movq g_guest_regs + 48(%rip), %rbp
|
|
movq g_guest_regs + 56(%rip), %r8
|
|
movq g_guest_regs + 64(%rip), %r9
|
|
movq g_guest_regs + 72(%rip), %r10
|
|
movq g_guest_regs + 80(%rip), %r11
|
|
movq g_guest_regs + 88(%rip), %r12
|
|
movq g_guest_regs + 96(%rip), %r13
|
|
movq g_guest_regs + 104(%rip), %r14
|
|
movq g_guest_regs + 112(%rip), %r15
|
|
|
|
/* Step 4: Check terminate flag */
|
|
movl g_guest_terminated(%rip), %eax
|
|
testl %eax, %eax
|
|
jnz .Lterminate
|
|
|
|
/* Resume guest: GPRs are now restored (with any C modifications).
|
|
* vmresume does not return on success — CPU enters guest. */
|
|
vmresume
|
|
/* If we reach here, vmresume failed (CF=1 or ZF=1). */
|
|
movl $1, g_guest_terminated(%rip)
|
|
/* Fall through to terminate */
|
|
|
|
.Lterminate:
|
|
/* Terminate: restore saved RSP and jmp to saved return RIP.
|
|
* The saved RSP is the caller's stack pointer before vmlaunch.
|
|
* The saved RIP is the "1:" label in the vmlaunch inline asm,
|
|
* which sets failed=0 and continues C flow. */
|
|
movq g_saved_host_rsp(%rip), %rsp
|
|
jmpq *g_saved_return_rip(%rip)
|