ppc patch queue 2019-12-17
This is the first pull request for the qemu-5.0 branch. It has a lot of accumulated changes, including: * SLOF update to support boot using the IOMMU (will become necessary for secure guests) * Clean ups to pnv handling of chip models * A number of extensions to the powernv machine model * TCG extensions to allow powernv emulated systems to run KVM guests * Outline support for POWER10 chips in powernv * Cleanups to the ibm,client-architecture-support feature negotiation path * XIVE reworks to better handle the powernv machine * Improvements to not waste interrupt queues and other semi-scarce resources when using XIVE under KVM -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAl34XKwACgkQbDjKyiDZ s5IFiRAAl6wCovC6C3/31ugEP7nyYNtTLIbaqjhR1zwBpvm+Umr0SHzkngrEEI3J WgkXVwbb7HWLsQskGS3kAP5I9qO3aIhYN3NOGLaf9AqkBpSMnn3+Vz6pOWIpoFx3 tBWVwpxlUU5iKCQQzmxBa2yXpOfo9Tnvpv+2nY/cKDyFA8LJyX8DMstEkJFgqW8i OhyDmibrMFLETQOvrd2yAuMr1XJneXE2WdAa1HE/DUgFAjN+uCVM69BtmU3AvQyl 8dKFpwe1dgFOUvOg2e6IwdHj51rmhrKrkavF8OyWPFt6MGp0vfl9mME6keO4KUVw oatFdh8XP9O2QkR7gh2t6HO6QhRzFhr9x75/PvpmqVO6Km8W5ROoi9VC8sWaRuUA kbhluahf0rCa926mO3Y1arLBrW1776Zb8Fz23f6a9ELco9YtNzcUsj2vIX6KTsir LobIGsE5ZhnnvOC/3jSLfEEkGM7YTP9dmEXS3L1sMS27h8kS2TM/zisSKFmJkA1u 4XyyzukSLqhmVagvkxr/wqrFrzGyoSuBi1KKOqoVLtsHO1lFsTs6nHumjKkrBDFV j0NJxuRpHpfhKL+WC3CSTmnQ1FMCIb328zXDdyPkckrQKHJuS0JciFwawq0AfylO BHxg1GCPJ09B2hIt8MQPRA+dtK4MHzmaBjU4BkiYgwX2wlg3sEI= =++Ha -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-5.0-20191217' into staging ppc patch queue 2019-12-17 This is the first pull request for the qemu-5.0 branch. It has a lot of accumulated changes, including: * SLOF update to support boot using the IOMMU (will become necessary for secure guests) * Clean ups to pnv handling of chip models * A number of extensions to the powernv machine model * TCG extensions to allow powernv emulated systems to run KVM guests * Outline support for POWER10 chips in powernv * Cleanups to the ibm,client-architecture-support feature negotiation path * XIVE reworks to better handle the powernv machine * Improvements to not waste interrupt queues and other semi-scarce resources when using XIVE under KVM # gpg: Signature made Tue 17 Dec 2019 04:42:20 GMT # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-5.0-20191217: (88 commits) pseries: Update SLOF firmware image ppc/pnv: Drop PnvChipClass::type ppc/pnv: Introduce PnvChipClass::xscom_pcba() method ppc/pnv: Drop pnv_chip_is_power9() and pnv_chip_is_power10() helpers ppc/pnv: Pass content of the "compatible" property to pnv_dt_xscom() ppc/pnv: Pass XSCOM base address and address size to pnv_dt_xscom() ppc/pnv: Introduce PnvChipClass::xscom_core_base() method ppc/pnv: Introduce PnvChipClass::intc_print_info() method ppc/pnv: Drop pnv_is_power9() and pnv_is_power10() helpers ppc/pnv: Introduce PnvMachineClass::dt_power_mgt() ppc/pnv: Introduce PnvMachineClass and PnvMachineClass::compat ppc/pnv: Drop PnvPsiClass::chip_type ppc/pnv: Introduce PnvPsiClass::compat ppc: Drop useless extern annotation for functions ppc/pnv: Fix OCC common area region mapping ppc/pnv: Introduce PBA registers ppc/pnv: Make PnvXScomInterface an incomplete type ppc/pnv: populate the DT with realized XSCOM devices ppc/pnv: Loop on the whole hierarchy to populate the DT with the XSCOM nodes target/ppc: Add SPR TBU40 ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
98ac38cd5a
@ -29,7 +29,7 @@
|
||||
|
||||
#include "pnv_xive_regs.h"
|
||||
|
||||
#define XIVE_DEBUG
|
||||
#undef XIVE_DEBUG
|
||||
|
||||
/*
|
||||
* Virtual structures table (VST)
|
||||
@ -85,13 +85,30 @@ static inline uint64_t SETFIELD(uint64_t mask, uint64_t word,
|
||||
return (word & ~mask) | ((value << ctz64(mask)) & mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* When PC_TCTXT_CHIPID_OVERRIDE is configured, the PC_TCTXT_CHIPID
|
||||
* field overrides the hardwired chip ID in the Powerbus operations
|
||||
* and for CAM compares
|
||||
*/
|
||||
static uint8_t pnv_xive_block_id(PnvXive *xive)
|
||||
{
|
||||
uint8_t blk = xive->chip->chip_id;
|
||||
uint64_t cfg_val = xive->regs[PC_TCTXT_CFG >> 3];
|
||||
|
||||
if (cfg_val & PC_TCTXT_CHIPID_OVERRIDE) {
|
||||
blk = GETFIELD(PC_TCTXT_CHIPID, cfg_val);
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remote access to controllers. HW uses MMIOs. For now, a simple scan
|
||||
* of the chips is good enough.
|
||||
*
|
||||
* TODO: Block scope support
|
||||
*/
|
||||
static PnvXive *pnv_xive_get_ic(uint8_t blk)
|
||||
static PnvXive *pnv_xive_get_remote(uint8_t blk)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
int i;
|
||||
@ -100,7 +117,7 @@ static PnvXive *pnv_xive_get_ic(uint8_t blk)
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]);
|
||||
PnvXive *xive = &chip9->xive;
|
||||
|
||||
if (xive->chip->chip_id == blk) {
|
||||
if (pnv_xive_block_id(xive) == blk) {
|
||||
return xive;
|
||||
}
|
||||
}
|
||||
@ -123,36 +140,22 @@ static uint64_t pnv_xive_vst_page_size_allowed(uint32_t page_shift)
|
||||
page_shift == 21 || page_shift == 24;
|
||||
}
|
||||
|
||||
static uint64_t pnv_xive_vst_size(uint64_t vsd)
|
||||
{
|
||||
uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12);
|
||||
|
||||
/*
|
||||
* Read the first descriptor to get the page size of the indirect
|
||||
* table.
|
||||
*/
|
||||
if (VSD_INDIRECT & vsd) {
|
||||
uint32_t nr_pages = vst_tsize / XIVE_VSD_SIZE;
|
||||
uint32_t page_shift;
|
||||
|
||||
vsd = ldq_be_dma(&address_space_memory, vsd & VSD_ADDRESS_MASK);
|
||||
page_shift = GETFIELD(VSD_TSIZE, vsd) + 12;
|
||||
|
||||
if (!pnv_xive_vst_page_size_allowed(page_shift)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nr_pages * (1ull << page_shift);
|
||||
}
|
||||
|
||||
return vst_tsize;
|
||||
}
|
||||
|
||||
static uint64_t pnv_xive_vst_addr_direct(PnvXive *xive, uint32_t type,
|
||||
uint64_t vsd, uint32_t idx)
|
||||
{
|
||||
const XiveVstInfo *info = &vst_infos[type];
|
||||
uint64_t vst_addr = vsd & VSD_ADDRESS_MASK;
|
||||
uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12);
|
||||
uint32_t idx_max;
|
||||
|
||||
idx_max = vst_tsize / info->size - 1;
|
||||
if (idx > idx_max) {
|
||||
#ifdef XIVE_DEBUG
|
||||
xive_error(xive, "VST: %s entry %x out of range [ 0 .. %x ] !?",
|
||||
info->name, idx, idx_max);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vst_addr + idx * info->size;
|
||||
}
|
||||
@ -171,7 +174,9 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type,
|
||||
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
|
||||
|
||||
if (!(vsd & VSD_ADDRESS_MASK)) {
|
||||
#ifdef XIVE_DEBUG
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -192,7 +197,9 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type,
|
||||
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
|
||||
|
||||
if (!(vsd & VSD_ADDRESS_MASK)) {
|
||||
#ifdef XIVE_DEBUG
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -215,7 +222,6 @@ static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk,
|
||||
{
|
||||
const XiveVstInfo *info = &vst_infos[type];
|
||||
uint64_t vsd;
|
||||
uint32_t idx_max;
|
||||
|
||||
if (blk >= info->max_blocks) {
|
||||
xive_error(xive, "VST: invalid block id %d for VST %s %d !?",
|
||||
@ -227,20 +233,11 @@ static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk,
|
||||
|
||||
/* Remote VST access */
|
||||
if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) {
|
||||
xive = pnv_xive_get_ic(blk);
|
||||
xive = pnv_xive_get_remote(blk);
|
||||
|
||||
return xive ? pnv_xive_vst_addr(xive, type, blk, idx) : 0;
|
||||
}
|
||||
|
||||
idx_max = pnv_xive_vst_size(vsd) / info->size - 1;
|
||||
if (idx > idx_max) {
|
||||
#ifdef XIVE_DEBUG
|
||||
xive_error(xive, "VST: %s entry %x/%x out of range [ 0 .. %x ] !?",
|
||||
info->name, blk, idx, idx_max);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (VSD_INDIRECT & vsd) {
|
||||
return pnv_xive_vst_addr_indirect(xive, type, vsd, idx);
|
||||
}
|
||||
@ -384,7 +381,10 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
{
|
||||
PnvXive *xive = PNV_XIVE(xrtr);
|
||||
|
||||
if (pnv_xive_get_ic(blk) != xive) {
|
||||
/*
|
||||
* EAT lookups should be local to the IC
|
||||
*/
|
||||
if (pnv_xive_block_id(xive) != blk) {
|
||||
xive_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx));
|
||||
return -1;
|
||||
}
|
||||
@ -392,31 +392,97 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
return pnv_xive_vst_read(xive, VST_TSEL_IVT, blk, idx, eas);
|
||||
}
|
||||
|
||||
static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
/*
|
||||
* One bit per thread id. The first register PC_THREAD_EN_REG0 covers
|
||||
* the first cores 0-15 (normal) of the chip or 0-7 (fused). The
|
||||
* second register covers cores 16-23 (normal) or 8-11 (fused).
|
||||
*/
|
||||
static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
|
||||
PnvXive *xive = NULL;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int pir = env->spr_cb[SPR_PIR].default_value;
|
||||
int pir = ppc_cpu_pir(cpu);
|
||||
uint32_t fc = PNV9_PIR2FUSEDCORE(pir);
|
||||
uint64_t reg = fc < 8 ? PC_THREAD_EN_REG0 : PC_THREAD_EN_REG1;
|
||||
uint32_t bit = pir & 0x3f;
|
||||
|
||||
/*
|
||||
* Perform an extra check on the HW thread enablement.
|
||||
*
|
||||
* The TIMA is shared among the chips and to identify the chip
|
||||
* from which the access is being done, we extract the chip id
|
||||
* from the PIR.
|
||||
*/
|
||||
xive = pnv_xive_get_ic((pir >> 8) & 0xf);
|
||||
if (!xive) {
|
||||
return NULL;
|
||||
return xive->regs[reg >> 3] & PPC_BIT(bit);
|
||||
}
|
||||
|
||||
static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
PnvXive *xive = PNV_XIVE(xptr);
|
||||
PnvChip *chip = xive->chip;
|
||||
int count = 0;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pc = chip->cores[i];
|
||||
CPUCore *cc = CPU_CORE(pc);
|
||||
|
||||
for (j = 0; j < cc->nr_threads; j++) {
|
||||
PowerPCCPU *cpu = pc->threads[j];
|
||||
XiveTCTX *tctx;
|
||||
int ring;
|
||||
|
||||
if (!pnv_xive_is_cpu_enabled(xive, cpu)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
|
||||
|
||||
/*
|
||||
* Check the thread context CAM lines and record matches.
|
||||
*/
|
||||
ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk,
|
||||
nvt_idx, cam_ignore, logic_serv);
|
||||
/*
|
||||
* Save the context and follow on to catch duplicates, that we
|
||||
* don't support yet.
|
||||
*/
|
||||
if (ring != -1) {
|
||||
if (match->tctx) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a "
|
||||
"thread context NVT %x/%x\n",
|
||||
nvt_blk, nvt_idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
match->ring = ring;
|
||||
match->tctx = tctx;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(xive->regs[PC_THREAD_EN_REG0 >> 3] & PPC_BIT(pir & 0x3f))) {
|
||||
xive_error(PNV_XIVE(xrtr), "IC: CPU %x is not enabled", pir);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
return tctx;
|
||||
static uint8_t pnv_xive_get_block_id(XiveRouter *xrtr)
|
||||
{
|
||||
return pnv_xive_block_id(PNV_XIVE(xrtr));
|
||||
}
|
||||
|
||||
/*
|
||||
* The TIMA MMIO space is shared among the chips and to identify the
|
||||
* chip from which the access is being done, we extract the chip id
|
||||
* from the PIR.
|
||||
*/
|
||||
static PnvXive *pnv_xive_tm_get_xive(PowerPCCPU *cpu)
|
||||
{
|
||||
int pir = ppc_cpu_pir(cpu);
|
||||
PnvChip *chip;
|
||||
PnvXive *xive;
|
||||
|
||||
chip = pnv_get_chip(PNV9_PIR2CHIP(pir));
|
||||
assert(chip);
|
||||
xive = &PNV9_CHIP(chip)->xive;
|
||||
|
||||
if (!pnv_xive_is_cpu_enabled(xive, cpu)) {
|
||||
xive_error(xive, "IC: CPU %x is not enabled", pir);
|
||||
}
|
||||
return xive;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -429,7 +495,7 @@ static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
static void pnv_xive_notify(XiveNotifier *xn, uint32_t srcno)
|
||||
{
|
||||
PnvXive *xive = PNV_XIVE(xn);
|
||||
uint8_t blk = xive->chip->chip_id;
|
||||
uint8_t blk = pnv_xive_block_id(xive);
|
||||
|
||||
xive_router_notify(xn, XIVE_EAS(blk, srcno));
|
||||
}
|
||||
@ -453,19 +519,50 @@ static uint64_t pnv_xive_pc_size(PnvXive *xive)
|
||||
return (~xive->regs[CQ_PC_BARM >> 3] + 1) & CQ_PC_BARM_MASK;
|
||||
}
|
||||
|
||||
static uint32_t pnv_xive_nr_ipis(PnvXive *xive)
|
||||
static uint32_t pnv_xive_nr_ipis(PnvXive *xive, uint8_t blk)
|
||||
{
|
||||
uint8_t blk = xive->chip->chip_id;
|
||||
uint64_t vsd = xive->vsds[VST_TSEL_SBE][blk];
|
||||
uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12);
|
||||
|
||||
return pnv_xive_vst_size(xive->vsds[VST_TSEL_SBE][blk]) * SBE_PER_BYTE;
|
||||
return VSD_INDIRECT & vsd ? 0 : vst_tsize * SBE_PER_BYTE;
|
||||
}
|
||||
|
||||
static uint32_t pnv_xive_nr_ends(PnvXive *xive)
|
||||
/*
|
||||
* Compute the number of entries per indirect subpage.
|
||||
*/
|
||||
static uint64_t pnv_xive_vst_per_subpage(PnvXive *xive, uint32_t type)
|
||||
{
|
||||
uint8_t blk = xive->chip->chip_id;
|
||||
uint8_t blk = pnv_xive_block_id(xive);
|
||||
uint64_t vsd = xive->vsds[type][blk];
|
||||
const XiveVstInfo *info = &vst_infos[type];
|
||||
uint64_t vsd_addr;
|
||||
uint32_t page_shift;
|
||||
|
||||
return pnv_xive_vst_size(xive->vsds[VST_TSEL_EQDT][blk])
|
||||
/ vst_infos[VST_TSEL_EQDT].size;
|
||||
/* For direct tables, fake a valid value */
|
||||
if (!(VSD_INDIRECT & vsd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get the page size of the indirect table. */
|
||||
vsd_addr = vsd & VSD_ADDRESS_MASK;
|
||||
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
|
||||
|
||||
if (!(vsd & VSD_ADDRESS_MASK)) {
|
||||
#ifdef XIVE_DEBUG
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
page_shift = GETFIELD(VSD_TSIZE, vsd) + 12;
|
||||
|
||||
if (!pnv_xive_vst_page_size_allowed(page_shift)) {
|
||||
xive_error(xive, "VST: invalid %s page shift %d", info->name,
|
||||
page_shift);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (1ull << page_shift) / info->size;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -598,6 +695,7 @@ static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type,
|
||||
XiveSource *xsrc = &xive->ipi_source;
|
||||
const XiveVstInfo *info = &vst_infos[type];
|
||||
uint32_t page_shift = GETFIELD(VSD_TSIZE, vsd) + 12;
|
||||
uint64_t vst_tsize = 1ull << page_shift;
|
||||
uint64_t vst_addr = vsd & VSD_ADDRESS_MASK;
|
||||
|
||||
/* Basic checks */
|
||||
@ -633,11 +731,16 @@ static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type,
|
||||
|
||||
case VST_TSEL_EQDT:
|
||||
/*
|
||||
* Backing store pages for the END. Compute the number of ENDs
|
||||
* provisioned by FW and resize the END ESB window accordingly.
|
||||
* Backing store pages for the END.
|
||||
*
|
||||
* If the table is direct, we can compute the number of PQ
|
||||
* entries provisioned by FW (such as skiboot) and resize the
|
||||
* END ESB window accordingly.
|
||||
*/
|
||||
memory_region_set_size(&end_xsrc->esb_mmio, pnv_xive_nr_ends(xive) *
|
||||
(1ull << (end_xsrc->esb_shift + 1)));
|
||||
if (!(VSD_INDIRECT & vsd)) {
|
||||
memory_region_set_size(&end_xsrc->esb_mmio, (vst_tsize / info->size)
|
||||
* (1ull << xsrc->esb_shift));
|
||||
}
|
||||
memory_region_add_subregion(&xive->end_edt_mmio, 0,
|
||||
&end_xsrc->esb_mmio);
|
||||
break;
|
||||
@ -646,11 +749,16 @@ static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type,
|
||||
/*
|
||||
* Backing store pages for the source PQ bits. The model does
|
||||
* not use these PQ bits backed in RAM because the XiveSource
|
||||
* model has its own. Compute the number of IRQs provisioned
|
||||
* by FW and resize the IPI ESB window accordingly.
|
||||
* model has its own.
|
||||
*
|
||||
* If the table is direct, we can compute the number of PQ
|
||||
* entries provisioned by FW (such as skiboot) and resize the
|
||||
* ESB window accordingly.
|
||||
*/
|
||||
memory_region_set_size(&xsrc->esb_mmio, pnv_xive_nr_ipis(xive) *
|
||||
(1ull << xsrc->esb_shift));
|
||||
if (!(VSD_INDIRECT & vsd)) {
|
||||
memory_region_set_size(&xsrc->esb_mmio, vst_tsize * SBE_PER_BYTE
|
||||
* (1ull << xsrc->esb_shift));
|
||||
}
|
||||
memory_region_add_subregion(&xive->ipi_edt_mmio, 0, &xsrc->esb_mmio);
|
||||
break;
|
||||
|
||||
@ -789,20 +897,7 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset,
|
||||
case PC_TCTXT_CFG:
|
||||
/*
|
||||
* TODO: block group support
|
||||
*
|
||||
* PC_TCTXT_CFG_BLKGRP_EN
|
||||
* PC_TCTXT_CFG_HARD_CHIPID_BLK :
|
||||
* Moves the chipid into block field for hardwired CAM compares.
|
||||
* Block offset value is adjusted to 0b0..01 & ThrdId
|
||||
*
|
||||
* Will require changes in xive_presenter_tctx_match(). I am
|
||||
* not sure how to handle that yet.
|
||||
*/
|
||||
|
||||
/* Overrides hardwired chip ID with the chip ID field */
|
||||
if (val & PC_TCTXT_CHIPID_OVERRIDE) {
|
||||
xive->tctx_chipid = GETFIELD(PC_TCTXT_CHIPID, val);
|
||||
}
|
||||
break;
|
||||
case PC_TCTXT_TRACK:
|
||||
/*
|
||||
@ -1349,12 +1444,13 @@ static const MemoryRegionOps pnv_xive_ic_lsi_ops = {
|
||||
*/
|
||||
|
||||
/*
|
||||
* When the TIMA is accessed from the indirect page, the thread id
|
||||
* (PIR) has to be configured in the IC registers before. This is used
|
||||
* for resets and for debug purpose also.
|
||||
* When the TIMA is accessed from the indirect page, the thread id of
|
||||
* the target CPU is configured in the PC_TCTXT_INDIR0 register before
|
||||
* use. This is used for resets and for debug purpose also.
|
||||
*/
|
||||
static XiveTCTX *pnv_xive_get_indirect_tctx(PnvXive *xive)
|
||||
{
|
||||
PnvChip *chip = xive->chip;
|
||||
uint64_t tctxt_indir = xive->regs[PC_TCTXT_INDIR0 >> 3];
|
||||
PowerPCCPU *cpu = NULL;
|
||||
int pir;
|
||||
@ -1364,15 +1460,15 @@ static XiveTCTX *pnv_xive_get_indirect_tctx(PnvXive *xive)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pir = GETFIELD(PC_TCTXT_INDIR_THRDID, tctxt_indir) & 0xff;
|
||||
cpu = ppc_get_vcpu_by_pir(pir);
|
||||
pir = (chip->chip_id << 8) | GETFIELD(PC_TCTXT_INDIR_THRDID, tctxt_indir);
|
||||
cpu = pnv_chip_find_cpu(chip, pir);
|
||||
if (!cpu) {
|
||||
xive_error(xive, "IC: invalid PIR %x for indirect access", pir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check that HW thread is XIVE enabled */
|
||||
if (!(xive->regs[PC_THREAD_EN_REG0 >> 3] & PPC_BIT(pir & 0x3f))) {
|
||||
if (!pnv_xive_is_cpu_enabled(xive, cpu)) {
|
||||
xive_error(xive, "IC: CPU %x is not enabled", pir);
|
||||
}
|
||||
|
||||
@ -1384,7 +1480,7 @@ static void xive_tm_indirect_write(void *opaque, hwaddr offset,
|
||||
{
|
||||
XiveTCTX *tctx = pnv_xive_get_indirect_tctx(PNV_XIVE(opaque));
|
||||
|
||||
xive_tctx_tm_write(tctx, offset, value, size);
|
||||
xive_tctx_tm_write(XIVE_PRESENTER(opaque), tctx, offset, value, size);
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_indirect_read(void *opaque, hwaddr offset,
|
||||
@ -1392,7 +1488,7 @@ static uint64_t xive_tm_indirect_read(void *opaque, hwaddr offset,
|
||||
{
|
||||
XiveTCTX *tctx = pnv_xive_get_indirect_tctx(PNV_XIVE(opaque));
|
||||
|
||||
return xive_tctx_tm_read(tctx, offset, size);
|
||||
return xive_tctx_tm_read(XIVE_PRESENTER(opaque), tctx, offset, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps xive_tm_indirect_ops = {
|
||||
@ -1409,6 +1505,39 @@ static const MemoryRegionOps xive_tm_indirect_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static void pnv_xive_tm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
|
||||
PnvXive *xive = pnv_xive_tm_get_xive(cpu);
|
||||
XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
|
||||
|
||||
xive_tctx_tm_write(XIVE_PRESENTER(xive), tctx, offset, value, size);
|
||||
}
|
||||
|
||||
static uint64_t pnv_xive_tm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
|
||||
PnvXive *xive = pnv_xive_tm_get_xive(cpu);
|
||||
XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
|
||||
|
||||
return xive_tctx_tm_read(XIVE_PRESENTER(xive), tctx, offset, size);
|
||||
}
|
||||
|
||||
const MemoryRegionOps pnv_xive_tm_ops = {
|
||||
.read = pnv_xive_tm_read,
|
||||
.write = pnv_xive_tm_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Interrupt controller XSCOM region.
|
||||
*/
|
||||
@ -1574,23 +1703,40 @@ static const MemoryRegionOps pnv_xive_pc_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx,
|
||||
Monitor *mon)
|
||||
{
|
||||
uint8_t eq_blk = xive_get_field32(NVT_W1_EQ_BLOCK, nvt->w1);
|
||||
uint32_t eq_idx = xive_get_field32(NVT_W1_EQ_INDEX, nvt->w1);
|
||||
|
||||
if (!xive_nvt_is_valid(nvt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x\n", nvt_idx,
|
||||
eq_blk, eq_idx,
|
||||
xive_get_field32(NVT_W4_IPB, nvt->w4));
|
||||
}
|
||||
|
||||
void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon)
|
||||
{
|
||||
XiveRouter *xrtr = XIVE_ROUTER(xive);
|
||||
uint8_t blk = xive->chip->chip_id;
|
||||
uint8_t blk = pnv_xive_block_id(xive);
|
||||
uint8_t chip_id = xive->chip->chip_id;
|
||||
uint32_t srcno0 = XIVE_EAS(blk, 0);
|
||||
uint32_t nr_ipis = pnv_xive_nr_ipis(xive);
|
||||
uint32_t nr_ends = pnv_xive_nr_ends(xive);
|
||||
uint32_t nr_ipis = pnv_xive_nr_ipis(xive, blk);
|
||||
XiveEAS eas;
|
||||
XiveEND end;
|
||||
XiveNVT nvt;
|
||||
int i;
|
||||
uint64_t xive_nvt_per_subpage;
|
||||
|
||||
monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0,
|
||||
srcno0 + nr_ipis - 1);
|
||||
monitor_printf(mon, "XIVE[%x] #%d Source %08x .. %08x\n", chip_id, blk,
|
||||
srcno0, srcno0 + nr_ipis - 1);
|
||||
xive_source_pic_print_info(&xive->ipi_source, srcno0, mon);
|
||||
|
||||
monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0,
|
||||
srcno0 + nr_ipis - 1);
|
||||
monitor_printf(mon, "XIVE[%x] #%d EAT %08x .. %08x\n", chip_id, blk,
|
||||
srcno0, srcno0 + nr_ipis - 1);
|
||||
for (i = 0; i < nr_ipis; i++) {
|
||||
if (xive_router_get_eas(xrtr, blk, i, &eas)) {
|
||||
break;
|
||||
@ -1600,21 +1746,25 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon)
|
||||
}
|
||||
}
|
||||
|
||||
monitor_printf(mon, "XIVE[%x] ENDT %08x .. %08x\n", blk, 0, nr_ends - 1);
|
||||
for (i = 0; i < nr_ends; i++) {
|
||||
if (xive_router_get_end(xrtr, blk, i, &end)) {
|
||||
break;
|
||||
}
|
||||
xive_end_pic_print_info(&end, i, mon);
|
||||
monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk);
|
||||
i = 0;
|
||||
while (!xive_router_get_end(xrtr, blk, i, &end)) {
|
||||
xive_end_pic_print_info(&end, i++, mon);
|
||||
}
|
||||
|
||||
monitor_printf(mon, "XIVE[%x] END Escalation %08x .. %08x\n", blk, 0,
|
||||
nr_ends - 1);
|
||||
for (i = 0; i < nr_ends; i++) {
|
||||
if (xive_router_get_end(xrtr, blk, i, &end)) {
|
||||
break;
|
||||
monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk);
|
||||
i = 0;
|
||||
while (!xive_router_get_end(xrtr, blk, i, &end)) {
|
||||
xive_end_eas_pic_print_info(&end, i++, mon);
|
||||
}
|
||||
|
||||
monitor_printf(mon, "XIVE[%x] #%d NVTT %08x .. %08x\n", chip_id, blk,
|
||||
0, XIVE_NVT_COUNT - 1);
|
||||
xive_nvt_per_subpage = pnv_xive_vst_per_subpage(xive, VST_TSEL_VPDT);
|
||||
for (i = 0; i < XIVE_NVT_COUNT; i += xive_nvt_per_subpage) {
|
||||
while (!xive_router_get_nvt(xrtr, blk, i, &nvt)) {
|
||||
xive_nvt_pic_print_info(&nvt, i++, mon);
|
||||
}
|
||||
xive_end_eas_pic_print_info(&end, i, mon);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1624,12 +1774,6 @@ static void pnv_xive_reset(void *dev)
|
||||
XiveSource *xsrc = &xive->ipi_source;
|
||||
XiveENDSource *end_xsrc = &xive->end_source;
|
||||
|
||||
/*
|
||||
* Use the PnvChip id to identify the XIVE interrupt controller.
|
||||
* It can be overriden by configuration at runtime.
|
||||
*/
|
||||
xive->tctx_chipid = xive->chip->chip_id;
|
||||
|
||||
/* Default page size (Should be changed at runtime to 64k) */
|
||||
xive->ic_shift = xive->vc_shift = xive->pc_shift = 12;
|
||||
|
||||
@ -1675,17 +1819,8 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp)
|
||||
XiveSource *xsrc = &xive->ipi_source;
|
||||
XiveENDSource *end_xsrc = &xive->end_source;
|
||||
Error *local_err = NULL;
|
||||
Object *obj;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "chip", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'chip' not found: ");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The PnvChip id identifies the XIVE interrupt controller. */
|
||||
xive->chip = PNV_CHIP(obj);
|
||||
assert(xive->chip);
|
||||
|
||||
/*
|
||||
* The XiveSource and XiveENDSource objects are realized with the
|
||||
@ -1695,8 +1830,8 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
object_property_set_int(OBJECT(xsrc), PNV_XIVE_NR_IRQS, "nr-irqs",
|
||||
&error_fatal);
|
||||
object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive),
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(xsrc), OBJECT(xive), "xive",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1705,8 +1840,8 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
object_property_set_int(OBJECT(end_xsrc), PNV_XIVE_NR_ENDS, "nr-ends",
|
||||
&error_fatal);
|
||||
object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive),
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(end_xsrc), OBJECT(xive), "xive",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1766,7 +1901,7 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp)
|
||||
"xive-pc", PNV9_XIVE_PC_SIZE);
|
||||
|
||||
/* Thread Interrupt Management Area (Direct) */
|
||||
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops,
|
||||
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &pnv_xive_tm_ops,
|
||||
xive, "xive-tima", PNV9_XIVE_TM_SIZE);
|
||||
|
||||
qemu_register_reset(pnv_xive_reset, dev);
|
||||
@ -1800,6 +1935,8 @@ static Property pnv_xive_properties[] = {
|
||||
DEFINE_PROP_UINT64("vc-bar", PnvXive, vc_base, 0),
|
||||
DEFINE_PROP_UINT64("pc-bar", PnvXive, pc_base, 0),
|
||||
DEFINE_PROP_UINT64("tm-bar", PnvXive, tm_base, 0),
|
||||
/* The PnvChip id identifies the XIVE interrupt controller. */
|
||||
DEFINE_PROP_LINK("chip", PnvXive, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1809,6 +1946,7 @@ static void pnv_xive_class_init(ObjectClass *klass, void *data)
|
||||
PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
|
||||
XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass);
|
||||
XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass);
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_CLASS(klass);
|
||||
|
||||
xdc->dt_xscom = pnv_xive_dt_xscom;
|
||||
|
||||
@ -1821,9 +1959,10 @@ static void pnv_xive_class_init(ObjectClass *klass, void *data)
|
||||
xrc->write_end = pnv_xive_write_end;
|
||||
xrc->get_nvt = pnv_xive_get_nvt;
|
||||
xrc->write_nvt = pnv_xive_write_nvt;
|
||||
xrc->get_tctx = pnv_xive_get_tctx;
|
||||
xrc->get_block_id = pnv_xive_get_block_id;
|
||||
|
||||
xnc->notify = pnv_xive_notify;
|
||||
xpc->match_nvt = pnv_xive_match_nvt;
|
||||
};
|
||||
|
||||
static const TypeInfo pnv_xive_info = {
|
||||
|
@ -205,6 +205,35 @@ void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable)
|
||||
memory_region_set_enabled(&xive->end_source.esb_mmio, false);
|
||||
}
|
||||
|
||||
static void spapr_xive_tm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
XiveTCTX *tctx = spapr_cpu_state(POWERPC_CPU(current_cpu))->tctx;
|
||||
|
||||
xive_tctx_tm_write(XIVE_PRESENTER(opaque), tctx, offset, value, size);
|
||||
}
|
||||
|
||||
static uint64_t spapr_xive_tm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
XiveTCTX *tctx = spapr_cpu_state(POWERPC_CPU(current_cpu))->tctx;
|
||||
|
||||
return xive_tctx_tm_read(XIVE_PRESENTER(opaque), tctx, offset, size);
|
||||
}
|
||||
|
||||
const MemoryRegionOps spapr_xive_tm_ops = {
|
||||
.read = spapr_xive_tm_read,
|
||||
.write = spapr_xive_tm_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static void spapr_xive_end_reset(XiveEND *end)
|
||||
{
|
||||
memset(end, 0, sizeof(*end));
|
||||
@ -276,8 +305,8 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
object_property_set_int(OBJECT(xsrc), xive->nr_irqs, "nr-irqs",
|
||||
&error_fatal);
|
||||
object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive),
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(xsrc), OBJECT(xive), "xive",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -290,8 +319,8 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
object_property_set_int(OBJECT(end_xsrc), xive->nr_irqs, "nr-ends",
|
||||
&error_fatal);
|
||||
object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive),
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(end_xsrc), OBJECT(xive), "xive",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -314,8 +343,8 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||
qemu_register_reset(spapr_xive_reset, dev);
|
||||
|
||||
/* TIMA initialization */
|
||||
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive,
|
||||
"xive.tima", 4ull << TM_SHIFT);
|
||||
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &spapr_xive_tm_ops,
|
||||
xive, "xive.tima", 4ull << TM_SHIFT);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
|
||||
|
||||
/*
|
||||
@ -398,11 +427,55 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk,
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static XiveTCTX *spapr_xive_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUState *cs;
|
||||
int count = 0;
|
||||
|
||||
return spapr_cpu_state(cpu)->tctx;
|
||||
CPU_FOREACH(cs) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
XiveTCTX *tctx = spapr_cpu_state(cpu)->tctx;
|
||||
int ring;
|
||||
|
||||
/*
|
||||
* Skip partially initialized vCPUs. This can happen when
|
||||
* vCPUs are hotplugged.
|
||||
*/
|
||||
if (!tctx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the thread context CAM lines and record matches.
|
||||
*/
|
||||
ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk, nvt_idx,
|
||||
cam_ignore, logic_serv);
|
||||
/*
|
||||
* Save the matching thread interrupt context and follow on to
|
||||
* check for duplicates which are invalid.
|
||||
*/
|
||||
if (ring != -1) {
|
||||
if (match->tctx) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread "
|
||||
"context NVT %x/%x\n", nvt_blk, nvt_idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
match->ring = ring;
|
||||
match->tctx = tctx;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static uint8_t spapr_xive_get_block_id(XiveRouter *xrtr)
|
||||
{
|
||||
return SPAPR_XIVE_BLOCK_ID;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_spapr_xive_end = {
|
||||
@ -651,12 +724,14 @@ static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers,
|
||||
plat_res_int_priorities, sizeof(plat_res_int_priorities)));
|
||||
}
|
||||
|
||||
static int spapr_xive_activate(SpaprInterruptController *intc, Error **errp)
|
||||
static int spapr_xive_activate(SpaprInterruptController *intc,
|
||||
uint32_t nr_servers, Error **errp)
|
||||
{
|
||||
SpaprXive *xive = SPAPR_XIVE(intc);
|
||||
|
||||
if (kvm_enabled()) {
|
||||
int rc = spapr_irq_init_kvm(kvmppc_xive_connect, intc, errp);
|
||||
int rc = spapr_irq_init_kvm(kvmppc_xive_connect, intc, nr_servers,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
@ -684,6 +759,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass);
|
||||
SpaprInterruptControllerClass *sicc = SPAPR_INTC_CLASS(klass);
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_CLASS(klass);
|
||||
|
||||
dc->desc = "sPAPR XIVE Interrupt Controller";
|
||||
dc->props = spapr_xive_properties;
|
||||
@ -695,7 +771,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
|
||||
xrc->write_end = spapr_xive_write_end;
|
||||
xrc->get_nvt = spapr_xive_get_nvt;
|
||||
xrc->write_nvt = spapr_xive_write_nvt;
|
||||
xrc->get_tctx = spapr_xive_get_tctx;
|
||||
xrc->get_block_id = spapr_xive_get_block_id;
|
||||
|
||||
sicc->activate = spapr_xive_activate;
|
||||
sicc->deactivate = spapr_xive_deactivate;
|
||||
@ -708,6 +784,8 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
|
||||
sicc->print_info = spapr_xive_print_info;
|
||||
sicc->dt = spapr_xive_dt;
|
||||
sicc->post_load = spapr_xive_post_load;
|
||||
|
||||
xpc->match_nvt = spapr_xive_match_nvt;
|
||||
}
|
||||
|
||||
static const TypeInfo spapr_xive_info = {
|
||||
|
@ -152,7 +152,8 @@ void kvmppc_xive_cpu_synchronize_state(XiveTCTX *tctx, Error **errp)
|
||||
|
||||
void kvmppc_xive_cpu_connect(XiveTCTX *tctx, Error **errp)
|
||||
{
|
||||
SpaprXive *xive = SPAPR_MACHINE(qdev_get_machine())->xive;
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
SpaprXive *xive = SPAPR_MACHINE(ms)->xive;
|
||||
unsigned long vcpu_id;
|
||||
int ret;
|
||||
|
||||
@ -171,8 +172,16 @@ void kvmppc_xive_cpu_connect(XiveTCTX *tctx, Error **errp)
|
||||
ret = kvm_vcpu_enable_cap(tctx->cs, KVM_CAP_PPC_IRQ_XIVE, 0, xive->fd,
|
||||
vcpu_id, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "XIVE: unable to connect CPU%ld to KVM device: %s",
|
||||
Error *local_err = NULL;
|
||||
|
||||
error_setg(&local_err,
|
||||
"XIVE: unable to connect CPU%ld to KVM device: %s",
|
||||
vcpu_id, strerror(errno));
|
||||
if (errno == ENOSPC) {
|
||||
error_append_hint(&local_err, "Try -smp maxcpus=N with N < %u\n",
|
||||
ms->smp.max_cpus);
|
||||
}
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -354,32 +363,20 @@ static void kvmppc_xive_source_get_state(XiveSource *xsrc)
|
||||
void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val)
|
||||
{
|
||||
XiveSource *xsrc = opaque;
|
||||
SpaprXive *xive = SPAPR_XIVE(xsrc->xive);
|
||||
struct kvm_irq_level args;
|
||||
int rc;
|
||||
|
||||
/* The KVM XIVE device should be in use */
|
||||
assert(xive->fd != -1);
|
||||
|
||||
args.irq = srcno;
|
||||
if (!xive_source_irq_is_lsi(xsrc, srcno)) {
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
args.level = KVM_INTERRUPT_SET;
|
||||
} else {
|
||||
if (val) {
|
||||
xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
|
||||
args.level = KVM_INTERRUPT_SET_LEVEL;
|
||||
} else {
|
||||
xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
|
||||
args.level = KVM_INTERRUPT_UNSET;
|
||||
}
|
||||
}
|
||||
rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args);
|
||||
if (rc < 0) {
|
||||
error_report("XIVE: kvm_irq_line() failed : %s", strerror(errno));
|
||||
}
|
||||
|
||||
xive_esb_trigger(xsrc, srcno);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -740,7 +737,8 @@ static void *kvmppc_xive_mmap(SpaprXive *xive, int pgoff, size_t len,
|
||||
* All the XIVE memory regions are now backed by mappings from the KVM
|
||||
* XIVE device.
|
||||
*/
|
||||
int kvmppc_xive_connect(SpaprInterruptController *intc, Error **errp)
|
||||
int kvmppc_xive_connect(SpaprInterruptController *intc, uint32_t nr_servers,
|
||||
Error **errp)
|
||||
{
|
||||
SpaprXive *xive = SPAPR_XIVE(intc);
|
||||
XiveSource *xsrc = &xive->source;
|
||||
@ -769,6 +767,16 @@ int kvmppc_xive_connect(SpaprInterruptController *intc, Error **errp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Tell KVM about the # of VCPUs we may have */
|
||||
if (kvm_device_check_attr(xive->fd, KVM_DEV_XIVE_GRP_CTRL,
|
||||
KVM_DEV_XIVE_NR_SERVERS)) {
|
||||
if (kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_CTRL,
|
||||
KVM_DEV_XIVE_NR_SERVERS, &nr_servers, true,
|
||||
&local_err)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Source ESB pages - KVM mapping
|
||||
*/
|
||||
|
@ -289,9 +289,6 @@ void icp_reset(ICPState *icp)
|
||||
icp->pending_priority = 0xff;
|
||||
icp->mfrr = 0xff;
|
||||
|
||||
/* Make all outputs are deasserted */
|
||||
qemu_set_irq(icp->output, 0);
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
@ -305,33 +302,13 @@ void icp_reset(ICPState *icp)
|
||||
static void icp_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ICPState *icp = ICP(dev);
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
Object *obj;
|
||||
Error *err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), ICP_PROP_XICS, &err);
|
||||
if (!obj) {
|
||||
error_propagate_prepend(errp, err,
|
||||
"required link '" ICP_PROP_XICS
|
||||
"' not found: ");
|
||||
return;
|
||||
}
|
||||
assert(icp->xics);
|
||||
assert(icp->cs);
|
||||
|
||||
icp->xics = XICS_FABRIC(obj);
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err);
|
||||
if (!obj) {
|
||||
error_propagate_prepend(errp, err,
|
||||
"required link '" ICP_PROP_CPU
|
||||
"' not found: ");
|
||||
return;
|
||||
}
|
||||
|
||||
cpu = POWERPC_CPU(obj);
|
||||
icp->cs = CPU(obj);
|
||||
|
||||
env = &cpu->env;
|
||||
env = &POWERPC_CPU(icp->cs)->env;
|
||||
switch (PPC_INPUT(env)) {
|
||||
case PPC_FLAGS_INPUT_POWER7:
|
||||
icp->output = env->irq_inputs[POWER7_INPUT_INT];
|
||||
@ -368,12 +345,20 @@ static void icp_unrealize(DeviceState *dev, Error **errp)
|
||||
vmstate_unregister(NULL, &vmstate_icp_server, icp);
|
||||
}
|
||||
|
||||
static Property icp_properties[] = {
|
||||
DEFINE_PROP_LINK(ICP_PROP_XICS, ICPState, xics, TYPE_XICS_FABRIC,
|
||||
XICSFabric *),
|
||||
DEFINE_PROP_LINK(ICP_PROP_CPU, ICPState, cs, TYPE_CPU, CPUState *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void icp_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = icp_realize;
|
||||
dc->unrealize = icp_unrealize;
|
||||
dc->props = icp_properties;
|
||||
/*
|
||||
* Reason: part of XICS interrupt controller, needs to be wired up
|
||||
* by icp_create().
|
||||
@ -397,11 +382,8 @@ Object *icp_create(Object *cpu, const char *type, XICSFabric *xi, Error **errp)
|
||||
obj = object_new(type);
|
||||
object_property_add_child(cpu, type, obj, &error_abort);
|
||||
object_unref(obj);
|
||||
object_ref(OBJECT(xi));
|
||||
object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(xi),
|
||||
&error_abort);
|
||||
object_ref(cpu);
|
||||
object_property_add_const_link(obj, ICP_PROP_CPU, cpu, &error_abort);
|
||||
object_property_set_link(obj, OBJECT(xi), ICP_PROP_XICS, &error_abort);
|
||||
object_property_set_link(obj, cpu, ICP_PROP_CPU, &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
object_unparent(obj);
|
||||
@ -416,8 +398,6 @@ void icp_destroy(ICPState *icp)
|
||||
{
|
||||
Object *obj = OBJECT(icp);
|
||||
|
||||
object_unref(object_property_get_link(obj, ICP_PROP_CPU, &error_abort));
|
||||
object_unref(object_property_get_link(obj, ICP_PROP_XICS, &error_abort));
|
||||
object_unparent(obj);
|
||||
}
|
||||
|
||||
@ -609,17 +589,8 @@ static void ics_reset_handler(void *dev)
|
||||
static void ics_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ICSState *ics = ICS(dev);
|
||||
Error *local_err = NULL;
|
||||
Object *obj;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &local_err);
|
||||
if (!obj) {
|
||||
error_propagate_prepend(errp, local_err,
|
||||
"required link '" ICS_PROP_XICS
|
||||
"' not found: ");
|
||||
return;
|
||||
}
|
||||
ics->xics = XICS_FABRIC(obj);
|
||||
assert(ics->xics);
|
||||
|
||||
if (!ics->nr_irqs) {
|
||||
error_setg(errp, "Number of interrupts needs to be greater 0");
|
||||
@ -699,6 +670,8 @@ static const VMStateDescription vmstate_ics = {
|
||||
|
||||
static Property ics_properties[] = {
|
||||
DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0),
|
||||
DEFINE_PROP_LINK(ICS_PROP_XICS, ICSState, xics, TYPE_XICS_FABRIC,
|
||||
XICSFabric *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -165,8 +165,15 @@ void icp_kvm_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, kernel_xics_fd, vcpu_id);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Unable to connect CPU%ld to kernel XICS: %s", vcpu_id,
|
||||
strerror(errno));
|
||||
Error *local_err = NULL;
|
||||
|
||||
error_setg(&local_err, "Unable to connect CPU%ld to kernel XICS: %s",
|
||||
vcpu_id, strerror(errno));
|
||||
if (errno == ENOSPC) {
|
||||
error_append_hint(&local_err, "Try -smp maxcpus=N with N < %u\n",
|
||||
MACHINE(qdev_get_machine())->smp.max_cpus);
|
||||
}
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
enabled_icp = g_malloc(sizeof(*enabled_icp));
|
||||
@ -342,7 +349,8 @@ void ics_kvm_set_irq(ICSState *ics, int srcno, int val)
|
||||
}
|
||||
}
|
||||
|
||||
int xics_kvm_connect(SpaprInterruptController *intc, Error **errp)
|
||||
int xics_kvm_connect(SpaprInterruptController *intc, uint32_t nr_servers,
|
||||
Error **errp)
|
||||
{
|
||||
ICSState *ics = ICS_SPAPR(intc);
|
||||
int rc;
|
||||
@ -398,6 +406,16 @@ int xics_kvm_connect(SpaprInterruptController *intc, Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Tell KVM about the # of VCPUs we may have (POWER9 and newer only) */
|
||||
if (kvm_device_check_attr(rc, KVM_DEV_XICS_GRP_CTRL,
|
||||
KVM_DEV_XICS_NR_SERVERS)) {
|
||||
if (kvm_device_access(rc, KVM_DEV_XICS_GRP_CTRL,
|
||||
KVM_DEV_XICS_NR_SERVERS, &nr_servers, true,
|
||||
&local_err)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
kernel_xics_fd = rc;
|
||||
kvm_kernel_irqchip = true;
|
||||
kvm_msi_via_irqfd_allowed = true;
|
||||
|
@ -422,10 +422,11 @@ static int xics_spapr_post_load(SpaprInterruptController *intc, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xics_spapr_activate(SpaprInterruptController *intc, Error **errp)
|
||||
static int xics_spapr_activate(SpaprInterruptController *intc,
|
||||
uint32_t nr_servers, Error **errp)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
return spapr_irq_init_kvm(xics_kvm_connect, intc, errp);
|
||||
return spapr_irq_init_kvm(xics_kvm_connect, intc, nr_servers, errp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
400
hw/intc/xive.c
400
hw/intc/xive.c
@ -47,12 +47,6 @@ static uint8_t ipb_to_pipr(uint8_t ibp)
|
||||
return ibp ? clz32((uint32_t)ibp << 24) : 0xff;
|
||||
}
|
||||
|
||||
static void ipb_update(uint8_t *regs, uint8_t priority)
|
||||
{
|
||||
regs[TM_IPB] |= priority_to_ipb(priority);
|
||||
regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]);
|
||||
}
|
||||
|
||||
static uint8_t exception_mask(uint8_t ring)
|
||||
{
|
||||
switch (ring) {
|
||||
@ -135,6 +129,15 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
|
||||
xive_tctx_notify(tctx, ring);
|
||||
}
|
||||
|
||||
void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb)
|
||||
{
|
||||
uint8_t *regs = &tctx->regs[ring];
|
||||
|
||||
regs[TM_IPB] |= ipb;
|
||||
regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]);
|
||||
xive_tctx_notify(tctx, ring);
|
||||
}
|
||||
|
||||
static inline uint32_t xive_tctx_word2(uint8_t *ring)
|
||||
{
|
||||
return *((uint32_t *) &ring[TM_WORD2]);
|
||||
@ -144,19 +147,20 @@ static inline uint32_t xive_tctx_word2(uint8_t *ring)
|
||||
* XIVE Thread Interrupt Management Area (TIMA)
|
||||
*/
|
||||
|
||||
static void xive_tm_set_hv_cppr(XiveTCTX *tctx, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
static void xive_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
xive_tctx_set_cppr(tctx, TM_QW3_HV_PHYS, value & 0xff);
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_ack_hv_reg(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
static uint64_t xive_tm_ack_hv_reg(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, unsigned size)
|
||||
{
|
||||
return xive_tctx_accept(tctx, TM_QW3_HV_PHYS);
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset,
|
||||
unsigned size)
|
||||
static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]);
|
||||
uint32_t qw2w2;
|
||||
@ -166,13 +170,14 @@ static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset,
|
||||
return qw2w2;
|
||||
}
|
||||
|
||||
static void xive_tm_vt_push(XiveTCTX *tctx, hwaddr offset,
|
||||
static void xive_tm_vt_push(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = value & 0xff;
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_vt_poll(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
static uint64_t xive_tm_vt_poll(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, unsigned size)
|
||||
{
|
||||
return tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] & 0xff;
|
||||
}
|
||||
@ -315,13 +320,14 @@ static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
* state changes (side effects) in addition to setting/returning the
|
||||
* interrupt management area context of the processor thread.
|
||||
*/
|
||||
static uint64_t xive_tm_ack_os_reg(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
static uint64_t xive_tm_ack_os_reg(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, unsigned size)
|
||||
{
|
||||
return xive_tctx_accept(tctx, TM_QW1_OS);
|
||||
}
|
||||
|
||||
static void xive_tm_set_os_cppr(XiveTCTX *tctx, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
static void xive_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
xive_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff);
|
||||
}
|
||||
@ -330,24 +336,114 @@ static void xive_tm_set_os_cppr(XiveTCTX *tctx, hwaddr offset,
|
||||
* Adjust the IPB to allow a CPU to process event queues of other
|
||||
* priorities during one physical interrupt cycle.
|
||||
*/
|
||||
static void xive_tm_set_os_pending(XiveTCTX *tctx, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
ipb_update(&tctx->regs[TM_QW1_OS], value & 0xff);
|
||||
xive_tctx_notify(tctx, TM_QW1_OS);
|
||||
xive_tctx_ipb_update(tctx, TM_QW1_OS, priority_to_ipb(value & 0xff));
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_pull_os_ctx(XiveTCTX *tctx, hwaddr offset,
|
||||
unsigned size)
|
||||
static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk,
|
||||
uint32_t *nvt_idx, bool *vo)
|
||||
{
|
||||
uint32_t qw1w2_prev = xive_tctx_word2(&tctx->regs[TM_QW1_OS]);
|
||||
uint32_t qw1w2;
|
||||
if (nvt_blk) {
|
||||
*nvt_blk = xive_nvt_blk(cam);
|
||||
}
|
||||
if (nvt_idx) {
|
||||
*nvt_idx = xive_nvt_idx(cam);
|
||||
}
|
||||
if (vo) {
|
||||
*vo = !!(cam & TM_QW1W2_VO);
|
||||
}
|
||||
}
|
||||
|
||||
qw1w2 = xive_set_field32(TM_QW1W2_VO, qw1w2_prev, 0);
|
||||
memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4);
|
||||
static uint32_t xive_tctx_get_os_cam(XiveTCTX *tctx, uint8_t *nvt_blk,
|
||||
uint32_t *nvt_idx, bool *vo)
|
||||
{
|
||||
uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]);
|
||||
uint32_t cam = be32_to_cpu(qw1w2);
|
||||
|
||||
xive_os_cam_decode(cam, nvt_blk, nvt_idx, vo);
|
||||
return qw1w2;
|
||||
}
|
||||
|
||||
static void xive_tctx_set_os_cam(XiveTCTX *tctx, uint32_t qw1w2)
|
||||
{
|
||||
memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4);
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32_t qw1w2;
|
||||
uint32_t qw1w2_new;
|
||||
uint8_t nvt_blk;
|
||||
uint32_t nvt_idx;
|
||||
bool vo;
|
||||
|
||||
qw1w2 = xive_tctx_get_os_cam(tctx, &nvt_blk, &nvt_idx, &vo);
|
||||
|
||||
if (!vo) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVT %x/%x !?\n",
|
||||
nvt_blk, nvt_idx);
|
||||
}
|
||||
|
||||
/* Invalidate CAM line */
|
||||
qw1w2_new = xive_set_field32(TM_QW1W2_VO, qw1w2, 0);
|
||||
xive_tctx_set_os_cam(tctx, qw1w2_new);
|
||||
return qw1w2;
|
||||
}
|
||||
|
||||
static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx)
|
||||
{
|
||||
XiveNVT nvt;
|
||||
uint8_t ipb;
|
||||
|
||||
/*
|
||||
* Grab the associated NVT to pull the pending bits, and merge
|
||||
* them with the IPB of the thread interrupt context registers
|
||||
*/
|
||||
if (xive_router_get_nvt(xrtr, nvt_blk, nvt_idx, &nvt)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVT %x/%x\n",
|
||||
nvt_blk, nvt_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
ipb = xive_get_field32(NVT_W4_IPB, nvt.w4);
|
||||
|
||||
if (ipb) {
|
||||
/* Reset the NVT value */
|
||||
nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, 0);
|
||||
xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4);
|
||||
|
||||
/* Merge in current context */
|
||||
xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Updating the OS CAM line can trigger a resend of interrupt
|
||||
*/
|
||||
static void xive_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
uint32_t cam = value;
|
||||
uint32_t qw1w2 = cpu_to_be32(cam);
|
||||
uint8_t nvt_blk;
|
||||
uint32_t nvt_idx;
|
||||
bool vo;
|
||||
|
||||
xive_os_cam_decode(cam, &nvt_blk, &nvt_idx, &vo);
|
||||
|
||||
/* First update the registers */
|
||||
xive_tctx_set_os_cam(tctx, qw1w2);
|
||||
|
||||
/* Check the interrupt pending bits */
|
||||
if (vo) {
|
||||
xive_tctx_need_resend(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define a mapping of "special" operations depending on the TIMA page
|
||||
* offset and the size of the operation.
|
||||
@ -356,9 +452,11 @@ typedef struct XiveTmOp {
|
||||
uint8_t page_offset;
|
||||
uint32_t op_offset;
|
||||
unsigned size;
|
||||
void (*write_handler)(XiveTCTX *tctx, hwaddr offset, uint64_t value,
|
||||
unsigned size);
|
||||
uint64_t (*read_handler)(XiveTCTX *tctx, hwaddr offset, unsigned size);
|
||||
void (*write_handler)(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset,
|
||||
uint64_t value, unsigned size);
|
||||
uint64_t (*read_handler)(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
|
||||
unsigned size);
|
||||
} XiveTmOp;
|
||||
|
||||
static const XiveTmOp xive_tm_operations[] = {
|
||||
@ -367,6 +465,7 @@ static const XiveTmOp xive_tm_operations[] = {
|
||||
* effects
|
||||
*/
|
||||
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive_tm_push_os_ctx, NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, xive_tm_vt_poll },
|
||||
@ -404,8 +503,8 @@ static const XiveTmOp *xive_tm_find_op(hwaddr offset, unsigned size, bool write)
|
||||
/*
|
||||
* TIMA MMIO handlers
|
||||
*/
|
||||
void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
const XiveTmOp *xto;
|
||||
|
||||
@ -422,7 +521,7 @@ void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value,
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA "
|
||||
"@%"HWADDR_PRIx"\n", offset);
|
||||
} else {
|
||||
xto->write_handler(tctx, offset, value, size);
|
||||
xto->write_handler(xptr, tctx, offset, value, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -432,7 +531,7 @@ void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value,
|
||||
*/
|
||||
xto = xive_tm_find_op(offset, size, true);
|
||||
if (xto) {
|
||||
xto->write_handler(tctx, offset, value, size);
|
||||
xto->write_handler(xptr, tctx, offset, value, size);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -442,7 +541,8 @@ void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value,
|
||||
xive_tm_raw_write(tctx, offset, value, size);
|
||||
}
|
||||
|
||||
uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
const XiveTmOp *xto;
|
||||
|
||||
@ -460,7 +560,7 @@ uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
"@%"HWADDR_PRIx"\n", offset);
|
||||
return -1;
|
||||
}
|
||||
return xto->read_handler(tctx, offset, size);
|
||||
return xto->read_handler(xptr, tctx, offset, size);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -468,7 +568,7 @@ uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
*/
|
||||
xto = xive_tm_find_op(offset, size, false);
|
||||
if (xto) {
|
||||
return xto->read_handler(tctx, offset, size);
|
||||
return xto->read_handler(xptr, tctx, offset, size);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -477,35 +577,6 @@ uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
return xive_tm_raw_read(tctx, offset, size);
|
||||
}
|
||||
|
||||
static void xive_tm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
XiveTCTX *tctx = xive_router_get_tctx(XIVE_ROUTER(opaque), current_cpu);
|
||||
|
||||
xive_tctx_tm_write(tctx, offset, value, size);
|
||||
}
|
||||
|
||||
static uint64_t xive_tm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
XiveTCTX *tctx = xive_router_get_tctx(XIVE_ROUTER(opaque), current_cpu);
|
||||
|
||||
return xive_tctx_tm_read(tctx, offset, size);
|
||||
}
|
||||
|
||||
const MemoryRegionOps xive_tm_ops = {
|
||||
.read = xive_tm_read,
|
||||
.write = xive_tm_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static char *xive_tctx_ring_print(uint8_t *ring)
|
||||
{
|
||||
uint32_t w2 = xive_tctx_word2(ring);
|
||||
@ -580,19 +651,11 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp)
|
||||
XiveTCTX *tctx = XIVE_TCTX(dev);
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "cpu", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'cpu' not found: ");
|
||||
return;
|
||||
}
|
||||
|
||||
cpu = POWERPC_CPU(obj);
|
||||
tctx->cs = CPU(obj);
|
||||
assert(tctx->cs);
|
||||
|
||||
cpu = POWERPC_CPU(tctx->cs);
|
||||
env = &cpu->env;
|
||||
switch (PPC_INPUT(env)) {
|
||||
case PPC_FLAGS_INPUT_POWER9:
|
||||
@ -662,6 +725,11 @@ static const VMStateDescription vmstate_xive_tctx = {
|
||||
},
|
||||
};
|
||||
|
||||
static Property xive_tctx_properties[] = {
|
||||
DEFINE_PROP_LINK("cpu", XiveTCTX, cs, TYPE_CPU, CPUState *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void xive_tctx_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@ -669,6 +737,7 @@ static void xive_tctx_class_init(ObjectClass *klass, void *data)
|
||||
dc->desc = "XIVE Interrupt Thread Context";
|
||||
dc->realize = xive_tctx_realize;
|
||||
dc->vmsd = &vmstate_xive_tctx;
|
||||
dc->props = xive_tctx_properties;
|
||||
/*
|
||||
* Reason: part of XIVE interrupt controller, needs to be wired up
|
||||
* by xive_tctx_create().
|
||||
@ -691,8 +760,7 @@ Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp)
|
||||
obj = object_new(TYPE_XIVE_TCTX);
|
||||
object_property_add_child(cpu, TYPE_XIVE_TCTX, obj, &error_abort);
|
||||
object_unref(obj);
|
||||
object_ref(cpu);
|
||||
object_property_add_const_link(obj, "cpu", cpu, &error_abort);
|
||||
object_property_set_link(obj, cpu, "cpu", &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
goto error;
|
||||
@ -710,7 +778,6 @@ void xive_tctx_destroy(XiveTCTX *tctx)
|
||||
{
|
||||
Object *obj = OBJECT(tctx);
|
||||
|
||||
object_unref(object_property_get_link(obj, "cpu", &error_abort));
|
||||
object_unparent(obj);
|
||||
}
|
||||
|
||||
@ -1064,17 +1131,8 @@ static void xive_source_reset(void *dev)
|
||||
static void xive_source_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XiveSource *xsrc = XIVE_SOURCE(dev);
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'xive' not found: ");
|
||||
return;
|
||||
}
|
||||
|
||||
xsrc->xive = XIVE_NOTIFIER(obj);
|
||||
assert(xsrc->xive);
|
||||
|
||||
if (!xsrc->nr_irqs) {
|
||||
error_setg(errp, "Number of interrupt needs to be greater than 0");
|
||||
@ -1120,6 +1178,8 @@ static Property xive_source_properties[] = {
|
||||
DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
|
||||
DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
|
||||
DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
|
||||
DEFINE_PROP_LINK("xive", XiveSource, xive, TYPE_XIVE_NOTIFIER,
|
||||
XiveNotifier *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1311,11 +1371,11 @@ int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
return xrc->write_nvt(xrtr, nvt_blk, nvt_idx, nvt, word_number);
|
||||
}
|
||||
|
||||
XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
static int xive_router_get_block_id(XiveRouter *xrtr)
|
||||
{
|
||||
XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr);
|
||||
XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr);
|
||||
|
||||
return xrc->get_tctx(xrtr, cs);
|
||||
return xrc->get_block_id(xrtr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1323,20 +1383,22 @@ XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
*
|
||||
* chip << 19 | 0000000 0 0001 thread (7Bit)
|
||||
*/
|
||||
static uint32_t xive_tctx_hw_cam_line(XiveTCTX *tctx)
|
||||
static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx)
|
||||
{
|
||||
CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env;
|
||||
uint32_t pir = env->spr_cb[SPR_PIR].default_value;
|
||||
uint8_t blk = xive_router_get_block_id(XIVE_ROUTER(xptr));
|
||||
|
||||
return xive_nvt_cam_line((pir >> 8) & 0xf, 1 << 7 | (pir & 0x7f));
|
||||
return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f));
|
||||
}
|
||||
|
||||
/*
|
||||
* The thread context register words are in big-endian format.
|
||||
*/
|
||||
static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint32_t logic_serv)
|
||||
int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint32_t logic_serv)
|
||||
{
|
||||
uint32_t cam = xive_nvt_cam_line(nvt_blk, nvt_idx);
|
||||
uint32_t qw3w2 = xive_tctx_word2(&tctx->regs[TM_QW3_HV_PHYS]);
|
||||
@ -1364,7 +1426,7 @@ static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format,
|
||||
|
||||
/* PHYS ring */
|
||||
if ((be32_to_cpu(qw3w2) & TM_QW3W2_VT) &&
|
||||
cam == xive_tctx_hw_cam_line(tctx)) {
|
||||
cam == xive_tctx_hw_cam_line(xptr, tctx)) {
|
||||
return TM_QW3_HV_PHYS;
|
||||
}
|
||||
|
||||
@ -1393,71 +1455,6 @@ static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format,
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct XiveTCTXMatch {
|
||||
XiveTCTX *tctx;
|
||||
uint8_t ring;
|
||||
} XiveTCTXMatch;
|
||||
|
||||
static bool xive_presenter_match(XiveRouter *xrtr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
||||
/*
|
||||
* TODO (PowerNV): handle chip_id overwrite of block field for
|
||||
* hardwired CAM compares
|
||||
*/
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
XiveTCTX *tctx = xive_router_get_tctx(xrtr, cs);
|
||||
int ring;
|
||||
|
||||
/*
|
||||
* Skip partially initialized vCPUs. This can happen when
|
||||
* vCPUs are hotplugged.
|
||||
*/
|
||||
if (!tctx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* HW checks that the CPU is enabled in the Physical Thread
|
||||
* Enable Register (PTER).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check the thread context CAM lines and record matches. We
|
||||
* will handle CPU exception delivery later
|
||||
*/
|
||||
ring = xive_presenter_tctx_match(tctx, format, nvt_blk, nvt_idx,
|
||||
cam_ignore, logic_serv);
|
||||
/*
|
||||
* Save the context and follow on to catch duplicates, that we
|
||||
* don't support yet.
|
||||
*/
|
||||
if (ring != -1) {
|
||||
if (match->tctx) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread "
|
||||
"context NVT %x/%x\n", nvt_blk, nvt_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
match->ring = ring;
|
||||
match->tctx = tctx;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match->tctx) {
|
||||
qemu_log_mask(LOG_UNIMP, "XIVE: NVT %x/%x is not dispatched\n",
|
||||
nvt_blk, nvt_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is our simple Xive Presenter Engine model. It is merged in the
|
||||
* Router as it does not require an extra object.
|
||||
@ -1473,22 +1470,31 @@ static bool xive_presenter_match(XiveRouter *xrtr, uint8_t format,
|
||||
*
|
||||
* The parameters represent what is sent on the PowerBus
|
||||
*/
|
||||
static bool xive_presenter_notify(XiveRouter *xrtr, uint8_t format,
|
||||
static bool xive_presenter_notify(uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv)
|
||||
{
|
||||
XiveFabric *xfb = XIVE_FABRIC(qdev_get_machine());
|
||||
XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb);
|
||||
XiveTCTXMatch match = { .tctx = NULL, .ring = 0 };
|
||||
bool found;
|
||||
int count;
|
||||
|
||||
found = xive_presenter_match(xrtr, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
priority, logic_serv, &match);
|
||||
if (found) {
|
||||
ipb_update(&match.tctx->regs[match.ring], priority);
|
||||
xive_tctx_notify(match.tctx, match.ring);
|
||||
/*
|
||||
* Ask the machine to scan the interrupt controllers for a match
|
||||
*/
|
||||
count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
priority, logic_serv, &match);
|
||||
if (count < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return found;
|
||||
/* handle CPU exception delivery */
|
||||
if (count) {
|
||||
xive_tctx_ipb_update(match.tctx, match.ring, priority_to_ipb(priority));
|
||||
}
|
||||
|
||||
return !!count;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1601,7 +1607,7 @@ static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk,
|
||||
return;
|
||||
}
|
||||
|
||||
found = xive_presenter_notify(xrtr, format, nvt_blk, nvt_idx,
|
||||
found = xive_presenter_notify(format, nvt_blk, nvt_idx,
|
||||
xive_get_field32(END_W7_F0_IGNORE, end.w7),
|
||||
priority,
|
||||
xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7));
|
||||
@ -1618,14 +1624,21 @@ static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk,
|
||||
* - logical server : forward request to IVPE (not supported)
|
||||
*/
|
||||
if (xive_end_is_backlog(&end)) {
|
||||
uint8_t ipb;
|
||||
|
||||
if (format == 1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"XIVE: END %x/%x invalid config: F1 & backlog\n",
|
||||
end_blk, end_idx);
|
||||
return;
|
||||
}
|
||||
/* Record the IPB in the associated NVT structure */
|
||||
ipb_update((uint8_t *) &nvt.w4, priority);
|
||||
/*
|
||||
* Record the IPB in the associated NVT structure for later
|
||||
* use. The presenter will resend the interrupt when the vCPU
|
||||
* is dispatched again on a HW thread.
|
||||
*/
|
||||
ipb = xive_get_field32(NVT_W4_IPB, nvt.w4) | priority_to_ipb(priority);
|
||||
nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, ipb);
|
||||
xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4);
|
||||
|
||||
/*
|
||||
@ -1718,6 +1731,7 @@ static const TypeInfo xive_router_info = {
|
||||
.class_init = xive_router_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_XIVE_NOTIFIER },
|
||||
{ TYPE_XIVE_PRESENTER },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
@ -1749,7 +1763,11 @@ static uint64_t xive_end_source_read(void *opaque, hwaddr addr, unsigned size)
|
||||
uint8_t pq;
|
||||
uint64_t ret = -1;
|
||||
|
||||
end_blk = xsrc->block_id;
|
||||
/*
|
||||
* The block id should be deduced from the load address on the END
|
||||
* ESB MMIO but our model only supports a single block per XIVE chip.
|
||||
*/
|
||||
end_blk = xive_router_get_block_id(xsrc->xrtr);
|
||||
end_idx = addr >> (xsrc->esb_shift + 1);
|
||||
|
||||
if (xive_router_get_end(xsrc->xrtr, end_blk, end_idx, &end)) {
|
||||
@ -1825,17 +1843,8 @@ static const MemoryRegionOps xive_end_source_ops = {
|
||||
static void xive_end_source_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XiveENDSource *xsrc = XIVE_END_SOURCE(dev);
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'xive' not found: ");
|
||||
return;
|
||||
}
|
||||
|
||||
xsrc->xrtr = XIVE_ROUTER(obj);
|
||||
assert(xsrc->xrtr);
|
||||
|
||||
if (!xsrc->nr_ends) {
|
||||
error_setg(errp, "Number of interrupt needs to be greater than 0");
|
||||
@ -1858,9 +1867,10 @@ static void xive_end_source_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
static Property xive_end_source_properties[] = {
|
||||
DEFINE_PROP_UINT8("block-id", XiveENDSource, block_id, 0),
|
||||
DEFINE_PROP_UINT32("nr-ends", XiveENDSource, nr_ends, 0),
|
||||
DEFINE_PROP_UINT32("shift", XiveENDSource, esb_shift, XIVE_ESB_64K),
|
||||
DEFINE_PROP_LINK("xive", XiveENDSource, xrtr, TYPE_XIVE_ROUTER,
|
||||
XiveRouter *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1894,10 +1904,30 @@ static const TypeInfo xive_notifier_info = {
|
||||
.class_size = sizeof(XiveNotifierClass),
|
||||
};
|
||||
|
||||
/*
|
||||
* XIVE Presenter
|
||||
*/
|
||||
static const TypeInfo xive_presenter_info = {
|
||||
.name = TYPE_XIVE_PRESENTER,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(XivePresenterClass),
|
||||
};
|
||||
|
||||
/*
|
||||
* XIVE Fabric
|
||||
*/
|
||||
static const TypeInfo xive_fabric_info = {
|
||||
.name = TYPE_XIVE_FABRIC,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(XiveFabricClass),
|
||||
};
|
||||
|
||||
static void xive_register_types(void)
|
||||
{
|
||||
type_register_static(&xive_fabric_info);
|
||||
type_register_static(&xive_source_info);
|
||||
type_register_static(&xive_notifier_info);
|
||||
type_register_static(&xive_presenter_info);
|
||||
type_register_static(&xive_router_info);
|
||||
type_register_static(&xive_end_source_info);
|
||||
type_register_static(&xive_tctx_info);
|
||||
|
@ -167,32 +167,14 @@ typedef struct IPMISensor {
|
||||
#define MAX_SENSORS 20
|
||||
#define IPMI_WATCHDOG_SENSOR 0
|
||||
|
||||
typedef struct IPMIBmcSim IPMIBmcSim;
|
||||
typedef struct RspBuffer RspBuffer;
|
||||
|
||||
#define MAX_NETFNS 64
|
||||
|
||||
typedef struct IPMICmdHandler {
|
||||
void (*cmd_handler)(IPMIBmcSim *s,
|
||||
uint8_t *cmd, unsigned int cmd_len,
|
||||
RspBuffer *rsp);
|
||||
unsigned int cmd_len_min;
|
||||
} IPMICmdHandler;
|
||||
|
||||
typedef struct IPMINetfn {
|
||||
unsigned int cmd_nums;
|
||||
const IPMICmdHandler *cmd_handlers;
|
||||
} IPMINetfn;
|
||||
|
||||
typedef struct IPMIRcvBufEntry {
|
||||
QTAILQ_ENTRY(IPMIRcvBufEntry) entry;
|
||||
uint8_t len;
|
||||
uint8_t buf[MAX_IPMI_MSG_SIZE];
|
||||
} IPMIRcvBufEntry;
|
||||
|
||||
#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim"
|
||||
#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \
|
||||
TYPE_IPMI_BMC_SIMULATOR)
|
||||
struct IPMIBmcSim {
|
||||
IPMIBmc parent;
|
||||
|
||||
@ -279,28 +261,8 @@ struct IPMIBmcSim {
|
||||
#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2
|
||||
#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3
|
||||
|
||||
struct RspBuffer {
|
||||
uint8_t buffer[MAX_IPMI_MSG_SIZE];
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
#define RSP_BUFFER_INITIALIZER { }
|
||||
|
||||
static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte)
|
||||
{
|
||||
rsp->buffer[2] = byte;
|
||||
}
|
||||
|
||||
/* Add a byte to the response. */
|
||||
static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte)
|
||||
{
|
||||
if (rsp->len >= sizeof(rsp->buffer)) {
|
||||
rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED);
|
||||
return;
|
||||
}
|
||||
rsp->buffer[rsp->len++] = byte;
|
||||
}
|
||||
|
||||
static inline void rsp_buffer_pushmore(RspBuffer *rsp, uint8_t *bytes,
|
||||
unsigned int n)
|
||||
{
|
||||
@ -630,8 +592,8 @@ static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s)
|
||||
}
|
||||
}
|
||||
|
||||
static int ipmi_register_netfn(IPMIBmcSim *s, unsigned int netfn,
|
||||
const IPMINetfn *netfnd)
|
||||
int ipmi_sim_register_netfn(IPMIBmcSim *s, unsigned int netfn,
|
||||
const IPMINetfn *netfnd)
|
||||
{
|
||||
if ((netfn & 1) || (netfn >= MAX_NETFNS) || (s->netfns[netfn / 2])) {
|
||||
return -1;
|
||||
@ -1860,10 +1822,10 @@ static const IPMINetfn storage_netfn = {
|
||||
|
||||
static void register_cmds(IPMIBmcSim *s)
|
||||
{
|
||||
ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn);
|
||||
ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn);
|
||||
ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn);
|
||||
ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn);
|
||||
ipmi_sim_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn);
|
||||
ipmi_sim_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn);
|
||||
ipmi_sim_register_netfn(s, IPMI_NETFN_APP, &app_netfn);
|
||||
ipmi_sim_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn);
|
||||
}
|
||||
|
||||
static uint8_t init_sdrs[] = {
|
||||
|
@ -9,7 +9,9 @@ obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o
|
||||
obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o
|
||||
# IBM PowerNV
|
||||
obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o
|
||||
obj-$(CONFIG_POWERNV) += pnv_homer.o
|
||||
obj-$(CONFIG_POWERNV) += pnv_homer.o pnv_pnor.o
|
||||
|
||||
|
||||
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
|
||||
obj-y += spapr_pci_vfio.o spapr_pci_nvlink2.o
|
||||
endif
|
||||
|
504
hw/ppc/pnv.c
504
hw/ppc/pnv.c
@ -44,6 +44,7 @@
|
||||
#include "hw/ppc/xics.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "hw/ppc/pnv_pnor.h"
|
||||
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/boards.h"
|
||||
@ -279,14 +280,16 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir,
|
||||
|
||||
static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
|
||||
{
|
||||
const char *typename = pnv_chip_core_typename(chip);
|
||||
size_t typesize = object_type_get_instance_size(typename);
|
||||
static const char compat[] = "ibm,power8-xscom\0ibm,xscom";
|
||||
int i;
|
||||
|
||||
pnv_dt_xscom(chip, fdt, 0);
|
||||
pnv_dt_xscom(chip, fdt, 0,
|
||||
cpu_to_be64(PNV_XSCOM_BASE(chip)),
|
||||
cpu_to_be64(PNV_XSCOM_SIZE),
|
||||
compat, sizeof(compat));
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
|
||||
PnvCore *pnv_core = chip->cores[i];
|
||||
|
||||
pnv_dt_core(chip, pnv_core, fdt);
|
||||
|
||||
@ -301,14 +304,16 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
|
||||
|
||||
static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt)
|
||||
{
|
||||
const char *typename = pnv_chip_core_typename(chip);
|
||||
size_t typesize = object_type_get_instance_size(typename);
|
||||
static const char compat[] = "ibm,power9-xscom\0ibm,xscom";
|
||||
int i;
|
||||
|
||||
pnv_dt_xscom(chip, fdt, 0);
|
||||
pnv_dt_xscom(chip, fdt, 0,
|
||||
cpu_to_be64(PNV9_XSCOM_BASE(chip)),
|
||||
cpu_to_be64(PNV9_XSCOM_SIZE),
|
||||
compat, sizeof(compat));
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
|
||||
PnvCore *pnv_core = chip->cores[i];
|
||||
|
||||
pnv_dt_core(chip, pnv_core, fdt);
|
||||
}
|
||||
@ -317,7 +322,30 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt)
|
||||
pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size);
|
||||
}
|
||||
|
||||
pnv_dt_lpc(chip, fdt, 0);
|
||||
pnv_dt_lpc(chip, fdt, 0, PNV9_LPCM_BASE(chip), PNV9_LPCM_SIZE);
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt)
|
||||
{
|
||||
static const char compat[] = "ibm,power10-xscom\0ibm,xscom";
|
||||
int i;
|
||||
|
||||
pnv_dt_xscom(chip, fdt, 0,
|
||||
cpu_to_be64(PNV10_XSCOM_BASE(chip)),
|
||||
cpu_to_be64(PNV10_XSCOM_SIZE),
|
||||
compat, sizeof(compat));
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pnv_core = chip->cores[i];
|
||||
|
||||
pnv_dt_core(chip, pnv_core, fdt);
|
||||
}
|
||||
|
||||
if (chip->ram_size) {
|
||||
pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size);
|
||||
}
|
||||
|
||||
pnv_dt_lpc(chip, fdt, 0, PNV10_LPCM_BASE(chip), PNV10_LPCM_SIZE);
|
||||
}
|
||||
|
||||
static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off)
|
||||
@ -456,7 +484,7 @@ static void pnv_dt_isa(PnvMachineState *pnv, void *fdt)
|
||||
&args);
|
||||
}
|
||||
|
||||
static void pnv_dt_power_mgt(void *fdt)
|
||||
static void pnv_dt_power_mgt(PnvMachineState *pnv, void *fdt)
|
||||
{
|
||||
int off;
|
||||
|
||||
@ -468,8 +496,7 @@ static void pnv_dt_power_mgt(void *fdt)
|
||||
|
||||
static void *pnv_dt_create(MachineState *machine)
|
||||
{
|
||||
const char plat_compat8[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv";
|
||||
const char plat_compat9[] = "qemu,powernv9\0ibm,powernv";
|
||||
PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
|
||||
PnvMachineState *pnv = PNV_MACHINE(machine);
|
||||
void *fdt;
|
||||
char *buf;
|
||||
@ -479,19 +506,15 @@ static void *pnv_dt_create(MachineState *machine)
|
||||
fdt = g_malloc0(FDT_MAX_SIZE);
|
||||
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
|
||||
|
||||
/* /qemu node */
|
||||
_FDT((fdt_add_subnode(fdt, 0, "qemu")));
|
||||
|
||||
/* Root node */
|
||||
_FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2)));
|
||||
_FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
|
||||
_FDT((fdt_setprop_string(fdt, 0, "model",
|
||||
"IBM PowerNV (emulated by qemu)")));
|
||||
if (pnv_is_power9(pnv)) {
|
||||
_FDT((fdt_setprop(fdt, 0, "compatible", plat_compat9,
|
||||
sizeof(plat_compat9))));
|
||||
} else {
|
||||
_FDT((fdt_setprop(fdt, 0, "compatible", plat_compat8,
|
||||
sizeof(plat_compat8))));
|
||||
}
|
||||
|
||||
_FDT((fdt_setprop(fdt, 0, "compatible", pmc->compat, pmc->compat_size)));
|
||||
|
||||
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
|
||||
_FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
|
||||
@ -528,9 +551,9 @@ static void *pnv_dt_create(MachineState *machine)
|
||||
pnv_dt_bmc_sensors(pnv->bmc, fdt);
|
||||
}
|
||||
|
||||
/* Create an extra node for power management on Power9 */
|
||||
if (pnv_is_power9(pnv)) {
|
||||
pnv_dt_power_mgt(fdt);
|
||||
/* Create an extra node for power management on machines that support it */
|
||||
if (pmc->dt_power_mgt) {
|
||||
pmc->dt_power_mgt(pnv, fdt);
|
||||
}
|
||||
|
||||
return fdt;
|
||||
@ -547,26 +570,10 @@ static void pnv_powerdown_notify(Notifier *n, void *opaque)
|
||||
|
||||
static void pnv_reset(MachineState *machine)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(machine);
|
||||
void *fdt;
|
||||
Object *obj;
|
||||
|
||||
qemu_devices_reset();
|
||||
|
||||
/*
|
||||
* OpenPOWER systems have a BMC, which can be defined on the
|
||||
* command line with:
|
||||
*
|
||||
* -device ipmi-bmc-sim,id=bmc0
|
||||
*
|
||||
* This is the internal simulator but it could also be an external
|
||||
* BMC.
|
||||
*/
|
||||
obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL);
|
||||
if (obj) {
|
||||
pnv->bmc = IPMI_BMC(obj);
|
||||
}
|
||||
|
||||
fdt = pnv_dt_create(machine);
|
||||
|
||||
/* Pack resulting tree */
|
||||
@ -594,6 +601,12 @@ static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp)
|
||||
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);
|
||||
return pnv_lpc_isa_create(&chip10->lpc, false, errp);
|
||||
}
|
||||
|
||||
static ISABus *pnv_isa_create(PnvChip *chip, Error **errp)
|
||||
{
|
||||
return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp);
|
||||
@ -614,6 +627,24 @@ static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon)
|
||||
pnv_psi_pic_print_info(&chip9->psi, mon);
|
||||
}
|
||||
|
||||
static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip,
|
||||
uint32_t core_id)
|
||||
{
|
||||
return PNV_XSCOM_EX_BASE(core_id);
|
||||
}
|
||||
|
||||
static uint64_t pnv_chip_power9_xscom_core_base(PnvChip *chip,
|
||||
uint32_t core_id)
|
||||
{
|
||||
return PNV9_XSCOM_EC_BASE(core_id);
|
||||
}
|
||||
|
||||
static uint64_t pnv_chip_power10_xscom_core_base(PnvChip *chip,
|
||||
uint32_t core_id)
|
||||
{
|
||||
return PNV10_XSCOM_EC_BASE(core_id);
|
||||
}
|
||||
|
||||
static bool pnv_match_cpu(const char *default_type, const char *cpu_type)
|
||||
{
|
||||
PowerPCCPUClass *ppc_default =
|
||||
@ -624,6 +655,23 @@ static bool pnv_match_cpu(const char *default_type, const char *cpu_type)
|
||||
return ppc_default->pvr_match(ppc_default, ppc->pvr);
|
||||
}
|
||||
|
||||
static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq)
|
||||
{
|
||||
Object *obj;
|
||||
|
||||
obj = OBJECT(isa_create(bus, "isa-ipmi-bt"));
|
||||
object_property_set_link(obj, OBJECT(bmc), "bmc", &error_fatal);
|
||||
object_property_set_int(obj, irq, "irq", &error_fatal);
|
||||
object_property_set_bool(obj, true, "realized", &error_fatal);
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon)
|
||||
{
|
||||
Pnv10Chip *chip10 = PNV10_CHIP(chip);
|
||||
|
||||
pnv_psi_pic_print_info(&chip10->psi, mon);
|
||||
}
|
||||
|
||||
static void pnv_init(MachineState *machine)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(machine);
|
||||
@ -633,6 +681,8 @@ static void pnv_init(MachineState *machine)
|
||||
long fw_size;
|
||||
int i;
|
||||
char *chip_typename;
|
||||
DriveInfo *pnor = drive_get(IF_MTD, 0, 0);
|
||||
DeviceState *dev;
|
||||
|
||||
/* allocate RAM */
|
||||
if (machine->ram_size < (1 * GiB)) {
|
||||
@ -644,6 +694,17 @@ static void pnv_init(MachineState *machine)
|
||||
machine->ram_size);
|
||||
memory_region_add_subregion(get_system_memory(), 0, ram);
|
||||
|
||||
/*
|
||||
* Create our simple PNOR device
|
||||
*/
|
||||
dev = qdev_create(NULL, TYPE_PNV_PNOR);
|
||||
if (pnor) {
|
||||
qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor),
|
||||
&error_abort);
|
||||
}
|
||||
qdev_init_nofail(dev);
|
||||
pnv->pnor = PNV_PNOR(dev);
|
||||
|
||||
/* load skiboot firmware */
|
||||
if (bios_name == NULL) {
|
||||
bios_name = FW_FILE_NAME;
|
||||
@ -733,6 +794,9 @@ static void pnv_init(MachineState *machine)
|
||||
}
|
||||
g_free(chip_typename);
|
||||
|
||||
/* Create the machine BMC simulator */
|
||||
pnv->bmc = pnv_bmc_create();
|
||||
|
||||
/* Instantiate ISA bus on chip 0 */
|
||||
pnv->isa_bus = pnv_isa_create(pnv->chips[0], &error_fatal);
|
||||
|
||||
@ -742,6 +806,9 @@ static void pnv_init(MachineState *machine)
|
||||
/* Create an RTC ISA device too */
|
||||
mc146818_rtc_init(pnv->isa_bus, 2000, NULL);
|
||||
|
||||
/* Create the IPMI BT device for communication with the BMC */
|
||||
pnv_ipmi_bt_init(pnv->isa_bus, pnv->bmc, 10);
|
||||
|
||||
/*
|
||||
* OpenPOWER systems use a IPMI SEL Event message to notify the
|
||||
* host to powerdown
|
||||
@ -794,6 +861,12 @@ static void pnv_chip_power8_intc_destroy(PnvChip *chip, PowerPCCPU *cpu)
|
||||
pnv_cpu->intc = NULL;
|
||||
}
|
||||
|
||||
static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu,
|
||||
Monitor *mon)
|
||||
{
|
||||
icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon);
|
||||
}
|
||||
|
||||
/*
|
||||
* 0:48 Reserved - Read as zeroes
|
||||
* 49:52 Node ID
|
||||
@ -809,6 +882,11 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
|
||||
return (chip->chip_id << 8) | (core_id << 2);
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_core_pir_p10(PnvChip *chip, uint32_t core_id)
|
||||
{
|
||||
return (chip->chip_id << 8) | (core_id << 2);
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu,
|
||||
Error **errp)
|
||||
{
|
||||
@ -846,6 +924,38 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu)
|
||||
pnv_cpu->intc = NULL;
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu,
|
||||
Monitor *mon)
|
||||
{
|
||||
xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon);
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu,
|
||||
Error **errp)
|
||||
{
|
||||
PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);
|
||||
|
||||
/* Will be defined when the interrupt controller is */
|
||||
pnv_cpu->intc = NULL;
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_intc_reset(PnvChip *chip, PowerPCCPU *cpu)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu)
|
||||
{
|
||||
PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);
|
||||
|
||||
pnv_cpu->intc = NULL;
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu,
|
||||
Monitor *mon)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowed core identifiers on a POWER8 Processor Chip :
|
||||
*
|
||||
@ -873,6 +983,9 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu)
|
||||
*/
|
||||
#define POWER9_CORE_MASK (0xffffffffffffffull)
|
||||
|
||||
|
||||
#define POWER10_CORE_MASK (0xffffffffffffffull)
|
||||
|
||||
static void pnv_chip_power8_instance_init(Object *obj)
|
||||
{
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(obj);
|
||||
@ -884,26 +997,18 @@ static void pnv_chip_power8_instance_init(Object *obj)
|
||||
|
||||
object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc),
|
||||
TYPE_PNV8_LPC, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip8->lpc), "psi",
|
||||
OBJECT(&chip8->psi), &error_abort);
|
||||
|
||||
object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ),
|
||||
TYPE_PNV8_OCC, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip8->occ), "psi",
|
||||
OBJECT(&chip8->psi), &error_abort);
|
||||
|
||||
object_initialize_child(obj, "homer", &chip8->homer, sizeof(chip8->homer),
|
||||
TYPE_PNV8_HOMER, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip8->homer), "chip", obj,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
|
||||
{
|
||||
PnvChip *chip = PNV_CHIP(chip8);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
const char *typename = pnv_chip_core_typename(chip);
|
||||
size_t typesize = object_type_get_instance_size(typename);
|
||||
int i, j;
|
||||
char *name;
|
||||
XICSFabric *xi = XICS_FABRIC(qdev_get_machine());
|
||||
@ -917,7 +1022,7 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
|
||||
|
||||
/* Map the ICP registers for each thread */
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
|
||||
PnvCore *pnv_core = chip->cores[i];
|
||||
int core_hwid = CPU_CORE(pnv_core)->core_id;
|
||||
|
||||
for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
|
||||
@ -964,6 +1069,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
&PNV_PSI(psi8)->xscom_regs);
|
||||
|
||||
/* Create LPC controller */
|
||||
object_property_set_link(OBJECT(&chip8->lpc), OBJECT(&chip8->psi), "psi",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip8->lpc), true, "realized",
|
||||
&error_fatal);
|
||||
pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs);
|
||||
@ -983,6 +1090,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
object_property_set_link(OBJECT(&chip8->occ), OBJECT(&chip8->psi), "psi",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip8->occ), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -991,35 +1100,49 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs);
|
||||
|
||||
/* OCC SRAM model */
|
||||
memory_region_add_subregion(get_system_memory(), PNV_OCC_COMMON_AREA(chip),
|
||||
memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip),
|
||||
&chip8->occ.sram_regs);
|
||||
|
||||
/* HOMER */
|
||||
object_property_set_link(OBJECT(&chip8->homer), OBJECT(chip), "chip",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip8->homer), true, "realized",
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs);
|
||||
|
||||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip),
|
||||
&chip8->homer.regs);
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
{
|
||||
addr &= (PNV_XSCOM_SIZE - 1);
|
||||
return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
|
||||
}
|
||||
|
||||
static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvChipClass *k = PNV_CHIP_CLASS(klass);
|
||||
|
||||
k->chip_type = PNV_CHIP_POWER8E;
|
||||
k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */
|
||||
k->cores_mask = POWER8E_CORE_MASK;
|
||||
k->core_pir = pnv_chip_core_pir_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;
|
||||
k->intc_print_info = pnv_chip_power8_intc_print_info;
|
||||
k->isa_create = pnv_chip_power8_isa_create;
|
||||
k->dt_populate = pnv_chip_power8_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power8_pic_print_info;
|
||||
k->xscom_core_base = pnv_chip_power8_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power8_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER8E";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power8_realize,
|
||||
@ -1031,16 +1154,18 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvChipClass *k = PNV_CHIP_CLASS(klass);
|
||||
|
||||
k->chip_type = PNV_CHIP_POWER8;
|
||||
k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
|
||||
k->cores_mask = POWER8_CORE_MASK;
|
||||
k->core_pir = pnv_chip_core_pir_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;
|
||||
k->intc_print_info = pnv_chip_power8_intc_print_info;
|
||||
k->isa_create = pnv_chip_power8_isa_create;
|
||||
k->dt_populate = pnv_chip_power8_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power8_pic_print_info;
|
||||
k->xscom_core_base = pnv_chip_power8_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power8_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER8";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power8_realize,
|
||||
@ -1052,16 +1177,18 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvChipClass *k = PNV_CHIP_CLASS(klass);
|
||||
|
||||
k->chip_type = PNV_CHIP_POWER8NVL;
|
||||
k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */
|
||||
k->cores_mask = POWER8_CORE_MASK;
|
||||
k->core_pir = pnv_chip_core_pir_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;
|
||||
k->intc_print_info = pnv_chip_power8_intc_print_info;
|
||||
k->isa_create = pnv_chip_power8nvl_isa_create;
|
||||
k->dt_populate = pnv_chip_power8_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power8_pic_print_info;
|
||||
k->xscom_core_base = pnv_chip_power8_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power8_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER8NVL";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power8_realize,
|
||||
@ -1074,35 +1201,23 @@ static void pnv_chip_power9_instance_init(Object *obj)
|
||||
|
||||
object_initialize_child(obj, "xive", &chip9->xive, sizeof(chip9->xive),
|
||||
TYPE_PNV_XIVE, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip9->xive), "chip", obj,
|
||||
&error_abort);
|
||||
|
||||
object_initialize_child(obj, "psi", &chip9->psi, sizeof(chip9->psi),
|
||||
TYPE_PNV9_PSI, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip9->psi), "chip", obj,
|
||||
&error_abort);
|
||||
|
||||
object_initialize_child(obj, "lpc", &chip9->lpc, sizeof(chip9->lpc),
|
||||
TYPE_PNV9_LPC, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip9->lpc), "psi",
|
||||
OBJECT(&chip9->psi), &error_abort);
|
||||
|
||||
object_initialize_child(obj, "occ", &chip9->occ, sizeof(chip9->occ),
|
||||
TYPE_PNV9_OCC, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip9->occ), "psi",
|
||||
OBJECT(&chip9->psi), &error_abort);
|
||||
|
||||
object_initialize_child(obj, "homer", &chip9->homer, sizeof(chip9->homer),
|
||||
TYPE_PNV9_HOMER, &error_abort, NULL);
|
||||
object_property_add_const_link(OBJECT(&chip9->homer), "chip", obj,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
||||
{
|
||||
PnvChip *chip = PNV_CHIP(chip9);
|
||||
const char *typename = pnv_chip_core_typename(chip);
|
||||
size_t typesize = object_type_get_instance_size(typename);
|
||||
int i;
|
||||
|
||||
chip9->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4);
|
||||
@ -1111,7 +1226,7 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
||||
for (i = 0; i < chip9->nr_quads; i++) {
|
||||
char eq_name[32];
|
||||
PnvQuad *eq = &chip9->quads[i];
|
||||
PnvCore *pnv_core = PNV_CORE(chip->cores + (i * 4) * typesize);
|
||||
PnvCore *pnv_core = chip->cores[i * 4];
|
||||
int core_id = CPU_CORE(pnv_core)->core_id;
|
||||
|
||||
snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id);
|
||||
@ -1163,6 +1278,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
"pc-bar", &error_fatal);
|
||||
object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_TM_BASE(chip),
|
||||
"tm-bar", &error_fatal);
|
||||
object_property_set_link(OBJECT(&chip9->xive), OBJECT(chip), "chip",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip9->xive), true, "realized",
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
@ -1184,6 +1301,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
&PNV_PSI(psi9)->xscom_regs);
|
||||
|
||||
/* LPC */
|
||||
object_property_set_link(OBJECT(&chip9->lpc), OBJECT(&chip9->psi), "psi",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip9->lpc), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1196,6 +1315,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
(uint64_t) PNV9_LPCM_BASE(chip));
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
object_property_set_link(OBJECT(&chip9->occ), OBJECT(&chip9->psi), "psi",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip9->occ), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1204,41 +1325,143 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs);
|
||||
|
||||
/* OCC SRAM model */
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_OCC_COMMON_AREA(chip),
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
|
||||
&chip9->occ.sram_regs);
|
||||
|
||||
/* HOMER */
|
||||
object_property_set_link(OBJECT(&chip9->homer), OBJECT(chip), "chip",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip9->homer), true, "realized",
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs);
|
||||
|
||||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip),
|
||||
&chip9->homer.regs);
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
{
|
||||
addr &= (PNV9_XSCOM_SIZE - 1);
|
||||
return addr >> 3;
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvChipClass *k = PNV_CHIP_CLASS(klass);
|
||||
|
||||
k->chip_type = PNV_CHIP_POWER9;
|
||||
k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */
|
||||
k->cores_mask = POWER9_CORE_MASK;
|
||||
k->core_pir = pnv_chip_core_pir_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;
|
||||
k->intc_print_info = pnv_chip_power9_intc_print_info;
|
||||
k->isa_create = pnv_chip_power9_isa_create;
|
||||
k->dt_populate = pnv_chip_power9_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power9_pic_print_info;
|
||||
k->xscom_core_base = pnv_chip_power9_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power9_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER9";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power9_realize,
|
||||
&k->parent_realize);
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_instance_init(Object *obj)
|
||||
{
|
||||
Pnv10Chip *chip10 = PNV10_CHIP(obj);
|
||||
|
||||
object_initialize_child(obj, "psi", &chip10->psi, sizeof(chip10->psi),
|
||||
TYPE_PNV10_PSI, &error_abort, NULL);
|
||||
object_initialize_child(obj, "lpc", &chip10->lpc, sizeof(chip10->lpc),
|
||||
TYPE_PNV10_LPC, &error_abort, NULL);
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
|
||||
PnvChip *chip = PNV_CHIP(dev);
|
||||
Pnv10Chip *chip10 = PNV10_CHIP(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* XSCOM bridge is first */
|
||||
pnv_xscom_realize(chip, PNV10_XSCOM_SIZE, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV10_XSCOM_BASE(chip));
|
||||
|
||||
pcc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Processor Service Interface (PSI) Host Bridge */
|
||||
object_property_set_int(OBJECT(&chip10->psi), PNV10_PSIHB_BASE(chip),
|
||||
"bar", &error_fatal);
|
||||
object_property_set_bool(OBJECT(&chip10->psi), true, "realized",
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_PSIHB_BASE,
|
||||
&PNV_PSI(&chip10->psi)->xscom_regs);
|
||||
|
||||
/* LPC */
|
||||
object_property_set_link(OBJECT(&chip10->lpc), OBJECT(&chip10->psi), "psi",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&chip10->lpc), true, "realized",
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(), PNV10_LPCM_BASE(chip),
|
||||
&chip10->lpc.xscom_regs);
|
||||
|
||||
chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0",
|
||||
(uint64_t) PNV10_LPCM_BASE(chip));
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
{
|
||||
addr &= (PNV10_XSCOM_SIZE - 1);
|
||||
return addr >> 3;
|
||||
}
|
||||
|
||||
static void pnv_chip_power10_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvChipClass *k = PNV_CHIP_CLASS(klass);
|
||||
|
||||
k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */
|
||||
k->cores_mask = POWER10_CORE_MASK;
|
||||
k->core_pir = pnv_chip_core_pir_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;
|
||||
k->intc_print_info = pnv_chip_power10_intc_print_info;
|
||||
k->isa_create = pnv_chip_power10_isa_create;
|
||||
k->dt_populate = pnv_chip_power10_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power10_pic_print_info;
|
||||
k->xscom_core_base = pnv_chip_power10_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power10_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER10";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power10_realize,
|
||||
&k->parent_realize);
|
||||
}
|
||||
|
||||
static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
|
||||
{
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
@ -1275,7 +1498,6 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
|
||||
Error *error = NULL;
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
const char *typename = pnv_chip_core_typename(chip);
|
||||
size_t typesize = object_type_get_instance_size(typename);
|
||||
int i, core_hwid;
|
||||
|
||||
if (!object_class_by_name(typename)) {
|
||||
@ -1290,21 +1512,24 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
chip->cores = g_malloc0(typesize * chip->nr_cores);
|
||||
chip->cores = g_new0(PnvCore *, chip->nr_cores);
|
||||
|
||||
for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
|
||||
&& (i < chip->nr_cores); core_hwid++) {
|
||||
char core_name[32];
|
||||
void *pnv_core = chip->cores + i * typesize;
|
||||
PnvCore *pnv_core;
|
||||
uint64_t xscom_core_base;
|
||||
|
||||
if (!(chip->cores_mask & (1ull << core_hwid))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pnv_core = PNV_CORE(object_new(typename));
|
||||
|
||||
snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid);
|
||||
object_initialize_child(OBJECT(chip), core_name, pnv_core, typesize,
|
||||
typename, &error_fatal, NULL);
|
||||
object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core),
|
||||
&error_abort);
|
||||
chip->cores[i] = pnv_core;
|
||||
object_property_set_int(OBJECT(pnv_core), ms->smp.threads, "nr-threads",
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(pnv_core), core_hwid,
|
||||
@ -1312,20 +1537,16 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
|
||||
object_property_set_int(OBJECT(pnv_core),
|
||||
pcc->core_pir(chip, core_hwid),
|
||||
"pir", &error_fatal);
|
||||
object_property_add_const_link(OBJECT(pnv_core), "chip",
|
||||
OBJECT(chip), &error_fatal);
|
||||
object_property_set_link(OBJECT(pnv_core), OBJECT(chip), "chip",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(pnv_core), true, "realized",
|
||||
&error_fatal);
|
||||
|
||||
/* Each core has an XSCOM MMIO region */
|
||||
if (!pnv_chip_is_power9(chip)) {
|
||||
xscom_core_base = PNV_XSCOM_EX_BASE(core_hwid);
|
||||
} else {
|
||||
xscom_core_base = PNV9_XSCOM_EC_BASE(core_hwid);
|
||||
}
|
||||
xscom_core_base = pcc->xscom_core_base(chip, core_hwid);
|
||||
|
||||
pnv_xscom_add_subregion(chip, xscom_core_base,
|
||||
&PNV_CORE(pnv_core)->xscom_regs);
|
||||
&pnv_core->xscom_regs);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@ -1362,6 +1583,23 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data)
|
||||
dc->desc = "PowerNV Chip";
|
||||
}
|
||||
|
||||
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pc = chip->cores[i];
|
||||
CPUCore *cc = CPU_CORE(pc);
|
||||
|
||||
for (j = 0; j < cc->nr_threads; j++) {
|
||||
if (ppc_cpu_pir(pc->threads[j]) == pir) {
|
||||
return pc->threads[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
@ -1405,11 +1643,9 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj,
|
||||
CPU_FOREACH(cs) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
|
||||
if (pnv_chip_is_power9(pnv->chips[0])) {
|
||||
xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon);
|
||||
} else {
|
||||
icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon);
|
||||
}
|
||||
/* XXX: loop on each chip/core/thread instead of CPU_FOREACH() */
|
||||
PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu,
|
||||
mon);
|
||||
}
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
@ -1417,6 +1653,49 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj,
|
||||
}
|
||||
}
|
||||
|
||||
static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv,
|
||||
XiveTCTXMatch *match)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xfb);
|
||||
int total_count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(&chip9->xive);
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
|
||||
int count;
|
||||
|
||||
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
priority, logic_serv, match);
|
||||
|
||||
if (count < 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
total_count += count;
|
||||
}
|
||||
|
||||
return total_count;
|
||||
}
|
||||
|
||||
PnvChip *pnv_get_chip(uint32_t chip_id)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
if (chip->chip_id == chip_id) {
|
||||
return chip;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
@ -1468,6 +1747,8 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
|
||||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
static const char compat[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv";
|
||||
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER8";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
|
||||
@ -1475,16 +1756,41 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data)
|
||||
xic->icp_get = pnv_icp_get;
|
||||
xic->ics_get = pnv_ics_get;
|
||||
xic->ics_resend = pnv_ics_resend;
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
}
|
||||
|
||||
static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc);
|
||||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
static const char compat[] = "qemu,powernv9\0ibm,powernv";
|
||||
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER9";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0");
|
||||
xfc->match_nvt = pnv_match_nvt;
|
||||
|
||||
mc->alias = "powernv";
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
}
|
||||
|
||||
static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
static const char compat[] = "qemu,powernv10\0ibm,powernv";
|
||||
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v1.0");
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
}
|
||||
|
||||
static void pnv_machine_class_init(ObjectClass *oc, void *data)
|
||||
@ -1524,11 +1830,27 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data)
|
||||
.parent = TYPE_PNV9_CHIP, \
|
||||
}
|
||||
|
||||
#define DEFINE_PNV10_CHIP_TYPE(type, class_initfn) \
|
||||
{ \
|
||||
.name = type, \
|
||||
.class_init = class_initfn, \
|
||||
.parent = TYPE_PNV10_CHIP, \
|
||||
}
|
||||
|
||||
static const TypeInfo types[] = {
|
||||
{
|
||||
.name = MACHINE_TYPE_NAME("powernv10"),
|
||||
.parent = TYPE_PNV_MACHINE,
|
||||
.class_init = pnv_machine_power10_class_init,
|
||||
},
|
||||
{
|
||||
.name = MACHINE_TYPE_NAME("powernv9"),
|
||||
.parent = TYPE_PNV_MACHINE,
|
||||
.class_init = pnv_machine_power9_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_XIVE_FABRIC },
|
||||
{ },
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = MACHINE_TYPE_NAME("powernv8"),
|
||||
@ -1546,6 +1868,7 @@ static const TypeInfo types[] = {
|
||||
.instance_size = sizeof(PnvMachineState),
|
||||
.instance_init = pnv_machine_instance_init,
|
||||
.class_init = pnv_machine_class_init,
|
||||
.class_size = sizeof(PnvMachineClass),
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_INTERRUPT_STATS_PROVIDER },
|
||||
{ },
|
||||
@ -1560,6 +1883,17 @@ static const TypeInfo types[] = {
|
||||
.abstract = true,
|
||||
},
|
||||
|
||||
/*
|
||||
* P10 chip and variants
|
||||
*/
|
||||
{
|
||||
.name = TYPE_PNV10_CHIP,
|
||||
.parent = TYPE_PNV_CHIP,
|
||||
.instance_init = pnv_chip_power10_instance_init,
|
||||
.instance_size = sizeof(Pnv10Chip),
|
||||
},
|
||||
DEFINE_PNV10_CHIP_TYPE(TYPE_PNV_CHIP_POWER10, pnv_chip_power10_class_init),
|
||||
|
||||
/*
|
||||
* P9 chip and variants
|
||||
*/
|
||||
|
116
hw/ppc/pnv_bmc.c
116
hw/ppc/pnv_bmc.c
@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "target/ppc/cpu.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
@ -114,3 +116,117 @@ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt)
|
||||
sdr->sensor_type)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HIOMAP protocol handler
|
||||
*/
|
||||
#define HIOMAP_C_RESET 1
|
||||
#define HIOMAP_C_GET_INFO 2
|
||||
#define HIOMAP_C_GET_FLASH_INFO 3
|
||||
#define HIOMAP_C_CREATE_READ_WINDOW 4
|
||||
#define HIOMAP_C_CLOSE_WINDOW 5
|
||||
#define HIOMAP_C_CREATE_WRITE_WINDOW 6
|
||||
#define HIOMAP_C_MARK_DIRTY 7
|
||||
#define HIOMAP_C_FLUSH 8
|
||||
#define HIOMAP_C_ACK 9
|
||||
#define HIOMAP_C_ERASE 10
|
||||
#define HIOMAP_C_DEVICE_NAME 11
|
||||
#define HIOMAP_C_LOCK 12
|
||||
|
||||
#define BLOCK_SHIFT 12 /* 4K */
|
||||
|
||||
static uint16_t bytes_to_blocks(uint32_t bytes)
|
||||
{
|
||||
return bytes >> BLOCK_SHIFT;
|
||||
}
|
||||
|
||||
static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len,
|
||||
RspBuffer *rsp)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
PnvPnor *pnor = pnv->pnor;
|
||||
uint32_t pnor_size = pnor->size;
|
||||
uint32_t pnor_addr = PNOR_SPI_OFFSET;
|
||||
bool readonly = false;
|
||||
|
||||
rsp_buffer_push(rsp, cmd[2]);
|
||||
rsp_buffer_push(rsp, cmd[3]);
|
||||
|
||||
switch (cmd[2]) {
|
||||
case HIOMAP_C_MARK_DIRTY:
|
||||
case HIOMAP_C_FLUSH:
|
||||
case HIOMAP_C_ERASE:
|
||||
case HIOMAP_C_ACK:
|
||||
break;
|
||||
|
||||
case HIOMAP_C_GET_INFO:
|
||||
rsp_buffer_push(rsp, 2); /* Version 2 */
|
||||
rsp_buffer_push(rsp, BLOCK_SHIFT); /* block size */
|
||||
rsp_buffer_push(rsp, 0); /* Timeout */
|
||||
rsp_buffer_push(rsp, 0); /* Timeout */
|
||||
break;
|
||||
|
||||
case HIOMAP_C_GET_FLASH_INFO:
|
||||
rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF);
|
||||
rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8);
|
||||
rsp_buffer_push(rsp, 0x01); /* erase size */
|
||||
rsp_buffer_push(rsp, 0x00); /* erase size */
|
||||
break;
|
||||
|
||||
case HIOMAP_C_CREATE_READ_WINDOW:
|
||||
readonly = true;
|
||||
/* Fall through */
|
||||
|
||||
case HIOMAP_C_CREATE_WRITE_WINDOW:
|
||||
memory_region_set_readonly(&pnor->mmio, readonly);
|
||||
memory_region_set_enabled(&pnor->mmio, true);
|
||||
|
||||
rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) & 0xFF);
|
||||
rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) >> 8);
|
||||
rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF);
|
||||
rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8);
|
||||
rsp_buffer_push(rsp, 0x00); /* offset */
|
||||
rsp_buffer_push(rsp, 0x00); /* offset */
|
||||
break;
|
||||
|
||||
case HIOMAP_C_CLOSE_WINDOW:
|
||||
memory_region_set_enabled(&pnor->mmio, false);
|
||||
break;
|
||||
|
||||
case HIOMAP_C_DEVICE_NAME:
|
||||
case HIOMAP_C_RESET:
|
||||
case HIOMAP_C_LOCK:
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknow command %02X\n", cmd[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define HIOMAP 0x5a
|
||||
|
||||
static const IPMICmdHandler hiomap_cmds[] = {
|
||||
[HIOMAP] = { hiomap_cmd, 3 },
|
||||
};
|
||||
|
||||
static const IPMINetfn hiomap_netfn = {
|
||||
.cmd_nums = ARRAY_SIZE(hiomap_cmds),
|
||||
.cmd_handlers = hiomap_cmds
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiate the machine BMC. PowerNV uses the QEMU internal
|
||||
* simulator but it could also be external.
|
||||
*/
|
||||
IPMIBmc *pnv_bmc_create(void)
|
||||
{
|
||||
Object *obj;
|
||||
|
||||
obj = object_new(TYPE_IPMI_BMC_SIMULATOR);
|
||||
object_property_set_bool(obj, true, "realized", &error_fatal);
|
||||
|
||||
/* Install the HIOMAP protocol handlers to access the PNOR */
|
||||
ipmi_sim_register_netfn(IPMI_BMC_SIMULATOR(obj), IPMI_NETFN_OEM,
|
||||
&hiomap_netfn);
|
||||
|
||||
return IPMI_BMC(obj);
|
||||
}
|
||||
|
@ -217,15 +217,8 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
||||
void *obj;
|
||||
int i, j;
|
||||
char name[32];
|
||||
Object *chip;
|
||||
|
||||
chip = object_property_get_link(OBJECT(dev), "chip", &local_err);
|
||||
if (!chip) {
|
||||
error_propagate_prepend(errp, local_err,
|
||||
"required link 'chip' not found: ");
|
||||
return;
|
||||
}
|
||||
pc->chip = PNV_CHIP(chip);
|
||||
assert(pc->chip);
|
||||
|
||||
pc->threads = g_new(PowerPCCPU *, cc->nr_threads);
|
||||
for (i = 0; i < cc->nr_threads; i++) {
|
||||
@ -254,6 +247,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
|
||||
/* TODO: check PNV_XSCOM_EX_SIZE for p10 */
|
||||
pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops,
|
||||
pc, name, PNV_XSCOM_EX_SIZE);
|
||||
|
||||
@ -297,6 +291,7 @@ static void pnv_core_unrealize(DeviceState *dev, Error **errp)
|
||||
|
||||
static Property pnv_core_properties[] = {
|
||||
DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
|
||||
DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -314,6 +309,14 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data)
|
||||
pcc->xscom_ops = &pnv_core_power9_xscom_ops;
|
||||
}
|
||||
|
||||
static void pnv_core_power10_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
|
||||
|
||||
/* TODO: Use the P9 XSCOMs for now on P10 */
|
||||
pcc->xscom_ops = &pnv_core_power9_xscom_ops;
|
||||
}
|
||||
|
||||
static void pnv_core_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
@ -343,6 +346,7 @@ static const TypeInfo pnv_core_infos[] = {
|
||||
DEFINE_PNV_CORE_TYPE(power8, "power8_v2.0"),
|
||||
DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"),
|
||||
DEFINE_PNV_CORE_TYPE(power9, "power9_v2.0"),
|
||||
DEFINE_PNV_CORE_TYPE(power10, "power10_v1.0"),
|
||||
};
|
||||
|
||||
DEFINE_TYPES(pnv_core_infos)
|
||||
|
@ -17,13 +17,16 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/ppc/pnv_homer.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
|
||||
|
||||
static bool core_max_array(PnvHomer *homer, hwaddr addr)
|
||||
@ -113,10 +116,67 @@ static const MemoryRegionOps pnv_power8_homer_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
/* P8 PBA BARs */
|
||||
#define PBA_BAR0 0x00
|
||||
#define PBA_BAR1 0x01
|
||||
#define PBA_BAR2 0x02
|
||||
#define PBA_BAR3 0x03
|
||||
#define PBA_BARMASK0 0x04
|
||||
#define PBA_BARMASK1 0x05
|
||||
#define PBA_BARMASK2 0x06
|
||||
#define PBA_BARMASK3 0x07
|
||||
|
||||
static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
PnvChip *chip = homer->chip;
|
||||
uint32_t reg = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (reg) {
|
||||
case PBA_BAR0:
|
||||
val = PNV_HOMER_BASE(chip);
|
||||
break;
|
||||
case PBA_BARMASK0: /* P8 homer region mask */
|
||||
val = (PNV_HOMER_SIZE - 1) & 0x300000;
|
||||
break;
|
||||
case PBA_BAR3: /* P8 occ common area */
|
||||
val = PNV_OCC_COMMON_AREA_BASE;
|
||||
break;
|
||||
case PBA_BARMASK3: /* P8 occ common area mask */
|
||||
val = (PNV_OCC_COMMON_AREA_SIZE - 1) & 0x700000;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pnv_homer_power8_pba_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_homer_power8_pba_ops = {
|
||||
.read = pnv_homer_power8_pba_read,
|
||||
.write = pnv_homer_power8_pba_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_homer_power8_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
|
||||
|
||||
homer->pba_size = PNV_XSCOM_PBA_SIZE;
|
||||
homer->pba_ops = &pnv_homer_power8_pba_ops;
|
||||
homer->homer_size = PNV_HOMER_SIZE;
|
||||
homer->homer_ops = &pnv_power8_homer_ops;
|
||||
homer->core_max_base = PNV8_CORE_MAX_BASE;
|
||||
@ -209,10 +269,57 @@ static const MemoryRegionOps pnv_power9_homer_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
PnvChip *chip = homer->chip;
|
||||
uint32_t reg = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (reg) {
|
||||
case PBA_BAR0:
|
||||
val = PNV9_HOMER_BASE(chip);
|
||||
break;
|
||||
case PBA_BARMASK0: /* P9 homer region mask */
|
||||
val = (PNV9_HOMER_SIZE - 1) & 0x300000;
|
||||
break;
|
||||
case PBA_BAR2: /* P9 occ common area */
|
||||
val = PNV9_OCC_COMMON_AREA_BASE;
|
||||
break;
|
||||
case PBA_BARMASK2: /* P9 occ common area size */
|
||||
val = (PNV9_OCC_COMMON_AREA_SIZE - 1) & 0x700000;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pnv_homer_power9_pba_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_homer_power9_pba_ops = {
|
||||
.read = pnv_homer_power9_pba_read,
|
||||
.write = pnv_homer_power9_pba_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_homer_power9_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
|
||||
|
||||
homer->pba_size = PNV9_XSCOM_PBA_SIZE;
|
||||
homer->pba_ops = &pnv_homer_power9_pba_ops;
|
||||
homer->homer_size = PNV9_HOMER_SIZE;
|
||||
homer->homer_ops = &pnv_power9_homer_ops;
|
||||
homer->core_max_base = PNV9_CORE_MAX_BASE;
|
||||
@ -229,28 +336,30 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(dev);
|
||||
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "chip", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'chip' not found: ");
|
||||
return;
|
||||
}
|
||||
homer->chip = PNV_CHIP(obj);
|
||||
assert(homer->chip);
|
||||
|
||||
pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops,
|
||||
homer, "xscom-pba", hmrc->pba_size);
|
||||
|
||||
/* homer region */
|
||||
memory_region_init_io(&homer->regs, OBJECT(dev),
|
||||
hmrc->homer_ops, homer, "homer-main-memory",
|
||||
hmrc->homer_size);
|
||||
}
|
||||
|
||||
static Property pnv_homer_properties[] = {
|
||||
DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_homer_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pnv_homer_realize;
|
||||
dc->desc = "PowerNV HOMER Memory";
|
||||
dc->props = pnv_homer_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_homer_type_info = {
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/isa/isa.h"
|
||||
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/ppc/pnv_lpc.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
@ -86,7 +86,7 @@ enum {
|
||||
#define ISA_FW_SIZE 0x10000000
|
||||
#define LPC_IO_OPB_ADDR 0xd0010000
|
||||
#define LPC_IO_OPB_SIZE 0x00010000
|
||||
#define LPC_MEM_OPB_ADDR 0xe0010000
|
||||
#define LPC_MEM_OPB_ADDR 0xe0000000
|
||||
#define LPC_MEM_OPB_SIZE 0x10000000
|
||||
#define LPC_FW_OPB_ADDR 0xf0000000
|
||||
#define LPC_FW_OPB_SIZE 0x10000000
|
||||
@ -122,26 +122,36 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset)
|
||||
}
|
||||
|
||||
/* POWER9 only */
|
||||
int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset)
|
||||
int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr,
|
||||
uint64_t lpcm_size)
|
||||
{
|
||||
const char compat[] = "ibm,power9-lpcm-opb\0simple-bus";
|
||||
const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc";
|
||||
char *name;
|
||||
int offset, lpcm_offset;
|
||||
uint64_t lpcm_addr = PNV9_LPCM_BASE(chip);
|
||||
uint32_t opb_ranges[8] = { 0,
|
||||
cpu_to_be32(lpcm_addr >> 32),
|
||||
cpu_to_be32((uint32_t)lpcm_addr),
|
||||
cpu_to_be32(PNV9_LPCM_SIZE / 2),
|
||||
cpu_to_be32(PNV9_LPCM_SIZE / 2),
|
||||
cpu_to_be32(lpcm_size / 2),
|
||||
cpu_to_be32(lpcm_size / 2),
|
||||
cpu_to_be32(lpcm_addr >> 32),
|
||||
cpu_to_be32(PNV9_LPCM_SIZE / 2),
|
||||
cpu_to_be32(PNV9_LPCM_SIZE / 2),
|
||||
cpu_to_be32(lpcm_size / 2),
|
||||
cpu_to_be32(lpcm_size / 2),
|
||||
};
|
||||
uint32_t opb_reg[4] = { cpu_to_be32(lpcm_addr >> 32),
|
||||
cpu_to_be32((uint32_t)lpcm_addr),
|
||||
cpu_to_be32(PNV9_LPCM_SIZE >> 32),
|
||||
cpu_to_be32((uint32_t)PNV9_LPCM_SIZE),
|
||||
cpu_to_be32(lpcm_size >> 32),
|
||||
cpu_to_be32((uint32_t)lpcm_size),
|
||||
};
|
||||
uint32_t lpc_ranges[12] = { 0, 0,
|
||||
cpu_to_be32(LPC_MEM_OPB_ADDR),
|
||||
cpu_to_be32(LPC_MEM_OPB_SIZE),
|
||||
cpu_to_be32(1), 0,
|
||||
cpu_to_be32(LPC_IO_OPB_ADDR),
|
||||
cpu_to_be32(LPC_IO_OPB_SIZE),
|
||||
cpu_to_be32(3), 0,
|
||||
cpu_to_be32(LPC_FW_OPB_ADDR),
|
||||
cpu_to_be32(LPC_FW_OPB_SIZE),
|
||||
};
|
||||
uint32_t reg[2];
|
||||
|
||||
@ -211,6 +221,8 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset)
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
|
||||
_FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat,
|
||||
sizeof(lpc_compat))));
|
||||
_FDT((fdt_setprop(fdt, offset, "ranges", lpc_ranges,
|
||||
sizeof(lpc_ranges))));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -679,20 +691,24 @@ static const TypeInfo pnv_lpc_power9_info = {
|
||||
.class_init = pnv_lpc_power9_class_init,
|
||||
};
|
||||
|
||||
static void pnv_lpc_power10_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->desc = "PowerNV LPC Controller POWER10";
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_lpc_power10_info = {
|
||||
.name = TYPE_PNV10_LPC,
|
||||
.parent = TYPE_PNV9_LPC,
|
||||
.class_init = pnv_lpc_power10_class_init,
|
||||
};
|
||||
|
||||
static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvLpcController *lpc = PNV_LPC(dev);
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "psi", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'psi' not found: ");
|
||||
return;
|
||||
}
|
||||
/* The LPC controller needs PSI to generate interrupts */
|
||||
lpc->psi = PNV_PSI(obj);
|
||||
assert(lpc->psi);
|
||||
|
||||
/* Reg inits */
|
||||
lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
|
||||
@ -734,12 +750,18 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
||||
&lpc->lpc_hc_regs);
|
||||
}
|
||||
|
||||
static Property pnv_lpc_properties[] = {
|
||||
DEFINE_PROP_LINK("psi", PnvLpcController, psi, TYPE_PNV_PSI, PnvPsi *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_lpc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pnv_lpc_realize;
|
||||
dc->desc = "PowerNV LPC Controller";
|
||||
dc->props = pnv_lpc_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_lpc_info = {
|
||||
@ -755,6 +777,7 @@ static void pnv_lpc_register_types(void)
|
||||
type_register_static(&pnv_lpc_info);
|
||||
type_register_static(&pnv_lpc_power8_info);
|
||||
type_register_static(&pnv_lpc_power9_info);
|
||||
type_register_static(&pnv_lpc_power10_info);
|
||||
}
|
||||
|
||||
type_init(pnv_lpc_register_types)
|
||||
@ -801,6 +824,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
|
||||
ISABus *isa_bus;
|
||||
qemu_irq *irqs;
|
||||
qemu_irq_handler handler;
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
|
||||
/* let isa_bus_new() create its own bridge on SysBus otherwise
|
||||
* devices speficied on the command line won't find the bus and
|
||||
@ -825,5 +849,17 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
|
||||
irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS);
|
||||
|
||||
isa_bus_irqs(isa_bus, irqs);
|
||||
|
||||
/*
|
||||
* TODO: Map PNOR on the LPC FW address space on demand ?
|
||||
*/
|
||||
memory_region_add_subregion(&lpc->isa_fw, PNOR_SPI_OFFSET,
|
||||
&pnv->pnor->mmio);
|
||||
/*
|
||||
* Start disabled. The HIOMAP protocol will activate the mapping
|
||||
* with HIOMAP_C_CREATE_WRITE_WINDOW
|
||||
*/
|
||||
memory_region_set_enabled(&pnv->pnor->mmio, false);
|
||||
|
||||
return isa_bus;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "hw/ppc/pnv_occ.h"
|
||||
@ -167,9 +167,7 @@ static void pnv_occ_power8_class_init(ObjectClass *klass, void *data)
|
||||
PnvOCCClass *poc = PNV_OCC_CLASS(klass);
|
||||
|
||||
poc->xscom_size = PNV_XSCOM_OCC_SIZE;
|
||||
poc->sram_size = PNV_OCC_COMMON_AREA_SIZE;
|
||||
poc->xscom_ops = &pnv_occ_power8_xscom_ops;
|
||||
poc->sram_ops = &pnv_occ_sram_ops;
|
||||
poc->psi_irq = PSIHB_IRQ_OCC;
|
||||
}
|
||||
|
||||
@ -240,9 +238,7 @@ static void pnv_occ_power9_class_init(ObjectClass *klass, void *data)
|
||||
PnvOCCClass *poc = PNV_OCC_CLASS(klass);
|
||||
|
||||
poc->xscom_size = PNV9_XSCOM_OCC_SIZE;
|
||||
poc->sram_size = PNV9_OCC_COMMON_AREA_SIZE;
|
||||
poc->xscom_ops = &pnv_occ_power9_xscom_ops;
|
||||
poc->sram_ops = &pnv_occ_sram_ops;
|
||||
poc->psi_irq = PSIHB9_IRQ_OCC;
|
||||
}
|
||||
|
||||
@ -257,34 +253,33 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvOCC *occ = PNV_OCC(dev);
|
||||
PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ);
|
||||
Object *obj;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(occ->psi);
|
||||
|
||||
occ->occmisc = 0;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "psi", &local_err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "required link 'psi' not found: ");
|
||||
return;
|
||||
}
|
||||
occ->psi = PNV_PSI(obj);
|
||||
|
||||
/* XScom region for OCC registers */
|
||||
pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), poc->xscom_ops,
|
||||
occ, "xscom-occ", poc->xscom_size);
|
||||
|
||||
/* XScom region for OCC SRAM registers */
|
||||
pnv_xscom_region_init(&occ->sram_regs, OBJECT(dev), poc->sram_ops,
|
||||
occ, "occ-common-area", poc->sram_size);
|
||||
/* OCC common area mmio region for OCC SRAM registers */
|
||||
memory_region_init_io(&occ->sram_regs, OBJECT(dev), &pnv_occ_sram_ops,
|
||||
occ, "occ-common-area",
|
||||
PNV_OCC_SENSOR_DATA_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static Property pnv_occ_properties[] = {
|
||||
DEFINE_PROP_LINK("psi", PnvOCC, psi, TYPE_PNV_PSI, PnvPsi *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_occ_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pnv_occ_realize;
|
||||
dc->desc = "PowerNV OCC Controller";
|
||||
dc->props = pnv_occ_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_occ_type_info = {
|
||||
|
135
hw/ppc/pnv_pnor.c
Normal file
135
hw/ppc/pnv_pnor.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* QEMU PowerNV PNOR simple model
|
||||
*
|
||||
* Copyright (c) 2015-2019, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/ppc/pnv_pnor.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
PnvPnor *s = PNV_PNOR(opaque);
|
||||
uint64_t ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
ret |= (uint64_t) s->storage[addr + i] << (8 * (size - i - 1));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pnv_pnor_update(PnvPnor *s, int offset, int size)
|
||||
{
|
||||
int offset_end;
|
||||
|
||||
if (s->blk) {
|
||||
return;
|
||||
}
|
||||
|
||||
offset_end = offset + size;
|
||||
offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
|
||||
offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
|
||||
|
||||
blk_pwrite(s->blk, offset, s->storage + offset,
|
||||
offset_end - offset, 0);
|
||||
}
|
||||
|
||||
static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPnor *s = PNV_PNOR(opaque);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
s->storage[addr + i] = (data >> (8 * (size - i - 1))) & 0xFF;
|
||||
}
|
||||
pnv_pnor_update(s, addr, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash
|
||||
* is BIG.
|
||||
*/
|
||||
static const MemoryRegionOps pnv_pnor_ops = {
|
||||
.read = pnv_pnor_read,
|
||||
.write = pnv_pnor_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void pnv_pnor_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPnor *s = PNV_PNOR(dev);
|
||||
int ret;
|
||||
|
||||
if (s->blk) {
|
||||
uint64_t perm = BLK_PERM_CONSISTENT_READ |
|
||||
(blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
|
||||
ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->size = blk_getlength(s->blk);
|
||||
if (s->size <= 0) {
|
||||
error_setg(errp, "failed to get flash size");
|
||||
return;
|
||||
}
|
||||
|
||||
s->storage = blk_blockalign(s->blk, s->size);
|
||||
|
||||
if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) {
|
||||
error_setg(errp, "failed to read the initial flash content");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
s->storage = blk_blockalign(NULL, s->size);
|
||||
memset(s->storage, 0xFF, s->size);
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s,
|
||||
TYPE_PNV_PNOR, s->size);
|
||||
}
|
||||
|
||||
static Property pnv_pnor_properties[] = {
|
||||
DEFINE_PROP_UINT32("size", PnvPnor, size, 128 << 20),
|
||||
DEFINE_PROP_DRIVE("drive", PnvPnor, blk),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_pnor_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pnv_pnor_realize;
|
||||
dc->props = pnv_pnor_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pnor_info = {
|
||||
.name = TYPE_PNV_PNOR,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PnvPnor),
|
||||
.class_init = pnv_pnor_class_init,
|
||||
};
|
||||
|
||||
static void pnv_pnor_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_pnor_info);
|
||||
}
|
||||
|
||||
type_init(pnv_pnor_register_types)
|
@ -497,8 +497,7 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
/* Create PSI interrupt control source */
|
||||
object_property_add_const_link(OBJECT(ics), ICS_PROP_XICS, obj,
|
||||
&error_abort);
|
||||
object_property_set_link(OBJECT(ics), obj, ICS_PROP_XICS, &error_abort);
|
||||
object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
@ -537,9 +536,6 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp)
|
||||
qemu_register_reset(pnv_psi_reset, dev);
|
||||
}
|
||||
|
||||
static const char compat_p8[] = "ibm,power8-psihb-x\0ibm,psihb-x";
|
||||
static const char compat_p9[] = "ibm,power9-psihb-x\0ibm,psihb-x";
|
||||
|
||||
static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset)
|
||||
{
|
||||
PnvPsiClass *ppc = PNV_PSI_GET_CLASS(dev);
|
||||
@ -558,13 +554,8 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset)
|
||||
_FDT(fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)));
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "#address-cells", 2));
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "#size-cells", 1));
|
||||
if (ppc->chip_type == PNV_CHIP_POWER9) {
|
||||
_FDT(fdt_setprop(fdt, offset, "compatible", compat_p9,
|
||||
sizeof(compat_p9)));
|
||||
} else {
|
||||
_FDT(fdt_setprop(fdt, offset, "compatible", compat_p8,
|
||||
sizeof(compat_p8)));
|
||||
}
|
||||
_FDT(fdt_setprop(fdt, offset, "compatible", ppc->compat,
|
||||
ppc->compat_size));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -578,15 +569,17 @@ static void pnv_psi_power8_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvPsiClass *ppc = PNV_PSI_CLASS(klass);
|
||||
static const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
|
||||
|
||||
dc->desc = "PowerNV PSI Controller POWER8";
|
||||
dc->realize = pnv_psi_power8_realize;
|
||||
|
||||
ppc->chip_type = PNV_CHIP_POWER8;
|
||||
ppc->xscom_pcba = PNV_XSCOM_PSIHB_BASE;
|
||||
ppc->xscom_size = PNV_XSCOM_PSIHB_SIZE;
|
||||
ppc->bar_mask = PSIHB_BAR_MASK;
|
||||
ppc->irq_set = pnv_psi_power8_irq_set;
|
||||
ppc->compat = compat;
|
||||
ppc->compat_size = sizeof(compat);
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_psi_power8_info = {
|
||||
@ -609,9 +602,12 @@ static const TypeInfo pnv_psi_power8_info = {
|
||||
#define PSIHB9_IRQ_METHOD PPC_BIT(0)
|
||||
#define PSIHB9_IRQ_RESET PPC_BIT(1)
|
||||
#define PSIHB9_ESB_CI_BASE 0x60
|
||||
#define PSIHB9_ESB_CI_VALID 1
|
||||
#define PSIHB9_ESB_CI_64K PPC_BIT(1)
|
||||
#define PSIHB9_ESB_CI_ADDR_MASK PPC_BITMASK(8, 47)
|
||||
#define PSIHB9_ESB_CI_VALID PPC_BIT(63)
|
||||
#define PSIHB9_ESB_NOTIF_ADDR 0x68
|
||||
#define PSIHB9_ESB_NOTIF_VALID 1
|
||||
#define PSIHB9_ESB_NOTIF_ADDR_MASK PPC_BITMASK(8, 60)
|
||||
#define PSIHB9_ESB_NOTIF_VALID PPC_BIT(63)
|
||||
#define PSIHB9_IVT_OFFSET 0x70
|
||||
#define PSIHB9_IVT_OFF_SHIFT 32
|
||||
|
||||
@ -851,8 +847,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp)
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(xsrc), PSIHB9_NUM_IRQS, "nr-irqs",
|
||||
&error_fatal);
|
||||
object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(psi),
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(xsrc), OBJECT(psi), "xive", &error_abort);
|
||||
object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -883,15 +878,17 @@ static void pnv_psi_power9_class_init(ObjectClass *klass, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvPsiClass *ppc = PNV_PSI_CLASS(klass);
|
||||
XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass);
|
||||
static const char compat[] = "ibm,power9-psihb-x\0ibm,psihb-x";
|
||||
|
||||
dc->desc = "PowerNV PSI Controller POWER9";
|
||||
dc->realize = pnv_psi_power9_realize;
|
||||
|
||||
ppc->chip_type = PNV_CHIP_POWER9;
|
||||
ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE;
|
||||
ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE;
|
||||
ppc->bar_mask = PSIHB9_BAR_MASK;
|
||||
ppc->irq_set = pnv_psi_power9_irq_set;
|
||||
ppc->compat = compat;
|
||||
ppc->compat_size = sizeof(compat);
|
||||
|
||||
xfc->notify = pnv_psi_notify;
|
||||
}
|
||||
@ -908,6 +905,26 @@ static const TypeInfo pnv_psi_power9_info = {
|
||||
},
|
||||
};
|
||||
|
||||
static void pnv_psi_power10_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvPsiClass *ppc = PNV_PSI_CLASS(klass);
|
||||
static const char compat[] = "ibm,power10-psihb-x\0ibm,psihb-x";
|
||||
|
||||
dc->desc = "PowerNV PSI Controller POWER10";
|
||||
|
||||
ppc->xscom_pcba = PNV10_XSCOM_PSIHB_BASE;
|
||||
ppc->xscom_size = PNV10_XSCOM_PSIHB_SIZE;
|
||||
ppc->compat = compat;
|
||||
ppc->compat_size = sizeof(compat);
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_psi_power10_info = {
|
||||
.name = TYPE_PNV10_PSI,
|
||||
.parent = TYPE_PNV9_PSI,
|
||||
.class_init = pnv_psi_power10_class_init,
|
||||
};
|
||||
|
||||
static void pnv_psi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@ -937,6 +954,7 @@ static void pnv_psi_register_types(void)
|
||||
type_register_static(&pnv_psi_info);
|
||||
type_register_static(&pnv_psi_power8_info);
|
||||
type_register_static(&pnv_psi_power9_info);
|
||||
type_register_static(&pnv_psi_power10_info);
|
||||
}
|
||||
|
||||
type_init(pnv_psi_register_types);
|
||||
|
@ -36,16 +36,6 @@
|
||||
#define PRD_P9_IPOLL_REG_MASK 0x000F0033
|
||||
#define PRD_P9_IPOLL_REG_STATUS 0x000F0034
|
||||
|
||||
/* PBA BARs */
|
||||
#define P8_PBA_BAR0 0x2013f00
|
||||
#define P8_PBA_BAR2 0x2013f02
|
||||
#define P8_PBA_BARMASK0 0x2013f04
|
||||
#define P8_PBA_BARMASK2 0x2013f06
|
||||
#define P9_PBA_BAR0 0x5012b00
|
||||
#define P9_PBA_BAR2 0x5012b02
|
||||
#define P9_PBA_BARMASK0 0x5012b04
|
||||
#define P9_PBA_BARMASK2 0x5012b06
|
||||
|
||||
static void xscom_complete(CPUState *cs, uint64_t hmer_bits)
|
||||
{
|
||||
/*
|
||||
@ -67,13 +57,7 @@ static void xscom_complete(CPUState *cs, uint64_t hmer_bits)
|
||||
|
||||
static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
{
|
||||
addr &= (PNV_XSCOM_SIZE - 1);
|
||||
|
||||
if (pnv_chip_is_power9(chip)) {
|
||||
return addr >> 3;
|
||||
} else {
|
||||
return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
|
||||
}
|
||||
return PNV_CHIP_GET_CLASS(chip)->xscom_pcba(chip, addr);
|
||||
}
|
||||
|
||||
static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
|
||||
@ -84,26 +68,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
|
||||
case 0x18002: /* ECID2 */
|
||||
return 0;
|
||||
|
||||
case P9_PBA_BAR0:
|
||||
return PNV9_HOMER_BASE(chip);
|
||||
case P8_PBA_BAR0:
|
||||
return PNV_HOMER_BASE(chip);
|
||||
|
||||
case P9_PBA_BARMASK0: /* P9 homer region size */
|
||||
return PNV9_HOMER_SIZE;
|
||||
case P8_PBA_BARMASK0: /* P8 homer region size */
|
||||
return PNV_HOMER_SIZE;
|
||||
|
||||
case P9_PBA_BAR2: /* P9 occ common area */
|
||||
return PNV9_OCC_COMMON_AREA(chip);
|
||||
case P8_PBA_BAR2: /* P8 occ common area */
|
||||
return PNV_OCC_COMMON_AREA(chip);
|
||||
|
||||
case P9_PBA_BARMASK2: /* P9 occ common area size */
|
||||
return PNV9_OCC_COMMON_AREA_SIZE;
|
||||
case P8_PBA_BARMASK2: /* P8 occ common area size */
|
||||
return PNV_OCC_COMMON_AREA_SIZE;
|
||||
|
||||
case 0x1010c00: /* PIBAM FIR */
|
||||
case 0x1010c03: /* PIBAM FIR MASK */
|
||||
|
||||
@ -124,9 +88,7 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
|
||||
case 0x202000f: /* ADU stuff, receive status register*/
|
||||
return 0;
|
||||
case 0x2013f01: /* PBA stuff */
|
||||
case 0x2013f03: /* PBA stuff */
|
||||
case 0x2013f05: /* PBA stuff */
|
||||
case 0x2013f07: /* PBA stuff */
|
||||
return 0;
|
||||
case 0x2013028: /* CAPP stuff */
|
||||
case 0x201302a: /* CAPP stuff */
|
||||
@ -298,31 +260,25 @@ static int xscom_dt_child(Object *child, void *opaque)
|
||||
PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child);
|
||||
PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd);
|
||||
|
||||
if (xc->dt_xscom) {
|
||||
/*
|
||||
* Only "realized" devices should be configured in the DT
|
||||
*/
|
||||
if (xc->dt_xscom && DEVICE(child)->realized) {
|
||||
_FDT((xc->dt_xscom(xd, args->fdt, args->xscom_offset)));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom";
|
||||
static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom";
|
||||
|
||||
int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset)
|
||||
int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset,
|
||||
uint64_t xscom_base, uint64_t xscom_size,
|
||||
const char *compat, int compat_size)
|
||||
{
|
||||
uint64_t reg[2];
|
||||
uint64_t reg[] = { xscom_base, xscom_size };
|
||||
int xscom_offset;
|
||||
ForeachPopulateArgs args;
|
||||
char *name;
|
||||
|
||||
if (pnv_chip_is_power9(chip)) {
|
||||
reg[0] = cpu_to_be64(PNV9_XSCOM_BASE(chip));
|
||||
reg[1] = cpu_to_be64(PNV9_XSCOM_SIZE);
|
||||
} else {
|
||||
reg[0] = cpu_to_be64(PNV_XSCOM_BASE(chip));
|
||||
reg[1] = cpu_to_be64(PNV_XSCOM_SIZE);
|
||||
}
|
||||
|
||||
name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
|
||||
xscom_offset = fdt_add_subnode(fdt, root_offset, name);
|
||||
_FDT(xscom_offset);
|
||||
@ -331,21 +287,18 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset)
|
||||
_FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1)));
|
||||
_FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1)));
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
|
||||
|
||||
if (pnv_chip_is_power9(chip)) {
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9,
|
||||
sizeof(compat_p9))));
|
||||
} else {
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8,
|
||||
sizeof(compat_p8))));
|
||||
}
|
||||
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size)));
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
|
||||
|
||||
args.fdt = fdt;
|
||||
args.xscom_offset = xscom_offset;
|
||||
|
||||
object_child_foreach(OBJECT(chip), xscom_dt_child, &args);
|
||||
/*
|
||||
* Loop on the whole object hierarchy to catch all
|
||||
* PnvXScomInterface objects which can lie a bit deeper than the
|
||||
* first layer.
|
||||
*/
|
||||
object_child_foreach_recursive(OBJECT(chip), xscom_dt_child, &args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
79
hw/ppc/ppc.c
79
hw/ppc/ppc.c
@ -275,10 +275,9 @@ void ppc970_irq_init(PowerPCCPU *cpu)
|
||||
static void power7_set_irq(void *opaque, int pin, int level)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
|
||||
env, pin, level);
|
||||
&cpu->env, pin, level);
|
||||
|
||||
switch (pin) {
|
||||
case POWER7_INPUT_INT:
|
||||
@ -292,11 +291,6 @@ static void power7_set_irq(void *opaque, int pin, int level)
|
||||
LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
|
||||
return;
|
||||
}
|
||||
if (level) {
|
||||
env->irq_input_state |= 1 << pin;
|
||||
} else {
|
||||
env->irq_input_state &= ~(1 << pin);
|
||||
}
|
||||
}
|
||||
|
||||
void ppcPOWER7_irq_init(PowerPCCPU *cpu)
|
||||
@ -311,10 +305,9 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu)
|
||||
static void power9_set_irq(void *opaque, int pin, int level)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
|
||||
env, pin, level);
|
||||
&cpu->env, pin, level);
|
||||
|
||||
switch (pin) {
|
||||
case POWER9_INPUT_INT:
|
||||
@ -334,11 +327,6 @@ static void power9_set_irq(void *opaque, int pin, int level)
|
||||
LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
|
||||
return;
|
||||
}
|
||||
if (level) {
|
||||
env->irq_input_state |= 1 << pin;
|
||||
} else {
|
||||
env->irq_input_state &= ~(1 << pin);
|
||||
}
|
||||
}
|
||||
|
||||
void ppcPOWER9_irq_init(PowerPCCPU *cpu)
|
||||
@ -694,6 +682,35 @@ void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value)
|
||||
&tb_env->atb_offset, ((uint64_t)value << 32) | tb);
|
||||
}
|
||||
|
||||
uint64_t cpu_ppc_load_vtb(CPUPPCState *env)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
|
||||
return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
tb_env->vtb_offset);
|
||||
}
|
||||
|
||||
void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
|
||||
cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
&tb_env->vtb_offset, value);
|
||||
}
|
||||
|
||||
void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t tb;
|
||||
|
||||
tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
tb_env->tb_offset);
|
||||
tb &= 0xFFFFFFUL;
|
||||
tb |= (value & ~0xFFFFFFUL);
|
||||
cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
&tb_env->tb_offset, tb);
|
||||
}
|
||||
|
||||
static void cpu_ppc_tb_stop (CPUPPCState *env)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
@ -805,12 +822,9 @@ target_ulong cpu_ppc_load_hdecr(CPUPPCState *env)
|
||||
uint64_t cpu_ppc_load_purr (CPUPPCState *env)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t diff;
|
||||
|
||||
diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start;
|
||||
|
||||
return tb_env->purr_load +
|
||||
muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND);
|
||||
return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
tb_env->purr_offset);
|
||||
}
|
||||
|
||||
/* When decrementer expires,
|
||||
@ -969,12 +983,12 @@ static void cpu_ppc_hdecr_cb(void *opaque)
|
||||
cpu_ppc_hdecr_excp(cpu);
|
||||
}
|
||||
|
||||
static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value)
|
||||
void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value)
|
||||
{
|
||||
ppc_tb_t *tb_env = cpu->env.tb_env;
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
|
||||
tb_env->purr_load = value;
|
||||
tb_env->purr_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
&tb_env->purr_offset, value);
|
||||
}
|
||||
|
||||
static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
|
||||
@ -991,7 +1005,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
|
||||
*/
|
||||
_cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32);
|
||||
_cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32);
|
||||
cpu_ppc_store_purr(cpu, 0x0000000000000000ULL);
|
||||
cpu_ppc_store_purr(env, 0x0000000000000000ULL);
|
||||
}
|
||||
|
||||
static void timebase_save(PPCTimebase *tb)
|
||||
@ -1495,18 +1509,31 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
|
||||
}
|
||||
}
|
||||
|
||||
int ppc_cpu_pir(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
return env->spr_cb[SPR_PIR].default_value;
|
||||
}
|
||||
|
||||
PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (env->spr_cb[SPR_PIR].default_value == pir) {
|
||||
if (ppc_cpu_pir(cpu) == pir) {
|
||||
return cpu;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ppc_irq_reset(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
env->irq_input_state = 0;
|
||||
kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0);
|
||||
}
|
||||
|
133
hw/ppc/spapr.c
133
hw/ppc/spapr.c
@ -76,7 +76,6 @@
|
||||
#include "hw/nmi.h"
|
||||
#include "hw/intc/intc.h"
|
||||
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/ppc/spapr_cpu_core.h"
|
||||
#include "hw/mem/memory-device.h"
|
||||
#include "hw/ppc/spapr_tpm_proxy.h"
|
||||
@ -897,69 +896,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool spapr_hotplugged_dev_before_cas(void)
|
||||
{
|
||||
Object *drc_container, *obj;
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator iter;
|
||||
|
||||
drc_container = container_get(object_get_root(), "/dr-connector");
|
||||
object_property_iter_init(&iter, drc_container);
|
||||
while ((prop = object_property_iter_next(&iter))) {
|
||||
if (!strstart(prop->type, "link<", NULL)) {
|
||||
continue;
|
||||
}
|
||||
obj = object_property_get_link(drc_container, prop->name, NULL);
|
||||
if (spapr_drc_needed(obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset);
|
||||
|
||||
int spapr_h_cas_compose_response(SpaprMachineState *spapr,
|
||||
target_ulong addr, target_ulong size,
|
||||
SpaprOptionVector *ov5_updates)
|
||||
{
|
||||
void *fdt;
|
||||
SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 };
|
||||
|
||||
if (spapr_hotplugged_dev_before_cas()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (size < sizeof(hdr) || size > FW_MAX_SIZE) {
|
||||
error_report("SLOF provided an unexpected CAS buffer size "
|
||||
TARGET_FMT_lu " (min: %zu, max: %u)",
|
||||
size, sizeof(hdr), FW_MAX_SIZE);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
size -= sizeof(hdr);
|
||||
|
||||
fdt = spapr_build_fdt(spapr, false);
|
||||
_FDT((fdt_pack(fdt)));
|
||||
|
||||
if (fdt_totalsize(fdt) + sizeof(hdr) > size) {
|
||||
g_free(fdt);
|
||||
trace_spapr_cas_failed(size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpu_physical_memory_write(addr, &hdr, sizeof(hdr));
|
||||
cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt));
|
||||
trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
|
||||
|
||||
g_free(spapr->fdt_blob);
|
||||
spapr->fdt_size = fdt_totalsize(fdt);
|
||||
spapr->fdt_initial_size = spapr->fdt_size;
|
||||
spapr->fdt_blob = fdt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt)
|
||||
{
|
||||
MachineState *ms = MACHINE(spapr);
|
||||
@ -1197,7 +1133,7 @@ static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt)
|
||||
}
|
||||
}
|
||||
|
||||
static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset)
|
||||
void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
|
||||
{
|
||||
MachineState *machine = MACHINE(spapr);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
@ -1207,8 +1143,8 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset)
|
||||
SpaprPhbState *phb;
|
||||
char *buf;
|
||||
|
||||
fdt = g_malloc0(FDT_MAX_SIZE);
|
||||
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
|
||||
fdt = g_malloc0(space);
|
||||
_FDT((fdt_create_empty_tree(fdt, space)));
|
||||
|
||||
/* Root node */
|
||||
_FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp"));
|
||||
@ -1723,19 +1659,13 @@ static void spapr_machine_reset(MachineState *machine)
|
||||
*/
|
||||
fdt_addr = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FDT_MAX_SIZE;
|
||||
|
||||
fdt = spapr_build_fdt(spapr, true);
|
||||
fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE);
|
||||
|
||||
rc = fdt_pack(fdt);
|
||||
|
||||
/* Should only fail if we've built a corrupted tree */
|
||||
assert(rc == 0);
|
||||
|
||||
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
|
||||
error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
|
||||
fdt_totalsize(fdt), FDT_MAX_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Load the fdt */
|
||||
qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
|
||||
cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
|
||||
@ -1910,8 +1840,6 @@ static bool spapr_ov5_cas_needed(void *opaque)
|
||||
{
|
||||
SpaprMachineState *spapr = opaque;
|
||||
SpaprOptionVector *ov5_mask = spapr_ovec_new();
|
||||
SpaprOptionVector *ov5_legacy = spapr_ovec_new();
|
||||
SpaprOptionVector *ov5_removed = spapr_ovec_new();
|
||||
bool cas_needed;
|
||||
|
||||
/* Prior to the introduction of SpaprOptionVector, we had two option
|
||||
@ -1943,17 +1871,11 @@ static bool spapr_ov5_cas_needed(void *opaque)
|
||||
spapr_ovec_set(ov5_mask, OV5_DRCONF_MEMORY);
|
||||
spapr_ovec_set(ov5_mask, OV5_DRMEM_V2);
|
||||
|
||||
/* spapr_ovec_diff returns true if bits were removed. we avoid using
|
||||
* the mask itself since in the future it's possible "legacy" bits may be
|
||||
* removed via machine options, which could generate a false positive
|
||||
* that breaks migration.
|
||||
*/
|
||||
spapr_ovec_intersect(ov5_legacy, spapr->ov5, ov5_mask);
|
||||
cas_needed = spapr_ovec_diff(ov5_removed, spapr->ov5, ov5_legacy);
|
||||
/* We need extra information if we have any bits outside the mask
|
||||
* defined above */
|
||||
cas_needed = !spapr_ovec_subset(spapr->ov5, ov5_mask);
|
||||
|
||||
spapr_ovec_cleanup(ov5_mask);
|
||||
spapr_ovec_cleanup(ov5_legacy);
|
||||
spapr_ovec_cleanup(ov5_removed);
|
||||
|
||||
return cas_needed;
|
||||
}
|
||||
@ -2564,7 +2486,7 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp)
|
||||
" requires the use of VSMT mode %d.\n",
|
||||
smp_threads, kvm_smt, spapr->vsmt);
|
||||
}
|
||||
kvmppc_hint_smt_possible(&local_err);
|
||||
kvmppc_error_append_smt_possible_hint(&local_err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -4275,6 +4197,42 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj,
|
||||
kvm_irqchip_in_kernel() ? "in-kernel" : "emulated");
|
||||
}
|
||||
|
||||
static int spapr_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(xfb);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(spapr->xive);
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
|
||||
int count;
|
||||
|
||||
/* This is a XIVE only operation */
|
||||
assert(spapr->active_intc == SPAPR_INTC(spapr->xive));
|
||||
|
||||
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
priority, logic_serv, match);
|
||||
if (count < 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we implement the save and restore of the thread interrupt
|
||||
* contexts in the enter/exit CPU handlers of the machine and the
|
||||
* escalations in QEMU, we should be able to handle non dispatched
|
||||
* vCPUs.
|
||||
*
|
||||
* Until this is done, the sPAPR machine should find at least one
|
||||
* matching context always.
|
||||
*/
|
||||
if (count == 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n",
|
||||
nvt_blk, nvt_idx);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int spapr_get_vcpu_id(PowerPCCPU *cpu)
|
||||
{
|
||||
return cpu->vcpu_id;
|
||||
@ -4371,6 +4329,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||
PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
|
||||
XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
|
||||
InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
|
||||
XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc);
|
||||
|
||||
mc->desc = "pSeries Logical Partition (PAPR compliant)";
|
||||
mc->ignore_boot_device_suffixes = true;
|
||||
@ -4447,6 +4406,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||
smc->linux_pci_probe = true;
|
||||
smc->smp_threads_vsmt = true;
|
||||
smc->nr_xirqs = SPAPR_NR_XIRQS;
|
||||
xfc->match_nvt = spapr_match_nvt;
|
||||
}
|
||||
|
||||
static const TypeInfo spapr_machine_info = {
|
||||
@ -4465,6 +4425,7 @@ static const TypeInfo spapr_machine_info = {
|
||||
{ TYPE_PPC_VIRTUAL_HYPERVISOR },
|
||||
{ TYPE_XICS_FABRIC },
|
||||
{ TYPE_INTERRUPT_STATS_PROVIDER },
|
||||
{ TYPE_XIVE_FABRIC },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "sysemu/runstate.h"
|
||||
@ -15,6 +16,7 @@
|
||||
#include "cpu-models.h"
|
||||
#include "trace.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "hw/ppc/fdt.h"
|
||||
#include "hw/ppc/spapr_ovec.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "hw/mem/memory-device.h"
|
||||
@ -1638,6 +1640,26 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu,
|
||||
return best_compat;
|
||||
}
|
||||
|
||||
static bool spapr_hotplugged_dev_before_cas(void)
|
||||
{
|
||||
Object *drc_container, *obj;
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator iter;
|
||||
|
||||
drc_container = container_get(object_get_root(), "/dr-connector");
|
||||
object_property_iter_init(&iter, drc_container);
|
||||
while ((prop = object_property_iter_next(&iter))) {
|
||||
if (!strstart(prop->type, "link<", NULL)) {
|
||||
continue;
|
||||
}
|
||||
obj = object_property_get_link(drc_container, prop->name, NULL);
|
||||
if (spapr_drc_needed(obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
||||
SpaprMachineState *spapr,
|
||||
target_ulong opcode,
|
||||
@ -1645,9 +1667,11 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
||||
{
|
||||
/* Working address in data buffer */
|
||||
target_ulong addr = ppc64_phys_to_real(args[0]);
|
||||
target_ulong fdt_buf = args[1];
|
||||
target_ulong fdt_bufsize = args[2];
|
||||
target_ulong ov_table;
|
||||
uint32_t cas_pvr;
|
||||
SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates;
|
||||
SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old;
|
||||
bool guest_radix;
|
||||
Error *local_err = NULL;
|
||||
bool raw_mode_supported = false;
|
||||
@ -1746,9 +1770,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
||||
/* capabilities that have been added since CAS-generated guest reset.
|
||||
* if capabilities have since been removed, generate another reset
|
||||
*/
|
||||
ov5_updates = spapr_ovec_new();
|
||||
spapr->cas_reboot = spapr_ovec_diff(ov5_updates,
|
||||
ov5_cas_old, spapr->ov5_cas);
|
||||
spapr->cas_reboot = !spapr_ovec_subset(ov5_cas_old, spapr->ov5_cas);
|
||||
spapr_ovec_cleanup(ov5_cas_old);
|
||||
/* Now that processing is finished, set the radix/hash bit for the
|
||||
* guest if it requested a valid mode; otherwise terminate the boot. */
|
||||
@ -1767,21 +1789,10 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
||||
}
|
||||
spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00);
|
||||
spapr_ovec_cleanup(ov1_guest);
|
||||
if (!spapr->cas_reboot) {
|
||||
/* If spapr_machine_reset() did not set up a HPT but one is necessary
|
||||
* (because the guest isn't going to use radix) then set it up here. */
|
||||
if ((spapr->patb_entry & PATE1_GR) && !guest_radix) {
|
||||
/* legacy hash or new hash: */
|
||||
spapr_setup_hpt_and_vrma(spapr);
|
||||
}
|
||||
spapr->cas_reboot =
|
||||
(spapr_h_cas_compose_response(spapr, args[1], args[2],
|
||||
ov5_updates) != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure the guest asks for an interrupt mode we support; otherwise
|
||||
* terminate the boot.
|
||||
* Ensure the guest asks for an interrupt mode we support;
|
||||
* otherwise terminate the boot.
|
||||
*/
|
||||
if (guest_xive) {
|
||||
if (!spapr->irq->xive) {
|
||||
@ -1797,17 +1808,44 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a machine reset when we have an update of the
|
||||
* interrupt mode. Only required when the machine supports both
|
||||
* modes.
|
||||
*/
|
||||
if (!spapr->cas_reboot) {
|
||||
spapr->cas_reboot = spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT)
|
||||
&& spapr->irq->xics && spapr->irq->xive;
|
||||
spapr_irq_update_active_intc(spapr);
|
||||
|
||||
if (spapr_hotplugged_dev_before_cas()) {
|
||||
spapr->cas_reboot = true;
|
||||
}
|
||||
|
||||
spapr_ovec_cleanup(ov5_updates);
|
||||
if (!spapr->cas_reboot) {
|
||||
void *fdt;
|
||||
SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 };
|
||||
|
||||
/* If spapr_machine_reset() did not set up a HPT but one is necessary
|
||||
* (because the guest isn't going to use radix) then set it up here. */
|
||||
if ((spapr->patb_entry & PATE1_GR) && !guest_radix) {
|
||||
/* legacy hash or new hash: */
|
||||
spapr_setup_hpt_and_vrma(spapr);
|
||||
}
|
||||
|
||||
if (fdt_bufsize < sizeof(hdr)) {
|
||||
error_report("SLOF provided insufficient CAS buffer "
|
||||
TARGET_FMT_lu " (min: %zu)", fdt_bufsize, sizeof(hdr));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fdt_bufsize -= sizeof(hdr);
|
||||
|
||||
fdt = spapr_build_fdt(spapr, false, fdt_bufsize);
|
||||
_FDT((fdt_pack(fdt)));
|
||||
|
||||
cpu_physical_memory_write(fdt_buf, &hdr, sizeof(hdr));
|
||||
cpu_physical_memory_write(fdt_buf + sizeof(hdr), fdt,
|
||||
fdt_totalsize(fdt));
|
||||
trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
|
||||
|
||||
g_free(spapr->fdt_blob);
|
||||
spapr->fdt_size = fdt_totalsize(fdt);
|
||||
spapr->fdt_initial_size = spapr->fdt_size;
|
||||
spapr->fdt_blob = fdt;
|
||||
}
|
||||
|
||||
if (spapr->cas_reboot) {
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_SUBSYSTEM_RESET);
|
||||
|
@ -70,15 +70,16 @@ void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num)
|
||||
bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num);
|
||||
}
|
||||
|
||||
int spapr_irq_init_kvm(int (*fn)(SpaprInterruptController *, Error **),
|
||||
int spapr_irq_init_kvm(SpaprInterruptControllerInitKvm fn,
|
||||
SpaprInterruptController *intc,
|
||||
uint32_t nr_servers,
|
||||
Error **errp)
|
||||
{
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) {
|
||||
if (fn(intc, &local_err) < 0) {
|
||||
if (fn(intc, nr_servers, &local_err) < 0) {
|
||||
if (machine_kernel_irqchip_required(machine)) {
|
||||
error_prepend(&local_err,
|
||||
"kernel_irqchip requested but unavailable: ");
|
||||
@ -313,25 +314,11 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
|
||||
Object *obj;
|
||||
|
||||
obj = object_new(TYPE_ICS_SPAPR);
|
||||
object_property_add_child(OBJECT(spapr), "ics", obj, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
||||
object_property_set_link(obj, OBJECT(spapr), ICS_PROP_XICS,
|
||||
&error_abort);
|
||||
object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -495,6 +482,7 @@ static void set_active_intc(SpaprMachineState *spapr,
|
||||
SpaprInterruptController *new_intc)
|
||||
{
|
||||
SpaprInterruptControllerClass *sicc;
|
||||
uint32_t nr_servers = spapr_max_server_number(spapr);
|
||||
|
||||
assert(new_intc);
|
||||
|
||||
@ -512,7 +500,7 @@ static void set_active_intc(SpaprMachineState *spapr,
|
||||
|
||||
sicc = SPAPR_INTC_GET_CLASS(new_intc);
|
||||
if (sicc->activate) {
|
||||
sicc->activate(new_intc, &error_fatal);
|
||||
sicc->activate(new_intc, nr_servers, &error_fatal);
|
||||
}
|
||||
|
||||
spapr->active_intc = new_intc;
|
||||
|
@ -76,31 +76,21 @@ void spapr_ovec_intersect(SpaprOptionVector *ov,
|
||||
bitmap_and(ov->bitmap, ov1->bitmap, ov2->bitmap, OV_MAXBITS);
|
||||
}
|
||||
|
||||
/* returns true if options bits were removed, false otherwise */
|
||||
bool spapr_ovec_diff(SpaprOptionVector *ov,
|
||||
SpaprOptionVector *ov_old,
|
||||
SpaprOptionVector *ov_new)
|
||||
/* returns true if ov1 has a subset of bits in ov2 */
|
||||
bool spapr_ovec_subset(SpaprOptionVector *ov1, SpaprOptionVector *ov2)
|
||||
{
|
||||
unsigned long *change_mask = bitmap_new(OV_MAXBITS);
|
||||
unsigned long *removed_bits = bitmap_new(OV_MAXBITS);
|
||||
bool bits_were_removed = false;
|
||||
unsigned long *tmp = bitmap_new(OV_MAXBITS);
|
||||
bool result;
|
||||
|
||||
g_assert(ov);
|
||||
g_assert(ov_old);
|
||||
g_assert(ov_new);
|
||||
g_assert(ov1);
|
||||
g_assert(ov2);
|
||||
|
||||
bitmap_xor(change_mask, ov_old->bitmap, ov_new->bitmap, OV_MAXBITS);
|
||||
bitmap_and(ov->bitmap, ov_new->bitmap, change_mask, OV_MAXBITS);
|
||||
bitmap_and(removed_bits, ov_old->bitmap, change_mask, OV_MAXBITS);
|
||||
bitmap_andnot(tmp, ov1->bitmap, ov2->bitmap, OV_MAXBITS);
|
||||
result = bitmap_empty(tmp, OV_MAXBITS);
|
||||
|
||||
if (!bitmap_empty(removed_bits, OV_MAXBITS)) {
|
||||
bits_were_removed = true;
|
||||
}
|
||||
g_free(tmp);
|
||||
|
||||
g_free(change_mask);
|
||||
g_free(removed_bits);
|
||||
|
||||
return bits_were_removed;
|
||||
return result;
|
||||
}
|
||||
|
||||
void spapr_ovec_cleanup(SpaprOptionVector *ov)
|
||||
|
@ -55,6 +55,7 @@ enum ipmi_op {
|
||||
#define IPMI_CC_COMMAND_NOT_SUPPORTED 0xd5
|
||||
|
||||
#define IPMI_NETFN_APP 0x06
|
||||
#define IPMI_NETFN_OEM 0x3a
|
||||
|
||||
#define IPMI_DEBUG 1
|
||||
|
||||
@ -265,4 +266,45 @@ int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid,
|
||||
const struct ipmi_sdr_compact **sdr, uint16_t *nextrec);
|
||||
void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log);
|
||||
|
||||
#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim"
|
||||
#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \
|
||||
TYPE_IPMI_BMC_SIMULATOR)
|
||||
|
||||
typedef struct IPMIBmcSim IPMIBmcSim;
|
||||
|
||||
typedef struct RspBuffer {
|
||||
uint8_t buffer[MAX_IPMI_MSG_SIZE];
|
||||
unsigned int len;
|
||||
} RspBuffer;
|
||||
|
||||
static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte)
|
||||
{
|
||||
rsp->buffer[2] = byte;
|
||||
}
|
||||
|
||||
/* Add a byte to the response. */
|
||||
static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte)
|
||||
{
|
||||
if (rsp->len >= sizeof(rsp->buffer)) {
|
||||
rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED);
|
||||
return;
|
||||
}
|
||||
rsp->buffer[rsp->len++] = byte;
|
||||
}
|
||||
|
||||
typedef struct IPMICmdHandler {
|
||||
void (*cmd_handler)(IPMIBmcSim *s,
|
||||
uint8_t *cmd, unsigned int cmd_len,
|
||||
RspBuffer *rsp);
|
||||
unsigned int cmd_len_min;
|
||||
} IPMICmdHandler;
|
||||
|
||||
typedef struct IPMINetfn {
|
||||
unsigned int cmd_nums;
|
||||
const IPMICmdHandler *cmd_handlers;
|
||||
} IPMINetfn;
|
||||
|
||||
int ipmi_sim_register_netfn(IPMIBmcSim *s, unsigned int netfn,
|
||||
const IPMINetfn *netfnd);
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "hw/ppc/pnv_lpc.h"
|
||||
#include "hw/ppc/pnv_pnor.h"
|
||||
#include "hw/ppc/pnv_psi.h"
|
||||
#include "hw/ppc/pnv_occ.h"
|
||||
#include "hw/ppc/pnv_homer.h"
|
||||
@ -37,13 +38,6 @@
|
||||
#define PNV_CHIP_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP)
|
||||
|
||||
typedef enum PnvChipType {
|
||||
PNV_CHIP_POWER8E, /* AKA Murano (default) */
|
||||
PNV_CHIP_POWER8, /* AKA Venice */
|
||||
PNV_CHIP_POWER8NVL, /* AKA Naples */
|
||||
PNV_CHIP_POWER9, /* AKA Nimbus */
|
||||
} PnvChipType;
|
||||
|
||||
typedef struct PnvChip {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
@ -55,7 +49,7 @@ typedef struct PnvChip {
|
||||
|
||||
uint32_t nr_cores;
|
||||
uint64_t cores_mask;
|
||||
void *cores;
|
||||
PnvCore **cores;
|
||||
|
||||
MemoryRegion xscom_mmio;
|
||||
MemoryRegion xscom;
|
||||
@ -98,12 +92,29 @@ typedef struct Pnv9Chip {
|
||||
PnvQuad *quads;
|
||||
} Pnv9Chip;
|
||||
|
||||
/*
|
||||
* A SMT8 fused core is a pair of SMT4 cores.
|
||||
*/
|
||||
#define PNV9_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf)
|
||||
#define PNV9_PIR2CHIP(pir) (((pir) >> 8) & 0x7f)
|
||||
|
||||
#define TYPE_PNV10_CHIP "pnv10-chip"
|
||||
#define PNV10_CHIP(obj) OBJECT_CHECK(Pnv10Chip, (obj), TYPE_PNV10_CHIP)
|
||||
|
||||
typedef struct Pnv10Chip {
|
||||
/*< private >*/
|
||||
PnvChip parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
Pnv9Psi psi;
|
||||
PnvLpcController lpc;
|
||||
} Pnv10Chip;
|
||||
|
||||
typedef struct PnvChipClass {
|
||||
/*< private >*/
|
||||
SysBusDeviceClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
PnvChipType chip_type;
|
||||
uint64_t chip_cfam_id;
|
||||
uint64_t cores_mask;
|
||||
|
||||
@ -113,9 +124,12 @@ typedef struct PnvChipClass {
|
||||
void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp);
|
||||
void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu);
|
||||
void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu);
|
||||
void (*intc_print_info)(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon);
|
||||
ISABus *(*isa_create)(PnvChip *chip, Error **errp);
|
||||
void (*dt_populate)(PnvChip *chip, void *fdt);
|
||||
void (*pic_print_info)(PnvChip *chip, Monitor *mon);
|
||||
uint64_t (*xscom_core_base)(PnvChip *chip, uint32_t core_id);
|
||||
uint32_t (*xscom_pcba)(PnvChip *chip, uint64_t addr);
|
||||
} PnvChipClass;
|
||||
|
||||
#define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP
|
||||
@ -137,6 +151,10 @@ typedef struct PnvChipClass {
|
||||
#define PNV_CHIP_POWER9(obj) \
|
||||
OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9)
|
||||
|
||||
#define TYPE_PNV_CHIP_POWER10 PNV_CHIP_TYPE_NAME("power10_v1.0")
|
||||
#define PNV_CHIP_POWER10(obj) \
|
||||
OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER10)
|
||||
|
||||
/*
|
||||
* This generates a HW chip id depending on an index, as found on a
|
||||
* two socket system with dual chip modules :
|
||||
@ -156,11 +174,30 @@ typedef struct PnvChipClass {
|
||||
#define PNV_CHIP_INDEX(chip) \
|
||||
(((chip)->chip_id >> 2) * 2 + ((chip)->chip_id & 0x3))
|
||||
|
||||
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir);
|
||||
|
||||
#define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv")
|
||||
#define PNV_MACHINE(obj) \
|
||||
OBJECT_CHECK(PnvMachineState, (obj), TYPE_PNV_MACHINE)
|
||||
#define PNV_MACHINE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(PnvMachineClass, obj, TYPE_PNV_MACHINE)
|
||||
#define PNV_MACHINE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PnvMachineClass, klass, TYPE_PNV_MACHINE)
|
||||
|
||||
typedef struct PnvMachineState {
|
||||
typedef struct PnvMachineState PnvMachineState;
|
||||
|
||||
typedef struct PnvMachineClass {
|
||||
/*< private >*/
|
||||
MachineClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
const char *compat;
|
||||
int compat_size;
|
||||
|
||||
void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt);
|
||||
} PnvMachineClass;
|
||||
|
||||
struct PnvMachineState {
|
||||
/*< private >*/
|
||||
MachineState parent_obj;
|
||||
|
||||
@ -175,17 +212,11 @@ typedef struct PnvMachineState {
|
||||
|
||||
IPMIBmc *bmc;
|
||||
Notifier powerdown_notifier;
|
||||
} PnvMachineState;
|
||||
|
||||
static inline bool pnv_chip_is_power9(const PnvChip *chip)
|
||||
{
|
||||
return PNV_CHIP_GET_CLASS(chip)->chip_type == PNV_CHIP_POWER9;
|
||||
}
|
||||
PnvPnor *pnor;
|
||||
};
|
||||
|
||||
static inline bool pnv_is_power9(PnvMachineState *pnv)
|
||||
{
|
||||
return pnv_chip_is_power9(pnv->chips[0]);
|
||||
}
|
||||
PnvChip *pnv_get_chip(uint32_t chip_id);
|
||||
|
||||
#define PNV_FDT_ADDR 0x01000000
|
||||
#define PNV_TIMEBASE_FREQ 512000000ULL
|
||||
@ -195,6 +226,7 @@ static inline bool pnv_is_power9(PnvMachineState *pnv)
|
||||
*/
|
||||
void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt);
|
||||
void pnv_bmc_powerdown(IPMIBmc *bmc);
|
||||
IPMIBmc *pnv_bmc_create(void);
|
||||
|
||||
/*
|
||||
* POWER8 MMIO base addresses
|
||||
@ -203,12 +235,12 @@ void pnv_bmc_powerdown(IPMIBmc *bmc);
|
||||
#define PNV_XSCOM_BASE(chip) \
|
||||
(0x0003fc0000000000ull + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
|
||||
|
||||
#define PNV_OCC_COMMON_AREA_SIZE 0x0000000000700000ull
|
||||
#define PNV_OCC_COMMON_AREA(chip) \
|
||||
(0x7fff800000ull + ((uint64_t)PNV_CHIP_INDEX(chip) * \
|
||||
PNV_OCC_COMMON_AREA_SIZE))
|
||||
#define PNV_OCC_COMMON_AREA_SIZE 0x0000000000800000ull
|
||||
#define PNV_OCC_COMMON_AREA_BASE 0x7fff800000ull
|
||||
#define PNV_OCC_SENSOR_BASE(chip) (PNV_OCC_COMMON_AREA_BASE + \
|
||||
PNV_OCC_SENSOR_DATA_BLOCK_BASE(PNV_CHIP_INDEX(chip)))
|
||||
|
||||
#define PNV_HOMER_SIZE 0x0000000000300000ull
|
||||
#define PNV_HOMER_SIZE 0x0000000000400000ull
|
||||
#define PNV_HOMER_BASE(chip) \
|
||||
(0x7ffd800000ull + ((uint64_t)PNV_CHIP_INDEX(chip)) * PNV_HOMER_SIZE)
|
||||
|
||||
@ -271,12 +303,31 @@ void pnv_bmc_powerdown(IPMIBmc *bmc);
|
||||
#define PNV9_XSCOM_SIZE 0x0000000400000000ull
|
||||
#define PNV9_XSCOM_BASE(chip) PNV9_CHIP_BASE(chip, 0x00603fc00000000ull)
|
||||
|
||||
#define PNV9_OCC_COMMON_AREA_SIZE 0x0000000000700000ull
|
||||
#define PNV9_OCC_COMMON_AREA(chip) \
|
||||
(0x203fff800000ull + ((uint64_t)PNV_CHIP_INDEX(chip) * \
|
||||
PNV9_OCC_COMMON_AREA_SIZE))
|
||||
#define PNV9_OCC_COMMON_AREA_SIZE 0x0000000000800000ull
|
||||
#define PNV9_OCC_COMMON_AREA_BASE 0x203fff800000ull
|
||||
#define PNV9_OCC_SENSOR_BASE(chip) (PNV9_OCC_COMMON_AREA_BASE + \
|
||||
PNV_OCC_SENSOR_DATA_BLOCK_BASE(PNV_CHIP_INDEX(chip)))
|
||||
|
||||
#define PNV9_HOMER_SIZE 0x0000000000300000ull
|
||||
#define PNV9_HOMER_SIZE 0x0000000000400000ull
|
||||
#define PNV9_HOMER_BASE(chip) \
|
||||
(0x203ffd800000ull + ((uint64_t)PNV_CHIP_INDEX(chip)) * PNV9_HOMER_SIZE)
|
||||
|
||||
/*
|
||||
* POWER10 MMIO base addresses - 16TB stride per chip
|
||||
*/
|
||||
#define PNV10_CHIP_BASE(chip, base) \
|
||||
((base) + ((uint64_t) (chip)->chip_id << 44))
|
||||
|
||||
#define PNV10_XSCOM_SIZE 0x0000000400000000ull
|
||||
#define PNV10_XSCOM_BASE(chip) PNV10_CHIP_BASE(chip, 0x00603fc00000000ull)
|
||||
|
||||
#define PNV10_LPCM_SIZE 0x0000000100000000ull
|
||||
#define PNV10_LPCM_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030000000000ull)
|
||||
|
||||
#define PNV10_PSIHB_ESB_SIZE 0x0000000000100000ull
|
||||
#define PNV10_PSIHB_ESB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030202000000ull)
|
||||
|
||||
#define PNV10_PSIHB_SIZE 0x0000000000100000ull
|
||||
#define PNV10_PSIHB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030203000000ull)
|
||||
|
||||
#endif /* PPC_PNV_H */
|
||||
|
@ -33,6 +33,7 @@ typedef struct PnvHomer {
|
||||
DeviceState parent;
|
||||
|
||||
struct PnvChip *chip;
|
||||
MemoryRegion pba_regs;
|
||||
MemoryRegion regs;
|
||||
} PnvHomer;
|
||||
|
||||
@ -44,6 +45,8 @@ typedef struct PnvHomer {
|
||||
typedef struct PnvHomerClass {
|
||||
DeviceClass parent_class;
|
||||
|
||||
int pba_size;
|
||||
const MemoryRegionOps *pba_ops;
|
||||
int homer_size;
|
||||
const MemoryRegionOps *homer_ops;
|
||||
|
||||
|
@ -31,6 +31,9 @@
|
||||
#define TYPE_PNV9_LPC TYPE_PNV_LPC "-POWER9"
|
||||
#define PNV9_LPC(obj) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV9_LPC)
|
||||
|
||||
#define TYPE_PNV10_LPC TYPE_PNV_LPC "-POWER10"
|
||||
#define PNV10_LPC(obj) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV10_LPC)
|
||||
|
||||
typedef struct PnvLpcController {
|
||||
DeviceState parent;
|
||||
|
||||
@ -97,6 +100,7 @@ typedef struct PnvLpcClass {
|
||||
struct PnvChip;
|
||||
|
||||
ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp);
|
||||
int pnv_dt_lpc(struct PnvChip *chip, void *fdt, int root_offset);
|
||||
int pnv_dt_lpc(struct PnvChip *chip, void *fdt, int root_offset,
|
||||
uint64_t lpcm_addr, uint64_t lpcm_size);
|
||||
|
||||
#endif /* PPC_PNV_LPC_H */
|
||||
|
@ -29,6 +29,9 @@
|
||||
#define TYPE_PNV9_OCC TYPE_PNV_OCC "-POWER9"
|
||||
#define PNV9_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV9_OCC)
|
||||
|
||||
#define PNV_OCC_SENSOR_DATA_BLOCK_OFFSET 0x00580000
|
||||
#define PNV_OCC_SENSOR_DATA_BLOCK_SIZE 0x00025800
|
||||
|
||||
typedef struct PnvOCC {
|
||||
DeviceState xd;
|
||||
|
||||
@ -50,10 +53,11 @@ typedef struct PnvOCCClass {
|
||||
DeviceClass parent_class;
|
||||
|
||||
int xscom_size;
|
||||
int sram_size;
|
||||
const MemoryRegionOps *xscom_ops;
|
||||
const MemoryRegionOps *sram_ops;
|
||||
int psi_irq;
|
||||
} PnvOCCClass;
|
||||
|
||||
#define PNV_OCC_SENSOR_DATA_BLOCK_BASE(i) \
|
||||
(PNV_OCC_SENSOR_DATA_BLOCK_OFFSET + (i) * PNV_OCC_SENSOR_DATA_BLOCK_SIZE)
|
||||
|
||||
#endif /* PPC_PNV_OCC_H */
|
||||
|
30
include/hw/ppc/pnv_pnor.h
Normal file
30
include/hw/ppc/pnv_pnor.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* QEMU PowerNV PNOR simple model
|
||||
*
|
||||
* Copyright (c) 2019, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef _PPC_PNV_PNOR_H
|
||||
#define _PPC_PNV_PNOR_H
|
||||
|
||||
/*
|
||||
* PNOR offset on the LPC FW address space
|
||||
*/
|
||||
#define PNOR_SPI_OFFSET 0x0c000000UL
|
||||
|
||||
#define TYPE_PNV_PNOR "pnv-pnor"
|
||||
#define PNV_PNOR(obj) OBJECT_CHECK(PnvPnor, (obj), TYPE_PNV_PNOR)
|
||||
|
||||
typedef struct PnvPnor {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
BlockBackend *blk;
|
||||
|
||||
uint8_t *storage;
|
||||
uint32_t size;
|
||||
MemoryRegion mmio;
|
||||
} PnvPnor;
|
||||
|
||||
#endif /* _PPC_PNV_PNOR_H */
|
@ -69,6 +69,8 @@ typedef struct Pnv9Psi {
|
||||
XiveSource source;
|
||||
} Pnv9Psi;
|
||||
|
||||
#define TYPE_PNV10_PSI TYPE_PNV_PSI "-POWER10"
|
||||
|
||||
#define PNV_PSI_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PnvPsiClass, (klass), TYPE_PNV_PSI)
|
||||
#define PNV_PSI_GET_CLASS(obj) \
|
||||
@ -77,10 +79,11 @@ typedef struct Pnv9Psi {
|
||||
typedef struct PnvPsiClass {
|
||||
SysBusDeviceClass parent_class;
|
||||
|
||||
int chip_type;
|
||||
uint32_t xscom_pcba;
|
||||
uint32_t xscom_size;
|
||||
uint64_t bar_mask;
|
||||
const char *compat;
|
||||
int compat_size;
|
||||
|
||||
void (*irq_set)(PnvPsi *psi, int, bool state);
|
||||
} PnvPsiClass;
|
||||
|
@ -72,9 +72,6 @@ typedef struct PnvXive {
|
||||
/* Interrupt controller registers */
|
||||
uint64_t regs[0x300];
|
||||
|
||||
/* Can be configured by FW */
|
||||
uint32_t tctx_chipid;
|
||||
|
||||
/*
|
||||
* Virtual Structure Descriptor tables : EAT, SBE, ENDT, NVTT, IRQ
|
||||
* These are in a SRAM protected by ECC.
|
||||
|
@ -22,13 +22,11 @@
|
||||
|
||||
#include "qom/object.h"
|
||||
|
||||
typedef struct PnvXScomInterface {
|
||||
Object parent;
|
||||
} PnvXScomInterface;
|
||||
typedef struct PnvXScomInterface PnvXScomInterface;
|
||||
|
||||
#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface"
|
||||
#define PNV_XSCOM_INTERFACE(obj) \
|
||||
OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE)
|
||||
INTERFACE_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE)
|
||||
#define PNV_XSCOM_INTERFACE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \
|
||||
TYPE_PNV_XSCOM_INTERFACE)
|
||||
@ -70,6 +68,12 @@ typedef struct PnvXScomInterfaceClass {
|
||||
#define PNV_XSCOM_OCC_BASE 0x0066000
|
||||
#define PNV_XSCOM_OCC_SIZE 0x6000
|
||||
|
||||
#define PNV_XSCOM_PBA_BASE 0x2013f00
|
||||
#define PNV_XSCOM_PBA_SIZE 0x40
|
||||
|
||||
/*
|
||||
* Layout of the XSCOM PCB addresses (POWER 9)
|
||||
*/
|
||||
#define PNV9_XSCOM_EC_BASE(core) \
|
||||
((uint64_t)(((core) & 0x1F) + 0x20) << 24)
|
||||
#define PNV9_XSCOM_EC_SIZE 0x100000
|
||||
@ -81,22 +85,46 @@ typedef struct PnvXScomInterfaceClass {
|
||||
#define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE
|
||||
#define PNV9_XSCOM_OCC_SIZE 0x8000
|
||||
|
||||
#define PNV9_XSCOM_PBA_BASE 0x5012b00
|
||||
#define PNV9_XSCOM_PBA_SIZE 0x40
|
||||
|
||||
#define PNV9_XSCOM_PSIHB_BASE 0x5012900
|
||||
#define PNV9_XSCOM_PSIHB_SIZE 0x100
|
||||
|
||||
#define PNV9_XSCOM_XIVE_BASE 0x5013000
|
||||
#define PNV9_XSCOM_XIVE_SIZE 0x300
|
||||
|
||||
extern void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp);
|
||||
extern int pnv_dt_xscom(PnvChip *chip, void *fdt, int offset);
|
||||
/*
|
||||
* Layout of the XSCOM PCB addresses (POWER 10)
|
||||
*/
|
||||
#define PNV10_XSCOM_EQ_CHIPLET(core) (0x20 + ((core) >> 2))
|
||||
#define PNV10_XSCOM_EQ(chiplet) ((chiplet) << 24)
|
||||
#define PNV10_XSCOM_EC(proc) \
|
||||
((0x2 << 16) | ((1 << (3 - (proc))) << 12))
|
||||
|
||||
extern void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset,
|
||||
MemoryRegion *mr);
|
||||
extern void pnv_xscom_region_init(MemoryRegion *mr,
|
||||
struct Object *owner,
|
||||
const MemoryRegionOps *ops,
|
||||
void *opaque,
|
||||
const char *name,
|
||||
uint64_t size);
|
||||
#define PNV10_XSCOM_EQ_BASE(core) \
|
||||
((uint64_t) PNV10_XSCOM_EQ(PNV10_XSCOM_EQ_CHIPLET(core)))
|
||||
#define PNV10_XSCOM_EQ_SIZE 0x100000
|
||||
|
||||
#define PNV10_XSCOM_EC_BASE(core) \
|
||||
((uint64_t) PNV10_XSCOM_EQ_BASE(core) | PNV10_XSCOM_EC(core & 0x3))
|
||||
#define PNV10_XSCOM_EC_SIZE 0x100000
|
||||
|
||||
#define PNV10_XSCOM_PSIHB_BASE 0x3011D00
|
||||
#define PNV10_XSCOM_PSIHB_SIZE 0x100
|
||||
|
||||
void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp);
|
||||
int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset,
|
||||
uint64_t xscom_base, uint64_t xscom_size,
|
||||
const char *compat, int compat_size);
|
||||
|
||||
void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset,
|
||||
MemoryRegion *mr);
|
||||
void pnv_xscom_region_init(MemoryRegion *mr,
|
||||
struct Object *owner,
|
||||
const MemoryRegionOps *ops,
|
||||
void *opaque,
|
||||
const char *name,
|
||||
uint64_t size);
|
||||
|
||||
#endif /* PPC_PNV_XSCOM_H */
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
|
||||
PowerPCCPU *ppc_get_vcpu_by_pir(int pir);
|
||||
int ppc_cpu_pir(PowerPCCPU *cpu);
|
||||
|
||||
/* PowerPC hardware exceptions management helpers */
|
||||
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
|
||||
@ -23,6 +24,7 @@ struct ppc_tb_t {
|
||||
/* Time base management */
|
||||
int64_t tb_offset; /* Compensation */
|
||||
int64_t atb_offset; /* Compensation */
|
||||
int64_t vtb_offset;
|
||||
uint32_t tb_freq; /* TB frequency */
|
||||
/* Decrementer management */
|
||||
uint64_t decr_next; /* Tick for next decr interrupt */
|
||||
@ -31,8 +33,7 @@ struct ppc_tb_t {
|
||||
/* Hypervisor decrementer management */
|
||||
uint64_t hdecr_next; /* Tick for next hdecr interrupt */
|
||||
QEMUTimer *hdecr_timer;
|
||||
uint64_t purr_load;
|
||||
uint64_t purr_start;
|
||||
int64_t purr_offset;
|
||||
void *opaque;
|
||||
uint32_t flags;
|
||||
};
|
||||
@ -76,6 +77,7 @@ static inline void ppc970_irq_init(PowerPCCPU *cpu) {}
|
||||
static inline void ppcPOWER7_irq_init(PowerPCCPU *cpu) {}
|
||||
static inline void ppcPOWER9_irq_init(PowerPCCPU *cpu) {}
|
||||
static inline void ppce500_irq_init(PowerPCCPU *cpu) {}
|
||||
static inline void ppc_irq_reset(PowerPCCPU *cpu) {}
|
||||
#else
|
||||
void ppc40x_irq_init(PowerPCCPU *cpu);
|
||||
void ppce500_irq_init(PowerPCCPU *cpu);
|
||||
@ -83,6 +85,7 @@ void ppc6xx_irq_init(PowerPCCPU *cpu);
|
||||
void ppc970_irq_init(PowerPCCPU *cpu);
|
||||
void ppcPOWER7_irq_init(PowerPCCPU *cpu);
|
||||
void ppcPOWER9_irq_init(PowerPCCPU *cpu);
|
||||
void ppc_irq_reset(PowerPCCPU *cpu);
|
||||
#endif
|
||||
|
||||
/* PPC machines for OpenBIOS */
|
||||
|
@ -766,11 +766,9 @@ struct SpaprEventLogEntry {
|
||||
QTAILQ_ENTRY(SpaprEventLogEntry) next;
|
||||
};
|
||||
|
||||
void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space);
|
||||
void spapr_events_init(SpaprMachineState *sm);
|
||||
void spapr_dt_events(SpaprMachineState *sm, void *fdt);
|
||||
int spapr_h_cas_compose_response(SpaprMachineState *sm,
|
||||
target_ulong addr, target_ulong size,
|
||||
SpaprOptionVector *ov5_updates);
|
||||
void close_htab_fd(SpaprMachineState *spapr);
|
||||
void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr);
|
||||
void spapr_free_hpt(SpaprMachineState *spapr);
|
||||
|
@ -43,7 +43,8 @@ typedef struct SpaprInterruptController SpaprInterruptController;
|
||||
typedef struct SpaprInterruptControllerClass {
|
||||
InterfaceClass parent;
|
||||
|
||||
int (*activate)(SpaprInterruptController *intc, Error **errp);
|
||||
int (*activate)(SpaprInterruptController *intc, uint32_t nr_servers,
|
||||
Error **errp);
|
||||
void (*deactivate)(SpaprInterruptController *intc);
|
||||
|
||||
/*
|
||||
@ -98,8 +99,13 @@ qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq);
|
||||
int spapr_irq_post_load(SpaprMachineState *spapr, int version_id);
|
||||
void spapr_irq_reset(SpaprMachineState *spapr, Error **errp);
|
||||
int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp);
|
||||
int spapr_irq_init_kvm(int (*fn)(SpaprInterruptController *, Error **),
|
||||
|
||||
typedef int (*SpaprInterruptControllerInitKvm)(SpaprInterruptController *,
|
||||
uint32_t, Error **);
|
||||
|
||||
int spapr_irq_init_kvm(SpaprInterruptControllerInitKvm fn,
|
||||
SpaprInterruptController *intc,
|
||||
uint32_t nr_servers,
|
||||
Error **errp);
|
||||
|
||||
/*
|
||||
|
@ -66,9 +66,7 @@ SpaprOptionVector *spapr_ovec_clone(SpaprOptionVector *ov_orig);
|
||||
void spapr_ovec_intersect(SpaprOptionVector *ov,
|
||||
SpaprOptionVector *ov1,
|
||||
SpaprOptionVector *ov2);
|
||||
bool spapr_ovec_diff(SpaprOptionVector *ov,
|
||||
SpaprOptionVector *ov_old,
|
||||
SpaprOptionVector *ov_new);
|
||||
bool spapr_ovec_subset(SpaprOptionVector *ov1, SpaprOptionVector *ov2);
|
||||
void spapr_ovec_cleanup(SpaprOptionVector *ov);
|
||||
void spapr_ovec_set(SpaprOptionVector *ov, long bitnr);
|
||||
void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr);
|
||||
|
@ -80,10 +80,10 @@ struct SpaprVioBus {
|
||||
uint32_t next_reg;
|
||||
};
|
||||
|
||||
extern SpaprVioBus *spapr_vio_bus_init(void);
|
||||
extern SpaprVioDevice *spapr_vio_find_by_reg(SpaprVioBus *bus, uint32_t reg);
|
||||
SpaprVioBus *spapr_vio_bus_init(void);
|
||||
SpaprVioDevice *spapr_vio_find_by_reg(SpaprVioBus *bus, uint32_t reg);
|
||||
void spapr_dt_vdevice(SpaprVioBus *bus, void *fdt);
|
||||
extern gchar *spapr_vio_stdout_path(SpaprVioBus *bus);
|
||||
gchar *spapr_vio_stdout_path(SpaprVioBus *bus);
|
||||
|
||||
static inline void spapr_vio_irq_pulse(SpaprVioDevice *dev)
|
||||
{
|
||||
|
@ -66,7 +66,8 @@ int spapr_xive_end_to_target(uint8_t end_blk, uint32_t end_idx,
|
||||
/*
|
||||
* KVM XIVE device helpers
|
||||
*/
|
||||
int kvmppc_xive_connect(SpaprInterruptController *intc, Error **errp);
|
||||
int kvmppc_xive_connect(SpaprInterruptController *intc, uint32_t nr_servers,
|
||||
Error **errp);
|
||||
void kvmppc_xive_disconnect(SpaprInterruptController *intc);
|
||||
void kvmppc_xive_reset(SpaprXive *xive, Error **errp);
|
||||
void kvmppc_xive_set_source_config(SpaprXive *xive, uint32_t lisn, XiveEAS *eas,
|
||||
|
@ -32,7 +32,8 @@
|
||||
#define TYPE_ICS_SPAPR "ics-spapr"
|
||||
#define ICS_SPAPR(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_SPAPR)
|
||||
|
||||
int xics_kvm_connect(SpaprInterruptController *intc, Error **errp);
|
||||
int xics_kvm_connect(SpaprInterruptController *intc, uint32_t nr_servers,
|
||||
Error **errp);
|
||||
void xics_kvm_disconnect(SpaprInterruptController *intc);
|
||||
bool xics_kvm_has_broken_disconnect(SpaprMachineState *spapr);
|
||||
|
||||
|
@ -351,7 +351,7 @@ typedef struct XiveRouterClass {
|
||||
XiveNVT *nvt);
|
||||
int (*write_nvt)(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
XiveNVT *nvt, uint8_t word_number);
|
||||
XiveTCTX *(*get_tctx)(XiveRouter *xrtr, CPUState *cs);
|
||||
uint8_t (*get_block_id)(XiveRouter *xrtr);
|
||||
} XiveRouterClass;
|
||||
|
||||
int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx,
|
||||
@ -364,9 +364,62 @@ int xive_router_get_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
XiveNVT *nvt);
|
||||
int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
XiveNVT *nvt, uint8_t word_number);
|
||||
XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs);
|
||||
void xive_router_notify(XiveNotifier *xn, uint32_t lisn);
|
||||
|
||||
/*
|
||||
* XIVE Presenter
|
||||
*/
|
||||
|
||||
typedef struct XiveTCTXMatch {
|
||||
XiveTCTX *tctx;
|
||||
uint8_t ring;
|
||||
} XiveTCTXMatch;
|
||||
|
||||
typedef struct XivePresenter XivePresenter;
|
||||
|
||||
#define TYPE_XIVE_PRESENTER "xive-presenter"
|
||||
#define XIVE_PRESENTER(obj) \
|
||||
INTERFACE_CHECK(XivePresenter, (obj), TYPE_XIVE_PRESENTER)
|
||||
#define XIVE_PRESENTER_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(XivePresenterClass, (klass), TYPE_XIVE_PRESENTER)
|
||||
#define XIVE_PRESENTER_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(XivePresenterClass, (obj), TYPE_XIVE_PRESENTER)
|
||||
|
||||
typedef struct XivePresenterClass {
|
||||
InterfaceClass parent;
|
||||
int (*match_nvt)(XivePresenter *xptr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match);
|
||||
} XivePresenterClass;
|
||||
|
||||
int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint32_t logic_serv);
|
||||
|
||||
/*
|
||||
* XIVE Fabric (Interface between Interrupt Controller and Machine)
|
||||
*/
|
||||
|
||||
typedef struct XiveFabric XiveFabric;
|
||||
|
||||
#define TYPE_XIVE_FABRIC "xive-fabric"
|
||||
#define XIVE_FABRIC(obj) \
|
||||
INTERFACE_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
|
||||
#define XIVE_FABRIC_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
|
||||
#define XIVE_FABRIC_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
|
||||
|
||||
typedef struct XiveFabricClass {
|
||||
InterfaceClass parent;
|
||||
int (*match_nvt)(XiveFabric *xfb, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match);
|
||||
} XiveFabricClass;
|
||||
|
||||
/*
|
||||
* XIVE END ESBs
|
||||
*/
|
||||
@ -379,7 +432,6 @@ typedef struct XiveENDSource {
|
||||
DeviceState parent;
|
||||
|
||||
uint32_t nr_ends;
|
||||
uint8_t block_id;
|
||||
|
||||
/* ESB memory region */
|
||||
uint32_t esb_shift;
|
||||
@ -408,20 +460,16 @@ typedef struct XiveENDSource {
|
||||
#define XIVE_TM_OS_PAGE 0x2
|
||||
#define XIVE_TM_USER_PAGE 0x3
|
||||
|
||||
extern const MemoryRegionOps xive_tm_ops;
|
||||
void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value,
|
||||
unsigned size);
|
||||
uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size);
|
||||
void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
|
||||
uint64_t value, unsigned size);
|
||||
uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
|
||||
unsigned size);
|
||||
|
||||
void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon);
|
||||
Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp);
|
||||
void xive_tctx_reset(XiveTCTX *tctx);
|
||||
void xive_tctx_destroy(XiveTCTX *tctx);
|
||||
|
||||
static inline uint32_t xive_nvt_cam_line(uint8_t nvt_blk, uint32_t nvt_idx)
|
||||
{
|
||||
return (nvt_blk << 19) | nvt_idx;
|
||||
}
|
||||
void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb);
|
||||
|
||||
/*
|
||||
* KVM XIVE device helpers
|
||||
|
@ -252,9 +252,12 @@ typedef struct XiveNVT {
|
||||
uint32_t w0;
|
||||
#define NVT_W0_VALID PPC_BIT32(0)
|
||||
uint32_t w1;
|
||||
#define NVT_W1_EQ_BLOCK PPC_BITMASK32(0, 3)
|
||||
#define NVT_W1_EQ_INDEX PPC_BITMASK32(4, 31)
|
||||
uint32_t w2;
|
||||
uint32_t w3;
|
||||
uint32_t w4;
|
||||
#define NVT_W4_IPB PPC_BITMASK32(16, 23)
|
||||
uint32_t w5;
|
||||
uint32_t w6;
|
||||
uint32_t w7;
|
||||
@ -271,4 +274,26 @@ typedef struct XiveNVT {
|
||||
|
||||
#define xive_nvt_is_valid(nvt) (be32_to_cpu((nvt)->w0) & NVT_W0_VALID)
|
||||
|
||||
/*
|
||||
* The VP number space in a block is defined by the END_W6_NVT_INDEX
|
||||
* field of the XIVE END
|
||||
*/
|
||||
#define XIVE_NVT_SHIFT 19
|
||||
#define XIVE_NVT_COUNT (1 << XIVE_NVT_SHIFT)
|
||||
|
||||
static inline uint32_t xive_nvt_cam_line(uint8_t nvt_blk, uint32_t nvt_idx)
|
||||
{
|
||||
return (nvt_blk << XIVE_NVT_SHIFT) | nvt_idx;
|
||||
}
|
||||
|
||||
static inline uint32_t xive_nvt_idx(uint32_t cam_line)
|
||||
{
|
||||
return cam_line & ((1 << XIVE_NVT_SHIFT) - 1);
|
||||
}
|
||||
|
||||
static inline uint32_t xive_nvt_blk(uint32_t cam_line)
|
||||
{
|
||||
return (cam_line >> XIVE_NVT_SHIFT) & 0xf;
|
||||
}
|
||||
|
||||
#endif /* PPC_XIVE_REGS_H */
|
||||
|
@ -1507,6 +1507,11 @@ enum ethtool_link_mode_bit_indices {
|
||||
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66,
|
||||
ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67,
|
||||
ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68,
|
||||
ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69,
|
||||
ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70,
|
||||
ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71,
|
||||
ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72,
|
||||
ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73,
|
||||
|
||||
/* must be last entry */
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS
|
||||
@ -1618,6 +1623,7 @@ enum ethtool_link_mode_bit_indices {
|
||||
#define SPEED_56000 56000
|
||||
#define SPEED_100000 100000
|
||||
#define SPEED_200000 200000
|
||||
#define SPEED_400000 400000
|
||||
|
||||
#define SPEED_UNKNOWN -1
|
||||
|
||||
|
@ -167,7 +167,7 @@ static inline void vring_init(struct vring *vr, unsigned int num, void *p,
|
||||
{
|
||||
vr->num = num;
|
||||
vr->desc = p;
|
||||
vr->avail = p + num*sizeof(struct vring_desc);
|
||||
vr->avail = (struct vring_avail *)((char *)p + num * sizeof(struct vring_desc));
|
||||
vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16)
|
||||
+ align-1) & ~(align - 1));
|
||||
}
|
||||
|
@ -131,8 +131,9 @@ struct kvm_vcpu_events {
|
||||
struct {
|
||||
__u8 serror_pending;
|
||||
__u8 serror_has_esr;
|
||||
__u8 ext_dabt_pending;
|
||||
/* Align it to 8 bytes */
|
||||
__u8 pad[6];
|
||||
__u8 pad[5];
|
||||
__u64 serror_esr;
|
||||
} exception;
|
||||
__u32 reserved[12];
|
||||
|
@ -164,8 +164,9 @@ struct kvm_vcpu_events {
|
||||
struct {
|
||||
__u8 serror_pending;
|
||||
__u8 serror_has_esr;
|
||||
__u8 ext_dabt_pending;
|
||||
/* Align it to 8 bytes */
|
||||
__u8 pad[6];
|
||||
__u8 pad[5];
|
||||
__u64 serror_esr;
|
||||
} exception;
|
||||
__u32 reserved[12];
|
||||
@ -323,6 +324,8 @@ struct kvm_vcpu_events {
|
||||
#define KVM_ARM_VCPU_TIMER_CTRL 1
|
||||
#define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0
|
||||
#define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1
|
||||
#define KVM_ARM_VCPU_PVTIME_CTRL 2
|
||||
#define KVM_ARM_VCPU_PVTIME_IPA 0
|
||||
|
||||
/* KVM_IRQ_LINE irq field index values */
|
||||
#define KVM_ARM_IRQ_VCPU2_SHIFT 28
|
||||
|
@ -364,6 +364,7 @@
|
||||
#define __NR_fsmount (__NR_Linux + 432)
|
||||
#define __NR_fspick (__NR_Linux + 433)
|
||||
#define __NR_pidfd_open (__NR_Linux + 434)
|
||||
#define __NR_clone3 (__NR_Linux + 435)
|
||||
|
||||
|
||||
#endif /* _ASM_MIPS_UNISTD_N32_H */
|
||||
|
@ -340,6 +340,7 @@
|
||||
#define __NR_fsmount (__NR_Linux + 432)
|
||||
#define __NR_fspick (__NR_Linux + 433)
|
||||
#define __NR_pidfd_open (__NR_Linux + 434)
|
||||
#define __NR_clone3 (__NR_Linux + 435)
|
||||
|
||||
|
||||
#endif /* _ASM_MIPS_UNISTD_N64_H */
|
||||
|
@ -410,6 +410,7 @@
|
||||
#define __NR_fsmount (__NR_Linux + 432)
|
||||
#define __NR_fspick (__NR_Linux + 433)
|
||||
#define __NR_pidfd_open (__NR_Linux + 434)
|
||||
#define __NR_clone3 (__NR_Linux + 435)
|
||||
|
||||
|
||||
#endif /* _ASM_MIPS_UNISTD_O32_H */
|
||||
|
@ -667,6 +667,8 @@ struct kvm_ppc_cpu_char {
|
||||
|
||||
/* PPC64 eXternal Interrupt Controller Specification */
|
||||
#define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */
|
||||
#define KVM_DEV_XICS_GRP_CTRL 2
|
||||
#define KVM_DEV_XICS_NR_SERVERS 1
|
||||
|
||||
/* Layout of 64-bit source attribute values */
|
||||
#define KVM_XICS_DESTINATION_SHIFT 0
|
||||
@ -683,6 +685,7 @@ struct kvm_ppc_cpu_char {
|
||||
#define KVM_DEV_XIVE_GRP_CTRL 1
|
||||
#define KVM_DEV_XIVE_RESET 1
|
||||
#define KVM_DEV_XIVE_EQ_SYNC 2
|
||||
#define KVM_DEV_XIVE_NR_SERVERS 3
|
||||
#define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */
|
||||
#define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3 /* 64-bit source identifier */
|
||||
#define KVM_DEV_XIVE_GRP_EQ_CONFIG 4 /* 64-bit EQ identifier */
|
||||
|
@ -235,6 +235,7 @@ struct kvm_hyperv_exit {
|
||||
#define KVM_EXIT_S390_STSI 25
|
||||
#define KVM_EXIT_IOAPIC_EOI 26
|
||||
#define KVM_EXIT_HYPERV 27
|
||||
#define KVM_EXIT_ARM_NISV 28
|
||||
|
||||
/* For KVM_EXIT_INTERNAL_ERROR */
|
||||
/* Emulate instruction failed. */
|
||||
@ -394,6 +395,11 @@ struct kvm_run {
|
||||
} eoi;
|
||||
/* KVM_EXIT_HYPERV */
|
||||
struct kvm_hyperv_exit hyperv;
|
||||
/* KVM_EXIT_ARM_NISV */
|
||||
struct {
|
||||
__u64 esr_iss;
|
||||
__u64 fault_ipa;
|
||||
} arm_nisv;
|
||||
/* Fix the size of the union. */
|
||||
char padding[256];
|
||||
};
|
||||
@ -1000,6 +1006,9 @@ struct kvm_ppc_resize_hpt {
|
||||
#define KVM_CAP_PMU_EVENT_FILTER 173
|
||||
#define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
|
||||
#define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
|
||||
#define KVM_CAP_PPC_GUEST_DEBUG_SSTEP 176
|
||||
#define KVM_CAP_ARM_NISV_TO_USER 177
|
||||
#define KVM_CAP_ARM_INJECT_EXT_DABT 178
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
@ -1227,6 +1236,8 @@ enum kvm_device_type {
|
||||
#define KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_ARM_VGIC_ITS
|
||||
KVM_DEV_TYPE_XIVE,
|
||||
#define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE
|
||||
KVM_DEV_TYPE_ARM_PV_TIME,
|
||||
#define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME
|
||||
KVM_DEV_TYPE_MAX,
|
||||
};
|
||||
|
||||
|
@ -58,6 +58,9 @@ typedef enum {
|
||||
SEV_RET_HWSEV_RET_PLATFORM,
|
||||
SEV_RET_HWSEV_RET_UNSAFE,
|
||||
SEV_RET_UNSUPPORTED,
|
||||
SEV_RET_INVALID_PARAM,
|
||||
SEV_RET_RESOURCE_LIMIT,
|
||||
SEV_RET_SECURE_DATA_INVALID,
|
||||
SEV_RET_MAX,
|
||||
} sev_ret_code;
|
||||
|
||||
|
@ -47,6 +47,11 @@ uint32_t cpu_ppc_load_atbu(CPUPPCState *env)
|
||||
return cpu_ppc_get_tb(env) >> 32;
|
||||
}
|
||||
|
||||
uint64_t cpu_ppc_load_vtb(CPUPPCState *env)
|
||||
{
|
||||
return cpu_ppc_get_tb(env);
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env)
|
||||
__attribute__ (( alias ("cpu_ppc_load_tbu") ));
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||
implementation for certain IBM POWER hardware. The sources are at
|
||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
||||
built from git tag qemu-slof-20191209.
|
||||
built from git tag qemu-slof-20191217.
|
||||
|
||||
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
|
||||
legacy x86 software to communicate with an attached serial console as
|
||||
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit 8ebf2f55e1ba1492b942ba4b682160e644fc0f98
|
||||
Subproject commit 9546892a80d5a4c73deea6719de46372f007f4a6
|
@ -51,36 +51,38 @@ static const CompatInfo compat_table[] = {
|
||||
{ /* POWER6, ISA2.05 */
|
||||
.name = "power6",
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_05,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 |
|
||||
PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS,
|
||||
.pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
|
||||
PCR_COMPAT_2_06 | PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS,
|
||||
.pcr_level = PCR_COMPAT_2_05,
|
||||
.max_vthreads = 2,
|
||||
},
|
||||
{ /* POWER7, ISA2.06 */
|
||||
.name = "power7",
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_06,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
|
||||
PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr_level = PCR_COMPAT_2_06,
|
||||
.max_vthreads = 4,
|
||||
},
|
||||
{
|
||||
.name = "power7+",
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_06_PLUS,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
|
||||
PCR_COMPAT_2_06 | PCR_TM_DIS,
|
||||
.pcr_level = PCR_COMPAT_2_06,
|
||||
.max_vthreads = 4,
|
||||
},
|
||||
{ /* POWER8, ISA2.07 */
|
||||
.name = "power8",
|
||||
.pvr = CPU_POWERPC_LOGICAL_2_07,
|
||||
.pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07,
|
||||
.pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07,
|
||||
.pcr_level = PCR_COMPAT_2_07,
|
||||
.max_vthreads = 8,
|
||||
},
|
||||
{ /* POWER9, ISA3.00 */
|
||||
.name = "power9",
|
||||
.pvr = CPU_POWERPC_LOGICAL_3_00,
|
||||
.pcr = PCR_COMPAT_3_00,
|
||||
.pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00,
|
||||
.pcr_level = PCR_COMPAT_3_00,
|
||||
/*
|
||||
* POWER9 hardware only supports 4 threads / core, but this
|
||||
@ -91,6 +93,13 @@ static const CompatInfo compat_table[] = {
|
||||
*/
|
||||
.max_vthreads = 8,
|
||||
},
|
||||
{ /* POWER10, ISA3.10 */
|
||||
.name = "power10",
|
||||
.pvr = CPU_POWERPC_LOGICAL_3_10,
|
||||
.pcr = PCR_COMPAT_3_10,
|
||||
.pcr_level = PCR_COMPAT_3_10,
|
||||
.max_vthreads = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static const CompatInfo *compat_by_pvr(uint32_t pvr)
|
||||
|
@ -774,6 +774,8 @@
|
||||
"POWER9 v1.0")
|
||||
POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9,
|
||||
"POWER9 v2.0")
|
||||
POWERPC_DEF("power10_v1.0", CPU_POWERPC_POWER10_DD1, POWER10,
|
||||
"POWER10 v1.0")
|
||||
#endif /* defined (TARGET_PPC64) */
|
||||
|
||||
/***************************************************************************/
|
||||
@ -950,6 +952,7 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
|
||||
{ "power8", "power8_v2.0" },
|
||||
{ "power8nvl", "power8nvl_v1.0" },
|
||||
{ "power9", "power9_v2.0" },
|
||||
{ "power10", "power10_v1.0" },
|
||||
#endif
|
||||
|
||||
/* Generic PowerPCs */
|
||||
|
@ -373,6 +373,8 @@ enum {
|
||||
CPU_POWERPC_POWER9_BASE = 0x004E0000,
|
||||
CPU_POWERPC_POWER9_DD1 = 0x004E0100,
|
||||
CPU_POWERPC_POWER9_DD20 = 0x004E1200,
|
||||
CPU_POWERPC_POWER10_BASE = 0x00800000,
|
||||
CPU_POWERPC_POWER10_DD1 = 0x00800100,
|
||||
CPU_POWERPC_970_v22 = 0x00390202,
|
||||
CPU_POWERPC_970FX_v10 = 0x00391100,
|
||||
CPU_POWERPC_970FX_v20 = 0x003C0200,
|
||||
@ -409,6 +411,7 @@ enum {
|
||||
CPU_POWERPC_LOGICAL_2_06_PLUS = 0x0F100003,
|
||||
CPU_POWERPC_LOGICAL_2_07 = 0x0F000004,
|
||||
CPU_POWERPC_LOGICAL_3_00 = 0x0F000005,
|
||||
CPU_POWERPC_LOGICAL_3_10 = 0x0F000006,
|
||||
};
|
||||
|
||||
/* System version register (used on MPC 8xxx) */
|
||||
|
@ -1090,7 +1090,9 @@ struct CPUPPCState {
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/*
|
||||
* This is the IRQ controller, which is implementation dependent
|
||||
* and only relevant when emulating a complete machine.
|
||||
* and only relevant when emulating a complete machine. Note that
|
||||
* this isn't used by recent Book3s compatible CPUs (POWER7 and
|
||||
* newer).
|
||||
*/
|
||||
uint32_t irq_input_state;
|
||||
void **irq_inputs;
|
||||
@ -1220,10 +1222,6 @@ PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
|
||||
PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
|
||||
PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc);
|
||||
|
||||
struct PPCVirtualHypervisor {
|
||||
Object parent;
|
||||
};
|
||||
|
||||
struct PPCVirtualHypervisorClass {
|
||||
InterfaceClass parent;
|
||||
void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu);
|
||||
@ -1305,12 +1303,16 @@ uint64_t cpu_ppc_load_atbl(CPUPPCState *env);
|
||||
uint32_t cpu_ppc_load_atbu(CPUPPCState *env);
|
||||
void cpu_ppc_store_atbl(CPUPPCState *env, uint32_t value);
|
||||
void cpu_ppc_store_atbu(CPUPPCState *env, uint32_t value);
|
||||
uint64_t cpu_ppc_load_vtb(CPUPPCState *env);
|
||||
void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value);
|
||||
bool ppc_decr_clear_on_delivery(CPUPPCState *env);
|
||||
target_ulong cpu_ppc_load_decr(CPUPPCState *env);
|
||||
void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value);
|
||||
target_ulong cpu_ppc_load_hdecr(CPUPPCState *env);
|
||||
void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value);
|
||||
void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value);
|
||||
uint64_t cpu_ppc_load_purr(CPUPPCState *env);
|
||||
void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value);
|
||||
uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env);
|
||||
uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@ -1777,6 +1779,7 @@ typedef PowerPCCPU ArchCPU;
|
||||
#define SPR_MPC_MD_DBRAM1 (0x32A)
|
||||
#define SPR_RCPU_L2U_RA3 (0x32B)
|
||||
#define SPR_TAR (0x32F)
|
||||
#define SPR_ASDR (0x330)
|
||||
#define SPR_IC (0x350)
|
||||
#define SPR_VTB (0x351)
|
||||
#define SPR_MMCRC (0x353)
|
||||
@ -2366,6 +2369,7 @@ enum {
|
||||
PCR_COMPAT_2_06 = PPC_BIT(61),
|
||||
PCR_COMPAT_2_07 = PPC_BIT(60),
|
||||
PCR_COMPAT_3_00 = PPC_BIT(59),
|
||||
PCR_COMPAT_3_10 = PPC_BIT(58),
|
||||
PCR_VEC_DIS = PPC_BIT(0), /* Vec. disable (bit NA since POWER8) */
|
||||
PCR_VSX_DIS = PPC_BIT(1), /* VSX disable (bit NA since POWER8) */
|
||||
PCR_TM_DIS = PPC_BIT(2), /* Trans. memory disable (POWER8) */
|
||||
|
@ -649,11 +649,13 @@ DEF_HELPER_FLAGS_1(load_tbl, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_1(load_tbu, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_1(load_atbl, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_1(load_atbu, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_1(load_vtb, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_1(load_601_rtcl, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_2(store_purr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_2(store_ptcr, void, env, tl)
|
||||
#endif
|
||||
DEF_HELPER_2(store_sdr1, void, env, tl)
|
||||
@ -669,6 +671,8 @@ DEF_HELPER_FLAGS_1(load_decr, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_2(store_decr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_1(load_hdecr, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_2(store_hdecr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(store_vtb, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(store_tbu40, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_2(store_hid0_601, void, env, tl)
|
||||
DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
|
||||
DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env)
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
static inline void hreg_swap_gpr_tgpr(CPUPPCState *env)
|
||||
@ -102,6 +103,10 @@ static inline void hreg_compute_hflags(CPUPPCState *env)
|
||||
|
||||
static inline void cpu_interrupt_exittb(CPUState *cs)
|
||||
{
|
||||
if (!kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
|
@ -2076,7 +2076,7 @@ int kvmppc_set_smt_threads(int smt)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvmppc_hint_smt_possible(Error **errp)
|
||||
void kvmppc_error_append_smt_possible_hint(Error **errp_in)
|
||||
{
|
||||
int i;
|
||||
GString *g;
|
||||
@ -2091,10 +2091,10 @@ void kvmppc_hint_smt_possible(Error **errp)
|
||||
}
|
||||
}
|
||||
s = g_string_free(g, false);
|
||||
error_append_hint(errp, "%s.\n", s);
|
||||
error_append_hint(errp_in, "%s.\n", s);
|
||||
g_free(s);
|
||||
} else {
|
||||
error_append_hint(errp,
|
||||
error_append_hint(errp_in,
|
||||
"This KVM seems to be too old to support VSMT.\n");
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ void kvmppc_set_papr(PowerPCCPU *cpu);
|
||||
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr);
|
||||
void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy);
|
||||
int kvmppc_smt_threads(void);
|
||||
void kvmppc_hint_smt_possible(Error **errp);
|
||||
void kvmppc_error_append_smt_possible_hint(Error **errp_in);
|
||||
int kvmppc_set_smt_threads(int smt);
|
||||
int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits);
|
||||
int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits);
|
||||
@ -164,7 +164,7 @@ static inline int kvmppc_smt_threads(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void kvmppc_hint_smt_possible(Error **errp)
|
||||
static inline void kvmppc_error_append_smt_possible_hint(Error **errp_in)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -45,11 +45,21 @@ target_ulong helper_load_atbu(CPUPPCState *env)
|
||||
return cpu_ppc_load_atbu(env);
|
||||
}
|
||||
|
||||
target_ulong helper_load_vtb(CPUPPCState *env)
|
||||
{
|
||||
return cpu_ppc_load_vtb(env);
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
target_ulong helper_load_purr(CPUPPCState *env)
|
||||
{
|
||||
return (target_ulong)cpu_ppc_load_purr(env);
|
||||
}
|
||||
|
||||
void helper_store_purr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
cpu_ppc_store_purr(env, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
target_ulong helper_load_601_rtcl(CPUPPCState *env)
|
||||
@ -113,6 +123,16 @@ void helper_store_hdecr(CPUPPCState *env, target_ulong val)
|
||||
cpu_ppc_store_hdecr(env, val);
|
||||
}
|
||||
|
||||
void helper_store_vtb(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
cpu_ppc_store_vtb(env, val);
|
||||
}
|
||||
|
||||
void helper_store_tbu40(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
cpu_ppc_store_tbu40(env, val);
|
||||
}
|
||||
|
||||
target_ulong helper_load_40x_pit(CPUPPCState *env)
|
||||
{
|
||||
return load_40x_pit(env);
|
||||
|
@ -287,6 +287,11 @@ static void spr_read_purr(DisasContext *ctx, int gprn, int sprn)
|
||||
gen_helper_load_purr(cpu_gpr[gprn], cpu_env);
|
||||
}
|
||||
|
||||
static void spr_write_purr(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_helper_store_purr(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
/* HDECR */
|
||||
static void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
@ -312,6 +317,21 @@ static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn)
|
||||
}
|
||||
}
|
||||
|
||||
static void spr_read_vtb(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
gen_helper_load_vtb(cpu_gpr[gprn], cpu_env);
|
||||
}
|
||||
|
||||
static void spr_write_vtb(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
static void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -3354,6 +3374,11 @@ static void init_excp_POWER9(CPUPPCState *env)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_excp_POWER10(CPUPPCState *env)
|
||||
{
|
||||
init_excp_POWER9(env);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -7833,6 +7858,16 @@ static void gen_spr_power5p_ear(CPUPPCState *env)
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void gen_spr_power5p_tb(CPUPPCState *env)
|
||||
{
|
||||
/* TBU40 (High 40 bits of the Timebase register */
|
||||
spr_register_hv(env, SPR_TBU40, "TBU40",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_tbu40,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
@ -7998,14 +8033,16 @@ static void gen_spr_book3s_purr(CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* PURR & SPURR: Hack - treat these as aliases for the TB for now */
|
||||
spr_register_kvm(env, SPR_PURR, "PURR",
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
KVM_REG_PPC_PURR, 0x00000000);
|
||||
spr_register_kvm(env, SPR_SPURR, "SPURR",
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
KVM_REG_PPC_SPURR, 0x00000000);
|
||||
spr_register_kvm_hv(env, SPR_PURR, "PURR",
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, &spr_write_purr,
|
||||
KVM_REG_PPC_PURR, 0x00000000);
|
||||
spr_register_kvm_hv(env, SPR_SPURR, "SPURR",
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, &spr_write_purr,
|
||||
KVM_REG_PPC_SPURR, 0x00000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -8169,10 +8206,11 @@ static void gen_spr_power8_ebb(CPUPPCState *env)
|
||||
/* Virtual Time Base */
|
||||
static void gen_spr_vtb(CPUPPCState *env)
|
||||
{
|
||||
spr_register_kvm(env, SPR_VTB, "VTB",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_tbl, SPR_NOACCESS,
|
||||
KVM_REG_PPC_VTB, 0x00000000);
|
||||
spr_register_kvm_hv(env, SPR_VTB, "VTB",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_vtb, SPR_NOACCESS,
|
||||
&spr_read_vtb, &spr_write_vtb,
|
||||
KVM_REG_PPC_VTB, 0x00000000);
|
||||
}
|
||||
|
||||
static void gen_spr_power8_fscr(CPUPPCState *env)
|
||||
@ -8272,6 +8310,12 @@ static void gen_spr_power9_mmu(CPUPPCState *env)
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_ptcr,
|
||||
KVM_REG_PPC_PTCR, 0x00000000);
|
||||
/* Address Segment Descriptor Register */
|
||||
spr_register_hv(env, SPR_ASDR, "ASDR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x0000000000000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -8375,6 +8419,7 @@ static void init_proc_power5plus(CPUPPCState *env)
|
||||
gen_spr_power5p_common(env);
|
||||
gen_spr_power5p_lpar(env);
|
||||
gen_spr_power5p_ear(env);
|
||||
gen_spr_power5p_tb(env);
|
||||
|
||||
/* env variables */
|
||||
env->dcache_line_size = 128;
|
||||
@ -8487,6 +8532,7 @@ static void init_proc_POWER7(CPUPPCState *env)
|
||||
gen_spr_power5p_common(env);
|
||||
gen_spr_power5p_lpar(env);
|
||||
gen_spr_power5p_ear(env);
|
||||
gen_spr_power5p_tb(env);
|
||||
gen_spr_power6_common(env);
|
||||
gen_spr_power6_dbg(env);
|
||||
gen_spr_power7_book4(env);
|
||||
@ -8628,6 +8674,7 @@ static void init_proc_POWER8(CPUPPCState *env)
|
||||
gen_spr_power5p_common(env);
|
||||
gen_spr_power5p_lpar(env);
|
||||
gen_spr_power5p_ear(env);
|
||||
gen_spr_power5p_tb(env);
|
||||
gen_spr_power6_common(env);
|
||||
gen_spr_power6_dbg(env);
|
||||
gen_spr_power8_tce_address_control(env);
|
||||
@ -8818,6 +8865,7 @@ static void init_proc_POWER9(CPUPPCState *env)
|
||||
gen_spr_power5p_common(env);
|
||||
gen_spr_power5p_lpar(env);
|
||||
gen_spr_power5p_ear(env);
|
||||
gen_spr_power5p_tb(env);
|
||||
gen_spr_power6_common(env);
|
||||
gen_spr_power6_dbg(env);
|
||||
gen_spr_power8_tce_address_control(env);
|
||||
@ -8996,6 +9044,216 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
|
||||
pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
/*
|
||||
* Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings
|
||||
* Encoded as array of int_32s in the form:
|
||||
* 0bxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
* x -> AP encoding
|
||||
* y -> radix mode supported page size (encoded as a shift)
|
||||
*/
|
||||
static struct ppc_radix_page_info POWER10_radix_page_info = {
|
||||
.count = 4,
|
||||
.entries = {
|
||||
0x0000000c, /* 4K - enc: 0x0 */
|
||||
0xa0000010, /* 64K - enc: 0x5 */
|
||||
0x20000015, /* 2M - enc: 0x1 */
|
||||
0x4000001e /* 1G - enc: 0x2 */
|
||||
}
|
||||
};
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
|
||||
static void init_proc_POWER10(CPUPPCState *env)
|
||||
{
|
||||
/* Common Registers */
|
||||
init_proc_book3s_common(env);
|
||||
gen_spr_book3s_207_dbg(env);
|
||||
|
||||
/* POWER8 Specific Registers */
|
||||
gen_spr_book3s_ids(env);
|
||||
gen_spr_amr(env);
|
||||
gen_spr_iamr(env);
|
||||
gen_spr_book3s_purr(env);
|
||||
gen_spr_power5p_common(env);
|
||||
gen_spr_power5p_lpar(env);
|
||||
gen_spr_power5p_ear(env);
|
||||
gen_spr_power6_common(env);
|
||||
gen_spr_power6_dbg(env);
|
||||
gen_spr_power8_tce_address_control(env);
|
||||
gen_spr_power8_ids(env);
|
||||
gen_spr_power8_ebb(env);
|
||||
gen_spr_power8_fscr(env);
|
||||
gen_spr_power8_pmu_sup(env);
|
||||
gen_spr_power8_pmu_user(env);
|
||||
gen_spr_power8_tm(env);
|
||||
gen_spr_power8_pspb(env);
|
||||
gen_spr_vtb(env);
|
||||
gen_spr_power8_ic(env);
|
||||
gen_spr_power8_book4(env);
|
||||
gen_spr_power8_rpr(env);
|
||||
gen_spr_power9_mmu(env);
|
||||
|
||||
/* POWER9 Specific registers */
|
||||
spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL,
|
||||
spr_read_generic, spr_write_generic,
|
||||
KVM_REG_PPC_TIDR, 0);
|
||||
|
||||
/* FIXME: Filter fields properly based on privilege level */
|
||||
spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL,
|
||||
spr_read_generic, spr_write_generic,
|
||||
KVM_REG_PPC_PSSCR, 0);
|
||||
|
||||
/* env variables */
|
||||
env->dcache_line_size = 128;
|
||||
env->icache_line_size = 128;
|
||||
|
||||
/* Allocate hardware IRQ controller */
|
||||
init_excp_POWER10(env);
|
||||
ppcPOWER9_irq_init(env_archcpu(env));
|
||||
}
|
||||
|
||||
static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
{
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER10_BASE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER10(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (cs->halted) {
|
||||
uint64_t psscr = env->spr[SPR_PSSCR];
|
||||
|
||||
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If EC is clear, just return true on any pending interrupt */
|
||||
if (!(psscr & PSSCR_EC)) {
|
||||
return true;
|
||||
}
|
||||
/* External Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_EEE)) {
|
||||
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
|
||||
if (heic == 0 || !msr_hv || msr_pr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Decrementer Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_DEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Machine Check or Hypervisor Maintenance Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK |
|
||||
1u << PPC_INTERRUPT_HMI)) && (env->spr[SPR_LPCR] & LPCR_OEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Privileged Doorbell Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_PDEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Hypervisor Doorbell Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Hypervisor virtualization exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_HVEE)) {
|
||||
return true;
|
||||
}
|
||||
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
dc->fw_name = "PowerPC,POWER10";
|
||||
dc->desc = "POWER10";
|
||||
dc->props = powerpc_servercpu_properties;
|
||||
pcc->pvr_match = ppc_pvr_match_power10;
|
||||
pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07 |
|
||||
PCR_COMPAT_3_00;
|
||||
pcc->pcr_supported = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
|
||||
PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
|
||||
pcc->init_proc = init_proc_POWER10;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
cc->has_work = cpu_has_work_POWER10;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_FRSQRTES |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_FLOAT_EXT |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBSYNC |
|
||||
PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
|
||||
PPC_SEGMENT_64B | PPC_SLBI |
|
||||
PPC_POPCNTB | PPC_POPCNTWD |
|
||||
PPC_CILDST;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
|
||||
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
|
||||
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
|
||||
PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 |
|
||||
PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 |
|
||||
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
|
||||
PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL;
|
||||
pcc->msr_mask = (1ull << MSR_SF) |
|
||||
(1ull << MSR_SHV) |
|
||||
(1ull << MSR_TM) |
|
||||
(1ull << MSR_VR) |
|
||||
(1ull << MSR_VSX) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_FP) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_FE0) |
|
||||
(1ull << MSR_SE) |
|
||||
(1ull << MSR_DE) |
|
||||
(1ull << MSR_FE1) |
|
||||
(1ull << MSR_IR) |
|
||||
(1ull << MSR_DR) |
|
||||
(1ull << MSR_PMM) |
|
||||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_3_00;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault;
|
||||
/* segment page size remain the same */
|
||||
pcc->hash64_opts = &ppc_hash64_opts_POWER7;
|
||||
pcc->radix_page_info = &POWER10_radix_page_info;
|
||||
pcc->lrg_decr_bits = 56;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_POWER9;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_POWER9;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
|
||||
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
|
||||
POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
|
||||
POWERPC_FLAG_VSX | POWERPC_FLAG_TM;
|
||||
pcc->l1_dcache_size = 0x8000;
|
||||
pcc->l1_icache_size = 0x8000;
|
||||
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
|
||||
pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp)
|
||||
{
|
||||
@ -10461,6 +10719,7 @@ static void ppc_cpu_reset(CPUState *s)
|
||||
env->pending_interrupts = 0;
|
||||
s->exception_index = POWERPC_EXCP_NONE;
|
||||
env->error_code = 0;
|
||||
ppc_irq_reset(cpu);
|
||||
|
||||
/* tininess for underflow is detected before rounding */
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
|
Loading…
Reference in New Issue
Block a user