@@ -192,50 +192,88 @@ func HostFeatureSet() FeatureSet {
192192var (
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
197- // Reads max cpu frequency from host /proc/cpuinfo. Must run before syscall
198- // filter installation. This value is used to create the fake /proc/cpuinfo
199- // from a FeatureSet .
200- func readMaxCPUFreq () {
200+ // readCPUInfo reads cpu frequency and checks for 5-level paging support from
201+ // /proc/cpuinfo in a single pass. It sets the global cpuFreqMHz and returns
202+ // whether 5-level paging is enabled .
203+ func readCPUInfo () {
201204 cpuinfoFile , err := os .Open ("/proc/cpuinfo" )
202205 if err != nil {
203- // Leave it as 0... the VDSO bails out in the same way.
206+ // Leave cpuFreqMHz as 0... the VDSO bails out in the same way.
207+ // Default to assuming 5-level paging is not enabled.
208+ IsFiveLevelPagingEnabled = false
204209 log .Warningf ("Could not open /proc/cpuinfo: %v" , err )
205210 return
206211 }
207212 defer cpuinfoFile .Close ()
208213
209- // We get the value straight from host /proc/cpuinfo. On machines with
210- // frequency scaling enabled, this will only get the current value
211- // which will likely be inaccurate. This is fine on machines with
212- // frequency scaling disabled.
214+ var (
215+ foundCPUFreq bool
216+ foundFlags bool
217+ )
218+
213219 s := bufio .NewScanner (cpuinfoFile )
214220 for s .Scan () {
215221 line := s .Bytes ()
216- if bytes .Contains (line , []byte ("cpu MHz" )) {
222+ // We get the value straight from host /proc/cpuinfo. On machines with
223+ // frequency scaling enabled, this will only get the current value
224+ // which will likely be inaccurate. This is fine on machines with
225+ // frequency scaling disabled.
226+ if ! foundCPUFreq && bytes .Contains (line , []byte ("cpu MHz" )) {
217227 splitMHz := bytes .Split (line , []byte (":" ))
218228 if len (splitMHz ) < 2 {
219229 log .Warningf ("Could not parse /proc/cpuinfo: malformed cpu MHz line: %q" , line )
220- return
230+ } else {
231+ splitMHzStr := string (bytes .TrimSpace (splitMHz [1 ]))
232+ f64MHz , err := strconv .ParseFloat (splitMHzStr , 64 )
233+ if err != nil {
234+ log .Warningf ("Could not parse cpu MHz value %q: %v" , splitMHzStr , err )
235+ } else {
236+ cpuFreqMHz = f64MHz
237+ }
221238 }
239+ foundCPUFreq = true
240+ }
222241
223- var err error
224- splitMHzStr := string (bytes .TrimSpace (splitMHz [1 ]))
225- f64MHz , err := strconv .ParseFloat (splitMHzStr , 64 )
226- if err != nil {
227- log .Warningf ("Could not parse cpu MHz value %q: %v" , splitMHzStr , err )
228- return
242+ // https://www.kernel.org/doc/html/v6.0/x86/cpuinfo.html#flags-are-missing-when-one-or-more-of-these-happen
243+ // We check for the "la57" flag instead of "address sizes" because /proc/cpuinfo
244+ // reports "57 bits virtual" for 5lvl-capable CPUs even if 5-level paging is disabled.
245+ // The "la57" flag correctly indicates that it is enabled.
246+ if ! foundFlags && bytes .Contains (line , []byte ("flags" )) {
247+ parts := bytes .SplitN (line , []byte (":" ), 2 )
248+ if len (parts ) >= 2 {
249+ flags := bytes .Fields (parts [1 ])
250+ for _ , flag := range flags {
251+ if bytes .Equal (flag , []byte ("la57" )) {
252+ IsFiveLevelPagingEnabled = true
253+ break
254+ }
255+ }
229256 }
230- cpuFreqMHz = f64MHz
231- return
257+ foundFlags = true
258+ }
259+
260+ if foundCPUFreq && foundFlags {
261+ break
232262 }
233263 }
264+
234265 if err := s .Err (); err != nil {
235266 log .Warningf ("Could not read /proc/cpuinfo: %v" , err )
236- return
237267 }
238- log .Warningf ("Could not parse /proc/cpuinfo, it is empty or does not contain cpu MHz" )
268+
269+ if ! foundCPUFreq {
270+ log .Warningf ("Could not parse /proc/cpuinfo, it is empty or does not contain cpu MHz" )
271+ }
272+ if ! foundFlags {
273+ log .Warningf ("Could not parse /proc/cpuinfo, it is empty or does not contain flags" )
274+ }
275+ // Default to assuming 5-level paging is not enabled.
276+ IsFiveLevelPagingEnabled = false
239277}
240278
241279// xgetbv reads an extended control register.
@@ -247,6 +285,6 @@ func archInitialize() {
247285 Function : & Native {},
248286 }.Fixed ()
249287
250- readMaxCPUFreq ()
288+ readCPUInfo ()
251289 initHWCap ()
252290}
0 commit comments