static void do_set_compat(CPUState *cs, run_on_cpu_data arg) { PowerPCCPU *cpu = POWERPC_CPU(cs); SetCompatState *s = arg.host_ptr; ppc_set_compat(cpu, s->compat_pvr, &s->err); }
int kvm_arch_init_vcpu(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *cenv = &cpu->env; int ret; /* Gather server mmu info from KVM and update the CPU state */ kvm_fixup_page_sizes(cpu); /* Synchronize sregs with kvm */ ret = kvm_arch_sync_sregs(cpu); if (ret) { return ret; } idle_timer = qemu_new_timer_ns(vm_clock, kvm_kick_cpu, cpu); /* Some targets support access to KVM's guest TLB. */ switch (cenv->mmu_model) { case POWERPC_MMU_BOOKE206: ret = kvm_booke206_tlb_init(cpu); break; default: break; } return ret; }
static void spapr_cpu_core_realize_child(Object *child, sPAPRMachineState *spapr, Error **errp) { Error *local_err = NULL; CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); object_property_set_bool(child, true, "realized", &local_err); if (local_err) { goto error; } spapr_cpu_init(spapr, cpu, &local_err); if (local_err) { goto error; } cpu->intc = icp_create(child, spapr->icp_type, XICS_FABRIC(spapr), &local_err); if (local_err) { goto error; } return; error: error_propagate(errp, local_err); }
static void spapr_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); cpu_reset(cs); /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ cs->halted = 1; env->spr[SPR_HIOR] = 0; /* Disable Power-saving mode Exit Cause exceptions for the CPU. * This can cause issues when rebooting the guest if a secondary * is awaken */ if (cs != first_cpu) { env->spr[SPR_LPCR] &= ~pcc->lpcr_pm; } /* Set compatibility mode to match the boot CPU, which was either set * by the machine reset code or by CAS. This should never fail. */ if (cs != first_cpu) { ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); } }
static void spapr_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); target_ulong lpcr; cpu_reset(cs); /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ cs->halted = 1; /* Set compatibility mode to match the boot CPU, which was either set * by the machine reset code or by CAS. This should never fail. */ ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); env->spr[SPR_HIOR] = 0; lpcr = env->spr[SPR_LPCR]; /* Set emulated LPCR to not send interrupts to hypervisor. Note that * under KVM, the actual HW LPCR will be set differently by KVM itself, * the settings below ensure proper operations with TCG in absence of * a real hypervisor. * * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for * real mode accesses, which thankfully defaults to 0 and isn't * accessible in guest mode. * * Disable Power-saving mode Exit Cause exceptions for the CPU, so * we don't get spurious wakups before an RTAS start-cpu call. */ lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); lpcr |= LPCR_LPES0 | LPCR_LPES1; /* Set RMLS to the max (ie, 16G) */ lpcr &= ~LPCR_RMLS; lpcr |= 1ull << LPCR_RMLS_SHIFT; ppc_store_lpcr(cpu, lpcr); /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = 0xffffffffffffffffull; spapr_cpu->vpa_addr = 0; spapr_cpu->slb_shadow_addr = 0; spapr_cpu->slb_shadow_size = 0; spapr_cpu->dtl_addr = 0; spapr_cpu->dtl_size = 0; spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu); kvm_check_mmu(cpu, &error_fatal); }
void ppc_cpu_do_interrupt(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; cs->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; }
static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); PowerPCCPU *cpu; CPUPPCState *env; Object *obj; Error *err = NULL; obj = object_property_get_link(OBJECT(dev), ICP_PROP_XICS, &err); if (!obj) { error_propagate_prepend(errp, err, "required link '" ICP_PROP_XICS "' not found: "); return; } icp->xics = XICS_FABRIC(obj); obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err); if (!obj) { error_propagate_prepend(errp, err, "required link '" ICP_PROP_CPU "' not found: "); return; } cpu = POWERPC_CPU(obj); icp->cs = CPU(obj); env = &cpu->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER7: icp->output = env->irq_inputs[POWER7_INPUT_INT]; break; case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */ icp->output = env->irq_inputs[POWER9_INPUT_INT]; break; case PPC_FLAGS_INPUT_970: icp->output = env->irq_inputs[PPC970_INPUT_INT]; break; default: error_setg(errp, "XICS interrupt controller does not support this CPU bus model"); return; } if (kvm_irqchip_in_kernel()) { icp_kvm_realize(dev, &err); if (err) { error_propagate(errp, err); return; } } qemu_register_reset(icp_reset_handler, dev); vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); }
int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; int r = ppc_gdb_register_len_apple(n); if (!r) { return r; } if (n < 32) { /* gprs */ gdb_get_reg64(mem_buf, env->gpr[n]); } else if (n < 64) { /* fprs */ stfq_p(mem_buf, env->fpr[n-32]); } else if (n < 96) { /* Altivec */ stq_p(mem_buf, n - 64); stq_p(mem_buf + 8, 0); } else { switch (n) { case 64 + 32: gdb_get_reg64(mem_buf, env->nip); break; case 65 + 32: gdb_get_reg64(mem_buf, env->msr); break; case 66 + 32: { uint32_t cr = 0; int i; for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } gdb_get_reg32(mem_buf, cr); break; } case 67 + 32: gdb_get_reg64(mem_buf, env->lr); break; case 68 + 32: gdb_get_reg64(mem_buf, env->ctr); break; case 69 + 32: gdb_get_reg64(mem_buf, env->xer); break; case 70 + 32: gdb_get_reg64(mem_buf, env->fpscr); break; } } if (msr_le) { /* If cpu is in LE mode, convert memory contents to LE. */ ppc_gdb_swap_register(mem_buf, n, r); } return r; }
static void spapr_cpu_set_endianness(PowerPCCPU *cpu) { PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(fcpu); if (!pcc->interrupts_big_endian(fcpu)) { cpu->env.spr[SPR_LPCR] |= LPCR_ILE; } }
static int spapr_irq_post_load_xics(sPAPRMachineState *spapr, int version_id) { if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { CPUState *cs; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); icp_resend(ICP(cpu->intc)); } }
CpuInfoList *qmp_query_cpus(Error **errp) { CpuInfoList *head = NULL, *cur_item = NULL; CPUState *cpu; for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { CpuInfoList *info; #if defined(TARGET_I386) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; #elif defined(TARGET_PPC) PowerPCCPU *ppc_cpu = POWERPC_CPU(cpu); CPUPPCState *env = &ppc_cpu->env; #elif defined(TARGET_SPARC) SPARCCPU *sparc_cpu = SPARC_CPU(cpu); CPUSPARCState *env = &sparc_cpu->env; #elif defined(TARGET_MIPS) MIPSCPU *mips_cpu = MIPS_CPU(cpu); CPUMIPSState *env = &mips_cpu->env; #endif cpu_synchronize_state(cpu); info = g_malloc0(sizeof(*info)); info->value = g_malloc0(sizeof(*info->value)); info->value->CPU = cpu->cpu_index; info->value->current = (cpu == first_cpu); info->value->halted = cpu->halted; info->value->thread_id = cpu->thread_id; #if defined(TARGET_I386) info->value->has_pc = true; info->value->pc = env->eip + env->segs[R_CS].base; #elif defined(TARGET_PPC) info->value->has_nip = true; info->value->nip = env->nip; #elif defined(TARGET_SPARC) info->value->has_pc = true; info->value->pc = env->pc; info->value->has_npc = true; info->value->npc = env->npc; #elif defined(TARGET_MIPS) info->value->has_PC = true; info->value->PC = env->active_tc.PC; #endif /* XXX: waiting for the qapi to support GSList */ if (!cur_item) { head = cur_item = info; } else { cur_item->next = info; cur_item = info; } } return head; }
static void do_spr_sync(void *arg) { struct SPRSyncState *s = arg; PowerPCCPU *cpu = POWERPC_CPU(s->cs); CPUPPCState *env = &cpu->env; cpu_synchronize_state(s->cs); env->spr[s->spr] &= ~s->mask; env->spr[s->spr] |= s->value; }
static int kvm_put_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; struct kvm_one_reg reg; int ret; /* SLB shadow or DTL can't be registered unless a master VPA is * registered. That means when restoring state, if a VPA *is* * registered, we need to set that up first. If not, we need to * deregister the others before deregistering the master VPA */ assert(env->vpa_addr || !(env->slb_shadow_addr || env->dtl_addr)); if (env->vpa_addr) { reg.id = KVM_REG_PPC_VPA_ADDR; reg.addr = (uintptr_t)&env->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to set VPA address to KVM: %s\n", strerror(errno)); return ret; } } assert((uintptr_t)&env->slb_shadow_size == ((uintptr_t)&env->slb_shadow_addr + 8)); reg.id = KVM_REG_PPC_VPA_SLB; reg.addr = (uintptr_t)&env->slb_shadow_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to set SLB shadow state to KVM: %s\n", strerror(errno)); return ret; } assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); reg.id = KVM_REG_PPC_VPA_DTL; reg.addr = (uintptr_t)&env->dtl_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to set dispatch trace log state to KVM: %s\n", strerror(errno)); return ret; } if (!env->vpa_addr) { reg.id = KVM_REG_PPC_VPA_ADDR; reg.addr = (uintptr_t)&env->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to set VPA address to KVM: %s\n", strerror(errno)); return ret; } } return 0; }
int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; int r = ppc_gdb_register_len_apple(n); if (!r) { return r; } if (msr_le) { /* If cpu is in LE mode, convert memory contents to LE. */ ppc_gdb_swap_register(mem_buf, n, r); } if (n < 32) { /* gprs */ env->gpr[n] = ldq_p(mem_buf); } else if (n < 64) { /* fprs */ env->fpr[n-32] = ldfq_p(mem_buf); } else { switch (n) { case 64 + 32: env->nip = ldq_p(mem_buf); break; case 65 + 32: ppc_store_msr(env, ldq_p(mem_buf)); break; case 66 + 32: { uint32_t cr = ldl_p(mem_buf); int i; for (i = 0; i < 8; i++) { env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; } break; } case 67 + 32: env->lr = ldq_p(mem_buf); break; case 68 + 32: env->ctr = ldq_p(mem_buf); break; case 69 + 32: env->xer = ldq_p(mem_buf); break; case 70 + 32: /* fpscr */ store_fpscr(env, ldq_p(mem_buf), 0xffffffff); break; } } return r; }
static void spapr_irq_print_info_xics(sPAPRMachineState *spapr, Monitor *mon) { CPUState *cs; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); icp_pic_print_info(ICP(cpu->intc), mon); } ics_pic_print_info(spapr->ics, mon); }
static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(first_cpu); CPUPPCState *env = &cpu->env; if (!val) { /* TODO: We don't support disabling dfp yet */ return; } if (!(env->insns_flags2 & PPC2_DFP)) { error_setg(errp, "DFP support not available, try cap-dfp=off"); } }
int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; int r = ppc_gdb_register_len(n); if (!r) { return r; } if (n < 32) { /* gprs */ gdb_get_regl(mem_buf, env->gpr[n]); } else if (n < 64) { /* fprs */ stfq_p(mem_buf, env->fpr[n-32]); } else { switch (n) { case 64: gdb_get_regl(mem_buf, env->nip); break; case 65: gdb_get_regl(mem_buf, env->msr); break; case 66: { uint32_t cr = 0; int i; for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } gdb_get_reg32(mem_buf, cr); break; } case 67: gdb_get_regl(mem_buf, env->lr); break; case 68: gdb_get_regl(mem_buf, env->ctr); break; case 69: gdb_get_regl(mem_buf, env->xer); break; case 70: gdb_get_reg32(mem_buf, env->fpscr); break; } } ppc_maybe_bswap_register(env, mem_buf, r); return r; }
int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; int r = ppc_gdb_register_len(n); if (!r) { return r; } ppc_maybe_bswap_register(env, mem_buf, r); if (n < 32) { /* gprs */ env->gpr[n] = ldtul_p(mem_buf); } else if (n < 64) { /* fprs */ env->fpr[n-32] = ldfq_p(mem_buf); } else { switch (n) { case 64: env->nip = ldtul_p(mem_buf); break; case 65: ppc_store_msr(env, ldtul_p(mem_buf)); break; case 66: { uint32_t cr = ldl_p(mem_buf); int i; for (i = 0; i < 8; i++) { env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; } break; } case 67: env->lr = ldtul_p(mem_buf); break; case 68: env->ctr = ldtul_p(mem_buf); break; case 69: env->xer = ldtul_p(mem_buf); break; case 70: /* fpscr */ store_fpscr(env, ldtul_p(mem_buf), 0xffffffff); break; } } return r; }
static void cap_vsx_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(first_cpu); CPUPPCState *env = &cpu->env; if (!val) { /* TODO: We don't support disabling vsx yet */ return; } /* Allowable CPUs in spapr_cpu_core.c should already have gotten * rid of anything that doesn't do VMX */ g_assert(env->insns_flags & PPC_ALTIVEC); if (!(env->insns_flags2 & PPC2_VSX)) { error_setg(errp, "VSX support not available, try cap-vsx=off"); } }
uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, uint32_t nret, uint64_t rets) { int token; for (token = 0; token < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; token++) { if (strcmp(cmd, rtas_table[token].name) == 0) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); PowerPCCPU *cpu = POWERPC_CPU(first_cpu); rtas_table[token].fn(cpu, spapr, token + RTAS_TOKEN_BASE, nargs, args, nret, rets); return H_SUCCESS; } } return H_PARAMETER; }
static void xscom_complete(CPUState *cs, uint64_t hmer_bits) { /* * TODO: When the read/write comes from the monitor, NULL is * passed for the cpu, and no CPU completion is generated. */ if (cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; /* * TODO: Need a CPU helper to set HMER, also handle generation * of HMIs */ cpu_synchronize_state(cs); env->spr[SPR_HMER] |= hmer_bits; } }
static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) { sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); int i; for (i = 0; i < cc->nr_threads; i++) { Object *obj = OBJECT(sc->threads[i]); DeviceState *dev = DEVICE(obj); CPUState *cs = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(cs); spapr_cpu_destroy(cpu); object_unparent(cpu->intc); cpu_remove_sync(cs); object_unparent(obj); } g_free(sc->threads); }
static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) { sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(dev)); size_t size = object_type_get_instance_size(scc->cpu_type); CPUCore *cc = CPU_CORE(dev); int i; for (i = 0; i < cc->nr_threads; i++) { void *obj = sc->threads + i * size; DeviceState *dev = DEVICE(obj); CPUState *cs = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(cs); spapr_cpu_destroy(cpu); object_unparent(cpu->intc); cpu_remove_sync(cs); object_unparent(obj); } g_free(sc->threads); }
static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) { SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); CPUCore *cc = CPU_CORE(sc); Object *obj; char *id; CPUState *cs; PowerPCCPU *cpu; Error *local_err = NULL; obj = object_new(scc->cpu_type); cs = CPU(obj); cpu = POWERPC_CPU(obj); cs->cpu_index = cc->core_id + i; spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); if (local_err) { goto err; } cpu->node_id = sc->node_id; id = g_strdup_printf("thread[%d]", i); object_property_add_child(OBJECT(sc), id, obj, &local_err); g_free(id); if (local_err) { goto err; } cpu->machine_data = g_new0(SpaprCpuState, 1); object_unref(obj); return cpu; err: object_unref(obj); error_propagate(errp, local_err); return NULL; }
static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr, unsigned size) { uint32_t value = 0; PowerPCCPU *cpu = POWERPC_CPU(current_cpu); CPUPPCState *env = &cpu->env; addr &= MPC8544_GUTS_MMIO_SIZE - 1; switch (addr) { case MPC8544_GUTS_ADDR_PVR: value = env->spr[SPR_PVR]; break; case MPC8544_GUTS_ADDR_SVR: value = env->spr[SPR_E500_SVR]; break; default: fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr); break; } return value; }
static void spapr_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; cpu_reset(cs); /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ cs->halted = 1; env->spr[SPR_HIOR] = 0; /* Set compatibility mode to match the boot CPU, which was either set * by the machine reset code or by CAS. This should never fail. */ if (cs != first_cpu) { ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); } }
static int kvm_get_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; struct kvm_one_reg reg; int ret; reg.id = KVM_REG_PPC_VPA_ADDR; reg.addr = (uintptr_t)&env->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to get VPA address from KVM: %s\n", strerror(errno)); return ret; } assert((uintptr_t)&env->slb_shadow_size == ((uintptr_t)&env->slb_shadow_addr + 8)); reg.id = KVM_REG_PPC_VPA_SLB; reg.addr = (uintptr_t)&env->slb_shadow_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to get SLB shadow state from KVM: %s\n", strerror(errno)); return ret; } assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); reg.id = KVM_REG_PPC_VPA_DTL; reg.addr = (uintptr_t)&env->dtl_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { dprintf("Unable to get dispatch trace log state from KVM: %s\n", strerror(errno)); return ret; } return 0; }
static void spapr_cpu_core_realize_child(Object *child, sPAPRMachineState *spapr, Error **errp) { Error *local_err = NULL; CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); Object *obj; object_property_set_bool(child, true, "realized", &local_err); if (local_err) { goto error; } spapr_cpu_init(spapr, cpu, &local_err); if (local_err) { goto error; } obj = object_new(spapr->icp_type); object_property_add_child(child, "icp", obj, &error_abort); object_unref(obj); object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(spapr), &error_abort); object_property_add_const_link(obj, ICP_PROP_CPU, child, &error_abort); object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { goto free_icp; } return; free_icp: object_unparent(obj); error: error_propagate(errp, local_err); }
/* * The PowerNV cores (and threads) need to use real HW ids and not an * incremental index like it has been done on other platforms. This HW * id is stored in the CPU PIR, it is used to create cpu nodes in the * device tree, used in XSCOM to address cores and in interrupt * servers. */ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) { CPUState *cs = CPU(DEVICE(pc->threads)); DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPU *cpu = POWERPC_CPU(cs); int smt_threads = CPU_CORE(pc)->nr_threads; CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); uint32_t servers_prop[smt_threads]; int i; uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; uint32_t tbfreq = PNV_TIMEBASE_FREQ; uint32_t cpufreq = 1000000000; uint32_t page_sizes_prop[64]; size_t page_sizes_prop_size; const uint8_t pa_features[] = { 24, 0, 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; int offset; char *nodename; int cpus_offset = get_cpus_node(fdt); nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir); offset = fdt_add_subnode(fdt, cpus_offset, nodename); _FDT(offset); g_free(nodename); _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id))); _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir))); _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir))); _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", env->dcache_line_size))); _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", env->dcache_line_size))); _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", env->icache_line_size))); _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", env->icache_line_size))); if (pcc->l1_dcache_size) { _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", pcc->l1_dcache_size))); } else { warn_report("Unknown L1 dcache size for cpu"); } if (pcc->l1_icache_size) { _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", pcc->l1_icache_size))); } else { warn_report("Unknown L1 icache size for cpu"); } _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); if (env->spr_cb[SPR_PURR].oea_read) { _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); } if (env->mmu_model & POWERPC_MMU_1TSEG) { _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", segs, sizeof(segs)))); } /* Advertise VMX/VSX (vector extensions) if available * 0 / no property == no vector extensions * 1 == VMX / Altivec available * 2 == VSX available */ if (env->insns_flags & PPC_ALTIVEC) { uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1; _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx))); } /* Advertise DFP (Decimal Floating Point) if available * 0 / no property == no DFP * 1 == DFP available */ if (env->insns_flags2 & PPC2_DFP) { _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); } page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop, sizeof(page_sizes_prop)); if (page_sizes_prop_size) { _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", page_sizes_prop, page_sizes_prop_size))); } _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, sizeof(pa_features)))); /* Build interrupt servers properties */ for (i = 0; i < smt_threads; i++) { servers_prop[i] = cpu_to_be32(pc->pir + i); } _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", servers_prop, sizeof(servers_prop)))); }
static int ppce500_load_device_tree(QEMUMachineInitArgs *args, PPCE500Params *params, hwaddr addr, hwaddr initrd_base, hwaddr initrd_size, bool dry_run) { CPUPPCState *env = first_cpu->env_ptr; int ret = -1; uint64_t mem_reg_property[] = { 0, cpu_to_be64(args->ram_size) }; int fdt_size; void *fdt; uint8_t hypercall[16]; uint32_t clock_freq = 400000000; uint32_t tb_freq = 400000000; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; char soc[128]; char mpic[128]; uint32_t mpic_ph; uint32_t msi_ph; char gutil[128]; char pci[128]; char msi[128]; uint32_t *pci_map = NULL; int len; uint32_t pci_ranges[14] = { 0x2000000, 0x0, 0xc0000000, 0x0, 0xc0000000, 0x0, 0x20000000, 0x1000000, 0x0, 0x0, 0x0, 0xe1000000, 0x0, 0x10000, }; QemuOpts *machine_opts = qemu_get_machine_opts(); const char *dtb_file = qemu_opt_get(machine_opts, "dtb"); const char *toplevel_compat = qemu_opt_get(machine_opts, "dt_compatible"); if (dtb_file) { char *filename; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); if (!filename) { goto out; } fdt = load_device_tree(filename, &fdt_size); if (!fdt) { goto out; } goto done; } fdt = create_device_tree(&fdt_size); if (fdt == NULL) { goto out; } /* Manipulate device tree in memory. */ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 2); qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 2); qemu_fdt_add_subnode(fdt, "/memory"); qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, sizeof(mem_reg_property)); qemu_fdt_add_subnode(fdt, "/chosen"); if (initrd_size) { ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_base); if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); } ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", (initrd_base + initrd_size)); if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); } } ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", args->kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); if (kvm_enabled()) { /* Read out host's frequencies */ clock_freq = kvmppc_get_clockfreq(); tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ qemu_fdt_add_subnode(fdt, "/hypervisor"); qemu_fdt_setprop_string(fdt, "/hypervisor", "compatible", "linux,kvm"); kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); qemu_fdt_setprop(fdt, "/hypervisor", "hcall-instructions", hypercall, sizeof(hypercall)); /* if KVM supports the idle hcall, set property indicating this */ if (kvmppc_get_hasidle(env)) { qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } } /* Create CPU nodes */ qemu_fdt_add_subnode(fdt, "/cpus"); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0); /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; PowerPCCPU *pcpu; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); cpu = qemu_get_cpu(i); if (cpu == NULL) { continue; } env = cpu->env_ptr; pcpu = POWERPC_CPU(cpu); snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_add_subnode(fdt, cpu_name); qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); qemu_fdt_setprop_cell(fdt, cpu_name, "reg", ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", env->dcache_line_size); qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", env->icache_line_size); qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", 0); if (cpu->cpu_index) { qemu_fdt_setprop_string(fdt, cpu_name, "status", "disabled"); qemu_fdt_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); qemu_fdt_setprop_u64(fdt, cpu_name, "cpu-release-addr", cpu_release_addr); } else { qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); } } qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); qemu_fdt_add_subnode(fdt, soc); qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, sizeof(compatible_sb)); qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, MPC8544_CCSRBAR_SIZE); /* XXX should contain a reasonable value */ qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); qemu_fdt_add_subnode(fdt, mpic); qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic"); qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); qemu_fdt_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, 0x40000); qemu_fdt_setprop_cell(fdt, mpic, "#address-cells", 0); qemu_fdt_setprop_cell(fdt, mpic, "#interrupt-cells", 2); mpic_ph = qemu_fdt_alloc_phandle(fdt); qemu_fdt_setprop_cell(fdt, mpic, "phandle", mpic_ph); qemu_fdt_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); qemu_fdt_setprop(fdt, mpic, "interrupt-controller", NULL, 0); /* * We have to generate ser1 first, because Linux takes the first * device it finds in the dt as serial output device. And we generate * devices in reverse order to the dt. */ dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, soc, mpic, "serial1", 1, false); dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, soc, mpic, "serial0", 0, true); snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, MPC8544_UTIL_OFFSET); qemu_fdt_add_subnode(fdt, gutil); qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, msi); qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); msi_ph = qemu_fdt_alloc_phandle(fdt); qemu_fdt_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); qemu_fdt_setprop_phandle(fdt, msi, "interrupt-parent", mpic); qemu_fdt_setprop_cells(fdt, msi, "interrupts", 0xe0, 0x0, 0xe1, 0x0, 0xe2, 0x0, 0xe3, 0x0, 0xe4, 0x0, 0xe5, 0x0, 0xe6, 0x0, 0xe7, 0x0); qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); qemu_fdt_add_subnode(fdt, pci); qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); qemu_fdt_setprop_string(fdt, pci, "device_type", "pci"); qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, 0x0, 0x7); pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic), params->pci_first_slot, params->pci_nr_slots, &len); qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len); qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic); qemu_fdt_setprop_cells(fdt, pci, "interrupts", 24, 2); qemu_fdt_setprop_cells(fdt, pci, "bus-range", 0, 255); for (i = 0; i < 14; i++) { pci_ranges[i] = cpu_to_be32(pci_ranges[i]); } qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); qemu_fdt_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, MPC8544_PCI_REGS_BASE, 0, 0x1000); qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); params->fixup_devtree(params, fdt); if (toplevel_compat) { qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat, strlen(toplevel_compat) + 1); } done: if (!dry_run) { qemu_fdt_dumpdtb(fdt, fdt_size); cpu_physical_memory_write(addr, fdt, fdt_size); } ret = fdt_size; out: g_free(pci_map); return ret; }