mirror of
https://github.com/qemu/qemu.git
synced 2024-12-14 15:03:33 +08:00
target/ppc: move opcode table logic to translate.c
code motion to remove opcode callback table from translate_init.c.inc to translate.c in preparation to remove the #include <translate_init.c.inc> from translate.c. Also created destroy_ppc_opcodes and removed that logic from ppc_cpu_unrealize Signed-off-by: Bruno Larsen (billionai) <bruno.larsen@eldorado.org.br> Message-Id: <20210429162130.2412-2-bruno.larsen@eldorado.org.br> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
35a5d74e82
commit
7468e2c842
@ -216,6 +216,14 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
|||||||
MMUAccessType access_type,
|
MMUAccessType access_type,
|
||||||
int mmu_idx, uintptr_t retaddr);
|
int mmu_idx, uintptr_t retaddr);
|
||||||
|
|
||||||
|
/* translate.c */
|
||||||
|
|
||||||
|
/* #define PPC_DUMP_CPU */
|
||||||
|
|
||||||
|
int ppc_fixup_cpu(PowerPCCPU *cpu);
|
||||||
|
void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp);
|
||||||
|
void destroy_ppc_opcodes(PowerPCCPU *cpu);
|
||||||
|
|
||||||
/* gdbstub.c */
|
/* gdbstub.c */
|
||||||
void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc);
|
void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc);
|
||||||
gchar *ppc_gdb_arch_name(CPUState *cs);
|
gchar *ppc_gdb_arch_name(CPUState *cs);
|
||||||
|
@ -7825,6 +7825,400 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
|||||||
#undef RFPL
|
#undef RFPL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Opcode types */
|
||||||
|
enum {
|
||||||
|
PPC_DIRECT = 0, /* Opcode routine */
|
||||||
|
PPC_INDIRECT = 1, /* Indirect opcode table */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PPC_OPCODE_MASK 0x3
|
||||||
|
|
||||||
|
static inline int is_indirect_opcode(void *handler)
|
||||||
|
{
|
||||||
|
return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline opc_handler_t **ind_table(void *handler)
|
||||||
|
{
|
||||||
|
return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Instruction table creation */
|
||||||
|
/* Opcodes tables creation */
|
||||||
|
static void fill_new_table(opc_handler_t **table, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
table[i] = &invalid_handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_new_table(opc_handler_t **table, unsigned char idx)
|
||||||
|
{
|
||||||
|
opc_handler_t **tmp;
|
||||||
|
|
||||||
|
tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||||
|
fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||||
|
table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_in_table(opc_handler_t **table, unsigned char idx,
|
||||||
|
opc_handler_t *handler)
|
||||||
|
{
|
||||||
|
if (table[idx] != &invalid_handler) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
table[idx] = handler;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_direct_insn(opc_handler_t **ppc_opcodes,
|
||||||
|
unsigned char idx, opc_handler_t *handler)
|
||||||
|
{
|
||||||
|
if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
|
||||||
|
printf("*** ERROR: opcode %02x already assigned in main "
|
||||||
|
"opcode table\n", idx);
|
||||||
|
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||||
|
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||||
|
ppc_opcodes[idx]->oname, handler->oname);
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_ind_in_table(opc_handler_t **table,
|
||||||
|
unsigned char idx1, unsigned char idx2,
|
||||||
|
opc_handler_t *handler)
|
||||||
|
{
|
||||||
|
if (table[idx1] == &invalid_handler) {
|
||||||
|
if (create_new_table(table, idx1) < 0) {
|
||||||
|
printf("*** ERROR: unable to create indirect table "
|
||||||
|
"idx=%02x\n", idx1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!is_indirect_opcode(table[idx1])) {
|
||||||
|
printf("*** ERROR: idx %02x already assigned to a direct "
|
||||||
|
"opcode\n", idx1);
|
||||||
|
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||||
|
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||||
|
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handler != NULL &&
|
||||||
|
insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
|
||||||
|
printf("*** ERROR: opcode %02x already assigned in "
|
||||||
|
"opcode table %02x\n", idx2, idx1);
|
||||||
|
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||||
|
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||||
|
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_ind_insn(opc_handler_t **ppc_opcodes,
|
||||||
|
unsigned char idx1, unsigned char idx2,
|
||||||
|
opc_handler_t *handler)
|
||||||
|
{
|
||||||
|
return register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_dblind_insn(opc_handler_t **ppc_opcodes,
|
||||||
|
unsigned char idx1, unsigned char idx2,
|
||||||
|
unsigned char idx3, opc_handler_t *handler)
|
||||||
|
{
|
||||||
|
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
||||||
|
printf("*** ERROR: unable to join indirect table idx "
|
||||||
|
"[%02x-%02x]\n", idx1, idx2);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
|
||||||
|
handler) < 0) {
|
||||||
|
printf("*** ERROR: unable to insert opcode "
|
||||||
|
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_trplind_insn(opc_handler_t **ppc_opcodes,
|
||||||
|
unsigned char idx1, unsigned char idx2,
|
||||||
|
unsigned char idx3, unsigned char idx4,
|
||||||
|
opc_handler_t *handler)
|
||||||
|
{
|
||||||
|
opc_handler_t **table;
|
||||||
|
|
||||||
|
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
||||||
|
printf("*** ERROR: unable to join indirect table idx "
|
||||||
|
"[%02x-%02x]\n", idx1, idx2);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
table = ind_table(ppc_opcodes[idx1]);
|
||||||
|
if (register_ind_in_table(table, idx2, idx3, NULL) < 0) {
|
||||||
|
printf("*** ERROR: unable to join 2nd-level indirect table idx "
|
||||||
|
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
table = ind_table(table[idx2]);
|
||||||
|
if (register_ind_in_table(table, idx3, idx4, handler) < 0) {
|
||||||
|
printf("*** ERROR: unable to insert opcode "
|
||||||
|
"[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn)
|
||||||
|
{
|
||||||
|
if (insn->opc2 != 0xFF) {
|
||||||
|
if (insn->opc3 != 0xFF) {
|
||||||
|
if (insn->opc4 != 0xFF) {
|
||||||
|
if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
||||||
|
insn->opc3, insn->opc4,
|
||||||
|
&insn->handler) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
||||||
|
insn->opc3, &insn->handler) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (register_ind_insn(ppc_opcodes, insn->opc1,
|
||||||
|
insn->opc2, &insn->handler) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_opcode_table(opc_handler_t **table, int len)
|
||||||
|
{
|
||||||
|
int i, count, tmp;
|
||||||
|
|
||||||
|
for (i = 0, count = 0; i < len; i++) {
|
||||||
|
/* Consistency fixup */
|
||||||
|
if (table[i] == NULL) {
|
||||||
|
table[i] = &invalid_handler;
|
||||||
|
}
|
||||||
|
if (table[i] != &invalid_handler) {
|
||||||
|
if (is_indirect_opcode(table[i])) {
|
||||||
|
tmp = test_opcode_table(ind_table(table[i]),
|
||||||
|
PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||||
|
if (tmp == 0) {
|
||||||
|
free(table[i]);
|
||||||
|
table[i] = &invalid_handler;
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fix_opcode_tables(opc_handler_t **ppc_opcodes)
|
||||||
|
{
|
||||||
|
if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) {
|
||||||
|
printf("*** WARNING: no opcode defined !\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
|
||||||
|
{
|
||||||
|
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||||
|
opcode_t *opc;
|
||||||
|
|
||||||
|
fill_new_table(cpu->opcodes, PPC_CPU_OPCODES_LEN);
|
||||||
|
for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
|
||||||
|
if (((opc->handler.type & pcc->insns_flags) != 0) ||
|
||||||
|
((opc->handler.type2 & pcc->insns_flags2) != 0)) {
|
||||||
|
if (register_insn(cpu->opcodes, opc) < 0) {
|
||||||
|
error_setg(errp, "ERROR initializing PowerPC instruction "
|
||||||
|
"0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2,
|
||||||
|
opc->opc3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fix_opcode_tables(cpu->opcodes);
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_ppc_opcodes(PowerPCCPU *cpu)
|
||||||
|
{
|
||||||
|
opc_handler_t **table, **table_2;
|
||||||
|
int i, j, k;
|
||||||
|
|
||||||
|
for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) {
|
||||||
|
if (cpu->opcodes[i] == &invalid_handler) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_indirect_opcode(cpu->opcodes[i])) {
|
||||||
|
table = ind_table(cpu->opcodes[i]);
|
||||||
|
for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) {
|
||||||
|
if (table[j] == &invalid_handler) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_indirect_opcode(table[j])) {
|
||||||
|
table_2 = ind_table(table[j]);
|
||||||
|
for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) {
|
||||||
|
if (table_2[k] != &invalid_handler &&
|
||||||
|
is_indirect_opcode(table_2[k])) {
|
||||||
|
g_free((opc_handler_t *)((uintptr_t)table_2[k] &
|
||||||
|
~PPC_INDIRECT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free((opc_handler_t *)((uintptr_t)table[j] &
|
||||||
|
~PPC_INDIRECT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free((opc_handler_t *)((uintptr_t)cpu->opcodes[i] &
|
||||||
|
~PPC_INDIRECT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(PPC_DUMP_CPU)
|
||||||
|
static void dump_ppc_insns(CPUPPCState *env)
|
||||||
|
{
|
||||||
|
opc_handler_t **table, *handler;
|
||||||
|
const char *p, *q;
|
||||||
|
uint8_t opc1, opc2, opc3, opc4;
|
||||||
|
|
||||||
|
printf("Instructions set:\n");
|
||||||
|
/* opc1 is 6 bits long */
|
||||||
|
for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) {
|
||||||
|
table = env->opcodes;
|
||||||
|
handler = table[opc1];
|
||||||
|
if (is_indirect_opcode(handler)) {
|
||||||
|
/* opc2 is 5 bits long */
|
||||||
|
for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) {
|
||||||
|
table = env->opcodes;
|
||||||
|
handler = env->opcodes[opc1];
|
||||||
|
table = ind_table(handler);
|
||||||
|
handler = table[opc2];
|
||||||
|
if (is_indirect_opcode(handler)) {
|
||||||
|
table = ind_table(handler);
|
||||||
|
/* opc3 is 5 bits long */
|
||||||
|
for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
||||||
|
opc3++) {
|
||||||
|
handler = table[opc3];
|
||||||
|
if (is_indirect_opcode(handler)) {
|
||||||
|
table = ind_table(handler);
|
||||||
|
/* opc4 is 5 bits long */
|
||||||
|
for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
||||||
|
opc4++) {
|
||||||
|
handler = table[opc4];
|
||||||
|
if (handler->handler != &gen_invalid) {
|
||||||
|
printf("INSN: %02x %02x %02x %02x -- "
|
||||||
|
"(%02d %04d %02d) : %s\n",
|
||||||
|
opc1, opc2, opc3, opc4,
|
||||||
|
opc1, (opc3 << 5) | opc2, opc4,
|
||||||
|
handler->oname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (handler->handler != &gen_invalid) {
|
||||||
|
/* Special hack to properly dump SPE insns */
|
||||||
|
p = strchr(handler->oname, '_');
|
||||||
|
if (p == NULL) {
|
||||||
|
printf("INSN: %02x %02x %02x (%02d %04d) : "
|
||||||
|
"%s\n",
|
||||||
|
opc1, opc2, opc3, opc1,
|
||||||
|
(opc3 << 5) | opc2,
|
||||||
|
handler->oname);
|
||||||
|
} else {
|
||||||
|
q = "speundef";
|
||||||
|
if ((p - handler->oname) != strlen(q)
|
||||||
|
|| (memcmp(handler->oname, q, strlen(q))
|
||||||
|
!= 0)) {
|
||||||
|
/* First instruction */
|
||||||
|
printf("INSN: %02x %02x %02x"
|
||||||
|
"(%02d %04d) : %.*s\n",
|
||||||
|
opc1, opc2 << 1, opc3, opc1,
|
||||||
|
(opc3 << 6) | (opc2 << 1),
|
||||||
|
(int)(p - handler->oname),
|
||||||
|
handler->oname);
|
||||||
|
}
|
||||||
|
if (strcmp(p + 1, q) != 0) {
|
||||||
|
/* Second instruction */
|
||||||
|
printf("INSN: %02x %02x %02x "
|
||||||
|
"(%02d %04d) : %s\n", opc1,
|
||||||
|
(opc2 << 1) | 1, opc3, opc1,
|
||||||
|
(opc3 << 6) | (opc2 << 1) | 1,
|
||||||
|
p + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (handler->handler != &gen_invalid) {
|
||||||
|
printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
|
||||||
|
opc1, opc2, opc1, opc2, handler->oname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (handler->handler != &gen_invalid) {
|
||||||
|
printf("INSN: %02x -- -- (%02d ----) : %s\n",
|
||||||
|
opc1, opc1, handler->oname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
int ppc_fixup_cpu(PowerPCCPU *cpu)
|
||||||
|
{
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TCG doesn't (yet) emulate some groups of instructions that are
|
||||||
|
* implemented on some otherwise supported CPUs (e.g. VSX and
|
||||||
|
* decimal floating point instructions on POWER7). We remove
|
||||||
|
* unsupported instruction groups from the cpu state's instruction
|
||||||
|
* masks and hope the guest can cope. For at least the pseries
|
||||||
|
* machine, the unavailability of these instructions can be
|
||||||
|
* advertised to the guest via the device tree.
|
||||||
|
*/
|
||||||
|
if ((env->insns_flags & ~PPC_TCG_INSNS)
|
||||||
|
|| (env->insns_flags2 & ~PPC_TCG_INSNS2)) {
|
||||||
|
warn_report("Disabling some instructions which are not "
|
||||||
|
"emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")",
|
||||||
|
env->insns_flags & ~PPC_TCG_INSNS,
|
||||||
|
env->insns_flags2 & ~PPC_TCG_INSNS2);
|
||||||
|
}
|
||||||
|
env->insns_flags &= PPC_TCG_INSNS;
|
||||||
|
env->insns_flags2 &= PPC_TCG_INSNS2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ppc_cpu_dump_statistics(CPUState *cs, int flags)
|
void ppc_cpu_dump_statistics(CPUState *cs, int flags)
|
||||||
{
|
{
|
||||||
#if defined(DO_PPC_STATISTICS)
|
#if defined(DO_PPC_STATISTICS)
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include "fpu/softfloat.h"
|
#include "fpu/softfloat.h"
|
||||||
#include "qapi/qapi-commands-machine-target.h"
|
#include "qapi/qapi-commands-machine-target.h"
|
||||||
|
|
||||||
/* #define PPC_DUMP_CPU */
|
|
||||||
/* #define PPC_DEBUG_SPR */
|
/* #define PPC_DEBUG_SPR */
|
||||||
/* #define PPC_DUMP_SPR_ACCESSES */
|
/* #define PPC_DUMP_SPR_ACCESSES */
|
||||||
/* #define USE_APPLE_GDB */
|
/* #define USE_APPLE_GDB */
|
||||||
@ -9560,366 +9559,6 @@ static void dump_ppc_sprs(CPUPPCState *env)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
/* Opcode types */
|
|
||||||
enum {
|
|
||||||
PPC_DIRECT = 0, /* Opcode routine */
|
|
||||||
PPC_INDIRECT = 1, /* Indirect opcode table */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PPC_OPCODE_MASK 0x3
|
|
||||||
|
|
||||||
static inline int is_indirect_opcode(void *handler)
|
|
||||||
{
|
|
||||||
return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline opc_handler_t **ind_table(void *handler)
|
|
||||||
{
|
|
||||||
return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Instruction table creation */
|
|
||||||
/* Opcodes tables creation */
|
|
||||||
static void fill_new_table(opc_handler_t **table, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
table[i] = &invalid_handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_new_table(opc_handler_t **table, unsigned char idx)
|
|
||||||
{
|
|
||||||
opc_handler_t **tmp;
|
|
||||||
|
|
||||||
tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN);
|
|
||||||
fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN);
|
|
||||||
table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int insert_in_table(opc_handler_t **table, unsigned char idx,
|
|
||||||
opc_handler_t *handler)
|
|
||||||
{
|
|
||||||
if (table[idx] != &invalid_handler) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
table[idx] = handler;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_direct_insn(opc_handler_t **ppc_opcodes,
|
|
||||||
unsigned char idx, opc_handler_t *handler)
|
|
||||||
{
|
|
||||||
if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
|
|
||||||
printf("*** ERROR: opcode %02x already assigned in main "
|
|
||||||
"opcode table\n", idx);
|
|
||||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
|
||||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
|
||||||
ppc_opcodes[idx]->oname, handler->oname);
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_ind_in_table(opc_handler_t **table,
|
|
||||||
unsigned char idx1, unsigned char idx2,
|
|
||||||
opc_handler_t *handler)
|
|
||||||
{
|
|
||||||
if (table[idx1] == &invalid_handler) {
|
|
||||||
if (create_new_table(table, idx1) < 0) {
|
|
||||||
printf("*** ERROR: unable to create indirect table "
|
|
||||||
"idx=%02x\n", idx1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!is_indirect_opcode(table[idx1])) {
|
|
||||||
printf("*** ERROR: idx %02x already assigned to a direct "
|
|
||||||
"opcode\n", idx1);
|
|
||||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
|
||||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
|
||||||
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (handler != NULL &&
|
|
||||||
insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
|
|
||||||
printf("*** ERROR: opcode %02x already assigned in "
|
|
||||||
"opcode table %02x\n", idx2, idx1);
|
|
||||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
|
||||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
|
||||||
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_ind_insn(opc_handler_t **ppc_opcodes,
|
|
||||||
unsigned char idx1, unsigned char idx2,
|
|
||||||
opc_handler_t *handler)
|
|
||||||
{
|
|
||||||
return register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_dblind_insn(opc_handler_t **ppc_opcodes,
|
|
||||||
unsigned char idx1, unsigned char idx2,
|
|
||||||
unsigned char idx3, opc_handler_t *handler)
|
|
||||||
{
|
|
||||||
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
|
||||||
printf("*** ERROR: unable to join indirect table idx "
|
|
||||||
"[%02x-%02x]\n", idx1, idx2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
|
|
||||||
handler) < 0) {
|
|
||||||
printf("*** ERROR: unable to insert opcode "
|
|
||||||
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_trplind_insn(opc_handler_t **ppc_opcodes,
|
|
||||||
unsigned char idx1, unsigned char idx2,
|
|
||||||
unsigned char idx3, unsigned char idx4,
|
|
||||||
opc_handler_t *handler)
|
|
||||||
{
|
|
||||||
opc_handler_t **table;
|
|
||||||
|
|
||||||
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
|
||||||
printf("*** ERROR: unable to join indirect table idx "
|
|
||||||
"[%02x-%02x]\n", idx1, idx2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
table = ind_table(ppc_opcodes[idx1]);
|
|
||||||
if (register_ind_in_table(table, idx2, idx3, NULL) < 0) {
|
|
||||||
printf("*** ERROR: unable to join 2nd-level indirect table idx "
|
|
||||||
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
table = ind_table(table[idx2]);
|
|
||||||
if (register_ind_in_table(table, idx3, idx4, handler) < 0) {
|
|
||||||
printf("*** ERROR: unable to insert opcode "
|
|
||||||
"[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn)
|
|
||||||
{
|
|
||||||
if (insn->opc2 != 0xFF) {
|
|
||||||
if (insn->opc3 != 0xFF) {
|
|
||||||
if (insn->opc4 != 0xFF) {
|
|
||||||
if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
|
||||||
insn->opc3, insn->opc4,
|
|
||||||
&insn->handler) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
|
||||||
insn->opc3, &insn->handler) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (register_ind_insn(ppc_opcodes, insn->opc1,
|
|
||||||
insn->opc2, &insn->handler) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int test_opcode_table(opc_handler_t **table, int len)
|
|
||||||
{
|
|
||||||
int i, count, tmp;
|
|
||||||
|
|
||||||
for (i = 0, count = 0; i < len; i++) {
|
|
||||||
/* Consistency fixup */
|
|
||||||
if (table[i] == NULL) {
|
|
||||||
table[i] = &invalid_handler;
|
|
||||||
}
|
|
||||||
if (table[i] != &invalid_handler) {
|
|
||||||
if (is_indirect_opcode(table[i])) {
|
|
||||||
tmp = test_opcode_table(ind_table(table[i]),
|
|
||||||
PPC_CPU_INDIRECT_OPCODES_LEN);
|
|
||||||
if (tmp == 0) {
|
|
||||||
free(table[i]);
|
|
||||||
table[i] = &invalid_handler;
|
|
||||||
} else {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fix_opcode_tables(opc_handler_t **ppc_opcodes)
|
|
||||||
{
|
|
||||||
if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) {
|
|
||||||
printf("*** WARNING: no opcode defined !\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
|
|
||||||
{
|
|
||||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
|
||||||
opcode_t *opc;
|
|
||||||
|
|
||||||
fill_new_table(cpu->opcodes, PPC_CPU_OPCODES_LEN);
|
|
||||||
for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
|
|
||||||
if (((opc->handler.type & pcc->insns_flags) != 0) ||
|
|
||||||
((opc->handler.type2 & pcc->insns_flags2) != 0)) {
|
|
||||||
if (register_insn(cpu->opcodes, opc) < 0) {
|
|
||||||
error_setg(errp, "ERROR initializing PowerPC instruction "
|
|
||||||
"0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2,
|
|
||||||
opc->opc3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fix_opcode_tables(cpu->opcodes);
|
|
||||||
fflush(stdout);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(PPC_DUMP_CPU)
|
|
||||||
static void dump_ppc_insns(CPUPPCState *env)
|
|
||||||
{
|
|
||||||
opc_handler_t **table, *handler;
|
|
||||||
const char *p, *q;
|
|
||||||
uint8_t opc1, opc2, opc3, opc4;
|
|
||||||
|
|
||||||
printf("Instructions set:\n");
|
|
||||||
/* opc1 is 6 bits long */
|
|
||||||
for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) {
|
|
||||||
table = env->opcodes;
|
|
||||||
handler = table[opc1];
|
|
||||||
if (is_indirect_opcode(handler)) {
|
|
||||||
/* opc2 is 5 bits long */
|
|
||||||
for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) {
|
|
||||||
table = env->opcodes;
|
|
||||||
handler = env->opcodes[opc1];
|
|
||||||
table = ind_table(handler);
|
|
||||||
handler = table[opc2];
|
|
||||||
if (is_indirect_opcode(handler)) {
|
|
||||||
table = ind_table(handler);
|
|
||||||
/* opc3 is 5 bits long */
|
|
||||||
for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
|
||||||
opc3++) {
|
|
||||||
handler = table[opc3];
|
|
||||||
if (is_indirect_opcode(handler)) {
|
|
||||||
table = ind_table(handler);
|
|
||||||
/* opc4 is 5 bits long */
|
|
||||||
for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
|
||||||
opc4++) {
|
|
||||||
handler = table[opc4];
|
|
||||||
if (handler->handler != &gen_invalid) {
|
|
||||||
printf("INSN: %02x %02x %02x %02x -- "
|
|
||||||
"(%02d %04d %02d) : %s\n",
|
|
||||||
opc1, opc2, opc3, opc4,
|
|
||||||
opc1, (opc3 << 5) | opc2, opc4,
|
|
||||||
handler->oname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (handler->handler != &gen_invalid) {
|
|
||||||
/* Special hack to properly dump SPE insns */
|
|
||||||
p = strchr(handler->oname, '_');
|
|
||||||
if (p == NULL) {
|
|
||||||
printf("INSN: %02x %02x %02x (%02d %04d) : "
|
|
||||||
"%s\n",
|
|
||||||
opc1, opc2, opc3, opc1,
|
|
||||||
(opc3 << 5) | opc2,
|
|
||||||
handler->oname);
|
|
||||||
} else {
|
|
||||||
q = "speundef";
|
|
||||||
if ((p - handler->oname) != strlen(q)
|
|
||||||
|| (memcmp(handler->oname, q, strlen(q))
|
|
||||||
!= 0)) {
|
|
||||||
/* First instruction */
|
|
||||||
printf("INSN: %02x %02x %02x"
|
|
||||||
"(%02d %04d) : %.*s\n",
|
|
||||||
opc1, opc2 << 1, opc3, opc1,
|
|
||||||
(opc3 << 6) | (opc2 << 1),
|
|
||||||
(int)(p - handler->oname),
|
|
||||||
handler->oname);
|
|
||||||
}
|
|
||||||
if (strcmp(p + 1, q) != 0) {
|
|
||||||
/* Second instruction */
|
|
||||||
printf("INSN: %02x %02x %02x "
|
|
||||||
"(%02d %04d) : %s\n", opc1,
|
|
||||||
(opc2 << 1) | 1, opc3, opc1,
|
|
||||||
(opc3 << 6) | (opc2 << 1) | 1,
|
|
||||||
p + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (handler->handler != &gen_invalid) {
|
|
||||||
printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
|
|
||||||
opc1, opc2, opc1, opc2, handler->oname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (handler->handler != &gen_invalid) {
|
|
||||||
printf("INSN: %02x -- -- (%02d ----) : %s\n",
|
|
||||||
opc1, opc1, handler->oname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
static int ppc_fixup_cpu(PowerPCCPU *cpu)
|
|
||||||
{
|
|
||||||
CPUPPCState *env = &cpu->env;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TCG doesn't (yet) emulate some groups of instructions that are
|
|
||||||
* implemented on some otherwise supported CPUs (e.g. VSX and
|
|
||||||
* decimal floating point instructions on POWER7). We remove
|
|
||||||
* unsupported instruction groups from the cpu state's instruction
|
|
||||||
* masks and hope the guest can cope. For at least the pseries
|
|
||||||
* machine, the unavailability of these instructions can be
|
|
||||||
* advertised to the guest via the device tree.
|
|
||||||
*/
|
|
||||||
if ((env->insns_flags & ~PPC_TCG_INSNS)
|
|
||||||
|| (env->insns_flags2 & ~PPC_TCG_INSNS2)) {
|
|
||||||
warn_report("Disabling some instructions which are not "
|
|
||||||
"emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")",
|
|
||||||
env->insns_flags & ~PPC_TCG_INSNS,
|
|
||||||
env->insns_flags2 & ~PPC_TCG_INSNS2);
|
|
||||||
}
|
|
||||||
env->insns_flags &= PPC_TCG_INSNS;
|
|
||||||
env->insns_flags2 &= PPC_TCG_INSNS2;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ppc_cpu_realize(DeviceState *dev, Error **errp)
|
static void ppc_cpu_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(dev);
|
CPUState *cs = CPU(dev);
|
||||||
@ -10131,40 +9770,12 @@ static void ppc_cpu_unrealize(DeviceState *dev)
|
|||||||
{
|
{
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(dev);
|
PowerPCCPU *cpu = POWERPC_CPU(dev);
|
||||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||||
opc_handler_t **table, **table_2;
|
|
||||||
int i, j, k;
|
|
||||||
|
|
||||||
pcc->parent_unrealize(dev);
|
pcc->parent_unrealize(dev);
|
||||||
|
|
||||||
cpu_remove_sync(CPU(cpu));
|
cpu_remove_sync(CPU(cpu));
|
||||||
|
|
||||||
for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) {
|
destroy_ppc_opcodes(cpu);
|
||||||
if (cpu->opcodes[i] == &invalid_handler) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_indirect_opcode(cpu->opcodes[i])) {
|
|
||||||
table = ind_table(cpu->opcodes[i]);
|
|
||||||
for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) {
|
|
||||||
if (table[j] == &invalid_handler) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_indirect_opcode(table[j])) {
|
|
||||||
table_2 = ind_table(table[j]);
|
|
||||||
for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) {
|
|
||||||
if (table_2[k] != &invalid_handler &&
|
|
||||||
is_indirect_opcode(table_2[k])) {
|
|
||||||
g_free((opc_handler_t *)((uintptr_t)table_2[k] &
|
|
||||||
~PPC_INDIRECT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_free((opc_handler_t *)((uintptr_t)table[j] &
|
|
||||||
~PPC_INDIRECT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_free((opc_handler_t *)((uintptr_t)cpu->opcodes[i] &
|
|
||||||
~PPC_INDIRECT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b)
|
static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b)
|
||||||
|
Loading…
Reference in New Issue
Block a user