spapr: Implement processor compatibility in ibm, client-architecture-support
Modern Linux kernels support last POWERPC CPUs so when a kernel boots, in most cases it can find a matching cpu_spec in the kernel's cpu_specs list. However if the kernel is quite old, it may be missing a definition of the actual CPU. To provide an ability for old kernels to work on modern hardware, a Processor Compatibility Mode has been introduced by the PowerISA specification. >From the hardware prospective, it is supported by the Processor Compatibility Register (PCR) which is defined in PowerISA. The register enables one of the compatibility modes (2.05/2.06/2.07). Since PCR is a hypervisor privileged register and cannot be directly accessed from the guest, the mode selection is done via ibm,client-architecture-support (CAS) RTAS call using which the guest specifies what "raw" and "architected" CPU versions it supports. QEMU works out the best match, changes a "cpu-version" property of every CPU and notifies the guest about the change by setting these properties in the buffer passed as a response on a custom H_CAS hypercall. This implements ibm,client-architecture-support parameters parsing (now only for PVRs) and cooks the device tree diff with new values for "cpu-version", "ibm,ppc-interrupt-server#s" and "ibm,ppc-interrupt-server#s" properties. Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
		
							parent
							
								
									2a48d99335
								
							
						
					
					
						commit
						3794d5482d
					
				| @ -34,6 +34,7 @@ | ||||
| #include "sysemu/kvm.h" | ||||
| #include "kvm_ppc.h" | ||||
| #include "mmu-hash64.h" | ||||
| #include "qom/cpu.h" | ||||
| 
 | ||||
| #include "hw/boards.h" | ||||
| #include "hw/ppc/ppc.h" | ||||
| @ -588,7 +589,8 @@ int spapr_h_cas_compose_response(target_ulong addr, target_ulong size) | ||||
|     _FDT((fdt_open_into(fdt_skel, fdt, size))); | ||||
|     g_free(fdt_skel); | ||||
| 
 | ||||
|     /* Place to make changes to the tree */ | ||||
|     /* Fix skeleton up */ | ||||
|     _FDT((spapr_fixup_cpu_dt(fdt, spapr))); | ||||
| 
 | ||||
|     /* Pack resulting tree */ | ||||
|     _FDT((fdt_pack(fdt))); | ||||
|  | ||||
| @ -3,6 +3,9 @@ | ||||
| #include "helper_regs.h" | ||||
| #include "hw/ppc/spapr.h" | ||||
| #include "mmu-hash64.h" | ||||
| #include "cpu-models.h" | ||||
| #include "trace.h" | ||||
| #include "kvm_ppc.h" | ||||
| 
 | ||||
| struct SPRSyncState { | ||||
|     CPUState *cs; | ||||
| @ -752,12 +755,115 @@ out: | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|     PowerPCCPU *cpu; | ||||
|     uint32_t cpu_version; | ||||
|     int ret; | ||||
| } SetCompatState; | ||||
| 
 | ||||
| static void do_set_compat(void *arg) | ||||
| { | ||||
|     SetCompatState *s = arg; | ||||
| 
 | ||||
|     cpu_synchronize_state(CPU(s->cpu)); | ||||
|     s->ret = ppc_set_compat(s->cpu, s->cpu_version); | ||||
| } | ||||
| 
 | ||||
