From 147ff8079ee0cf45562e202c1054814f14628b65 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Tue, 7 Mar 2017 10:23:40 +0100 Subject: ppc/spapr: QOM'ify sPAPRRTCState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also use an 'sPAPRRTCState' attribute under the sPAPR machine to hold the RTC object. Overall, these changes remove an unnecessary and implicit dependency on SysBus. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- include/hw/ppc/spapr.h | 21 ++++++++++++++++----- include/hw/ppc/xics.h | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 808aac8703..ba9e689ee2 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -20,6 +20,18 @@ typedef struct sPAPREventSource sPAPREventSource; #define SPAPR_TIMEBASE_FREQ 512000000ULL +#define TYPE_SPAPR_RTC "spapr-rtc" + +#define SPAPR_RTC(obj) \ + OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) + +typedef struct sPAPRRTCState sPAPRRTCState; +struct sPAPRRTCState { + /*< private >*/ + DeviceState parent_obj; + int64_t ns_offset; +}; + typedef struct sPAPRMachineClass sPAPRMachineClass; #define TYPE_SPAPR_MACHINE "spapr-machine" @@ -58,7 +70,7 @@ struct sPAPRMachineState { QLIST_HEAD(, sPAPRPHBState) phbs; struct sPAPRNVRAM *nvram; ICSState *ics; - DeviceState *rtc; + sPAPRRTCState rtc; void *htab; uint32_t htab_shift; @@ -629,11 +641,10 @@ struct sPAPRConfigureConnectorState { void spapr_ccs_reset_hook(void *opaque); -#define TYPE_SPAPR_RTC "spapr-rtc" -#define TYPE_SPAPR_RNG "spapr-rng" +void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns); +int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset); -void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); -int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset); +#define TYPE_SPAPR_RNG "spapr-rng" int spapr_rng_populate_dt(void *fdt); diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 9a5e715fe5..ce230183a1 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -28,7 +28,7 @@ #ifndef XICS_H #define XICS_H -#include "hw/sysbus.h" +#include "hw/qdev.h" #define XICS_IPI 0x2 #define XICS_BUID 0x1 -- cgit 1.4.1 From c64abd1f9c732f58181d0a46a0da72168759e77b Mon Sep 17 00:00:00 2001 From: Sam Bobroff Date: Mon, 20 Mar 2017 10:46:43 +1100 Subject: spapr: Add ibm,processor-radix-AP-encodings to the device tree Use the new ioctl, KVM_PPC_GET_RMMU_INFO, to fetch radix MMU information from KVM and present the page encodings in the device tree under ibm,processor-radix-AP-encodings. This provides page size information to the guest which is necessary for it to use radix mode. Signed-off-by: Sam Bobroff [dwg: Compile fix for 32-bit targets, style nit fix] Signed-off-by: David Gibson --- hw/ppc/spapr.c | 13 +++++++++++++ include/sysemu/kvm.h | 1 + target/ppc/cpu-qom.h | 1 + target/ppc/cpu.h | 4 ++++ target/ppc/kvm.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+) (limited to 'include') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3edc3dd037..ea247e6c81 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -459,6 +459,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; int drc_index; + uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; + int i; drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index); if (drc) { @@ -544,6 +546,17 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); + + if (pcc->radix_page_info) { + for (i = 0; i < pcc->radix_page_info->count; i++) { + radix_AP_encodings[i] = + cpu_to_be32(pcc->radix_page_info->entries[i]); + } + _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", + radix_AP_encodings, + pcc->radix_page_info->count * + sizeof(radix_AP_encodings[0])))); + } } static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 24281fc7f8..5cc83f2003 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -527,5 +527,6 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); * Returns: 0 on success, or a negative errno on failure. */ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); +struct ppc_radix_page_info *kvm_get_radix_page_info(void); int kvm_get_max_memslots(void); #endif diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 81500e5748..d0cf6ca2a9 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -197,6 +197,7 @@ typedef struct PowerPCCPUClass { int bfd_mach; uint32_t l1_dcache_size, l1_icache_size; const struct ppc_segment_page_sizes *sps; + struct ppc_radix_page_info *radix_page_info; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 5ee33b3fd3..cacdd0a750 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -943,6 +943,10 @@ struct ppc_segment_page_sizes { struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; }; +struct ppc_radix_page_info { + uint32_t count; + uint32_t entries[PPC_PAGE_SIZES_MAX_SZ]; +}; /*****************************************************************************/ /* The whole PowerPC CPU context */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 79b90a6910..9dc2f7fa55 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -50,6 +50,7 @@ #include "hw/ppc/spapr_cpu_core.h" #endif #include "elf.h" +#include "sysemu/kvm_int.h" //#define DEBUG_KVM @@ -333,6 +334,30 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) kvm_get_fallback_smmu_info(cpu, info); } +struct ppc_radix_page_info *kvm_get_radix_page_info(void) +{ + KVMState *s = KVM_STATE(current_machine->accelerator); + struct ppc_radix_page_info *radix_page_info; + struct kvm_ppc_rmmu_info rmmu_info; + int i; + + if (!kvm_check_extension(s, KVM_CAP_PPC_MMU_RADIX)) { + return NULL; + } + if (kvm_vm_ioctl(s, KVM_PPC_GET_RMMU_INFO, &rmmu_info)) { + return NULL; + } + radix_page_info = g_malloc0(sizeof(*radix_page_info)); + radix_page_info->count = 0; + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + if (rmmu_info.ap_encodings[i]) { + radix_page_info->entries[i] = rmmu_info.ap_encodings[i]; + radix_page_info->count++; + } + } + return radix_page_info; +} + static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) { if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { @@ -2303,6 +2328,10 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) if (icache_size != -1) { pcc->l1_icache_size = icache_size; } + +#if defined(TARGET_PPC64) + pcc->radix_page_info = kvm_get_radix_page_info(); +#endif /* defined(TARGET_PPC64) */ } bool kvmppc_has_cap_epr(void) -- cgit 1.4.1 From d77a98b01575bbbeff87a83f6e7f5ca0ce3aefdb Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Mon, 20 Mar 2017 10:46:45 +1100 Subject: target/ppc: Add new H-CALL shells for in memory table translation The use of the new in memory tables introduced in ISAv3.00 for translation, also referred to as process tables, requires the introduction of 3 new H-CALLs; H_REGISTER_PROCESS_TABLE, H_CLEAN_SLB, and H_INVALIDATE_PID. Add shells for each of these and register them as the hypercall handlers. Currently they all log an unimplemented hypercall and return H_FUNCTION. Signed-off-by: Suraj Jitindar Singh [dwg: Fix style nits] Signed-off-by: David Gibson --- hw/ppc/spapr_hcall.c | 31 +++++++++++++++++++++++++++++++ include/hw/ppc/spapr.h | 3 +++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f05a90ed2c..79521291ad 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -878,6 +878,32 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } +static target_ulong h_clean_slb(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", + opcode, " (H_CLEAN_SLB)"); + return H_FUNCTION; +} + +static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", + opcode, " (H_INVALIDATE_PID)"); + return H_FUNCTION; +} + +static target_ulong h_register_process_table(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", + opcode, " (H_REGISTER_PROC_TBL)"); + return H_FUNCTION; +} + #define H_SIGNAL_SYS_RESET_ALL -1 #define H_SIGNAL_SYS_RESET_ALLBUTSELF -2 @@ -1084,6 +1110,11 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_PAGE_INIT, h_page_init); spapr_register_hypercall(H_SET_MODE, h_set_mode); + /* In Memory Table MMU h-calls */ + spapr_register_hypercall(H_CLEAN_SLB, h_clean_slb); + spapr_register_hypercall(H_INVALIDATE_PID, h_invalidate_pid); + spapr_register_hypercall(H_REGISTER_PROC_TBL, h_register_process_table); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index ba9e689ee2..342f7a61f4 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -361,6 +361,9 @@ struct sPAPRMachineState { #define H_XIRR_X 0x2FC #define H_RANDOM 0x300 #define H_SET_MODE 0x31C +#define H_CLEAN_SLB 0x374 +#define H_INVALIDATE_PID 0x378 +#define H_REGISTER_PROC_TBL 0x37C #define H_SIGNAL_SYS_RESET 0x380 #define MAX_HCALL_OPCODE H_SIGNAL_SYS_RESET -- cgit 1.4.1 From b4db54132ffeadafa9516cc553ba9548e42d42ad Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Mon, 20 Mar 2017 10:46:46 +1100 Subject: target/ppc: Implement H_REGISTER_PROCESS_TABLE H_CALL The H_REGISTER_PROCESS_TABLE H_CALL is used by a guest to indicate to the hypervisor where in memory its process table is and how translation should be performed using this process table. Provide the implementation of this H_CALL for a guest. We first check for invalid flags, then parse the flags to determine the operation, and then check the other parameters for valid values based on the operation (register new table/deregister table/maintain registration). The process table is then stored in the appropriate location and registered with the hypervisor (if running under KVM), and the LPCR_[UPRT/GTSE] bits are updated as required. Signed-off-by: Suraj Jitindar Singh Signed-off-by: Sam Bobroff [dwg: Correct missing prototype and uninitialized variable] Signed-off-by: David Gibson --- hw/ppc/spapr.c | 35 +++++++++------ hw/ppc/spapr_hcall.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++-- include/hw/ppc/spapr.h | 2 + target/ppc/kvm.c | 31 ++++++++++++++ target/ppc/kvm_ppc.h | 10 +++++ 5 files changed, 176 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ea247e6c81..54391a1f2e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -40,6 +40,7 @@ #include "kvm_ppc.h" #include "migration/migration.h" #include "mmu-hash64.h" +#include "mmu-book3s-v3.h" #include "qom/cpu.h" #include "hw/boards.h" @@ -1113,7 +1114,7 @@ static int get_htab_fd(sPAPRMachineState *spapr) return spapr->htab_fd; } -static void close_htab_fd(sPAPRMachineState *spapr) +void close_htab_fd(sPAPRMachineState *spapr) { if (spapr->htab_fd >= 0) { close(spapr->htab_fd); @@ -1240,6 +1241,19 @@ static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, } } +void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) +{ + spapr_reallocate_hpt(spapr, + spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size), + &error_fatal); + if (spapr->vrma_adjust) { + spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), + spapr->htab_shift); + } + /* We're setting up a hash table, so that means we're not radix */ + spapr->patb_entry = 0; +} + static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) { bool matched = false; @@ -1268,17 +1282,14 @@ static void ppc_spapr_reset(void) /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - spapr->patb_entry = 0; - - /* Allocate and/or reset the hash page table */ - spapr_reallocate_hpt(spapr, - spapr_hpt_shift_for_ramsize(machine->maxram_size), - &error_fatal); - - /* Update the RMA size if necessary */ - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), - spapr->htab_shift); + if (kvm_enabled() && kvmppc_has_cap_mmu_radix()) { + /* If using KVM with radix mode available, VCPUs can be started + * without a HPT because KVM will start them in radix mode. + * Set the GR bit in PATB so that we know there is no HPT. */ + spapr->patb_entry = PATBE1_GR; + } else { + spapr->patb_entry = 0; + spapr_setup_hpt_and_vrma(spapr); } qemu_devices_reset(); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 79521291ad..a958feed7c 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -12,6 +12,8 @@ #include "trace.h" #include "kvm_ppc.h" #include "hw/ppc/spapr_ovec.h" +#include "qemu/error-report.h" +#include "mmu-book3s-v3.h" struct SPRSyncState { int spr; @@ -894,14 +896,119 @@ static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_FUNCTION; } +static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, + uint64_t patbe_old, uint64_t patbe_new) +{ + /* + * We have 4 Options: + * HASH->HASH || RADIX->RADIX || NOTHING->RADIX : Do Nothing + * HASH->RADIX : Free HPT + * RADIX->HASH : Allocate HPT + * NOTHING->HASH : Allocate HPT + * Note: NOTHING implies the case where we said the guest could choose + * later and so assumed radix and now it's called H_REG_PROC_TBL + */ + + if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) { + /* We assume RADIX, so this catches all the "Do Nothing" cases */ + } else if (!(patbe_old & PATBE1_GR)) { + /* HASH->RADIX : Free HPT */ + g_free(spapr->htab); + spapr->htab = NULL; + spapr->htab_shift = 0; + close_htab_fd(spapr); + } else if (!(patbe_new & PATBE1_GR)) { + /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ + spapr_setup_hpt_and_vrma(spapr); + } + return; +} + +#define FLAGS_MASK 0x01FULL +#define FLAG_MODIFY 0x10 +#define FLAG_REGISTER 0x08 +#define FLAG_RADIX 0x04 +#define FLAG_HASH_PROC_TBL 0x02 +#define FLAG_GTSE 0x01 + static target_ulong h_register_process_table(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", - opcode, " (H_REGISTER_PROC_TBL)"); - return H_FUNCTION; + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong proc_tbl = args[1]; + target_ulong page_size = args[2]; + target_ulong table_size = args[3]; + uint64_t cproc; + + if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */ + return H_PARAMETER; + } + if (flags & FLAG_MODIFY) { + if (flags & FLAG_REGISTER) { + if (flags & FLAG_RADIX) { /* Register new RADIX process table */ + if (proc_tbl & 0xfff || proc_tbl >> 60) { + return H_P2; + } else if (page_size) { + return H_P3; + } else if (table_size > 24) { + return H_P4; + } + cproc = PATBE1_GR | proc_tbl | table_size; + } else { /* Register new HPT process table */ + if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */ + /* TODO - Not Supported */ + /* Technically caused by flag bits => H_PARAMETER */ + return H_PARAMETER; + } else { /* Hash with SLB */ + if (proc_tbl >> 38) { + return H_P2; + } else if (page_size & ~0x7) { + return H_P3; + } else if (table_size > 24) { + return H_P4; + } + } + cproc = (proc_tbl << 25) | page_size << 5 | table_size; + } + + } else { /* Deregister current process table */ + /* Set to benign value: (current GR) | 0. This allows + * deregistration in KVM to succeed even if the radix bit in flags + * doesn't match the radix bit in the old PATB. */ + cproc = spapr->patb_entry & PATBE1_GR; + } + } else { /* Maintain current registration */ + if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) { + /* Technically caused by flag bits => H_PARAMETER */ + return H_PARAMETER; /* Existing Process Table Mismatch */ + } + cproc = spapr->patb_entry; + } + + /* Check if we need to setup OR free the hpt */ + spapr_check_setup_free_hpt(spapr, spapr->patb_entry, cproc); + + spapr->patb_entry = cproc; /* Save new process table */ + if ((flags & FLAG_RADIX) || (flags & FLAG_HASH_PROC_TBL)) { + /* Use Process TBL */ + env->spr[SPR_LPCR] |= LPCR_UPRT; + } else { + env->spr[SPR_LPCR] &= ~LPCR_UPRT; + } + if (flags & FLAG_GTSE) { /* Partition Uses Guest Translation Shootdwn */ + env->spr[SPR_LPCR] |= LPCR_GTSE; + } else { + env->spr[SPR_LPCR] &= ~LPCR_GTSE; + } + + if (kvm_enabled()) { + return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, + flags & FLAG_GTSE, cproc); + } + return H_SUCCESS; } #define H_SIGNAL_SYS_RESET_ALL -1 diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 342f7a61f4..d234efcd29 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -608,6 +608,8 @@ void spapr_dt_events(sPAPRMachineState *sm, void *fdt); int spapr_h_cas_compose_response(sPAPRMachineState *sm, target_ulong addr, target_ulong size, sPAPROptionVector *ov5_updates); +void close_htab_fd(sPAPRMachineState *spapr); +void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(sPAPRTCETable *tcet, uint32_t page_shift, uint64_t bus_offset, diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 38db27b485..c959b90b59 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -362,6 +362,37 @@ struct ppc_radix_page_info *kvm_get_radix_page_info(void) return radix_page_info; } +target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, + bool radix, bool gtse, + uint64_t proc_tbl) +{ + CPUState *cs = CPU(cpu); + int ret; + uint64_t flags = 0; + struct kvm_ppc_mmuv3_cfg cfg = { + .process_table = proc_tbl, + }; + + if (radix) { + flags |= KVM_PPC_MMUV3_RADIX; + } + if (gtse) { + flags |= KVM_PPC_MMUV3_GTSE; + } + cfg.flags = flags; + ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_CONFIGURE_V3_MMU, &cfg); + switch (ret) { + case 0: + return H_SUCCESS; + case -EINVAL: + return H_PARAMETER; + case -ENODEV: + return H_NOT_AVAILABLE; + default: + return H_HARDWARE; + } +} + static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) { if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 64189a48fa..4b2fd9a609 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -33,6 +33,9 @@ int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); int kvmppc_set_tcr(PowerPCCPU *cpu); int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); +target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, + bool radix, bool gtse, + uint64_t proc_tbl); #ifndef CONFIG_USER_ONLY off_t kvmppc_alloc_rma(void **rma); bool kvmppc_spapr_use_multitce(void); @@ -159,6 +162,13 @@ static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu) return -1; } +static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, + bool radix, bool gtse, + uint64_t proc_tbl) +{ + return 0; +} + #ifndef CONFIG_USER_ONLY static inline off_t kvmppc_alloc_rma(void **rma) { -- cgit 1.4.1 From 9fb4541f5803f8d2ba116b12113386e26482ba30 Mon Sep 17 00:00:00 2001 From: Sam Bobroff Date: Thu, 23 Mar 2017 14:46:00 +1100 Subject: spapr: Enable ISA 3.0 MMU mode selection via CAS Add the new node, /chosen/ibm,arch-vec-5-platform-support to the device tree. This allows the guest to determine which modes are supported by the hypervisor. Update the option vector processing in h_client_architecture_support() to handle the new MMU bits. This allows guests to request hash or radix mode and QEMU to create the guest's HPT at this time if it is necessary but hasn't yet been done. QEMU will terminate the guest if it requests an unavailable mode, as required by the architecture. Extend the ibm,pa-features node with the new ISA 3.0 values and set the radix bit if KVM supports radix mode. This probably won't be used directly by guests to determine the availability of radix mode (that is indicated by the new node added above) but the architecture requires that it be set when the hardware supports it. If QEMU is using KVM, and KVM is capable of running in radix mode, guests can be run in real-mode without allocating a HPT (because KVM will use a minimal RPT). So in this case, we avoid creating the HPT at reset time and later (during CAS) create it if it is necessary. ISA 3.0 guests will now begin to call h_register_process_table(), which has been added previously. Signed-off-by: Sam Bobroff [dwg: Strip some unneeded prefix from error messages] Signed-off-by: David Gibson --- hw/ppc/spapr.c | 71 ++++++++++++++++++++++++++++++++++++--------- hw/ppc/spapr_hcall.c | 30 +++++++++++++++++++ include/hw/ppc/spapr_ovec.h | 5 ++++ 3 files changed, 93 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 21da9a1d79..d967ec30b4 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -237,20 +237,31 @@ static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00 }; - /* Currently we don't advertise any of the "new" ISAv3.00 functionality */ - uint8_t pa_features_300[] = { 64, 0, - 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ + uint8_t pa_features_300[] = { 66, 0, + /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ + /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, SSO, 5: LE|CFAR|EB|LSQ */ + 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */ + /* 6: DS207 */ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ + /* 16: Vector */ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ - 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24 - 29 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 - 35 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 36 - 41 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 - 47 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 48 - 53 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 54 - 59 */ - 0x00, 0x00, 0x00, 0x00 }; /* 60 - 63 */ - + /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 18 - 23 */ + /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ + /* 30: MMR, 32: LE atomic, 34: EBB + ext EBB */ + 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ + /* 36: SPR SO, 38: Copy/Paste, 40: Radix MMU */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 36 - 41 */ + /* 42: PM, 44: PC RA, 46: SC vec'd */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ + /* 48: SIMD, 50: QP BFP, 52: String */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ + /* 54: DecFP, 56: DecI, 58: SHA */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ + /* 60: NM atomic, 62: RNG */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ + }; uint8_t *pa_features; size_t pa_size; @@ -856,6 +867,33 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) spapr_dt_rtas_tokens(fdt, rtas); } +/* Prepare ibm,arch-vec-5-platform-support, which indicates the MMU features + * that the guest may request and thus the valid values for bytes 24..26 of + * option vector 5: */ +static void spapr_dt_ov5_platform_support(void *fdt, int chosen) +{ + char val[2 * 3] = { + 24, 0x00, /* Hash/Radix, filled in below. */ + 25, 0x00, /* Hash options: Segment Tables == no, GTSE == no. */ + 26, 0x40, /* Radix options: GTSE == yes. */ + }; + + if (kvm_enabled()) { + if (kvmppc_has_cap_mmu_radix() && kvmppc_has_cap_mmu_hash_v3()) { + val[1] = 0x80; /* OV5_MMU_BOTH */ + } else if (kvmppc_has_cap_mmu_radix()) { + val[1] = 0x40; /* OV5_MMU_RADIX_300 */ + } else { + val[1] = 0x00; /* Hash */ + } + } else { + /* TODO: TCG case, hash */ + val[1] = 0x00; + } + _FDT(fdt_setprop(fdt, chosen, "ibm,arch-vec-5-platform-support", + val, sizeof(val))); +} + static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) { MachineState *machine = MACHINE(spapr); @@ -909,6 +947,8 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); } + spapr_dt_ov5_platform_support(fdt, chosen); + g_free(stdout_path); g_free(bootlist); } @@ -2078,6 +2118,11 @@ static void ppc_spapr_init(MachineState *machine) } spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); + if (kvmppc_has_cap_mmu_radix()) { + /* KVM always allows GTSE with radix... */ + spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); + } + /* ... but not with hash (currently). */ /* advertise support for dedicated HP event source to guests */ if (spapr->use_hotplug_event_source) { diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index a958feed7c..cbd1f296d6 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1063,6 +1063,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, uint32_t best_compat = 0; int i; sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; + bool guest_radix; /* * We scan the supplied table of PVRs looking for two things @@ -1114,6 +1115,13 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, ov_table = list; ov5_guest = spapr_ovec_parse_vector(ov_table, 5); + if (spapr_ovec_test(ov5_guest, OV5_MMU_BOTH)) { + error_report("guest requested hash and radix MMU, which is invalid."); + exit(EXIT_FAILURE); + } + /* The radix/hash bit in byte 24 requires special handling: */ + guest_radix = spapr_ovec_test(ov5_guest, OV5_MMU_RADIX_300); + spapr_ovec_clear(ov5_guest, OV5_MMU_RADIX_300); /* NOTE: there are actually a number of ov5 bits where input from the * guest is always zero, and the platform/QEMU enables them independently @@ -1132,6 +1140,21 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, ov5_updates = spapr_ovec_new(); spapr->cas_reboot = spapr_ovec_diff(ov5_updates, ov5_cas_old, spapr->ov5_cas); + /* Now that processing is finished, set the radix/hash bit for the + * guest if it requested a valid mode; otherwise terminate the boot. */ + if (guest_radix) { + if (kvm_enabled() && !kvmppc_has_cap_mmu_radix()) { + error_report("Guest requested unavailable MMU mode (radix)."); + exit(EXIT_FAILURE); + } + spapr_ovec_set(spapr->ov5_cas, OV5_MMU_RADIX_300); + } else { + if (kvm_enabled() && kvmppc_has_cap_mmu_radix() + && !kvmppc_has_cap_mmu_hash_v3()) { + error_report("Guest requested unavailable MMU mode (hash)."); + exit(EXIT_FAILURE); + } + } if (!spapr->cas_reboot) { spapr->cas_reboot = @@ -1142,6 +1165,13 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, if (spapr->cas_reboot) { qemu_system_reset_request(); + } else { + /* If ppc_spapr_reset() did not set up a HPT but one is necessary + * (because the guest isn't going to use radix) then set it up here. */ + if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) { + /* legacy hash or new hash: */ + spapr_setup_hpt_and_vrma(spapr); + } } return H_SUCCESS; diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 355a34411f..f7f2abeb16 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -48,6 +48,11 @@ typedef struct sPAPROptionVector sPAPROptionVector; #define OV5_FORM1_AFFINITY OV_BIT(5, 0) #define OV5_HP_EVT OV_BIT(6, 5) +/* ISA 3.00 MMU features: */ +#define OV5_MMU_BOTH OV_BIT(24, 0) /* Radix and hash */ +#define OV5_MMU_RADIX_300 OV_BIT(24, 1) /* 1=Radix only, 0=Hash only */ +#define OV5_MMU_RADIX_GTSE OV_BIT(26, 1) /* Radix GTSE */ + /* interfaces */ sPAPROptionVector *spapr_ovec_new(void); sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); -- cgit 1.4.1 From e957f6a9b92439a222ecd4ff1c8cdc9700710c72 Mon Sep 17 00:00:00 2001 From: Sam Bobroff Date: Mon, 20 Mar 2017 10:46:49 +1100 Subject: spapr: Workaround for broken radix guests For a little while around 4.9, Linux kernels that saw the radix bit in ibm,pa-features would attempt to set up the MMU as if they were a hypervisor, even if they were a guest, which would cause them to crash. Work around this by detecting pre-ISA 3.0 guests by their lack of that bit in option vector 1, and then removing the radix bit from ibm,pa-features. Note: This now requires regeneration of that node after CAS negotiation. Signed-off-by: Sam Bobroff [dwg: Fix style nits] Signed-off-by: David Gibson --- hw/ppc/spapr.c | 15 +++++++++++++-- hw/ppc/spapr_hcall.c | 6 ++++-- include/hw/ppc/spapr.h | 1 + include/hw/ppc/spapr_ovec.h | 3 +++ 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d967ec30b4..a355512849 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -228,7 +228,8 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, CPUState *cs) } /* Populate the "ibm,pa-features" property */ -static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset) +static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset, + bool legacy_guest) { uint8_t pa_features_206[] = { 6, 0, 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; @@ -295,6 +296,12 @@ static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset) if (kvmppc_has_cap_htm() && pa_size > 24) { pa_features[24] |= 0x80; /* Transactional memory support */ } + if (legacy_guest && pa_size > 40) { + /* Workaround for broken kernels that attempt (guest) radix + * mode when they can't handle it, if they see the radix bit set + * in pa-features. So hide it from them. */ + pa_features[40 + 2] &= ~0x80; /* Radix MMU */ + } _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); } @@ -309,6 +316,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; DeviceClass *dc = DEVICE_GET_CLASS(cs); int index = ppc_get_vcpu_dt_id(cpu); int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); @@ -350,6 +358,9 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) if (ret < 0) { return ret; } + + spapr_populate_pa_features(env, fdt, offset, + spapr->cas_legacy_guest_workaround); } return ret; } @@ -547,7 +558,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, page_sizes_prop, page_sizes_prop_size))); } - spapr_populate_pa_features(env, fdt, offset); + spapr_populate_pa_features(env, fdt, offset, false); _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index cbd1f296d6..9f18f75b88 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1062,7 +1062,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, uint32_t max_compat = cpu->max_compat; uint32_t best_compat = 0; int i; - sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; + sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; bool guest_radix; /* @@ -1114,6 +1114,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, /* For the future use: here @ov_table points to the first option vector */ ov_table = list; + ov1_guest = spapr_ovec_parse_vector(ov_table, 1); ov5_guest = spapr_ovec_parse_vector(ov_table, 5); if (spapr_ovec_test(ov5_guest, OV5_MMU_BOTH)) { error_report("guest requested hash and radix MMU, which is invalid."); @@ -1155,7 +1156,8 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, exit(EXIT_FAILURE); } } - + spapr->cas_legacy_guest_workaround = !spapr_ovec_test(ov1_guest, + OV1_PPC_3_00); if (!spapr->cas_reboot) { spapr->cas_reboot = (spapr_h_cas_compose_response(spapr, args[1], args[2], diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index d234efcd29..e27de64d31 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -89,6 +89,7 @@ struct sPAPRMachineState { sPAPROptionVector *ov5; /* QEMU-supported option vectors */ sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ bool cas_reboot; + bool cas_legacy_guest_workaround; Notifier epow_notifier; QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index f7f2abeb16..f088833204 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -43,6 +43,9 @@ typedef struct sPAPROptionVector sPAPROptionVector; #define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) +/* option vector 1 */ +#define OV1_PPC_3_00 OV_BIT(3, 0) /* guest supports PowerPC 3.00? */ + /* option vector 5 */ #define OV5_DRCONF_MEMORY OV_BIT(2, 2) #define OV5_FORM1_AFFINITY OV_BIT(5, 0) -- cgit 1.4.1 From ad5d1add86b9560c22c3fb8718d6a99eabaaed6a Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 29 Mar 2017 15:53:23 +0200 Subject: ppc/xics: introduce an 'intc' backlink under PowerPCCPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today, the ICPState array of the sPAPR machine is indexed with 'cpu_index' of the CPUState. This numbering of CPUs is internal to QEMU and the guest only knows about what is exposed in the device tree, that is the 'cpu_dt_id'. This is why sPAPR uses the helper xics_get_cpu_index_by_dt_id() to do the mapping in a couple of places. To provide a more generic XICS layer, we need to abstract the IRQ 'server' number and remove any assumption made on its nature. It should not be used as a 'cpu_index' for lookups like xics_cpu_setup() and xics_cpu_destroy() do. To reach that goal, we choose to introduce a generic 'intc' backlink under PowerPCCPU, and let the machine core init routine do the ICPState lookup. The resulting object is passed on to xics_cpu_setup() which does the store under PowerPCCPU. The IRQ 'server' number in XICS is now generic. sPAPR uses 'cpu_dt_id' and PowerNV will use 'PIR' number. This also has the benefit of simplifying the sPAPR hcall routines which do not need to do any ICPState lookups anymore. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 6 +++--- hw/intc/xics_spapr.c | 20 +++++--------------- hw/ppc/spapr_cpu_core.c | 4 +++- include/hw/ppc/xics.h | 2 +- target/ppc/cpu.h | 1 + 5 files changed, 13 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/hw/intc/xics.c b/hw/intc/xics.c index e740989a11..56fe70cd10 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -52,7 +52,7 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id) void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(xi, cs->cpu_index); + ICPState *icp = ICP(cpu->intc); assert(icp); assert(cs == icp->cs); @@ -61,15 +61,15 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) icp->cs = NULL; } -void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu) +void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - ICPState *icp = xics_icp_get(xi, cs->cpu_index); ICPStateClass *icpc; assert(icp); + cpu->intc = OBJECT(icp); icp->cs = cs; icpc = ICP_GET_CLASS(icp); diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 84d24b2837..58f100d379 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -43,11 +43,9 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); target_ulong cppr = args[0]; - icp_set_cppr(icp, cppr); + icp_set_cppr(ICP(cpu->intc), cppr); return H_SUCCESS; } @@ -69,9 +67,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); - uint32_t xirr = icp_accept(icp); + uint32_t xirr = icp_accept(ICP(cpu->intc)); args[0] = xirr; return H_SUCCESS; @@ -80,9 +76,7 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); - uint32_t xirr = icp_accept(icp); + uint32_t xirr = icp_accept(ICP(cpu->intc)); args[0] = xirr; args[1] = cpu_get_host_ticks(); @@ -92,21 +86,17 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); target_ulong xirr = args[0]; - icp_eoi(icp, xirr); + icp_eoi(ICP(cpu->intc), xirr); return H_SUCCESS; } static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUState *cs = CPU(cpu); - ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); uint32_t mfrr; - uint32_t xirr = icp_ipoll(icp, &mfrr); + uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr); args[0] = xirr; args[1] = mfrr; diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 6883f0991a..7db61bd724 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -63,6 +63,8 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) { CPUPPCState *env = &cpu->env; + XICSFabric *xi = XICS_FABRIC(spapr); + ICPState *icp = xics_icp_get(xi, CPU(cpu)->cpu_index); /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); @@ -80,7 +82,7 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, } } - xics_cpu_setup(XICS_FABRIC(spapr), cpu); + xics_cpu_setup(xi, cpu, icp); qemu_register_reset(spapr_cpu_reset, cpu); spapr_cpu_reset(cpu); diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index ce230183a1..b97d30bbc6 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -168,7 +168,7 @@ void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle); qemu_irq xics_get_qirq(XICSFabric *xi, int irq); ICPState *xics_icp_get(XICSFabric *xi, int server); -void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu); +void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp); void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu); /* Internal XICS interfaces */ diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index cacdd0a750..e0ff0412d6 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1200,6 +1200,7 @@ struct PowerPCCPU { uint32_t max_compat; uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; + Object *intc; /* Fields related to migration compatibility hacks */ bool pre_2_8_migration; -- cgit 1.4.1 From 5bc8d26de20ca6befe6390f381cb98120237e4be Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 3 Apr 2017 09:45:58 +0200 Subject: spapr: allocate the ICPState object from under sPAPRCPUCore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today, all the ICPs are created before the CPUs, stored in an array under the sPAPR machine and linked to the CPU when the core threads are realized. This modeling brings some complexity when a lookup in the array is required and it can be simplified by allocating the ICPs when the CPUs are. This is the purpose of this proposal which introduces a new 'icp_type' field under the machine and creates the ICP objects of the right type (KVM or not) before the PowerPCCPU object are. This change allows more cleanups : the removal of the icps array under the sPAPR machine and the removal of the xics_get_cpu_index_by_dt_id() helper. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/intc/xics.c | 11 ----------- hw/ppc/spapr.c | 47 ++++++++++++++--------------------------------- hw/ppc/spapr_cpu_core.c | 18 ++++++++++++++---- include/hw/ppc/spapr.h | 2 +- include/hw/ppc/xics.h | 2 -- 5 files changed, 29 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 56fe70cd10..d4428b41b0 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -38,17 +38,6 @@ #include "monitor/monitor.h" #include "hw/intc/intc.h" -int xics_get_cpu_index_by_dt_id(int cpu_dt_id) -{ - PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); - - if (cpu) { - return cpu->parent_obj.cpu_index; - } - - return -1; -} - void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 08f8615f0d..703b14a15b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -104,7 +104,6 @@ static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics, XICSFabric *xi = XICS_FABRIC(spapr); Error *err = NULL, *local_err = NULL; ICSState *ics = NULL; - int i; ics = ICS_SIMPLE(object_new(type_ics)); object_property_add_child(OBJECT(spapr), "ics", OBJECT(ics), NULL); @@ -113,34 +112,14 @@ static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics, object_property_set_bool(OBJECT(ics), true, "realized", &local_err); error_propagate(&err, local_err); if (err) { - goto error; + error_propagate(errp, err); + return -1; } - spapr->icps = g_malloc0(nr_servers * sizeof(ICPState)); spapr->nr_servers = nr_servers; - - for (i = 0; i < nr_servers; i++) { - ICPState *icp = &spapr->icps[i]; - - object_initialize(icp, sizeof(*icp), type_icp); - object_property_add_child(OBJECT(spapr), "icp[*]", OBJECT(icp), NULL); - object_property_add_const_link(OBJECT(icp), "xics", OBJECT(xi), NULL); - object_property_set_bool(OBJECT(icp), true, "realized", &err); - if (err) { - goto error; - } - object_unref(OBJECT(icp)); - } - spapr->ics = ics; + spapr->icp_type = type_icp; return 0; - -error: - error_propagate(errp, err); - if (ics) { - object_unparent(OBJECT(ics)); - } - return -1; } static int xics_system_init(MachineState *machine, @@ -1441,9 +1420,10 @@ static int spapr_post_load(void *opaque, int version_id) int err = 0; if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { - int i; - for (i = 0; i < spapr->nr_servers; i++) { - icp_resend(&spapr->icps[i]); + CPUState *cs; + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + icp_resend(ICP(cpu->intc)); } } @@ -3114,20 +3094,21 @@ static void spapr_ics_resend(XICSFabric *dev) static ICPState *spapr_icp_get(XICSFabric *xi, int cpu_dt_id) { - sPAPRMachineState *spapr = SPAPR_MACHINE(xi); - int server = xics_get_cpu_index_by_dt_id(cpu_dt_id); + PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); - return (server < spapr->nr_servers) ? &spapr->icps[server] : NULL; + return cpu ? ICP(cpu->intc) : NULL; } static void spapr_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - int i; + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); - for (i = 0; i < spapr->nr_servers; i++) { - icp_pic_print_info(&spapr->icps[i], mon); + icp_pic_print_info(ICP(cpu->intc), mon); } ics_pic_print_info(spapr->ics, mon); diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 4e1a99591d..2e689b542d 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -63,8 +63,6 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) { CPUPPCState *env = &cpu->env; - XICSFabric *xi = XICS_FABRIC(spapr); - ICPState *icp = xics_icp_get(xi, cpu->cpu_dt_id); /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); @@ -82,8 +80,6 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, } } - xics_cpu_setup(xi, cpu, icp); - qemu_register_reset(spapr_cpu_reset, cpu); spapr_cpu_reset(cpu); } @@ -143,18 +139,32 @@ static void spapr_cpu_core_realize_child(Object *child, Error **errp) sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); + Object *obj; + + obj = object_new(spapr->icp_type); + object_property_add_child(OBJECT(cpu), "icp", obj, NULL); + object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } object_property_set_bool(child, true, "realized", &local_err); if (local_err) { + object_unparent(obj); error_propagate(errp, local_err); return; } spapr_cpu_init(spapr, cpu, &local_err); if (local_err) { + object_unparent(obj); error_propagate(errp, local_err); return; } + + xics_cpu_setup(XICS_FABRIC(spapr), cpu, ICP(obj)); } static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index e27de64d31..7dbba577b0 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -109,7 +109,7 @@ struct sPAPRMachineState { MemoryHotplugState hotplug_memory; uint32_t nr_servers; - ICPState *icps; + const char *icp_type; }; #define H_SUCCESS 0 diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index b97d30bbc6..b07f56fcc5 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -172,8 +172,6 @@ void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp); void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu); /* Internal XICS interfaces */ -int xics_get_cpu_index_by_dt_id(int cpu_dt_id); - void icp_set_cppr(ICPState *icp, uint8_t cppr); void icp_set_mfrr(ICPState *icp, uint8_t mfrr); uint32_t icp_accept(ICPState *ss); -- cgit 1.4.1 From 439071a92dc85d67b5b6c55e7d1098dc6c3e8e89 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 3 Apr 2017 09:45:59 +0200 Subject: ppc/xics: add a realize() handler to ICPStateClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used by derived classes in PowerNV for customization. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/intc/xics.c | 5 +++++ include/hw/ppc/xics.h | 1 + 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/hw/intc/xics.c b/hw/intc/xics.c index d4428b41b0..292fffecd3 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -337,6 +337,7 @@ static void icp_reset(void *dev) static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); + ICPStateClass *icpc = ICP_GET_CLASS(dev); Object *obj; Error *err = NULL; @@ -349,6 +350,10 @@ static void icp_realize(DeviceState *dev, Error **errp) icp->xics = XICS_FABRIC(obj); + if (icpc->realize) { + icpc->realize(dev, errp); + } + qemu_register_reset(icp_reset, dev); } diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index b07f56fcc5..731e1779f1 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -60,6 +60,7 @@ typedef struct XICSFabric XICSFabric; struct ICPStateClass { DeviceClass parent_class; + void (*realize)(DeviceState *dev, Error **errp); void (*pre_save)(ICPState *s); int (*post_load)(ICPState *s, int version_id); void (*cpu_setup)(ICPState *icp, PowerPCCPU *cpu); -- cgit 1.4.1 From 99285aae1695f77ea55230554f547b66b685e123 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 3 Apr 2017 09:46:00 +0200 Subject: ppc/pnv: add a PnvICPState object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides a new ICPState object for the PowerNV machine (POWER8). Access to the Interrupt Management area is done though a memory region. It contains the registers of the Interrupt Control Presenters of each thread which are used to accept, return, forward interrupts in the system. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/intc/Makefile.objs | 1 + hw/intc/xics_pnv.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/xics.h | 12 ++++ 3 files changed, 205 insertions(+) create mode 100644 hw/intc/xics_pnv.c (limited to 'include') diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index adedd0da5f..78426a7daf 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o +obj-$(CONFIG_POWERNV) += xics_pnv.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c new file mode 100644 index 0000000000..12ae605f10 --- /dev/null +++ b/hw/intc/xics_pnv.c @@ -0,0 +1,192 @@ +/* + * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model + * + * Copyright (c) 2017, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/ppc/xics.h" + +#define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */ +#define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */ +#define ICP_MFRR 12 /* 1 byte access only */ + +#define ICP_LINKA 16 /* unused */ +#define ICP_LINKB 20 /* unused */ +#define ICP_LINKC 24 /* unused */ + +static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) +{ + ICPState *icp = ICP(opaque); + PnvICPState *picp = PNV_ICP(opaque); + bool byte0 = (width == 1 && (addr & 0x3) == 0); + uint64_t val = 0xffffffff; + + switch (addr & 0xffc) { + case ICP_XIRR_POLL: + val = icp_ipoll(icp, NULL); + if (byte0) { + val >>= 24; + } else if (width != 4) { + goto bad_access; + } + break; + case ICP_XIRR: + if (byte0) { + val = icp_ipoll(icp, NULL) >> 24; + } else if (width == 4) { + val = icp_accept(icp); + } else { + goto bad_access; + } + break; + case ICP_MFRR: + if (byte0) { + val = icp->mfrr; + } else { + goto bad_access; + } + break; + case ICP_LINKA: + if (width == 4) { + val = picp->links[0]; + } else { + goto bad_access; + } + break; + case ICP_LINKB: + if (width == 4) { + val = picp->links[1]; + } else { + goto bad_access; + } + break; + case ICP_LINKC: + if (width == 4) { + val = picp->links[2]; + } else { + goto bad_access; + } + break; + default: +bad_access: + qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" + HWADDR_PRIx"/%d\n", addr, width); + } + + return val; +} + +static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + ICPState *icp = ICP(opaque); + PnvICPState *picp = PNV_ICP(opaque); + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + switch (addr & 0xffc) { + case ICP_XIRR: + if (byte0) { + icp_set_cppr(icp, val); + } else if (width == 4) { + icp_eoi(icp, val); + } else { + goto bad_access; + } + break; + case ICP_MFRR: + if (byte0) { + icp_set_mfrr(icp, val); + } else { + goto bad_access; + } + break; + case ICP_LINKA: + if (width == 4) { + picp->links[0] = val; + } else { + goto bad_access; + } + break; + case ICP_LINKB: + if (width == 4) { + picp->links[1] = val; + } else { + goto bad_access; + } + break; + case ICP_LINKC: + if (width == 4) { + picp->links[2] = val; + } else { + goto bad_access; + } + break; + default: +bad_access: + qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" + HWADDR_PRIx"/%d\n", addr, width); + } +} + +static const MemoryRegionOps pnv_icp_ops = { + .read = pnv_icp_read, + .write = pnv_icp_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void pnv_icp_realize(DeviceState *dev, Error **errp) +{ + PnvICPState *icp = PNV_ICP(dev); + + memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops, + icp, "icp-thread", 0x1000); +} + +static void pnv_icp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICPStateClass *icpc = ICP_CLASS(klass); + + icpc->realize = pnv_icp_realize; + dc->desc = "PowerNV ICP"; +} + +static const TypeInfo pnv_icp_info = { + .name = TYPE_PNV_ICP, + .parent = TYPE_ICP, + .instance_size = sizeof(PnvICPState), + .class_init = pnv_icp_class_init, + .class_size = sizeof(ICPStateClass), +}; + +static void pnv_icp_register_types(void) +{ + type_register_static(&pnv_icp_info); +} + +type_init(pnv_icp_register_types) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 731e1779f1..c215dc72a4 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -41,10 +41,12 @@ */ typedef struct ICPStateClass ICPStateClass; typedef struct ICPState ICPState; +typedef struct PnvICPState PnvICPState; typedef struct ICSStateClass ICSStateClass; typedef struct ICSState ICSState; typedef struct ICSIRQState ICSIRQState; typedef struct XICSFabric XICSFabric; +typedef struct PowerPCCPU PowerPCCPU; #define TYPE_ICP "icp" #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) @@ -52,6 +54,9 @@ typedef struct XICSFabric XICSFabric; #define TYPE_KVM_ICP "icp-kvm" #define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) +#define TYPE_PNV_ICP "pnv-icp" +#define PNV_ICP(obj) OBJECT_CHECK(PnvICPState, (obj), TYPE_PNV_ICP) + #define ICP_CLASS(klass) \ OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) #define ICP_GET_CLASS(obj) \ @@ -81,6 +86,13 @@ struct ICPState { XICSFabric *xics; }; +struct PnvICPState { + ICPState parent_obj; + + MemoryRegion mmio; + uint32_t links[3]; +}; + #define TYPE_ICS_BASE "ics-base" #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE) -- cgit 1.4.1 From 5509db4aec13217022d41b131c8216040fb723f1 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 3 Apr 2017 09:46:04 +0200 Subject: ppc/pnv: add a helper to calculate MMIO addresses registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some controllers (ICP, PSI) have a base register address which is calculated using the chip id. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- include/hw/ppc/pnv.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index df98a72006..5693ba181d 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -91,14 +91,24 @@ typedef struct PnvChipClass { OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) /* - * This generates a HW chip id depending on an index: + * This generates a HW chip id depending on an index, as found on a + * two socket system with dual chip modules : * * 0x0, 0x1, 0x10, 0x11 * * 4 chips should be the maximum + * + * TODO: use a machine property to define the chip ids */ #define PNV_CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1)) +/* + * Converts back a HW chip id to an index. This is useful to calculate + * the MMIO addresses of some controllers which depend on the chip id. + */ +#define PNV_CHIP_INDEX(chip) \ + (((chip)->chip_id >> 2) * 2 + ((chip)->chip_id & 0x3)) + #define TYPE_POWERNV_MACHINE MACHINE_TYPE_NAME("powernv") #define POWERNV_MACHINE(obj) \ OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) -- cgit 1.4.1 From bf5615e77cbe5518f201a9be96e13bedb6a5b26d Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 3 Apr 2017 09:46:05 +0200 Subject: ppc/pnv: add memory regions for the ICP registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides to a PowerNV chip (POWER8) access to the Interrupt Management area, which contains the registers of the Interrupt Control Presenters of each thread. These are used to accept, return, forward interrupts in the system. This area is modeled with a per-chip container memory region holding all the ICP registers. Each thread of a chip is then associated with its ICP registers using a memory subregion indexed by its PIR number in the overall region. The device tree is populated accordingly. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/pnv.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 19 ++++++++++++ 2 files changed, 100 insertions(+) (limited to 'include') diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 2add2ad5ac..1fa90d636b 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -218,6 +218,43 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) servers_prop, sizeof(servers_prop)))); } +static void powernv_populate_icp(PnvChip *chip, void *fdt, uint32_t pir, + uint32_t nr_threads) +{ + uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); + char *name; + const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; + uint32_t irange[2], i, rsize; + uint64_t *reg; + int offset; + + irange[0] = cpu_to_be32(pir); + irange[1] = cpu_to_be32(nr_threads); + + rsize = sizeof(uint64_t) * 2 * nr_threads; + reg = g_malloc(rsize); + for (i = 0; i < nr_threads; i++) { + reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000)); + reg[i * 2 + 1] = cpu_to_be64(0x1000); + } + + name = g_strdup_printf("interrupt-controller@%"PRIX64, addr); + offset = fdt_add_subnode(fdt, 0, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); + _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", + "PowerPC-External-Interrupt-Presentation"))); + _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0))); + _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges", + irange, sizeof(irange)))); + _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1))); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0))); + g_free(reg); +} + static void powernv_populate_chip(PnvChip *chip, void *fdt) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); @@ -231,6 +268,10 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt) PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); powernv_create_core_node(chip, pnv_core, fdt); + + /* Interrupt Control Presenters (ICP). One per core. */ + powernv_populate_icp(chip, fdt, pnv_core->pir, + CPU_CORE(pnv_core)->nr_threads); } if (chip->ram_size) { @@ -643,6 +684,38 @@ static void pnv_chip_init(Object *obj) object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); } +static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + char *typename = pnv_core_typename(pcc->cpu_model); + size_t typesize = object_type_get_instance_size(typename); + int i, j; + char *name; + XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); + + name = g_strdup_printf("icp-%x", chip->chip_id); + memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio); + g_free(name); + + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); + + /* Map the ICP registers for each thread */ + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + int core_hwid = CPU_CORE(pnv_core)->core_id; + + for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { + uint32_t pir = pcc->core_pir(chip, core_hwid) + j; + PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); + + memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio); + } + } + + g_free(typename); +} + static void pnv_chip_realize(DeviceState *dev, Error **errp) { PnvChip *chip = PNV_CHIP(dev); @@ -713,6 +786,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&chip->lpc), true, "realized", &error_fatal); pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); + + /* Interrupt Management Area. This is the memory region holding + * all the Interrupt Control Presenter (ICP) registers */ + pnv_chip_icp_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } } static Property pnv_chip_properties[] = { diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 5693ba181d..96231c1ff7 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -54,6 +54,7 @@ typedef struct PnvChip { MemoryRegion xscom_mmio; MemoryRegion xscom; AddressSpace xscom_as; + MemoryRegion icp_mmio; PnvLpcController lpc; } PnvChip; @@ -136,4 +137,22 @@ typedef struct PnvMachineState { #define PNV_XSCOM_BASE(chip) \ (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) +/* + * XSCOM 0x20109CA defines the ICP BAR: + * + * 0:29 : bits 14 to 43 of address to define 1 MB region. + * 30 : 1 to enable ICP to receive loads/stores against its BAR region + * 31:63 : Constant 0 + * + * Usually defined as : + * + * 0xffffe00200000000 -> 0x0003ffff80000000 + * 0xffffe00600000000 -> 0x0003ffff80100000 + * 0xffffe02200000000 -> 0x0003ffff80800000 + * 0xffffe02600000000 -> 0x0003ffff80900000 + */ +#define PNV_ICP_SIZE 0x0000000000100000ull +#define PNV_ICP_BASE(chip) \ + (0x0003ffff80000000ull + (uint64_t) PNV_CHIP_INDEX(chip) * PNV_ICP_SIZE) + #endif /* _PPC_PNV_H */ -- cgit 1.4.1 From 54f59d786c05765bf7410eadd10e88f5579df9e7 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 5 Apr 2017 14:41:26 +0200 Subject: ppc/pnv: Add cut down PSI bridge model and hookup external interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Processor Service Interface (PSI) Controller is one of the engines of the "Bridge" unit which connects the different interfaces to the Power Processor. This adds just enough of the PSI bridge to handle various on-chip and the one external interrupt. The rest of PSI has to do with the link to the IBM FSP service processor which we don't plan to emulate (not used on OpenPower machines). The ics_get() and ics_resend() handlers of the XICSFabric interface of the PowerNV machine are now defined to handle the Interrupt Control Source of PSI. The InterruptStatsProvider interface is also modified to dump the new ICS. Originally from Benjamin Herrenschmidt Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/Makefile.objs | 2 +- hw/ppc/pnv.c | 65 +++++- hw/ppc/pnv_psi.c | 571 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 13 ++ include/hw/ppc/pnv_psi.h | 67 ++++++ include/hw/ppc/pnv_xscom.h | 3 + 6 files changed, 714 insertions(+), 7 deletions(-) create mode 100644 hw/ppc/pnv_psi.c create mode 100644 include/hw/ppc/pnv_psi.h (limited to 'include') diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 001293423c..dc19ee17fa 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o # IBM PowerNV -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 1fa90d636b..a516acbe19 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -353,15 +353,22 @@ static void ppc_powernv_reset(void) * have a CPLD that will collect the SerIRQ and shoot them as a * single level interrupt to the P8 chip. So let's setup a hook * for doing just that. - * - * Note: The actual interrupt input isn't emulated yet, this will - * come with the PSI bridge model. */ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) { - /* We don't yet emulate the PSI bridge which provides the external - * interrupt, so just drop interrupts on the floor - */ + PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + uint32_t old_state = pnv->cpld_irqstate; + PnvChip *chip = opaque; + + if (level) { + pnv->cpld_irqstate |= 1u << n; + } else { + pnv->cpld_irqstate &= ~(1u << n); + } + if (pnv->cpld_irqstate != old_state) { + pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL, + pnv->cpld_irqstate != 0); + } } static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) @@ -682,6 +689,11 @@ static void pnv_chip_init(Object *obj) object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); + + object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI); + object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL); + object_property_add_const_link(OBJECT(&chip->psi), "xics", + OBJECT(qdev_get_machine()), &error_abort); } static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) @@ -794,6 +806,16 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) error_propagate(errp, error); return; } + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs); } static Property pnv_chip_properties[] = { @@ -824,6 +846,29 @@ static const TypeInfo pnv_chip_info = { .abstract = true, }; +static ICSState *pnv_ics_get(XICSFabric *xi, int irq) +{ + PnvMachineState *pnv = POWERNV_MACHINE(xi); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) { + return &pnv->chips[i]->psi.ics; + } + } + return NULL; +} + +static void pnv_ics_resend(XICSFabric *xi) +{ + PnvMachineState *pnv = POWERNV_MACHINE(xi); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + ics_resend(&pnv->chips[i]->psi.ics); + } +} + static PowerPCCPU *ppc_get_vcpu_by_pir(int pir) { CPUState *cs; @@ -850,6 +895,8 @@ static ICPState *pnv_icp_get(XICSFabric *xi, int pir) static void pnv_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) { + PnvMachineState *pnv = POWERNV_MACHINE(obj); + int i; CPUState *cs; CPU_FOREACH(cs) { @@ -857,6 +904,10 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, icp_pic_print_info(ICP(cpu->intc), mon); } + + for (i = 0; i < pnv->num_chips; i++) { + ics_pic_print_info(&pnv->chips[i]->psi.ics, mon); + } } static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name, @@ -922,6 +973,8 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data) mc->default_boot_order = NULL; mc->default_ram_size = 1 * G_BYTE; xic->icp_get = pnv_icp_get; + xic->ics_get = pnv_ics_get; + xic->ics_resend = pnv_ics_resend; ispc->print_info = pnv_pic_print_info; powernv_machine_class_props_init(oc); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c new file mode 100644 index 0000000000..2bf5bfe3fd --- /dev/null +++ b/hw/ppc/pnv_psi.c @@ -0,0 +1,571 @@ +/* + * QEMU PowerPC PowerNV Processor Service Interface (PSI) model + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "target/ppc/cpu.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#include "exec/address-spaces.h" + +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_psi.h" + +#include + +#define PSIHB_XSCOM_FIR_RW 0x00 +#define PSIHB_XSCOM_FIR_AND 0x01 +#define PSIHB_XSCOM_FIR_OR 0x02 +#define PSIHB_XSCOM_FIRMASK_RW 0x03 +#define PSIHB_XSCOM_FIRMASK_AND 0x04 +#define PSIHB_XSCOM_FIRMASK_OR 0x05 +#define PSIHB_XSCOM_FIRACT0 0x06 +#define PSIHB_XSCOM_FIRACT1 0x07 + +/* Host Bridge Base Address Register */ +#define PSIHB_XSCOM_BAR 0x0a +#define PSIHB_BAR_EN 0x0000000000000001ull + +/* FSP Base Address Register */ +#define PSIHB_XSCOM_FSPBAR 0x0b + +/* PSI Host Bridge Control/Status Register */ +#define PSIHB_XSCOM_CR 0x0e +#define PSIHB_CR_FSP_CMD_ENABLE 0x8000000000000000ull +#define PSIHB_CR_FSP_MMIO_ENABLE 0x4000000000000000ull +#define PSIHB_CR_FSP_IRQ_ENABLE 0x1000000000000000ull +#define PSIHB_CR_FSP_ERR_RSP_ENABLE 0x0800000000000000ull +#define PSIHB_CR_PSI_LINK_ENABLE 0x0400000000000000ull +#define PSIHB_CR_FSP_RESET 0x0200000000000000ull +#define PSIHB_CR_PSIHB_RESET 0x0100000000000000ull +#define PSIHB_CR_PSI_IRQ 0x0000800000000000ull +#define PSIHB_CR_FSP_IRQ 0x0000400000000000ull +#define PSIHB_CR_FSP_LINK_ACTIVE 0x0000200000000000ull +#define PSIHB_CR_IRQ_CMD_EXPECT 0x0000010000000000ull + /* and more ... */ + +/* PSIHB Status / Error Mask Register */ +#define PSIHB_XSCOM_SEMR 0x0f + +/* XIVR, to signal interrupts to the CEC firmware. more XIVR below. */ +#define PSIHB_XSCOM_XIVR_FSP 0x10 +#define PSIHB_XIVR_SERVER_SH 40 +#define PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH) +#define PSIHB_XIVR_PRIO_SH 32 +#define PSIHB_XIVR_PRIO_MSK (0xffull << PSIHB_XIVR_PRIO_SH) +#define PSIHB_XIVR_SRC_SH 29 +#define PSIHB_XIVR_SRC_MSK (0x7ull << PSIHB_XIVR_SRC_SH) +#define PSIHB_XIVR_PENDING 0x01000000ull + +/* PSI Host Bridge Set Control/ Status Register */ +#define PSIHB_XSCOM_SCR 0x12 + +/* PSI Host Bridge Clear Control/ Status Register */ +#define PSIHB_XSCOM_CCR 0x13 + +/* DMA Upper Address Register */ +#define PSIHB_XSCOM_DMA_UPADD 0x14 + +/* Interrupt Status */ +#define PSIHB_XSCOM_IRQ_STAT 0x15 +#define PSIHB_IRQ_STAT_OCC 0x0000001000000000ull +#define PSIHB_IRQ_STAT_FSI 0x0000000800000000ull +#define PSIHB_IRQ_STAT_LPCI2C 0x0000000400000000ull +#define PSIHB_IRQ_STAT_LOCERR 0x0000000200000000ull +#define PSIHB_IRQ_STAT_EXT 0x0000000100000000ull + +/* remaining XIVR */ +#define PSIHB_XSCOM_XIVR_OCC 0x16 +#define PSIHB_XSCOM_XIVR_FSI 0x17 +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18 +#define PSIHB_XSCOM_XIVR_LOCERR 0x19 +#define PSIHB_XSCOM_XIVR_EXT 0x1a + +/* Interrupt Requester Source Compare Register */ +#define PSIHB_XSCOM_IRSN 0x1b +#define PSIHB_IRSN_COMP_SH 45 +#define PSIHB_IRSN_COMP_MSK (0x7ffffull << PSIHB_IRSN_COMP_SH) +#define PSIHB_IRSN_IRQ_MUX 0x0000000800000000ull +#define PSIHB_IRSN_IRQ_RESET 0x0000000400000000ull +#define PSIHB_IRSN_DOWNSTREAM_EN 0x0000000200000000ull +#define PSIHB_IRSN_UPSTREAM_EN 0x0000000100000000ull +#define PSIHB_IRSN_COMPMASK_SH 13 +#define PSIHB_IRSN_COMPMASK_MSK (0x7ffffull << PSIHB_IRSN_COMPMASK_SH) + +#define PSIHB_BAR_MASK 0x0003fffffff00000ull +#define PSIHB_FSPBAR_MASK 0x0003ffff00000000ull + +static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) +{ + MemoryRegion *sysmem = get_system_memory(); + uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; + + psi->regs[PSIHB_XSCOM_BAR] = bar & (PSIHB_BAR_MASK | PSIHB_BAR_EN); + + /* Update MR, always remove it first */ + if (old & PSIHB_BAR_EN) { + memory_region_del_subregion(sysmem, &psi->regs_mr); + } + + /* Then add it back if needed */ + if (bar & PSIHB_BAR_EN) { + uint64_t addr = bar & PSIHB_BAR_MASK; + memory_region_add_subregion(sysmem, addr, &psi->regs_mr); + } +} + +static void pnv_psi_update_fsp_mr(PnvPsi *psi) +{ + /* TODO: Update FSP MR if/when we support FSP BAR */ +} + +static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr) +{ + uint64_t old = psi->regs[PSIHB_XSCOM_CR]; + + psi->regs[PSIHB_XSCOM_CR] = cr; + + /* Check some bit changes */ + if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) { + pnv_psi_update_fsp_mr(psi); + } +} + +static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val) +{ + ICSState *ics = &psi->ics; + + /* In this model we ignore the up/down enable bits for now + * as SW doesn't use them (other than setting them at boot). + * We ignore IRQ_MUX, its meaning isn't clear and we don't use + * it and finally we ignore reset (XXX fix that ?) + */ + psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK | + PSIHB_IRSN_IRQ_MUX | + PSIHB_IRSN_IRQ_RESET | + PSIHB_IRSN_DOWNSTREAM_EN | + PSIHB_IRSN_UPSTREAM_EN); + + /* We ignore the compare mask as well, our ICS emulation is too + * simplistic to make any use if it, and we extract the offset + * from the compare value + */ + ics->offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH; +} + +/* + * FSP and PSI interrupts are muxed under the same number. + */ +static const uint32_t xivr_regs[] = { + [PSIHB_IRQ_PSI] = PSIHB_XSCOM_XIVR_FSP, + [PSIHB_IRQ_FSP] = PSIHB_XSCOM_XIVR_FSP, + [PSIHB_IRQ_OCC] = PSIHB_XSCOM_XIVR_OCC, + [PSIHB_IRQ_FSI] = PSIHB_XSCOM_XIVR_FSI, + [PSIHB_IRQ_LPC_I2C] = PSIHB_XSCOM_XIVR_LPCI2C, + [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_XIVR_LOCERR, + [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_XIVR_EXT, +}; + +static const uint32_t stat_regs[] = { + [PSIHB_IRQ_PSI] = PSIHB_XSCOM_CR, + [PSIHB_IRQ_FSP] = PSIHB_XSCOM_CR, + [PSIHB_IRQ_OCC] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_FSI] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_LPC_I2C] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_IRQ_STAT, +}; + +static const uint64_t stat_bits[] = { + [PSIHB_IRQ_PSI] = PSIHB_CR_PSI_IRQ, + [PSIHB_IRQ_FSP] = PSIHB_CR_FSP_IRQ, + [PSIHB_IRQ_OCC] = PSIHB_IRQ_STAT_OCC, + [PSIHB_IRQ_FSI] = PSIHB_IRQ_STAT_FSI, + [PSIHB_IRQ_LPC_I2C] = PSIHB_IRQ_STAT_LPCI2C, + [PSIHB_IRQ_LOCAL_ERR] = PSIHB_IRQ_STAT_LOCERR, + [PSIHB_IRQ_EXTERNAL] = PSIHB_IRQ_STAT_EXT, +}; + +void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) +{ + ICSState *ics = &psi->ics; + uint32_t xivr_reg; + uint32_t stat_reg; + uint32_t src; + bool masked; + + if (irq > PSIHB_IRQ_EXTERNAL) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); + return; + } + + xivr_reg = xivr_regs[irq]; + stat_reg = stat_regs[irq]; + + src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + if (state) { + psi->regs[stat_reg] |= stat_bits[irq]; + /* TODO: optimization, check mask here. That means + * re-evaluating when unmasking + */ + qemu_irq_raise(ics->qirqs[src]); + } else { + psi->regs[stat_reg] &= ~stat_bits[irq]; + + /* FSP and PSI are muxed so don't lower if either is still set */ + if (stat_reg != PSIHB_XSCOM_CR || + !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { + qemu_irq_lower(ics->qirqs[src]); + } else { + state = true; + } + } + + /* Note about the emulation of the pending bit: This isn't + * entirely correct. The pending bit should be cleared when the + * EOI has been received. However, we don't have callbacks on EOI + * (especially not under KVM) so no way to emulate that properly, + * so instead we just set that bit as the logical "output" of the + * XIVR (ie pending & !masked) + * + * CLG: We could define a new ICS object with a custom eoi() + * handler to clear the pending bit. But I am not sure this would + * be useful for the software anyhow. + */ + masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK; + if (state && !masked) { + psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING; + } else { + psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING; + } +} + +static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val) +{ + ICSState *ics = &psi->ics; + uint16_t server; + uint8_t prio; + uint8_t src; + + psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) | + (val & (PSIHB_XIVR_SERVER_MSK | + PSIHB_XIVR_PRIO_MSK | + PSIHB_XIVR_SRC_MSK)); + val = psi->regs[reg]; + server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH; + prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH; + src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + + if (src >= PSI_NUM_INTERRUPTS) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", src); + return; + } + + /* Remove pending bit if the IRQ is masked */ + if ((psi->regs[reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK) { + psi->regs[reg] &= ~PSIHB_XIVR_PENDING; + } + + /* The low order 2 bits are the link pointer (Type II interrupts). + * Shift back to get a valid IRQ server. + */ + server >>= 2; + + /* Now because of source remapping, weird things can happen + * if you change the source number dynamically, our simple ICS + * doesn't deal with remapping. So we just poke a different + * ICS entry based on what source number was written. This will + * do for now but a more accurate implementation would instead + * use a fixed server/prio and a remapper of the generated irq. + */ + ics_simple_write_xive(ics, src, server, prio, prio); +} + +static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio) +{ + uint64_t val = 0xffffffffffffffffull; + + switch (offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_BAR: + case PSIHB_XSCOM_FSPBAR: + case PSIHB_XSCOM_CR: + case PSIHB_XSCOM_XIVR_FSP: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + case PSIHB_XSCOM_IRQ_STAT: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + case PSIHB_XSCOM_IRSN: + val = psi->regs[offset]; + break; + default: + qemu_log_mask(LOG_UNIMP, "PSI: read at Ox%" PRIx32 "\n", offset); + } + return val; +} + +static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val, + bool mmio) +{ + switch (offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + psi->regs[offset] = val; + break; + case PSIHB_XSCOM_FIR_OR: + psi->regs[PSIHB_XSCOM_FIR_RW] |= val; + break; + case PSIHB_XSCOM_FIR_AND: + psi->regs[PSIHB_XSCOM_FIR_RW] &= val; + break; + case PSIHB_XSCOM_BAR: + /* Only XSCOM can write this one */ + if (!mmio) { + pnv_psi_set_bar(psi, val); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of BAR\n"); + } + break; + case PSIHB_XSCOM_FSPBAR: + psi->regs[PSIHB_XSCOM_FSPBAR] = val & PSIHB_FSPBAR_MASK; + pnv_psi_update_fsp_mr(psi); + break; + case PSIHB_XSCOM_CR: + pnv_psi_set_cr(psi, val); + break; + case PSIHB_XSCOM_SCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val); + break; + case PSIHB_XSCOM_CCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val); + break; + case PSIHB_XSCOM_XIVR_FSP: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + pnv_psi_set_xivr(psi, offset, val); + break; + case PSIHB_XSCOM_IRQ_STAT: + /* Read only */ + qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of IRQ_STAT\n"); + break; + case PSIHB_XSCOM_IRSN: + pnv_psi_set_irsn(psi, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "PSI: write at Ox%" PRIx32 "\n", offset); + } +} + +/* + * The values of the registers when accessed through the MMIO region + * follow the relation : xscom = (mmio + 0x50) >> 3 + */ +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + return pnv_psi_reg_read(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, true); +} + +static void pnv_psi_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + pnv_psi_reg_write(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, val, true); +} + +static const MemoryRegionOps psi_mmio_ops = { + .read = pnv_psi_mmio_read, + .write = pnv_psi_mmio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_psi_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + return pnv_psi_reg_read(opaque, addr >> 3, false); +} + +static void pnv_psi_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + pnv_psi_reg_write(opaque, addr >> 3, val, false); +} + +static const MemoryRegionOps pnv_psi_xscom_ops = { + .read = pnv_psi_xscom_read, + .write = pnv_psi_xscom_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + } +}; + +static void pnv_psi_init(Object *obj) +{ + PnvPsi *psi = PNV_PSI(obj); + + object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE); + object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL); +} + +static const uint8_t irq_to_xivr[] = { + PSIHB_XSCOM_XIVR_FSP, + PSIHB_XSCOM_XIVR_OCC, + PSIHB_XSCOM_XIVR_FSI, + PSIHB_XSCOM_XIVR_LPCI2C, + PSIHB_XSCOM_XIVR_LOCERR, + PSIHB_XSCOM_XIVR_EXT, +}; + +static void pnv_psi_realize(DeviceState *dev, Error **errp) +{ + PnvPsi *psi = PNV_PSI(dev); + ICSState *ics = &psi->ics; + Object *obj; + Error *err = NULL; + unsigned int i; + + obj = object_property_get_link(OBJECT(dev), "xics", &err); + if (!obj) { + error_setg(errp, "%s: required link 'xics' not found: %s", + __func__, error_get_pretty(err)); + return; + } + + /* Create PSI interrupt control source */ + object_property_add_const_link(OBJECT(ics), "xics", obj, &error_abort); + object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(ics), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + for (i = 0; i < ics->nr_irqs; i++) { + ics_set_irq_type(ics, i, true); + } + + /* XSCOM region for PSI registers */ + pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops, + psi, "xscom-psi", PNV_XSCOM_PSIHB_SIZE); + + /* Initialize MMIO region */ + memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, + "psihb", PNV_PSIHB_SIZE); + + /* Default BAR for MMIO region */ + pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); + + /* Default sources in XIVR */ + for (i = 0; i < PSI_NUM_INTERRUPTS; i++) { + uint8_t xivr = irq_to_xivr[i]; + psi->regs[xivr] = PSIHB_XIVR_PRIO_MSK | + ((uint64_t) i << PSIHB_XIVR_SRC_SH); + } +} + +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) +{ + const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; + char *name; + int offset; + uint32_t lpc_pcba = PNV_XSCOM_PSIHB_BASE; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV_XSCOM_PSIHB_SIZE) + }; + + name = g_strdup_printf("psihb@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, + sizeof(compat)))); + return 0; +} + +static Property pnv_psi_properties[] = { + DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), + DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_psi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xdc->populate = pnv_psi_populate; + + dc->realize = pnv_psi_realize; + dc->props = pnv_psi_properties; +} + +static const TypeInfo pnv_psi_info = { + .name = TYPE_PNV_PSI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvPsi), + .instance_init = pnv_psi_init, + .class_init = pnv_psi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_psi_register_types(void) +{ + type_register_static(&pnv_psi_info); +} + +type_init(pnv_psi_register_types) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 96231c1ff7..a3ed2ee647 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -22,6 +22,7 @@ #include "hw/boards.h" #include "hw/sysbus.h" #include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_psi.h" #define TYPE_PNV_CHIP "powernv-chip" #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) @@ -57,6 +58,7 @@ typedef struct PnvChip { MemoryRegion icp_mmio; PnvLpcController lpc; + PnvPsi psi; } PnvChip; typedef struct PnvChipClass { @@ -125,6 +127,7 @@ typedef struct PnvMachineState { PnvChip **chips; ISABus *isa_bus; + uint32_t cpld_irqstate; } PnvMachineState; #define PNV_FDT_ADDR 0x01000000 @@ -155,4 +158,14 @@ typedef struct PnvMachineState { #define PNV_ICP_BASE(chip) \ (0x0003ffff80000000ull + (uint64_t) PNV_CHIP_INDEX(chip) * PNV_ICP_SIZE) + +#define PNV_PSIHB_SIZE 0x0000000000100000ull +#define PNV_PSIHB_BASE(chip) \ + (0x0003fffe80000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * PNV_PSIHB_SIZE) + +#define PNV_PSIHB_FSP_SIZE 0x0000000100000000ull +#define PNV_PSIHB_FSP_BASE(chip) \ + (0x0003ffe000000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * \ + PNV_PSIHB_FSP_SIZE) + #endif /* _PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h new file mode 100644 index 0000000000..11d83e43f8 --- /dev/null +++ b/include/hw/ppc/pnv_psi.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC PowerNV Processor Service Interface (PSI) model + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef _PPC_PNV_PSI_H +#define _PPC_PNV_PSI_H + +#include "hw/sysbus.h" +#include "hw/ppc/xics.h" + +#define TYPE_PNV_PSI "pnv-psi" +#define PNV_PSI(obj) \ + OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI) + +#define PSIHB_XSCOM_MAX 0x20 + +typedef struct XICSState XICSState; + +typedef struct PnvPsi { + SysBusDevice parent; + + MemoryRegion regs_mr; + uint64_t bar; + + /* FSP region not supported */ + /* MemoryRegion fsp_mr; */ + uint64_t fsp_bar; + + /* Interrupt generation */ + ICSState ics; + + /* Registers */ + uint64_t regs[PSIHB_XSCOM_MAX]; + + MemoryRegion xscom_regs; +} PnvPsi; + +/* The PSI and FSP interrupts are muxed on the same IRQ number */ +typedef enum PnvPsiIrq { + PSIHB_IRQ_PSI, /* internal use only */ + PSIHB_IRQ_FSP, /* internal use only */ + PSIHB_IRQ_OCC, + PSIHB_IRQ_FSI, + PSIHB_IRQ_LPC_I2C, + PSIHB_IRQ_LOCAL_ERR, + PSIHB_IRQ_EXTERNAL, +} PnvPsiIrq; + +#define PSI_NUM_INTERRUPTS 6 + +extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state); + +#endif /* _PPC_PNV_PSI_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 0faa1847bf..15f7fdb147 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass { #define PNV_XSCOM_LPC_BASE 0xb0020 #define PNV_XSCOM_LPC_SIZE 0x4 +#define PNV_XSCOM_PSIHB_BASE 0x2010900 +#define PNV_XSCOM_PSIHB_SIZE 0x20 + extern void pnv_xscom_realize(PnvChip *chip, Error **errp); extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); -- cgit 1.4.1 From 0722d05ad8516636e3c2e9033cba7d2d27b59624 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 14:41:27 +0200 Subject: ppc/pnv: Add OCC model stub with interrupt support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OCC is an on-chip microcontroller based on a ppc405 core used for various power management tasks. It comes with a pile of additional hardware sitting on the PIB (aka XSCOM bus). At this point we don't emulate it (nor plan to do so). However there is one facility which is provided by the surrounding hardware that we do need, which is the interrupt generation facility. OPAL uses it to send itself interrupts under some circumstances and there are other uses around the corner. So this implement just enough to support this. Signed-off-by: Benjamin Herrenschmidt [clg: - updated for qemu-2.9 - changed the XSCOM interface to fit new model - QOMified the model ] Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/Makefile.objs | 2 +- hw/ppc/pnv.c | 13 +++++ hw/ppc/pnv_occ.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 2 + include/hw/ppc/pnv_occ.h | 38 +++++++++++++ include/hw/ppc/pnv_xscom.h | 3 + 6 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 hw/ppc/pnv_occ.c create mode 100644 include/hw/ppc/pnv_occ.h (limited to 'include') diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index dc19ee17fa..ef67ea8201 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o # IBM PowerNV -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a516acbe19..16f32c9bbd 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -694,6 +694,11 @@ static void pnv_chip_init(Object *obj) object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL); object_property_add_const_link(OBJECT(&chip->psi), "xics", OBJECT(qdev_get_machine()), &error_abort); + + object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC); + object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL); + object_property_add_const_link(OBJECT(&chip->occ), "psi", + OBJECT(&chip->psi), &error_abort); } static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) @@ -816,6 +821,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) return; } pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs); + + /* Create the simplified OCC model */ + object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs); } static Property pnv_chip_properties[] = { diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c new file mode 100644 index 0000000000..04880f26d6 --- /dev/null +++ b/hw/ppc/pnv_occ.c @@ -0,0 +1,136 @@ +/* + * QEMU PowerPC PowerNV Emulation of a few OCC related registers + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_occ.h" + +#define OCB_OCI_OCCMISC 0x4020 +#define OCB_OCI_OCCMISC_AND 0x4021 +#define OCB_OCI_OCCMISC_OR 0x4022 + +static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) +{ + bool irq_state; + + val &= 0xffff000000000000ull; + + occ->occmisc = val; + irq_state = !!(val >> 63); + pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state); +} + +static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvOCC *occ = PNV_OCC(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case OCB_OCI_OCCMISC: + val = occ->occmisc; + break; + default: + qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } + return val; +} + +static void pnv_occ_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvOCC *occ = PNV_OCC(opaque); + uint32_t offset = addr >> 3; + + switch (offset) { + case OCB_OCI_OCCMISC_AND: + pnv_occ_set_misc(occ, occ->occmisc & val); + break; + case OCB_OCI_OCCMISC_OR: + pnv_occ_set_misc(occ, occ->occmisc | val); + break; + case OCB_OCI_OCCMISC: + pnv_occ_set_misc(occ, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps pnv_occ_xscom_ops = { + .read = pnv_occ_xscom_read, + .write = pnv_occ_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + + +static void pnv_occ_realize(DeviceState *dev, Error **errp) +{ + PnvOCC *occ = PNV_OCC(dev); + Object *obj; + Error *error = NULL; + + occ->occmisc = 0; + + /* get PSI object from chip */ + obj = object_property_get_link(OBJECT(dev), "psi", &error); + if (!obj) { + error_setg(errp, "%s: required link 'psi' not found: %s", + __func__, error_get_pretty(error)); + return; + } + occ->psi = PNV_PSI(obj); + + /* XScom region for OCC registers */ + pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops, + occ, "xscom-occ", PNV_XSCOM_OCC_SIZE); +} + +static void pnv_occ_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_occ_realize; +} + +static const TypeInfo pnv_occ_type_info = { + .name = TYPE_PNV_OCC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvOCC), + .class_init = pnv_occ_class_init, +}; + +static void pnv_occ_register_types(void) +{ + type_register_static(&pnv_occ_type_info); +} + +type_init(pnv_occ_register_types) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index a3ed2ee647..b45a0d91c8 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -23,6 +23,7 @@ #include "hw/sysbus.h" #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_psi.h" +#include "hw/ppc/pnv_occ.h" #define TYPE_PNV_CHIP "powernv-chip" #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) @@ -59,6 +60,7 @@ typedef struct PnvChip { PnvLpcController lpc; PnvPsi psi; + PnvOCC occ; } PnvChip; typedef struct PnvChipClass { diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h new file mode 100644 index 0000000000..f8ec330abf --- /dev/null +++ b/include/hw/ppc/pnv_occ.h @@ -0,0 +1,38 @@ +/* + * QEMU PowerPC PowerNV Emulation of a few OCC related registers + * + * Copyright (c) 2015-2017, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef _PPC_PNV_OCC_H +#define _PPC_PNV_OCC_H + +#define TYPE_PNV_OCC "pnv-occ" +#define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC) + +typedef struct PnvPsi PnvPsi; + +typedef struct PnvOCC { + DeviceState xd; + + /* OCC Misc interrupt */ + uint64_t occmisc; + + PnvPsi *psi; + + MemoryRegion xscom_regs; +} PnvOCC; + +#endif /* _PPC_PNV_OCC_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 15f7fdb147..3757b2cab9 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -63,6 +63,9 @@ typedef struct PnvXScomInterfaceClass { #define PNV_XSCOM_PSIHB_BASE 0x2010900 #define PNV_XSCOM_PSIHB_SIZE 0x20 +#define PNV_XSCOM_OCC_BASE 0x0066000 +#define PNV_XSCOM_OCC_SIZE 0x6000 + extern void pnv_xscom_realize(PnvChip *chip, Error **errp); extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); -- cgit 1.4.1 From 7fabcdb942c2eefa3a40f4cc5ebae25cc13f6ddc Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 5 Apr 2017 14:41:33 +0200 Subject: ipmi: introduce an ipmi_bmc_sdr_find() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch exposes a new IPMI routine to query a sdr entry from the sdr table maintained by the IPMI BMC simulator. The API is very similar to the internal sdr_find_entry() routine and should be used the same way to query one or all sdrs. A typical use would be to loop on the sdrs to build nodes of a device tree. Signed-off-by: Cédric Le Goater Acked-by: Corey Minyard Signed-off-by: David Gibson --- hw/ipmi/ipmi_bmc_sim.c | 16 ++++++++++++++++ include/hw/ipmi/ipmi.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index feb7627b05..09213ab36f 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -416,6 +416,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, return 1; } +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec) + +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + unsigned int pos; + + pos = 0; + if (sdr_find_entry(&ibs->sdr, recid, &pos, nextrec)) { + return -1; + } + + *sdr = (const struct ipmi_sdr_compact *) &ibs->sdr.sdr[pos]; + return 0; +} + static void sel_inc_reservation(IPMISel *sel) { sel->reservation++; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 91b83b5bb0..0d36cfc6b7 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -259,4 +259,6 @@ struct ipmi_sdr_compact { typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); #endif -- cgit 1.4.1 From cd60d85ef6a557acd519874827b52517e6fbc523 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 5 Apr 2017 14:41:34 +0200 Subject: ipmi: introduce an ipmi_bmc_gen_event() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used to fill the message buffer with custom events expected by some systems. Typically, an Open PowerNV platform guest is notified with an OEM SEL message before a shutdown or a reboot. Signed-off-by: Cédric Le Goater Acked-by: Corey Minyard Signed-off-by: David Gibson --- hw/ipmi/ipmi_bmc_sim.c | 24 ++++++++++++++++++++++++ include/hw/ipmi/ipmi.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 09213ab36f..277c28cb40 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -473,6 +473,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs) IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); } +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { + return; + } + + if (log && IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { + sel_add_event(ibs, evt); + } + + if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { + goto out; + } + + memcpy(ibs->evtbuf, evt, 16); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + out: + return; +} static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) { diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 0d36cfc6b7..0affe5a4d8 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -261,4 +261,6 @@ typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); + #endif -- cgit 1.4.1 From 71cd4dace9abf51469cfbf6db622124993955f78 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 5 Apr 2017 08:37:44 +0200 Subject: spapr: remove the 'nr_servers' field from the machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xics_system_init() does not need 'nr_servers' anymore as it is only used to define the 'interrupt-controller' node in the device tree. So let's just compute the value when calling spapr_dt_xics(). This also gives us an opportunity to simplify the xics_system_init() routine and introduce a specific spapr_ics_create() helper to create the sPAPR ICS object. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/spapr.c | 56 ++++++++++++++++++++++---------------------------- include/hw/ppc/spapr.h | 1 - 2 files changed, 24 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 703b14a15b..80d12d005c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -97,45 +97,40 @@ #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) -static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics, - const char *type_icp, int nr_servers, - int nr_irqs, Error **errp) +static ICSState *spapr_ics_create(sPAPRMachineState *spapr, + const char *type_ics, + int nr_irqs, Error **errp) { - XICSFabric *xi = XICS_FABRIC(spapr); Error *err = NULL, *local_err = NULL; - ICSState *ics = NULL; + Object *obj; - ics = ICS_SIMPLE(object_new(type_ics)); - object_property_add_child(OBJECT(spapr), "ics", OBJECT(ics), NULL); - object_property_set_int(OBJECT(ics), nr_irqs, "nr-irqs", &err); - object_property_add_const_link(OBJECT(ics), "xics", OBJECT(xi), NULL); - object_property_set_bool(OBJECT(ics), true, "realized", &local_err); + obj = object_new(type_ics); + object_property_add_child(OBJECT(spapr), "ics", obj, NULL); + object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); + object_property_set_int(obj, nr_irqs, "nr-irqs", &err); + object_property_set_bool(obj, true, "realized", &local_err); error_propagate(&err, local_err); if (err) { error_propagate(errp, err); - return -1; + return NULL; } - spapr->nr_servers = nr_servers; - spapr->ics = ics; - spapr->icp_type = type_icp; - return 0; + return ICS_SIMPLE(obj); } -static int xics_system_init(MachineState *machine, - int nr_servers, int nr_irqs, Error **errp) +static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { - int rc = -1; + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); if (kvm_enabled()) { Error *err = NULL; if (machine_kernel_irqchip_allowed(machine) && - !xics_kvm_init(SPAPR_MACHINE(machine), errp)) { - rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_KVM, - TYPE_KVM_ICP, nr_servers, nr_irqs, &err); + !xics_kvm_init(spapr, errp)) { + spapr->icp_type = TYPE_KVM_ICP; + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, &err); } - if (machine_kernel_irqchip_required(machine) && rc < 0) { + if (machine_kernel_irqchip_required(machine) && !spapr->ics) { error_reportf_err(err, "kernel_irqchip requested but unavailable: "); } else { @@ -143,13 +138,11 @@ static int xics_system_init(MachineState *machine, } } - if (rc < 0) { - xics_spapr_init(SPAPR_MACHINE(machine), errp); - rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_SIMPLE, - TYPE_ICP, nr_servers, nr_irqs, errp); + if (!spapr->ics) { + xics_spapr_init(spapr, errp); + spapr->icp_type = TYPE_ICP; + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); } - - return rc; } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, @@ -977,6 +970,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, void *fdt; sPAPRPHBState *phb; char *buf; + int smt = kvmppc_smt_threads(); fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); @@ -1016,7 +1010,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); /* /interrupt controller */ - spapr_dt_xics(spapr->nr_servers, fdt, PHANDLE_XICP); + spapr_dt_xics(DIV_ROUND_UP(max_cpus * smt, smp_threads), fdt, PHANDLE_XICP); ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { @@ -2045,7 +2039,6 @@ static void ppc_spapr_init(MachineState *machine) hwaddr node0_size = spapr_node0_size(); long load_limit, fw_size; char *filename; - int smt = kvmppc_smt_threads(); msi_nonbroken = true; @@ -2096,8 +2089,7 @@ static void ppc_spapr_init(MachineState *machine) load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; /* Set up Interrupt Controller before we create the VCPUs */ - xics_system_init(machine, DIV_ROUND_UP(max_cpus * smt, smp_threads), - XICS_IRQS_SPAPR, &error_fatal); + xics_system_init(machine, XICS_IRQS_SPAPR, &error_fatal); /* Set up containers for ibm,client-set-architecture negotiated options */ spapr->ov5 = spapr_ovec_new(); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 7dbba577b0..5802f888c3 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -108,7 +108,6 @@ struct sPAPRMachineState { char *kvm_type; MemoryHotplugState hotplug_memory; - uint32_t nr_servers; const char *icp_type; }; -- cgit 1.4.1 From 4d1df88b63c68f84a3c1a84a7f88cb8e6fa99490 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 11 Apr 2017 17:29:59 +0200 Subject: ppc/pnv: Add support for POWER8+ LPC Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It adds the Naples chip which supports proper LPC interrupts via the LPC controller rather than via an external CPLD. Signed-off-by: Benjamin Herrenschmidt [clg: - updated for qemu-2.9 - ported on latest PowerNV patchset - moved the IRQ handler in pnv_lpc.c - introduced pnv_lpc_isa_irq_create() to create the ISA IRQs ] Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 45 +++------------------- hw/ppc/pnv_lpc.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++- include/hw/ppc/pnv_lpc.h | 8 ++++ 3 files changed, 108 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 16f32c9bbd..27589b91d1 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -346,36 +346,6 @@ static void ppc_powernv_reset(void) cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); } -/* If we don't use the built-in LPC interrupt deserializer, we need - * to provide a set of qirqs for the ISA bus or things will go bad. - * - * Most machines using pre-Naples chips (without said deserializer) - * have a CPLD that will collect the SerIRQ and shoot them as a - * single level interrupt to the P8 chip. So let's setup a hook - * for doing just that. - */ -static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) -{ - PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); - uint32_t old_state = pnv->cpld_irqstate; - PnvChip *chip = opaque; - - if (level) { - pnv->cpld_irqstate |= 1u << n; - } else { - pnv->cpld_irqstate &= ~(1u << n); - } - if (pnv->cpld_irqstate != old_state) { - pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL, - pnv->cpld_irqstate != 0); - } -} - -static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) -{ - /* XXX TODO */ -} - static ISABus *pnv_isa_create(PnvChip *chip) { PnvLpcController *lpc = &chip->lpc; @@ -390,16 +360,7 @@ static ISABus *pnv_isa_create(PnvChip *chip) isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &error_fatal); - /* Not all variants have a working serial irq decoder. If not, - * handling of LPC interrupts becomes a platform issue (some - * platforms have a CPLD to do it). - */ - if (pcc->chip_type == PNV_CHIP_POWER8NVL) { - irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS); - } else { - irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip, - ISA_NUM_IRQS); - } + irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS); isa_bus_irqs(isa_bus, irqs); return isa_bus; @@ -699,6 +660,10 @@ static void pnv_chip_init(Object *obj) object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL); object_property_add_const_link(OBJECT(&chip->occ), "psi", OBJECT(&chip->psi), &error_abort); + + /* The LPC controller needs PSI to generate interrupts */ + object_property_add_const_link(OBJECT(&chip->lpc), "psi", + OBJECT(&chip->psi), &error_abort); } static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 78db52415b..5d20c15e67 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -250,6 +250,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void pnv_lpc_eval_irqs(PnvLpcController *lpc) +{ + bool lpc_to_opb_irq = false; + + /* Update LPC controller to OPB line */ + if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { + uint32_t irqs; + + irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; + lpc_to_opb_irq = (irqs != 0); + } + + /* We don't honor the polarity register, it's pointless and unused + * anyway + */ + if (lpc_to_opb_irq) { + lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; + } else { + lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; + } + + /* Update OPB internal latch */ + lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; + + /* Reflect the interrupt */ + pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0); +} + static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) { PnvLpcController *lpc = opaque; @@ -300,12 +328,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, break; case LPC_HC_IRQSER_CTRL: lpc->lpc_hc_irqser_ctrl = val; + pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQMASK: lpc->lpc_hc_irqmask = val; + pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQSTAT: lpc->lpc_hc_irqstat &= ~val; + pnv_lpc_eval_irqs(lpc); break; case LPC_HC_ERROR_ADDRESS: break; @@ -363,14 +394,15 @@ static void opb_master_write(void *opaque, hwaddr addr, switch (addr) { case OPB_MASTER_LS_IRQ_STAT: lpc->opb_irq_stat &= ~val; + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_MASK: - /* XXX Filter out reserved bits */ lpc->opb_irq_mask = val; + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_POL: - /* XXX Filter out reserved bits */ lpc->opb_irq_pol = val; + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_INPUT: /* Read only */ @@ -398,6 +430,8 @@ static const MemoryRegionOps opb_master_ops = { static void pnv_lpc_realize(DeviceState *dev, Error **errp) { PnvLpcController *lpc = PNV_LPC(dev); + Object *obj; + Error *error = NULL; /* Reg inits */ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; @@ -441,6 +475,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), &pnv_lpc_xscom_ops, lpc, "xscom-lpc", PNV_XSCOM_LPC_SIZE); + + /* get PSI object from chip */ + obj = object_property_get_link(OBJECT(dev), "psi", &error); + if (!obj) { + error_setg(errp, "%s: required link 'psi' not found: %s", + __func__, error_get_pretty(error)); + return; + } + lpc->psi = PNV_PSI(obj); } static void pnv_lpc_class_init(ObjectClass *klass, void *data) @@ -470,3 +513,53 @@ static void pnv_lpc_register_types(void) } type_init(pnv_lpc_register_types) + +/* If we don't use the built-in LPC interrupt deserializer, we need + * to provide a set of qirqs for the ISA bus or things will go bad. + * + * Most machines using pre-Naples chips (without said deserializer) + * have a CPLD that will collect the SerIRQ and shoot them as a + * single level interrupt to the P8 chip. So let's setup a hook + * for doing just that. + */ +static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) +{ + PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + uint32_t old_state = pnv->cpld_irqstate; + PnvLpcController *lpc = PNV_LPC(opaque); + + if (level) { + pnv->cpld_irqstate |= 1u << n; + } else { + pnv->cpld_irqstate &= ~(1u << n); + } + + if (pnv->cpld_irqstate != old_state) { + pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0); + } +} + +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + + /* The Naples HW latches the 1 levels, clearing is done by SW */ + if (level) { + lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; + pnv_lpc_eval_irqs(lpc); + } +} + +qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, + int nirqs) +{ + /* Not all variants have a working serial irq decoder. If not, + * handling of LPC interrupts becomes a platform issue (some + * platforms have a CPLD to do it). + */ + if (chip_type == PNV_CHIP_POWER8NVL) { + return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs); + } else { + return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs); + } +} diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 38e5506975..ccf969af94 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -23,6 +23,8 @@ #define PNV_LPC(obj) \ OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) +typedef struct PnvPsi PnvPsi; + typedef struct PnvLpcController { DeviceState parent; @@ -62,6 +64,12 @@ typedef struct PnvLpcController { /* XSCOM registers */ MemoryRegion xscom_regs; + + /* PSI to generate interrupts */ + PnvPsi *psi; } PnvLpcController; +qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, + int nirqs); + #endif /* _PPC_PNV_LPC_H */ -- cgit 1.4.1 From aeaef83dabfec1c1666e65a0c5375983c7a23089 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Tue, 11 Apr 2017 17:30:05 +0200 Subject: ppc/pnv: add initial IPMI sensors for the BMC simulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skiboot, the firmware for the PowerNV platform, expects the BMC to provide some specific IPMI sensors. These sensors are exposed in the device tree and their values are updated by the firmware at boot time. Sensors of interest are : "FW Boot Progress" "Boot Count" As such a device is defined on the command line, we can only detect its presence at reset time. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/Makefile.objs | 2 +- hw/ppc/pnv.c | 21 ++++++++++++++ hw/ppc/pnv_bmc.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 9 ++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 hw/ppc/pnv_bmc.c (limited to 'include') diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index ef67ea8201..7efc686748 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o # IBM PowerNV -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 977e126aca..685eb122d2 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -35,6 +35,7 @@ #include "qapi/visitor.h" #include "monitor/monitor.h" #include "hw/intc/intc.h" +#include "hw/ipmi/ipmi.h" #include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" @@ -476,16 +477,36 @@ static void *powernv_create_fdt(MachineState *machine) /* Populate ISA devices on chip 0 */ lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt); powernv_populate_isa(pnv->isa_bus, fdt, lpc_offset); + + if (pnv->bmc) { + pnv_bmc_populate_sensors(pnv->bmc, fdt); + } + return fdt; } static void ppc_powernv_reset(void) { MachineState *machine = MACHINE(qdev_get_machine()); + PnvMachineState *pnv = POWERNV_MACHINE(machine); void *fdt; + Object *obj; qemu_devices_reset(); + /* OpenPOWER systems have a BMC, which can be defined on the + * command line with: + * + * -device ipmi-bmc-sim,id=bmc0 + * + * This is the internal simulator but it could also be an external + * BMC. + */ + obj = object_resolve_path_type("", TYPE_IPMI_BMC, NULL); + if (obj) { + pnv->bmc = IPMI_BMC(obj); + } + fdt = powernv_create_fdt(machine); /* Pack resulting tree */ diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c new file mode 100644 index 0000000000..a0820dca55 --- /dev/null +++ b/hw/ppc/pnv_bmc.c @@ -0,0 +1,81 @@ +/* + * QEMU PowerNV, BMC related functions + * + * Copyright (c) 2016-2017, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/ipmi/ipmi.h" +#include "hw/ppc/fdt.h" + +#include "hw/ppc/pnv.h" + +#include + +/* TODO: include definition in ipmi.h */ +#define IPMI_SDR_FULL_TYPE 1 + +void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt) +{ + int offset; + int i; + const struct ipmi_sdr_compact *sdr; + uint16_t nextrec; + + offset = fdt_add_subnode(fdt, 0, "/bmc"); + _FDT(offset); + + _FDT((fdt_setprop_string(fdt, offset, "name", "bmc"))); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); + + offset = fdt_add_subnode(fdt, offset, "sensors"); + _FDT(offset); + + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); + + for (i = 0; !ipmi_bmc_sdr_find(bmc, i, &sdr, &nextrec); i++) { + int off; + char *name; + + if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE && + sdr->header.rec_type != IPMI_SDR_FULL_TYPE) { + continue; + } + + name = g_strdup_printf("sensor@%x", sdr->sensor_owner_number); + off = fdt_add_subnode(fdt, offset, name); + _FDT(off); + g_free(name); + + _FDT((fdt_setprop_cell(fdt, off, "reg", sdr->sensor_owner_number))); + _FDT((fdt_setprop_string(fdt, off, "name", "sensor"))); + _FDT((fdt_setprop_string(fdt, off, "compatible", "ibm,ipmi-sensor"))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-reading-type", + sdr->reading_type))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-id", + sdr->entity_id))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-instance", + sdr->entity_instance))); + _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-type", + sdr->sensor_type))); + } +} diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index b45a0d91c8..02f6cf565c 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -118,6 +118,8 @@ typedef struct PnvChipClass { #define POWERNV_MACHINE(obj) \ OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) +typedef struct IPMIBmc IPMIBmc; + typedef struct PnvMachineState { /*< private >*/ MachineState parent_obj; @@ -130,11 +132,18 @@ typedef struct PnvMachineState { ISABus *isa_bus; uint32_t cpld_irqstate; + + IPMIBmc *bmc; } PnvMachineState; #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL +/* + * BMC helpers + */ +void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt); + /* * POWER8 MMIO base addresses */ -- cgit 1.4.1 From bce0b6915971968e3d00e13af5369f6df3daaeb6 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Tue, 11 Apr 2017 17:30:06 +0200 Subject: ppc/pnv: generate an OEM SEL event on shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenPOWER systems expect to be notified with such an event before a shutdown or a reboot. An OEM SEL message is sent with specific identifiers and a user data containing the request : OFF or REBOOT. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/pnv.c | 14 ++++++++++++++ hw/ppc/pnv_bmc.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 2 ++ 3 files changed, 57 insertions(+) (limited to 'include') diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 685eb122d2..d4bcdb027f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -485,6 +485,15 @@ static void *powernv_create_fdt(MachineState *machine) return fdt; } +static void pnv_powerdown_notify(Notifier *n, void *opaque) +{ + PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + + if (pnv->bmc) { + pnv_bmc_powerdown(pnv->bmc); + } +} + static void ppc_powernv_reset(void) { MachineState *machine = MACHINE(qdev_get_machine()); @@ -638,6 +647,11 @@ static void ppc_powernv_init(MachineState *machine) /* Create an RTC ISA device too */ rtc_init(pnv->isa_bus, 2000, NULL); + + /* OpenPOWER systems use a IPMI SEL Event message to notify the + * host to powerdown */ + pnv->powerdown_notifier.notify = pnv_powerdown_notify; + qemu_register_powerdown_notifier(&pnv->powerdown_notifier); } /* diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index a0820dca55..7b60b4c360 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -32,6 +32,47 @@ /* TODO: include definition in ipmi.h */ #define IPMI_SDR_FULL_TYPE 1 +/* + * OEM SEL Event data packet sent by BMC in response of a Read Event + * Message Buffer command + */ +typedef struct OemSel { + /* SEL header */ + uint8_t id[2]; + uint8_t type; + uint8_t timestamp[4]; + uint8_t manuf_id[3]; + + /* OEM SEL data (6 bytes) follows */ + uint8_t netfun; + uint8_t cmd; + uint8_t data[4]; +} OemSel; + +#define SOFT_OFF 0x00 +#define SOFT_REBOOT 0x01 + +static void pnv_gen_oem_sel(IPMIBmc *bmc, uint8_t reboot) +{ + /* IPMI SEL Event are 16 bytes long */ + OemSel sel = { + .id = { 0x55 , 0x55 }, + .type = 0xC0, /* OEM */ + .manuf_id = { 0x0, 0x0, 0x0 }, + .timestamp = { 0x0, 0x0, 0x0, 0x0 }, + .netfun = 0x3A, /* IBM */ + .cmd = 0x04, /* AMI OEM SEL Power Notification */ + .data = { reboot, 0xFF, 0xFF, 0xFF }, + }; + + ipmi_bmc_gen_event(bmc, (uint8_t *) &sel, 0 /* do not log the event */); +} + +void pnv_bmc_powerdown(IPMIBmc *bmc) +{ + pnv_gen_oem_sel(bmc, SOFT_OFF); +} + void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt) { int offset; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 02f6cf565c..c1288f974d 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -134,6 +134,7 @@ typedef struct PnvMachineState { uint32_t cpld_irqstate; IPMIBmc *bmc; + Notifier powerdown_notifier; } PnvMachineState; #define PNV_FDT_ADDR 0x01000000 @@ -143,6 +144,7 @@ typedef struct PnvMachineState { * BMC helpers */ void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt); +void pnv_bmc_powerdown(IPMIBmc *bmc); /* * POWER8 MMIO base addresses -- cgit 1.4.1