diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 67b773692f..e1d1386306 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -398,6 +398,18 @@ int kvm_create_vcpu(CPUState *cpu) return 0; } +int kvm_create_and_park_vcpu(CPUState *cpu) +{ + int ret = 0; + + ret = kvm_create_vcpu(cpu); + if (!ret) { + kvm_park_vcpu(cpu); + } + + return ret; +} + static int do_kvm_destroy_vcpu(CPUState *cpu) { KVMState *s = kvm_state; diff --git a/cpu-common.c b/cpu-common.c index 7ae136f98c..6b262233a3 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -57,14 +57,12 @@ void cpu_list_unlock(void) qemu_mutex_unlock(&qemu_cpu_list_lock); } -static bool cpu_index_auto_assigned; -static int cpu_get_free_index(void) +int cpu_get_free_index(void) { CPUState *some_cpu; int max_cpu_index = 0; - cpu_index_auto_assigned = true; CPU_FOREACH(some_cpu) { if (some_cpu->cpu_index >= max_cpu_index) { max_cpu_index = some_cpu->cpu_index + 1; @@ -83,8 +81,11 @@ unsigned int cpu_list_generation_id_get(void) void cpu_list_add(CPUState *cpu) { + static bool cpu_index_auto_assigned; + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) { + cpu_index_auto_assigned = true; cpu->cpu_index = cpu_get_free_index(); assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); } else { diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 9e99107b42..0b94af3653 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -357,6 +357,9 @@ static const FlashPartInfo known_devices[] = { .sfdp_read = m25p80_sfdp_w25q512jv }, { INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K), .sfdp_read = m25p80_sfdp_w25q01jvq }, + + /* Microchip */ + { INFO("25csm04", 0x29cc00, 0x100, 64 << 10, 8, 0) }, }; typedef enum { diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 2fb4fa29d4..78609105a8 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -25,6 +25,7 @@ #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/qtest.h" #include @@ -32,6 +33,16 @@ #undef XIVE2_DEBUG +/* XIVE Sync or Flush Notification Block */ +typedef struct XiveSfnBlock { + uint8_t bytes[32]; +} XiveSfnBlock; + +/* XIVE Thread Sync or Flush Notification Area */ +typedef struct XiveThreadNA { + XiveSfnBlock topo[16]; +} XiveThreadNA; + /* * Virtual structures table (VST) */ @@ -45,16 +56,16 @@ typedef struct XiveVstInfo { static const XiveVstInfo vst_infos[] = { - [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, - [VST_ESB] = { "ESB", 1, 16 }, - [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, + [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, + [VST_ESB] = { "ESB", 1, 16 }, + [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, - [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, - [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, - [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, + [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, + [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, + [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, - [VST_IC] = { "IC", 1 /* ? */ , 16 }, /* Topology # */ - [VST_SYNC] = { "SYNC", 1 /* ? */ , 16 }, /* Topology # */ + [VST_IC] = { "IC", 1, /* ? */ 16 }, /* Topology # */ + [VST_SYNC] = { "SYNC", sizeof(XiveThreadNA), 16 }, /* Topology # */ /* * This table contains the backing store pages for the interrupt @@ -206,6 +217,20 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, return pnv_xive2_vst_addr_direct(xive, type, vsd, (idx % vst_per_page)); } +static uint8_t pnv_xive2_nvc_table_compress_shift(PnvXive2 *xive) +{ + uint8_t shift = GETFIELD(PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + return shift > 8 ? 0 : shift; +} + +static uint8_t pnv_xive2_nvg_table_compress_shift(PnvXive2 *xive) +{ + uint8_t shift = GETFIELD(PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + return shift > 8 ? 0 : shift; +} + static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, uint32_t idx) { @@ -219,6 +244,11 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, } vsd = xive->vsds[type][blk]; + if (vsd == 0) { + xive2_error(xive, "VST: vsd == 0 block id %d for VST %s %d !?", + blk, info->name, idx); + return 0; + } /* Remote VST access */ if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { @@ -227,6 +257,12 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0; } + if (type == VST_NVG) { + idx >>= pnv_xive2_nvg_table_compress_shift(xive); + } else if (type == VST_NVC) { + idx >>= pnv_xive2_nvc_table_compress_shift(xive); + } + if (VSD_INDIRECT & vsd) { return pnv_xive2_vst_addr_indirect(xive, type, vsd, idx); } @@ -329,40 +365,115 @@ static int pnv_xive2_write_end(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_end_update(PnvXive2 *xive) +static inline int pnv_xive2_get_current_pir(PnvXive2 *xive) { - uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - int i; + if (!qtest_enabled()) { + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + return ppc_cpu_pir(cpu); + } + return 0; +} + +/* + * After SW injects a Queue Sync or Cache Flush operation, HW will notify + * SW of the completion of the operation by writing a byte of all 1's (0xff) + * to a specific memory location. The memory location is calculated by first + * looking up a base address in the SYNC VSD using the Topology ID of the + * originating thread as the "block" number. This points to a + * 64k block of memory that is further divided into 128 512 byte chunks of + * memory, which is indexed by the thread id of the requesting thread. + * Finally, this 512 byte chunk of memory is divided into 16 32 byte + * chunks which are indexed by the topology id of the targeted IC's chip. + * The values below are the offsets into that 32 byte chunk of memory for + * each type of cache flush or queue sync operation. + */ +#define PNV_XIVE2_QUEUE_IPI 0x00 +#define PNV_XIVE2_QUEUE_HW 0x01 +#define PNV_XIVE2_QUEUE_NXC 0x02 +#define PNV_XIVE2_QUEUE_INT 0x03 +#define PNV_XIVE2_QUEUE_OS 0x04 +#define PNV_XIVE2_QUEUE_POOL 0x05 +#define PNV_XIVE2_QUEUE_HARD 0x06 +#define PNV_XIVE2_CACHE_ENDC 0x08 +#define PNV_XIVE2_CACHE_ESBC 0x09 +#define PNV_XIVE2_CACHE_EASC 0x0a +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10 +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15 +#define PNV_XIVE2_CACHE_NXC 0x18 + +static int pnv_xive2_inject_notify(PnvXive2 *xive, int type) +{ + uint64_t addr; + int pir = pnv_xive2_get_current_pir(xive); + int thread_nr = PNV10_PIR2THREAD(pir); + int thread_topo_id = PNV10_PIR2CHIP(pir); + int ic_topo_id = xive->chip->chip_id; + uint64_t offset = ic_topo_id * sizeof(XiveSfnBlock); + uint8_t byte = 0xff; + MemTxResult result; + + /* Retrieve the address of requesting thread's notification area */ + addr = pnv_xive2_vst_addr(xive, VST_SYNC, thread_topo_id, thread_nr); + + if (!addr) { + xive2_error(xive, "VST: no SYNC entry %x/%x !?", + thread_topo_id, thread_nr); + return -1; + } + + address_space_stb(&address_space_memory, addr + offset + type, byte, + MEMTXATTRS_UNSPECIFIED, &result); + assert(result == MEMTX_OK); + + return 0; +} + +static int pnv_xive2_end_update(PnvXive2 *xive, uint8_t watch_engine) +{ + uint8_t blk; + uint32_t idx; + int i, spec_reg, data_reg; uint64_t endc_watch[4]; + assert(watch_engine < ARRAY_SIZE(endc_watch)); + + spec_reg = (VC_ENDC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (VC_ENDC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, xive->vc_regs[spec_reg]); + idx = GETFIELD(VC_ENDC_WATCH_INDEX, xive->vc_regs[spec_reg]); + for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { - endc_watch[i] = - cpu_to_be64(xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i]); + endc_watch[i] = cpu_to_be64(xive->vc_regs[data_reg + i]); } return pnv_xive2_vst_write(xive, VST_END, blk, idx, endc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_end_cache_load(PnvXive2 *xive) +static void pnv_xive2_end_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + uint8_t blk; + uint32_t idx; uint64_t endc_watch[4] = { 0 }; - int i; + int i, spec_reg, data_reg; + + assert(watch_engine < ARRAY_SIZE(endc_watch)); + + spec_reg = (VC_ENDC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (VC_ENDC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, xive->vc_regs[spec_reg]); + idx = GETFIELD(VC_ENDC_WATCH_INDEX, xive->vc_regs[spec_reg]); if (pnv_xive2_vst_read(xive, VST_END, blk, idx, endc_watch)) { xive2_error(xive, "VST: no END entry %x/%x !?", blk, idx); } for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { - xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i] = - be64_to_cpu(endc_watch[i]); + xive->vc_regs[data_reg + i] = be64_to_cpu(endc_watch[i]); } } @@ -379,40 +490,75 @@ static int pnv_xive2_write_nvp(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_nvp_update(PnvXive2 *xive) +static int pnv_xive2_nxc_to_table_type(uint8_t nxc_type, uint32_t *table_type) { - uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - int i; + switch (nxc_type) { + case PC_NXC_WATCH_NXC_NVP: + *table_type = VST_NVP; + break; + case PC_NXC_WATCH_NXC_NVG: + *table_type = VST_NVG; + break; + case PC_NXC_WATCH_NXC_NVC: + *table_type = VST_NVC; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid table type for nxc operation\n"); + return -1; + } + return 0; +} + +static int pnv_xive2_nxc_update(PnvXive2 *xive, uint8_t watch_engine) +{ + uint8_t blk, nxc_type; + uint32_t idx, table_type = -1; + int i, spec_reg, data_reg; uint64_t nxc_watch[4]; + assert(watch_engine < ARRAY_SIZE(nxc_watch)); + + spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + nxc_type = GETFIELD(PC_NXC_WATCH_NXC_TYPE, xive->pc_regs[spec_reg]); + blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); + idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); + + assert(!pnv_xive2_nxc_to_table_type(nxc_type, &table_type)); + for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { - nxc_watch[i] = - cpu_to_be64(xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i]); + nxc_watch[i] = cpu_to_be64(xive->pc_regs[data_reg + i]); } - return pnv_xive2_vst_write(xive, VST_NVP, blk, idx, nxc_watch, + return pnv_xive2_vst_write(xive, table_type, blk, idx, nxc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_nvp_cache_load(PnvXive2 *xive) +static void pnv_xive2_nxc_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + uint8_t blk, nxc_type; + uint32_t idx, table_type = -1; uint64_t nxc_watch[4] = { 0 }; - int i; + int i, spec_reg, data_reg; - if (pnv_xive2_vst_read(xive, VST_NVP, blk, idx, nxc_watch)) { - xive2_error(xive, "VST: no NVP entry %x/%x !?", blk, idx); + assert(watch_engine < ARRAY_SIZE(nxc_watch)); + + spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + nxc_type = GETFIELD(PC_NXC_WATCH_NXC_TYPE, xive->pc_regs[spec_reg]); + blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); + idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); + + assert(!pnv_xive2_nxc_to_table_type(nxc_type, &table_type)); + + if (pnv_xive2_vst_read(xive, table_type, blk, idx, nxc_watch)) { + xive2_error(xive, "VST: no NXC entry %x/%x in %s table!?", + blk, idx, vst_infos[table_type].name); } for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { - xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i] = - be64_to_cpu(nxc_watch[i]); + xive->pc_regs[data_reg + i] = be64_to_cpu(nxc_watch[i]); } } @@ -581,6 +727,7 @@ static int pnv_xive2_stt_set_data(PnvXive2 *xive, uint64_t val) case CQ_TAR_NVPG: case CQ_TAR_ESB: case CQ_TAR_END: + case CQ_TAR_NVC: xive->tables[tsel][entry] = val; break; default: @@ -641,6 +788,9 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, * entries provisioned by FW (such as skiboot) and resize the * ESB window accordingly. */ + if (memory_region_is_mapped(&xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->esb_mmio, &xsrc->esb_mmio); + } if (!(VSD_INDIRECT & vsd)) { memory_region_set_size(&xsrc->esb_mmio, vst_tsize * SBE_PER_BYTE * (1ull << xsrc->esb_shift)); @@ -656,6 +806,9 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, /* * Backing store pages for the END. */ + if (memory_region_is_mapped(&end_xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->end_mmio, &end_xsrc->esb_mmio); + } if (!(VSD_INDIRECT & vsd)) { memory_region_set_size(&end_xsrc->esb_mmio, (vst_tsize / info->size) * (1ull << end_xsrc->esb_shift)); @@ -680,13 +833,10 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, * Both PC and VC sub-engines are configured as each use the Virtual * Structure Tables */ -static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) +static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd, + uint8_t type, uint8_t blk) { uint8_t mode = GETFIELD(VSD_MODE, vsd); - uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, - xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); - uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, - xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; if (type > VST_ERQ) { @@ -721,6 +871,16 @@ static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) } } +static void pnv_xive2_vc_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + + pnv_xive2_vst_set_data(xive, vsd, type, blk); +} + /* * MMIO handlers */ @@ -964,12 +1124,70 @@ static const MemoryRegionOps pnv_xive2_ic_cq_ops = { }, }; +static uint8_t pnv_xive2_cache_watch_assign(uint64_t engine_mask, + uint64_t *state) +{ + uint8_t val = 0xFF; + int i; + + for (i = 3; i >= 0; i--) { + if (BIT(i) & engine_mask) { + if (!(BIT(i) & *state)) { + *state |= BIT(i); + val = 3 - i; + break; + } + } + } + return val; +} + +static void pnv_xive2_cache_watch_release(uint64_t *state, uint8_t watch_engine) +{ + uint8_t engine_bit = 3 - watch_engine; + + if (*state & BIT(engine_bit)) { + *state &= ~BIT(engine_bit); + } +} + +static uint8_t pnv_xive2_endc_cache_watch_assign(PnvXive2 *xive) +{ + uint64_t engine_mask = GETFIELD(VC_ENDC_CFG_CACHE_WATCH_ASSIGN, + xive->vc_regs[VC_ENDC_CFG >> 3]); + uint64_t state = xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3]; + uint8_t val; + + /* + * We keep track of which engines are currently busy in the + * VC_ENDC_WATCH_ASSIGN register directly. When the firmware reads + * the register, we don't return its value but the ID of an engine + * it can use. + * There are 4 engines. 0xFF means no engine is available. + */ + val = pnv_xive2_cache_watch_assign(engine_mask, &state); + if (val != 0xFF) { + xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3] = state; + } + return val; +} + +static void pnv_xive2_endc_cache_watch_release(PnvXive2 *xive, + uint8_t watch_engine) +{ + uint64_t state = xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3]; + + pnv_xive2_cache_watch_release(&state, watch_engine); + xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3] = state; +} + static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint64_t val = 0; uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1000,24 +1218,44 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, val = xive->vc_regs[reg]; break; + case VC_ENDC_WATCH_ASSIGN: + val = pnv_xive2_endc_cache_watch_assign(xive); + break; + + case VC_ENDC_CFG: + val = xive->vc_regs[reg]; + break; + /* * END cache updates */ case VC_ENDC_WATCH0_SPEC: + case VC_ENDC_WATCH1_SPEC: + case VC_ENDC_WATCH2_SPEC: + case VC_ENDC_WATCH3_SPEC: + watch_engine = (offset - VC_ENDC_WATCH0_SPEC) >> 6; xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT); + pnv_xive2_endc_cache_watch_release(xive, watch_engine); val = xive->vc_regs[reg]; break; case VC_ENDC_WATCH0_DATA0: + case VC_ENDC_WATCH1_DATA0: + case VC_ENDC_WATCH2_DATA0: + case VC_ENDC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the * SPEC register */ - pnv_xive2_end_cache_load(xive); + watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; + pnv_xive2_end_cache_load(xive, watch_engine); val = xive->vc_regs[reg]; break; case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + case VC_ENDC_WATCH1_DATA1 ... VC_ENDC_WATCH1_DATA3: + case VC_ENDC_WATCH2_DATA1 ... VC_ENDC_WATCH2_DATA3: + case VC_ENDC_WATCH3_DATA1 ... VC_ENDC_WATCH3_DATA3: val = xive->vc_regs[reg]; break; @@ -1063,6 +1301,7 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, { PnvXive2 *xive = PNV_XIVE2(opaque); uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1071,7 +1310,7 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, case VC_VSD_TABLE_ADDR: break; case VC_VSD_TABLE_DATA: - pnv_xive2_vst_set_data(xive, val); + pnv_xive2_vc_vst_set_data(xive, val); break; /* @@ -1083,6 +1322,10 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* ESB update */ break; + case VC_ESBC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_ESBC); + break; + case VC_ESBC_CFG: break; @@ -1095,19 +1338,36 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* EAS update */ break; + case VC_EASC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_EASC); + break; + + case VC_ENDC_CFG: + break; + /* * END cache updates */ case VC_ENDC_WATCH0_SPEC: + case VC_ENDC_WATCH1_SPEC: + case VC_ENDC_WATCH2_SPEC: + case VC_ENDC_WATCH3_SPEC: val &= ~VC_ENDC_WATCH_CONFLICT; /* HW will set this bit */ break; case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + case VC_ENDC_WATCH1_DATA1 ... VC_ENDC_WATCH1_DATA3: + case VC_ENDC_WATCH2_DATA1 ... VC_ENDC_WATCH2_DATA3: + case VC_ENDC_WATCH3_DATA1 ... VC_ENDC_WATCH3_DATA3: break; case VC_ENDC_WATCH0_DATA0: + case VC_ENDC_WATCH1_DATA0: + case VC_ENDC_WATCH2_DATA0: + case VC_ENDC_WATCH3_DATA0: /* writing to DATA0 triggers the cache write */ + watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; xive->vc_regs[reg] = val; - pnv_xive2_end_update(xive); + pnv_xive2_end_update(xive, watch_engine); break; @@ -1116,6 +1376,10 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID; break; + case VC_ENDC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_ENDC); + break; + /* * Indirect invalidation */ @@ -1157,12 +1421,43 @@ static const MemoryRegionOps pnv_xive2_ic_vc_ops = { }, }; +static uint8_t pnv_xive2_nxc_cache_watch_assign(PnvXive2 *xive) +{ + uint64_t engine_mask = GETFIELD(PC_NXC_PROC_CONFIG_WATCH_ASSIGN, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + uint64_t state = xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3]; + uint8_t val; + + /* + * We keep track of which engines are currently busy in the + * PC_NXC_WATCH_ASSIGN register directly. When the firmware reads + * the register, we don't return its value but the ID of an engine + * it can use. + * There are 4 engines. 0xFF means no engine is available. + */ + val = pnv_xive2_cache_watch_assign(engine_mask, &state); + if (val != 0xFF) { + xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3] = state; + } + return val; +} + +static void pnv_xive2_nxc_cache_watch_release(PnvXive2 *xive, + uint8_t watch_engine) +{ + uint64_t state = xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3]; + + pnv_xive2_cache_watch_release(&state, watch_engine); + xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3] = state; +} + static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint64_t val = -1; uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1173,24 +1468,44 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, val = xive->pc_regs[reg]; break; + case PC_NXC_WATCH_ASSIGN: + val = pnv_xive2_nxc_cache_watch_assign(xive); + break; + + case PC_NXC_PROC_CONFIG: + val = xive->pc_regs[reg]; + break; + /* * cache updates */ case PC_NXC_WATCH0_SPEC: + case PC_NXC_WATCH1_SPEC: + case PC_NXC_WATCH2_SPEC: + case PC_NXC_WATCH3_SPEC: + watch_engine = (offset - PC_NXC_WATCH0_SPEC) >> 6; xive->pc_regs[reg] &= ~(PC_NXC_WATCH_FULL | PC_NXC_WATCH_CONFLICT); + pnv_xive2_nxc_cache_watch_release(xive, watch_engine); val = xive->pc_regs[reg]; break; case PC_NXC_WATCH0_DATA0: + case PC_NXC_WATCH1_DATA0: + case PC_NXC_WATCH2_DATA0: + case PC_NXC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the * SPEC register */ - pnv_xive2_nvp_cache_load(xive); + watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; + pnv_xive2_nxc_cache_load(xive, watch_engine); val = xive->pc_regs[reg]; break; case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + case PC_NXC_WATCH1_DATA1 ... PC_NXC_WATCH1_DATA3: + case PC_NXC_WATCH2_DATA1 ... PC_NXC_WATCH2_DATA3: + case PC_NXC_WATCH3_DATA1 ... PC_NXC_WATCH3_DATA3: val = xive->pc_regs[reg]; break; @@ -1214,36 +1529,66 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, return val; } +static void pnv_xive2_pc_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t type = GETFIELD(PC_VSD_TABLE_SELECT, + xive->pc_regs[PC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(PC_VSD_TABLE_ADDRESS, + xive->pc_regs[PC_VSD_TABLE_ADDR >> 3]); + + pnv_xive2_vst_set_data(xive, vsd, type, blk); +} + static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* - * VSD table settings. Only taken into account in the VC - * sub-engine because the Xive2Router model combines both VC and PC - * sub-engines + * VSD table settings. + * The Xive2Router model combines both VC and PC sub-engines. We + * allow to configure the tables through both, for the rare cases + * where a table only really needs to be configured for one of + * them (e.g. the NVG table for the presenter). It assumes that + * firmware passes the same address to the VC and PC when tables + * are defined for both, which seems acceptable. */ case PC_VSD_TABLE_ADDR: + break; case PC_VSD_TABLE_DATA: + pnv_xive2_pc_vst_set_data(xive, val); + break; + + case PC_NXC_PROC_CONFIG: break; /* * cache updates */ case PC_NXC_WATCH0_SPEC: + case PC_NXC_WATCH1_SPEC: + case PC_NXC_WATCH2_SPEC: + case PC_NXC_WATCH3_SPEC: val &= ~PC_NXC_WATCH_CONFLICT; /* HW will set this bit */ break; case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + case PC_NXC_WATCH1_DATA1 ... PC_NXC_WATCH1_DATA3: + case PC_NXC_WATCH2_DATA1 ... PC_NXC_WATCH2_DATA3: + case PC_NXC_WATCH3_DATA1 ... PC_NXC_WATCH3_DATA3: break; case PC_NXC_WATCH0_DATA0: + case PC_NXC_WATCH1_DATA0: + case PC_NXC_WATCH2_DATA0: + case PC_NXC_WATCH3_DATA0: /* writing to DATA0 triggers the cache write */ + watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; xive->pc_regs[reg] = val; - pnv_xive2_nvp_update(xive); + pnv_xive2_nxc_update(xive, watch_engine); break; /* case PC_NXC_FLUSH_CTRL: */ @@ -1251,6 +1596,10 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID; break; + case PC_NXC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_NXC); + break; + /* * Indirect invalidation */ @@ -1547,13 +1896,19 @@ static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { /* * Sync MMIO page (write only) */ -#define PNV_XIVE2_SYNC_IPI 0x000 -#define PNV_XIVE2_SYNC_HW 0x080 -#define PNV_XIVE2_SYNC_NxC 0x100 -#define PNV_XIVE2_SYNC_INT 0x180 -#define PNV_XIVE2_SYNC_OS_ESC 0x200 -#define PNV_XIVE2_SYNC_POOL_ESC 0x280 -#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_IPI 0x000 +#define PNV_XIVE2_SYNC_HW 0x080 +#define PNV_XIVE2_SYNC_NxC 0x100 +#define PNV_XIVE2_SYNC_INT 0x180 +#define PNV_XIVE2_SYNC_OS_ESC 0x200 +#define PNV_XIVE2_SYNC_POOL_ESC 0x280 +#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80 static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, unsigned size) @@ -1565,22 +1920,72 @@ static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, return -1; } +/* + * The sync MMIO space spans two pages. The lower page is use for + * queue sync "poll" requests while the upper page is used for queue + * sync "inject" requests. Inject requests require the HW to write + * a byte of all 1's to a predetermined location in memory in order + * to signal completion of the request. Both pages have the same + * layout, so it is easiest to handle both with a single function. + */ static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + int inject_type; + hwaddr pg_offset_mask = (1ull << xive->ic_shift) - 1; - switch (offset) { + /* adjust offset for inject page */ + hwaddr adj_offset = offset & pg_offset_mask; + + switch (adj_offset) { case PNV_XIVE2_SYNC_IPI: + inject_type = PNV_XIVE2_QUEUE_IPI; + break; case PNV_XIVE2_SYNC_HW: + inject_type = PNV_XIVE2_QUEUE_HW; + break; case PNV_XIVE2_SYNC_NxC: + inject_type = PNV_XIVE2_QUEUE_NXC; + break; case PNV_XIVE2_SYNC_INT: + inject_type = PNV_XIVE2_QUEUE_INT; + break; case PNV_XIVE2_SYNC_OS_ESC: + inject_type = PNV_XIVE2_QUEUE_OS; + break; case PNV_XIVE2_SYNC_POOL_ESC: + inject_type = PNV_XIVE2_QUEUE_POOL; + break; case PNV_XIVE2_SYNC_HARD_ESC: + inject_type = PNV_XIVE2_QUEUE_HARD; + break; + case PNV_XIVE2_SYNC_NXC_LD_LCL_NCO: + inject_type = PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO; + break; + case PNV_XIVE2_SYNC_NXC_LD_LCL_CO: + inject_type = PNV_XIVE2_QUEUE_NXC_LD_LCL_CO; + break; + case PNV_XIVE2_SYNC_NXC_ST_LCL_NCI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI; + break; + case PNV_XIVE2_SYNC_NXC_ST_LCL_CI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_LCL_CI; + break; + case PNV_XIVE2_SYNC_NXC_ST_RMT_NCI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI; + break; + case PNV_XIVE2_SYNC_NXC_ST_RMT_CI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_CI; break; default: xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset); + return; + } + + /* Write Queue Sync notification byte if writing to sync inject page */ + if ((offset & ~pg_offset_mask) != 0) { + pnv_xive2_inject_notify(xive, inject_type); } } @@ -1814,6 +2219,12 @@ static void pnv_xive2_reset(void *dev) xive->cq_regs[CQ_XIVE_CFG >> 3] |= SETFIELD(CQ_XIVE_CFG_HYP_HARD_BLOCK_ID, 0ull, xive->chip->chip_id); + /* VC and PC cache watch assign mechanism */ + xive->vc_regs[VC_ENDC_CFG >> 3] = + SETFIELD(VC_ENDC_CFG_CACHE_WATCH_ASSIGN, 0ull, 0b0111); + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3] = + SETFIELD(PC_NXC_PROC_CONFIG_WATCH_ASSIGN, 0ull, 0b0111); + /* Set default page size to 64k */ xive->ic_shift = xive->esb_shift = xive->end_shift = 16; xive->nvc_shift = xive->nvpg_shift = xive->tm_shift = 16; @@ -2025,33 +2436,6 @@ static void pnv_xive2_register_types(void) type_init(pnv_xive2_register_types) -static void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, - GString *buf) -{ - uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); - uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); - - if (!xive2_nvp_is_valid(nvp)) { - return; - } - - g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x", - nvp_idx, eq_blk, eq_idx, - xive_get_field32(NVP2_W2_IPB, nvp->w2)); - /* - * When the NVP is HW controlled, more fields are updated - */ - if (xive2_nvp_is_hw(nvp)) { - g_string_append_printf(buf, " CPPR:%02x", - xive_get_field32(NVP2_W2_CPPR, nvp->w2)); - if (xive2_nvp_is_co(nvp)) { - g_string_append_printf(buf, " CO:%04x", - xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); - } - } - g_string_append_c(buf, '\n'); -} - /* * If the table is direct, we can compute the number of PQ entries * provisioned by FW. diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index 7165dc8704..e8b87b3d2c 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -232,6 +232,10 @@ #define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* ESBC cache flush inject register */ +#define X_VC_ESBC_FLUSH_INJECT 0x142 +#define VC_ESBC_FLUSH_INJECT 0x210 + /* ESBC configuration */ #define X_VC_ESBC_CFG 0x148 #define VC_ESBC_CFG 0x240 @@ -250,6 +254,10 @@ #define VC_EASC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_EASC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* EASC flush inject register */ +#define X_VC_EASC_FLUSH_INJECT 0x162 +#define VC_EASC_FLUSH_INJECT 0x310 + /* * VC2 */ @@ -270,6 +278,10 @@ #define VC_ENDC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define VC_ENDC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* ENDC flush inject register */ +#define X_VC_ENDC_FLUSH_INJECT 0x182 +#define VC_ENDC_FLUSH_INJECT 0x410 + /* ENDC Sync done */ #define X_VC_ENDC_SYNC_DONE 0x184 #define VC_ENDC_SYNC_DONE 0x420 @@ -283,6 +295,15 @@ #define VC_ENDC_SYNC_QUEUE_HARD PPC_BIT(6) #define VC_QUEUE_COUNT 7 +/* ENDC cache watch assign */ +#define X_VC_ENDC_WATCH_ASSIGN 0x186 +#define VC_ENDC_WATCH_ASSIGN 0x430 + +/* ENDC configuration register */ +#define X_VC_ENDC_CFG 0x188 +#define VC_ENDC_CFG 0x440 +#define VC_ENDC_CFG_CACHE_WATCH_ASSIGN PPC_BITMASK(32, 35) + /* ENDC cache watch specification 0 */ #define X_VC_ENDC_WATCH0_SPEC 0x1A0 #define VC_ENDC_WATCH0_SPEC 0x500 @@ -302,6 +323,42 @@ #define VC_ENDC_WATCH0_DATA2 0x530 #define VC_ENDC_WATCH0_DATA3 0x538 +/* ENDC cache watch 1 */ +#define X_VC_ENDC_WATCH1_SPEC 0x1A8 +#define VC_ENDC_WATCH1_SPEC 0x540 +#define X_VC_ENDC_WATCH1_DATA0 0x1AC +#define X_VC_ENDC_WATCH1_DATA1 0x1AD +#define X_VC_ENDC_WATCH1_DATA2 0x1AE +#define X_VC_ENDC_WATCH1_DATA3 0x1AF +#define VC_ENDC_WATCH1_DATA0 0x560 +#define VC_ENDC_WATCH1_DATA1 0x568 +#define VC_ENDC_WATCH1_DATA2 0x570 +#define VC_ENDC_WATCH1_DATA3 0x578 + +/* ENDC cache watch 2 */ +#define X_VC_ENDC_WATCH2_SPEC 0x1B0 +#define VC_ENDC_WATCH2_SPEC 0x580 +#define X_VC_ENDC_WATCH2_DATA0 0x1B4 +#define X_VC_ENDC_WATCH2_DATA1 0x1B5 +#define X_VC_ENDC_WATCH2_DATA2 0x1B6 +#define X_VC_ENDC_WATCH2_DATA3 0x1B7 +#define VC_ENDC_WATCH2_DATA0 0x5A0 +#define VC_ENDC_WATCH2_DATA1 0x5A8 +#define VC_ENDC_WATCH2_DATA2 0x5B0 +#define VC_ENDC_WATCH2_DATA3 0x5B8 + +/* ENDC cache watch 3 */ +#define X_VC_ENDC_WATCH3_SPEC 0x1B8 +#define VC_ENDC_WATCH3_SPEC 0x5C0 +#define X_VC_ENDC_WATCH3_DATA0 0x1BC +#define X_VC_ENDC_WATCH3_DATA1 0x1BD +#define X_VC_ENDC_WATCH3_DATA2 0x1BE +#define X_VC_ENDC_WATCH3_DATA3 0x1BF +#define VC_ENDC_WATCH3_DATA0 0x5E0 +#define VC_ENDC_WATCH3_DATA1 0x5E8 +#define VC_ENDC_WATCH3_DATA2 0x5F0 +#define VC_ENDC_WATCH3_DATA3 0x5F8 + /* * PC LSB1 */ @@ -358,6 +415,21 @@ #define PC_NXC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define PC_NXC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* NxC Cache flush inject */ +#define X_PC_NXC_FLUSH_INJECT 0x282 +#define PC_NXC_FLUSH_INJECT 0x410 + +/* NxC Cache watch assign */ +#define X_PC_NXC_WATCH_ASSIGN 0x286 +#define PC_NXC_WATCH_ASSIGN 0x430 + +/* NxC Proc config */ +#define X_PC_NXC_PROC_CONFIG 0x28A +#define PC_NXC_PROC_CONFIG 0x450 +#define PC_NXC_PROC_CONFIG_WATCH_ASSIGN PPC_BITMASK(0, 3) +#define PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS PPC_BITMASK(32, 35) +#define PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS PPC_BITMASK(36, 39) + /* NxC Cache Watch 0 Specification */ #define X_PC_NXC_WATCH0_SPEC 0x2A0 #define PC_NXC_WATCH0_SPEC 0x500 @@ -381,6 +453,42 @@ #define PC_NXC_WATCH0_DATA2 0x530 #define PC_NXC_WATCH0_DATA3 0x538 +/* NxC Cache Watch 1 */ +#define X_PC_NXC_WATCH1_SPEC 0x2A8 +#define PC_NXC_WATCH1_SPEC 0x540 +#define X_PC_NXC_WATCH1_DATA0 0x2AC +#define X_PC_NXC_WATCH1_DATA1 0x2AD +#define X_PC_NXC_WATCH1_DATA2 0x2AE +#define X_PC_NXC_WATCH1_DATA3 0x2AF +#define PC_NXC_WATCH1_DATA0 0x560 +#define PC_NXC_WATCH1_DATA1 0x568 +#define PC_NXC_WATCH1_DATA2 0x570 +#define PC_NXC_WATCH1_DATA3 0x578 + +/* NxC Cache Watch 2 */ +#define X_PC_NXC_WATCH2_SPEC 0x2B0 +#define PC_NXC_WATCH2_SPEC 0x580 +#define X_PC_NXC_WATCH2_DATA0 0x2B4 +#define X_PC_NXC_WATCH2_DATA1 0x2B5 +#define X_PC_NXC_WATCH2_DATA2 0x2B6 +#define X_PC_NXC_WATCH2_DATA3 0x2B7 +#define PC_NXC_WATCH2_DATA0 0x5A0 +#define PC_NXC_WATCH2_DATA1 0x5A8 +#define PC_NXC_WATCH2_DATA2 0x5B0 +#define PC_NXC_WATCH2_DATA3 0x5B8 + +/* NxC Cache Watch 3 */ +#define X_PC_NXC_WATCH3_SPEC 0x2B8 +#define PC_NXC_WATCH3_SPEC 0x5C0 +#define X_PC_NXC_WATCH3_DATA0 0x2BC +#define X_PC_NXC_WATCH3_DATA1 0x2BD +#define X_PC_NXC_WATCH3_DATA2 0x2BE +#define X_PC_NXC_WATCH3_DATA3 0x2BF +#define PC_NXC_WATCH3_DATA0 0x5E0 +#define PC_NXC_WATCH3_DATA1 0x5E8 +#define PC_NXC_WATCH3_DATA2 0x5F0 +#define PC_NXC_WATCH3_DATA3 0x5F8 + /* * TCTXT Registers */ diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 70f11f993b..5a02dd8e02 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -692,9 +692,15 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf) } } - g_string_append_printf(buf, "CPU[%04x]: " - "QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR W2\n", - cpu_index); + if (xive_presenter_get_config(tctx->xptr) & XIVE_PRESENTER_GEN1_TIMA_OS) { + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR" + " W2\n", cpu_index); + } else { + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB - LGS T PIPR" + " W2\n", cpu_index); + } for (i = 0; i < XIVE_TM_RING_COUNT; i++) { char *s = xive_tctx_ring_print(&tctx->regs[i * XIVE_TM_RING_SIZE]); diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 3e7238c663..1f150685bf 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -89,7 +89,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) pq = xive_get_field32(END2_W1_ESn, end->w1); g_string_append_printf(buf, - " %08x %c%c %c%c%c%c%c%c%c%c%c%c " + " %08x %c%c %c%c%c%c%c%c%c%c%c%c%c %c%c " "prio:%d nvp:%02x/%04x", end_idx, pq & XIVE_ESB_VAL_P ? 'P' : '-', @@ -98,12 +98,15 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) xive2_end_is_enqueue(end) ? 'q' : '-', xive2_end_is_notify(end) ? 'n' : '-', xive2_end_is_backlog(end) ? 'b' : '-', + xive2_end_is_precluded_escalation(end) ? 'p' : '-', xive2_end_is_escalate(end) ? 'e' : '-', xive2_end_is_escalate_end(end) ? 'N' : '-', xive2_end_is_uncond_escalation(end) ? 'u' : '-', xive2_end_is_silent_escalation(end) ? 's' : '-', xive2_end_is_firmware1(end) ? 'f' : '-', xive2_end_is_firmware2(end) ? 'F' : '-', + xive2_end_is_ignore(end) ? 'i' : '-', + xive2_end_is_crowd(end) ? 'c' : '-', priority, nvp_blk, nvp_idx); if (qaddr_base) { @@ -137,6 +140,32 @@ void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } +void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf) +{ + uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); + uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); + + if (!xive2_nvp_is_valid(nvp)) { + return; + } + + g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x", + nvp_idx, eq_blk, eq_idx, + xive_get_field32(NVP2_W2_IPB, nvp->w2)); + /* + * When the NVP is HW controlled, more fields are updated + */ + if (xive2_nvp_is_hw(nvp)) { + g_string_append_printf(buf, " CPPR:%02x", + xive_get_field32(NVP2_W2_CPPR, nvp->w2)); + if (xive2_nvp_is_co(nvp)) { + g_string_append_printf(buf, " CO:%04x", + xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); + } + } + g_string_append_c(buf, '\n'); +} + static void xive2_end_enqueue(Xive2End *end, uint32_t data) { uint64_t qaddr_base = xive2_end_qaddr(end); @@ -650,7 +679,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, - xive_get_field32(END2_W6_IGNORE, end.w7), + xive2_end_is_ignore(&end), priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7)); diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 347212f4db..c235519881 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -39,6 +39,9 @@ config POWERNV select PCI_POWERNV select PCA9552 select PCA9554 + select SSI + select SSI_M25P80 + select PNV_SPI config PPC405 bool diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 3ebbf329bc..7cd9189869 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -42,6 +42,7 @@ endif ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv.c', 'pnv_xscom.c', + 'pnv_adu.c', 'pnv_core.c', 'pnv_i2c.c', 'pnv_lpc.c', diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 6b41d1d2dd..3526852685 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -141,9 +141,9 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); PnvChipClass *pnv_cc = PNV_CHIP_GET_CLASS(chip); - g_autofree uint32_t *servers_prop = g_new(uint32_t, smt_threads); + uint32_t *servers_prop; int i; - uint32_t pir; + uint32_t pir, tir; uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; uint32_t tbfreq = PNV_TIMEBASE_FREQ; @@ -154,7 +154,10 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) char *nodename; int cpus_offset = get_cpus_node(fdt); - pir = pnv_cc->chip_pir(chip, pc->hwid, 0); + pnv_cc->get_pir_tir(chip, pc->hwid, 0, &pir, &tir); + + /* Only one DT node per (big) core */ + g_assert(tir == 0); nodename = g_strdup_printf("%s@%x", dc->fw_name, pir); offset = fdt_add_subnode(fdt, cpus_offset, nodename); @@ -235,11 +238,28 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) } /* Build interrupt servers properties */ - for (i = 0; i < smt_threads; i++) { - servers_prop[i] = cpu_to_be32(pnv_cc->chip_pir(chip, pc->hwid, i)); + if (pc->big_core) { + servers_prop = g_new(uint32_t, smt_threads * 2); + for (i = 0; i < smt_threads; i++) { + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i * 2] = cpu_to_be32(pir); + + pnv_cc->get_pir_tir(chip, pc->hwid + 1, i, &pir, NULL); + servers_prop[i * 2 + 1] = cpu_to_be32(pir); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(*servers_prop) * smt_threads + * 2))); + } else { + servers_prop = g_new(uint32_t, smt_threads); + for (i = 0; i < smt_threads; i++) { + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i] = cpu_to_be32(pir); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(*servers_prop) * smt_threads))); } - _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", - servers_prop, sizeof(*servers_prop) * smt_threads))); + g_free(servers_prop); return offset; } @@ -248,14 +268,17 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t hwid, uint32_t nr_threads) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - uint32_t pir = pcc->chip_pir(chip, hwid, 0); - uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); + uint32_t pir; + uint64_t addr; char *name; const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; uint32_t irange[2], i, rsize; uint64_t *reg; int offset; + pcc->get_pir_tir(chip, hwid, 0, &pir, NULL); + addr = PNV_ICP_BASE(chip) | (pir << 12); + irange[0] = cpu_to_be32(pir); irange[1] = cpu_to_be32(nr_threads); @@ -385,6 +408,10 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features_300, sizeof(pa_features_300)))); + + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { @@ -446,6 +473,10 @@ static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features_31, sizeof(pa_features_31)))); + + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { @@ -727,7 +758,8 @@ static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(chip); qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_EXTERNAL); - qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); + qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq); + return pnv_lpc_isa_create(&chip8->lpc, true, errp); } @@ -736,25 +768,48 @@ static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(chip); qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_LPC_I2C); - qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); + qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq); + return pnv_lpc_isa_create(&chip8->lpc, false, errp); } static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) { Pnv9Chip *chip9 = PNV9_CHIP(chip); - qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 3, irq); - qdev_connect_gpio_out(DEVICE(&chip9->lpc), 0, irq); return pnv_lpc_isa_create(&chip9->lpc, false, errp); } static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) { Pnv10Chip *chip10 = PNV10_CHIP(chip); - qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 3, irq); - qdev_connect_gpio_out(DEVICE(&chip10->lpc), 0, irq); return pnv_lpc_isa_create(&chip10->lpc, false, errp); } @@ -875,6 +930,7 @@ static void pnv_init(MachineState *machine) PnvMachineState *pnv = PNV_MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine); + int max_smt_threads = pmc->max_smt_threads; char *fw_filename; long fw_size; uint64_t chip_ram_start = 0; @@ -970,20 +1026,52 @@ static void pnv_init(MachineState *machine) exit(1); } + /* Set lpar-per-core mode if lpar-per-thread is not supported */ + if (!pmc->has_lpar_per_thread) { + pnv->lpar_per_core = true; + } + pnv->num_chips = machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); - if (machine->smp.threads > 8) { - error_report("Cannot support more than 8 threads/core " - "on a powernv machine"); + if (pnv->big_core) { + if (machine->smp.threads % 2 == 1) { + error_report("Cannot support %d threads with big-core option " + "because it must be an even number", + machine->smp.threads); + exit(1); + } + max_smt_threads *= 2; + } + + if (machine->smp.threads > max_smt_threads) { + error_report("Cannot support more than %d threads/core " + "on %s machine", max_smt_threads, mc->desc); + if (pmc->max_smt_threads == 4) { + error_report("(use big-core=on for 8 threads per core)"); + } exit(1); } + + if (pnv->big_core) { + /* + * powernv models PnvCore as a SMT4 core. Big-core requires 2xPnvCore + * per core, so adjust topology here. pnv_dt_core() processor + * device-tree and TCG SMT code make the 2 cores appear as one big core + * from software point of view. pnv pervasive models and xscoms tend to + * see the big core as 2 small core halves. + */ + machine->smp.cores *= 2; + machine->smp.threads /= 2; + } + if (!is_power_of_2(machine->smp.threads)) { - error_report("Cannot support %d threads/core on a powernv" + error_report("Cannot support %d threads/core on a powernv " "machine because it must be a power of 2", machine->smp.threads); exit(1); } + /* * TODO: should we decide on how many chips we can create based * on #cores and Venice vs. Murano vs. Naples chip type etc..., @@ -1017,6 +1105,10 @@ static void pnv_init(MachineState *machine) &error_fatal); object_property_set_int(chip, "nr-threads", machine->smp.threads, &error_fatal); + object_property_set_bool(chip, "big-core", pnv->big_core, + &error_fatal); + object_property_set_bool(chip, "lpar-per-core", pnv->lpar_per_core, + &error_fatal); /* * The POWER8 machine use the XICS interrupt interface. * Propagate the XICS fabric to the chip and its controllers. @@ -1079,10 +1171,16 @@ static void pnv_init(MachineState *machine) * 25:28 Core number * 29:31 Thread ID */ -static uint32_t pnv_chip_pir_p8(PnvChip *chip, uint32_t core_id, - uint32_t thread_id) +static void pnv_get_pir_tir_p8(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - return (chip->chip_id << 7) | (core_id << 3) | thread_id; + if (pir) { + *pir = (chip->chip_id << 7) | (core_id << 3) | thread_id; + } + if (tir) { + *tir = thread_id; + } } static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1134,14 +1232,26 @@ static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, * * We only care about the lower bits. uint32_t is fine for the moment. */ -static uint32_t pnv_chip_pir_p9(PnvChip *chip, uint32_t core_id, - uint32_t thread_id) +static void pnv_get_pir_tir_p9(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - if (chip->nr_threads == 8) { - return (chip->chip_id << 8) | ((thread_id & 1) << 2) | (core_id << 3) | - (thread_id >> 1); + if (chip->big_core) { + /* Big-core interleaves thread ID between small-cores */ + thread_id <<= 1; + thread_id |= core_id & 1; + core_id >>= 1; + + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 3) | thread_id; + } } else { - return (chip->chip_id << 8) | (core_id << 2) | thread_id; + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; + } + } + if (tir) { + *tir = thread_id; } } @@ -1156,14 +1266,26 @@ static uint32_t pnv_chip_pir_p9(PnvChip *chip, uint32_t core_id, * * We only care about the lower bits. uint32_t is fine for the moment. */ -static uint32_t pnv_chip_pir_p10(PnvChip *chip, uint32_t core_id, - uint32_t thread_id) +static void pnv_get_pir_tir_p10(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - if (chip->nr_threads == 8) { - return (chip->chip_id << 8) | ((core_id / 4) << 4) | - ((core_id % 2) << 3) | thread_id; + if (chip->big_core) { + /* Big-core interleaves thread ID between small-cores */ + thread_id <<= 1; + thread_id |= core_id & 1; + core_id >>= 1; + + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 3) | thread_id; + } } else { - return (chip->chip_id << 8) | (core_id << 2) | thread_id; + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; + } + } + if (tir) { + *tir = thread_id; } } @@ -1343,8 +1465,11 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) int core_hwid = CPU_CORE(pnv_core)->core_id; for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { - uint32_t pir = pcc->chip_pir(chip, core_hwid, j); - PnvICPState *icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); + uint32_t pir; + PnvICPState *icp; + + pcc->get_pir_tir(chip, core_hwid, j, &pir, NULL); + icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); memory_region_add_subregion(&chip8->icp_mmio, pir << 12, &icp->mmio); @@ -1456,7 +1581,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ k->cores_mask = POWER8E_CORE_MASK; k->num_phbs = 3; - k->chip_pir = pnv_chip_pir_p8; + k->get_pir_tir = pnv_get_pir_tir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; @@ -1480,7 +1605,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ k->cores_mask = POWER8_CORE_MASK; k->num_phbs = 3; - k->chip_pir = pnv_chip_pir_p8; + k->get_pir_tir = pnv_get_pir_tir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; @@ -1504,7 +1629,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; k->num_phbs = 4; - k->chip_pir = pnv_chip_pir_p8; + k->get_pir_tir = pnv_get_pir_tir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; @@ -1527,6 +1652,7 @@ static void pnv_chip_power9_instance_init(Object *obj) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; + object_initialize_child(obj, "adu", &chip9->adu, TYPE_PNV_ADU); object_initialize_child(obj, "xive", &chip9->xive, TYPE_PNV_XIVE); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip9->xive), "xive-fabric"); @@ -1637,6 +1763,15 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) return; } + /* ADU */ + object_property_set_link(OBJECT(&chip9->adu), "lpc", OBJECT(&chip9->lpc), + &error_abort); + if (!qdev_realize(DEVICE(&chip9->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_ADU_BASE, + &chip9->adu.xscom_regs); + pnv_chip_quad_realize(chip9, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1777,7 +1912,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; - k->chip_pir = pnv_chip_pir_p9; + k->get_pir_tir = pnv_get_pir_tir_p9; k->intc_create = pnv_chip_power9_intc_create; k->intc_reset = pnv_chip_power9_intc_reset; k->intc_destroy = pnv_chip_power9_intc_destroy; @@ -1803,6 +1938,7 @@ static void pnv_chip_power10_instance_init(Object *obj) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; + object_initialize_child(obj, "adu", &chip10->adu, TYPE_PNV_ADU); object_initialize_child(obj, "xive", &chip10->xive, TYPE_PNV_XIVE2); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip10->xive), "xive-fabric"); @@ -1826,6 +1962,11 @@ static void pnv_chip_power10_instance_init(Object *obj) for (i = 0; i < pcc->i2c_num_engines; i++) { object_initialize_child(obj, "i2c[*]", &chip10->i2c[i], TYPE_PNV_I2C); } + + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_initialize_child(obj, "pib_spic[*]", &chip10->pib_spic[i], + TYPE_PNV_SPI); + } } static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) @@ -1895,6 +2036,15 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + /* ADU */ + object_property_set_link(OBJECT(&chip10->adu), "lpc", OBJECT(&chip10->lpc), + &error_abort); + if (!qdev_realize(DEVICE(&chip10->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_ADU_BASE, + &chip10->adu.xscom_regs); + pnv_chip_power10_quad_realize(chip10, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -2040,7 +2190,21 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_SBE_I2C)); } - + /* PIB SPI Controller */ + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "spic_num", + i, &error_fatal); + /* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */ + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "transfer_len", + (i == 2) ? 1 : 4, &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT + (&chip10->pib_spic[i])), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PIB_SPIC_BASE + + i * PNV10_XSCOM_PIB_SPIC_SIZE, + &chip10->pib_spic[i].xscom_spic_regs); + } } static void pnv_rainier_i2c_init(PnvMachineState *pnv) @@ -2087,9 +2251,9 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) PnvChipClass *k = PNV_CHIP_CLASS(klass); static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; - k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */ + k->chip_cfam_id = 0x220da04980000000ull; /* P10 DD2.0 (with NX) */ k->cores_mask = POWER10_CORE_MASK; - k->chip_pir = pnv_chip_pir_p10; + k->get_pir_tir = pnv_get_pir_tir_p10; k->intc_create = pnv_chip_power10_intc_create; k->intc_reset = pnv_chip_power10_intc_reset; k->intc_destroy = pnv_chip_power10_intc_destroy; @@ -2108,7 +2272,8 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) +static void pnv_chip_core_sanitize(PnvMachineState *pnv, PnvChip *chip, + Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); int cores_max; @@ -2129,6 +2294,17 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) } chip->cores_mask &= pcc->cores_mask; + /* Ensure small-cores a paired up in big-core mode */ + if (pnv->big_core) { + uint64_t even_cores = chip->cores_mask & 0x5555555555555555ULL; + uint64_t odd_cores = chip->cores_mask & 0xaaaaaaaaaaaaaaaaULL; + + if (even_cores ^ (odd_cores >> 1)) { + error_setg(errp, "warning: unpaired cores in big-core mode !"); + return; + } + } + /* now that we have a sane layout, let check the number of cores */ cores_max = ctpop64(chip->cores_mask); if (chip->nr_cores > cores_max) { @@ -2140,11 +2316,12 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(pnv); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); int i, core_hwid; - PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); if (!object_class_by_name(typename)) { error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename); @@ -2152,7 +2329,7 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) } /* Cores */ - pnv_chip_core_sanitize(chip, &error); + pnv_chip_core_sanitize(pnv, chip, &error); if (error) { error_propagate(errp, error); return; @@ -2183,8 +2360,15 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) &error_fatal); object_property_set_int(OBJECT(pnv_core), "hrmor", pnv->fw_load_addr, &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "big-core", chip->big_core, + &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "quirk-tb-big-core", + pmc->quirk_tb_big_core, &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "lpar-per-core", + chip->lpar_per_core, &error_fatal); object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip), &error_abort); + qdev_realize(DEVICE(pnv_core), NULL, &error_fatal); /* Each core has an XSCOM MMIO region */ @@ -2216,6 +2400,8 @@ static Property pnv_chip_properties[] = { DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), + DEFINE_PROP_BOOL("big-core", PnvChip, big_core, false), + DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), DEFINE_PROP_END_OF_LIST(), }; @@ -2424,6 +2610,46 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, return total_count; } +static bool pnv_machine_get_big_core(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + return pnv->big_core; +} + +static void pnv_machine_set_big_core(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + pnv->big_core = value; +} + +static bool pnv_machine_get_lpar_per_core(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + return pnv->lpar_per_core; +} + +static void pnv_machine_set_lpar_per_core(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + pnv->lpar_per_core = value; +} + +static bool pnv_machine_get_hb(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + return !!pnv->fw_load_addr; +} + +static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + if (value) { + pnv->fw_load_addr = 0x8000000; + } +} + static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -2446,6 +2672,9 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 8; + /* POWER8 is always lpar-per-core mode */ + pmc->has_lpar_per_thread = false; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } @@ -2470,9 +2699,23 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; pmc->dt_power_mgt = pnv_dt_power_mgt; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); + + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); } static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) @@ -2494,6 +2737,9 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; + pmc->quirk_tb_big_core = true; pmc->dt_power_mgt = pnv_dt_power_mgt; xfc->match_nvt = pnv10_xive_match_nvt; @@ -2507,6 +2753,23 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) pnv_machine_p10_common_class_init(oc, data); mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; + + /* + * This is the parent of POWER10 Rainier class, so properies go here + * rather than common init (which would add them to both parent and + * child which is invalid). + */ + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); } static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) @@ -2519,22 +2782,6 @@ static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) pmc->i2c_init = pnv_rainier_i2c_init; } -static bool pnv_machine_get_hb(Object *obj, Error **errp) -{ - PnvMachineState *pnv = PNV_MACHINE(obj); - - return !!pnv->fw_load_addr; -} - -static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) -{ - PnvMachineState *pnv = PNV_MACHINE(obj); - - if (value) { - pnv->fw_load_addr = 0x8000000; - } -} - static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) { CPUPPCState *env = cpu_env(cs); @@ -2561,11 +2808,23 @@ static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) */ env->spr[SPR_SRR1] |= SRR1_WAKESCOM; } + if (arg.host_int == 1) { + cpu_resume(cs); + } +} + +/* + * Send a SRESET (NMI) interrupt to the CPU, and resume execution if it was + * paused. + */ +void pnv_cpu_do_nmi_resume(CPUState *cs) +{ + async_run_on_cpu(cs, pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(1)); } static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) { - async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); + async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(0)); } static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c new file mode 100644 index 0000000000..81b7d6e526 --- /dev/null +++ b/hw/ppc/pnv_adu.c @@ -0,0 +1,206 @@ +/* + * QEMU PowerPC PowerNV ADU unit + * + * The ADU unit actually implements XSCOM, which is the bridge between MMIO + * and PIB. However it also includes control and status registers and other + * functions that are exposed as PIB (xscom) registers. + * + * To keep things simple, pnv_xscom.c remains the XSCOM bridge + * implementation, and pnv_adu.c implements the ADU registers and other + * functions. + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_adu.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_xscom.h" +#include "trace.h" + +#define ADU_LPC_BASE_REG 0x40 +#define ADU_LPC_CMD_REG 0x41 +#define ADU_LPC_DATA_REG 0x42 +#define ADU_LPC_STATUS_REG 0x43 + +static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width) +{ + PnvADU *adu = PNV_ADU(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case 0x18: /* Receive status reg */ + case 0x12: /* log register */ + case 0x13: /* error register */ + break; + case ADU_LPC_BASE_REG: + /* + * LPC Address Map in Pervasive ADU Workbook + * + * return PNV10_LPCM_BASE(chip) & PPC_BITMASK(8, 31); + * XXX: implement as class property, or get from LPC? + */ + qemu_log_mask(LOG_UNIMP, "ADU: LPC_BASE_REG is not implemented\n"); + break; + case ADU_LPC_CMD_REG: + val = adu->lpc_cmd_reg; + break; + case ADU_LPC_DATA_REG: + val = adu->lpc_data_reg; + break; + case ADU_LPC_STATUS_REG: + val = PPC_BIT(0); /* ack / done */ + break; + + default: + qemu_log_mask(LOG_UNIMP, "ADU Unimplemented read register: Ox%08x\n", + offset); + } + + trace_pnv_adu_xscom_read(addr, val); + + return val; +} + +static bool lpc_cmd_read(PnvADU *adu) +{ + return !!(adu->lpc_cmd_reg & PPC_BIT(0)); +} + +static bool lpc_cmd_write(PnvADU *adu) +{ + return !lpc_cmd_read(adu); +} + +static uint32_t lpc_cmd_addr(PnvADU *adu) +{ + return (adu->lpc_cmd_reg & PPC_BITMASK(32, 63)) >> PPC_BIT_NR(63); +} + +static uint32_t lpc_cmd_size(PnvADU *adu) +{ + return (adu->lpc_cmd_reg & PPC_BITMASK(5, 11)) >> PPC_BIT_NR(11); +} + +static void pnv_adu_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + PnvADU *adu = PNV_ADU(opaque); + uint32_t offset = addr >> 3; + + trace_pnv_adu_xscom_write(addr, val); + + switch (offset) { + case 0x18: /* Receive status reg */ + case 0x12: /* log register */ + case 0x13: /* error register */ + break; + + case ADU_LPC_BASE_REG: + qemu_log_mask(LOG_UNIMP, + "ADU: Changing LPC_BASE_REG is not implemented\n"); + break; + + case ADU_LPC_CMD_REG: + adu->lpc_cmd_reg = val; + if (lpc_cmd_read(adu)) { + uint32_t lpc_addr = lpc_cmd_addr(adu); + uint32_t lpc_size = lpc_cmd_size(adu); + uint64_t data = 0; + + pnv_lpc_opb_read(adu->lpc, lpc_addr, (void *)&data, lpc_size); + + /* + * ADU access is performed within 8-byte aligned sectors. Smaller + * access sizes don't get formatted to the least significant byte, + * but rather appear in the data reg at the same offset as the + * address in memory. This shifts them into that position. + */ + adu->lpc_data_reg = be64_to_cpu(data) >> ((lpc_addr & 7) * 8); + } + break; + + case ADU_LPC_DATA_REG: + adu->lpc_data_reg = val; + if (lpc_cmd_write(adu)) { + uint32_t lpc_addr = lpc_cmd_addr(adu); + uint32_t lpc_size = lpc_cmd_size(adu); + uint64_t data; + + data = cpu_to_be64(val) >> ((lpc_addr & 7) * 8); /* See above */ + pnv_lpc_opb_write(adu->lpc, lpc_addr, (void *)&data, lpc_size); + } + break; + + case ADU_LPC_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "ADU: Changing LPC_STATUS_REG is not implemented\n"); + break; + + default: + qemu_log_mask(LOG_UNIMP, "ADU Unimplemented write register: Ox%08x\n", + offset); + } +} + +const MemoryRegionOps pnv_adu_xscom_ops = { + .read = pnv_adu_xscom_read, + .write = pnv_adu_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_adu_realize(DeviceState *dev, Error **errp) +{ + PnvADU *adu = PNV_ADU(dev); + + assert(adu->lpc); + + /* XScom regions for ADU registers */ + pnv_xscom_region_init(&adu->xscom_regs, OBJECT(dev), + &pnv_adu_xscom_ops, adu, "xscom-adu", + PNV9_XSCOM_ADU_SIZE); +} + +static Property pnv_adu_properties[] = { + DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_adu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_adu_realize; + dc->desc = "PowerNV ADU"; + device_class_set_props(dc, pnv_adu_properties); + dc->user_creatable = false; +} + +static const TypeInfo pnv_adu_type_info = { + .name = TYPE_PNV_ADU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvADU), + .class_init = pnv_adu_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } }, +}; + +static void pnv_adu_register_types(void) +{ + type_register_static(&pnv_adu_type_info); +} + +type_init(pnv_adu_register_types); diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index 3831a72101..1e41fe557a 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -364,8 +364,7 @@ static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" " TOD_MOVE_TOD_TO_TB_REG with no slave target\n"); } else { - PowerPCCPU *cpu = chiptod->slave_pc_target->threads[0]; - CPUPPCState *env = &cpu->env; + PnvCore *pc = chiptod->slave_pc_target; /* * Moving TOD to TB will set the TB of all threads in a @@ -377,8 +376,8 @@ static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, * thread 0. */ - if (env->pnv_tod_tbst.tb_ready_for_tod) { - env->pnv_tod_tbst.tod_sent_to_tb = 1; + if (pc->tod_state.tb_ready_for_tod) { + pc->tod_state.tod_sent_to_tb = 1; } else { qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" " TOD_MOVE_TOD_TO_TB_REG with TB not ready to" diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index f40ab721d6..a30693990b 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -58,6 +58,10 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu) env->nip = 0x10; env->msr |= MSR_HVB; /* Hypervisor mode */ env->spr[SPR_HRMOR] = pc->hrmor; + if (pc->big_core) { + /* Clear "small core" bit on Power9/10 (this is set in default PVR) */ + env->spr[SPR_PVR] &= ~PPC_BIT(51); + } hreg_compute_hflags(env); ppc_maybe_interrupt(env); @@ -181,16 +185,43 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = { */ #define PNV10_XSCOM_EC_CORE_THREAD_STATE 0x412 +#define PNV10_XSCOM_EC_CORE_THREAD_INFO 0x413 +#define PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS 0x449 +#define PNV10_XSCOM_EC_CORE_RAS_STATUS 0x454 static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, unsigned int width) { + PnvCore *pc = PNV_CORE(opaque); + int nr_threads = CPU_CORE(pc)->nr_threads; + int i; uint32_t offset = addr >> 3; uint64_t val = 0; switch (offset) { case PNV10_XSCOM_EC_CORE_THREAD_STATE: - val = 0; + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + + if (cs->halted) { + val |= PPC_BIT(56 + i); + } + } + if (pc->lpar_per_core) { + val |= PPC_BIT(62); + } + break; + case PNV10_XSCOM_EC_CORE_THREAD_INFO: + break; + case PNV10_XSCOM_EC_CORE_RAS_STATUS: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + if (cs->stopped) { + val |= PPC_BIT(0 + 8 * i) | PPC_BIT(1 + 8 * i); + } + } break; default: qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, @@ -203,9 +234,46 @@ static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { + PnvCore *pc = PNV_CORE(opaque); + int nr_threads = CPU_CORE(pc)->nr_threads; + int i; uint32_t offset = addr >> 3; switch (offset) { + case PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + + if (val & PPC_BIT(7 + 8 * i)) { /* stop */ + val &= ~PPC_BIT(7 + 8 * i); + cpu_pause(cs); + } + if (val & PPC_BIT(6 + 8 * i)) { /* start */ + val &= ~PPC_BIT(6 + 8 * i); + cpu_resume(cs); + } + if (val & PPC_BIT(4 + 8 * i)) { /* sreset */ + val &= ~PPC_BIT(4 + 8 * i); + pnv_cpu_do_nmi_resume(cs); + } + if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */ + /* + * Hardware has very particular cases for where clear maint + * must be used and where start must be used to resume a + * thread. These are not modelled exactly, just treat + * this and start the same. + */ + val &= ~PPC_BIT(3 + 8 * i); + cpu_resume(cs); + } + } + if (val) { + qemu_log_mask(LOG_UNIMP, "%s: unimp bits in DIRECT_CONTROLS " + "0x%016" PRIx64 "\n", __func__, val); + } + break; + default: qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, offset); @@ -227,8 +295,9 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, { CPUPPCState *env = &cpu->env; int core_hwid; - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - ppc_spr_t *tir = &env->spr_cb[SPR_TIR]; + ppc_spr_t *pir_spr = &env->spr_cb[SPR_PIR]; + ppc_spr_t *tir_spr = &env->spr_cb[SPR_TIR]; + uint32_t pir, tir; Error *local_err = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); @@ -244,8 +313,20 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, core_hwid = object_property_get_uint(OBJECT(pc), "hwid", &error_abort); - tir->default_value = thread_index; - pir->default_value = pcc->chip_pir(pc->chip, core_hwid, thread_index); + pcc->get_pir_tir(pc->chip, core_hwid, thread_index, &pir, &tir); + pir_spr->default_value = pir; + tir_spr->default_value = tir; + + if (pc->big_core) { + /* 2 "small cores" get the same core index for SMT operations */ + env->core_index = core_hwid >> 1; + } else { + env->core_index = core_hwid; + } + + if (pc->lpar_per_core) { + cpu_ppc_set_1lpar(cpu); + } /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); @@ -278,16 +359,22 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { PowerPCCPU *cpu; + PnvCPUState *pnv_cpu; obj = object_new(typename); cpu = POWERPC_CPU(obj); pc->threads[i] = POWERPC_CPU(obj); + if (cc->nr_threads > 1) { + cpu->env.has_smt_siblings = true; + } snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj); cpu->machine_data = g_new0(PnvCPUState, 1); + pnv_cpu = pnv_cpu_state(cpu); + pnv_cpu->pnv_core = pc; object_unref(obj); } @@ -344,6 +431,10 @@ static void pnv_core_unrealize(DeviceState *dev) static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0), DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0), + DEFINE_PROP_BOOL("big-core", PnvCore, big_core, false), + DEFINE_PROP_BOOL("quirk-tb-big-core", PnvCore, tod_state.big_core_quirk, + false), + DEFINE_PROP_BOOL("lpar-per-core", PnvCore, lpar_per_core, false), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; @@ -504,6 +595,7 @@ static const MemoryRegionOps pnv_quad_power10_xscom_ops = { static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, unsigned int width) { + PnvQuad *eq = PNV_QUAD(opaque); uint32_t offset = addr >> 3; uint64_t val = -1; @@ -511,10 +603,14 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, * Forth nibble selects the core within a quad, mask it to process read * for any core. */ - switch (offset & ~0xf000) { - case P10_QME_SPWU_HYP: + switch (offset & ~PPC_BITMASK32(16, 19)) { case P10_QME_SSH_HYP: - return 0; + val = 0; + if (eq->special_wakeup_done) { + val |= PPC_BIT(1); /* SPWU DONE */ + val |= PPC_BIT(4); /* SSH SPWU DONE */ + } + break; default: qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, offset); @@ -526,9 +622,22 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, static void pnv_qme_power10_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { + PnvQuad *eq = PNV_QUAD(opaque); uint32_t offset = addr >> 3; + bool set; + int i; - switch (offset) { + switch (offset & ~PPC_BITMASK32(16, 19)) { + case P10_QME_SPWU_HYP: + set = !!(val & PPC_BIT(0)); + eq->special_wakeup_done = set; + for (i = 0; i < 4; i++) { + /* These bits select cores in the quad */ + if (offset & PPC_BIT32(16 + i)) { + eq->special_wakeup[i] = set; + } + } + break; default: qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, offset); diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d692858bee..f8aad955b5 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -64,6 +64,7 @@ enum { #define LPC_HC_IRQSER_START_4CLK 0x00000000 #define LPC_HC_IRQSER_START_6CLK 0x01000000 #define LPC_HC_IRQSER_START_8CLK 0x02000000 +#define LPC_HC_IRQSER_AUTO_CLEAR 0x00800000 #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ #define LPC_HC_IRQSTAT 0x38 #define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ @@ -235,16 +236,16 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, * TODO: rework to use address_space_stq() and address_space_ldq() * instead. */ -static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, - int sz) +bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz) { /* XXX Handle access size limits and FW read caching here */ return !address_space_read(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, data, sz); } -static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, - int sz) +bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz) { /* XXX Handle access size limits here */ return !address_space_write(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, @@ -276,7 +277,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) } if (cmd & ECCB_CTL_READ) { - success = opb_read(lpc, opb_addr, data, sz); + success = pnv_lpc_opb_read(lpc, opb_addr, data, sz); if (success) { lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | (((uint64_t)data[0]) << 24 | @@ -293,7 +294,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) data[2] = lpc->eccb_data_reg >> 8; data[3] = lpc->eccb_data_reg; - success = opb_write(lpc, opb_addr, data, sz); + success = pnv_lpc_opb_write(lpc, opb_addr, data, sz); lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; } /* XXX Which error bit (if any) to signal OPB error ? */ @@ -420,32 +421,90 @@ static const MemoryRegionOps pnv_lpc_mmio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +/* Program the POWER9 LPC irq to PSI serirq routing table */ +static void pnv_lpc_eval_serirq_routes(PnvLpcController *lpc) +{ + int irq; + + if (!lpc->psi_has_serirq) { + if ((lpc->opb_irq_route0 & PPC_BITMASK(8, 13)) || + (lpc->opb_irq_route1 & PPC_BITMASK(4, 31))) { + qemu_log_mask(LOG_GUEST_ERROR, + "OPB: setting serirq routing on POWER8 system, ignoring.\n"); + } + return; + } + + for (irq = 0; irq <= 13; irq++) { + int serirq = (lpc->opb_irq_route1 >> (31 - 5 - (irq * 2))) & 0x3; + lpc->irq_to_serirq_route[irq] = serirq; + } + + for (irq = 14; irq < ISA_NUM_IRQS; irq++) { + int serirq = (lpc->opb_irq_route0 >> (31 - 9 - (irq * 2))) & 0x3; + lpc->irq_to_serirq_route[irq] = serirq; + } +} + static void pnv_lpc_eval_irqs(PnvLpcController *lpc) { - bool lpc_to_opb_irq = false; + uint32_t active_irqs = 0; + + if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) { + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: " + "0x%08"PRIx32"\n", lpc->lpc_hc_irqstat); + } - /* 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); + active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; } - /* 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 */ - qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0); + if (!lpc->psi_has_serirq) { + /* + * POWER8 ORs all irqs together (also with LPCHC internal interrupt + * sources) and outputs a single line that raises the PSI LPCHC irq + * which then latches an OPB IRQ status register that sends the irq + * to PSI. + * + * We don't honor the polarity register, it's pointless and unused + * anyway + */ + if (active_irqs) { + 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; + + qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); + } else { + /* + * POWER9 and POWER10 have routing fields in OPB master registers that + * send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs. + * These don't appear to get latched into an OPB register like the + * LPCHC irqs. + * + * POWER9 LPC controller internal irqs still go via the OPB + * and LPCHC PSI irqs like P8, but we have no such internal sources + * modelled yet. + */ + bool serirq_out[4] = { false, false, false, false }; + int irq; + + for (irq = 0; irq < ISA_NUM_IRQS; irq++) { + if (active_irqs & (LPC_HC_IRQ_SERIRQ0 >> irq)) { + serirq_out[lpc->irq_to_serirq_route[irq]] = true; + } + } + + qemu_set_irq(lpc->psi_irq_serirq[0], serirq_out[0]); + qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]); + qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]); + qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]); + } } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) @@ -505,7 +564,14 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQSTAT: - lpc->lpc_hc_irqstat &= ~val; + /* + * This register is write-to-clear for the IRQSER (LPC device IRQ) + * status. However if the device has not de-asserted its interrupt + * that will just raise this IRQ status bit again. Model this by + * keeping track of the inputs and only clearing if the inputs are + * deasserted. + */ + lpc->lpc_hc_irqstat &= ~(val & ~lpc->lpc_hc_irq_inputs); pnv_lpc_eval_irqs(lpc); break; case LPC_HC_ERROR_ADDRESS: @@ -536,10 +602,10 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) uint64_t val = 0xfffffffffffffffful; switch (addr) { - case OPB_MASTER_LS_ROUTE0: /* TODO */ + case OPB_MASTER_LS_ROUTE0: val = lpc->opb_irq_route0; break; - case OPB_MASTER_LS_ROUTE1: /* TODO */ + case OPB_MASTER_LS_ROUTE1: val = lpc->opb_irq_route1; break; case OPB_MASTER_LS_IRQ_STAT: @@ -568,11 +634,15 @@ static void opb_master_write(void *opaque, hwaddr addr, PnvLpcController *lpc = opaque; switch (addr) { - case OPB_MASTER_LS_ROUTE0: /* TODO */ + case OPB_MASTER_LS_ROUTE0: lpc->opb_irq_route0 = val; + pnv_lpc_eval_serirq_routes(lpc); + pnv_lpc_eval_irqs(lpc); break; - case OPB_MASTER_LS_ROUTE1: /* TODO */ + case OPB_MASTER_LS_ROUTE1: lpc->opb_irq_route1 = val; + pnv_lpc_eval_serirq_routes(lpc); + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_STAT: lpc->opb_irq_stat &= ~val; @@ -657,6 +727,8 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); Error *local_err = NULL; + object_property_set_bool(OBJECT(lpc), "psi-serirq", true, &error_abort); + plc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -666,6 +738,9 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) /* P9 uses a MMIO region */ memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops, lpc, "lpcm", PNV9_LPCM_SIZE); + + /* P9 LPC routes ISA irqs to 4 PSI SERIRQ lines */ + qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4); } static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) @@ -744,13 +819,19 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); - qdev_init_gpio_out(dev, &lpc->psi_irq, 1); + qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1); } +static Property pnv_lpc_properties[] = { + DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, pnv_lpc_properties); dc->realize = pnv_lpc_realize; dc->desc = "PowerNV LPC Controller"; dc->user_creatable = false; @@ -796,18 +877,34 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) } if (pnv->cpld_irqstate != old_state) { - qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0); + qemu_set_irq(lpc->psi_irq_lpchc, pnv->cpld_irqstate != 0); } } static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) { PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t irq_bit = LPC_HC_IRQ_SERIRQ0 >> n; - /* The Naples HW latches the 1 levels, clearing is done by SW */ if (level) { - lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; + lpc->lpc_hc_irq_inputs |= irq_bit; + + /* + * The LPC HC in Naples and later latches LPC IRQ into a bit field in + * the IRQSTAT register, and that drives the PSI IRQ to the IC. + * Software clears this bit manually (see LPC_HC_IRQSTAT handler). + */ + lpc->lpc_hc_irqstat |= irq_bit; pnv_lpc_eval_irqs(lpc); + } else { + lpc->lpc_hc_irq_inputs &= ~irq_bit; + + /* POWER9 adds an auto-clear mode that clears IRQSTAT bits on EOI */ + if (lpc->psi_has_serirq && + (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_AUTO_CLEAR)) { + lpc->lpc_hc_irqstat &= ~irq_bit; + pnv_lpc_eval_irqs(lpc); + } } } @@ -838,6 +935,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) handler = pnv_lpc_isa_irq_handler; } + /* POWER has a 17th irq, QEMU only implements the 16 regular device irqs */ irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); isa_bus_register_input_irqs(isa_bus, irqs); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index a17816d072..d192bbe2c2 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -75,11 +75,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case PRD_P9_IPOLL_REG_MASK: case PRD_P9_IPOLL_REG_STATUS: - /* P9 xscom reset */ - case 0x0090018: /* Receive status reg */ - case 0x0090012: /* log register */ - case 0x0090013: /* error register */ - /* P8 xscom reset */ case 0x2020007: /* ADU stuff, log register */ case 0x2020009: /* ADU stuff, error register */ @@ -119,10 +114,6 @@ static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val) case 0x1010c03: /* PIBAM FIR MASK */ case 0x1010c04: /* PIBAM FIR MASK */ case 0x1010c05: /* PIBAM FIR MASK */ - /* P9 xscom reset */ - case 0x0090018: /* Receive status reg */ - case 0x0090012: /* log register */ - case 0x0090013: /* error register */ /* P8 xscom reset */ case 0x2020007: /* ADU stuff, log register */ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 98fa3aa6a8..370d7c35d3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2195,6 +2195,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_fwnmi, &vmstate_spapr_fwnmi, &vmstate_spapr_cap_rpt_invalidate, + &vmstate_spapr_cap_ail_mode_3, &vmstate_spapr_cap_nested_papr, NULL } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 0a15415a1d..2f74923560 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -974,6 +974,7 @@ SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); SPAPR_CAP_MIG_STATE(rpt_invalidate, SPAPR_CAP_RPT_INVALIDATE); +SPAPR_CAP_MIG_STATE(ail_mode_3, SPAPR_CAP_AIL_MODE_3); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index e7c9edd033..56090abcd1 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -300,11 +300,13 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) g_autofree char *id = NULL; CPUState *cs; PowerPCCPU *cpu; + CPUPPCState *env; obj = object_new(scc->cpu_type); cs = CPU(obj); cpu = POWERPC_CPU(obj); + env = &cpu->env; /* * All CPUs start halted. CPU0 is unhalted from the machine level reset code * and the rest are explicitly started up by the guest using an RTAS call. @@ -315,6 +317,8 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) return NULL; } + env->core_index = cc->core_id; + cpu->node_id = sc->node_id; id = g_strdup_printf("thread[%d]", i); @@ -345,9 +349,15 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) qemu_register_reset(spapr_cpu_core_reset_handler, sc); sc->threads = g_new0(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - sc->threads[i] = spapr_create_vcpu(sc, i, errp); - if (!sc->threads[i] || - !spapr_realize_vcpu(sc->threads[i], spapr, sc, i, errp)) { + PowerPCCPU *cpu; + + cpu = spapr_create_vcpu(sc, i, errp); + sc->threads[i] = cpu; + if (cpu && cc->nr_threads > 1) { + cpu->env.has_smt_siblings = true; + } + + if (!cpu || !spapr_realize_vcpu(cpu, spapr, sc, i, errp)) { spapr_cpu_core_unrealize(dev); return; } diff --git a/hw/ppc/spapr_vhyp_mmu.c b/hw/ppc/spapr_vhyp_mmu.c index b3dd8b3a59..2d41d7f77b 100644 --- a/hw/ppc/spapr_vhyp_mmu.c +++ b/hw/ppc/spapr_vhyp_mmu.c @@ -15,19 +15,6 @@ #include "helper_regs.h" #include "hw/ppc/spapr.h" #include "mmu-hash64.h" -#include "mmu-book3s-v3.h" - - -static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) -{ - /* - * hash value/pteg group index is normalized by HPT mask - */ - if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { - return false; - } - return true; -} static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) @@ -70,7 +57,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, pteh &= ~0x60ULL; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return H_PARAMETER; } @@ -119,7 +106,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu const ppc_hash_pte64_t *hptes; target_ulong v, r; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return REMOVE_PARM; } @@ -250,7 +237,7 @@ static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, const ppc_hash_pte64_t *hptes; target_ulong v, r; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return H_PARAMETER; } @@ -287,7 +274,7 @@ static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, int i, ridx, n_entries = 1; const ppc_hash_pte64_t *hptes; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return H_PARAMETER; } diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index 09f29be0b9..c02eaacfed 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -28,7 +28,7 @@ target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr, void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt) { - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + g_autofree char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); vof_build_dt(fdt, spapr->vof); diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index bf29bbfd4b..1f125ce841 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -95,6 +95,10 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 +# pnv_adu.c +pnv_adu_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_adu_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 + # pnv_chiptod.c pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index e3b430a81f..b5b6514d79 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -646,7 +646,7 @@ static void vof_dt_memory_available(void *fdt, GArray *claimed, uint64_t base) mem0_reg = fdt_getprop(fdt, offset, "reg", &proplen); g_assert(mem0_reg && proplen == sizeof(uint32_t) * (ac + sc)); if (sc == 2) { - mem0_end = be64_to_cpu(*(uint64_t *)(mem0_reg + sizeof(uint32_t) * ac)); + mem0_end = ldq_be_p(mem0_reg + sizeof(uint32_t) * ac); } else { mem0_end = be32_to_cpu(*(uint32_t *)(mem0_reg + sizeof(uint32_t) * ac)); } diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 83ee53c1d0..8d180de7cf 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -24,3 +24,7 @@ config STM32F2XX_SPI config BCM2835_SPI bool select SSI + +config PNV_SPI + bool + select SSI diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index b999aeb027..b7ad7fca3b 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) +system_ss.add(when: 'CONFIG_PNV_SPI', if_true: files('pnv_spi.c')) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c new file mode 100644 index 0000000000..c1297ab733 --- /dev/null +++ b/hw/ssi/pnv_spi.c @@ -0,0 +1,1268 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ssi/pnv_spi.h" +#include "hw/ssi/pnv_spi_regs.h" +#include "hw/ssi/ssi.h" +#include +#include "hw/irq.h" +#include "trace.h" + +#define PNV_SPI_OPCODE_LO_NIBBLE(x) (x & 0x0F) +#define PNV_SPI_MASKED_OPCODE(x) (x & 0xF0) + +/* + * Macro from include/hw/ppc/fdt.h + * fdt.h cannot be included here as it contain ppc target specific dependency. + */ +#define _FDT(exp) \ + do { \ + int _ret = (exp); \ + if (_ret < 0) { \ + qemu_log_mask(LOG_GUEST_ERROR, \ + "error creating device tree: %s: %s", \ + #exp, fdt_strerror(_ret)); \ + exit(1); \ + } \ + } while (0) + +/* PnvXferBuffer */ +typedef struct PnvXferBuffer { + + uint32_t len; + uint8_t *data; + +} PnvXferBuffer; + +/* pnv_spi_xfer_buffer_methods */ +static PnvXferBuffer *pnv_spi_xfer_buffer_new(void) +{ + PnvXferBuffer *payload = g_malloc0(sizeof(*payload)); + + return payload; +} + +static void pnv_spi_xfer_buffer_free(PnvXferBuffer *payload) +{ + free(payload->data); + free(payload); +} + +static uint8_t *pnv_spi_xfer_buffer_write_ptr(PnvXferBuffer *payload, + uint32_t offset, uint32_t length) +{ + if (payload->len < (offset + length)) { + payload->len = offset + length; + payload->data = g_realloc(payload->data, payload->len); + } + return &payload->data[offset]; +} + +static bool does_rdr_match(PnvSpi *s) +{ + /* + * According to spec, the mask bits that are 0 are compared and the + * bits that are 1 are ignored. + */ + uint16_t rdr_match_mask = GETFIELD(SPI_MM_RDR_MATCH_MASK, + s->regs[SPI_MM_REG]); + uint16_t rdr_match_val = GETFIELD(SPI_MM_RDR_MATCH_VAL, + s->regs[SPI_MM_REG]); + + if ((~rdr_match_mask & rdr_match_val) == ((~rdr_match_mask) & + GETFIELD(PPC_BITMASK(48, 63), s->regs[SPI_RCV_DATA_REG]))) { + return true; + } + return false; +} + +static uint8_t get_from_offset(PnvSpi *s, uint8_t offset) +{ + uint8_t byte; + + /* + * Offset is an index between 0 and PNV_SPI_REG_SIZE - 1 + * Check the offset before using it. + */ + if (offset < PNV_SPI_REG_SIZE) { + byte = (s->regs[SPI_XMIT_DATA_REG] >> (56 - offset * 8)) & 0xFF; + } else { + /* + * Log an error and return a 0xFF since we have to assign something + * to byte before returning. + */ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid offset = %d used to get byte " + "from TDR\n", offset); + byte = 0xff; + } + return byte; +} + +static uint8_t read_from_frame(PnvSpi *s, uint8_t *read_buf, uint8_t nr_bytes, + uint8_t ecc_count, uint8_t shift_in_count) +{ + uint8_t byte; + int count = 0; + + while (count < nr_bytes) { + shift_in_count++; + if ((ecc_count != 0) && + (shift_in_count == (PNV_SPI_REG_SIZE + ecc_count))) { + shift_in_count = 0; + } else { + byte = read_buf[count]; + trace_pnv_spi_shift_rx(byte, count); + s->regs[SPI_RCV_DATA_REG] = (s->regs[SPI_RCV_DATA_REG] << 8) | byte; + } + count++; + } /* end of while */ + return shift_in_count; +} + +static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) +{ + uint8_t ecc_count; + uint8_t shift_in_count; + + /* + * Processing here must handle: + * - Which bytes in the payload we should move to the RDR + * - Explicit mode counter configuration settings + * - RDR full and RDR overrun status + */ + + /* + * First check that the response payload is the exact same + * number of bytes as the request payload was + */ + if (rsp_payload->len != (s->N1_bytes + s->N2_bytes)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in " + "bytes, expected %d, got %d\n", + (s->N1_bytes + s->N2_bytes), rsp_payload->len); + } else { + uint8_t ecc_control; + trace_pnv_spi_rx_received(rsp_payload->len); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * Adding an ECC count let's us know when we have found a payload byte + * that was shifted in but cannot be loaded into RDR. Bits 29-30 of + * clock_config_reset_control register equal to either 0b00 or 0b10 + * indicate that we are taking in data with ECC and either applying + * the ECC or discarding it. + */ + ecc_count = 0; + ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + ecc_count = 1; + } + /* + * Use the N1_rx and N2_rx counts to control shifting data from the + * payload into the RDR. Keep an overall count of the number of bytes + * shifted into RDR so we can discard every 9th byte when ECC is + * enabled. + */ + shift_in_count = 0; + /* Handle the N1 portion of the frame first */ + if (s->N1_rx != 0) { + trace_pnv_spi_rx_read_N1frame(); + shift_in_count = read_from_frame(s, &rsp_payload->data[0], + s->N1_bytes, ecc_count, shift_in_count); + } + /* Handle the N2 portion of the frame */ + if (s->N2_rx != 0) { + trace_pnv_spi_rx_read_N2frame(); + shift_in_count = read_from_frame(s, + &rsp_payload->data[s->N1_bytes], s->N2_bytes, + ecc_count, shift_in_count); + } + if ((s->N1_rx + s->N2_rx) > 0) { + /* + * Data was received so handle RDR status. + * It is easier to handle RDR_full and RDR_overrun status here + * since the RDR register's shift_byte_in method is called + * multiple times in a row. Controlling RDR status is done here + * instead of in the RDR scoped methods for that reason. + */ + if (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1) { + /* + * Data was shifted into the RDR before having been read + * causing previous data to have been overrun. + */ + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, 1); + } else { + /* + * Set status to indicate that the received data register is + * full. This flag is only cleared once the RDR is unloaded. + */ + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 1); + } + } + } /* end of else */ +} /* end of spi_response() */ + +static void transfer(PnvSpi *s, PnvXferBuffer *payload) +{ + uint32_t tx; + uint32_t rx; + PnvXferBuffer *rsp_payload = NULL; + + rsp_payload = pnv_spi_xfer_buffer_new(); + for (int offset = 0; offset < payload->len; offset += s->transfer_len) { + tx = 0; + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload->len) { + tx <<= 8; + } else { + tx = (tx << 8) | payload->data[offset + i]; + } + } + rx = ssi_transfer(s->ssi_bus, tx); + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload->len) { + break; + } + *(pnv_spi_xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) = + (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF; + } + } + if (rsp_payload != NULL) { + spi_response(s, s->N1_bits, rsp_payload); + } +} + +static inline uint8_t get_seq_index(PnvSpi *s) +{ + return GETFIELD(SPI_STS_SEQ_INDEX, s->status); +} + +static inline void next_sequencer_fsm(PnvSpi *s) +{ + uint8_t seq_index = get_seq_index(s); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, (seq_index + 1)); + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); +} + +/* + * Calculate the N1 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N1 opcode + * and doesn't test it. + * The counters returned are: + * N1 bits: Number of bits in the payload data that are significant + * to the responder. + * N1_bytes: Total count of payload bytes for the N1 (portion of the) frame. + * N1_tx: Total number of bytes taken from TDR for N1 + * N1_rx: Total number of bytes taken from the payload for N1 + */ +static void calculate_N1(PnvSpi *s, uint8_t opcode) +{ + /* + * Shift_N1 opcode form: 0x3M + * Implicit mode: + * If M != 0 the shift count is M bytes and M is the number of tx bytes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count control + * register fields. Note that we only check for forced Implicit mode when + * M != 0 since the mode doesn't make sense when M = 0. + * Explicit mode: + * If M == 0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) { + /* Explicit mode */ + s->N1_bits = GETFIELD(SPI_CTR_CFG_N1, s->regs[SPI_CTR_CFG_REG]); + s->N1_bytes = (s->N1_bits + 7) / 8; + s->N1_tx = 0; + s->N1_rx = 0; + /* If tx count control for N1 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_tx = s->N1_bytes; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_rx = s->N1_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + s->N1_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->N1_bits = s->N1_bytes * 8; + /* + * Assume that we are going to transmit the count + * (pure Implicit only) + */ + s->N1_tx = s->N1_bytes; + s->N1_rx = 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate transmit then reset the tx count to 0 + */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, + s->regs[SPI_CTR_CFG_REG]) == 0) { + s->N1_tx = 0; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, + s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_rx = s->N1_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the known size + * of the shift register, 64 bits or 72 bits if ECC is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM + * error bit. + */ + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, + s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + if (s->N1_bytes > (PNV_SPI_REG_SIZE + 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when " + "ECC enabled, bytes = 0x%x, bits = 0x%x\n", + s->N1_bytes, s->N1_bits); + s->N1_bytes = PNV_SPI_REG_SIZE + 1; + s->N1_bits = s->N1_bytes * 8; + } + } else if (s->N1_bytes > PNV_SPI_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, " + "bytes = 0x%x, bits = 0x%x\n", + s->N1_bytes, s->N1_bits); + s->N1_bytes = PNV_SPI_REG_SIZE; + s->N1_bits = s->N1_bytes * 8; + } +} /* end of calculate_N1 */ + +/* + * Shift_N1 operation handler method + */ +static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, + PnvXferBuffer **payload, bool send_n1_alone) +{ + uint8_t n1_count; + bool stop = false; + + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload == NULL) { + *payload = pnv_spi_xfer_buffer_new(); + } + /* + * Use a combination of N1 counters to build the N1 portion of the + * transmit payload. + * We only care about transmit at this time since the request payload + * only represents data going out on the controller output line. + * Leave mode specific considerations in the calculate function since + * all we really care about are counters that tell use exactly how + * many bytes are in the payload and how many of those bytes to + * include from the TDR into the payload. + */ + calculate_N1(s, opcode); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * Zero out the N2 counters here in case there is no N2 operation following + * the N1 operation in the sequencer. This keeps leftover N2 information + * from interfering with spi_response logic. + */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + /* + * N1_bytes is the overall size of the N1 portion of the frame regardless of + * whether N1 is used for tx, rx or both. Loop over the size to build a + * payload that is N1_bytes long. + * N1_tx is the count of bytes to take from the TDR and "shift" into the + * frame which means append those bytes to the payload for the N1 portion + * of the frame. + * If N1_tx is 0 or if the count exceeds the size of the TDR append 0xFF to + * the frame until the overall N1 count is reached. + */ + n1_count = 0; + while (n1_count < s->N1_bytes) { + /* + * Assuming that if N1_tx is not equal to 0 then it is the same as + * N1_bytes. + */ + if ((s->N1_tx != 0) && (n1_count < PNV_SPI_REG_SIZE)) { + + if (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1) { + /* + * Note that we are only appending to the payload IF the TDR + * is full otherwise we don't touch the payload because we are + * going to NOT send the payload and instead tell the sequencer + * that called us to stop and wait for a TDR write so we have + * data to load into the payload. + */ + uint8_t n1_byte = 0x00; + n1_byte = get_from_offset(s, n1_count); + trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = + n1_byte; + } else { + /* + * We hit a shift_n1 opcode TX but the TDR is empty, tell the + * sequencer to stop and break this loop. + */ + trace_pnv_spi_sequencer_stop_requested("Shift N1" + "set for transmit but TDR is empty"); + stop = true; + break; + } + } else { + /* + * Cases here: + * - we are receiving during the N1 frame segment and the RDR + * is full so we need to stop until the RDR is read + * - we are transmitting and we don't care about RDR status + * since we won't be loading RDR during the frame segment. + * - we are receiving and the RDR is empty so we allow the operation + * to proceed. + */ + if ((s->N1_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL, + s->status) == 1)) { + trace_pnv_spi_sequencer_stop_requested("shift N1" + "set for receive but RDR is full"); + stop = true; + break; + } else { + trace_pnv_spi_tx_append_FF("n1_byte"); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = 0xff; + } + } + n1_count++; + } /* end of while */ + /* + * If we are not stopping due to an empty TDR and we are doing an N1 TX + * and the TDR is full we need to clear the TDR_full status. + * Do this here instead of up in the loop above so we don't log the message + * in every loop iteration. + * Ignore the send_n1_alone flag, all that does is defer the TX until the N2 + * operation, which was found immediately after the current opcode. The TDR + * was unloaded and will be shifted so we have to clear the TDR_full status. + */ + if (!stop && (s->N1_tx != 0) && + (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); + } + /* + * There are other reasons why the shifter would stop, such as a TDR empty + * or RDR full condition with N1 set to receive. If we haven't stopped due + * to either one of those conditions then check if the send_n1_alone flag is + * equal to False, indicating the next opcode is an N2 operation, AND if + * the N2 counter reload switch (bit 0 of the N2 count control field) is + * set. This condition requires a pacing write to "kick" off the N2 + * shift which includes the N1 shift as well when send_n1_alone is False. + */ + if (!stop && !send_n1_alone && + (GETFIELD(SPI_CTR_CFG_N2_CTRL_B0, s->regs[SPI_CTR_CFG_REG]) == 1)) { + trace_pnv_spi_sequencer_stop_requested("N2 counter reload " + "active, stop N1 shift, TDR_underrun set to 1"); + stop = true; + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 1); + } + /* + * If send_n1_alone is set AND we have a full TDR then this is the first and + * last payload to send and we don't have an N2 frame segment to add to the + * payload. + */ + if (send_n1_alone && !stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + trace_pnv_spi_tx_request("Shifting N1 frame", (*payload)->len); + transfer(s, *payload); + /* The N1 frame shift is complete so reset the N1 counters */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + pnv_spi_xfer_buffer_free(*payload); + *payload = NULL; + } + return stop; +} /* end of operation_shiftn1() */ + +/* + * Calculate the N2 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N2 opcode + * and doesn't test it. + * The counters returned are: + * N2 bits: Number of bits in the payload data that are significant + * to the responder. + * N2_bytes: Total count of payload bytes for the N2 frame. + * N2_tx: Total number of bytes taken from TDR for N2 + * N2_rx: Total number of bytes taken from the payload for N2 + */ +static void calculate_N2(PnvSpi *s, uint8_t opcode) +{ + /* + * Shift_N2 opcode form: 0x4M + * Implicit mode: + * If M!=0 the shift count is M bytes and M is the number of rx bytes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count control + * register fields. Note that we only check for Forced Implicit mode when + * M != 0 since the mode doesn't make sense when M = 0. + * Explicit mode: + * If M==0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) { + /* Explicit mode */ + s->N2_bits = GETFIELD(SPI_CTR_CFG_N2, s->regs[SPI_CTR_CFG_REG]); + s->N2_bytes = (s->N2_bits + 7) / 8; + s->N2_tx = 0; + s->N2_rx = 0; + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_tx = s->N2_bytes; + } + /* If rx count control for N2 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_rx = s->N2_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + s->N2_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->N2_bits = s->N2_bytes * 8; + /* Assume that we are going to receive the count */ + s->N2_rx = s->N2_bytes; + s->N2_tx = 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate a receive then reset the rx count to 0 + */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, + s->regs[SPI_CTR_CFG_REG]) == 0) { + s->N2_rx = 0; + } + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, + s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_tx = s->N2_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the + * known size of the shift register, 64 bits or 72 bits if ECC + * is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM + * error bit. + */ + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, + s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + if (s->N2_bytes > (PNV_SPI_REG_SIZE + 1)) { + /* Unsupported N2 shift size when ECC enabled */ + s->N2_bytes = PNV_SPI_REG_SIZE + 1; + s->N2_bits = s->N2_bytes * 8; + } + } else if (s->N2_bytes > PNV_SPI_REG_SIZE) { + /* Unsupported N2 shift size */ + s->N2_bytes = PNV_SPI_REG_SIZE; + s->N2_bits = s->N2_bytes * 8; + } +} /* end of calculate_N2 */ + +/* + * Shift_N2 operation handler method + */ + +static bool operation_shiftn2(PnvSpi *s, uint8_t opcode, + PnvXferBuffer **payload) +{ + uint8_t n2_count; + bool stop = false; + + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload == NULL) { + *payload = pnv_spi_xfer_buffer_new(); + } + /* + * Use a combination of N2 counters to build the N2 portion of the + * transmit payload. + */ + calculate_N2(s, opcode); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * The only difference between this code and the code for shift N1 is + * that this code has to account for the possible presence of N1 transmit + * bytes already taken from the TDR. + * If there are bytes to be transmitted for the N2 portion of the frame + * and there are still bytes in TDR that have not been copied into the + * TX data of the payload, this code will handle transmitting those + * remaining bytes. + * If for some reason the transmit count(s) add up to more than the size + * of the TDR we will just append 0xFF to the transmit payload data until + * the payload is N1 + N2 bytes long. + */ + n2_count = 0; + while (n2_count < s->N2_bytes) { + /* + * If the RDR is full and we need to RX just bail out, letting the + * code continue will end up building the payload twice in the same + * buffer since RDR full causes a sequence stop and restart. + */ + if ((s->N2_rx != 0) && + (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) { + trace_pnv_spi_sequencer_stop_requested("shift N2 set" + "for receive but RDR is full"); + stop = true; + break; + } + if ((s->N2_tx != 0) && ((s->N1_tx + n2_count) < + PNV_SPI_REG_SIZE)) { + /* Always append data for the N2 segment if it is set for TX */ + uint8_t n2_byte = 0x00; + n2_byte = get_from_offset(s, (s->N1_tx + n2_count)); + trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count)); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = n2_byte; + } else { + /* + * Regardless of whether or not N2 is set for TX or RX, we need + * the number of bytes in the payload to match the overall length + * of the operation. + */ + trace_pnv_spi_tx_append_FF("n2_byte"); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = 0xff; + } + n2_count++; + } /* end of while */ + if (!stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + trace_pnv_spi_tx_request("Shifting N2 frame", (*payload)->len); + transfer(s, *payload); + /* + * If we are doing an N2 TX and the TDR is full we need to clear the + * TDR_full status. Do this here instead of up in the loop above so we + * don't log the message in every loop iteration. + */ + if ((s->N2_tx != 0) && + (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); + } + /* + * The N2 frame shift is complete so reset the N2 counters. + * Reset the N1 counters also in case the frame was a combination of + * N1 and N2 segments. + */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + s->N1_bits = 0; + s->N1_bytes = 0; + s->N1_tx = 0; + s->N1_rx = 0; + pnv_spi_xfer_buffer_free(*payload); + *payload = NULL; + } + return stop; +} /* end of operation_shiftn2()*/ + +static void operation_sequencer(PnvSpi *s) +{ + /* + * Loop through each sequencer operation ID and perform the requested + * operations. + * Flag for indicating if we should send the N1 frame or wait to combine + * it with a preceding N2 frame. + */ + bool send_n1_alone = true; + bool stop = false; /* Flag to stop the sequencer */ + uint8_t opcode = 0; + uint8_t masked_opcode = 0; + + /* + * PnvXferBuffer for containing the payload of the SPI frame. + * This is a static because there are cases where a sequence has to stop + * and wait for the target application to unload the RDR. If this occurs + * during a sequence where N1 is not sent alone and instead combined with + * N2 since the N1 tx length + the N2 tx length is less than the size of + * the TDR. + */ + static PnvXferBuffer *payload; + + if (payload == NULL) { + payload = pnv_spi_xfer_buffer_new(); + } + /* + * Clear the sequencer FSM error bit - general_SPI_status[3] + * before starting a sequence. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 0); + /* + * If the FSM is idle set the sequencer index to 0 + * (new/restarted sequence) + */ + if (GETFIELD(SPI_STS_SEQ_FSM, s->status) == SEQ_STATE_IDLE) { + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + } + /* + * There are only 8 possible operation IDs to iterate through though + * some operations may cause more than one frame to be sequenced. + */ + while (get_seq_index(s) < NUM_SEQ_OPS) { + opcode = s->seq_op[get_seq_index(s)]; + /* Set sequencer state to decode */ + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_DECODE); + /* + * Only the upper nibble of the operation ID is needed to know what + * kind of operation is requested. + */ + masked_opcode = PNV_SPI_MASKED_OPCODE(opcode); + switch (masked_opcode) { + /* + * Increment the operation index in each case instead of just + * once at the end in case an operation like the branch + * operation needs to change the index. + */ + case SEQ_OP_STOP: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* A stop operation in any position stops the sequencer */ + trace_pnv_spi_sequencer_op("STOP", get_seq_index(s)); + + stop = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); + break; + + case SEQ_OP_SELECT_SLAVE: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SELECT_SLAVE", get_seq_index(s)); + /* + * This device currently only supports a single responder + * connection at position 0. De-selecting a responder is fine + * and expected at the end of a sequence but selecting any + * responder other than 0 should cause an error. + */ + s->responder_select = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + if (s->responder_select == 0) { + trace_pnv_spi_shifter_done(); + qemu_set_irq(s->cs_line[0], 1); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + (get_seq_index(s) + 1)); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_DONE); + } else if (s->responder_select != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 " + "not supported, select = 0x%x\n", + s->responder_select); + trace_pnv_spi_sequencer_stop_requested("invalid " + "responder select"); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + stop = true; + } else { + /* + * Only allow an FSM_START state when a responder is + * selected + */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_START); + trace_pnv_spi_shifter_stating(); + qemu_set_irq(s->cs_line[0], 0); + /* + * A Shift_N2 operation is only valid after a Shift_N1 + * according to the spec. The spec doesn't say if that means + * immediately after or just after at any point. We will track + * the occurrence of a Shift_N1 to enforce this requirement in + * the most generic way possible by assuming that the rule + * applies once a valid responder select has occurred. + */ + s->shift_n1_done = false; + next_sequencer_fsm(s); + } + break; + + case SEQ_OP_SHIFT_N1: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SHIFT_N1", get_seq_index(s)); + /* + * Only allow a shift_n1 when the state is not IDLE or DONE. + * In either of those two cases the sequencer is not in a proper + * state to perform shift operations because the sequencer has: + * - processed a responder deselect (DONE) + * - processed a stop opcode (IDLE) + * - encountered an error (IDLE) + */ + if ((GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_IDLE) || + (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_DONE)) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N1 not allowed in " + "shifter state = 0x%llx", GETFIELD( + SPI_STS_SHIFTER_FSM, s->status)); + /* + * Set sequencer FSM error bit 3 (general_SPI_status[3]) + * in status reg. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); + trace_pnv_spi_sequencer_stop_requested("invalid shifter state"); + stop = true; + } else { + /* + * Look for the special case where there is a shift_n1 set for + * transmit and it is followed by a shift_n2 set for transmit + * AND the combined transmit length of the two operations is + * less than or equal to the size of the TDR register. In this + * case we want to use both this current shift_n1 opcode and the + * following shift_n2 opcode to assemble the frame for + * transmission to the responder without requiring a refill of + * the TDR between the two operations. + */ + if (PNV_SPI_MASKED_OPCODE(s->seq_op[get_seq_index(s) + 1]) + == SEQ_OP_SHIFT_N2) { + send_n1_alone = false; + } + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N1); + stop = operation_shiftn1(s, opcode, &payload, send_n1_alone); + if (stop) { + /* + * The operation code says to stop, this can occur if: + * (1) RDR is full and the N1 shift is set for receive + * (2) TDR was empty at the time of the N1 shift so we need + * to wait for data. + * (3) Neither 1 nor 2 are occurring and we aren't sending + * N1 alone and N2 counter reload is set (bit 0 of the N2 + * counter reload field). In this case TDR_underrun will + * will be set and the Payload has been loaded so it is + * ok to advance the sequencer. + */ + if (GETFIELD(SPI_STS_TDR_UNDERRUN, s->status)) { + s->shift_n1_done = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N2); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + (get_seq_index(s) + 1)); + } else { + /* + * This is case (1) or (2) so the sequencer needs to + * wait and NOT go to the next sequence yet. + */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_WAIT); + } + } else { + /* Ok to move on to the next index */ + s->shift_n1_done = true; + next_sequencer_fsm(s); + } + } + break; + + case SEQ_OP_SHIFT_N2: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SHIFT_N2", get_seq_index(s)); + if (!s->shift_n1_done) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N2 is not allowed if a " + "Shift_N1 is not done, shifter state = 0x%llx", + GETFIELD(SPI_STS_SHIFTER_FSM, s->status)); + /* + * In case the sequencer actually stops if an N2 shift is + * requested before any N1 shift is done. Set sequencer FSM + * error bit 3 (general_SPI_status[3]) in status reg. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); + trace_pnv_spi_sequencer_stop_requested("shift_n2 " + "w/no shift_n1 done"); + stop = true; + } else { + /* Ok to do a Shift_N2 */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N2); + stop = operation_shiftn2(s, opcode, &payload); + /* + * If the operation code says to stop set the shifter state to + * wait and stop + */ + if (stop) { + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_WAIT); + } else { + /* Ok to move on to the next index */ + next_sequencer_fsm(s); + } + } + break; + + case SEQ_OP_BRANCH_IFNEQ_RDR: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_RDR", get_seq_index(s)); + /* + * The memory mapping register RDR match value is compared against + * the 16 rightmost bytes of the RDR (potentially with masking). + * Since this comparison is performed against the contents of the + * RDR then a receive must have previously occurred otherwise + * there is no data to compare and the operation cannot be + * completed and will stop the sequencer until RDR full is set to + * 1. + */ + if (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1) { + bool rdr_matched = false; + rdr_matched = does_rdr_match(s); + if (rdr_matched) { + trace_pnv_spi_RDR_match("success"); + /* A match occurred, increment the sequencer index. */ + next_sequencer_fsm(s); + } else { + trace_pnv_spi_RDR_match("failed"); + /* + * Branch the sequencer to the index coded into the op + * code. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + } + /* + * Regardless of where the branch ended up we want the + * sequencer to continue shifting so we have to clear + * RDR_full. + */ + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + } else { + trace_pnv_spi_sequencer_stop_requested("RDR not" + "full for 0x6x opcode"); + stop = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT); + } + break; + + case SEQ_OP_TRANSFER_TDR: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\n"); + next_sequencer_fsm(s); + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_1: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_1", get_seq_index(s)); + /* + * The spec says the loop should execute count compare + 1 times. + * However we learned from engineering that we really only loop + * count_compare times, count compare = 0 makes this op code a + * no-op + */ + if (s->loop_counter_1 != + GETFIELD(SPI_CTR_CFG_CMP1, s->regs[SPI_CTR_CFG_REG])) { + /* + * Next index is the lower nibble of the branch operation ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + s->loop_counter_1++; + } else { + /* Continue to next index if loop counter is reached */ + next_sequencer_fsm(s); + } + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_2: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", get_seq_index(s)); + uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, + s->regs[SPI_CTR_CFG_REG]); + /* + * The spec says the loop should execute count compare + 1 times. + * However we learned from engineering that we really only loop + * count_compare times, count compare = 0 makes this op code a + * no-op + */ + if (s->loop_counter_2 != condition2) { + /* + * Next index is the lower nibble of the branch operation ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, + s->status, PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + s->loop_counter_2++; + } else { + /* Continue to next index if loop counter is reached */ + next_sequencer_fsm(s); + } + break; + + default: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* Ignore unsupported operations. */ + next_sequencer_fsm(s); + break; + } /* end of switch */ + /* + * If we used all 8 opcodes without seeing a 00 - STOP in the sequence + * we need to go ahead and end things as if there was a STOP at the + * end. + */ + if (get_seq_index(s) == NUM_SEQ_OPS) { + /* All 8 opcodes completed, sequencer idling */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); + break; + } + /* Break the loop if a stop was requested */ + if (stop) { + break; + } + } /* end of while */ + return; +} /* end of operation_sequencer() */ + +/* + * The SPIC engine and its internal sequencer can be interrupted and reset by + * a hardware signal, the sbe_spicst_hard_reset bits from Pervasive + * Miscellaneous Register of sbe_register_bo device. + * Reset immediately aborts any SPI transaction in progress and returns the + * sequencer and state machines to idle state. + * The configuration register values are not changed. The status register is + * not reset. The engine registers are not reset. + * The SPIC engine reset does not have any affect on the attached devices. + * Reset handling of any attached devices is beyond the scope of the engine. + */ +static void do_reset(DeviceState *dev) +{ + PnvSpi *s = PNV_SPI(dev); + DeviceState *ssi_dev; + + trace_pnv_spi_reset(); + + /* Connect cs irq */ + ssi_dev = ssi_get_cs(s->ssi_bus, 0); + if (ssi_dev) { + qemu_irq cs_line = qdev_get_gpio_in_named(ssi_dev, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(s), "cs", 0, cs_line); + } + + /* Reset all N1 and N2 counters, and other constants */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + s->N1_bits = 0; + s->N1_bytes = 0; + s->N1_tx = 0; + s->N1_rx = 0; + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + /* Disconnected from responder */ + qemu_set_irq(s->cs_line[0], 1); +} + +static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvSpi *s = PNV_SPI(opaque); + uint32_t reg = addr >> 3; + uint64_t val = ~0ull; + + switch (reg) { + case ERROR_REG: + case SPI_CTR_CFG_REG: + case CONFIG_REG1: + case SPI_CLK_CFG_REG: + case SPI_MM_REG: + case SPI_XMIT_DATA_REG: + val = s->regs[reg]; + break; + case SPI_RCV_DATA_REG: + val = s->regs[reg]; + trace_pnv_spi_read_RDR(val); + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + if (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_WAIT) { + trace_pnv_spi_start_sequencer(); + operation_sequencer(s); + } + break; + case SPI_SEQ_OP_REG: + val = 0; + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { + val = (val << 8) | s->seq_op[i]; + } + break; + case SPI_STS_REG: + val = s->status; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " + "read at 0x%" PRIx32 "\n", reg); + } + + trace_pnv_spi_read(addr, val); + return val; +} + +static void pnv_spi_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvSpi *s = PNV_SPI(opaque); + uint32_t reg = addr >> 3; + + trace_pnv_spi_write(addr, val); + + switch (reg) { + case ERROR_REG: + case SPI_CTR_CFG_REG: + case CONFIG_REG1: + case SPI_MM_REG: + case SPI_RCV_DATA_REG: + s->regs[reg] = val; + break; + case SPI_CLK_CFG_REG: + /* + * To reset the SPI controller write the sequence 0x5 0xA to + * reset_control field + */ + if ((GETFIELD(SPI_CLK_CFG_RST_CTRL, s->regs[SPI_CLK_CFG_REG]) == 0x5) + && (GETFIELD(SPI_CLK_CFG_RST_CTRL, val) == 0xA)) { + /* SPI controller reset sequence completed, resetting */ + s->regs[reg] = SPI_CLK_CFG_HARD_RST; + } else { + s->regs[reg] = val; + } + break; + case SPI_XMIT_DATA_REG: + /* + * Writing to the transmit data register causes the transmit data + * register full status bit in the status register to be set. Writing + * when the transmit data register full status bit is already set + * causes a "Resource Not Available" condition. This is not possible + * in the model since writes to this register are not asynchronous to + * the operation sequence like it would be in hardware. + */ + s->regs[reg] = val; + trace_pnv_spi_write_TDR(val); + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1); + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0); + trace_pnv_spi_start_sequencer(); + operation_sequencer(s); + break; + case SPI_SEQ_OP_REG: + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { + s->seq_op[i] = (val >> (56 - i * 8)) & 0xFF; + } + break; + case SPI_STS_REG: + /* other fields are ignore_write */ + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, + GETFIELD(SPI_STS_RDR, val)); + s->status = SETFIELD(SPI_STS_TDR_OVERRUN, s->status, + GETFIELD(SPI_STS_TDR, val)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " + "write at 0x%" PRIx32 "\n", reg); + } + return; +} + +static const MemoryRegionOps pnv_spi_xscom_ops = { + .read = pnv_spi_xscom_read, + .write = pnv_spi_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 Property pnv_spi_properties[] = { + DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), + DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_spi_realize(DeviceState *dev, Error **errp) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name = g_strdup_printf(TYPE_PNV_SPI_BUS ".%d", + s->spic_num); + s->ssi_bus = ssi_create_bus(dev, name); + s->cs_line = g_new0(qemu_irq, 1); + qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1); + + /* spi scoms */ + pnv_xscom_region_init(&s->xscom_spic_regs, OBJECT(s), &pnv_spi_xscom_ops, + s, "xscom-spi", PNV10_XSCOM_PIB_SPIC_SIZE); +} + +static int pnv_spi_dt_xscom(PnvXScomInterface *dev, void *fdt, + int offset) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name; + int s_offset; + const char compat[] = "ibm,power10-spi"; + uint32_t spic_pcba = PNV10_XSCOM_PIB_SPIC_BASE + + s->spic_num * PNV10_XSCOM_PIB_SPIC_SIZE; + uint32_t reg[] = { + cpu_to_be32(spic_pcba), + cpu_to_be32(PNV10_XSCOM_PIB_SPIC_SIZE) + }; + name = g_strdup_printf("pnv_spi@%x", spic_pcba); + s_offset = fdt_add_subnode(fdt, offset, name); + _FDT(s_offset); + + _FDT(fdt_setprop(fdt, s_offset, "reg", reg, sizeof(reg))); + _FDT(fdt_setprop(fdt, s_offset, "compatible", compat, sizeof(compat))); + _FDT((fdt_setprop_cell(fdt, s_offset, "spic_num#", s->spic_num))); + return 0; +} + +static void pnv_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xscomc->dt_xscom = pnv_spi_dt_xscom; + + dc->desc = "PowerNV SPI"; + dc->realize = pnv_spi_realize; + dc->reset = do_reset; + device_class_set_props(dc, pnv_spi_properties); +} + +static const TypeInfo pnv_spi_info = { + .name = TYPE_PNV_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvSpi), + .class_init = pnv_spi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_spi_register_types(void) +{ + type_register_static(&pnv_spi_info); +} + +type_init(pnv_spi_register_types); diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index 7b5ad6a939..089d269994 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -32,3 +32,24 @@ ibex_spi_host_reset(const char *msg) "%s" ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32 ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:" + +#pnv_spi.c +pnv_spi_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_spi_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_spi_read_RDR(uint64_t val) "data extracted = 0x%" PRIx64 +pnv_spi_write_TDR(uint64_t val) "being written, data written = 0x%" PRIx64 +pnv_spi_start_sequencer(void) "" +pnv_spi_reset(void) "spic engine sequencer configuration and spi communication" +pnv_spi_sequencer_op(const char* op, uint8_t index) "%s at index = 0x%x" +pnv_spi_shifter_stating(void) "pull CS line low" +pnv_spi_shifter_done(void) "pull the CS line high" +pnv_spi_log_Ncounts(uint8_t N1_bits, uint8_t N1_bytes, uint8_t N1_tx, uint8_t N1_rx, uint8_t N2_bits, uint8_t N2_bytes, uint8_t N2_tx, uint8_t N2_rx) "N1_bits = %d, N1_bytes = %d, N1_tx = %d, N1_rx = %d, N2_bits = %d, N2_bytes = %d, N2_tx = %d, N2_rx = %d" +pnv_spi_tx_append(const char* frame, uint8_t byte, uint8_t tdr_index) "%s = 0x%2.2x to payload from TDR at index %d" +pnv_spi_tx_append_FF(const char* frame) "%s to Payload" +pnv_spi_tx_request(const char* frame, uint32_t payload_len) "%s, payload len = %d" +pnv_spi_rx_received(uint32_t payload_len) "payload len = %d" +pnv_spi_rx_read_N1frame(void) "" +pnv_spi_rx_read_N2frame(void) "" +pnv_spi_shift_rx(uint8_t byte, uint32_t index) "byte = 0x%2.2x into RDR from payload index %d" +pnv_spi_sequencer_stop_requested(const char* reason) "due to %s" +pnv_spi_RDR_match(const char* result) "%s" diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 240ee04369..2e1b499cb7 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -35,6 +35,8 @@ void cpu_list_lock(void); void cpu_list_unlock(void); unsigned int cpu_list_generation_id_get(void); +int cpu_get_free_index(void); + void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 476b136146..fcb6699150 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -76,6 +76,9 @@ struct PnvMachineClass { /*< public >*/ const char *compat; int compat_size; + int max_smt_threads; + bool has_lpar_per_thread; + bool quirk_tb_big_core; void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); void (*i2c_init)(PnvMachineState *pnv); @@ -100,6 +103,9 @@ struct PnvMachineState { PnvPnor *pnor; hwaddr fw_load_addr; + + bool big_core; + bool lpar_per_core; }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); @@ -108,6 +114,8 @@ PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb); #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL +void pnv_cpu_do_nmi_resume(CPUState *cs); + /* * BMC helpers */ diff --git a/include/hw/ppc/pnv_adu.h b/include/hw/ppc/pnv_adu.h new file mode 100644 index 0000000000..f9dbd8c8b3 --- /dev/null +++ b/include/hw/ppc/pnv_adu.h @@ -0,0 +1,32 @@ +/* + * QEMU PowerPC PowerNV Emulation of some ADU behaviour + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_ADU_H +#define PPC_PNV_ADU_H + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_lpc.h" +#include "hw/qdev-core.h" + +#define TYPE_PNV_ADU "pnv-adu" + +OBJECT_DECLARE_TYPE(PnvADU, PnvADUClass, PNV_ADU) + +struct PnvADU { + DeviceState xd; + + /* LPCMC (LPC Master Controller) access engine */ + PnvLpcController *lpc; + uint64_t lpc_base_reg; + uint64_t lpc_cmd_reg; + uint64_t lpc_data_reg; + + MemoryRegion xscom_regs; +}; + +#endif /* PPC_PNV_ADU_H */ diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index a4ed17ac59..24ce37a9c8 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -2,10 +2,12 @@ #define PPC_PNV_CHIP_H #include "hw/pci-host/pnv_phb4.h" +#include "hw/ppc/pnv_adu.h" #include "hw/ppc/pnv_chiptod.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_homer.h" #include "hw/ppc/pnv_n1_chiplet.h" +#include "hw/ssi/pnv_spi.h" #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_occ.h" #include "hw/ppc/pnv_psi.h" @@ -26,6 +28,8 @@ struct PnvChip { uint64_t ram_start; uint64_t ram_size; + bool big_core; + bool lpar_per_core; uint32_t nr_cores; uint32_t nr_threads; uint64_t cores_mask; @@ -77,6 +81,7 @@ struct Pnv9Chip { PnvChip parent_obj; /*< public >*/ + PnvADU adu; PnvXive xive; Pnv9Psi psi; PnvLpcController lpc; @@ -110,6 +115,7 @@ struct Pnv10Chip { PnvChip parent_obj; /*< public >*/ + PnvADU adu; PnvXive2 xive; Pnv9Psi psi; PnvLpcController lpc; @@ -118,6 +124,8 @@ struct Pnv10Chip { PnvSBE sbe; PnvHomer homer; PnvN1Chiplet n1_chiplet; +#define PNV10_CHIP_MAX_PIB_SPIC 6 + PnvSpi pib_spic[PNV10_CHIP_MAX_PIB_SPIC]; uint32_t nr_quads; PnvQuad *quads; @@ -131,6 +139,7 @@ struct Pnv10Chip { #define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) #define PNV10_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) +#define PNV10_PIR2THREAD(pir) (((pir) & 0x7f)) struct PnvChipClass { /*< private >*/ @@ -147,7 +156,9 @@ struct PnvChipClass { DeviceRealize parent_realize; - uint32_t (*chip_pir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id); + /* Get PIR and TIR values for a CPU thread identified by core/thread id */ + void (*get_pir_tir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir); void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp); void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu); void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu); diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index c6d62fd145..d8afb4f95f 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -25,6 +25,27 @@ #include "hw/ppc/pnv.h" #include "qom/object.h" +/* Per-core ChipTOD / TimeBase state */ +typedef struct PnvCoreTODState { + /* + * POWER10 DD2.0 - big core TFMR drives the state machine on the even + * small core. Skiboot has a workaround that targets the even small core + * for CHIPTOD_TO_TB ops. + */ + bool big_core_quirk; + + int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ + int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ + + /* + * "Timers" for async TBST events are simulated by mfTFAC because TFAC + * is polled for such events. These are just used to ensure firmware + * performs the polling at least a few times. + */ + int tb_state_timer; + int tb_sync_pulse_timer; +} PnvCoreTODState; + #define TYPE_PNV_CORE "powernv-cpu-core" OBJECT_DECLARE_TYPE(PnvCore, PnvCoreClass, PNV_CORE) @@ -35,9 +56,15 @@ struct PnvCore { /*< public >*/ PowerPCCPU **threads; + bool big_core; + bool lpar_per_core; uint32_t pir; uint32_t hwid; uint64_t hrmor; + + target_ulong scratch[8]; /* SPRC/SPRD indirect SCRATCH registers */ + PnvCoreTODState tod_state; + PnvChip *chip; MemoryRegion xscom_regs; @@ -54,6 +81,7 @@ struct PnvCoreClass { #define PNV_CORE_TYPE_NAME(cpu_model) cpu_model PNV_CORE_TYPE_SUFFIX typedef struct PnvCPUState { + PnvCore *pnv_core; Object *intc; } PnvCPUState; @@ -82,6 +110,9 @@ OBJECT_DECLARE_TYPE(PnvQuad, PnvQuadClass, PNV_QUAD) struct PnvQuad { DeviceState parent_obj; + bool special_wakeup_done; + bool special_wakeup[4]; + uint32_t quad_id; MemoryRegion xscom_regs; MemoryRegion xscom_qme_regs; diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 5d22c45570..174add4c53 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -23,6 +23,7 @@ #include "exec/memory.h" #include "hw/ppc/pnv.h" #include "hw/qdev-core.h" +#include "hw/isa/isa.h" /* For ISA_NUM_IRQS */ #define TYPE_PNV_LPC "pnv-lpc" typedef struct PnvLpcClass PnvLpcClass; @@ -73,6 +74,9 @@ struct PnvLpcController { uint32_t opb_irq_pol; uint32_t opb_irq_input; + /* LPC device IRQ state */ + uint32_t lpc_hc_irq_inputs; + /* LPC HC registers */ uint32_t lpc_hc_fw_seg_idsel; uint32_t lpc_hc_fw_rd_acc_size; @@ -84,8 +88,19 @@ struct PnvLpcController { /* XSCOM registers */ MemoryRegion xscom_regs; + /* + * In P8, ISA irqs are combined with internal sources to drive the + * LPCHC interrupt output. P9 ISA irqs raise one of 4 lines that + * drive PSI SERIRQ irqs, routing according to OPB routing registers. + */ + bool psi_has_serirq; + /* PSI to generate interrupts */ - qemu_irq psi_irq; + qemu_irq psi_irq_lpchc; + + /* P9 serirq lines and irq routing table */ + qemu_irq psi_irq_serirq[4]; + int irq_to_serirq_route[ISA_NUM_IRQS]; }; struct PnvLpcClass { @@ -94,6 +109,11 @@ struct PnvLpcClass { DeviceRealize parent_realize; }; +bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz); +bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz); + ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp); int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, uint64_t lpcm_size); diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 6209e18492..648388a599 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -21,9 +21,9 @@ #define PPC_PNV_XSCOM_H #include "exec/memory.h" -#include "hw/ppc/pnv.h" typedef struct PnvXScomInterface PnvXScomInterface; +typedef struct PnvChip PnvChip; #define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" #define PNV_XSCOM_INTERFACE(obj) \ @@ -82,6 +82,9 @@ struct PnvXScomInterfaceClass { #define PNV_XSCOM_PBCQ_SPCI_BASE 0x9013c00 #define PNV_XSCOM_PBCQ_SPCI_SIZE 0x5 +#define PNV9_XSCOM_ADU_BASE 0x0090000 +#define PNV9_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 9) */ @@ -128,6 +131,9 @@ struct PnvXScomInterfaceClass { #define PNV9_XSCOM_PEC_PCI_STK1 0x140 #define PNV9_XSCOM_PEC_PCI_STK2 0x180 +#define PNV10_XSCOM_ADU_BASE 0x0090000 +#define PNV10_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 10) */ @@ -194,6 +200,9 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PEC_PCI_BASE 0x8010800 /* index goes upwards ... */ #define PNV10_XSCOM_PEC_PCI_SIZE 0x200 +#define PNV10_XSCOM_PIB_SPIC_BASE 0xc0000 +#define PNV10_XSCOM_PIB_SPIC_SIZE 0x20 + void pnv_xscom_init(PnvChip *chip, uint64_t size, hwaddr addr); int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, uint64_t xscom_base, uint64_t xscom_size, diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 4aaf23d28f..f6de3e9972 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -1004,6 +1004,7 @@ extern const VMStateDescription vmstate_spapr_cap_large_decr; extern const VMStateDescription vmstate_spapr_cap_ccf_assist; extern const VMStateDescription vmstate_spapr_cap_fwnmi; extern const VMStateDescription vmstate_spapr_cap_rpt_invalidate; +extern const VMStateDescription vmstate_spapr_cap_ail_mode_3; extern const VMStateDescription vmstate_spapr_wdt; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 4e5e17cd89..4349d009d0 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -97,6 +97,7 @@ typedef struct Xive2End { uint32_t w6; #define END2_W6_FORMAT_BIT PPC_BIT32(0) #define END2_W6_IGNORE PPC_BIT32(1) +#define END2_W6_CROWD PPC_BIT32(2) #define END2_W6_VP_BLOCK PPC_BITMASK32(4, 7) #define END2_W6_VP_OFFSET PPC_BITMASK32(8, 31) #define END2_W6_VP_OFFSET_GEN1 PPC_BITMASK32(13, 31) @@ -111,6 +112,8 @@ typedef struct Xive2End { #define xive2_end_is_notify(end) \ (be32_to_cpu((end)->w0) & END2_W0_UCOND_NOTIFY) #define xive2_end_is_backlog(end) (be32_to_cpu((end)->w0) & END2_W0_BACKLOG) +#define xive2_end_is_precluded_escalation(end) \ + (be32_to_cpu((end)->w0) & END2_W0_PRECL_ESC_CTL) #define xive2_end_is_escalate(end) \ (be32_to_cpu((end)->w0) & END2_W0_ESCALATE_CTL) #define xive2_end_is_uncond_escalation(end) \ @@ -123,6 +126,10 @@ typedef struct Xive2End { (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE1) #define xive2_end_is_firmware2(end) \ (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE2) +#define xive2_end_is_ignore(end) \ + (be32_to_cpu((end)->w6) & END2_W6_IGNORE) +#define xive2_end_is_crowd(end) \ + (be32_to_cpu((end)->w6) & END2_W6_CROWD) static inline uint64_t xive2_end_qaddr(Xive2End *end) { @@ -194,6 +201,8 @@ static inline uint32_t xive2_nvp_blk(uint32_t cam_line) return (cam_line >> XIVE2_NVP_SHIFT) & 0xf; } +void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf); + /* * Notification Virtual Group or Crowd (NVG/NVC) */ diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h new file mode 100644 index 0000000000..8815f67d45 --- /dev/null +++ b/include/hw/ssi/pnv_spi.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This model Supports a connection to a single SPI responder. + * Introduced for P10 to provide access to SPI seeproms, TPM, flash device + * and an ADC controller. + * + * All SPI function control is mapped into the SPI register space to enable + * full control by firmware. + * + * SPI Controller has sequencer and shift engine. The SPI shift engine + * performs serialization and de-serialization according to the control by + * the sequencer and according to the setup defined in the configuration + * registers and the SPI sequencer implements the main control logic. + */ + +#ifndef PPC_PNV_SPI_H +#define PPC_PNV_SPI_H + +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" + +#define TYPE_PNV_SPI "pnv-spi" +OBJECT_DECLARE_SIMPLE_TYPE(PnvSpi, PNV_SPI) + +#define PNV_SPI_REG_SIZE 8 +#define PNV_SPI_REGS 7 + +#define TYPE_PNV_SPI_BUS "pnv-spi-bus" +typedef struct PnvSpi { + SysBusDevice parent_obj; + + SSIBus *ssi_bus; + qemu_irq *cs_line; + MemoryRegion xscom_spic_regs; + /* SPI object number */ + uint32_t spic_num; + uint8_t transfer_len; + uint8_t responder_select; + /* To verify if shift_n1 happens prior to shift_n2 */ + bool shift_n1_done; + /* Loop counter for branch operation opcode Ex/Fx */ + uint8_t loop_counter_1; + uint8_t loop_counter_2; + /* N1/N2_bits specifies the size of the N1/N2 segment of a frame in bits.*/ + uint8_t N1_bits; + uint8_t N2_bits; + /* Number of bytes in a payload for the N1/N2 frame segment.*/ + uint8_t N1_bytes; + uint8_t N2_bytes; + /* Number of N1/N2 bytes marked for transmit */ + uint8_t N1_tx; + uint8_t N2_tx; + /* Number of N1/N2 bytes marked for receive */ + uint8_t N1_rx; + uint8_t N2_rx; + + /* SPI registers */ + uint64_t regs[PNV_SPI_REGS]; + uint8_t seq_op[PNV_SPI_REG_SIZE]; + uint64_t status; +} PnvSpi; +#endif /* PPC_PNV_SPI_H */ diff --git a/include/hw/ssi/pnv_spi_regs.h b/include/hw/ssi/pnv_spi_regs.h new file mode 100644 index 0000000000..596e2c1911 --- /dev/null +++ b/include/hw/ssi/pnv_spi_regs.h @@ -0,0 +1,133 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_SPI_CONTROLLER_REGS_H +#define PNV_SPI_CONTROLLER_REGS_H + +/* + * Macros from target/ppc/cpu.h + * These macros are copied from ppc target specific file target/ppc/cpu.h + * as target/ppc/cpu.h cannot be included here. + */ +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) | PPC_BIT8(bs)) +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) +#define SETFIELD(m, v, val) \ + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) + +/* Error Register */ +#define ERROR_REG 0x00 + +/* counter_config_reg */ +#define SPI_CTR_CFG_REG 0x01 +#define SPI_CTR_CFG_N1 PPC_BITMASK(0, 7) +#define SPI_CTR_CFG_N2 PPC_BITMASK(8, 15) +#define SPI_CTR_CFG_CMP1 PPC_BITMASK(24, 31) +#define SPI_CTR_CFG_CMP2 PPC_BITMASK(32, 39) +#define SPI_CTR_CFG_N1_CTRL_B1 PPC_BIT(49) +#define SPI_CTR_CFG_N1_CTRL_B2 PPC_BIT(50) +#define SPI_CTR_CFG_N1_CTRL_B3 PPC_BIT(51) +#define SPI_CTR_CFG_N2_CTRL_B0 PPC_BIT(52) +#define SPI_CTR_CFG_N2_CTRL_B1 PPC_BIT(53) +#define SPI_CTR_CFG_N2_CTRL_B2 PPC_BIT(54) +#define SPI_CTR_CFG_N2_CTRL_B3 PPC_BIT(55) + +/* config_reg */ +#define CONFIG_REG1 0x02 + +/* clock_config_reset_control_ecc_enable_reg */ +#define SPI_CLK_CFG_REG 0x03 +#define SPI_CLK_CFG_HARD_RST 0x0084000000000000; +#define SPI_CLK_CFG_RST_CTRL PPC_BITMASK(24, 27) +#define SPI_CLK_CFG_ECC_EN PPC_BIT(28) +#define SPI_CLK_CFG_ECC_CTRL PPC_BITMASK(29, 30) + +/* memory_mapping_reg */ +#define SPI_MM_REG 0x04 +#define SPI_MM_RDR_MATCH_VAL PPC_BITMASK(32, 47) +#define SPI_MM_RDR_MATCH_MASK PPC_BITMASK(48, 63) + +/* transmit_data_reg */ +#define SPI_XMIT_DATA_REG 0x05 + +/* receive_data_reg */ +#define SPI_RCV_DATA_REG 0x06 + +/* sequencer_operation_reg */ +#define SPI_SEQ_OP_REG 0x07 + +/* status_reg */ +#define SPI_STS_REG 0x08 +#define SPI_STS_RDR_FULL PPC_BIT(0) +#define SPI_STS_RDR_OVERRUN PPC_BIT(1) +#define SPI_STS_RDR_UNDERRUN PPC_BIT(2) +#define SPI_STS_TDR_FULL PPC_BIT(4) +#define SPI_STS_TDR_OVERRUN PPC_BIT(5) +#define SPI_STS_TDR_UNDERRUN PPC_BIT(6) +#define SPI_STS_SEQ_FSM PPC_BITMASK(8, 15) +#define SPI_STS_SHIFTER_FSM PPC_BITMASK(16, 27) +#define SPI_STS_SEQ_INDEX PPC_BITMASK(28, 31) +#define SPI_STS_GEN_STATUS_B3 PPC_BIT(35) +#define SPI_STS_RDR PPC_BITMASK(1, 3) +#define SPI_STS_TDR PPC_BITMASK(5, 7) + +/* + * Shifter states + * + * These are the same values defined for the Shifter FSM field of the + * status register. It's a 12 bit field so we will represent it as three + * nibbles in the constants. + * + * These are shifter_fsm values + * + * Status reg bits 16-27 -> field bits 0-11 + * bits 0,1,2,5 unused/reserved + * bit 4 crc shift in (unused) + * bit 8 crc shift out (unused) + */ + +#define FSM_DONE 0x100 /* bit 3 */ +#define FSM_SHIFT_N2 0x020 /* bit 6 */ +#define FSM_WAIT 0x010 /* bit 7 */ +#define FSM_SHIFT_N1 0x004 /* bit 9 */ +#define FSM_START 0x002 /* bit 10 */ +#define FSM_IDLE 0x001 /* bit 11 */ + +/* + * Sequencer states + * + * These are sequencer_fsm values + * + * Status reg bits 8-15 -> field bits 0-7 + * bits 0-3 unused/reserved + * + */ +#define SEQ_STATE_INDEX_INCREMENT 0x08 /* bit 4 */ +#define SEQ_STATE_EXECUTE 0x04 /* bit 5 */ +#define SEQ_STATE_DECODE 0x02 /* bit 6 */ +#define SEQ_STATE_IDLE 0x01 /* bit 7 */ + +/* + * These are the supported sequencer operations. + * Only the upper nibble is significant because for many operations + * the lower nibble is a variable specific to the operation. + */ +#define SEQ_OP_STOP 0x00 +#define SEQ_OP_SELECT_SLAVE 0x10 +#define SEQ_OP_SHIFT_N1 0x30 +#define SEQ_OP_SHIFT_N2 0x40 +#define SEQ_OP_BRANCH_IFNEQ_RDR 0x60 +#define SEQ_OP_TRANSFER_TDR 0xC0 +#define SEQ_OP_BRANCH_IFNEQ_INC_1 0xE0 +#define SEQ_OP_BRANCH_IFNEQ_INC_2 0xF0 +#define NUM_SEQ_OPS 8 + +#endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index c4a914b3d8..9cf14ca3d5 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -338,6 +338,14 @@ void kvm_park_vcpu(CPUState *cpu); */ int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); +/** + * kvm_create_and_park_vcpu - Create and park a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be created and parked. + * + * @returns: 0 when success, errno (<0) when failed. + */ +int kvm_create_and_park_vcpu(CPUState *cpu); + /* Arch specific hooks */ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 1691297a76..eaeda00178 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -645,6 +645,9 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_SIER3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc3) #define KVM_REG_PPC_DAWR1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc4) #define KVM_REG_PPC_DAWRX1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc5) +#define KVM_REG_PPC_DEXCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc6) +#define KVM_REG_PPC_HASHKEYR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc7) +#define KVM_REG_PPC_HASHPKEYR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc8) /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index a8315659d9..f45474133a 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -47,9 +47,14 @@ struct PPCUserRegStruct { } QEMU_PACKED; struct PPCElfPrstatus { - char pad1[112]; + char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pid; + char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ struct PPCUserRegStruct pr_reg; - char pad2[40]; + char pad3[40]; /* 40 == sizeof(struct elf_prstatus) - + offsetof(struct elf_prstatus, pr_reg) - + sizeof(struct user_pt_regs) */ } QEMU_PACKED; @@ -96,7 +101,7 @@ typedef struct NoteFuncArg { DumpState *state; } NoteFuncArg; -static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; reg_t cr; @@ -109,6 +114,7 @@ static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) prstatus = ¬e->contents.prstatus; memset(prstatus, 0, sizeof(*prstatus)); + prstatus->pid = cpu_to_dump32(s, id); reg = &prstatus->pr_reg; for (i = 0; i < 32; i++) { @@ -127,7 +133,7 @@ static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) reg->ccr = cpu_to_dump_reg(s, cr); } -static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfFpregset *fpregset; @@ -146,7 +152,7 @@ static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) fpregset->fpscr = cpu_to_dump_reg(s, cpu->env.fpscr); } -static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfVmxregset *vmxregset; @@ -178,7 +184,7 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) vmxregset->vscr.u32[3] = cpu_to_dump32(s, ppc_get_vscr(&cpu->env)); } -static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfVsxregset *vsxregset; @@ -195,7 +201,7 @@ static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) } } -static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { struct PPCElfSperegset *speregset; Note *note = &arg->note; @@ -211,7 +217,7 @@ static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) static const struct NoteFuncDescStruct { int contents_size; - void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu); + void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu, int id); } note_func[] = { {sizeof_field(Note, contents.prstatus), ppc_write_elf_prstatus}, {sizeof_field(Note, contents.fpregset), ppc_write_elf_fpregset}, @@ -282,7 +288,7 @@ static int ppc_write_all_elf_notes(const char *note_name, arg.note.hdr.n_descsz = cpu_to_dump32(s, nf->contents_size); strncpy(arg.note.name, note_name, sizeof(arg.note.name)); - (*nf->note_contents_func)(&arg, cpu); + (*nf->note_contents_func)(&arg, cpu, id); note_size = sizeof(arg.note) - sizeof(arg.note.contents) + nf->contents_size; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2015e603d4..321ed2da75 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1196,21 +1196,6 @@ DEXCR_ASPECT(SRAPD, 4) DEXCR_ASPECT(NPHIE, 5) DEXCR_ASPECT(PHIE, 6) -/*****************************************************************************/ -/* PowerNV ChipTOD and TimeBase State Machine */ -struct pnv_tod_tbst { - int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ - int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ - - /* - * "Timers" for async TBST events are simulated by mfTFAC because TFAC - * is polled for such events. These are just used to ensure firmware - * performs the polling at least a few times. - */ - int tb_state_timer; - int tb_sync_pulse_timer; -}; - /*****************************************************************************/ /* The whole PowerPC CPU context */ @@ -1262,15 +1247,16 @@ struct CPUArchState { /* when a memory exception occurs, the access type is stored here */ int access_type; + /* For SMT processors */ + bool has_smt_siblings; + int core_index; + #if !defined(CONFIG_USER_ONLY) /* MMU context, only relevant for full system emulation */ #if defined(TARGET_PPC64) ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ struct CPUBreakpoint *ciabr_breakpoint; struct CPUWatchpoint *dawr0_watchpoint; - - /* POWER CPU regs/state */ - target_ulong scratch[8]; /* SCRATCH registers (shared across core) */ #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ @@ -1291,12 +1277,6 @@ struct CPUArchState { uint32_t tlb_need_flush; /* Delayed flush needed */ #define TLB_NEED_LOCAL_FLUSH 0x1 #define TLB_NEED_GLOBAL_FLUSH 0x2 - -#if defined(TARGET_PPC64) - /* PowerNV chiptod / timebase facility state. */ - /* Would be nice to put these into PnvCore */ - struct pnv_tod_tbst pnv_tod_tbst; -#endif #endif /* Other registers */ @@ -1426,12 +1406,10 @@ struct CPUArchState { uint64_t pmu_base_time; }; -#define _CORE_ID(cs) \ - (POWERPC_CPU(cs)->env.spr_cb[SPR_PIR].default_value & ~(cs->nr_threads - 1)) - #define THREAD_SIBLING_FOREACH(cs, cs_sibling) \ CPU_FOREACH(cs_sibling) \ - if (_CORE_ID(cs) == _CORE_ID(cs_sibling)) + if (POWERPC_CPU(cs)->env.core_index == \ + POWERPC_CPU(cs_sibling)->env.core_index) #define SET_FIT_PERIOD(a_, b_, c_, d_) \ do { \ @@ -1535,6 +1513,17 @@ struct PowerPCCPUClass { int (*check_attn)(CPUPPCState *env); }; +static inline bool ppc_cpu_core_single_threaded(CPUState *cs) +{ + return !POWERPC_CPU(cs)->env.has_smt_siblings; +} + +static inline bool ppc_cpu_lpar_single_threaded(CPUState *cs) +{ + return !(POWERPC_CPU(cs)->env.flags & POWERPC_FLAG_SMT_1LPAR) || + ppc_cpu_core_single_threaded(cs); +} + ObjectClass *ppc_cpu_class_by_name(const char *name); PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index cdada7987d..23881d09e9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5760,16 +5760,6 @@ static void register_power_common_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_core_write_generic, 0x00000000); - spr_register_hv(env, SPR_POWER_SPRC, "SPRC", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_sprc, - 0x00000000); - spr_register_hv(env, SPR_POWER_SPRD, "SPRD", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_sprd, &spr_write_sprd, - 0x00000000); #endif } @@ -5803,6 +5793,17 @@ static void register_power8_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_WORT, 0); + /* SPRC/SPRD exist in earlier CPUs but only tested on POWER9/10 */ + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sprc, + 0x00000000); + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sprd, &spr_write_sprd, + 0x00000000); #endif } @@ -5873,22 +5874,22 @@ static void register_power10_hash_sprs(CPUPPCState *env) ((uint64_t)g_rand_int(rand) << 32) | (uint64_t)g_rand_int(rand); g_rand_free(rand); #endif - spr_register(env, SPR_HASHKEYR, "HASHKEYR", + spr_register_kvm(env, SPR_HASHKEYR, "HASHKEYR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - hashkeyr_initial_value); - spr_register_hv(env, SPR_HASHPKEYR, "HASHPKEYR", + KVM_REG_PPC_HASHKEYR, hashkeyr_initial_value); + spr_register_kvm_hv(env, SPR_HASHPKEYR, "HASHPKEYR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - hashpkeyr_initial_value); + KVM_REG_PPC_HASHPKEYR, hashpkeyr_initial_value); } static void register_power10_dexcr_sprs(CPUPPCState *env) { - spr_register(env, SPR_DEXCR, "DEXCR", + spr_register_kvm(env, SPR_DEXCR, "DEXCR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, KVM_REG_PPC_DEXCR, 0); spr_register(env, SPR_UDEXCR, "UDEXCR", @@ -6785,7 +6786,8 @@ void cpu_ppc_set_1lpar(PowerPCCPU *cpu) /* * pseries SMT means "LPAR per core" mode, e.g., msgsndp is usable - * between threads. + * between threads. powernv be in either mode, and it mostly affects + * supervisor visible registers and instructions. */ if (env->flags & POWERPC_FLAG_SMT) { env->flags |= POWERPC_FLAG_SMT_1LPAR; @@ -6975,7 +6977,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) pcc->parent_realize(dev, errp); - if (env_cpu(env)->nr_threads > 1) { + if (!ppc_cpu_core_single_threaded(cs)) { env->flags |= POWERPC_FLAG_SMT; } diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0cd542675f..f33fc36db2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2998,6 +2998,34 @@ static inline bool dbell_bcast_subproc(target_ulong rb) return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; } +/* + * Send an interrupt to a thread in the same core as env). + */ +static void msgsnd_core_tir(CPUPPCState *env, uint32_t target_tir, int irq) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (ppc_cpu_lpar_single_threaded(cs)) { + if (target_tir == 0) { + ppc_set_irq(cpu, irq, 1); + } + } else { + CPUState *ccs; + + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + if (target_tir == ppc_cpu_tir(ccpu)) { + ppc_set_irq(ccpu, irq, 1); + break; + } + } + bql_unlock(); + } +} + void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { if (!dbell_type_server(rb)) { @@ -3018,6 +3046,13 @@ void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) return; } + /* POWER8 msgsnd is like msgsndp (targets a thread within core) */ + if (!(env->insns_flags2 & PPC2_ISA300)) { + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_HDOORBELL); + return; + } + + /* POWER9 and later msgsnd is a global (targets any thread) */ cpu = ppc_get_vcpu_by_pir(pir); if (!cpu) { return; @@ -3029,7 +3064,7 @@ void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) brdcast = true; } - if (cs->nr_threads == 1 || !brdcast) { + if (ppc_cpu_core_single_threaded(cs) || !brdcast) { ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); return; } @@ -3064,41 +3099,13 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) */ void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) { - CPUState *cs = env_cpu(env); - PowerPCCPU *cpu = env_archcpu(env); - CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - int ttir = rb & PPC_BITMASK(57, 63); - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/ - } - - if (!dbell_type_server(rb) || ttir >= nr_threads) { + if (!dbell_type_server(rb)) { return; } - if (nr_threads == 1) { - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1); - return; - } - - /* Does iothread need to be locked for walking CPU list? */ - bql_lock(); - THREAD_SIBLING_FOREACH(cs, ccs) { - PowerPCCPU *ccpu = POWERPC_CPU(ccs); - uint32_t thread_id = ppc_cpu_tir(ccpu); - - if (ttir == thread_id) { - ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1); - bql_unlock(); - return; - } - } - - g_assert_not_reached(); + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_DOORBELL); } #endif /* TARGET_PPC64 */ diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 51bce99fd5..230466a87f 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -1599,14 +1599,14 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_ADD_SUB(xsadddp, add, 1, float64, VsrD(0), 1, 0) -VSX_ADD_SUB(xsaddsp, add, 1, float64, VsrD(0), 1, 1) -VSX_ADD_SUB(xvadddp, add, 2, float64, VsrD(i), 0, 0) -VSX_ADD_SUB(xvaddsp, add, 4, float32, VsrW(i), 0, 0) -VSX_ADD_SUB(xssubdp, sub, 1, float64, VsrD(0), 1, 0) -VSX_ADD_SUB(xssubsp, sub, 1, float64, VsrD(0), 1, 1) -VSX_ADD_SUB(xvsubdp, sub, 2, float64, VsrD(i), 0, 0) -VSX_ADD_SUB(xvsubsp, sub, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(XSADDDP, add, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(XSADDSP, add, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(XVADDDP, add, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(XVADDSP, add, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(XSSUBDP, sub, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(XSSUBSP, sub, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(XVSUBDP, sub, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(XVSUBSP, sub, 4, float32, VsrW(i), 0, 0) void helper_xsaddqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -1676,10 +1676,10 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_MUL(xsmuldp, 1, float64, VsrD(0), 1, 0) -VSX_MUL(xsmulsp, 1, float64, VsrD(0), 1, 1) -VSX_MUL(xvmuldp, 2, float64, VsrD(i), 0, 0) -VSX_MUL(xvmulsp, 4, float32, VsrW(i), 0, 0) +VSX_MUL(XSMULDP, 1, float64, VsrD(0), 1, 0) +VSX_MUL(XSMULSP, 1, float64, VsrD(0), 1, 1) +VSX_MUL(XVMULDP, 2, float64, VsrD(i), 0, 0) +VSX_MUL(XVMULSP, 4, float32, VsrW(i), 0, 0) void helper_xsmulqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -1750,10 +1750,10 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_DIV(xsdivdp, 1, float64, VsrD(0), 1, 0) -VSX_DIV(xsdivsp, 1, float64, VsrD(0), 1, 1) -VSX_DIV(xvdivdp, 2, float64, VsrD(i), 0, 0) -VSX_DIV(xvdivsp, 4, float32, VsrW(i), 0, 0) +VSX_DIV(XSDIVDP, 1, float64, VsrD(0), 1, 0) +VSX_DIV(XSDIVSP, 1, float64, VsrD(0), 1, 1) +VSX_DIV(XVDIVDP, 2, float64, VsrD(i), 0, 0) +VSX_DIV(XVDIVSP, 4, float32, VsrW(i), 0, 0) void helper_xsdivqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -2383,12 +2383,12 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, false, GETPC()); \ } -VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, VsrD(0)) -VSX_MAX_MIN(xvmaxdp, maxnum, 2, float64, VsrD(i)) -VSX_MAX_MIN(xvmaxsp, maxnum, 4, float32, VsrW(i)) -VSX_MAX_MIN(xsmindp, minnum, 1, float64, VsrD(0)) -VSX_MAX_MIN(xvmindp, minnum, 2, float64, VsrD(i)) -VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(XSMAXDP, maxnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(XVMAXDP, maxnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(XVMAXSP, maxnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(XSMINDP, minnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(XVMINDP, minnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(XVMINSP, minnum, 4, float32, VsrW(i)) #define VSX_MAX_MINC(name, max, tp, fld) \ void helper_##name(CPUPPCState *env, \ @@ -2527,14 +2527,14 @@ uint32_t helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ return crf6; \ } -VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) -VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1) -VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1) -VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0) -VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1) -VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1) -VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1) -VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) +VSX_CMP(XVCMPEQDP, 2, float64, VsrD(i), eq, 0, 1) +VSX_CMP(XVCMPGEDP, 2, float64, VsrD(i), le, 1, 1) +VSX_CMP(XVCMPGTDP, 2, float64, VsrD(i), lt, 1, 1) +VSX_CMP(XVCMPNEDP, 2, float64, VsrD(i), eq, 0, 0) +VSX_CMP(XVCMPEQSP, 4, float32, VsrW(i), eq, 0, 1) +VSX_CMP(XVCMPGESP, 4, float32, VsrW(i), le, 1, 1) +VSX_CMP(XVCMPGTSP, 4, float32, VsrW(i), lt, 1, 1) +VSX_CMP(XVCMPNESP, 4, float32, VsrW(i), eq, 0, 0) /* * VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 4fa089cbf9..5a77e761bd 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -203,18 +203,18 @@ DEF_HELPER_FLAGS_3(vsro, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vsrv, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vslv, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VPRTYBQ, TCG_CALL_NO_RWG, void, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddubs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vadduhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vadduws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsububs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubuhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubuws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDSBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDSHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDSWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBUBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBUHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBUWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_3(VADDUQM, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_4(VADDECUQ, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) DEF_HELPER_FLAGS_4(VADDEUQM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) @@ -277,10 +277,10 @@ DEF_HELPER_3(STVEBX, void, env, avr, tl) DEF_HELPER_3(STVEHX, void, env, avr, tl) DEF_HELPER_3(STVEWX, void, env, avr, tl) #if defined(TARGET_PPC64) -DEF_HELPER_4(lxvl, void, env, tl, vsr, tl) -DEF_HELPER_4(lxvll, void, env, tl, vsr, tl) -DEF_HELPER_4(stxvl, void, env, tl, vsr, tl) -DEF_HELPER_4(stxvll, void, env, tl, vsr, tl) +DEF_HELPER_4(LXVL, void, env, tl, vsr, tl) +DEF_HELPER_4(LXVLL, void, env, tl, vsr, tl) +DEF_HELPER_4(STXVL, void, env, tl, vsr, tl) +DEF_HELPER_4(STXVLL, void, env, tl, vsr, tl) #endif DEF_HELPER_4(vsumsws, void, env, avr, avr, avr) DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr) @@ -364,12 +364,12 @@ DEF_HELPER_FLAGS_4(bcdsr, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) DEF_HELPER_FLAGS_4(bcdtrunc, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) DEF_HELPER_FLAGS_4(bcdutrunc, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) -DEF_HELPER_4(xsadddp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSADDDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsaddqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xssubdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmuldp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSSUBDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMULDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsmulqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xsdivdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSDIVDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsdivqp, void, env, i32, vsr, vsr, vsr) DEF_HELPER_3(xsredp, void, env, vsr, vsr) DEF_HELPER_3(xssqrtdp, void, env, vsr, vsr) @@ -392,8 +392,8 @@ DEF_HELPER_4(xscmpodp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpudp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpoqp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpuqp, void, env, i32, vsr, vsr) -DEF_HELPER_4(xsmaxdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmindp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMAXDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMINDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMAXCDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMINCDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMAXJDP, void, env, vsr, vsr, vsr) @@ -439,10 +439,10 @@ DEF_HELPER_4(xsrqpxp, void, env, i32, vsr, vsr) DEF_HELPER_4(xssqrtqp, void, env, i32, vsr, vsr) DEF_HELPER_5(xssubqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xsaddsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xssubsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmulsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsdivsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSADDSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSSUBSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMULSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSDIVSP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xsresp, void, env, vsr, vsr) DEF_HELPER_2(xsrsp, i64, env, i64) DEF_HELPER_3(xssqrtsp, void, env, vsr, vsr) @@ -461,10 +461,10 @@ DEF_HELPER_5(XSNMADDQPO, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(XSNMSUBQP, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(XSNMSUBQPO, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvadddp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvsubdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmuldp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvdivdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVADDDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVSUBDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMULDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVDIVDP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xvredp, void, env, vsr, vsr) DEF_HELPER_3(xvsqrtdp, void, env, vsr, vsr) DEF_HELPER_3(xvrsqrtedp, void, env, vsr, vsr) @@ -474,12 +474,12 @@ DEF_HELPER_5(xvmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvmsubdp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubdp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvmaxdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmindp, void, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpeqdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgtdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpnedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMAXDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMINDP, void, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPEQDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGEDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGTDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPNEDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvdpsp, void, env, vsr, vsr) DEF_HELPER_3(xvcvdpsxds, void, env, vsr, vsr) DEF_HELPER_3(xvcvdpsxws, void, env, vsr, vsr) @@ -495,10 +495,10 @@ DEF_HELPER_3(xvrdpim, void, env, vsr, vsr) DEF_HELPER_3(xvrdpip, void, env, vsr, vsr) DEF_HELPER_3(xvrdpiz, void, env, vsr, vsr) -DEF_HELPER_4(xvaddsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvsubsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmulsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvdivsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVADDSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVSUBSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMULSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVDIVSP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xvresp, void, env, vsr, vsr) DEF_HELPER_3(xvsqrtsp, void, env, vsr, vsr) DEF_HELPER_3(xvrsqrtesp, void, env, vsr, vsr) @@ -508,12 +508,12 @@ DEF_HELPER_5(xvmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvmsubsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubsp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvmaxsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvminsp, void, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpeqsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgtsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpnesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMAXSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMINSP, void, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPEQSP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGESP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGTSP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPNESP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvspdp, void, env, vsr, vsr) DEF_HELPER_3(xvcvsphp, void, env, vsr, vsr) DEF_HELPER_3(xvcvhpsp, void, env, vsr, vsr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index ee33141476..e53fd2840d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -241,6 +241,9 @@ &XX3 xt xa xb @XX3 ...... ..... ..... ..... ........ ... &XX3 xt=%xx_xt xa=%xx_xa xb=%xx_xb +&XX3_rc xt xa xb rc:bool +@XX3_rc ...... ..... ..... ..... rc:1 ....... ... &XX3_rc xt=%xx_xt xa=%xx_xa xb=%xx_xb + # 32 bit GER instructions have all mask bits considered 1 &MMIRR_XX3 xa xb xt pmsk xmsk ymsk %xx_at 23:3 @@ -832,6 +835,14 @@ VADDCUW 000100 ..... ..... ..... 00110000000 @VX VADDCUQ 000100 ..... ..... ..... 00101000000 @VX VADDUQM 000100 ..... ..... ..... 00100000000 @VX +VADDSBS 000100 ..... ..... ..... 01100000000 @VX +VADDSHS 000100 ..... ..... ..... 01101000000 @VX +VADDSWS 000100 ..... ..... ..... 01110000000 @VX + +VADDUBS 000100 ..... ..... ..... 01000000000 @VX +VADDUHS 000100 ..... ..... ..... 01001000000 @VX +VADDUWS 000100 ..... ..... ..... 01010000000 @VX + VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA @@ -839,6 +850,14 @@ VSUBCUW 000100 ..... ..... ..... 10110000000 @VX VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX VSUBUQM 000100 ..... ..... ..... 10100000000 @VX +VSUBSBS 000100 ..... ..... ..... 11100000000 @VX +VSUBSHS 000100 ..... ..... ..... 11101000000 @VX +VSUBSWS 000100 ..... ..... ..... 11110000000 @VX + +VSUBUBS 000100 ..... ..... ..... 11000000000 @VX +VSUBUHS 000100 ..... ..... ..... 11001000000 @VX +VSUBUWS 000100 ..... ..... ..... 11010000000 @VX + VSUBECUQ 000100 ..... ..... ..... ..... 111111 @VA VSUBEUQM 000100 ..... ..... ..... ..... 111110 @VA @@ -977,6 +996,35 @@ STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX +LXSDX 011111 ..... ..... ..... 1001001100 . @X_TSX +LXSIWAX 011111 ..... ..... ..... 0001001100 . @X_TSX +LXSIBZX 011111 ..... ..... ..... 1100001101 . @X_TSX +LXSIHZX 011111 ..... ..... ..... 1100101101 . @X_TSX +LXSIWZX 011111 ..... ..... ..... 0000001100 . @X_TSX +LXSSPX 011111 ..... ..... ..... 1000001100 . @X_TSX + +STXSDX 011111 ..... ..... ..... 1011001100 . @X_TSX +STXSIBX 011111 ..... ..... ..... 1110001101 . @X_TSX +STXSIHX 011111 ..... ..... ..... 1110101101 . @X_TSX +STXSIWX 011111 ..... ..... ..... 0010001100 . @X_TSX +STXSSPX 011111 ..... ..... ..... 1010001100 . @X_TSX + +LXVB16X 011111 ..... ..... ..... 1101101100 . @X_TSX +LXVD2X 011111 ..... ..... ..... 1101001100 . @X_TSX +LXVH8X 011111 ..... ..... ..... 1100101100 . @X_TSX +LXVW4X 011111 ..... ..... ..... 1100001100 . @X_TSX +LXVDSX 011111 ..... ..... ..... 0101001100 . @X_TSX +LXVWSX 011111 ..... ..... ..... 0101101100 . @X_TSX +LXVL 011111 ..... ..... ..... 0100001101 . @X_TSX +LXVLL 011111 ..... ..... ..... 0100101101 . @X_TSX + +STXVB16X 011111 ..... ..... ..... 1111101100 . @X_TSX +STXVD2X 011111 ..... ..... ..... 1111001100 . @X_TSX +STXVH8X 011111 ..... ..... ..... 1110101100 . @X_TSX +STXVW4X 011111 ..... ..... ..... 1110001100 . @X_TSX +STXVL 011111 ..... ..... ..... 0110001101 . @X_TSX +STXVLL 011111 ..... ..... ..... 0110101101 . @X_TSX + ## VSX Vector Binary Floating-Point Sign Manipulation Instructions XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2 @@ -988,6 +1036,28 @@ XVNEGSP 111100 ..... 00000 ..... 110111001 .. @XX2 XVCPSGNDP 111100 ..... ..... ..... 11110000 ... @XX3 XVCPSGNSP 111100 ..... ..... ..... 11010000 ... @XX3 +## VSX Binary Floating-Point Arithmetic Instructions + +XSADDSP 111100 ..... ..... ..... 00000000 ... @XX3 +XSSUBSP 111100 ..... ..... ..... 00001000 ... @XX3 +XSMULSP 111100 ..... ..... ..... 00010000 ... @XX3 +XSDIVSP 111100 ..... ..... ..... 00011000 ... @XX3 + +XSADDDP 111100 ..... ..... ..... 00100000 ... @XX3 +XSSUBDP 111100 ..... ..... ..... 00101000 ... @XX3 +XSMULDP 111100 ..... ..... ..... 00110000 ... @XX3 +XSDIVDP 111100 ..... ..... ..... 00111000 ... @XX3 + +XVADDSP 111100 ..... ..... ..... 01000000 ... @XX3 +XVSUBSP 111100 ..... ..... ..... 01001000 ... @XX3 +XVMULSP 111100 ..... ..... ..... 01010000 ... @XX3 +XVDIVSP 111100 ..... ..... ..... 01011000 ... @XX3 + +XVADDDP 111100 ..... ..... ..... 01100000 ... @XX3 +XVSUBDP 111100 ..... ..... ..... 01101000 ... @XX3 +XVMULDP 111100 ..... ..... ..... 01110000 ... @XX3 +XVDIVDP 111100 ..... ..... ..... 01111000 ... @XX3 + ## VSX Scalar Multiply-Add Instructions XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3 @@ -1057,6 +1127,23 @@ XSCMPEQQP 111111 ..... ..... ..... 0001000100 - @X XSCMPGEQP 111111 ..... ..... ..... 0011000100 - @X XSCMPGTQP 111111 ..... ..... ..... 0011100100 - @X +XVCMPEQSP 111100 ..... ..... ..... . 1000011 ... @XX3_rc +XVCMPGTSP 111100 ..... ..... ..... . 1001011 ... @XX3_rc +XVCMPGESP 111100 ..... ..... ..... . 1010011 ... @XX3_rc +XVCMPNESP 111100 ..... ..... ..... . 1011011 ... @XX3_rc +XVCMPEQDP 111100 ..... ..... ..... . 1100011 ... @XX3_rc +XVCMPGTDP 111100 ..... ..... ..... . 1101011 ... @XX3_rc +XVCMPGEDP 111100 ..... ..... ..... . 1110011 ... @XX3_rc +XVCMPNEDP 111100 ..... ..... ..... . 1111011 ... @XX3_rc + +XSMAXDP 111100 ..... ..... ..... 10100000 ... @XX3 +XSMINDP 111100 ..... ..... ..... 10101000 ... @XX3 + +XVMAXSP 111100 ..... ..... ..... 11000000 ... @XX3 +XVMINSP 111100 ..... ..... ..... 11001000 ... @XX3 +XVMAXDP 111100 ..... ..... ..... 11100000 ... @XX3 +XVMINDP 111100 ..... ..... ..... 11101000 ... @XX3 + ## VSX Binary Floating-Point Convert Instructions XSCVQPDP 111111 ..... 10100 ..... 1101000100 . @X_tb_rc @@ -1092,6 +1179,17 @@ XXMFACC 011111 ... -- 00000 ----- 0010110001 - @X_a XXMTACC 011111 ... -- 00001 ----- 0010110001 - @X_a XXSETACCZ 011111 ... -- 00011 ----- 0010110001 - @X_a +## VSX Vector Logical instructions + +XXLAND 111100 ..... ..... ..... 10000010 ... @XX3 +XXLANDC 111100 ..... ..... ..... 10001010 ... @XX3 +XXLOR 111100 ..... ..... ..... 10010010 ... @XX3 +XXLXOR 111100 ..... ..... ..... 10011010 ... @XX3 +XXLNOR 111100 ..... ..... ..... 10100010 ... @XX3 +XXLEQV 111100 ..... ..... ..... 10111010 ... @XX3 +XXLNAND 111100 ..... ..... ..... 10110010 ... @XX3 +XXLORC 111100 ..... ..... ..... 10101010 ... @XX3 + ## VSX GER instruction XVI4GER8 111011 ... -- ..... ..... 00100011 ..- @XX3_at xa=%xx_xa diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 2c6b633d65..ef4b2e75d6 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -541,7 +541,7 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } #define VARITHSAT_DO(name, op, optype, cvt, element) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ + void helper_V##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ ppc_avr_t *a, ppc_avr_t *b, uint32_t desc) \ { \ int sat = 0; \ @@ -555,17 +555,17 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } \ } #define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element) + VARITHSAT_DO(ADDS##suffix##S, +, optype, cvt, element) \ + VARITHSAT_DO(SUBS##suffix##S, -, optype, cvt, element) #define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element) -VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb) -VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh) -VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw) -VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub) -VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh) -VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) + VARITHSAT_DO(ADDU##suffix##S, +, optype, cvt, element) \ + VARITHSAT_DO(SUBU##suffix##S, -, optype, cvt, element) +VARITHSAT_SIGNED(B, s8, int16_t, cvtshsb) +VARITHSAT_SIGNED(H, s16, int32_t, cvtswsh) +VARITHSAT_SIGNED(W, s32, int64_t, cvtsdsw) +VARITHSAT_UNSIGNED(B, u8, uint16_t, cvtshub) +VARITHSAT_UNSIGNED(H, u16, uint32_t, cvtswuh) +VARITHSAT_UNSIGNED(W, u32, uint64_t, cvtsduw) #undef VARITHSAT_CASE #undef VARITHSAT_DO #undef VARITHSAT_SIGNED diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 2c3932200b..907dba60d1 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -48,6 +48,8 @@ #include "qemu/mmap-alloc.h" #include "elf.h" #include "sysemu/kvm_int.h" +#include "sysemu/kvm.h" +#include "hw/core/accel-cpu.h" #include CONFIG_DEVICES @@ -2346,6 +2348,30 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) } } +static bool kvmppc_cpu_realize(CPUState *cs, Error **errp) +{ + int ret; + const char *vcpu_str = (cs->parent_obj.hotplugged == true) ? + "hotplug" : "create"; + cs->cpu_index = cpu_get_free_index(); + + POWERPC_CPU(cs)->vcpu_id = cs->cpu_index; + + /* create and park to fail gracefully in case vcpu hotplug fails */ + ret = kvm_create_and_park_vcpu(cs); + if (ret) { + /* + * This causes QEMU to terminate if initial CPU creation + * fails, and only CPU hotplug failure if the error happens + * there. + */ + error_setg(errp, "%s: vcpu %s failed with %d", + __func__, vcpu_str, ret); + return false; + } + return true; +} + static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2966,3 +2992,23 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) void kvm_arch_accel_class_init(ObjectClass *oc) { } + +static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_target_realize = kvmppc_cpu_realize; +} + +static const TypeInfo kvm_cpu_accel_type_info = { + .name = ACCEL_CPU_NAME("kvm"), + + .parent = TYPE_ACCEL_CPU, + .class_init = kvm_cpu_accel_class_init, + .abstract = true, +}; +static void kvm_cpu_accel_register_types(void) +{ + type_register_static(&kvm_cpu_accel_type_info); +} +type_init(kvm_cpu_accel_register_types); diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 953dd08d5d..51b137febd 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -475,8 +475,8 @@ void helper_##name(CPUPPCState *env, target_ulong addr, \ *xt = t; \ } -VSX_LXVL(lxvl, 0) -VSX_LXVL(lxvll, 1) +VSX_LXVL(LXVL, 0) +VSX_LXVL(LXVLL, 1) #undef VSX_LXVL #define VSX_STXVL(name, lj) \ @@ -504,8 +504,8 @@ void helper_##name(CPUPPCState *env, target_ulong addr, \ } \ } -VSX_STXVL(stxvl, 0) -VSX_STXVL(stxvll, 1) +VSX_STXVL(STXVL, 0) +VSX_STXVL(STXVLL, 1) #undef VSX_STXVL #undef GET_NB #endif /* TARGET_PPC64 */ diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index fa47be2298..1b83971375 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -48,9 +48,8 @@ void helper_spr_core_write_generic(CPUPPCState *env, uint32_t sprn, { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1) { + if (ppc_cpu_core_single_threaded(cs)) { env->spr[sprn] = val; return; } @@ -195,7 +194,7 @@ void helper_store_ptcr(CPUPPCState *env, target_ulong val) return; } - if (cs->nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { env->spr[SPR_PTCR] = val; tlb_flush(cs); } else { @@ -242,16 +241,12 @@ target_ulong helper_load_dpdes(CPUPPCState *env) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; target_ulong dpdes = 0; helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ - } - - if (nr_threads == 1) { + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { dpdes = 1; } @@ -278,21 +273,11 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) PowerPCCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ - } - - if (val & ~(nr_threads - 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value " - TARGET_FMT_lx"\n", val); - val &= (nr_threads - 1); /* Ignore the invalid bits */ - } - - if (nr_threads == 1) { + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1); return; } @@ -308,6 +293,13 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) bql_unlock(); } +/* + * qemu-user breaks with pnv headers, so they go under ifdefs for now. + * A clean up may be to move powernv specific registers and helpers into + * target/ppc/pnv_helper.c + */ +#include "hw/ppc/pnv_core.h" + /* Indirect SCOM (SPRC/SPRD) access to SCRATCH0-7 are implemented. */ void helper_store_sprc(CPUPPCState *env, target_ulong val) { @@ -321,11 +313,35 @@ void helper_store_sprc(CPUPPCState *env, target_ulong val) target_ulong helper_load_sprd(CPUPPCState *env) { + /* + * SPRD is a HV-only register for Power CPUs, so this will only be + * accessed by powernv machines. + */ + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; target_ulong sprc = env->spr[SPR_POWER_SPRC]; - switch (sprc & 0x3c0) { - case 0: /* SCRATCH0-7 */ - return env->scratch[(sprc >> 3) & 0x7]; + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + return pc->scratch[(sprc >> 3) & 0x7]; + + case 0x1e0: /* core thread state */ + if (env->excp_model == POWERPC_EXCP_POWER9) { + /* + * Only implement for POWER9 because skiboot uses it to check + * big-core mode. Other bits are unimplemented so we would + * prefer to get unimplemented message on POWER10 if it were + * used anywhere. + */ + if (pc->big_core) { + return PPC_BIT(63); + } else { + return 0; + } + } + /* fallthru */ + default: qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" TARGET_FMT_lx"\n", sprc); @@ -334,41 +350,28 @@ target_ulong helper_load_sprd(CPUPPCState *env) return 0; } -static void do_store_scratch(CPUPPCState *env, int nr, target_ulong val) -{ - CPUState *cs = env_cpu(env); - CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - - /* - * Log stores to SCRATCH, because some firmware uses these for debugging - * and logging, but they would normally be read by the BMC, which is - * not implemented in QEMU yet. This gives a way to get at the information. - * Could also dump these upon checkstop. - */ - qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); - - if (nr_threads == 1) { - env->scratch[nr] = val; - return; - } - - THREAD_SIBLING_FOREACH(cs, ccs) { - CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; - cenv->scratch[nr] = val; - } -} - void helper_store_sprd(CPUPPCState *env, target_ulong val) { target_ulong sprc = env->spr[SPR_POWER_SPRC]; + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + int nr; - switch (sprc & 0x3c0) { - case 0: /* SCRATCH0-7 */ - do_store_scratch(env, (sprc >> 3) & 0x7, val); + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + /* + * Log stores to SCRATCH, because some firmware uses these for + * debugging and logging, but they would normally be read by the BMC, + * which is not implemented in QEMU yet. This gives a way to get at the + * information. Could also dump these upon checkstop. + */ + nr = (sprc >> 3) & 0x7; + qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); + pc->scratch[nr] = val; break; default: - qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" TARGET_FMT_lx"\n", sprc); break; } diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index c8f69b3df9..a812cb5113 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" -#include "mmu-radix64.h" bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index f3f7993958..be66e26604 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -20,9 +20,6 @@ #ifndef PPC_MMU_BOOK3S_V3_H #define PPC_MMU_BOOK3S_V3_H -#include "mmu-hash64.h" -#include "mmu-books.h" - #ifndef CONFIG_USER_ONLY /* @@ -83,46 +80,6 @@ static inline bool ppc64_v3_radix(PowerPCCPU *cpu) return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR); } -static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) -{ - uint64_t base; - - if (cpu->vhyp) { - return 0; - } - if (cpu->env.mmu_model == POWERPC_MMU_3_00) { - ppc_v3_pate_t pate; - - if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { - return 0; - } - base = pate.dw0; - } else { - base = cpu->env.spr[SPR_SDR1]; - } - return base & SDR_64_HTABORG; -} - -static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) -{ - uint64_t base; - - if (cpu->vhyp) { - return cpu->vhyp_class->hpt_mask(cpu->vhyp); - } - if (cpu->env.mmu_model == POWERPC_MMU_3_00) { - ppc_v3_pate_t pate; - - if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { - return 0; - } - base = pate.dw0; - } else { - base = cpu->env.spr[SPR_SDR1]; - } - return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; -} - #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index d5f2057eb1..44b16142ab 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -37,17 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -static int ppc_hash32_pte_prot(int mmu_idx, - target_ulong sr, ppc_hash_pte32_t pte) -{ - unsigned pp, key; - - key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); - pp = pte.pte1 & HPTE32_R_PP; - - return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); -} - static target_ulong hash32_bat_size(int mmu_idx, target_ulong batu, target_ulong batl) { @@ -59,22 +48,6 @@ static target_ulong hash32_bat_size(int mmu_idx, return BATU32_BEPI & ~((batu & BATU32_BL) << 15); } -static int hash32_bat_prot(PowerPCCPU *cpu, - target_ulong batu, target_ulong batl) -{ - int pp, prot; - - prot = 0; - pp = batl & BATL32_PP; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } - return prot; -} - static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, MMUAccessType access_type, int *prot, int mmu_idx) @@ -106,7 +79,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { hwaddr raddr = (batl & mask) | (ea & ~mask); - *prot = hash32_bat_prot(cpu, batu, batl); + *prot = ppc_hash32_bat_prot(batu, batl); return raddr & TARGET_PAGE_MASK; } @@ -145,7 +118,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); @@ -206,7 +178,11 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, cpu_abort(cs, "ERROR: insn should not need address translation\n"); } - *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; + if (ppc_hash32_key(mmuidx_pr(mmu_idx), sr)) { + *prot = PAGE_READ | PAGE_WRITE; + } else { + *prot = PAGE_READ; + } if (check_prot_access_type(*prot, access_type)) { *raddr = eaddr; return true; @@ -225,13 +201,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, return false; } -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) -{ - target_ulong mask = ppc_hash32_hpt_mask(cpu); - - return (hash * HASH_PTEG_SIZE_32) & mask; -} - static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte32_t *pte) @@ -322,15 +291,6 @@ static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, return pte_offset; } -static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, - target_ulong eaddr) -{ - hwaddr rpn = pte.pte1 & HPTE32_R_RPN; - hwaddr mask = ~TARGET_PAGE_MASK; - - return (rpn & ~mask) | (eaddr & mask); -} - bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible) @@ -338,10 +298,10 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong sr; - hwaddr pte_offset; + hwaddr pte_offset, raddr; ppc_hash_pte32_t pte; + bool key; int prot; - hwaddr raddr; /* There are no hash32 large pages. */ *psizep = TARGET_PAGE_BITS; @@ -423,8 +383,8 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 7. Check access permissions */ - - prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); + key = ppc_hash32_key(mmuidx_pr(mmu_idx), sr); + prot = ppc_hash32_prot(key, pte.pte1 & HPTE32_R_PP, sr & SR32_NX); if (!check_prot_access_type(prot, access_type)) { /* Access right violation */ @@ -464,11 +424,12 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, */ prot &= ~PAGE_WRITE; } - } + } + *protp = prot; /* 9. Determine the real address from the PTE */ - - *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr); - *protp = prot; + *raddrp = pte.pte1 & HPTE32_R_RPN; + *raddrp &= TARGET_PAGE_MASK; + *raddrp |= eaddr & ~TARGET_PAGE_MASK; return true; } diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index f0ce6951b4..2838de031c 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -3,7 +3,6 @@ #ifndef CONFIG_USER_ONLY -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -102,48 +101,63 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } -static inline int ppc_hash32_pp_prot(bool key, int pp, bool nx) +static inline hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) +{ + return (hash * HASH_PTEG_SIZE_32) & ppc_hash32_hpt_mask(cpu); +} + +static inline bool ppc_hash32_key(bool pr, target_ulong sr) +{ + return pr ? (sr & SR32_KP) : (sr & SR32_KS); +} + +static inline int ppc_hash32_prot(bool key, int pp, bool nx) { int prot; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - case 0x3: - prot = PAGE_READ; - break; - - default: - abort(); - } - } else { + if (key) { switch (pp) { case 0x0: prot = 0; break; - case 0x1: case 0x3: prot = PAGE_READ; break; - case 0x2: prot = PAGE_READ | PAGE_WRITE; break; - default: - abort(); + g_assert_not_reached(); + } + } else { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + case 0x3: + prot = PAGE_READ; + break; + default: + g_assert_not_reached(); } } - if (nx == 0) { - prot |= PAGE_EXEC; - } + return nx ? prot : prot | PAGE_EXEC; +} +static inline int ppc_hash32_bat_prot(target_ulong batu, target_ulong batl) +{ + int prot = 0; + int pp = batl & BATL32_PP; + + if (pp) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } return prot; } diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index cbc8efa0c3..5e1983e334 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -31,6 +31,7 @@ #include "hw/hw.h" #include "internal.h" #include "mmu-book3s-v3.h" +#include "mmu-books.h" #include "helper_regs.h" #ifdef CONFIG_TCG @@ -508,6 +509,46 @@ static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) return prot; } +static hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return 0; + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return base & SDR_64_HTABORG; +} + +static hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return cpu->vhyp_class->hpt_mask(cpu->vhyp); + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; +} + const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n) { @@ -545,6 +586,15 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, false, n * HASH_PTE_SIZE_64); } +bool ppc_hash64_valid_ptex(PowerPCCPU *cpu, target_ulong ptex) +{ + /* hash value/pteg group index is normalized by HPT mask */ + if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { + return false; + } + return true; +} + static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps, uint64_t pte0, uint64_t pte1) { diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index de653fcae5..ae8d4b37ae 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -120,6 +120,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n); void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n); +bool ppc_hash64_valid_ptex(PowerPCCPU *cpu, target_ulong ptex); static inline uint64_t ppc_hash64_hpte0(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, int i) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 5a02e4963b..be7a45f254 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -28,6 +28,38 @@ #include "internal.h" #include "mmu-radix64.h" #include "mmu-book3s-v3.h" +#include "mmu-books.h" + +/* Radix Partition Table Entry Fields */ +#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 +#define PATE1_R_PRTS 0x000000000000001F + +/* Radix Process Table Entry Fields */ +#define PRTBE_R_GET_RTS(rts) \ + ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31) +#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00 +#define PRTBE_R_RPDS 0x000000000000001F + +/* Radix Page Directory/Table Entry Fields */ +#define R_PTE_VALID 0x8000000000000000 +#define R_PTE_LEAF 0x4000000000000000 +#define R_PTE_SW0 0x2000000000000000 +#define R_PTE_RPN 0x01FFFFFFFFFFF000 +#define R_PTE_SW1 0x0000000000000E00 +#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7)) +#define R_PTE_R 0x0000000000000100 +#define R_PTE_C 0x0000000000000080 +#define R_PTE_ATT 0x0000000000000030 +#define R_PTE_ATT_NORMAL 0x0000000000000000 +#define R_PTE_ATT_SAO 0x0000000000000010 +#define R_PTE_ATT_NI_IO 0x0000000000000020 +#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030 +#define R_PTE_EAA_PRIV 0x0000000000000008 +#define R_PTE_EAA_R 0x0000000000000004 +#define R_PTE_EAA_RW 0x0000000000000002 +#define R_PTE_EAA_X 0x0000000000000001 +#define R_PDE_NLB PRTBE_R_RPDB +#define R_PDE_NLS PRTBE_R_RPDS static bool ppc_radix64_get_fully_qualified_addr(const CPUPPCState *env, vaddr eaddr, @@ -180,6 +212,24 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, } } +static int ppc_radix64_get_prot_eaa(uint64_t pte) +{ + return (pte & R_PTE_EAA_R ? PAGE_READ : 0) | + (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) | + (pte & R_PTE_EAA_X ? PAGE_EXEC : 0); +} + +static int ppc_radix64_get_prot_amr(const PowerPCCPU *cpu) +{ + const CPUPPCState *env = &cpu->env; + int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */ + int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */ + + return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */ + (amr & 0x1 ? 0 : PAGE_READ) | + (iamr & 0x1 ? 0 : PAGE_EXEC); +} + static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, uint64_t pte, int *fault_cause, int *prot, int mmu_idx, bool partition_scoped) diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index c5c04a1527..6620b3d648 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -3,7 +3,7 @@ #ifndef CONFIG_USER_ONLY -#include "exec/page-protection.h" +#ifdef TARGET_PPC64 /* Radix Quadrants */ #define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF @@ -14,61 +14,10 @@ #define R_EADDR_QUADRANT2 0x8000000000000000 #define R_EADDR_QUADRANT3 0xC000000000000000 -/* Radix Partition Table Entry Fields */ -#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 -#define PATE1_R_PRTS 0x000000000000001F - -/* Radix Process Table Entry Fields */ -#define PRTBE_R_GET_RTS(rts) \ - ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31) -#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00 -#define PRTBE_R_RPDS 0x000000000000001F - -/* Radix Page Directory/Table Entry Fields */ -#define R_PTE_VALID 0x8000000000000000 -#define R_PTE_LEAF 0x4000000000000000 -#define R_PTE_SW0 0x2000000000000000 -#define R_PTE_RPN 0x01FFFFFFFFFFF000 -#define R_PTE_SW1 0x0000000000000E00 -#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7)) -#define R_PTE_R 0x0000000000000100 -#define R_PTE_C 0x0000000000000080 -#define R_PTE_ATT 0x0000000000000030 -#define R_PTE_ATT_NORMAL 0x0000000000000000 -#define R_PTE_ATT_SAO 0x0000000000000010 -#define R_PTE_ATT_NI_IO 0x0000000000000020 -#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030 -#define R_PTE_EAA_PRIV 0x0000000000000008 -#define R_PTE_EAA_R 0x0000000000000004 -#define R_PTE_EAA_RW 0x0000000000000002 -#define R_PTE_EAA_X 0x0000000000000001 -#define R_PDE_NLB PRTBE_R_RPDB -#define R_PDE_NLS PRTBE_R_RPDS - -#ifdef TARGET_PPC64 - bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddr, int *psizep, int *protp, int mmu_idx, bool guest_visible); -static inline int ppc_radix64_get_prot_eaa(uint64_t pte) -{ - return (pte & R_PTE_EAA_R ? PAGE_READ : 0) | - (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) | - (pte & R_PTE_EAA_X ? PAGE_EXEC : 0); -} - -static inline int ppc_radix64_get_prot_amr(const PowerPCCPU *cpu) -{ - const CPUPPCState *env = &cpu->env; - int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */ - int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */ - - return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */ - (amr & 0x1 ? 0 : PAGE_READ) | - (iamr & 0x1 ? 0 : PAGE_EXEC); -} - #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index e2542694f0..60f8736210 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -37,17 +37,6 @@ /* #define DUMP_PAGE_TABLES */ -/* Context used internally during MMU translations */ -typedef struct { - hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - int nx; /* Non-execute area */ -} mmu_ctx_t; - void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); @@ -94,86 +83,23 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, return nr; } -static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, - MMUAccessType access_type) -{ - target_ulong ptem, mmask; - int ret, pteh, ptev, pp; - - ret = -1; - /* Check validity and table match */ - ptev = pte_is_valid(pte0); - pteh = (pte0 >> 6) & 1; - if (ptev && h == pteh) { - /* Check vsid & api */ - ptem = pte0 & PTE_PTEM_MASK; - mmask = PTE_CHECK_MASK; - pp = pte1 & 0x00000003; - if (ptem == ctx->ptem) { - if (ctx->raddr != (hwaddr)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Keep the matching PTE information */ - ctx->raddr = pte1; - ctx->prot = ppc_hash32_pp_prot(ctx->key, pp, ctx->nx); - if (check_prot_access_type(ctx->prot, access_type)) { - /* Access granted */ - qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); - ret = 0; - } else { - /* Access right violation */ - qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - ret = -2; - } - } - } - - return ret; -} - -static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, MMUAccessType access_type) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & 0x00000100)) { - /* Update accessed flag */ - *pte1p |= 0x00000100; - store = 1; - } - if (!(*pte1p & 0x00000080)) { - if (access_type == MMU_DATA_STORE && ret == 0) { - /* Update changed flag */ - *pte1p |= 0x00000080; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - /* Software driven TLB helpers */ -static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type) +static int ppc6xx_tlb_check(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong eaddr, MMUAccessType access_type, + target_ulong ptem, bool key, bool nx) { ppc6xx_tlb_t *tlb; - int nr, best, way; - int ret; + target_ulong *pte1p; + int nr, best, way, ret; + bool is_code = (access_type == MMU_INST_FETCH); + /* Initialize real address with an invalid value */ + *raddr = (hwaddr)-1ULL; best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == MMU_INST_FETCH); + nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); tlb = &env->tlb.tlb6[nr]; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { @@ -191,37 +117,51 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, tlb->EPN, eaddr, tlb->pte1, access_type == MMU_DATA_STORE ? 'S' : 'L', access_type == MMU_INST_FETCH ? 'I' : 'D'); - switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, - 0, access_type)) { - case -2: - /* Access violation */ - ret = -2; - best = nr; - break; - case -1: /* No match */ - case -3: /* TLB inconsistency */ - default: - break; - case 0: - /* access granted */ - /* - * XXX: we should go on looping to check all TLBs - * consistency but we can speed-up the whole thing as - * the result would be undefined if TLBs are not - * consistent. - */ + /* Check validity and table match */ + if (!pte_is_valid(tlb->pte0) || ((tlb->pte0 >> 6) & 1) != 0 || + (tlb->pte0 & PTE_PTEM_MASK) != ptem) { + continue; + } + /* all matches should have equal RPN, WIMG & PP */ + if (*raddr != (hwaddr)-1ULL && + (*raddr & PTE_CHECK_MASK) != (tlb->pte1 & PTE_CHECK_MASK)) { + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); + /* TLB inconsistency */ + continue; + } + /* Keep the matching PTE information */ + best = nr; + *raddr = tlb->pte1; + *prot = ppc_hash32_prot(key, tlb->pte1 & HPTE32_R_PP, nx); + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); ret = 0; - best = nr; - goto done; + break; + } else { + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + ret = -2; } } if (best != -1) { -done: qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " HWADDR_FMT_plx " prot=%01x ret=%d\n", - ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); + *raddr & TARGET_PAGE_MASK, *prot, ret); /* Update page flags */ - pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); + pte1p = &env->tlb.tlb6[best].pte1; + *pte1p |= 0x00000100; /* Update accessed flag */ + if (!(*pte1p & 0x00000080)) { + if (access_type == MMU_DATA_STORE && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + } else { + /* Force page fault for first write access */ + *prot &= ~PAGE_WRITE; + } + } + } + if (ret == -1) { + int r = is_code ? SPR_ICMP : SPR_DCMP; + env->spr[r] = ptem; } #if defined(DUMP_PAGE_TABLES) if (qemu_loglevel_mask(CPU_LOG_MMU)) { @@ -247,44 +187,17 @@ done: return ret; } -/* Perform BAT hit & translation */ -static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, target_ulong *BATu, - target_ulong *BATl) -{ - target_ulong bl; - int pp, valid, prot; - - bl = (*BATu & 0x00001FFC) << 15; - valid = 0; - prot = 0; - if ((!FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000002)) || - (FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000001))) { - valid = 1; - pp = *BATl & 0x00000003; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } - } - *blp = bl; - *validp = valid; - *protp = prot; -} - -static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, MMUAccessType access_type) +static int get_bat_6xx_tlb(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong eaddr, MMUAccessType access_type, + bool pr) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; - int i, valid, prot; - int ret = -1; + int i, ret = -1; bool ifetch = access_type == MMU_INST_FETCH; qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', virtual); + ifetch ? 'I' : 'D', eaddr); if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; @@ -295,27 +208,26 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); - if ((virtual & 0xF0000000) == BEPIu && - ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { - /* BAT matches */ - if (valid != 0) { + ifetch ? 'I' : 'D', i, eaddr, *BATu, *BATl); + bl = (*BATu & BATU32_BL) << 15; + if ((!pr && (*BATu & BATU32_VS)) || (pr && (*BATu & BATU32_VP))) { + if ((eaddr & BATU32_BEPIU) == BEPIu && + ((eaddr & BATU32_BEPIL) & ~bl) == BEPIl) { /* Get physical address */ - ctx->raddr = (*BATl & 0xF0000000) | - ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | - (virtual & 0x0001F000); + *raddr = (*BATl & BATU32_BEPIU) | + ((eaddr & BATU32_BEPIL & bl) | (*BATl & BATU32_BEPIL)) | + (eaddr & 0x0001F000); /* Compute access rights */ - ctx->prot = prot; - if (check_prot_access_type(ctx->prot, access_type)) { + *prot = ppc_hash32_bat_prot(*BATu, *BATl); + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx - " prot=%c%c\n", i, ctx->raddr, - ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); + " prot=%c%c\n", i, *raddr, + *prot & PAGE_READ ? 'R' : '-', + *prot & PAGE_WRITE ? 'W' : '-'); ret = 0; } else { ret = -2; @@ -327,18 +239,18 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, if (ret < 0) { if (qemu_log_enabled()) { qemu_log_mask(CPU_LOG_MMU, "no BAT match for " - TARGET_FMT_lx ":\n", virtual); + TARGET_FMT_lx ":\n", eaddr); for (i = 0; i < 4; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & BATU32_BL) << 15; qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', - i, virtual, *BATu, *BATl, BEPIu, BEPIl, bl); + i, eaddr, *BATu, *BATl, BEPIu, BEPIl, bl); } } } @@ -346,32 +258,30 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, +static int mmu6xx_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong eaddr, + hwaddr *hashp, bool *keyp, MMUAccessType access_type, int type) { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; - target_ulong vsid, sr, pgidx; - int ds, target_page_bits; - bool pr; + target_ulong vsid, sr, pgidx, ptem; + bool key, ds, nx; + bool pr = FIELD_EX64(env->msr, MSR, PR); /* First try to find a BAT entry if there are any */ - if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { + if (env->nb_BATs && + get_bat_6xx_tlb(env, raddr, prot, eaddr, access_type, pr) == 0) { return 0; } /* Perform segment based translation when no BATs matched */ - pr = FIELD_EX64(env->msr, MSR, PR); - ctx->eaddr = eaddr; - sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && pr) || - ((sr & 0x40000000) && !pr)) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; - target_page_bits = TARGET_PAGE_BITS; + key = ppc_hash32_key(pr, sr); + *keyp = key; + ds = sr & SR32_T; + nx = sr & SR32_NX; + vsid = sr & SR32_VSID; qemu_log_mask(CPU_LOG_MMU, "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx @@ -380,15 +290,15 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, (int)FIELD_EX64(env->msr, MSR, IR), (int)FIELD_EX64(env->msr, MSR, DR), pr ? 1 : 0, access_type == MMU_DATA_STORE, type); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); + ptem = (vsid << 7) | (pgidx >> 10); /* Virtual segment ID | API */ qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " - TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); + TARGET_FMT_lx "\n", key, ds, nx, vsid); if (!ds) { /* Check if instruction fetch is allowed, if needed */ - if (type == ACCESS_CODE && ctx->nx) { + if (type == ACCESS_CODE && nx) { qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); return -3; } @@ -396,13 +306,11 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx " htab_mask " HWADDR_FMT_plx " hash " HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; + *hashp = hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ - return ppc6xx_tlb_check(env, ctx, eaddr, access_type); + return ppc6xx_tlb_check(env, raddr, prot, eaddr, + access_type, ptem, key, nx); } /* Direct-store segment : absolutely *BUGGY* for now */ @@ -411,15 +319,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, case ACCESS_INT: /* Integer load/store : only access allowed */ break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; case ACCESS_CACHE: /* * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi @@ -427,19 +326,17 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, * Should make the instruction do no-op. As it already do * no-op, it's quite easy :-) */ - ctx->raddr = eaddr; + *raddr = eaddr; return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need address" - " translation\n"); + case ACCESS_CODE: /* No code fetch is allowed in direct-store areas */ + case ACCESS_FLOAT: /* Floating point load/store */ + case ACCESS_RES: /* lwarx, ldarx or srwcx. */ + case ACCESS_EXT: /* eciwx or ecowx */ return -4; } - if ((access_type == MMU_DATA_STORE || ctx->key != 1) && - (access_type == MMU_DATA_LOAD || ctx->key != 0)) { - ctx->raddr = eaddr; + if ((access_type == MMU_DATA_STORE || !key) && + (access_type == MMU_DATA_LOAD || key)) { + *raddr = eaddr; return 2; } return -2; @@ -589,9 +486,9 @@ static void mmu6xx_dump_BATs(CPUPPCState *env, int type) for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & BATU32_BL) << 15; qemu_printf("%s BAT%d BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", @@ -777,9 +674,9 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - mmu_ctx_t ctx; - int type; - int ret; + hwaddr hash = 0; /* init to 0 to avoid used uninit warning */ + bool key; + int type, ret; if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { return true; @@ -795,13 +692,9 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, type = ACCESS_INT; } - ctx.prot = 0; - ctx.hash[0] = 0; - ctx.hash[1] = 0; - ret = mmu6xx_get_physical_address(env, &ctx, eaddr, access_type, type); + ret = mmu6xx_get_physical_address(env, raddrp, protp, eaddr, &hash, &key, + access_type, type); if (ret == 0) { - *raddrp = ctx.raddr; - *protp = ctx.prot; *psizep = TARGET_PAGE_BITS; return true; } else if (!guest_visible) { @@ -816,7 +709,7 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, cs->exception_index = POWERPC_EXCP_IFTLB; env->error_code = 1 << 18; env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + env->spr[SPR_ICMP] |= 0x80000000; goto tlb_miss; case -2: /* Access rights violation */ @@ -847,13 +740,13 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, env->error_code = 0; } env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + env->spr[SPR_DCMP] |= 0x80000000; tlb_miss: - env->error_code |= ctx.key << 19; + env->error_code |= key << 19; env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); + get_pteg_offset32(cpu, hash); env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); + get_pteg_offset32(cpu, ~hash); break; case -2: /* Access rights violation */ diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 39d397416e..73120323b4 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -62,9 +62,8 @@ void helper_store_purr(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_purr(env, val); return; } @@ -81,9 +80,8 @@ void helper_store_tbl(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_tbl(env, val); return; } @@ -98,9 +96,8 @@ void helper_store_tbu(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_tbu(env, val); return; } @@ -140,9 +137,8 @@ void helper_store_hdecr(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_hdecr(env, val); return; } @@ -157,9 +153,8 @@ void helper_store_vtb(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_vtb(env, val); return; } @@ -174,9 +169,8 @@ void helper_store_tbu40(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_tbu40(env, val); return; } @@ -217,7 +211,14 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) store_booke_tsr(env, val); } -#if defined(TARGET_PPC64) +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +/* + * qemu-user breaks with pnv headers, so they go under ifdefs for now. + * A clean up may be to move powernv specific registers and helpers into + * target/ppc/pnv_helper.c + */ +#include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_chip.h" /* * POWER processor Timebase Facility */ @@ -287,7 +288,7 @@ static void write_tfmr(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); - if (cs->nr_threads == 1) { + if (ppc_cpu_core_single_threaded(cs)) { env->spr[SPR_TFMR] = val; } else { CPUState *ccs; @@ -298,8 +299,25 @@ static void write_tfmr(CPUPPCState *env, target_ulong val) } } +static PnvCoreTODState *cpu_get_tbst(PowerPCCPU *cpu) +{ + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + + if (pc->big_core && pc->tod_state.big_core_quirk) { + /* Must operate on the even small core */ + int core_id = CPU_CORE(pc)->core_id; + if (core_id & 1) { + pc = pc->chip->cores[core_id & ~1]; + } + } + + return &pc->tod_state; +} + static void tb_state_machine_step(CPUPPCState *env) { + PowerPCCPU *cpu = env_archcpu(env); + PnvCoreTODState *tod_state = cpu_get_tbst(cpu); uint64_t tfmr = env->spr[SPR_TFMR]; unsigned int tbst = tfmr_get_tb_state(tfmr); @@ -307,15 +325,15 @@ static void tb_state_machine_step(CPUPPCState *env) return; } - if (env->pnv_tod_tbst.tb_sync_pulse_timer) { - env->pnv_tod_tbst.tb_sync_pulse_timer--; + if (tod_state->tb_sync_pulse_timer) { + tod_state->tb_sync_pulse_timer--; } else { tfmr |= TFMR_TB_SYNC_OCCURED; write_tfmr(env, tfmr); } - if (env->pnv_tod_tbst.tb_state_timer) { - env->pnv_tod_tbst.tb_state_timer--; + if (tod_state->tb_state_timer) { + tod_state->tb_state_timer--; return; } @@ -332,20 +350,20 @@ static void tb_state_machine_step(CPUPPCState *env) } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { if (tbst == TBST_SYNC_WAIT) { tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD); - env->pnv_tod_tbst.tb_state_timer = 3; + tod_state->tb_state_timer = 3; } else if (tbst == TBST_GET_TOD) { - if (env->pnv_tod_tbst.tod_sent_to_tb) { + if (tod_state->tod_sent_to_tb) { tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING); tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; - env->pnv_tod_tbst.tb_ready_for_tod = 0; - env->pnv_tod_tbst.tod_sent_to_tb = 0; + tod_state->tb_ready_for_tod = 0; + tod_state->tod_sent_to_tb = 0; } } else { qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " "state machine in invalid state 0x%x\n", tbst); tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; - env->pnv_tod_tbst.tb_ready_for_tod = 0; + tod_state->tb_ready_for_tod = 0; } } @@ -361,6 +379,8 @@ target_ulong helper_load_tfmr(CPUPPCState *env) void helper_store_tfmr(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = env_archcpu(env); + PnvCoreTODState *tod_state = cpu_get_tbst(cpu); uint64_t tfmr = env->spr[SPR_TFMR]; uint64_t clear_on_write; unsigned int tbst = tfmr_get_tb_state(tfmr); @@ -384,14 +404,7 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) * after the second mfspr. */ tfmr &= ~TFMR_TB_SYNC_OCCURED; - env->pnv_tod_tbst.tb_sync_pulse_timer = 1; - - if (ppc_cpu_tir(env_archcpu(env)) != 0 && - (val & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB))) { - qemu_log_mask(LOG_UNIMP, "TFMR timebase state machine can only be " - "driven by thread 0\n"); - goto out; - } + tod_state->tb_sync_pulse_timer = 1; if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) == (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) { @@ -399,7 +412,7 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) "MOVE_CHIP_TOD_TO_TB both set\n"); tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; - env->pnv_tod_tbst.tb_ready_for_tod = 0; + tod_state->tb_ready_for_tod = 0; goto out; } @@ -413,8 +426,8 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) tfmr &= ~TFMR_LOAD_TOD_MOD; tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; tfmr &= ~TFMR_FIRMWARE_CONTROL_ERROR; /* XXX: should this be cleared? */ - env->pnv_tod_tbst.tb_ready_for_tod = 0; - env->pnv_tod_tbst.tod_sent_to_tb = 0; + tod_state->tb_ready_for_tod = 0; + tod_state->tod_sent_to_tb = 0; goto out; } @@ -427,19 +440,19 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) if (tfmr & TFMR_LOAD_TOD_MOD) { /* Wait for an arbitrary 3 mfspr until the next state transition. */ - env->pnv_tod_tbst.tb_state_timer = 3; + tod_state->tb_state_timer = 3; } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { if (tbst == TBST_NOT_SET) { tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT); - env->pnv_tod_tbst.tb_ready_for_tod = 1; - env->pnv_tod_tbst.tb_state_timer = 3; /* arbitrary */ + tod_state->tb_ready_for_tod = 1; + tod_state->tb_state_timer = 3; /* arbitrary */ } else { qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " "not in TB not set state 0x%x\n", tbst); tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; - env->pnv_tod_tbst.tb_ready_for_tod = 0; + tod_state->tb_ready_for_tod = 0; } } diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cba943a49d..71513ba964 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2543,6 +2543,7 @@ static inline void gen_align_no_le(DisasContext *ctx) (ctx->opcode & 0x03FF0000) | POWERPC_EXCP_ALIGN_LE); } +/* EA <- {(ra == 0) ? 0 : GPR[ra]} + displ */ static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) { TCGv ea = tcg_temp_new(); @@ -2557,6 +2558,22 @@ static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) return ea; } +#if defined(TARGET_PPC64) +/* EA <- (ra == 0) ? 0 : GPR[ra] */ +static TCGv do_ea_calc_ra(DisasContext *ctx, int ra) +{ + TCGv EA = tcg_temp_new(); + if (!ra) { + tcg_gen_movi_tl(EA, 0); + } else if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[ra]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[ra]); + } + return EA; +} +#endif + /*** Integer load ***/ #define DEF_MEMOP(op) ((op) | ctx->default_tcg_memop_mask) #define BSWAP_MEMOP(op) ((op) | (ctx->default_tcg_memop_mask ^ MO_BSWAP)) @@ -5541,16 +5558,6 @@ static inline void set_fpr(int regno, TCGv_i64 src) tcg_gen_st_i64(tcg_constant_i64(0), tcg_env, vsr64_offset(regno, false)); } -static inline void get_avr64(TCGv_i64 dst, int regno, bool high) -{ - tcg_gen_ld_i64(dst, tcg_env, avr64_offset(regno, high)); -} - -static inline void set_avr64(int regno, TCGv_i64 src, bool high) -{ - tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); -} - /* * Helpers for decodetree used by !function for decoding arguments. */ diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 8084af75cc..70d0ad2e71 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -14,25 +14,39 @@ static inline TCGv_ptr gen_avr_ptr(int reg) return r; } +static inline void get_avr64(TCGv_i64 dst, int regno, bool high) +{ + tcg_gen_ld_i64(dst, tcg_env, avr64_offset(regno, high)); +} + +static inline void set_avr64(int regno, TCGv_i64 src, bool high) +{ + tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); +} + +static inline void get_avr_full(TCGv_i128 dst, int regno) +{ + tcg_gen_ld_i128(dst, tcg_env, avr_full_offset(regno)); +} + +static inline void set_avr_full(int regno, TCGv_i128 src) +{ + tcg_gen_st_i128(src, tcg_env, avr_full_offset(regno)); +} + static bool trans_LVX(DisasContext *ctx, arg_X *a) { TCGv EA; - TCGv_i64 avr; + TCGv_i128 avr; REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); REQUIRE_VECTOR(ctx); gen_set_access_type(ctx, ACCESS_INT); - avr = tcg_temp_new_i64(); + avr = tcg_temp_new_i128(); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_andi_tl(EA, EA, ~0xf); - /* - * We only need to swap high and low halves. gen_qemu_ld64_i64 - * does necessary 64-bit byteswap already. - */ - gen_qemu_ld64_i64(ctx, avr, EA); - set_avr64(a->rt, avr, !ctx->le_mode); - tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, avr, EA); - set_avr64(a->rt, avr, ctx->le_mode); + tcg_gen_qemu_ld_i128(avr, EA, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR)); + set_avr_full(a->rt, avr); return true; } @@ -46,22 +60,16 @@ static bool trans_LVXL(DisasContext *ctx, arg_LVXL *a) static bool trans_STVX(DisasContext *ctx, arg_STVX *a) { TCGv EA; - TCGv_i64 avr; + TCGv_i128 avr; REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); REQUIRE_VECTOR(ctx); gen_set_access_type(ctx, ACCESS_INT); - avr = tcg_temp_new_i64(); + avr = tcg_temp_new_i128(); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_andi_tl(EA, EA, ~0xf); - /* - * We only need to swap high and low halves. gen_qemu_st64_i64 - * does necessary 64-bit byteswap already. - */ - get_avr64(avr, a->rt, !ctx->le_mode); - gen_qemu_st64_i64(ctx, avr, EA); - tcg_gen_addi_tl(EA, EA, 8); - get_avr64(avr, a->rt, ctx->le_mode); - gen_qemu_st64_i64(ctx, avr, EA); + get_avr_full(avr, a->rt); + tcg_gen_qemu_st_i128(avr, EA, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR)); return true; } @@ -1047,58 +1055,6 @@ TRANS(VRLQ, do_vector_rotl_quad, false, false) TRANS(VRLQNM, do_vector_rotl_quad, true, false) TRANS(VRLQMI, do_vector_rotl_quad, false, true) -#define GEN_VXFORM_SAT(NAME, VECE, NORM, SAT, OPC2, OPC3) \ -static void glue(glue(gen_, NAME), _vec)(unsigned vece, TCGv_vec t, \ - TCGv_vec sat, TCGv_vec a, \ - TCGv_vec b) \ -{ \ - TCGv_vec x = tcg_temp_new_vec_matching(t); \ - glue(glue(tcg_gen_, NORM), _vec)(VECE, x, a, b); \ - glue(glue(tcg_gen_, SAT), _vec)(VECE, t, a, b); \ - tcg_gen_cmp_vec(TCG_COND_NE, VECE, x, x, t); \ - tcg_gen_or_vec(VECE, sat, sat, x); \ -} \ -static void glue(gen_, NAME)(DisasContext *ctx) \ -{ \ - static const TCGOpcode vecop_list[] = { \ - glue(glue(INDEX_op_, NORM), _vec), \ - glue(glue(INDEX_op_, SAT), _vec), \ - INDEX_op_cmp_vec, 0 \ - }; \ - static const GVecGen4 g = { \ - .fniv = glue(glue(gen_, NAME), _vec), \ - .fno = glue(gen_helper_, NAME), \ - .opt_opc = vecop_list, \ - .write_aofs = true, \ - .vece = VECE, \ - }; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - tcg_gen_gvec_4(avr_full_offset(rD(ctx->opcode)), \ - offsetof(CPUPPCState, vscr_sat), \ - avr_full_offset(rA(ctx->opcode)), \ - avr_full_offset(rB(ctx->opcode)), \ - 16, 16, &g); \ -} - -GEN_VXFORM_SAT(vaddubs, MO_8, add, usadd, 0, 8); -GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ - vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) -GEN_VXFORM_SAT(vadduhs, MO_16, add, usadd, 0, 9); -GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ - vmul10euq, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_SAT(vadduws, MO_32, add, usadd, 0, 10); -GEN_VXFORM_SAT(vaddsbs, MO_8, add, ssadd, 0, 12); -GEN_VXFORM_SAT(vaddshs, MO_16, add, ssadd, 0, 13); -GEN_VXFORM_SAT(vaddsws, MO_32, add, ssadd, 0, 14); -GEN_VXFORM_SAT(vsububs, MO_8, sub, ussub, 0, 24); -GEN_VXFORM_SAT(vsubuhs, MO_16, sub, ussub, 0, 25); -GEN_VXFORM_SAT(vsubuws, MO_32, sub, ussub, 0, 26); -GEN_VXFORM_SAT(vsubsbs, MO_8, sub, sssub, 0, 28); -GEN_VXFORM_SAT(vsubshs, MO_16, sub, sssub, 0, 29); -GEN_VXFORM_SAT(vsubsws, MO_32, sub, sssub, 0, 30); GEN_VXFORM_TRANS(vsl, 2, 7); GEN_VXFORM_TRANS(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); @@ -2641,26 +2597,14 @@ static void gen_xpnd04_2(DisasContext *ctx) } } - -GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \ - xpnd04_2, PPC_NONE, PPC2_ISA300) - GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \ bcdadd, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vsububs, PPC_ALTIVEC, PPC_NONE, \ - bcdadd, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ bcdsub, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ - bcdsub, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vaddshs, PPC_ALTIVEC, PPC_NONE, \ - bcdcpsgn, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubudm, PPC2_ALTIVEC_207, PPC_NONE, \ bcds, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \ bcdus, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \ - bcdtrunc, PPC_NONE, PPC2_ISA300) static void gen_vsbox(DisasContext *ctx) { @@ -2937,6 +2881,180 @@ static bool do_vx_vaddsubcuw(DisasContext *ctx, arg_VX *a, int add) TRANS(VSUBCUW, do_vx_vaddsubcuw, 0) TRANS(VADDCUW, do_vx_vaddsubcuw, 1) +/* Integer Add/Sub Saturate Instructions */ +static inline void do_vadd_vsub_sat +( + unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b, + void (*norm_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec), + void (*sat_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + norm_op(vece, x, a, b); + sat_op(vece, t, a, b); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); +} + +static void gen_vadd_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_add_vec, tcg_gen_usadd_vec); +} + +static void gen_vadd_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_add_vec, tcg_gen_ssadd_vec); +} + +static void gen_vsub_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_sub_vec, tcg_gen_ussub_vec); +} + +static void gen_vsub_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_sub_vec, tcg_gen_sssub_vec); +} + +/* + * Signed/Unsigned add/sub helper ops for byte/halfword/word + * GVecGen4 struct variants. + */ +static const TCGOpcode vecop_list_sub_u[] = { + INDEX_op_sub_vec, INDEX_op_ussub_vec, 0 +}; +static const TCGOpcode vecop_list_sub_s[] = { + INDEX_op_sub_vec, INDEX_op_sssub_vec, 0 +}; +static const TCGOpcode vecop_list_add_u[] = { + INDEX_op_add_vec, INDEX_op_usadd_vec, 0 +}; +static const TCGOpcode vecop_list_add_s[] = { + INDEX_op_add_vec, INDEX_op_ssadd_vec, 0 +}; + +static const GVecGen4 op_vsububs = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUBS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vaddubs = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUBS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vsubuhs = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUHS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vadduhs = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUHS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vsubuws = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUWS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vadduws = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUWS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vsubsbs = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSBS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vaddsbs = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSBS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vsubshs = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSHS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vaddshs = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSHS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vsubsws = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSWS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vaddsws = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSWS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_32 +}; + +static bool do_vx_vadd_vsub_sat(DisasContext *ctx, arg_VX *a, const GVecGen4 *op) +{ + REQUIRE_VECTOR(ctx); + tcg_gen_gvec_4(avr_full_offset(a->vrt), offsetof(CPUPPCState, vscr_sat), + avr_full_offset(a->vra), avr_full_offset(a->vrb), + 16, 16, op); + + return true; +} + +TRANS_FLAGS(ALTIVEC, VSUBUBS, do_vx_vadd_vsub_sat, &op_vsububs) +TRANS_FLAGS(ALTIVEC, VSUBUHS, do_vx_vadd_vsub_sat, &op_vsubuhs) +TRANS_FLAGS(ALTIVEC, VSUBUWS, do_vx_vadd_vsub_sat, &op_vsubuws) +TRANS_FLAGS(ALTIVEC, VSUBSBS, do_vx_vadd_vsub_sat, &op_vsubsbs) +TRANS_FLAGS(ALTIVEC, VSUBSHS, do_vx_vadd_vsub_sat, &op_vsubshs) +TRANS_FLAGS(ALTIVEC, VSUBSWS, do_vx_vadd_vsub_sat, &op_vsubsws) +TRANS_FLAGS(ALTIVEC, VADDUBS, do_vx_vadd_vsub_sat, &op_vaddubs) +TRANS_FLAGS(ALTIVEC, VADDUHS, do_vx_vadd_vsub_sat, &op_vadduhs) +TRANS_FLAGS(ALTIVEC, VADDUWS, do_vx_vadd_vsub_sat, &op_vadduws) +TRANS_FLAGS(ALTIVEC, VADDSBS, do_vx_vadd_vsub_sat, &op_vaddsbs) +TRANS_FLAGS(ALTIVEC, VADDSHS, do_vx_vadd_vsub_sat, &op_vaddshs) +TRANS_FLAGS(ALTIVEC, VADDSWS, do_vx_vadd_vsub_sat, &op_vaddsws) + static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 7bb11b0549..e28958a126 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -54,18 +54,13 @@ GEN_VXFORM(vsro, 6, 17), GEN_VXFORM(xpnd04_1, 0, 22), GEN_VXFORM_300(bcdsr, 0, 23), GEN_VXFORM_300(bcdsr, 0, 31), -GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vadduws, 0, 10), -GEN_VXFORM(vaddsbs, 0, 12), -GEN_VXFORM_DUAL(vaddshs, bcdcpsgn, 0, 13, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vaddsws, 0, 14), -GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vsubuws, 0, 26), -GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_ALTIVEC, PPC2_ISA300), -GEN_VXFORM(vsubshs, 0, 29), -GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_300_EXT(vmul10uq, 0, 8, 0x0000F800), +GEN_VXFORM_300(vmul10euq, 0, 9), +GEN_VXFORM_300(bcdcpsgn, 0, 13), +GEN_VXFORM_207(bcdadd, 0, 24), +GEN_VXFORM_207(bcdsub, 0, 25), +GEN_VXFORM_300(bcdtrunc, 0, 28), +GEN_VXFORM_300(xpnd04_2, 0, 30), GEN_VXFORM_300(bcdtrunc, 0, 20), GEN_VXFORM_300(bcdutrunc, 0, 21), GEN_VXFORM(vsl, 2, 7), diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 0266f09119..40a87ddc4a 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -10,6 +10,16 @@ static inline void set_cpu_vsr(int n, TCGv_i64 src, bool high) tcg_gen_st_i64(src, tcg_env, vsr64_offset(n, high)); } +static inline void get_vsr_full(TCGv_i128 dst, int reg) +{ + tcg_gen_ld_i128(dst, tcg_env, vsr_full_offset(reg)); +} + +static inline void set_vsr_full(int reg, TCGv_i128 src) +{ + tcg_gen_st_i128(src, tcg_env, vsr_full_offset(reg)); +} + static inline TCGv_ptr gen_vsr_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); @@ -24,66 +34,59 @@ static inline TCGv_ptr gen_acc_ptr(int reg) return r; } -#define VSX_LOAD_SCALAR(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - gen_qemu_##operation(ctx, t0, EA); \ - set_cpu_vsr(xT(ctx->opcode), t0, true); \ - /* NOTE: cpu_vsrl is undefined */ \ -} - -VSX_LOAD_SCALAR(lxsdx, ld64_i64) -VSX_LOAD_SCALAR(lxsiwax, ld32s_i64) -VSX_LOAD_SCALAR(lxsibzx, ld8u_i64) -VSX_LOAD_SCALAR(lxsihzx, ld16u_i64) -VSX_LOAD_SCALAR(lxsiwzx, ld32u_i64) -VSX_LOAD_SCALAR(lxsspx, ld32fs) - -static void gen_lxvd2x(DisasContext *ctx) +static bool do_lxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, true); - tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, false); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + op(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, true); + /* NOTE: cpu_vsrl is undefined */ + return true; } -static void gen_lxvw4x(DisasContext *ctx) +TRANS_FLAGS2(VSX, LXSDX, do_lxs, gen_qemu_ld64_i64); +TRANS_FLAGS2(VSX207, LXSIWAX, do_lxs, gen_qemu_ld32s_i64); +TRANS_FLAGS2(ISA300, LXSIBZX, do_lxs, gen_qemu_ld8u_i64); +TRANS_FLAGS2(ISA300, LXSIHZX, do_lxs, gen_qemu_ld16u_i64); +TRANS_FLAGS2(VSX207, LXSIWZX, do_lxs, gen_qemu_ld32u_i64); +TRANS_FLAGS2(VSX207, LXSSPX, do_lxs, gen_qemu_ld32fs); + +static bool trans_LXVD2X(DisasContext *ctx, arg_LXVD2X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + TCGv_i64 t0; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, true); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, false); + return true; +} + +static bool trans_LXVW4X(DisasContext *ctx, arg_LXVW4X *a) +{ + TCGv EA; + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -100,55 +103,45 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); } - set_cpu_vsr(xT(ctx->opcode), xth, true); - set_cpu_vsr(xT(ctx->opcode), xtl, false); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } -static void gen_lxvwsx(DisasContext *ctx) +static bool trans_LXVWSX(DisasContext *ctx, arg_LXVWSX *a) { TCGv EA; TCGv_i32 data; - if (xT(ctx->opcode) < 32) { - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + if (a->rt < 32) { + REQUIRE_VSX(ctx); } else { - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } + REQUIRE_VECTOR(ctx); } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); - + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); data = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UL)); - tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); + tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(a->rt), 16, 16, data); + return true; } -static void gen_lxvdsx(DisasContext *ctx) +static bool trans_LXVDSX(DisasContext *ctx, arg_LXVDSX *a) { TCGv EA; TCGv_i64 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); - + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); data = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UQ)); - tcg_gen_gvec_dup_i64(MO_UQ, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); + tcg_gen_gvec_dup_i64(MO_UQ, vsr_full_offset(a->rt), 16, 16, data); + return true; } static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, @@ -187,145 +180,166 @@ static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, tcg_gen_deposit_i64(outl, outl, lo, 32, 32); } -static void gen_lxvh8x(DisasContext *ctx) +static bool trans_LXVH8X(DisasContext *ctx, arg_LXVH8X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); if (ctx->le_mode) { gen_bswap16x8(xth, xtl, xth, xtl); } - set_cpu_vsr(xT(ctx->opcode), xth, true); - set_cpu_vsr(xT(ctx->opcode), xtl, false); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } -static void gen_lxvb16x(DisasContext *ctx) +static bool trans_LXVB16X(DisasContext *ctx, arg_LXVB16X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; + TCGv_i128 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - xth = tcg_temp_new_i64(); - xtl = tcg_temp_new_i64(); + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + + data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); - tcg_gen_addi_tl(EA, EA, 8); - tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); - set_cpu_vsr(xT(ctx->opcode), xth, true); - set_cpu_vsr(xT(ctx->opcode), xtl, false); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_qemu_ld_i128(data, EA, ctx->mem_idx, + MO_BE | MO_128 | MO_ATOM_IFALIGN_PAIR); + set_vsr_full(a->rt, data); + return true; } -#ifdef TARGET_PPC64 -#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_ptr xt; \ - \ - if (xT(ctx->opcode) < 32) { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - EA = tcg_temp_new(); \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - gen_set_access_type(ctx, ACCESS_INT); \ - gen_addr_register(ctx, EA); \ - gen_helper_##name(tcg_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \ +#if defined(TARGET_PPC64) +static bool do_ld_st_vl(DisasContext *ctx, arg_X *a, + void (*helper)(TCGv_ptr, TCGv, TCGv_ptr, TCGv)) +{ + TCGv EA; + TCGv_ptr xt; + if (a->rt < 32) { + REQUIRE_VSX(ctx); + } else { + REQUIRE_VECTOR(ctx); + } + xt = gen_vsr_ptr(a->rt); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc_ra(ctx, a->ra); + helper(tcg_env, EA, xt, cpu_gpr[a->rb]); + return true; } - -VSX_VECTOR_LOAD_STORE_LENGTH(lxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(lxvll) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) #endif -#define VSX_STORE_SCALAR(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - get_cpu_vsr(t0, xS(ctx->opcode), true); \ - gen_qemu_##operation(ctx, t0, EA); \ +static bool trans_LXVL(DisasContext *ctx, arg_LXVL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_LXVL); +#else + qemu_build_not_reached(); +#endif + return true; } -VSX_STORE_SCALAR(stxsdx, st64_i64) +static bool trans_LXVLL(DisasContext *ctx, arg_LXVLL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_LXVLL); +#else + qemu_build_not_reached(); +#endif + return true; +} -VSX_STORE_SCALAR(stxsibx, st8_i64) -VSX_STORE_SCALAR(stxsihx, st16_i64) -VSX_STORE_SCALAR(stxsiwx, st32_i64) -VSX_STORE_SCALAR(stxsspx, st32fs) +static bool trans_STXVL(DisasContext *ctx, arg_STXVL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_STXVL); +#else + qemu_build_not_reached(); +#endif + return true; +} -static void gen_stxvd2x(DisasContext *ctx) +static bool trans_STXVLL(DisasContext *ctx, arg_STXVLL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_STXVLL); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_stxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - get_cpu_vsr(t0, xS(ctx->opcode), true); - gen_qemu_st64_i64(ctx, t0, EA); - tcg_gen_addi_tl(EA, EA, 8); - get_cpu_vsr(t0, xS(ctx->opcode), false); - gen_qemu_st64_i64(ctx, t0, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); + op(ctx, t0, EA); + return true; } -static void gen_stxvw4x(DisasContext *ctx) +TRANS_FLAGS2(VSX, STXSDX, do_stxs, gen_qemu_st64_i64); +TRANS_FLAGS2(ISA300, STXSIBX, do_stxs, gen_qemu_st8_i64); +TRANS_FLAGS2(ISA300, STXSIHX, do_stxs, gen_qemu_st16_i64); +TRANS_FLAGS2(VSX207, STXSIWX, do_stxs, gen_qemu_st32_i64); +TRANS_FLAGS2(VSX207, STXSSPX, do_stxs, gen_qemu_st32fs); + +static bool trans_STXVD2X(DisasContext *ctx, arg_STXVD2X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 t0; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); + gen_qemu_st64_i64(ctx, t0, EA); + tcg_gen_addi_tl(EA, EA, 8); + get_cpu_vsr(t0, a->rt, false); + gen_qemu_st64_i64(ctx, t0, EA); + return true; +} + +static bool trans_STXVW4X(DisasContext *ctx, arg_STXVW4X *a) +{ + TCGv EA; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -342,25 +356,23 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } + return true; } -static void gen_stxvh8x(DisasContext *ctx) +static bool trans_STXVH8X(DisasContext *ctx, arg_STXVH8X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 outh = tcg_temp_new_i64(); TCGv_i64 outl = tcg_temp_new_i64(); @@ -374,28 +386,24 @@ static void gen_stxvh8x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } + return true; } -static void gen_stxvb16x(DisasContext *ctx) +static bool trans_STXVB16X(DisasContext *ctx, arg_STXVB16X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i128 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - xsh = tcg_temp_new_i64(); - xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + + data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEUQ); - tcg_gen_addi_tl(EA, EA, 8); - tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_vsr_full(data, a->rt); + tcg_gen_qemu_st_i128(data, EA, ctx->mem_idx, + MO_BE | MO_128 | MO_ATOM_IFALIGN_PAIR); + return true; } static void gen_mfvsrwz(DisasContext *ctx) @@ -788,34 +796,28 @@ static bool do_xvcpsgn(DisasContext *ctx, arg_XX3 *a, unsigned vece) TRANS(XVCPSGNSP, do_xvcpsgn, MO_32) TRANS(XVCPSGNDP, do_xvcpsgn, MO_64) -#define VSX_CMP(name, op1, op2, inval, type) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_i32 ignored; \ - TCGv_ptr xt, xa, xb; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ - xb = gen_vsr_ptr(xB(ctx->opcode)); \ - if ((ctx->opcode >> (31 - 21)) & 1) { \ - gen_helper_##name(cpu_crf[6], tcg_env, xt, xa, xb); \ - } else { \ - ignored = tcg_temp_new_i32(); \ - gen_helper_##name(ignored, tcg_env, xt, xa, xb); \ - } \ +static bool do_cmp(DisasContext *ctx, arg_XX3_rc *a, + void (*helper)(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_i32 dest; + TCGv_ptr xt, xa, xb; + REQUIRE_VSX(ctx); + xt = gen_vsr_ptr(a->xt); + xa = gen_vsr_ptr(a->xa); + xb = gen_vsr_ptr(a->xb); + dest = a->rc ? cpu_crf[6] : tcg_temp_new_i32(); + helper(dest, tcg_env, xt, xa, xb); + return true; } -VSX_CMP(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) -VSX_CMP(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) -VSX_CMP(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) -VSX_CMP(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300) -VSX_CMP(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) -VSX_CMP(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) -VSX_CMP(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) -VSX_CMP(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) +TRANS_FLAGS2(VSX, XVCMPEQSP, do_cmp, gen_helper_XVCMPEQSP); +TRANS_FLAGS2(VSX, XVCMPGTSP, do_cmp, gen_helper_XVCMPGTSP); +TRANS_FLAGS2(VSX, XVCMPGESP, do_cmp, gen_helper_XVCMPGESP); +TRANS_FLAGS2(ISA300, XVCMPNESP, do_cmp, gen_helper_XVCMPNESP); +TRANS_FLAGS2(VSX, XVCMPEQDP, do_cmp, gen_helper_XVCMPEQDP); +TRANS_FLAGS2(VSX, XVCMPGTDP, do_cmp, gen_helper_XVCMPGTDP); +TRANS_FLAGS2(VSX, XVCMPGEDP, do_cmp, gen_helper_XVCMPGEDP); +TRANS_FLAGS2(ISA300, XVCMPNEDP, do_cmp, gen_helper_XVCMPNEDP); static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a) { @@ -864,20 +866,6 @@ static void gen_##name(DisasContext *ctx) \ gen_helper_##name(tcg_env, opc); \ } -#define GEN_VSX_HELPER_X3(name, op1, op2, inval, type) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr xt, xa, xb; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ - xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(tcg_env, xt, xa, xb); \ -} - #define GEN_VSX_HELPER_X2(name, op1, op2, inval, type) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -983,12 +971,8 @@ static void gen_##name(DisasContext *ctx) \ set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); \ } -GEN_VSX_HELPER_X3(xsadddp, 0x00, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsaddqp, 0x04, 0x00, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xssubdp, 0x00, 0x05, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsmulqp, 0x04, 0x01, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsdivqp, 0x04, 0x11, 0, PPC2_ISA300) GEN_VSX_HELPER_X2(xsredp, 0x14, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) @@ -1001,8 +985,6 @@ GEN_VSX_HELPER_X2_AB(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R2_AB(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R2_AB(xscmpuqp, 0x04, 0x14, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmindp, 0x00, 0x15, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300) GEN_VSX_HELPER_X2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) GEN_VSX_HELPER_R2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300) @@ -1233,27 +1215,17 @@ GEN_VSX_HELPER_R2(xsrqpi, 0x05, 0x00, 0, PPC2_ISA300) GEN_VSX_HELPER_R2(xsrqpxp, 0x05, 0x01, 0, PPC2_ISA300) GEN_VSX_HELPER_R2(xssqrtqp, 0x04, 0x19, 0x1B, PPC2_ISA300) GEN_VSX_HELPER_R3(xssubqp, 0x04, 0x10, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xsaddsp, 0x00, 0x00, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xssubsp, 0x00, 0x01, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xsmulsp, 0x00, 0x02, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xsdivsp, 0x00, 0x03, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xsresp, 0x14, 0x01, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmuldp, 0x00, 0x0E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvdivdp, 0x00, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvredp, 0x14, 0x0D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvsqrtdp, 0x16, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrsqrtedp, 0x14, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xvtdivdp, 0x14, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xvtsqrtdp, 0x14, 0x0E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmaxdp, 0x00, 0x1C, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) @@ -1269,17 +1241,11 @@ GEN_VSX_HELPER_X2(xvrdpim, 0x12, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrdpip, 0x12, 0x0E, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrdpiz, 0x12, 0x0D, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvaddsp, 0x00, 0x08, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvsubsp, 0x00, 0x09, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmulsp, 0x00, 0x0A, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvdivsp, 0x00, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvresp, 0x14, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvsqrtsp, 0x16, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrsqrtesp, 0x14, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xvtdivsp, 0x14, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xvtsqrtsp, 0x14, 0x0A, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmaxsp, 0x00, 0x18, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvminsp, 0x00, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300) GEN_VSX_HELPER_X2(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300) @@ -1609,26 +1575,24 @@ static void gen_xxbrw(DisasContext *ctx) set_cpu_vsr(xT(ctx->opcode), xtl, false); } -#define VSX_LOGICAL(name, vece, tcg_op) \ -static void glue(gen_, name)(DisasContext *ctx) \ - { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - tcg_op(vece, vsr_full_offset(xT(ctx->opcode)), \ - vsr_full_offset(xA(ctx->opcode)), \ - vsr_full_offset(xB(ctx->opcode)), 16, 16); \ - } +static bool do_logical_op(DisasContext *ctx, arg_XX3 *a, unsigned vece, + void (*helper)(unsigned, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)) +{ + REQUIRE_VSX(ctx); + helper(vece, vsr_full_offset(a->xt), + vsr_full_offset(a->xa), + vsr_full_offset(a->xb), 16, 16); + return true; +} -VSX_LOGICAL(xxland, MO_64, tcg_gen_gvec_and) -VSX_LOGICAL(xxlandc, MO_64, tcg_gen_gvec_andc) -VSX_LOGICAL(xxlor, MO_64, tcg_gen_gvec_or) -VSX_LOGICAL(xxlxor, MO_64, tcg_gen_gvec_xor) -VSX_LOGICAL(xxlnor, MO_64, tcg_gen_gvec_nor) -VSX_LOGICAL(xxleqv, MO_64, tcg_gen_gvec_eqv) -VSX_LOGICAL(xxlnand, MO_64, tcg_gen_gvec_nand) -VSX_LOGICAL(xxlorc, MO_64, tcg_gen_gvec_orc) +TRANS_FLAGS2(VSX, XXLAND, do_logical_op, MO_64, tcg_gen_gvec_and); +TRANS_FLAGS2(VSX, XXLANDC, do_logical_op, MO_64, tcg_gen_gvec_andc); +TRANS_FLAGS2(VSX, XXLOR, do_logical_op, MO_64, tcg_gen_gvec_or); +TRANS_FLAGS2(VSX, XXLXOR, do_logical_op, MO_64, tcg_gen_gvec_xor); +TRANS_FLAGS2(VSX, XXLNOR, do_logical_op, MO_64, tcg_gen_gvec_nor); +TRANS_FLAGS2(VSX207, XXLEQV, do_logical_op, MO_64, tcg_gen_gvec_eqv); +TRANS_FLAGS2(VSX207, XXLNAND, do_logical_op, MO_64, tcg_gen_gvec_nand); +TRANS_FLAGS2(VSX207, XXLORC, do_logical_op, MO_64, tcg_gen_gvec_orc); #define VSX_XXMRG(name, high) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -2215,13 +2179,13 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, int rt, bool store, bool paired) { TCGv ea; - TCGv_i64 xt; + TCGv_i128 data; MemOp mop; int rt1, rt2; - xt = tcg_temp_new_i64(); + data = tcg_temp_new_i128(); - mop = DEF_MEMOP(MO_UQ); + mop = DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR); gen_set_access_type(ctx, ACCESS_INT); ea = do_ea_calc(ctx, ra, displ); @@ -2235,32 +2199,20 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, } if (store) { - get_cpu_vsr(xt, rt1, !ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt1, ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + get_vsr_full(data, rt1); + tcg_gen_qemu_st_i128(data, ea, ctx->mem_idx, mop); if (paired) { - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt2, !ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt2, ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + gen_addr_add(ctx, ea, ea, 16); + get_vsr_full(data, rt2); + tcg_gen_qemu_st_i128(data, ea, ctx->mem_idx, mop); } } else { - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt1, xt, !ctx->le_mode); - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt1, xt, ctx->le_mode); + tcg_gen_qemu_ld_i128(data, ea, ctx->mem_idx, mop); + set_vsr_full(rt1, data); if (paired) { - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt2, xt, !ctx->le_mode); - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt2, xt, ctx->le_mode); + gen_addr_add(ctx, ea, ea, 16); + tcg_gen_qemu_ld_i128(data, ea, ctx->mem_idx, mop); + set_vsr_full(rt2, data); } } return true; @@ -2712,8 +2664,6 @@ static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) { TCGv_ptr xt, xa, xb; - - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_VSX(ctx); xt = gen_vsr_ptr(a->xt); @@ -2724,13 +2674,40 @@ static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, return true; } -TRANS(XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) -TRANS(XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) -TRANS(XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) -TRANS(XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) -TRANS(XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) -TRANS(XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) -TRANS(XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) +TRANS_FLAGS2(ISA300, XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) +TRANS_FLAGS2(ISA300, XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) +TRANS_FLAGS2(ISA300, XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) +TRANS_FLAGS2(ISA300, XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) +TRANS_FLAGS2(ISA300, XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) +TRANS_FLAGS2(ISA300, XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) +TRANS_FLAGS2(ISA300, XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) + +TRANS_FLAGS2(VSX207, XSADDSP, do_helper_XX3, gen_helper_XSADDSP) +TRANS_FLAGS2(VSX207, XSSUBSP, do_helper_XX3, gen_helper_XSSUBSP) +TRANS_FLAGS2(VSX207, XSMULSP, do_helper_XX3, gen_helper_XSMULSP) +TRANS_FLAGS2(VSX207, XSDIVSP, do_helper_XX3, gen_helper_XSDIVSP) + +TRANS_FLAGS2(VSX, XSADDDP, do_helper_XX3, gen_helper_XSADDDP) +TRANS_FLAGS2(VSX, XSSUBDP, do_helper_XX3, gen_helper_XSSUBDP) +TRANS_FLAGS2(VSX, XSMULDP, do_helper_XX3, gen_helper_XSMULDP) +TRANS_FLAGS2(VSX, XSDIVDP, do_helper_XX3, gen_helper_XSDIVDP) + +TRANS_FLAGS2(VSX, XVADDSP, do_helper_XX3, gen_helper_XVADDSP) +TRANS_FLAGS2(VSX, XVSUBSP, do_helper_XX3, gen_helper_XVSUBSP) +TRANS_FLAGS2(VSX, XVMULSP, do_helper_XX3, gen_helper_XVMULSP) +TRANS_FLAGS2(VSX, XVDIVSP, do_helper_XX3, gen_helper_XVDIVSP) + +TRANS_FLAGS2(VSX, XVADDDP, do_helper_XX3, gen_helper_XVADDDP) +TRANS_FLAGS2(VSX, XVSUBDP, do_helper_XX3, gen_helper_XVSUBDP) +TRANS_FLAGS2(VSX, XVMULDP, do_helper_XX3, gen_helper_XVMULDP) +TRANS_FLAGS2(VSX, XVDIVDP, do_helper_XX3, gen_helper_XVDIVDP) + +TRANS_FLAGS2(VSX, XSMAXDP, do_helper_XX3, gen_helper_XSMAXDP) +TRANS_FLAGS2(VSX, XSMINDP, do_helper_XX3, gen_helper_XSMINDP) +TRANS_FLAGS2(VSX, XVMAXSP, do_helper_XX3, gen_helper_XVMAXSP) +TRANS_FLAGS2(VSX, XVMINSP, do_helper_XX3, gen_helper_XVMINSP) +TRANS_FLAGS2(VSX, XVMAXDP, do_helper_XX3, gen_helper_XVMAXDP) +TRANS_FLAGS2(VSX, XVMINDP, do_helper_XX3, gen_helper_XVMINDP) static bool do_helper_X(arg_X *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) @@ -2910,4 +2887,3 @@ TRANS64(PMXVF64GERNN, do_ger, gen_helper_XVF64GERNN) #undef GEN_XX2IFORM #undef GEN_XX3_RC_FORM #undef GEN_XX3FORM_DM -#undef VSX_LOGICAL diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index a3ba094d62..e553b5b8fa 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -1,34 +1,3 @@ -GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxsiwax, 0x1F, 0x0C, 0x02, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxsiwzx, 0x1F, 0x0C, 0x00, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxsibzx, 0x1F, 0x0D, 0x18, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxsihzx, 0x1F, 0x0D, 0x19, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxsspx, 0x1F, 0x0C, 0x10, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvwsx, 0x1F, 0x0C, 0x0B, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300), -#endif - -GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxsihx, 0x1F, 0xD, 0x1D, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxsiwx, 0x1F, 0xC, 0x04, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(stxsspx, 0x1F, 0xC, 0x14, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300), -#endif - GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwz, 0x1F, 0x13, 0x07, 0x0000F800, PPC_NONE, PPC2_VSX207), @@ -74,16 +43,6 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 1, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 1, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 1, PPC_NONE, fl2) -#define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x10, 0, PPC_NONE, fl2) - #define GEN_XX3FORM_DM(name, opc2, opc3) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ @@ -153,12 +112,8 @@ GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300), GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300), GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300), -GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0), -GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), -GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), GEN_VSX_XFORM_300(xsmulqp, 0x04, 0x01, 0x0), -GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), @@ -170,8 +125,6 @@ GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001), GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001), -GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), -GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300), GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), @@ -191,10 +144,6 @@ GEN_XX2FORM(xsrdpim, 0x12, 0x07, PPC2_VSX), GEN_XX2FORM(xsrdpip, 0x12, 0x06, PPC2_VSX), GEN_XX2FORM(xsrdpiz, 0x12, 0x05, PPC2_VSX), -GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), -GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), -GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), -GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), GEN_VSX_XFORM_300(xsdivqp, 0x04, 0x11, 0x0), GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), @@ -203,10 +152,6 @@ GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), -GEN_XX3FORM(xvadddp, 0x00, 0x0C, PPC2_VSX), -GEN_XX3FORM(xvsubdp, 0x00, 0x0D, PPC2_VSX), -GEN_XX3FORM(xvmuldp, 0x00, 0x0E, PPC2_VSX), -GEN_XX3FORM(xvdivdp, 0x00, 0x0F, PPC2_VSX), GEN_XX2FORM(xvredp, 0x14, 0x0D, PPC2_VSX), GEN_XX2FORM(xvsqrtdp, 0x16, 0x0C, PPC2_VSX), GEN_XX2FORM(xvrsqrtedp, 0x14, 0x0C, PPC2_VSX), @@ -220,12 +165,6 @@ GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddadp", 0x04, 0x1C, PPC2_VSX), GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddmdp", 0x04, 0x1D, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubadp", 0x04, 0x1E, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubmdp", 0x04, 0x1F, PPC2_VSX), -GEN_XX3FORM(xvmaxdp, 0x00, 0x1C, PPC2_VSX), -GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300), GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), @@ -241,10 +180,6 @@ GEN_XX2FORM(xvrdpim, 0x12, 0x0F, PPC2_VSX), GEN_XX2FORM(xvrdpip, 0x12, 0x0E, PPC2_VSX), GEN_XX2FORM(xvrdpiz, 0x12, 0x0D, PPC2_VSX), -GEN_XX3FORM(xvaddsp, 0x00, 0x08, PPC2_VSX), -GEN_XX3FORM(xvsubsp, 0x00, 0x09, PPC2_VSX), -GEN_XX3FORM(xvmulsp, 0x00, 0x0A, PPC2_VSX), -GEN_XX3FORM(xvdivsp, 0x00, 0x0B, PPC2_VSX), GEN_XX2FORM(xvresp, 0x14, 0x09, PPC2_VSX), GEN_XX2FORM(xvsqrtsp, 0x16, 0x08, PPC2_VSX), GEN_XX2FORM(xvrsqrtesp, 0x14, 0x08, PPC2_VSX), @@ -258,12 +193,6 @@ GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddasp", 0x04, 0x18, PPC2_VSX), GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddmsp", 0x04, 0x19, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubasp", 0x04, 0x1A, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubmsp", 0x04, 0x1B, PPC2_VSX), -GEN_XX3FORM(xvmaxsp, 0x00, 0x18, PPC2_VSX), -GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300), GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), @@ -285,17 +214,6 @@ GEN_XX2FORM_EO(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300), GEN_XX2FORM_EO(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300), GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), -#define VSX_LOGICAL(name, opc2, opc3, fl2) \ -GEN_XX3FORM(name, opc2, opc3, fl2) - -VSX_LOGICAL(xxland, 0x8, 0x10, PPC2_VSX), -VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX), -VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX), -VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX), -VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX), -VSX_LOGICAL(xxleqv, 0x8, 0x17, PPC2_VSX207), -VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), -VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index e7ab2a4312..2f0d3ef080 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -171,6 +171,7 @@ qtests_ppc64 = \ qtests_ppc + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-spi-seeprom-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \ (slirp.found() ? ['pxe-test'] : []) + \ diff --git a/tests/qtest/pnv-spi-seeprom-test.c b/tests/qtest/pnv-spi-seeprom-test.c new file mode 100644 index 0000000000..57f20af76e --- /dev/null +++ b/tests/qtest/pnv-spi-seeprom-test.c @@ -0,0 +1,110 @@ +/* + * QTest testcase for PowerNV 10 Seeprom Communications + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bswap.h" +#include "hw/ssi/pnv_spi_regs.h" +#include "pnv-xscom.h" + +#define FLASH_SIZE (512 * 1024) +#define SPIC2_XSCOM_BASE 0xc0040 + +/* To transmit READ opcode and address */ +#define READ_OP_TDR_DATA 0x0300010000000000 +/* + * N1 shift - tx 4 bytes (transmit opcode and address) + * N2 shift - tx and rx 8 bytes. + */ +#define READ_OP_COUNTER_CONFIG 0x2040000000002b00 +/* SEQ_OP_SELECT_RESPONDER - N1 Shift - N2 Shift * 5 - SEQ_OP_STOP */ +#define READ_OP_SEQUENCER 0x1130404040404010 + +/* To transmit WREN(Set Write Enable Latch in status0 register) opcode */ +#define WRITE_OP_WREN 0x0600000000000000 +/* To transmit WRITE opcode, address and data */ +#define WRITE_OP_TDR_DATA 0x0300010012345678 +/* N1 shift - tx 8 bytes (transmit opcode, address and data) */ +#define WRITE_OP_COUNTER_CONFIG 0x4000000000002000 +/* SEQ_OP_SELECT_RESPONDER - N1 Shift - SEQ_OP_STOP */ +#define WRITE_OP_SEQUENCER 0x1130100000000000 + +static void pnv_spi_xscom_write(QTestState *qts, const PnvChip *chip, + uint32_t reg, uint64_t val) +{ + uint32_t pcba = SPIC2_XSCOM_BASE + reg; + qtest_writeq(qts, pnv_xscom_addr(chip, pcba), val); +} + +static uint64_t pnv_spi_xscom_read(QTestState *qts, const PnvChip *chip, + uint32_t reg) +{ + uint32_t pcba = SPIC2_XSCOM_BASE + reg; + return qtest_readq(qts, pnv_xscom_addr(chip, pcba)); +} + +static void spi_seeprom_transaction(QTestState *qts, const PnvChip *chip) +{ + /* SPI transactions to SEEPROM to read from SEEPROM image */ + pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, READ_OP_COUNTER_CONFIG); + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, READ_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, READ_OP_TDR_DATA); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, 0); + /* Read 5*8 bytes from SEEPROM at 0x100 */ + uint64_t rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + g_test_message("RDR READ = 0x%" PRIx64, rdr_val); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + g_test_message("RDR READ = 0x%" PRIx64, rdr_val); + + /* SPI transactions to SEEPROM to write to SEEPROM image */ + pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, WRITE_OP_COUNTER_CONFIG); + /* Set Write Enable Latch bit of status0 register */ + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_WREN); + /* write 8 bytes to SEEPROM at 0x100 */ + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_TDR_DATA); +} + +static void test_spi_seeprom(const void *data) +{ + const PnvChip *chip = data; + QTestState *qts = NULL; + g_autofree char *tmp_path = NULL; + int ret; + int fd; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("qtest-seeprom-XXXXXX", &tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, FLASH_SIZE); + g_assert(ret == 0); + close(fd); + + qts = qtest_initf("-machine powernv10 -smp 2,cores=2," + "threads=1 -accel tcg,thread=single -nographic " + "-blockdev node-name=pib_spic2,driver=file," + "filename=%s -device 25csm04,bus=pnv-spi-bus.2,cs=0," + "drive=pib_spic2", tmp_path); + spi_seeprom_transaction(qts, chip); + qtest_quit(qts); + unlink(tmp_path); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + char *tname = g_strdup_printf("pnv-xscom/spi-seeprom/%s", + pnv_chips[3].cpu_model); + qtest_add_data_func(tname, &pnv_chips[3], test_spi_seeprom); + g_free(tname); + return g_test_run(); +} diff --git a/tests/qtest/pnv-xscom.h b/tests/qtest/pnv-xscom.h index 6f62941744..5aa1701ea7 100644 --- a/tests/qtest/pnv-xscom.h +++ b/tests/qtest/pnv-xscom.h @@ -56,7 +56,7 @@ static const PnvChip pnv_chips[] = { .chip_type = PNV_CHIP_POWER10, .cpu_model = "POWER10", .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x120da04900008000ull, + .cfam_id = 0x220da04980000000ull, .first_core = 0x0, .num_i2c = 4, }, diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index 8c3e4e4038..509a20be2b 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -11,6 +11,18 @@ config-cc.mak: Makefile -include config-cc.mak +# multi-threaded tests are known to fail (e.g., clang-user CI job) +# See: https://gitlab.com/qemu-project/qemu/-/issues/2456 +run-signals: signals + $(call skip-test, $<, "BROKEN (flaky with clang) ") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN (flaky with clang) ") + +run-threadcount: threadcount + $(call skip-test, $<, "BROKEN (flaky with clang) ") +run-plugin-threadcount-with-%: + $(call skip-test, $<, "BROKEN (flaky with clang) ") + ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) PPC64_TESTS=bcdsub non_signalling_xscv endif