mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
support sError injection in kvm module on Arm64
There are 3 types of asynchronous exceptions on Arm64: sError, IRQ, FIQ. In this case, we use the sError injection method in bluepillHandler to force the guest to quit. So that the test case of "TestBounce" can be passed on Arm64. Signed-off-by: Bin Lu <bin.lu@arm.com>
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
@@ -60,3 +61,27 @@ func dieArchSetup(c *vCPU, context *arch.SignalContext64, guestRegs *userRegs) {
|
||||
func getHypercallID(addr uintptr) int {
|
||||
return _KVM_HYPERCALL_MAX
|
||||
}
|
||||
|
||||
// bluepillStopGuest is reponsible for injecting interrupt.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillStopGuest(c *vCPU) {
|
||||
// Interrupt: we must have requested an interrupt
|
||||
// window; set the interrupt line.
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_INTERRUPT,
|
||||
uintptr(unsafe.Pointer(&bounce))); errno != 0 {
|
||||
throw("interrupt injection failed")
|
||||
}
|
||||
// Clear previous injection request.
|
||||
c.runData.requestInterruptWindow = 0
|
||||
}
|
||||
|
||||
// bluepillReadyStopGuest checks whether the current vCPU is ready for interrupt injection.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillReadyStopGuest(c *vCPU) bool {
|
||||
return c.runData.readyForInterruptInjection != 0
|
||||
}
|
||||
|
||||
@@ -26,6 +26,17 @@ import (
|
||||
var (
|
||||
// The action for bluepillSignal is changed by sigaction().
|
||||
bluepillSignal = syscall.SIGILL
|
||||
|
||||
// vcpuSErr is the event of system error.
|
||||
vcpuSErr = kvmVcpuEvents{
|
||||
exception: exception{
|
||||
sErrPending: 1,
|
||||
sErrHasEsr: 0,
|
||||
pad: [6]uint8{0, 0, 0, 0, 0, 0},
|
||||
sErrEsr: 1,
|
||||
},
|
||||
rsvd: [12]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
)
|
||||
|
||||
// bluepillArchEnter is called during bluepillEnter.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
@@ -74,3 +75,23 @@ func getHypercallID(addr uintptr) int {
|
||||
return int(((addr) - arm64HypercallMMIOBase) >> 3)
|
||||
}
|
||||
}
|
||||
|
||||
// bluepillStopGuest is reponsible for injecting sError.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillStopGuest(c *vCPU) {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_VCPU_EVENTS,
|
||||
uintptr(unsafe.Pointer(&vcpuSErr))); errno != 0 {
|
||||
throw("sErr injection failed")
|
||||
}
|
||||
}
|
||||
|
||||
// bluepillReadyStopGuest checks whether the current vCPU is ready for sError injection.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillReadyStopGuest(c *vCPU) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -133,12 +133,12 @@ func bluepillHandler(context unsafe.Pointer) {
|
||||
// PIC, we can't inject an interrupt while they are
|
||||
// masked. We need to request a window if it's not
|
||||
// ready.
|
||||
if c.runData.readyForInterruptInjection == 0 {
|
||||
c.runData.requestInterruptWindow = 1
|
||||
continue // Rerun vCPU.
|
||||
} else {
|
||||
if bluepillReadyStopGuest(c) {
|
||||
// Force injection below; the vCPU is ready.
|
||||
c.runData.exitReason = _KVM_EXIT_IRQ_WINDOW_OPEN
|
||||
} else {
|
||||
c.runData.requestInterruptWindow = 1
|
||||
continue // Rerun vCPU.
|
||||
}
|
||||
case syscall.EFAULT:
|
||||
// If a fault is not serviceable due to the host
|
||||
@@ -217,17 +217,7 @@ func bluepillHandler(context unsafe.Pointer) {
|
||||
}
|
||||
}
|
||||
case _KVM_EXIT_IRQ_WINDOW_OPEN:
|
||||
// Interrupt: we must have requested an interrupt
|
||||
// window; set the interrupt line.
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_INTERRUPT,
|
||||
uintptr(unsafe.Pointer(&bounce))); errno != 0 {
|
||||
throw("interrupt injection failed")
|
||||
}
|
||||
// Clear previous injection request.
|
||||
c.runData.requestInterruptWindow = 0
|
||||
bluepillStopGuest(c)
|
||||
case _KVM_EXIT_SHUTDOWN:
|
||||
c.die(bluepillArchContext(context), "shutdown")
|
||||
return
|
||||
|
||||
@@ -46,6 +46,18 @@ type userRegs struct {
|
||||
fpRegs userFpsimdState
|
||||
}
|
||||
|
||||
type exception struct {
|
||||
sErrPending uint8
|
||||
sErrHasEsr uint8
|
||||
pad [6]uint8
|
||||
sErrEsr uint64
|
||||
}
|
||||
|
||||
type kvmVcpuEvents struct {
|
||||
exception
|
||||
rsvd [12]uint32
|
||||
}
|
||||
|
||||
// updateGlobalOnce does global initialization. It has to be called only once.
|
||||
func updateGlobalOnce(fd int) error {
|
||||
physicalInit()
|
||||
|
||||
@@ -35,6 +35,8 @@ const (
|
||||
_KVM_GET_SUPPORTED_CPUID = 0xc008ae05
|
||||
_KVM_SET_CPUID2 = 0x4008ae90
|
||||
_KVM_SET_SIGNAL_MASK = 0x4004ae8b
|
||||
_KVM_GET_VCPU_EVENTS = 0x8040ae9f
|
||||
_KVM_SET_VCPU_EVENTS = 0x4040aea0
|
||||
)
|
||||
|
||||
// KVM exit reasons.
|
||||
@@ -54,8 +56,10 @@ const (
|
||||
|
||||
// KVM capability options.
|
||||
const (
|
||||
_KVM_CAP_MAX_VCPUS = 0x42
|
||||
_KVM_CAP_ARM_VM_IPA_SIZE = 0xa5
|
||||
_KVM_CAP_MAX_VCPUS = 0x42
|
||||
_KVM_CAP_ARM_VM_IPA_SIZE = 0xa5
|
||||
_KVM_CAP_VCPU_EVENTS = 0x29
|
||||
_KVM_CAP_ARM_INJECT_SERROR_ESR = 0x9e
|
||||
)
|
||||
|
||||
// KVM limits.
|
||||
|
||||
@@ -273,8 +273,8 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
|
||||
|
||||
case ring0.PageFault:
|
||||
return c.fault(int32(syscall.SIGSEGV), info)
|
||||
case 0xaa:
|
||||
return usermem.NoAccess, nil
|
||||
case ring0.Vector(bounce): // ring0.VirtualizationException
|
||||
return usermem.NoAccess, platform.ErrContextInterrupt
|
||||
default:
|
||||
return usermem.NoAccess, platform.ErrContextSignal
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user