#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "sysemu/hw_accel.h"
#include "sysemu/sysemu.h"
#include "qemu/log.h"
#include "cpu.h"
#include "mmu-hash64.h"
#include "cpu-models.h"
#include "trace.h"
-#include "sysemu/kvm.h"
#include "kvm_ppc.h"
+#include "hw/ppc/spapr_ovec.h"
struct SPRSyncState {
- CPUState *cs;
int spr;
target_ulong value;
target_ulong mask;
};
-static void do_spr_sync(void *arg)
+static void do_spr_sync(CPUState *cs, run_on_cpu_data arg)
{
- struct SPRSyncState *s = arg;
- PowerPCCPU *cpu = POWERPC_CPU(s->cs);
+ struct SPRSyncState *s = arg.host_ptr;
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
- cpu_synchronize_state(s->cs);
+ cpu_synchronize_state(cs);
env->spr[s->spr] &= ~s->mask;
env->spr[s->spr] |= s->value;
}
target_ulong mask)
{
struct SPRSyncState s = {
- .cs = cs,
.spr = spr,
.value = value,
.mask = mask
};
- run_on_cpu(cs, do_spr_sync, &s);
+ run_on_cpu(cs, do_spr_sync, RUN_ON_CPU_HOST_PTR(&s));
}
static bool has_spr(PowerPCCPU *cpu, int spr)
return cpu->env.spr_cb[spr].name != NULL;
}
-static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index)
+static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex)
{
/*
- * hash value/pteg group index is normalized by htab_mask
+ * hash value/pteg group index is normalized by HPT mask
*/
- if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) {
+ if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) {
return false;
}
return true;
static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
- CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
- target_ulong pte_index = args[1];
+ target_ulong ptex = args[1];
target_ulong pteh = args[2];
target_ulong ptel = args[3];
unsigned apshift;
target_ulong raddr;
- target_ulong index;
- uint64_t token;
+ target_ulong slot;
+ const ppc_hash_pte64_t *hptes;
apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel);
if (!apshift) {
pteh &= ~0x60ULL;
- if (!valid_pte_index(env, pte_index)) {
+ if (!valid_ptex(cpu, ptex)) {
return H_PARAMETER;
}
- index = 0;
+ slot = ptex & 7ULL;
+ ptex = ptex & ~7ULL;
+
if (likely((flags & H_EXACT) == 0)) {
- pte_index &= ~7ULL;
- token = ppc_hash64_start_access(cpu, pte_index);
- for (; index < 8; index++) {
- if (!(ppc_hash64_load_hpte0(cpu, token, index) & HPTE64_V_VALID)) {
+ hptes = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP);
+ for (slot = 0; slot < 8; slot++) {
+ if (!(ppc_hash64_hpte0(cpu, hptes, slot) & HPTE64_V_VALID)) {
break;
}
}
- ppc_hash64_stop_access(cpu, token);
- if (index == 8) {
+ ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP);
+ if (slot == 8) {
return H_PTEG_FULL;
}
} else {
- token = ppc_hash64_start_access(cpu, pte_index);
- if (ppc_hash64_load_hpte0(cpu, token, 0) & HPTE64_V_VALID) {
- ppc_hash64_stop_access(cpu, token);
+ hptes = ppc_hash64_map_hptes(cpu, ptex + slot, 1);
+ if (ppc_hash64_hpte0(cpu, hptes, 0) & HPTE64_V_VALID) {
+ ppc_hash64_unmap_hptes(cpu, hptes, ptex + slot, 1);
return H_PTEG_FULL;
}
- ppc_hash64_stop_access(cpu, token);
+ ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1);
}
- ppc_hash64_store_hpte(cpu, pte_index + index,
- pteh | HPTE64_V_HPTE_DIRTY, ptel);
+ ppc_hash64_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel);
- args[0] = pte_index + index;
+ args[0] = ptex + slot;
return H_SUCCESS;
}
target_ulong flags,
target_ulong *vp, target_ulong *rp)
{
- CPUPPCState *env = &cpu->env;
- uint64_t token;
+ const ppc_hash_pte64_t *hptes;
target_ulong v, r;
- if (!valid_pte_index(env, ptex)) {
+ if (!valid_ptex(cpu, ptex)) {
return REMOVE_PARM;
}
- token = ppc_hash64_start_access(cpu, ptex);
- v = ppc_hash64_load_hpte0(cpu, token, 0);
- r = ppc_hash64_load_hpte1(cpu, token, 0);
- ppc_hash64_stop_access(cpu, token);
+ hptes = ppc_hash64_map_hptes(cpu, ptex, 1);
+ v = ppc_hash64_hpte0(cpu, hptes, 0);
+ r = ppc_hash64_hpte1(cpu, hptes, 0);
+ ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1);
if ((v & HPTE64_V_VALID) == 0 ||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
{
CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
- target_ulong pte_index = args[1];
+ target_ulong ptex = args[1];
target_ulong avpn = args[2];
RemoveResult ret;
- ret = remove_hpte(cpu, pte_index, avpn, flags,
+ ret = remove_hpte(cpu, ptex, avpn, flags,
&args[0], &args[1]);
switch (ret) {
case REMOVE_SUCCESS:
- check_tlb_flush(env);
+ check_tlb_flush(env, true);
return H_SUCCESS;
case REMOVE_NOT_FOUND:
}
}
exit:
- check_tlb_flush(env);
+ check_tlb_flush(env, true);
return rc;
}
{
CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
- target_ulong pte_index = args[1];
+ target_ulong ptex = args[1];
target_ulong avpn = args[2];
- uint64_t token;
+ const ppc_hash_pte64_t *hptes;
target_ulong v, r;
- if (!valid_pte_index(env, pte_index)) {
+ if (!valid_ptex(cpu, ptex)) {
return H_PARAMETER;
}
- token = ppc_hash64_start_access(cpu, pte_index);
- v = ppc_hash64_load_hpte0(cpu, token, 0);
- r = ppc_hash64_load_hpte1(cpu, token, 0);
- ppc_hash64_stop_access(cpu, token);
+ hptes = ppc_hash64_map_hptes(cpu, ptex, 1);
+ v = ppc_hash64_hpte0(cpu, hptes, 0);
+ r = ppc_hash64_hpte1(cpu, hptes, 0);
+ ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1);
if ((v & HPTE64_V_VALID) == 0 ||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
r |= (flags << 55) & HPTE64_R_PP0;
r |= (flags << 48) & HPTE64_R_KEY_HI;
r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
- ppc_hash64_store_hpte(cpu, pte_index,
+ ppc_hash64_store_hpte(cpu, ptex,
(v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0);
- ppc_hash64_tlb_flush_hpte(cpu, pte_index, v, r);
+ ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r);
+ /* Flush the tlb */
+ check_tlb_flush(env, true);
/* Don't need a memory barrier, due to qemu's global lock */
- ppc_hash64_store_hpte(cpu, pte_index, v | HPTE64_V_HPTE_DIRTY, r);
+ ppc_hash64_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r);
return H_SUCCESS;
}
static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
- CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
- target_ulong pte_index = args[1];
+ target_ulong ptex = args[1];
uint8_t *hpte;
int i, ridx, n_entries = 1;
- if (!valid_pte_index(env, pte_index)) {
+ if (!valid_ptex(cpu, ptex)) {
return H_PARAMETER;
}
if (flags & H_READ_4) {
/* Clear the two low order bits */
- pte_index &= ~(3ULL);
+ ptex &= ~(3ULL);
n_entries = 4;
}
- hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+ hpte = spapr->htab + (ptex * HASH_PTE_SIZE_64);
for (i = 0, ridx = 0; i < n_entries; i++) {
args[ridx++] = ldq_p(hpte);
return ret;
}
-/*
- * Return the offset to the requested option vector @vector in the
- * option vector table @table.
- */
-static target_ulong cas_get_option_vector(int vector, target_ulong table)
-{
- int i;
- char nr_vectors, nr_entries;
-
- if (!table) {
- return 0;
- }
-
- nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1;
- if (!vector || vector > nr_vectors) {
- return 0;
- }
- table++; /* skip nr option vectors */
-
- for (i = 0; i < vector - 1; i++) {
- nr_entries = ldl_phys(&address_space_memory, table) >> 24;
- table += nr_entries + 2;
- }
- return table;
-}
-
-typedef struct {
- PowerPCCPU *cpu;
- uint32_t cpu_version;
- Error *err;
-} SetCompatState;
+#define H_SIGNAL_SYS_RESET_ALL -1
+#define H_SIGNAL_SYS_RESET_ALLBUTSELF -2
-static void do_set_compat(void *arg)
+static target_ulong h_signal_sys_reset(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
{
- SetCompatState *s = arg;
+ target_long target = args[0];
+ CPUState *cs;
- cpu_synchronize_state(CPU(s->cpu));
- ppc_set_compat(s->cpu, s->cpu_version, &s->err);
-}
+ if (target < 0) {
+ /* Broadcast */
+ if (target < H_SIGNAL_SYS_RESET_ALLBUTSELF) {
+ return H_PARAMETER;
+ }
+
+ CPU_FOREACH(cs) {
+ PowerPCCPU *c = POWERPC_CPU(cs);
-#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)
+ if (target == H_SIGNAL_SYS_RESET_ALLBUTSELF) {
+ if (c == cpu) {
+ continue;
+ }
+ }
+ run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
+ }
+ return H_SUCCESS;
-static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr,
- unsigned max_lvl, unsigned *compat_lvl,
- unsigned *cpu_version)
-{
- unsigned lvl = get_compat_level(pvr);
- bool is205, is206, is207;
-
- if (!lvl) {
- return;
- }
-
- /* If it is a logical PVR, try to determine the highest level */
- is205 = (pcc->pcr_supported & PCR_COMPAT_2_05) &&
- (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05));
- is206 = (pcc->pcr_supported & PCR_COMPAT_2_06) &&
- ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) ||
- (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS)));
- is207 = (pcc->pcr_supported & PCR_COMPAT_2_07) &&
- (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_07));
-
- if (is205 || is206 || is207) {
- if (!max_lvl) {
- /* User did not set the level, choose the highest */
- if (*compat_lvl <= lvl) {
- *compat_lvl = lvl;
- *cpu_version = pvr;
+ } else {
+ /* Unicast */
+ CPU_FOREACH(cs) {
+ if (cpu->cpu_dt_id == target) {
+ run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
+ return H_SUCCESS;
}
- } else if (max_lvl >= lvl) {
- /* User chose the level, don't set higher than this */
- *compat_lvl = lvl;
- *cpu_version = pvr;
}
+ return H_PARAMETER;
}
}
-#define OV5_DRCONF_MEMORY 0x20
-
-static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
+static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
target_ulong list = ppc64_phys_to_real(args[0]);
- target_ulong ov_table, ov5;
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_);
- CPUState *cs;
- bool cpu_match = false, cpu_update = true, memory_update = 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;
- char ov5_byte2;
-
- /* Parse PVR list */
- for (counter = 0; counter < 512; ++counter) {
+ target_ulong ov_table;
+ bool explicit_match = false; /* Matched the CPU's real PVR */
+ uint32_t max_compat = cpu->max_compat;
+ uint32_t best_compat = 0;
+ int i;
+ sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates;
+
+ /*
+ * We scan the supplied table of PVRs looking for two things
+ * 1. Is our real CPU PVR in the list?
+ * 2. What's the "best" listed logical PVR
+ */
+ for (i = 0; i < 512; ++i) {
uint32_t pvr, pvr_mask;
pvr_mask = ldl_be_phys(&address_space_memory, list);
- list += 4;
- pvr = ldl_be_phys(&address_space_memory, list);
- 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) {
- cas_handle_compat_cpu(pcc, pvr, max_lvl, &compat_lvl, &cpu_version);
- }
- /* Terminator record */
+ pvr = ldl_be_phys(&address_space_memory, list + 4);
+ list += 8;
+
if (~pvr_mask & pvr) {
- break;
+ break; /* Terminator record */
+ }
+
+ if ((cpu->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask)) {
+ explicit_match = true;
+ } else {
+ if (ppc_check_compat(cpu, pvr, best_compat, max_compat)) {
+ best_compat = pvr;
+ }
}
}
+ if ((best_compat == 0) && (!explicit_match || max_compat)) {
+ /* We couldn't find a suitable compatibility mode, and either
+ * the guest doesn't support "raw" mode for this CPU, or raw
+ * mode is disabled because a maximum compat mode is set */
+ return H_HARDWARE;
+ }
+
/* Parsing finished */
- trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match,
- cpu_version, pcc->pcr_mask);
+ trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat);
/* Update CPUs */
- if (old_cpu_version != cpu_version) {
- CPU_FOREACH(cs) {
- SetCompatState s = {
- .cpu = POWERPC_CPU(cs),
- .cpu_version = cpu_version,
- .err = NULL,
- };
+ if (cpu->compat_pvr != best_compat) {
+ Error *local_err = NULL;
- run_on_cpu(cs, do_set_compat, &s);
-
- if (s.err) {
- error_report_err(s.err);
- return H_HARDWARE;
- }
+ ppc_set_compat_all(best_compat, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ return H_HARDWARE;
}
}
- if (!cpu_version) {
- cpu_update = false;
- }
-
/* For the future use: here @ov_table points to the first option vector */
ov_table = list;
- ov5 = cas_get_option_vector(5, ov_table);
- if (!ov5) {
- return H_SUCCESS;
- }
+ ov5_guest = spapr_ovec_parse_vector(ov_table, 5);
+
+ /* NOTE: there are actually a number of ov5 bits where input from the
+ * guest is always zero, and the platform/QEMU enables them independently
+ * of guest input. To model these properly we'd want some sort of mask,
+ * but since they only currently apply to memory migration as defined
+ * by LoPAPR 1.1, 14.5.4.8, which QEMU doesn't implement, we don't need
+ * to worry about this for now.
+ */
+ ov5_cas_old = spapr_ovec_clone(spapr->ov5_cas);
+ /* full range of negotiated ov5 capabilities */
+ spapr_ovec_intersect(spapr->ov5_cas, spapr->ov5, ov5_guest);
+ spapr_ovec_cleanup(ov5_guest);
+ /* capabilities that have been added since CAS-generated guest reset.
+ * if capabilities have since been removed, generate another reset
+ */
+ ov5_updates = spapr_ovec_new();
+ spapr->cas_reboot = spapr_ovec_diff(ov5_updates,
+ ov5_cas_old, spapr->ov5_cas);
- /* @list now points to OV 5 */
- ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2);
- if (ov5_byte2 & OV5_DRCONF_MEMORY) {
- memory_update = true;
+ if (!spapr->cas_reboot) {
+ spapr->cas_reboot =
+ (spapr_h_cas_compose_response(spapr, args[1], args[2],
+ ov5_updates) != 0);
}
+ spapr_ovec_cleanup(ov5_updates);
- if (spapr_h_cas_compose_response(spapr, args[1], args[2],
- cpu_update, memory_update)) {
+ if (spapr->cas_reboot) {
qemu_system_reset_request();
}
/* hcall-splpar */
spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
spapr_register_hypercall(H_CEDE, h_cede);
+ spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset);
/* processor register resource access h-calls */
spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0);