cas_check_pvr() stops at the first entry whose value has bits outside
its mask, before comparing that entry with the processor's PVR.  PAPR
has the PVR list processed "until either a List-entry allows the
process to continue, or the Terminator-list-entry has been processed",
and both of its "List-entry values of special interest", 0x00000000
0xFFFFFFFF ("matches any PVR value") and 0xFF000000 0x0FFFFFFF, are
terminators (B.6.2.3 in PAPR v10.60; the same text is in B.5.2.3 of
LoPAR 2.9 and B.6.2.3 of LoPAPR 1.1).  The terminator takes part in
the match like any other entry.

The check came after the comparison until commit 152ef803ceb1
("pseries: Rewrite CAS PVR compatibility logic") moved it in front.
That commit describes a change in how the compatibility mode is
chosen, not this one.

A client whose only entry for the processor is the terminator gets
"Couldn't negotiate a suitable PVR during CAS" today.  NetBSD's
ofwboot ends its list with 0xffff0000 0x0080ffff to cover Power10, and
fails on -cpu power10 this way.

Compare the entry first, and only then stop at a terminator.

Linux ends its list with a logical PVR that doubles as the terminator:
0xfffffffe 0x0f000001 (2.04 and earlier) in v6.1, and 0xfffffffd
0x0f000002 (2.05) now.  That entry now counts towards best_compat too.
Linux lists higher levels before it, so the result does not change:
with a Debian 6.1 kernel, explicit_match and best_compat came out the
same before and after on POWER8, POWER9 and Power10.

Fixes: 152ef803ceb1 ("pseries: Rewrite CAS PVR compatibility logic")
Signed-off-by: Showta Ishizaki <[email protected]>
---
 hw/ppc/spapr_hcall.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 708902934c..29b726b474 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1151,10 +1151,6 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t 
max_compat,
         pvr = ldl_be_phys(&address_space_memory, *addr + 4);
         *addr += 8;
 
-        if (~pvr_mask & pvr) {
-            break; /* Terminator record */
-        }
-
         if ((cpu->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask)) {
             explicit_match = true;
         } else {
@@ -1162,6 +1158,10 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t 
max_compat,
                 best_compat = pvr;
             }
         }
+
+        if (~pvr_mask & pvr) {
+            break; /* Terminator record */
+        }
     }
 
     if (explicit_match && kvm_enabled()) {
-- 
2.54.0 (Apple Git-157)


Reply via email to