Skip to content

Commit f962820

Browse files
tianyuzhou95gvisor-bot
authored andcommitted
platform: add SlimVM platform
SlimVM is a lightweight VMX-based platform for gVisor that accelerates sentry system calls by eliminating unnecessary user/kernel context switches. In a traditional gVisor + KVM setup, every guest syscall triggers a full VM exit to userspace, then re-enters the kernel via a regular syscall: gr0 -> hr0 (vm_exit) -> hr3 (ioctl return) -> hr0 (re-invoke syscall) SlimVM eliminates this overhead. When the guest executes a VMCALL, the VM exits into VMX root mode where SlimVM directly invokes the host kernel's syscall handler, then immediately resumes the guest: gr0 -> hr0 (vmcall + direct function call) SlimVM requires a companion kernel module that provides the /dev/slimvm device interface. See https://github.com/antgroup/slimvm for the kernel module and full documentation. A sentry->host getpid microbenchmark (Xeon 6982P-C, host kernel 5.10.134) shows the effect: | path | ns/op (mean of 10) | vs base | | ------ | -----------------: | ------: | | base | 82.5 | 1.0x | | slimvm | 420.9 | 5.1x | | kvm | 3566.1 | 43.2x | slimvm is ~8.5x faster than the kvm platform on this path. See the issue for details. Updates: #13330 Co-developed-by: Chenggang <chenggang.qcg@antfin.com> Co-developed-by: Robin Luk <lubin.lu@antgroup.com> Co-developed-by: Tiwei Bie <tiwei.btw@antgroup.com> Co-developed-by: Jianfeng Tan <henry.tjf@antgroup.com> Co-developed-by: liushi.ls <liushi.ls@antfin.com> Co-developed-by: Yong He <chenglang.hy@antgroup.com> Co-developed-by: Aaron Lu <ziqian.lzq@antfin.com> Co-developed-by: chris.zn <chris.zn@alibaba-inc.com> Co-developed-by: Lai Jiangshan <jiangshan.ljs@antfin.com> Co-developed-by: Lingfu <yupeng.chenyp@alibaba-inc.com> Co-developed-by: Min Le <lemin.lm@antgroup.com> Co-developed-by: Quan Xu <wutu.xq@alibaba-inc.com> Co-developed-by: Zhang Haoyu <zhanghaoyu.zhy@alibaba-inc.com> Co-developed-by: Dawei Shen <shendawei.sdw@antgroup.com> FUTURE_COPYBARA_INTEGRATE_REVIEW=#13331 from tianyuzhou95:albert/slimvm-pr 86eddc2 PiperOrigin-RevId: 940094281
1 parent 2153438 commit f962820

56 files changed

Lines changed: 4526 additions & 33 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.buildkite/pipeline.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ steps:
310310
# All system call tests.
311311
- <<: *common
312312
label: ":toolbox: System call tests (AMD64)"
313-
command: make BAZEL_OPTIONS=--test_tag_filters=-allsave syscall-tests
313+
command: make BAZEL_OPTIONS=--test_tag_filters=-allsave,-runsc_slimvm syscall-tests
314314
parallelism: 20
315315
agents:
316316
<<: *platform_specific_agents

