]> xenbits.xensource.com Git - xen.git/commitdiff
x86/cpuid: Calculate FEATURESET_NR_ENTRIES more helpfully
authorAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 10 May 2023 18:58:43 +0000 (19:58 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 3 Aug 2023 18:13:39 +0000 (19:13 +0100)
When adding new featureset words, it is convenient to split the work into
several patches.  However, GCC 12 spotted that the way we prefer to split the
work results in a real (transient) breakage whereby the policy <-> featureset
helpers perform out-of-bounds accesses on the featureset array.

Fix this by having gen-cpuid.py calculate FEATURESET_NR_ENTRIES from the
comments describing the word blocks, rather than from the XEN_CPUFEATURE()
with the greatest value.

For simplicty, require that the word blocks appear in order.  This can be
revisted if we find a good reason to have blocks out of order.

No functional change.

Reported-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
(cherry picked from commit 56e2c8e5860090a35d5f0cafe168223a2a7c0e62)

xen/tools/gen-cpuid.py

index 1e7020de462b90d9d5ba623f18292a61c85d33d6..2fe409e75e2d7e3bd8cc6660515cdd33a3819459 100755 (executable)
@@ -50,13 +50,37 @@ def parse_definitions(state):
         "\s+([\s\d]+\*[\s\d]+\+[\s\d]+)\)"
         "\s+/\*([\w!]*) .*$")
 
+    word_regex = re.compile(
+        r"^/\* .* word (\d*) \*/$")
+    last_word = -1
+
     this = sys.modules[__name__]
 
     for l in state.input.readlines():
-        # Short circuit the regex...
-        if not l.startswith("XEN_CPUFEATURE("):
+
+        # Short circuit the regexes...
+        if not (l.startswith("XEN_CPUFEATURE(") or
+                l.startswith("/* ")):
             continue
 
+        # Handle /* ... word $N */ lines
+        if l.startswith("/* "):
+
+            res = word_regex.match(l)
+            if res is None:
+                continue # Some other comment
+
+            word = int(res.groups()[0])
+
+            if word != last_word + 1:
+                raise Fail("Featureset word %u out of order (last word %u)"
+                           % (word, last_word))
+
+            last_word = word
+            state.nr_entries = word + 1
+            continue
+
+        # Handle XEN_CPUFEATURE( lines
         res = feat_regex.match(l)
 
         if res is None:
@@ -94,6 +118,15 @@ def parse_definitions(state):
     if len(state.names) == 0:
         raise Fail("No features found")
 
+    if state.nr_entries == 0:
+        raise Fail("No featureset word info found")
+
+    max_val = max(state.names.keys())
+    if (max_val >> 5) >= state.nr_entries:
+        max_name = state.names[max_val]
+        raise Fail("Feature %s (%d*32+%d) exceeds FEATURESET_NR_ENTRIES (%d)"
+                   % (max_name, max_val >> 5, max_val & 31, state.nr_entries))
+
 def featureset_to_uint32s(fs, nr):
     """ Represent a featureset as a list of C-compatible uint32_t's """
 
@@ -122,9 +155,6 @@ def format_uint32s(state, featureset, indent):
 
 def crunch_numbers(state):
 
-    # Size of bitmaps
-    state.nr_entries = nr_entries = (max(state.names.keys()) >> 5) + 1
-
     # Features common between 1d and e1d.
     common_1d = (FPU, VME, DE, PSE, TSC, MSR, PAE, MCE, CX8, APIC,
                  MTRR, PGE, MCA, CMOV, PAT, PSE36, MMX, FXSR)
@@ -325,7 +355,7 @@ def crunch_numbers(state):
     state.nr_deep_deps = len(state.deep_deps.keys())
 
     # Calculate the bitfield name declarations
-    for word in range(nr_entries):
+    for word in range(state.nr_entries):
 
         names = []
         for bit in range(32):