Skip to content

Commit e99487f

Browse files
tranji-cloudgvisor-bot
authored andcommitted
Detect if 5-level paging is actually enabled on the host.
The CPUID can indicate support for 5-level paging (57 virtual address bits), but the kernel might not have it enabled. This change adds a check using `mmap` to see if addresses above 2^48 are usable. If not, `VirtualAddressBits` is capped at 48, even if CPUID suggests 57. Fixes #12308 PiperOrigin-RevId: 832518109
1 parent 4304bcd commit e99487f

File tree

5 files changed

+52
-4
lines changed

5 files changed

+52
-4
lines changed

pkg/cpuid/native_amd64.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ func HostFeatureSet() FeatureSet {
192192
var (
193193
// cpuFreqMHz is the native CPU frequency.
194194
cpuFreqMHz float64
195+
196+
// IsFiveLevelPagingEnabled is true if 5-level paging is enabled on the host.
197+
IsFiveLevelPagingEnabled bool
195198
)
196199

197200
// Reads max cpu frequency from host /proc/cpuinfo. Must run before syscall
@@ -241,6 +244,43 @@ func readMaxCPUFreq() {
241244
// xgetbv reads an extended control register.
242245
func xgetbv(reg uintptr) uint64
243246

247+
// fiveLevelPagingEnabled checks if 5-level paging is enabled on the host.
248+
func fiveLevelPagingEnabled() bool {
249+
cpuinfoFile, err := os.Open("/proc/cpuinfo")
250+
if err != nil {
251+
log.Warningf("Could not open /proc/cpuinfo: %v", err)
252+
panic(err)
253+
}
254+
defer cpuinfoFile.Close()
255+
256+
s := bufio.NewScanner(cpuinfoFile)
257+
for s.Scan() {
258+
line := s.Bytes()
259+
if bytes.Contains(line, []byte("flags")) {
260+
parts := bytes.SplitN(line, []byte(":"), 2)
261+
if len(parts) < 2 {
262+
continue
263+
}
264+
// https://www.kernel.org/doc/html/v6.0/x86/cpuinfo.html#flags-are-missing-when-one-or-more-of-these-happen
265+
// We check for the "la57" flag instead of "address sizes" because /proc/cpuinfo
266+
// reports "57 bits virtual" for 5lvl-capable CPUs even if 5-level paging is disabled.
267+
// The "la57" flag correctly indicates that it is enabled.
268+
flags := bytes.Fields(parts[1])
269+
for _, flag := range flags {
270+
if bytes.Equal(flag, []byte("la57")) {
271+
return true
272+
}
273+
}
274+
return false
275+
}
276+
}
277+
if err := s.Err(); err != nil {
278+
log.Warningf("Could not read /proc/cpuinfo: %v", err)
279+
panic(err)
280+
}
281+
panic("Could not parse /proc/cpuinfo, it is empty or does not contain flags")
282+
}
283+
244284
// archInitialize initializes hostFeatureSet.
245285
func archInitialize() {
246286
hostFeatureSet = FeatureSet{
@@ -249,4 +289,5 @@ func archInitialize() {
249289

250290
readMaxCPUFreq()
251291
initHWCap()
292+
IsFiveLevelPagingEnabled = fiveLevelPagingEnabled()
252293
}

pkg/ring0/kernel_amd64.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func (c *CPU) CR4() uint64 {
205205
if hasUMIP {
206206
cr4 |= _CR4_UMIP
207207
}
208-
if hasLA57 {
208+
if hasLA57 && cpuid.IsFiveLevelPagingEnabled {
209209
cr4 |= _CR4_LA57
210210
}
211211
return cr4
@@ -222,6 +222,9 @@ func (c *CPU) EFER() uint64 {
222222
//
223223
//go:nosplit
224224
func IsCanonical(addr uint64) bool {
225+
if hasLA57 && cpuid.IsFiveLevelPagingEnabled {
226+
return addr <= 0x00ffffffffffffff || addr >= 0xff00000000000000
227+
}
225228
return addr <= 0x00007fffffffffff || addr >= 0xffff800000000000
226229
}
227230

pkg/ring0/lib_amd64.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ var (
100100
// another time with a different FeatureSet.
101101
func Init(fs cpuid.FeatureSet) {
102102
// Initialize all sizes.
103-
VirtualAddressBits = uintptr(fs.VirtualAddressBits())
103+
if fs.VirtualAddressBits() > 48 && !cpuid.IsFiveLevelPagingEnabled {
104+
VirtualAddressBits = 48
105+
} else {
106+
VirtualAddressBits = uintptr(fs.VirtualAddressBits())
107+
}
104108
if PhysicalAddressBits == 0 {
105109
PhysicalAddressBits = uintptr(fs.PhysicalAddressBits())
106110
}

pkg/ring0/pagetables/pagetables_amd64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const (
5454
//go:nosplit
5555
func (p *PageTables) InitArch(allocator Allocator) {
5656
featureSet := cpuid.HostFeatureSet()
57-
if featureSet.HasFeature(cpuid.X86FeatureLA57) {
57+
if featureSet.HasFeature(cpuid.X86FeatureLA57) && cpuid.IsFiveLevelPagingEnabled {
5858
p.largeAddressesEnabled = true
5959
lowerTop = 0x00FFFFFFFFFFFFFF
6060
upperBottom = 0xFF00000000000000

pkg/ring0/pagetables/pagetables_amd64_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var (
3131

3232
func getLargeAddressesEnabled() bool {
3333
featureSet := cpuid.HostFeatureSet()
34-
return featureSet.HasFeature(cpuid.X86FeatureLA57)
34+
return featureSet.HasFeature(cpuid.X86FeatureLA57) && cpuid.IsFiveLevelPagingEnabled
3535
}
3636

3737
func getLowerTopAligned() uintptr {

0 commit comments

Comments
 (0)