diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 89dce7822864..4d0c0c829b15 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1200,6 +1200,7 @@ static void ppc_spapr_reset(void) first_ppc_cpu->env.gpr[5] = 0; first_cpu->halted = 0; first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT; + kvmppc_set_reg_ppc_online(first_ppc_cpu, 1); } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index e53ecc040ab6..7a342e01fcf5 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -31,6 +31,7 @@ #include "hw/qdev.h" #include "sysemu/device_tree.h" #include "sysemu/cpus.h" +#include "kvm_ppc.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -223,6 +224,8 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, spapr_cpu_set_endianness(cpu); spapr_cpu_update_tb_offset(cpu); + kvmppc_set_reg_ppc_online(cpu, 1); + qemu_cpu_kick(cs); rtas_st(rets, 0, RTAS_OUT_SUCCESS); @@ -242,6 +245,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUPPCState *env = &cpu->env; cs->halted = 1; + kvmppc_set_reg_ppc_online(cpu, 0); qemu_cpu_kick(cs); /* * While stopping a CPU, the guest calls H_CPPR which @@ -281,14 +285,21 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { + int virtcores_nr = smp_cpus / smp_threads; + int max_virtcores_nr = max_cpus / smp_threads; + int max_ent = max_virtcores_nr * 100; + int physical_cores_nr = kvmppc_count_ppc_cores_dt(); + if (physical_cores_nr < 0) { + physical_cores_nr = max_virtcores_nr; + } char *param_val = g_strdup_printf("MaxEntCap=%d," "DesMem=%llu," "DesProcs=%d," "MaxPlatProcs=%d", - max_cpus, + max_ent, current_machine->ram_size / M_BYTE, - smp_cpus, - max_cpus); + virtcores_nr, + physical_cores_nr); ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1); g_free(param_val); break; @@ -299,6 +310,42 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, ret = sysparm_st(buffer, length, ¶m_val, sizeof(param_val)); break; } + case RTAS_SYSPARM_PROCESSOR_MODULE_INFO: { + int i, offset = 0; + uint16_t cores[SPAPR_MAX_MODULE_TYPES], + chips[SPAPR_MAX_MODULE_TYPES], + sockets[SPAPR_MAX_MODULE_TYPES], + mtypes = 0, len; + + if (kvmppc_rtas_get_proc_module_info(&mtypes,sockets,chips,cores)) { + ret = RTAS_OUT_NOT_SUPPORTED; + break; + } + len = (mtypes*6) + 2; + + stw_be_phys(&address_space_memory, + ppc64_phys_to_real(buffer+offset), len); + offset += 2; + + stw_be_phys(&address_space_memory, + ppc64_phys_to_real(buffer+offset), mtypes); + offset += 2; + + for (i = 0; i < mtypes; i++) { + stw_be_phys(&address_space_memory, + ppc64_phys_to_real(buffer+offset), sockets[i]); + offset += 2; + stw_be_phys(&address_space_memory, + ppc64_phys_to_real(buffer+offset), chips[i]); + offset += 2; + stw_be_phys(&address_space_memory, + ppc64_phys_to_real(buffer+offset), cores[i]); + offset += 2; + } + + ret = RTAS_OUT_SUCCESS; + break; + } case RTAS_SYSPARM_UUID: ret = sysparm_st(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); break; @@ -321,6 +368,7 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: + case RTAS_SYSPARM_PROCESSOR_MODULE_INFO: case RTAS_SYSPARM_UUID: ret = RTAS_OUT_NOT_AUTHORIZED; break; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index ff5dc8753c49..23112da66ef3 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -489,6 +489,7 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 #define RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE 42 +#define RTAS_SYSPARM_PROCESSOR_MODULE_INFO 43 #define RTAS_SYSPARM_UUID 48 /* RTAS indicator/sensor types @@ -658,4 +659,6 @@ int spapr_rng_populate_dt(void *fdt); */ #define SPAPR_LMB_FLAGS_ASSIGNED 0x00000008 +#define SPAPR_MAX_MODULE_TYPES 1 + #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index b1e235dfcd6f..a5ce4b546ef4 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -580,6 +580,7 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_WORT (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb9) #define KVM_REG_PPC_SPRG9 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xba) #define KVM_REG_PPC_DBSR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbb) +#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf) /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index f338ae1de4ae..2350e48c15e9 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -16,6 +16,7 @@ #include "qemu/osdep.h" #include +#include #include #include #include @@ -2682,3 +2683,142 @@ int kvmppc_enable_hwrng(void) return kvmppc_enable_hcall(kvm_state, H_RANDOM); } + +void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) +{ + CPUState *cs = CPU(cpu); + + if (kvm_enabled()) { + kvm_set_one_reg(cs, KVM_REG_PPC_ONLINE, &online); + } +} + +/* Read an identifier from the file @path and add the identifier + * to the hash table @gt unless its already in the table. + */ +static int kvmppc_hash_file_contents(GHashTable *gt, char *path) +{ + uint32_t idx; + + idx = kvmppc_read_int_dt(path); + if (idx == -1) { + return -1; + } + + if (g_hash_table_contains(gt, GINT_TO_POINTER(idx))) { + return 0; + } + + if (!g_hash_table_insert(gt, GINT_TO_POINTER(idx), NULL)) { + fprintf(stderr, "%s() Unable to add key %d\n", __func__, idx); + return -1; + } + + return 0; +} + +static int kvmppc_glob_count_ids_dt(const char *pattern, int *count) +{ + int i, rc; + glob_t dtglob; + GHashTable *htbl; + + rc = glob(pattern, GLOB_NOSORT, NULL, &dtglob); + if (rc) + return -1; + + rc = -1; + htbl = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); + + for (i = 0; i < dtglob.gl_pathc; i++) { + if (kvmppc_hash_file_contents(htbl, dtglob.gl_pathv[i])) { + goto cleanup; + } + } + + *count = g_hash_table_size(htbl); + rc = 0; + +cleanup: + globfree(&dtglob); + g_hash_table_remove_all(htbl); + g_hash_table_destroy(htbl); + + return rc; +} + +/* Each socket's (aka module's) id is contained in the 'ibm,hw-module-id' + * file in an "xscom" directory (/proc/device-tree/xscom*). Similarly each + * chip's id is contained in the 'ibm,chip-id' file in an xscom directory. + * + * Search the xscom directories and count the number of _UNIQUE_ modules + * and chips in the system. + * + * If the system does not contain 'ibm,hw-module-id', it is assumed to be + * one chip per module (SCM), so make modules count equals to chips count. + * + * Return 0 if one or more modules and chips each are found. Return -1 + * otherwise. + */ +static int kvmppc_count_sockets_chips_dt(int *num_sockets, int *num_chips) +{ + const char *chip_pattern = "/proc/device-tree/xscom*/ibm,chip-id"; + const char *module_pattern = +"/proc/device-tree/xscom*/ibm,hw-module-id"; + + if (kvmppc_glob_count_ids_dt(module_pattern, num_sockets)) + // SCM system + if (kvmppc_glob_count_ids_dt(chip_pattern, num_sockets)) + return -1; + if (kvmppc_glob_count_ids_dt(chip_pattern, num_chips)) + return -1; + + if (*num_sockets == 0 || *num_chips == 0) { + return -1; + } + + return 0; +} + +int kvmppc_count_ppc_cores_dt(void) +{ + int rc,num_cores = 0; + const char *cores_pattern = "/proc/device-tree/cpus/PowerPC,POWER*"; + glob_t dtglob; + + rc = glob(cores_pattern, GLOB_NOSORT, NULL, &dtglob); + if (rc) { + fprintf(stderr, "%s() glob(%s) returns %d, errno %d\n", __func__, + cores_pattern, rc, errno); + return -1; + } + + num_cores = dtglob.gl_pathc; + globfree(&dtglob); + + if (num_cores == 0) + return -1; + + return num_cores; +} + +int kvmppc_rtas_get_proc_module_info(uint16_t *mtypes, uint16_t *sockets, uint16_t *chips, uint16_t *cores) +{ + int _sockets = 0, _chips = 0, _cores = 0; + + /* POWER systems only support 1 module type per system + * currently + */ + mtypes[0] = 1; + if (kvmppc_count_sockets_chips_dt(&_sockets, &_chips)) + return -1; + _cores = kvmppc_count_ppc_cores_dt(); + if ( _cores <= 0 ) + return -1; + + sockets[0] = _sockets; + chips[0] = _chips / _sockets; + cores[0] = _cores / _chips; + + return 0; +} diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 15c8af37f7eb..5c02c28f5879 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -59,6 +59,9 @@ uint64_t kvmppc_get_cap_ppc_cpu_char_behaviour(void); int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); +void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online); +int kvmppc_count_ppc_cores_dt(void); +int kvmppc_rtas_get_proc_module_info(uint16_t *mtypes, uint16_t *sockets, uint16_t *chips, uint16_t *cores); #else @@ -153,6 +156,24 @@ static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu) return -1; } +static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) +{ + return; +} + +static inline int kvmppc_count_ppc_cores_dt(void) +{ + return -1; +} + +static int kvmppc_rtas_get_proc_module_info(uint16_t *mtypes, + uint16_t *sockets, + uint16_t *chips, + uint16_t *cores) +{ + return -1; +} + #ifndef CONFIG_USER_ONLY static inline off_t kvmppc_alloc_rma(void **rma) { @@ -316,16 +337,4 @@ static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) #endif /* CONFIG_KVM */ -#ifndef KVM_INTERRUPT_SET -#define KVM_INTERRUPT_SET -1 -#endif - -#ifndef KVM_INTERRUPT_UNSET -#define KVM_INTERRUPT_UNSET -2 -#endif - -#ifndef KVM_INTERRUPT_SET_LEVEL -#define KVM_INTERRUPT_SET_LEVEL -3 -#endif - #endif /* __KVM_PPC_H__ */