Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 60 additions & 22 deletions pkg/cpuid/native_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,50 +192,88 @@ func HostFeatureSet() FeatureSet {
var (
// cpuFreqMHz is the native CPU frequency.
cpuFreqMHz float64

// IsFiveLevelPagingEnabled is true if 5-level paging is enabled on the host.
IsFiveLevelPagingEnabled bool
)

// Reads max cpu frequency from host /proc/cpuinfo. Must run before syscall
// filter installation. This value is used to create the fake /proc/cpuinfo
// from a FeatureSet.
func readMaxCPUFreq() {
// readCPUInfo reads cpu frequency and checks for 5-level paging support from
// /proc/cpuinfo in a single pass. It sets the global cpuFreqMHz and returns
// whether 5-level paging is enabled.
func readCPUInfo() {
cpuinfoFile, err := os.Open("/proc/cpuinfo")
if err != nil {
// Leave it as 0... the VDSO bails out in the same way.
// Leave cpuFreqMHz as 0... the VDSO bails out in the same way.
// Default to assuming 5-level paging is not enabled.
IsFiveLevelPagingEnabled = false
log.Warningf("Could not open /proc/cpuinfo: %v", err)
return
}
defer cpuinfoFile.Close()

// We get the value straight from host /proc/cpuinfo. On machines with
// frequency scaling enabled, this will only get the current value
// which will likely be inaccurate. This is fine on machines with
// frequency scaling disabled.
var (
foundCPUFreq bool
foundFlags bool
)

s := bufio.NewScanner(cpuinfoFile)
for s.Scan() {
line := s.Bytes()
if bytes.Contains(line, []byte("cpu MHz")) {
// We get the value straight from host /proc/cpuinfo. On machines with
// frequency scaling enabled, this will only get the current value
// which will likely be inaccurate. This is fine on machines with
// frequency scaling disabled.
if !foundCPUFreq && bytes.Contains(line, []byte("cpu MHz")) {
splitMHz := bytes.Split(line, []byte(":"))
if len(splitMHz) < 2 {
log.Warningf("Could not parse /proc/cpuinfo: malformed cpu MHz line: %q", line)
return
} else {
splitMHzStr := string(bytes.TrimSpace(splitMHz[1]))
f64MHz, err := strconv.ParseFloat(splitMHzStr, 64)
if err != nil {
log.Warningf("Could not parse cpu MHz value %q: %v", splitMHzStr, err)
} else {
cpuFreqMHz = f64MHz
}
}
foundCPUFreq = true
}

var err error
splitMHzStr := string(bytes.TrimSpace(splitMHz[1]))
f64MHz, err := strconv.ParseFloat(splitMHzStr, 64)
if err != nil {
log.Warningf("Could not parse cpu MHz value %q: %v", splitMHzStr, err)
return
// https://www.kernel.org/doc/html/v6.0/x86/cpuinfo.html#flags-are-missing-when-one-or-more-of-these-happen
// We check for the "la57" flag instead of "address sizes" because /proc/cpuinfo
// reports "57 bits virtual" for 5lvl-capable CPUs even if 5-level paging is disabled.
// The "la57" flag correctly indicates that it is enabled.
if !foundFlags && bytes.Contains(line, []byte("flags")) {
parts := bytes.SplitN(line, []byte(":"), 2)
if len(parts) >= 2 {
flags := bytes.Fields(parts[1])
for _, flag := range flags {
if bytes.Equal(flag, []byte("la57")) {
IsFiveLevelPagingEnabled = true
break
}
}
}
cpuFreqMHz = f64MHz
return
foundFlags = true
}

if foundCPUFreq && foundFlags {
break
}
}

if err := s.Err(); err != nil {
log.Warningf("Could not read /proc/cpuinfo: %v", err)
return
}
log.Warningf("Could not parse /proc/cpuinfo, it is empty or does not contain cpu MHz")

if !foundCPUFreq {
log.Warningf("Could not parse /proc/cpuinfo, it is empty or does not contain cpu MHz")
}
if !foundFlags {
log.Warningf("Could not parse /proc/cpuinfo, it is empty or does not contain flags")
}
// Default to assuming 5-level paging is not enabled.
IsFiveLevelPagingEnabled = false
}

// xgetbv reads an extended control register.
Expand All @@ -247,6 +285,6 @@ func archInitialize() {
Function: &Native{},
}.Fixed()

readMaxCPUFreq()
readCPUInfo()
initHWCap()
}
5 changes: 4 additions & 1 deletion pkg/ring0/kernel_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func (c *CPU) CR4() uint64 {
if hasUMIP {
cr4 |= _CR4_UMIP
}
if hasLA57 {
if hasLA57 && cpuid.IsFiveLevelPagingEnabled {
cr4 |= _CR4_LA57
}
return cr4
Expand All @@ -222,6 +222,9 @@ func (c *CPU) EFER() uint64 {
//
//go:nosplit
func IsCanonical(addr uint64) bool {
if hasLA57 && cpuid.IsFiveLevelPagingEnabled {
return addr <= 0x00ffffffffffffff || addr >= 0xff00000000000000
}
return addr <= 0x00007fffffffffff || addr >= 0xffff800000000000
}

Expand Down
6 changes: 5 additions & 1 deletion pkg/ring0/lib_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ var (
// another time with a different FeatureSet.
func Init(fs cpuid.FeatureSet) {
// Initialize all sizes.
VirtualAddressBits = uintptr(fs.VirtualAddressBits())
if fs.VirtualAddressBits() > 48 && !cpuid.IsFiveLevelPagingEnabled {
VirtualAddressBits = 48
} else {
VirtualAddressBits = uintptr(fs.VirtualAddressBits())
}
if PhysicalAddressBits == 0 {
PhysicalAddressBits = uintptr(fs.PhysicalAddressBits())
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/ring0/pagetables/pagetables_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const (
//go:nosplit
func (p *PageTables) InitArch(allocator Allocator) {
featureSet := cpuid.HostFeatureSet()
if featureSet.HasFeature(cpuid.X86FeatureLA57) {
if featureSet.HasFeature(cpuid.X86FeatureLA57) && cpuid.IsFiveLevelPagingEnabled {
p.largeAddressesEnabled = true
lowerTop = 0x00FFFFFFFFFFFFFF
upperBottom = 0xFF00000000000000
Expand Down
2 changes: 1 addition & 1 deletion pkg/ring0/pagetables/pagetables_amd64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var (

func getLargeAddressesEnabled() bool {
featureSet := cpuid.HostFeatureSet()
return featureSet.HasFeature(cpuid.X86FeatureLA57)
return featureSet.HasFeature(cpuid.X86FeatureLA57) && cpuid.IsFiveLevelPagingEnabled
}

func getLowerTopAligned() uintptr {
Expand Down