Skip to content

Commit

Permalink
kernel/idt: Ensure #HV handler RIP checks work when interrupts are en…
Browse files Browse the repository at this point in the history
…abled

The #HV handler currently checks EFLAGS.IF=1 at the start of the handler
and immediately processes the #HV events if enabled without performing
checks to see if RIP is within the VMPL switch or the iret window. The
RIP windows are only checked if EFLAGS.IF=0.

This means that if interrupts are enabled during either of these windows
and a #HV occurs then the behaviour is undefined.

The code has been reorganised in this patch to examine EFLAGS.IF only
after both RIP windows have been checked.

Signed-off-by: Roy Hopkins <[email protected]>
  • Loading branch information
roy-hopkins committed Oct 22, 2024
1 parent 9870390 commit 8392dc8
Showing 1 changed file with 12 additions and 9 deletions.
21 changes: 12 additions & 9 deletions kernel/src/cpu/idt/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,7 @@ asm_entry_hv:
pushq $0
pushq %rax
pushq %rbx
pushq %rcx
// Check whether interrupts were enabled at the time of #HV. If so,
// commit to processing all #HV events immediately.
testl $0x200, 0x30(%rsp)
jnz continue_hv
pushq %rcx
// Check whether the trap RIP is within the guest VMPL return window.
movq 0x20(%rsp), %rax // fetch RIP from the trap frame.
leaq switch_vmpl_window_start(%rip), %rbx
Expand All @@ -100,18 +96,19 @@ asm_entry_hv:
// RIP is in the return window, so update RIP to the cancel point.
leaq switch_vmpl_cancel(%rip), %rbx
movq %rbx, 0x20(%rsp)
// Defer any further processing until interrupts can be processed.
jmp postpone_hv
// Continue processing the #HV
jmp hv_check_if

hv_not_vmpl_switch:
// Load the RSP value that was live at the time of the #HV.
movq 0x38(%rsp), %rcx
// Check to see whether this interrupt occurred on the IRET path
leaq iret_return_window(%rip), %rbx
cmp %rbx, %rax
jb postpone_hv
jb hv_check_if
leaq default_iret(%rip), %rbx
cmp %rbx, %rax
ja postpone_hv
ja hv_check_if
// RIP is within the IRET sequence, so the IRET should be aborted, and
// the previous exception should be handled as if it were #HV. At this
// point, there are two possibilities. If RIP is before the IRET
Expand Down Expand Up @@ -142,6 +139,12 @@ hv_not_vmpl_switch:
movq %rcx, %rsp
jmp handle_as_hv

hv_check_if:
// Check whether interrupts were enabled at the time of #HV. If so,
// commit to processing all #HV events immediately.
testl $0x200, 0x30(%rsp)
jnz continue_hv

postpone_hv:
popq %rcx
popq %rbx
Expand Down

0 comments on commit 8392dc8

Please sign in to comment.