Skip to content

Commit

Permalink
target/ppc: Introduce ppc_interrupts_little_endian()
Browse files Browse the repository at this point in the history
PowerPC CPUs use big endian by default but starting with POWER7,
server grade CPUs use the ILE bit of the LPCR special purpose
register to decide on the endianness to use when handling
interrupts. This gives a clue to QEMU on the endianness the
guest kernel is running, which is needed when generating an
ELF dump of the guest or when delivering an FWNMI machine
check interrupt.

Commit 382d2db ("target-ppc: Introduce callback for interrupt
endianness") added a class method to PowerPCCPUClass to modelize
this : default implementation returns a fixed "big endian" value,
while POWER7 and newer do the LPCR_ILE check. This is suboptimal
as it forces to implement the method for every new CPU family, and
it is very unlikely that this will ever be different than what we
have today.

We basically only have three cases to consider:
a) CPU doesn't have an LPCR => big endian
b) CPU has an LPCR but doesn't support the ILE bit => big endian
c) CPU has an LPCR and supports the ILE bit => little or big endian

Instead of class methods, introduce an inline helper that checks the
ILE bit in the LPCR_MASK to decide on the outcome. The new helper
words little endian instead of big endian. This allows to drop a !
operator in ppc_cpu_do_fwnmi_machine_check().

Signed-off-by: Greg Kurz <groug@kaod.org>
Message-Id: <20210622140926.677618-2-groug@kaod.org>
Reviewed-by: Fabiano Rosas <farosas@linux.ibm.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
  • Loading branch information
gkurz authored and dgibson committed Jul 9, 2021
1 parent 9db3065 commit c11dc15
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 7 deletions.
8 changes: 3 additions & 5 deletions target/ppc/arch_dump.c
Expand Up @@ -227,22 +227,20 @@ int cpu_get_dump_info(ArchDumpInfo *info,
const struct GuestPhysBlockList *guest_phys_blocks)
{
PowerPCCPU *cpu;
PowerPCCPUClass *pcc;

if (first_cpu == NULL) {
return -1;
}

cpu = POWERPC_CPU(first_cpu);
pcc = POWERPC_CPU_GET_CLASS(cpu);

info->d_machine = PPC_ELF_MACHINE;
info->d_class = ELFCLASS;

if ((*pcc->interrupts_big_endian)(cpu)) {
info->d_endian = ELFDATA2MSB;
} else {
if (ppc_interrupts_little_endian(cpu)) {
info->d_endian = ELFDATA2LSB;
} else {
info->d_endian = ELFDATA2MSB;
}
/* 64KB is the max page size for pseries kernel */
if (strncmp(object_get_typename(qdev_get_machine()),
Expand Down
15 changes: 15 additions & 0 deletions target/ppc/cpu.h
Expand Up @@ -2643,6 +2643,21 @@ static inline bool ppc_has_spr(PowerPCCPU *cpu, int spr)
return cpu->env.spr_cb[spr].name != NULL;
}

static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);

/*
* Only models that have an LPCR and know about LPCR_ILE can do little
* endian.
*/
if (pcc->lpcr_mask & LPCR_ILE) {
return !!(cpu->env.spr[SPR_LPCR] & LPCR_ILE);
}

return false;
}

void dump_mmu(CPUPPCState *env);

void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len);
Expand Down
3 changes: 1 addition & 2 deletions target/ppc/excp_helper.c
Expand Up @@ -1099,7 +1099,6 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
target_ulong msr = 0;

/*
Expand All @@ -1108,7 +1107,7 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector)
*/
msr = (1ULL << MSR_ME);
msr |= env->msr & (1ULL << MSR_SF);
if (!(*pcc->interrupts_big_endian)(cpu)) {
if (ppc_interrupts_little_endian(cpu)) {
msr |= (1ULL << MSR_LE);
}

Expand Down

0 comments on commit c11dc15

Please sign in to comment.