pkg/cpuid/cpuid_amd64.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,24 @@ func (fs FeatureSet) VirtualAddressBits() uint32 {
192192
//go:nosplit
193193
func (fs FeatureSet) PhysicalAddressBits() uint32 {
194194
ax, _, _, _ := fs.query(addressSizes)
195-
return ax & 0xff
195+
physBits := ax & 0xff
196+
if !fs.AMD() {
197+
return physBits
198+
}
199+
200+
maxExtended, _, _, _ := fs.query(extendedFunctionInfo)
201+
if maxExtended < uint32(amdMemoryEncryptionInfo) {
202+
return physBits
203+
}
204+
205+
memEncAX, memEncBX, _, _ := fs.query(amdMemoryEncryptionInfo)
206+
if memEncAX&amdMemoryEncryptionFeatureMask == 0 {
207+
return physBits
208+
}
209+
// AMD memory encryption reduces usable physical address width by the
210+
// CPUID-reported amount. Match Linux's
211+
// arch/x86/kernel/cpu/amd.c:early_detect_mem_encrypt().
212+
return physBits - ((memEncBX >> amdPhysAddrReductionShift) & amdPhysAddrReductionMask)
196213
}
197214

198215
// CacheType describes the type of a cache, as returned in eax[4:0] for eax=4.

pkg/cpuid/native_amd64.go

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,25 @@ const xSaveInfoNumLeaves = 64 // Maximum number of xSaveInfo leaves.
6363

6464
// The "extended" functions.
6565
const (
66-
extendedStart cpuidFunction = 0x80000000
67-
extendedFunctionInfo cpuidFunction = extendedStart + 0 // Returns highest available extended function in eax.
68-
extendedFeatures = extendedStart + 1 // Returns some extended feature bits in edx and ecx.
69-
processorBrandString2 = extendedStart + 2 // Processor Name String Identifier.
70-
processorBrandString3 = extendedStart + 3 // Processor Name String Identifier.
71-
processorBrandString4 = extendedStart + 4 // Processor Name String Identifier.
72-
l1CacheAndTLBInfo = extendedStart + 5 // Returns L2 cache information.
73-
l2CacheInfo = extendedStart + 6 // Returns L2 cache information.
74-
addressSizes = extendedStart + 8 // Physical and virtual address sizes.
66+
extendedStart cpuidFunction = 0x80000000
67+
extendedFunctionInfo cpuidFunction = extendedStart + 0 // Returns highest available extended function in eax.
68+
extendedFeatures = extendedStart + 1 // Returns some extended feature bits in edx and ecx.
69+
processorBrandString2 = extendedStart + 2 // Processor Name String Identifier.
70+
processorBrandString3 = extendedStart + 3 // Processor Name String Identifier.
71+
processorBrandString4 = extendedStart + 4 // Processor Name String Identifier.
72+
l1CacheAndTLBInfo = extendedStart + 5 // Returns L2 cache information.
73+
l2CacheInfo = extendedStart + 6 // Returns L2 cache information.
74+
addressSizes = extendedStart + 8 // Physical and virtual address sizes.
75+
amdMemoryEncryptionInfo = extendedStart + 31 // AMD memory encryption information.
76+
)
77+
78+
// AMD-defined memory encryption feature bits and fields.
79+
const (
80+
amdSecureMemoryEncryption = 1 << 0
81+
amdSecureEncryptedVirtualization = 1 << 1
82+
amdMemoryEncryptionFeatureMask = amdSecureMemoryEncryption | amdSecureEncryptedVirtualization
83+
amdPhysAddrReductionShift = 6
84+
amdPhysAddrReductionMask = 0x3f
7585
)
7686

7787
var allowedBasicFunctions = [...]bool{
@@ -84,14 +94,15 @@ var allowedBasicFunctions = [...]bool{
8494
}
8595

8696
var allowedExtendedFunctions = [...]bool{
87-
extendedFunctionInfo - extendedStart: true,
88-
extendedFeatures - extendedStart: true,
89-
addressSizes - extendedStart: true,
90-
processorBrandString2 - extendedStart: true,
91-
processorBrandString3 - extendedStart: true,
92-
processorBrandString4 - extendedStart: true,
93-
l1CacheAndTLBInfo - extendedStart: true,
94-
l2CacheInfo - extendedStart: true,
97+
extendedFunctionInfo - extendedStart: true,
98+
extendedFeatures - extendedStart: true,
99+
addressSizes - extendedStart: true,
100+
processorBrandString2 - extendedStart: true,
101+
processorBrandString3 - extendedStart: true,
102+
processorBrandString4 - extendedStart: true,
103+
l1CacheAndTLBInfo - extendedStart: true,
104+
l2CacheInfo - extendedStart: true,
105+
amdMemoryEncryptionInfo - extendedStart: true,
95106
}
96107

97108
// Function executes a CPUID function.
@@ -119,7 +130,7 @@ func (i *In) normalize() {
119130
switch cpuidFunction(i.Eax) {
120131
case vendorID, featureInfo, intelCacheDescriptors, extendedFunctionInfo, extendedFeatures:
121132
i.Ecx = 0 // Ignore.
122-
case processorBrandString2, processorBrandString3, processorBrandString4, l1CacheAndTLBInfo, l2CacheInfo:
133+
case processorBrandString2, processorBrandString3, processorBrandString4, l1CacheAndTLBInfo, l2CacheInfo, amdMemoryEncryptionInfo:
123134
i.Ecx = 0 // Ignore.
124135
case intelDeterministicCacheParams, extendedFeatureInfo:
125136
// Preserve i.Ecx.

pkg/hostarch/hostarch_arm64.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@ const (
2929
// HugePageSize is the system huge page size.
3030
HugePageSize = 1 << HugePageShift
3131

32+
// JumboPageSize is the 1GB jumbo page size.
33+
JumboPageSize = 1 << JumboPageShift
34+
3235
// CacheLineSize is the size of the cache line.
3336
CacheLineSize = 1 << CacheLineShift
3437

38+
// JumboPageShift is the binary log of jumbo page whose size is 1GB.
39+
JumboPageShift = 30
40+
3541
// CacheLineShift is the binary log of the cache line size.
3642
CacheLineShift = 6
3743
)

pkg/hostarch/hostarch_x86.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ const (
2626
// HugePageSize is the system huge page size.
2727
HugePageSize = 1 << HugePageShift
2828

29+
// JumboPageSize is the 1GB jumbo page size.
30+
JumboPageSize = 1 << JumboPageShift
31+
2932
// CacheLineSize is the size of the cache line.
3033
CacheLineSize = 1 << CacheLineShift
3134

@@ -35,6 +38,9 @@ const (
3538
// HugePageShift is the binary log of the system huge page size.
3639
HugePageShift = 21
3740

41+
// JumboPageShift is the binary log of jumbo page whose size is 1GB.
42+
JumboPageShift = 30
43+
3844
// CacheLineShift is the binary log of the cache line size.
3945
CacheLineShift = 6
4046
)

pkg/hostarch/sizes_util.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const (
1111
PageMask = PageSize - 1
1212
HugePageMask = HugePageSize - 1
1313
CacheLineMask = CacheLineSize - 1
14+
JumboPageMask = ^uintptr(JumboPageSize - 1)
1415
)
1516

1617
type bytecount interface {

pkg/ring0/defs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ import (
2020
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
2121
)
2222

23+
const (
24+
// CPUIntel is Intel CPU.
25+
CPUIntel uint64 = iota
26+
27+
// CPUAMD is AMD (and compatible) CPU.
28+
CPUAMD
29+
)
30+
31+
var (
32+
CPUVendor uint64
33+
)
34+
2335
// Kernel is a global kernel object.
2436
//
2537
// This contains global state, shared by multiple CPUs.

pkg/ring0/defs_amd64.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ type kernelEntry struct {
108108
// kernelCR3 is the cr3 used for sentry kernel.
109109
kernelCR3 uintptr
110110

111+
// whether enable VMCALL
112+
enableVMCALL uint64
113+
111114
// gdt is the CPU's descriptor table.
112115
gdt descriptorTable
113116

@@ -180,6 +183,14 @@ func (c *CPU) FaultAddr() uintptr {
180183
return c.faultAddr
181184
}
182185

186+
func (c *CPU) EnableVMCALL() {
187+
c.enableVMCALL = 1
188+
}
189+
190+
func (c *CPU) DisableVMCALL() {
191+
c.enableVMCALL = 0
192+
}
193+
183194
// SwitchArchOpts are embedded in SwitchOpts.
184195
type SwitchArchOpts struct {
185196
// UserPCID indicates that the application PCID to be used on switch,

pkg/ring0/entry_amd64.s

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#define ENTRY_STACK_TOP 264 // +checkoffset . kernelEntry.stackTop
3434
#define ENTRY_CPU_SELF 272 // +checkoffset . kernelEntry.cpuSelf
3535
#define ENTRY_KERNEL_CR3 280 // +checkoffset . kernelEntry.kernelCR3
36+
#define ENTRY_ENABLE_VMCALL 288 // +checkoffset . kernelEntry.enableVMCALL
3637

3738
// Bits.
3839
#define _RFLAGS_IF 512 // +checkconst . _RFLAGS_IF
@@ -64,6 +65,12 @@
6465
#define SyscallInt80 128 // +checkconst . SyscallInt80
6566
#define Syscall 256 // +checkconst . Syscall
6667

68+
#define SyscallExit 60 // +checkconst . SyscallExit
69+
#define SyscallExitGroup 231 // +checkconst . SyscallExitGroup
70+
#define SyscallRedPill 4294967295 // +checkconst . SyscallRedPill
71+
72+
#define CPUIntel 0 // +checkconst . CPUIntel
73+
6774
#define PTRACE_R15 0 // +checkoffset linux PtraceRegs.R15
6875
#define PTRACE_R14 8 // +checkoffset linux PtraceRegs.R14
6976
#define PTRACE_R13 16 // +checkoffset linux PtraceRegs.R13
@@ -160,6 +167,15 @@
160167
#define LOAD_KERNEL_STACK(entry) \
161168
MOVQ ENTRY_STACK_TOP(entry), SP;
162169

170+
// VMCALL do vmcall/vmmcal instruction
171+
#define VMCALL() \
172+
CMPQ ·CPUVendor(SB), $CPUIntel; \
173+
JE 2(PC); \
174+
JMP 5(PC); \ // vmmcall and vmcall will be treated as 3 independent instructions
175+
BYTE $0x0F; BYTE $0x01; BYTE $0xC1; \
176+
JMP 4(PC); \ // vmmcall and vmcall will be treated as 3 independent instructions
177+
BYTE $0x0F; BYTE $0x01; BYTE $0xD9;
178+
163179
// ADDR_OF_FUNC defines a function named 'name' that returns the address of
164180
// 'symbol'.
165181
#define ADDR_OF_FUNC(name, symbol) \
@@ -488,6 +504,45 @@ sysenter_skip_gs:
488504
RET
489505

490506
kernel:
507+
// Handle any syscalls from GR0 in HR3 when EnableVMCALL is false.
508+
// Currently there are 2 use cases:
509+
// 1. Using KVM platform.
510+
// 2. Upgrading SlimVM platform. This is one such method to return M to
511+
// user mode (HR3) for upgrading platform.
512+
CMPQ ENTRY_ENABLE_VMCALL(GS), $0
513+
JE hr3_do_syscall
514+
515+
CMPQ AX, $SyscallRedPill
516+
JE hr3_do_syscall
517+
518+
CMPQ AX, $SyscallExit
519+
JE hr3_do_syscall
520+
521+
CMPQ AX, $SyscallExitGroup
522+
JE hr3_do_syscall
523+
524+
vmcall:
525+
// handle syscall from GR0 in host kernel
526+
// copy from "handle system calls from G0" part of __dune_syscall in libdune/dune.S
527+
PUSHQ R11
528+
POPFQ
529+
530+
CMPQ AX, $158 // arch_prctl syscall
531+
JNE 3(PC)
532+
CMPQ DI, $0x1002 //ARCH_SET_FS
533+
JE arch_prctl_vmcall
534+
535+
VMCALL()
536+
JMP *CX
537+
538+
arch_prctl_vmcall:
539+
VMCALL()
540+
CMPQ AX, $0
541+
JNE 2(PC)
542+
MOVQ SI, CPU_REGISTERS+PTRACE_FS_BASE(GS)
543+
JMP *CX
544+
545+
hr3_do_syscall:
491546
// We can't restore the original stack, but we can access the registers
492547
// in the CPU state directly. No need for temporary juggling.
493548
MOVQ AX, ENTRY_SCRATCH0(GS)

pkg/ring0/kernel_amd64.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,30 @@ func startGo(c *CPU) {
324324
func ReadCR2() uintptr {
325325
return readCR2()
326326
}
327+
328+
//go:noinline
329+
//go:nosplit
330+
func (c *CPU) PrefaultIDT() uint32 {
331+
return c.kernel.globalIDT[0].bits[0] + c.kernel.globalIDT[_NR_INTERRUPTS-1].bits[3]
332+
}
333+
334+
// SetCPUIDFaulting sets CPUID faulting per the boolean value.
335+
//
336+
// True is returned if faulting could be set.
337+
//
338+
//go:nosplit
339+
func SetCPUIDFaulting(on bool) bool {
340+
// Per the SDM (Vol 3, Table 2-43), PLATFORM_INFO bit 31 denotes support
341+
// for CPUID faulting, and we enable and disable via the MISC_FEATURES MSR.
342+
if rdmsr(_MSR_PLATFORM_INFO)&_PLATFORM_INFO_CPUID_FAULT != 0 {
343+
features := rdmsr(_MSR_MISC_FEATURES)
344+
if on {
345+
features |= _MISC_FEATURE_CPUID_TRAP
346+
} else {
347+
features &^= _MISC_FEATURE_CPUID_TRAP
348+
}
349+
wrmsr(_MSR_MISC_FEATURES, features)
350+
return true // Setting successful.
351+
}
352+
return false
353+
}

0 commit comments

Comments
 (0)