| #define get_compat_level(cpuver) ( \ | ||||
|     ((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \ | ||||
|     ((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \ | ||||
|     ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ | ||||
|     ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) | ||||
| 
 | ||||
| static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, | ||||
|                                                   sPAPREnvironment *spapr, | ||||
|                                                   target_ulong opcode, | ||||
|                                                   target_ulong *args) | ||||
| { | ||||
|     target_ulong list = args[0]; | ||||
|     PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_); | ||||
|     CPUState *cs; | ||||
|     bool cpu_match = false; | ||||
|     unsigned old_cpu_version = cpu_->cpu_version; | ||||
|     unsigned compat_lvl = 0, cpu_version = 0; | ||||
|     unsigned max_lvl = get_compat_level(cpu_->max_compat); | ||||
|     int counter; | ||||
| 
 | ||||
|     /* Parse PVR list */ | ||||
|     for (counter = 0; counter < 512; ++counter) { | ||||
|         uint32_t pvr, pvr_mask; | ||||
| 
 | ||||
|         pvr_mask = rtas_ld(list, 0); | ||||
|         list += 4; | ||||
|         pvr = rtas_ld(list, 0); | ||||
|         list += 4; | ||||
| 
 | ||||
|         trace_spapr_cas_pvr_try(pvr); | ||||
|         if (!max_lvl && | ||||
|             ((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) { | ||||
|             cpu_match = true; | ||||
|             cpu_version = 0; | ||||
|         } else if (pvr == cpu_->cpu_version) { | ||||
|             cpu_match = true; | ||||
|             cpu_version = cpu_->cpu_version; | ||||
|         } else if (!cpu_match) { | ||||
|             /* If it is a logical PVR, try to determine the highest level */ | ||||
|             unsigned lvl = get_compat_level(pvr); | ||||
|             if (lvl) { | ||||
|                 bool is205 = (pcc_->pcr_mask & PCR_COMPAT_2_05) && | ||||
|                      (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); | ||||
|                 bool is206 = (pcc_->pcr_mask & PCR_COMPAT_2_06) && | ||||
|                     ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || | ||||
|                     (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); | ||||
| 
 | ||||
|                 if (is205 || is206) { | ||||
|                     if (!max_lvl) { | ||||
|                         /* User did not set the level, choose the highest */ | ||||
|                         if (compat_lvl <= lvl) { | ||||
|                             compat_lvl = lvl; | ||||
|                             cpu_version = pvr; | ||||
|                         } | ||||
|                     } else if (max_lvl >= lvl) { | ||||
|                         /* User chose the level, don't set higher than this */ | ||||
|                         compat_lvl = lvl; | ||||
|                         cpu_version = pvr; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         /* Terminator record */ | ||||
|         if (~pvr_mask & pvr) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* For the future use: here @list points to the first capability */ | ||||
| 
 | ||||
|     /* Parsing finished */ | ||||
|     trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, | ||||
|                         cpu_version, pcc_->pcr_mask); | ||||
| 
 | ||||
|     /* Update CPUs */ | ||||
|     if (old_cpu_version != cpu_version) { | ||||
|         CPU_FOREACH(cs) { | ||||
|             SetCompatState s = { | ||||
|                 .cpu = POWERPC_CPU(cs), | ||||
|                 .cpu_version = cpu_version, | ||||
|                 .ret = 0 | ||||
|             }; | ||||
| 
 | ||||
|             run_on_cpu(cs, do_set_compat, &s); | ||||
| 
 | ||||
|             if (s.ret < 0) { | ||||
|                 fprintf(stderr, "Unable to set compatibility mode\n"); | ||||
|                 return H_HARDWARE; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!cpu_version) { | ||||
|         return H_SUCCESS; | ||||
|     } | ||||
| 
 | ||||
|     if (!list) { | ||||
|         return H_SUCCESS; | ||||
|  | ||||
| @ -1193,6 +1193,10 @@ xics_ics_eoi(int nr) "ics_eoi: irq %#x" | ||||
| spapr_cas_failed(unsigned long n) "DT diff buffer is too small: %ld bytes" | ||||
| spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes" | ||||
| 
 | ||||
| # hw/ppc/spapr_hcall.c | ||||
| spapr_cas_pvr_try(uint32_t pvr) "%x" | ||||
| spapr_cas_pvr(uint32_t cur_pvr, bool cpu_match, uint32_t new_pvr, uint64_t pcr) "current=%x, cpu_match=%u, new=%x, compat flags=%"PRIx64 | ||||
| 
 | ||||
| # hw/ppc/spapr_iommu.c | ||||
| spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 | ||||
| spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Alexey Kardashevskiy
						Alexey Kardashevskiy