diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-03-01 21:10:16 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-03-01 21:10:16 +0300 |
commit | b286cedd473006b33d5ae076afac509e6b2c3bf4 (patch) | |
tree | f45cbb0337238771d6ebe2220f9d5b4f02771903 /arch/powerpc/xmon/ppc-dis.c | |
parent | 522214d9be9c9f00f34ed89cb95e901b7ac31c59 (diff) | |
parent | 9f3768e02335ddd6ebe1d85d5cb3a68ee6264004 (diff) | |
download | linux-b286cedd473006b33d5ae076afac509e6b2c3bf4.tar.xz |
Merge tag 'powerpc-4.11-2' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull more powerpc updates from Michael Ellerman:
"Highlights include:
- an update of the disassembly code used by xmon to the latest
versions in binutils. We've received permission from all the
authors of the relevant binutils changes to relicense their changes
to the relevant files from GPLv3 to GPLv2, for inclusion in Linux.
Thanks to Peter Bergner for doing the leg work to get permission
from everyone.
- addition of the "architected" Power9 CPU table entry, allowing us
to boot in Power9 architected mode under a hypervisor.
- updates to the Power9 PMU code.
- implementation of clear_bit_unlock_is_negative_byte() to optimise
unlock_page().
- Freescale updates from Scott: "Highlights include 8xx breakpoints
and perf, t1042rdb display support, and board updates."
Thanks to:
Al Viro, Andrew Donnellan, Aneesh Kumar K.V, Balbir Singh, Douglas
Miller, Frédéric Weisbecker, Gavin Shan, Madhavan Srinivasan,
Michael Roth, Nathan Fontenot, Naveen N. Rao, Nicholas Piggin, Peter
Bergner, Paul E. McKenney, Rashmica Gupta, Russell Currey, Sahil
Mehta, Stewart Smith"
* tag 'powerpc-4.11-2' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (48 commits)
powerpc: Remove leftover cputime_to_nsecs call causing build error
powerpc/mm/hash: Always clear UPRT and Host Radix bits when setting up CPU
powerpc/optprobes: Fix TOC handling in optprobes trampoline
powerpc/pseries: Advertise Hot Plug Event support to firmware
cxl: fix nested locking hang during EEH hotplug
powerpc/xmon: Dump memory in CPU endian format
powerpc/pseries: Revert 'Auto-online hotplugged memory'
powerpc/powernv: Make PCI non-optional
powerpc/64: Implement clear_bit_unlock_is_negative_byte()
powerpc/powernv: Remove unused variable in pnv_pci_sriov_disable()
powerpc/kernel: Remove error message in pcibios_setup_phb_resources()
powerpc/mm: Fix typo in set_pte_at()
pci/hotplug/pnv-php: Disable MSI and PCI device properly
pci/hotplug/pnv-php: Disable surprise hotplug capability on conflicts
pci/hotplug/pnv-php: Remove WARN_ON() in pnv_php_put_slot()
powerpc: Add POWER9 architected mode to cputable
powerpc/perf: use is_kernel_addr macro in perf_get_misc_flags()
powerpc/perf: Avoid FAB_*_MATCH checks for power9
powerpc/perf: Add restrictions to PMC5 in power9 DD1
powerpc/perf: Use Instruction Counter value
...
Diffstat (limited to 'arch/powerpc/xmon/ppc-dis.c')
-rw-r--r-- | arch/powerpc/xmon/ppc-dis.c | 260 |
1 files changed, 182 insertions, 78 deletions
diff --git a/arch/powerpc/xmon/ppc-dis.c b/arch/powerpc/xmon/ppc-dis.c index ee9891734149..31db8c072acd 100644 --- a/arch/powerpc/xmon/ppc-dis.c +++ b/arch/powerpc/xmon/ppc-dis.c @@ -1,6 +1,5 @@ /* ppc-dis.c -- Disassemble PowerPC instructions - Copyright 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1994-2016 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support This file is part of GDB, GAS, and the GNU binutils. @@ -26,57 +25,94 @@ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, US #include "ppc.h" #include "dis-asm.h" -/* Print a PowerPC or POWER instruction. */ +/* This file provides several disassembler functions, all of which use + the disassembler interface defined in dis-asm.h. Several functions + are provided because this file handles disassembly for the PowerPC + in both big and little endian mode and also for the POWER (RS/6000) + chip. */ + +/* Extract the operand value from the PowerPC or POWER instruction. */ -int -print_insn_powerpc (unsigned long insn, unsigned long memaddr) +static long +operand_value_powerpc (const struct powerpc_operand *operand, + unsigned long insn, ppc_cpu_t dialect) { - const struct powerpc_opcode *opcode; - const struct powerpc_opcode *opcode_end; - unsigned long op; - int dialect; + long value; + int invalid; + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, dialect, &invalid); + else + { + if (operand->shift >= 0) + value = (insn >> operand->shift) & operand->bitm; + else + value = (insn << -operand->shift) & operand->bitm; + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + { + /* BITM is always some number of zeros followed by some + number of ones, followed by some number of zeros. */ + unsigned long top = operand->bitm; + /* top & -top gives the rightmost 1 bit, so this + fills in any trailing zeros. */ + top |= (top & -top) - 1; + top &= ~(top >> 1); + value = (value ^ top) - top; + } + } - dialect = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_COMMON - | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_ALTIVEC; + return value; +} - if (cpu_has_feature(CPU_FTRS_POWER5)) - dialect |= PPC_OPCODE_POWER5; +/* Determine whether the optional operand(s) should be printed. */ - if (cpu_has_feature(CPU_FTRS_CELL)) - dialect |= PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC; +static int +skip_optional_operands (const unsigned char *opindex, + unsigned long insn, ppc_cpu_t dialect) +{ + const struct powerpc_operand *operand; - if (cpu_has_feature(CPU_FTRS_POWER6)) - dialect |= PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC; + for (; *opindex != 0; opindex++) + { + operand = &powerpc_operands[*opindex]; + if ((operand->flags & PPC_OPERAND_NEXT) != 0 + || ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && operand_value_powerpc (operand, insn, dialect) != + ppc_optional_operand_value (operand))) + return 0; + } + + return 1; +} + +/* Find a match for INSN in the opcode table, given machine DIALECT. + A DIALECT of -1 is special, matching all machine opcode variations. */ + +static const struct powerpc_opcode * +lookup_powerpc (unsigned long insn, ppc_cpu_t dialect) +{ + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; /* Get the major opcode of the instruction. */ op = PPC_OP (insn); - /* Find the first match in the opcode table. We could speed this up - a bit by doing a binary search on the major opcode. */ opcode_end = powerpc_opcodes + powerpc_num_opcodes; - again: - for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + /* Find the first match in the opcode table for this major opcode. */ + for (opcode = powerpc_opcodes; opcode < opcode_end; ++opcode) { - unsigned long table_op; const unsigned char *opindex; const struct powerpc_operand *operand; int invalid; - int need_comma; - int need_paren; - - table_op = PPC_OP (opcode->opcode); - if (op < table_op) - break; - if (op > table_op) - continue; if ((insn & opcode->mask) != opcode->opcode - || (opcode->flags & dialect) == 0) + || (dialect != (ppc_cpu_t) -1 + && ((opcode->flags & dialect) == 0 + || (opcode->deprecated & dialect) != 0))) continue; - /* Make two passes over the operands. First see if any of them - have extraction functions, and, if they do, make sure the - instruction is valid. */ + /* Check validity of operands. */ invalid = 0; for (opindex = opcode->operands; *opindex != 0; opindex++) { @@ -87,14 +123,77 @@ print_insn_powerpc (unsigned long insn, unsigned long memaddr) if (invalid) continue; - /* The instruction is valid. */ - printf("%s", opcode->name); + return opcode; + } + + return NULL; +} + +/* Print a PowerPC or POWER instruction. */ + +int print_insn_powerpc (unsigned long insn, unsigned long memaddr) +{ + const struct powerpc_opcode *opcode; + bool insn_is_short; + ppc_cpu_t dialect; + + dialect = PPC_OPCODE_PPC | PPC_OPCODE_COMMON + | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_ALTIVEC; + + if (cpu_has_feature(CPU_FTRS_POWER5)) + dialect |= PPC_OPCODE_POWER5; + + if (cpu_has_feature(CPU_FTRS_CELL)) + dialect |= (PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC); + + if (cpu_has_feature(CPU_FTRS_POWER6)) + dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC); + + if (cpu_has_feature(CPU_FTRS_POWER7)) + dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 + | PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX); + + if (cpu_has_feature(CPU_FTRS_POWER8)) + dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 + | PPC_OPCODE_POWER8 | PPC_OPCODE_HTM + | PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 | PPC_OPCODE_VSX); + + if (cpu_has_feature(CPU_FTRS_POWER9)) + dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 + | PPC_OPCODE_POWER8 | PPC_OPCODE_POWER9 | PPC_OPCODE_HTM + | PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 + | PPC_OPCODE_VSX | PPC_OPCODE_VSX3), + + /* Get the major opcode of the insn. */ + opcode = NULL; + insn_is_short = false; + + if (opcode == NULL) + opcode = lookup_powerpc (insn, dialect); + if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0) + opcode = lookup_powerpc (insn, (ppc_cpu_t) -1); + + if (opcode != NULL) + { + const unsigned char *opindex; + const struct powerpc_operand *operand; + int need_comma; + int need_paren; + int skip_optional; + if (opcode->operands[0] != 0) - printf("\t"); + printf("%-7s ", opcode->name); + else + printf("%s", opcode->name); + + if (insn_is_short) + /* The operands will be fetched out of the 16-bit instruction. */ + insn >>= 16; /* Now extract and print the operands. */ need_comma = 0; need_paren = 0; + skip_optional = -1; for (opindex = opcode->operands; *opindex != 0; opindex++) { long value; @@ -107,23 +206,18 @@ print_insn_powerpc (unsigned long insn, unsigned long memaddr) if ((operand->flags & PPC_OPERAND_FAKE) != 0) continue; - /* Extract the value from the instruction. */ - if (operand->extract) - value = (*operand->extract) (insn, dialect, &invalid); - else + /* If all of the optional operands have the value zero, + then don't print any of them. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) { - value = (insn >> operand->shift) & ((1 << operand->bits) - 1); - if ((operand->flags & PPC_OPERAND_SIGNED) != 0 - && (value & (1 << (operand->bits - 1))) != 0) - value -= 1 << operand->bits; + if (skip_optional < 0) + skip_optional = skip_optional_operands (opindex, insn, + dialect); + if (skip_optional) + continue; } - /* If the operand is optional, and the value is zero, don't - print anything. */ - if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 - && (operand->flags & PPC_OPERAND_NEXT) == 0 - && value == 0) - continue; + value = operand_value_powerpc (operand, insn, dialect); if (need_comma) { @@ -139,30 +233,38 @@ print_insn_powerpc (unsigned long insn, unsigned long memaddr) printf("f%ld", value); else if ((operand->flags & PPC_OPERAND_VR) != 0) printf("v%ld", value); + else if ((operand->flags & PPC_OPERAND_VSR) != 0) + printf("vs%ld", value); else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) - print_address (memaddr + value); + print_address(memaddr + value); else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) - print_address (value & 0xffffffff); - else if ((operand->flags & PPC_OPERAND_CR) == 0 - || (dialect & PPC_OPCODE_PPC) == 0) + print_address(value & 0xffffffff); + else if ((operand->flags & PPC_OPERAND_FSL) != 0) + printf("fsl%ld", value); + else if ((operand->flags & PPC_OPERAND_FCR) != 0) + printf("fcr%ld", value); + else if ((operand->flags & PPC_OPERAND_UDI) != 0) printf("%ld", value); - else + else if ((operand->flags & PPC_OPERAND_CR_REG) != 0 + && (((dialect & PPC_OPCODE_PPC) != 0) + || ((dialect & PPC_OPCODE_VLE) != 0))) + printf("cr%ld", value); + else if (((operand->flags & PPC_OPERAND_CR_BIT) != 0) + && (((dialect & PPC_OPCODE_PPC) != 0) + || ((dialect & PPC_OPCODE_VLE) != 0))) { - if (operand->bits == 3) - printf("cr%ld", value); - else - { - static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; - int cr; - int cc; - - cr = value >> 2; - if (cr != 0) - printf("4*cr%d+", cr); - cc = value & 3; - printf("%s", cbnames[cc]); - } + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + printf("4*cr%d+", cr); + cc = value & 3; + printf("%s", cbnames[cc]); } + else + printf("%d", (int) value); if (need_paren) { @@ -179,14 +281,16 @@ print_insn_powerpc (unsigned long insn, unsigned long memaddr) } } - /* We have found and printed an instruction; return. */ - return 4; - } - - if ((dialect & PPC_OPCODE_ANY) != 0) - { - dialect = ~PPC_OPCODE_ANY; - goto again; + /* We have found and printed an instruction. + If it was a short VLE instruction we have more to do. */ + if (insn_is_short) + { + memaddr += 2; + return 2; + } + else + /* Otherwise, return. */ + return 4; } /* We could not find a match. */ |