github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/platform/kvm/kvm_amd64.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build amd64
    16  // +build amd64
    17  
    18  package kvm
    19  
    20  import (
    21  	"github.com/metacubex/gvisor/pkg/cpuid"
    22  	"github.com/metacubex/gvisor/pkg/ring0"
    23  	"github.com/metacubex/gvisor/pkg/sentry/arch/fpu"
    24  )
    25  
    26  // userRegs represents KVM user registers.
    27  //
    28  // This mirrors kvm_regs.
    29  type userRegs struct {
    30  	RAX    uint64
    31  	RBX    uint64
    32  	RCX    uint64
    33  	RDX    uint64
    34  	RSI    uint64
    35  	RDI    uint64
    36  	RSP    uint64
    37  	RBP    uint64
    38  	R8     uint64
    39  	R9     uint64
    40  	R10    uint64
    41  	R11    uint64
    42  	R12    uint64
    43  	R13    uint64
    44  	R14    uint64
    45  	R15    uint64
    46  	RIP    uint64
    47  	RFLAGS uint64
    48  }
    49  
    50  // systemRegs represents KVM system registers.
    51  //
    52  // This mirrors kvm_sregs.
    53  type systemRegs struct {
    54  	CS              segment
    55  	DS              segment
    56  	ES              segment
    57  	FS              segment
    58  	GS              segment
    59  	SS              segment
    60  	TR              segment
    61  	LDT             segment
    62  	GDT             descriptor
    63  	IDT             descriptor
    64  	CR0             uint64
    65  	CR2             uint64
    66  	CR3             uint64
    67  	CR4             uint64
    68  	CR8             uint64
    69  	EFER            uint64
    70  	apicBase        uint64
    71  	interruptBitmap [(_KVM_NR_INTERRUPTS + 63) / 64]uint64
    72  }
    73  
    74  // segment is the expanded form of a segment register.
    75  //
    76  // This mirrors kvm_segment.
    77  type segment struct {
    78  	base     uint64
    79  	limit    uint32
    80  	selector uint16
    81  	typ      uint8
    82  	present  uint8
    83  	DPL      uint8
    84  	DB       uint8
    85  	S        uint8
    86  	L        uint8
    87  	G        uint8
    88  	AVL      uint8
    89  	unusable uint8
    90  	_        uint8
    91  }
    92  
    93  // Clear clears the segment and marks it unusable.
    94  func (s *segment) Clear() {
    95  	*s = segment{unusable: 1}
    96  }
    97  
    98  // selector is a segment selector.
    99  type selector uint16
   100  
   101  // tobool is a simple helper.
   102  func tobool(x ring0.SegmentDescriptorFlags) uint8 {
   103  	if x != 0 {
   104  		return 1
   105  	}
   106  	return 0
   107  }
   108  
   109  // Load loads the segment described by d into the segment s.
   110  //
   111  // The argument sel is recorded as the segment selector index.
   112  func (s *segment) Load(d *ring0.SegmentDescriptor, sel ring0.Selector) {
   113  	flag := d.Flags()
   114  	if flag&ring0.SegmentDescriptorPresent == 0 {
   115  		s.Clear()
   116  		return
   117  	}
   118  	s.base = uint64(d.Base())
   119  	s.limit = d.Limit()
   120  	s.typ = uint8((flag>>8)&0xF) | 1
   121  	s.S = tobool(flag & ring0.SegmentDescriptorSystem)
   122  	s.DPL = uint8(d.DPL())
   123  	s.present = tobool(flag & ring0.SegmentDescriptorPresent)
   124  	s.AVL = tobool(flag & ring0.SegmentDescriptorAVL)
   125  	s.L = tobool(flag & ring0.SegmentDescriptorLong)
   126  	s.DB = tobool(flag & ring0.SegmentDescriptorDB)
   127  	s.G = tobool(flag & ring0.SegmentDescriptorG)
   128  	if s.L != 0 {
   129  		s.limit = 0xffffffff
   130  	}
   131  	s.unusable = 0
   132  	s.selector = uint16(sel)
   133  }
   134  
   135  // descriptor describes a region of physical memory.
   136  //
   137  // It corresponds to the pseudo-descriptor used in the x86 LGDT and LIDT
   138  // instructions, and mirrors kvm_dtable.
   139  type descriptor struct {
   140  	base  uint64
   141  	limit uint16
   142  	_     [3]uint16
   143  }
   144  
   145  // modelControlRegister is an MSR entry.
   146  //
   147  // This mirrors kvm_msr_entry.
   148  type modelControlRegister struct {
   149  	index uint32
   150  	_     uint32
   151  	data  uint64
   152  }
   153  
   154  // modelControlRegisers is a collection of MSRs.
   155  //
   156  // This mirrors kvm_msrs.
   157  type modelControlRegisters struct {
   158  	nmsrs   uint32
   159  	_       uint32
   160  	entries [16]modelControlRegister
   161  }
   162  
   163  // cpuidEntry is a single CPUID entry.
   164  //
   165  // This mirrors kvm_cpuid_entry2.
   166  type cpuidEntry struct {
   167  	function uint32
   168  	index    uint32
   169  	flags    uint32
   170  	eax      uint32
   171  	ebx      uint32
   172  	ecx      uint32
   173  	edx      uint32
   174  	_        [3]uint32
   175  }
   176  
   177  // cpuidEntries is a collection of CPUID entries.
   178  //
   179  // This mirrors kvm_cpuid2.
   180  type cpuidEntries struct {
   181  	nr      uint32
   182  	_       uint32
   183  	entries [_KVM_NR_CPUID_ENTRIES]cpuidEntry
   184  }
   185  
   186  // Query implements cpuid.Function.Query.
   187  func (c *cpuidEntries) Query(in cpuid.In) (out cpuid.Out) {
   188  	for i := 0; i < int(c.nr); i++ {
   189  		if c.entries[i].function == in.Eax && c.entries[i].index == in.Ecx {
   190  			out.Eax = c.entries[i].eax
   191  			out.Ebx = c.entries[i].ebx
   192  			out.Ecx = c.entries[i].ecx
   193  			out.Edx = c.entries[i].edx
   194  			return
   195  		}
   196  	}
   197  	return
   198  }
   199  
   200  // Set implements cpuid.ChangeableSet.Set.
   201  func (c *cpuidEntries) Set(in cpuid.In, out cpuid.Out) {
   202  	i := 0
   203  	for ; i < int(c.nr); i++ {
   204  		if c.entries[i].function == in.Eax && c.entries[i].index == in.Ecx {
   205  			break
   206  		}
   207  	}
   208  	if i == _KVM_NR_CPUID_ENTRIES {
   209  		panic("exceeded KVM_NR_CPUID_ENTRIES")
   210  	}
   211  
   212  	c.entries[i].eax = out.Eax
   213  	c.entries[i].ebx = out.Ebx
   214  	c.entries[i].ecx = out.Ecx
   215  	c.entries[i].edx = out.Edx
   216  	if i == int(c.nr) {
   217  		c.nr++
   218  	}
   219  }
   220  
   221  // updateGlobalOnce does global initialization. It has to be called only once.
   222  func updateGlobalOnce(fd int) error {
   223  	fpu.InitHostState()
   224  	bitsForScaling = getBitsForScaling()
   225  	if err := updateSystemValues(int(fd)); err != nil {
   226  		return err
   227  	}
   228  	fs := cpuid.FeatureSet{
   229  		Function: &cpuidSupported,
   230  	}
   231  	// Calculate whether guestPCID is supported.
   232  	hasGuestPCID = fs.HasFeature(cpuid.X86FeaturePCID)
   233  	// Create a static feature set from the KVM entries. Then, we
   234  	// explicitly set OSXSAVE, since this does not come in the feature
   235  	// entries, but can be provided when the relevant CR4 bit is set.
   236  	s := &cpuidSupported
   237  	cpuid.X86FeatureOSXSAVE.Set(s)
   238  	// Explicitly disable nested virtualization. Since we don't provide
   239  	// any virtualization APIs, there is no need to enable this feature.
   240  	cpuid.X86FeatureVMX.Unset(s)
   241  	cpuid.X86FeatureSVM.Unset(s)
   242  	ring0.Init(cpuid.FeatureSet{
   243  		Function: s,
   244  	})
   245  	physicalInit()
   246  	return nil
   247  }