snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для неё не нужна. Module path github.com/sagernet/gvisor сохранён намеренно: на него опирается replace-директива суперпроекта. Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show и переносилась на новый пин копированием. SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
This commit is contained in:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
496
pkg/cpuid/cpuid_amd64.go
Normal file
496
pkg/cpuid/cpuid_amd64.go
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// FeatureSet defines features in terms of CPUID leaves and bits.
|
||||
// The kernel also exposes the presence of features to userspace through
|
||||
// a set of flags(HWCAP/HWCAP2) bits, exposed in the auxiliary vector, which
|
||||
// are necessary to read for some features (e.g. FSGSBASE).
|
||||
//
|
||||
// Common references:
|
||||
//
|
||||
// Intel:
|
||||
// - Intel SDM Volume 2, Chapter 3.2 "CPUID" (more up-to-date)
|
||||
// - Intel Application Note 485 (more detailed)
|
||||
//
|
||||
// AMD:
|
||||
// - AMD64 APM Volume 3, Appendix 3 "Obtaining Processor Information ..."
|
||||
//
|
||||
// +stateify savable
|
||||
type FeatureSet struct {
|
||||
// Function is the underlying CPUID Function.
|
||||
//
|
||||
// This is exported to allow direct calls of the underlying CPUID
|
||||
// function, where required.
|
||||
Function `state:".(Static)"`
|
||||
// hwCap stores HWCAP1/2 exposed from the elf auxiliary vector.
|
||||
hwCap hwCap
|
||||
}
|
||||
|
||||
// saveFunction saves the function as a static query.
|
||||
func (fs *FeatureSet) saveFunction() Static {
|
||||
if s, ok := fs.Function.(Static); ok {
|
||||
return s
|
||||
}
|
||||
return fs.ToStatic()
|
||||
}
|
||||
|
||||
// loadFunction saves the function as a static query.
|
||||
func (fs *FeatureSet) loadFunction(_ context.Context, s Static) {
|
||||
fs.Function = s
|
||||
}
|
||||
|
||||
// Helper to convert 3 regs into 12-byte vendor ID.
|
||||
//
|
||||
//go:nosplit
|
||||
func vendorIDFromRegs(bx, cx, dx uint32) (r [12]byte) {
|
||||
for i := uint(0); i < 4; i++ {
|
||||
b := byte(bx >> (i * 8))
|
||||
r[i] = b
|
||||
}
|
||||
|
||||
for i := uint(0); i < 4; i++ {
|
||||
b := byte(dx >> (i * 8))
|
||||
r[4+i] = b
|
||||
}
|
||||
|
||||
for i := uint(0); i < 4; i++ {
|
||||
b := byte(cx >> (i * 8))
|
||||
r[8+i] = b
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Helper to merge a 12-byte vendor ID back to registers.
|
||||
//
|
||||
// Used by static_amd64.go.
|
||||
func regsFromVendorID(r [12]byte) (bx, cx, dx uint32) {
|
||||
bx |= uint32(r[0])
|
||||
bx |= uint32(r[1]) << 8
|
||||
bx |= uint32(r[2]) << 16
|
||||
bx |= uint32(r[3]) << 24
|
||||
cx |= uint32(r[4])
|
||||
cx |= uint32(r[5]) << 8
|
||||
cx |= uint32(r[6]) << 16
|
||||
cx |= uint32(r[7]) << 24
|
||||
dx |= uint32(r[8])
|
||||
dx |= uint32(r[9]) << 8
|
||||
dx |= uint32(r[10]) << 16
|
||||
dx |= uint32(r[10]) << 24
|
||||
return
|
||||
}
|
||||
|
||||
// VendorID is the 12-char string returned in ebx:edx:ecx for eax=0.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) VendorID() [12]byte {
|
||||
_, bx, cx, dx := fs.query(vendorID)
|
||||
return vendorIDFromRegs(bx, cx, dx)
|
||||
}
|
||||
|
||||
// Helper to deconstruct signature dword.
|
||||
//
|
||||
//go:nosplit
|
||||
func signatureSplit(v uint32) (ef, em, pt, f, m, sid uint8) {
|
||||
sid = uint8(v & 0xf)
|
||||
m = uint8(v>>4) & 0xf
|
||||
f = uint8(v>>8) & 0xf
|
||||
pt = uint8(v>>12) & 0x3
|
||||
em = uint8(v>>16) & 0xf
|
||||
ef = uint8(v >> 20)
|
||||
return
|
||||
}
|
||||
|
||||
// ExtendedFamily is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ExtendedFamily() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
ef, _, _, _, _, _ := signatureSplit(ax)
|
||||
return ef
|
||||
}
|
||||
|
||||
// ExtendedModel is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ExtendedModel() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, em, _, _, _, _ := signatureSplit(ax)
|
||||
return em
|
||||
}
|
||||
|
||||
// ProcessorType is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ProcessorType() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, pt, _, _, _ := signatureSplit(ax)
|
||||
return pt
|
||||
}
|
||||
|
||||
// Family is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) Family() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, _, f, _, _ := signatureSplit(ax)
|
||||
return f
|
||||
}
|
||||
|
||||
// Model is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) Model() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, _, _, m, _ := signatureSplit(ax)
|
||||
return m
|
||||
}
|
||||
|
||||
// SteppingID is part of the processor signature.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) SteppingID() uint8 {
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
_, _, _, _, _, sid := signatureSplit(ax)
|
||||
return sid
|
||||
}
|
||||
|
||||
// VirtualAddressBits returns the number of bits available for virtual
|
||||
// addresses.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) VirtualAddressBits() uint32 {
|
||||
ax, _, _, _ := fs.query(addressSizes)
|
||||
return (ax >> 8) & 0xff
|
||||
}
|
||||
|
||||
// PhysicalAddressBits returns the number of bits available for physical
|
||||
// addresses.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) PhysicalAddressBits() uint32 {
|
||||
ax, _, _, _ := fs.query(addressSizes)
|
||||
return ax & 0xff
|
||||
}
|
||||
|
||||
// CacheType describes the type of a cache, as returned in eax[4:0] for eax=4.
|
||||
type CacheType uint8
|
||||
|
||||
const (
|
||||
// cacheNull indicates that there are no more entries.
|
||||
cacheNull CacheType = iota
|
||||
|
||||
// CacheData is a data cache.
|
||||
CacheData
|
||||
|
||||
// CacheInstruction is an instruction cache.
|
||||
CacheInstruction
|
||||
|
||||
// CacheUnified is a unified instruction and data cache.
|
||||
CacheUnified
|
||||
)
|
||||
|
||||
// Cache describes the parameters of a single cache on the system.
|
||||
//
|
||||
// This is returned by the Caches method on FeatureSet.
|
||||
type Cache struct {
|
||||
// Level is the hierarchical level of this cache (L1, L2, etc).
|
||||
Level uint32
|
||||
|
||||
// Type is the type of cache.
|
||||
Type CacheType
|
||||
|
||||
// FullyAssociative indicates that entries may be placed in any block.
|
||||
FullyAssociative bool
|
||||
|
||||
// Partitions is the number of physical partitions in the cache.
|
||||
Partitions uint32
|
||||
|
||||
// Ways is the number of ways of associativity in the cache.
|
||||
Ways uint32
|
||||
|
||||
// Sets is the number of sets in the cache.
|
||||
Sets uint32
|
||||
|
||||
// InvalidateHierarchical indicates that WBINVD/INVD from threads
|
||||
// sharing this cache acts upon lower level caches for threads sharing
|
||||
// this cache.
|
||||
InvalidateHierarchical bool
|
||||
|
||||
// Inclusive indicates that this cache is inclusive of lower cache
|
||||
// levels.
|
||||
Inclusive bool
|
||||
|
||||
// DirectMapped indicates that this cache is directly mapped from
|
||||
// address, rather than using a hash function.
|
||||
DirectMapped bool
|
||||
}
|
||||
|
||||
// Caches describes the caches on the CPU.
|
||||
//
|
||||
// Only supported on Intel; requires allocation.
|
||||
func (fs FeatureSet) Caches() (caches []Cache) {
|
||||
if !fs.Intel() {
|
||||
return
|
||||
}
|
||||
// Check against the cache line, which should be consistent.
|
||||
cacheLine := fs.CacheLine()
|
||||
for i := uint32(0); ; i++ {
|
||||
out := fs.Query(In{
|
||||
Eax: uint32(intelDeterministicCacheParams),
|
||||
Ecx: i,
|
||||
})
|
||||
t := CacheType(out.Eax & 0xf)
|
||||
if t == cacheNull {
|
||||
break
|
||||
}
|
||||
lineSize := (out.Ebx & 0xfff) + 1
|
||||
if lineSize != cacheLine {
|
||||
panic(fmt.Sprintf("Mismatched cache line size: %d vs %d", lineSize, cacheLine))
|
||||
}
|
||||
caches = append(caches, Cache{
|
||||
Type: t,
|
||||
Level: (out.Eax >> 5) & 0x7,
|
||||
FullyAssociative: ((out.Eax >> 9) & 1) == 1,
|
||||
Partitions: ((out.Ebx >> 12) & 0x3ff) + 1,
|
||||
Ways: ((out.Ebx >> 22) & 0x3ff) + 1,
|
||||
Sets: out.Ecx + 1,
|
||||
InvalidateHierarchical: (out.Edx & 1) == 0,
|
||||
Inclusive: ((out.Edx >> 1) & 1) == 1,
|
||||
DirectMapped: ((out.Edx >> 2) & 1) == 0,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CacheLine is the size of a cache line in bytes.
|
||||
//
|
||||
// All caches use the same line size. This is not enforced in the CPUID
|
||||
// encoding, but is true on all known x86 processors.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) CacheLine() uint32 {
|
||||
_, bx, _, _ := fs.query(featureInfo)
|
||||
return 8 * (bx >> 8) & 0xff
|
||||
}
|
||||
|
||||
// HasFeature tests whether or not a feature is in the given feature set.
|
||||
//
|
||||
// This function is safe to call from a nosplit context, as long as the
|
||||
// FeatureSet does not have any masked features.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) HasFeature(feature Feature) bool {
|
||||
return feature.check(fs)
|
||||
}
|
||||
|
||||
// WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is
|
||||
// a minimal /proc/cpuinfo, it is missing some fields like "microcode" that are
|
||||
// not always printed in Linux. Several fields are simply made up.
|
||||
func (fs FeatureSet) WriteCPUInfoTo(cpu, numCPU uint, w io.Writer) {
|
||||
// Avoid many redundant calls here, since this can occasionally appear
|
||||
// in the hot path. Read all basic information up front, see above.
|
||||
ax, _, _, _ := fs.query(featureInfo)
|
||||
ef, em, _, f, m, _ := signatureSplit(ax)
|
||||
vendor := fs.VendorID()
|
||||
fmt.Fprintf(w, "processor\t: %d\n", cpu)
|
||||
fmt.Fprintf(w, "vendor_id\t: %s\n", string(vendor[:]))
|
||||
fmt.Fprintf(w, "cpu family\t: %d\n", ((ef<<4)&0xff)|f)
|
||||
fmt.Fprintf(w, "model\t\t: %d\n", ((em<<4)&0xff)|m)
|
||||
fmt.Fprintf(w, "model name\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(w, "stepping\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(w, "cpu MHz\t\t: %.3f\n", cpuFreqMHz)
|
||||
// Pretend the CPU has 8192 KB of cache. Note that real /proc/cpuinfo exposes total L3 cache
|
||||
// size on Intel and per-core L2 cache size on AMD (as of Linux 6.1.0), so the value of this
|
||||
// field is not really important in practice. Any value that is chosen here will be wrong
|
||||
// by an order of magnitude on a significant chunk of x86 machines.
|
||||
// 8192 KB is selected because it is a reasonable size that will be effectively usable on
|
||||
// lightly loaded machines - most machines have 1-4MB of L3 cache per core.
|
||||
fmt.Fprintf(w, "cache size\t: 8192 KB\n")
|
||||
fmt.Fprintf(w, "physical id\t: 0\n") // Pretend all CPUs are in the same socket.
|
||||
fmt.Fprintf(w, "siblings\t: %d\n", numCPU)
|
||||
fmt.Fprintf(w, "core id\t\t: %d\n", cpu)
|
||||
fmt.Fprintf(w, "cpu cores\t: %d\n", numCPU) // Pretend each CPU is a distinct core (rather than a hyperthread).
|
||||
fmt.Fprintf(w, "apicid\t\t: %d\n", cpu)
|
||||
fmt.Fprintf(w, "initial apicid\t: %d\n", cpu)
|
||||
fmt.Fprintf(w, "fpu\t\t: yes\n")
|
||||
fmt.Fprintf(w, "fpu_exception\t: yes\n")
|
||||
fmt.Fprintf(w, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID.
|
||||
fmt.Fprintf(w, "wp\t\t: yes\n")
|
||||
fmt.Fprintf(w, "flags\t\t: %s\n", fs.FlagString())
|
||||
fmt.Fprintf(w, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
|
||||
fmt.Fprintf(w, "clflush size\t: %d\n", fs.CacheLine())
|
||||
fmt.Fprintf(w, "cache_alignment\t: %d\n", fs.CacheLine())
|
||||
fmt.Fprintf(w, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48)
|
||||
fmt.Fprintf(w, "power management:\n") // This is always here, but can be blank.
|
||||
fmt.Fprintf(w, "\n") // The /proc/cpuinfo file ends with an extra newline.
|
||||
}
|
||||
|
||||
var (
|
||||
authenticAMD = [12]byte{'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D'}
|
||||
genuineIntel = [12]byte{'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l'}
|
||||
)
|
||||
|
||||
// AMD returns true if fs describes an AMD CPU.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) AMD() bool {
|
||||
return fs.VendorID() == authenticAMD
|
||||
}
|
||||
|
||||
// Intel returns true if fs describes an Intel CPU.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) Intel() bool {
|
||||
return fs.VendorID() == genuineIntel
|
||||
}
|
||||
|
||||
// Leaf 0 of xsaveinfo function returns the size for currently
|
||||
// enabled xsave features in ebx, the maximum size if all valid
|
||||
// features are saved with xsave in ecx, and valid XCR0 bits in
|
||||
// edx:eax.
|
||||
//
|
||||
// If xSaveInfo isn't supported, cpuid will not fault but will
|
||||
// return bogus values.
|
||||
var (
|
||||
xsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ebx
|
||||
maxXsaveSize = native(In{Eax: uint32(xSaveInfo)}).Ecx
|
||||
amxTileCfgSize = native(In{Eax: uint32(xSaveInfo), Ecx: 17}).Eax
|
||||
amxTileDataSize = native(In{Eax: uint32(xSaveInfo), Ecx: 18}).Eax
|
||||
)
|
||||
|
||||
const (
|
||||
// XCR0AMXMask are the bits that enable xsave to operate on AMX TILECFG
|
||||
// and TILEDATA.
|
||||
//
|
||||
// Note: TILECFG and TILEDATA are always either both enabled or both
|
||||
// disabled.
|
||||
//
|
||||
// See Intel® 64 and IA-32 Architectures Software Developer’s Manual Vol.1
|
||||
// section 13.3 for details.
|
||||
XCR0AMXMask = uint64((1 << 17) | (1 << 18))
|
||||
)
|
||||
|
||||
// ExtendedStateSize returns the number of bytes needed to save the "extended
|
||||
// state" for the enabled features and the boundary it must be aligned to.
|
||||
// Extended state includes floating point registers, and other cpu state that's
|
||||
// not associated with the normal task context.
|
||||
//
|
||||
// Note: the return value matches the size of signal FP state frames.
|
||||
// Look at check_xstate_in_sigframe() in the kernel sources for more details.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ExtendedStateSize() (size, align uint) {
|
||||
if fs.UseXsave() {
|
||||
return uint(xsaveSize), 64
|
||||
}
|
||||
|
||||
// If we don't support xsave, we fall back to fxsave, which requires
|
||||
// 512 bytes aligned to 16 bytes.
|
||||
return 512, 16
|
||||
}
|
||||
|
||||
// AMXExtendedStateSize returns the number of bytes within the "extended state"
|
||||
// area that is used for AMX.
|
||||
func (fs FeatureSet) AMXExtendedStateSize() uint {
|
||||
if fs.UseXsave() {
|
||||
xcr0 := xgetbv(0)
|
||||
if (xcr0 & XCR0AMXMask) != 0 {
|
||||
return uint(amxTileCfgSize + amxTileDataSize)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ValidXCR0Mask returns the valid bits in control register XCR0.
|
||||
//
|
||||
// Always exclude AMX bits, because we do not support it.
|
||||
// TODO(gvisor.dev/issues/9896): Implement AMX Support.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) ValidXCR0Mask() uint64 {
|
||||
if !fs.HasFeature(X86FeatureXSAVE) {
|
||||
return 0
|
||||
}
|
||||
ax, _, _, dx := fs.query(xSaveInfo)
|
||||
return (uint64(dx)<<32 | uint64(ax)) &^ XCR0AMXMask
|
||||
}
|
||||
|
||||
// UseXsave returns the choice of fp state saving instruction.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) UseXsave() bool {
|
||||
return fs.HasFeature(X86FeatureXSAVE) && fs.HasFeature(X86FeatureOSXSAVE)
|
||||
}
|
||||
|
||||
// UseXsaveopt returns true if 'fs' supports the "xsaveopt" instruction.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) UseXsaveopt() bool {
|
||||
return fs.UseXsave() && fs.HasFeature(X86FeatureXSAVEOPT)
|
||||
}
|
||||
|
||||
// UseXsavec returns true if 'fs' supports the "xsavec" instruction.
|
||||
//
|
||||
//go:nosplit
|
||||
func (fs FeatureSet) UseXsavec() bool {
|
||||
return fs.UseXsaveopt() && fs.HasFeature(X86FeatureXSAVEC)
|
||||
}
|
||||
|
||||
// UseFSGSBASE returns true if 'fs' supports the (RD|WR)(FS|GS)BASE instructions.
|
||||
func (fs FeatureSet) UseFSGSBASE() bool {
|
||||
HWCAP2_FSGSBASE := uint64(1) << 1
|
||||
return fs.HasFeature(X86FeatureFSGSBase) && ((fs.hwCap.hwCap2 & HWCAP2_FSGSBASE) != 0)
|
||||
}
|
||||
|
||||
// archCheckHostCompatible checks for compatibility.
|
||||
func (fs FeatureSet) archCheckHostCompatible(hfs FeatureSet) error {
|
||||
// The size of a cache line must match, as it is critical to correctly
|
||||
// utilizing CLFLUSH. Other cache properties are allowed to change, as
|
||||
// they are not important to correctness.
|
||||
fsCache := fs.CacheLine()
|
||||
hostCache := hfs.CacheLine()
|
||||
if fsCache != hostCache {
|
||||
return &ErrIncompatible{
|
||||
reason: fmt.Sprintf("CPU cache line size %d incompatible with host cache line size %d", fsCache, hostCache),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllowedHWCap1 returns the HWCAP1 bits that the guest is allowed to depend
|
||||
// on.
|
||||
func (fs FeatureSet) AllowedHWCap1() uint64 {
|
||||
// HWCAPS are not supported on amd64.
|
||||
return 0
|
||||
}
|
||||
|
||||
// AllowedHWCap2 returns the HWCAP2 bits that the guest is allowed to depend
|
||||
// on.
|
||||
func (fs FeatureSet) AllowedHWCap2() uint64 {
|
||||
// HWCAPS are not supported on amd64.
|
||||
return 0
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue