mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-06 12:09:26 +08:00
8d619c01db
This patch adds support for Hardware Transactional Memory registers for the powerpc linux native and core file targets, and for the pwoerpc linux server stub. These registers include both the HTM special-purpose registers (TFHAR, TEXASR and TFIAR) as well as the set of registers that are checkpointed (saved) when a transaction is initiated, which the processor restores in the event of a transaction failure. The set of checkpointed general-purpose registers is returned by the linux kernel in the same format as the regular general-purpose registers, defined in struct pt_regs. However, the architecture specifies that only some of the registers present in pt_regs are checkpointed (GPRs 0-31, CR, XER, LR and CTR). The kernel fills the slots for MSR and NIP with other info. The other fields usually don't have meaningful values. GDB doesn't define registers that are not checkpointed in the architecture, but when generating a core file, GDB fills the slot for the checkpointed MSR with the regular MSR. These are usually similar, although some bits might be different, and in some cases the checkpointed MSR will have a value of 0 in a kernel-generated core-file. The checkpointed NIP is filled with TFHAR by GDB in the core-file, which is what the kernel does. The other fields are set to 0 by GDB. Core files generated by the kernel have a note section for checkpointed GPRs with the same size for both 32-bit and 64-bit threads, and the values for the registers of a 32-bit thread are squeezed in the first half, with no useful data in the second half. GDB generates a smaller note section for 32-bit threads, but can read both sizes. The checkpointed XER is required to be 32-bit in the target description documentation, even though the more recent ISAs define it as 64-bit wide, since the high-order 32-bits are reserved, and because in Linux there is no way to get a 64-bit checkpointed XER for 32-bit threads. If this changes in the future, the target description feature requirement can be relaxed to allow for a 64-bit checkpointed XER. Access to the checkpointed CR (condition register) can be confusing. The architecture only specifies that CR fields 1 to 7 (the 24 least significant bits) are checkpointed, but the kernel provides all 8 fields (32 bits). The value of field 0 is not masked by ptrace, so it will sometimes show the result of some kernel operation, probably treclaim., which sets this field. The checkpointed registers are marked not to be saved and restored. Inferior function calls during an active transaction don't work well, and it's unclear what should be done in this case. TEXASR and TFIAR can be altered asynchronously, during transaction failure recording, so they are also not saved and restored. For consistency neither is TFHAR. Record and replay also doesn't work well when transactions are involved. This patch doesn't address this, so the values of the HTM SPRs will sometimes be innacurate when the record/relay target is enabled. For instance, executing a "tbegin." alters TFHAR and TEXASR, but these changes are not currently recorded. Because the checkpointed registers are only available when a transaction is active (or suspended), ptrace can return ENODATA when gdb tries to read these registers and the inferior is not in a transactional state. The registers are set to the unavailable state when this happens. When gbd tries to write to one of these registers, and it is unavailable, an error is raised. The "fill" functions for checkpointed register sets in the server stub are not implemented for the same reason as for the EBB register set, since ptrace can also return ENODATA for checkpointed regsets. The same issues with 'G' packets apply here. Just like for the EBB registers, tracepoints will not mark the checkpointed registers as unavailable if the inferior was not in a transaction, so their content will also show 0 instead of <unavailable> when inspecting trace data. The new tests record the values of the regular registers before stepping the inferior through a "tbegin." instruction to start a transaction, then the checkpointed registers are checked against the recorded pre-transactional values. New values are written to the checkpointed registers and recorded, the inferior continues until the transaction aborts (which is usually immediately when it is resumed), and the regular registers are checked against the recorded values, because the abort should have reverted the registers to these values. Like for the EBB registers, target_store_registers will ignore the checkpointed registers when called with -1 as the regno argument (store all registers in one go). gdb/ChangeLog: 2018-10-26 Edjunior Barbosa Machado <emachado@linux.vnet.ibm.com> Pedro Franco de Carvalho <pedromfc@linux.ibm.com> * arch/ppc-linux-tdesc.h (tdesc_powerpc_isa207_htm_vsx32l) (tdesc_powerpc_isa207_htm_vsx64l): Declare. * arch/ppc-linux-common.h (PPC_LINUX_SIZEOF_TM_SPRREGSET) (PPC32_LINUX_SIZEOF_CGPRREGSET, PPC64_LINUX_SIZEOF_CGPRREGSET) (PPC_LINUX_SIZEOF_CFPRREGSET, PPC_LINUX_SIZEOF_CVMXREGSET) (PPC_LINUX_SIZEOF_CVSXREGSET, PPC_LINUX_SIZEOF_CPPRREGSET) (PPC_LINUX_SIZEOF_CDSCRREGSET, PPC_LINUX_SIZEOF_CTARREGSET): Define. (struct ppc_linux_features) <htm>: New field. (ppc_linux_no_features): Add initializer for htm field. * arch/ppc-linux-common.c (ppc_linux_match_description): Return new tdescs. * nat/ppc-linux.h (PPC_FEATURE2_HTM, NT_PPC_TM_CGPR) (NT_PPC_TM_CFPR, NT_PPC_TM_CVMX, NT_PPC_TM_CVSX) (NT_PPC_TM_SPR, NT_PPC_TM_CTAR, NT_PPC_TM_CPPR, NT_PPC_TM_CDSCR): Define if not already defined. * features/Makefile (WHICH): Add rs6000/powerpc-isa207-htm-vsx32l and rs6000/powerpc-isa207-htm-vsx64l. (XMLTOC): Add rs6000/powerpc-isa207-htm-vsx32l.xml and rs6000/powerpc-isa207-htm-vsx64l.xml. * features/rs6000/power-htm-spr.xml: New file. * features/rs6000/power-htm-core.xml: New file. * features/rs6000/power64-htm-core.xml: New file. * features/rs6000/power-htm-fpu.xml: New file. * features/rs6000/power-htm-altivec.xml: New file. * features/rs6000/power-htm-vsx.xml: New file. * features/rs6000/power-htm-ppr.xml: New file. * features/rs6000/power-htm-dscr.xml: New file. * features/rs6000/power-htm-tar.xml: New file. * features/rs6000/powerpc-isa207-htm-vsx32l.xml: New file. * features/rs6000/powerpc-isa207-htm-vsx64l.xml: New file. * features/rs6000/powerpc-isa207-htm-vsx32l.c: Generate. * features/rs6000/powerpc-isa207-htm-vsx64l.c: Generate. * regformats/rs6000/powerpc-isa207-htm-vsx32l.dat: Generate. * regformats/rs6000/powerpc-isa207-htm-vsx64l.dat: Generate. * ppc-linux-nat.c (fetch_register, fetch_ppc_registers): Call fetch_regset with HTM regsets. (store_register, store_ppc_registers): Call store_regset with HTM regsets. (ppc_linux_nat_target::read_description): Set htm field in the features struct if needed. * ppc-linux-tdep.c: Include features/rs6000/powerpc-isa207-htm-vsx32l.c and features/rs6000/powerpc-isa207-htm-vsx64l.c. (ppc32_regmap_tm_spr, ppc32_regmap_cgpr, ppc64_le_regmap_cgpr) (ppc64_be_regmap_cgpr, ppc32_regmap_cfpr, ppc32_le_regmap_cvmx) (ppc32_be_regmap_cvmx, ppc32_regmap_cvsx, ppc32_regmap_cppr) (ppc32_regmap_cdscr, ppc32_regmap_ctar): New globals. (ppc32_linux_tm_sprregset, ppc32_linux_cgprregset) (ppc64_be_linux_cgprregset, ppc64_le_linux_cgprregset) (ppc32_linux_cfprregset, ppc32_le_linux_cvmxregset) (ppc32_be_linux_cvmxregset, ppc32_linux_cvsxregset) (ppc32_linux_cpprregset, ppc32_linux_cdscrregset) (ppc32_linux_ctarregset): New globals. (ppc_linux_cgprregset, ppc_linux_cvmxregset): New functions. (ppc_linux_collect_core_cpgrregset): New function. (ppc_linux_iterate_over_regset_sections): Call back with the htm regsets. (ppc_linux_core_read_description): Check if the tm spr section is present and set htm in the features struct. (_initialize_ppc_linux_tdep): Call initialize_tdesc_powerpc_isa207_htm_vsx32l and initialize_tdesc_powerpc_isa207_htm_vsx64l. * ppc-linux-tdep.h (ppc_linux_cgprregset, ppc_linux_cvmxregset): Declare. (ppc32_linux_tm_sprregset, ppc32_linux_cfprregset) (ppc32_linux_cvsxregset, ppc32_linux_cpprregset) (ppc32_linux_cdscrregset, ppc32_linux_ctarregset): Declare. * ppc-tdep.h (struct gdbarch_tdep) <have_htm_spr, have_htm_core>: New fields. <have_htm_fpu, have_htm_altivec, have_htm_vsx>: Likewise. <ppc_cppr_regnum, ppc_cdscr_regnum, ppc_ctar_regnum>: Likewise. <ppc_cdl0_regnum, ppc_cvsr0_regnum, ppc_cefpr0_regnum>: Likewise. (enum) <PPC_TFHAR_REGNUM, PPC_TEXASR_REGNUM, PPC_TFIAR_REGNUM>: New enum fields. <PPC_CR0_REGNUM, PPC_CCR_REGNUM, PPC_CXER_REGNUM>: Likewise. <PPC_CLR_REGNUM, PPC_CCTR_REGNUM, PPC_CF0_REGNUM>: Likewise. <PPC_CFPSCR_REGNUM, PPC_CVR0_REGNUM, PPC_CVSCR_REGNUM>: Likewise. <PPC_CVRSAVE_REGNUM, PPC_CVSR0_UPPER_REGNUM>: Likewise. <PPC_CPPR_REGNUM, PPC_CDSCR_REGNUM>: Likewise. <PPC_CTAR_REGNUM>: Likewise. (PPC_IS_TMSPR_REGNUM, PPC_IS_CKPTGP_REGNUM, PPC_IS_CKPTFP_REGNUM) (PPC_IS_CKPTVMX_REGNUM, PPC_IS_CKPTVSX_REGNUM): Define. * rs6000-tdep.c (IS_CDFP_PSEUDOREG, IS_CVSX_PSEUDOREG) (IS_CEFP_PSEUDOREG): Define. (rs6000_register_name): Hide the upper halves of checkpointed VSX registers. Return names for the checkpointed DFP, VSX, and EFP pseudo registers. (rs6000_pseudo_register_type): Remove initial assert and raise an internal error in the else clause instead. Return types for the checkpointed DFP, VSX, and EFP pseudo registers. (dfp_pseudo_register_read, dfp_pseudo_register_write): Handle checkpointed DFP pseudo registers. (vsx_pseudo_register_read, vsx_pseudo_register_write): Handle checkpointed VSX pseudo registers. (efp_pseudo_register_read, efp_pseudo_register_write): Rename from efpr_pseudo_register_read and efpr_pseudo_register_write. Handle checkpointed EFP pseudo registers. (rs6000_pseudo_register_read, rs6000_pseudo_register_write): Handle checkpointed DFP, VSX, and EFP registers. (dfp_ax_pseudo_register_collect, vsx_ax_pseudo_register_collect) (efp_ax_pseudo_register_collect): New functions. (rs6000_ax_pseudo_register_collect): Move DFP, VSX and EFP pseudo register logic to new functions. Handle checkpointed DFP, VSX, and EFP pseudo registers. (rs6000_gdbarch_init): Look for and validate the htm features. Include checkpointed DFP, VSX and EFP pseudo-registers. * NEWS: Mention access to PPR, DSCR, TAR, EBB/PMU registers and HTM registers. gdb/gdbserver/ChangeLog: 2018-10-26 Pedro Franco de Carvalho <pedromfc@linux.ibm.com> * configure.srv (ipa_ppc_linux_regobj): Add powerpc-isa207-htm-vsx32l-ipa.o and powerpc-isa207-htm-vsx64l-ipa.o. (powerpc*-*-linux*): Add powerpc-isa207-htm-vsx32l.o and powerpc-isa207-htm-vsx64l.o to srv_regobj. Add rs6000/power-htm-spr.xml, rs6000/power-htm-core.xml, rs6000/power64-htm-core.xml, rs6000/power-htm-fpu.xml, rs6000/power-htm-altivec.xml, rs6000/power-htm-vsx.xml, rs6000/power-htm-ppr.xml, rs6000/power-htm-dscr.xml, rs6000/power-htm-tar.xml, rs6000/powerpc-isa207-htm-vsx32l.xml, and rs6000/powerpc-isa207-htm-vsx64l.xml to srv_xmlfiles. * linux-ppc-tdesc-init.h (enum ppc_linux_tdesc) <PPC_TDESC_ISA207_HTM_VSX>: New enum value. (init_registers_powerpc_isa207_htm_vsx32l) (init_registers_powerpc_isa207_htm_vsx64l): Declare. * linux-ppc-low.c (ppc_fill_tm_sprregset, ppc_store_tm_sprregset) (ppc_store_tm_cgprregset, ppc_store_tm_cfprregset) (ppc_store_tm_cvrregset, ppc_store_tm_cvsxregset) (ppc_store_tm_cpprregset, ppc_store_tm_cdscrregset) (ppc_store_tm_ctarregset): New functions. (ppc_regsets): Add entries for HTM regsets. (ppc_arch_setup): Set htm in features struct when needed. Set sizes for the HTM regsets. (ppc_get_ipa_tdesc_idx): Return PPC_TDESC_ISA207_HTM_VSX. (initialize_low_arch): Call init_registers_powerpc_isa207_htm_vsx32l and init_registers_powerpc_isa207_htm_vsx64l. * linux-ppc-ipa.c (get_ipa_tdesc): Handle PPC_TDESC_ISA207_HTM_VSX. (initialize_low_tracepoint): Call init_registers_powerpc_isa207_htm_vsx32l and init_registers_powerpc_isa207_htm_vsx64l. gdb/testsuite/ChangeLog: 2018-10-26 Pedro Franco de Carvalho <pedromfc@linux.ibm.com> * gdb.arch/powerpc-htm-regs.c: New file. * gdb.arch/powerpc-htm-regs.exp: New file. gdb/doc/ChangeLog: 2018-10-26 Pedro Franco de Carvalho <pedromfc@linux.ibm.com> * gdb.texinfo (PowerPC Features): Describe new features "org.gnu.gdb.power.htm.spr", "org.gnu.gdb.power.htm.core", "org.gnu.gdb.power.htm.fpu", "org.gnu.gdb.power.htm.altivec", "org.gnu.gdb.power.htm.vsx", "org.gnu.gdb.power.htm.ppr", "org.gnu.gdb.power.htm.dscr", "org.gnu.gdb.power.htm.tar".
3567 lines
90 KiB
C
3567 lines
90 KiB
C
/* GNU/Linux/PowerPC specific low level interface, for the remote server for
|
|
GDB.
|
|
Copyright (C) 1995-2018 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "server.h"
|
|
#include "linux-low.h"
|
|
|
|
#include "elf/common.h"
|
|
#include <sys/uio.h>
|
|
#include <elf.h>
|
|
#include <asm/ptrace.h>
|
|
|
|
#include "arch/ppc-linux-common.h"
|
|
#include "arch/ppc-linux-tdesc.h"
|
|
#include "nat/ppc-linux.h"
|
|
#include "linux-ppc-tdesc-init.h"
|
|
#include "ax.h"
|
|
#include "tracepoint.h"
|
|
|
|
#define PPC_FIELD(value, from, len) \
|
|
(((value) >> (32 - (from) - (len))) & ((1 << (len)) - 1))
|
|
#define PPC_SEXT(v, bs) \
|
|
((((CORE_ADDR) (v) & (((CORE_ADDR) 1 << (bs)) - 1)) \
|
|
^ ((CORE_ADDR) 1 << ((bs) - 1))) \
|
|
- ((CORE_ADDR) 1 << ((bs) - 1)))
|
|
#define PPC_OP6(insn) PPC_FIELD (insn, 0, 6)
|
|
#define PPC_BO(insn) PPC_FIELD (insn, 6, 5)
|
|
#define PPC_LI(insn) (PPC_SEXT (PPC_FIELD (insn, 6, 24), 24) << 2)
|
|
#define PPC_BD(insn) (PPC_SEXT (PPC_FIELD (insn, 16, 14), 14) << 2)
|
|
|
|
/* Holds the AT_HWCAP auxv entry. */
|
|
|
|
static unsigned long ppc_hwcap;
|
|
|
|
/* Holds the AT_HWCAP2 auxv entry. */
|
|
|
|
static unsigned long ppc_hwcap2;
|
|
|
|
|
|
#define ppc_num_regs 73
|
|
|
|
#ifdef __powerpc64__
|
|
/* We use a constant for FPSCR instead of PT_FPSCR, because
|
|
many shipped PPC64 kernels had the wrong value in ptrace.h. */
|
|
static int ppc_regmap[] =
|
|
{PT_R0 * 8, PT_R1 * 8, PT_R2 * 8, PT_R3 * 8,
|
|
PT_R4 * 8, PT_R5 * 8, PT_R6 * 8, PT_R7 * 8,
|
|
PT_R8 * 8, PT_R9 * 8, PT_R10 * 8, PT_R11 * 8,
|
|
PT_R12 * 8, PT_R13 * 8, PT_R14 * 8, PT_R15 * 8,
|
|
PT_R16 * 8, PT_R17 * 8, PT_R18 * 8, PT_R19 * 8,
|
|
PT_R20 * 8, PT_R21 * 8, PT_R22 * 8, PT_R23 * 8,
|
|
PT_R24 * 8, PT_R25 * 8, PT_R26 * 8, PT_R27 * 8,
|
|
PT_R28 * 8, PT_R29 * 8, PT_R30 * 8, PT_R31 * 8,
|
|
PT_FPR0*8, PT_FPR0*8 + 8, PT_FPR0*8+16, PT_FPR0*8+24,
|
|
PT_FPR0*8+32, PT_FPR0*8+40, PT_FPR0*8+48, PT_FPR0*8+56,
|
|
PT_FPR0*8+64, PT_FPR0*8+72, PT_FPR0*8+80, PT_FPR0*8+88,
|
|
PT_FPR0*8+96, PT_FPR0*8+104, PT_FPR0*8+112, PT_FPR0*8+120,
|
|
PT_FPR0*8+128, PT_FPR0*8+136, PT_FPR0*8+144, PT_FPR0*8+152,
|
|
PT_FPR0*8+160, PT_FPR0*8+168, PT_FPR0*8+176, PT_FPR0*8+184,
|
|
PT_FPR0*8+192, PT_FPR0*8+200, PT_FPR0*8+208, PT_FPR0*8+216,
|
|
PT_FPR0*8+224, PT_FPR0*8+232, PT_FPR0*8+240, PT_FPR0*8+248,
|
|
PT_NIP * 8, PT_MSR * 8, PT_CCR * 8, PT_LNK * 8,
|
|
PT_CTR * 8, PT_XER * 8, PT_FPR0*8 + 256,
|
|
PT_ORIG_R3 * 8, PT_TRAP * 8 };
|
|
#else
|
|
/* Currently, don't check/send MQ. */
|
|
static int ppc_regmap[] =
|
|
{PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
|
|
PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
|
|
PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
|
|
PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
|
|
PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
|
|
PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
|
|
PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
|
|
PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
|
|
PT_FPR0*4, PT_FPR0*4 + 8, PT_FPR0*4+16, PT_FPR0*4+24,
|
|
PT_FPR0*4+32, PT_FPR0*4+40, PT_FPR0*4+48, PT_FPR0*4+56,
|
|
PT_FPR0*4+64, PT_FPR0*4+72, PT_FPR0*4+80, PT_FPR0*4+88,
|
|
PT_FPR0*4+96, PT_FPR0*4+104, PT_FPR0*4+112, PT_FPR0*4+120,
|
|
PT_FPR0*4+128, PT_FPR0*4+136, PT_FPR0*4+144, PT_FPR0*4+152,
|
|
PT_FPR0*4+160, PT_FPR0*4+168, PT_FPR0*4+176, PT_FPR0*4+184,
|
|
PT_FPR0*4+192, PT_FPR0*4+200, PT_FPR0*4+208, PT_FPR0*4+216,
|
|
PT_FPR0*4+224, PT_FPR0*4+232, PT_FPR0*4+240, PT_FPR0*4+248,
|
|
PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
|
|
PT_CTR * 4, PT_XER * 4, PT_FPSCR * 4,
|
|
PT_ORIG_R3 * 4, PT_TRAP * 4
|
|
};
|
|
|
|
static int ppc_regmap_e500[] =
|
|
{PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
|
|
PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
|
|
PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
|
|
PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
|
|
PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
|
|
PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
|
|
PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
|
|
PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
-1, -1, -1, -1,
|
|
PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
|
|
PT_CTR * 4, PT_XER * 4, -1,
|
|
PT_ORIG_R3 * 4, PT_TRAP * 4
|
|
};
|
|
#endif
|
|
|
|
/* Check whether the kernel provides a register set with number
|
|
REGSET_ID of size REGSETSIZE for process/thread TID. */
|
|
|
|
static int
|
|
ppc_check_regset (int tid, int regset_id, int regsetsize)
|
|
{
|
|
void *buf = alloca (regsetsize);
|
|
struct iovec iov;
|
|
|
|
iov.iov_base = buf;
|
|
iov.iov_len = regsetsize;
|
|
|
|
if (ptrace (PTRACE_GETREGSET, tid, regset_id, &iov) >= 0
|
|
|| errno == ENODATA)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ppc_cannot_store_register (int regno)
|
|
{
|
|
const struct target_desc *tdesc = current_process ()->tdesc;
|
|
|
|
#ifndef __powerpc64__
|
|
/* Some kernels do not allow us to store fpscr. */
|
|
if (!(ppc_hwcap & PPC_FEATURE_HAS_SPE)
|
|
&& regno == find_regno (tdesc, "fpscr"))
|
|
return 2;
|
|
#endif
|
|
|
|
/* Some kernels do not allow us to store orig_r3 or trap. */
|
|
if (regno == find_regno (tdesc, "orig_r3")
|
|
|| regno == find_regno (tdesc, "trap"))
|
|
return 2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ppc_cannot_fetch_register (int regno)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ppc_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
|
|
{
|
|
memset (buf, 0, sizeof (long));
|
|
|
|
if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
{
|
|
/* Little-endian values always sit at the left end of the buffer. */
|
|
collect_register (regcache, regno, buf);
|
|
}
|
|
else if (__BYTE_ORDER == __BIG_ENDIAN)
|
|
{
|
|
/* Big-endian values sit at the right end of the buffer. In case of
|
|
registers whose sizes are smaller than sizeof (long), we must use a
|
|
padding to access them correctly. */
|
|
int size = register_size (regcache->tdesc, regno);
|
|
|
|
if (size < sizeof (long))
|
|
collect_register (regcache, regno, buf + sizeof (long) - size);
|
|
else
|
|
collect_register (regcache, regno, buf);
|
|
}
|
|
else
|
|
perror_with_name ("Unexpected byte order");
|
|
}
|
|
|
|
static void
|
|
ppc_supply_ptrace_register (struct regcache *regcache,
|
|
int regno, const char *buf)
|
|
{
|
|
if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
{
|
|
/* Little-endian values always sit at the left end of the buffer. */
|
|
supply_register (regcache, regno, buf);
|
|
}
|
|
else if (__BYTE_ORDER == __BIG_ENDIAN)
|
|
{
|
|
/* Big-endian values sit at the right end of the buffer. In case of
|
|
registers whose sizes are smaller than sizeof (long), we must use a
|
|
padding to access them correctly. */
|
|
int size = register_size (regcache->tdesc, regno);
|
|
|
|
if (size < sizeof (long))
|
|
supply_register (regcache, regno, buf + sizeof (long) - size);
|
|
else
|
|
supply_register (regcache, regno, buf);
|
|
}
|
|
else
|
|
perror_with_name ("Unexpected byte order");
|
|
}
|
|
|
|
|
|
#define INSTR_SC 0x44000002
|
|
#define NR_spu_run 0x0116
|
|
|
|
/* If the PPU thread is currently stopped on a spu_run system call,
|
|
return to FD and ADDR the file handle and NPC parameter address
|
|
used with the system call. Return non-zero if successful. */
|
|
static int
|
|
parse_spufs_run (struct regcache *regcache, int *fd, CORE_ADDR *addr)
|
|
{
|
|
CORE_ADDR curr_pc;
|
|
int curr_insn;
|
|
int curr_r0;
|
|
|
|
if (register_size (regcache->tdesc, 0) == 4)
|
|
{
|
|
unsigned int pc, r0, r3, r4;
|
|
collect_register_by_name (regcache, "pc", &pc);
|
|
collect_register_by_name (regcache, "r0", &r0);
|
|
collect_register_by_name (regcache, "orig_r3", &r3);
|
|
collect_register_by_name (regcache, "r4", &r4);
|
|
curr_pc = (CORE_ADDR) pc;
|
|
curr_r0 = (int) r0;
|
|
*fd = (int) r3;
|
|
*addr = (CORE_ADDR) r4;
|
|
}
|
|
else
|
|
{
|
|
unsigned long pc, r0, r3, r4;
|
|
collect_register_by_name (regcache, "pc", &pc);
|
|
collect_register_by_name (regcache, "r0", &r0);
|
|
collect_register_by_name (regcache, "orig_r3", &r3);
|
|
collect_register_by_name (regcache, "r4", &r4);
|
|
curr_pc = (CORE_ADDR) pc;
|
|
curr_r0 = (int) r0;
|
|
*fd = (int) r3;
|
|
*addr = (CORE_ADDR) r4;
|
|
}
|
|
|
|
/* Fetch instruction preceding current NIP. */
|
|
if ((*the_target->read_memory) (curr_pc - 4,
|
|
(unsigned char *) &curr_insn, 4) != 0)
|
|
return 0;
|
|
/* It should be a "sc" instruction. */
|
|
if (curr_insn != INSTR_SC)
|
|
return 0;
|
|
/* System call number should be NR_spu_run. */
|
|
if (curr_r0 != NR_spu_run)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static CORE_ADDR
|
|
ppc_get_pc (struct regcache *regcache)
|
|
{
|
|
CORE_ADDR addr;
|
|
int fd;
|
|
|
|
if (parse_spufs_run (regcache, &fd, &addr))
|
|
{
|
|
unsigned int pc;
|
|
(*the_target->read_memory) (addr, (unsigned char *) &pc, 4);
|
|
return ((CORE_ADDR)1 << 63)
|
|
| ((CORE_ADDR)fd << 32) | (CORE_ADDR) (pc - 4);
|
|
}
|
|
else if (register_size (regcache->tdesc, 0) == 4)
|
|
{
|
|
unsigned int pc;
|
|
collect_register_by_name (regcache, "pc", &pc);
|
|
return (CORE_ADDR) pc;
|
|
}
|
|
else
|
|
{
|
|
unsigned long pc;
|
|
collect_register_by_name (regcache, "pc", &pc);
|
|
return (CORE_ADDR) pc;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ppc_set_pc (struct regcache *regcache, CORE_ADDR pc)
|
|
{
|
|
CORE_ADDR addr;
|
|
int fd;
|
|
|
|
if (parse_spufs_run (regcache, &fd, &addr))
|
|
{
|
|
unsigned int newpc = pc;
|
|
(*the_target->write_memory) (addr, (unsigned char *) &newpc, 4);
|
|
}
|
|
else if (register_size (regcache->tdesc, 0) == 4)
|
|
{
|
|
unsigned int newpc = pc;
|
|
supply_register_by_name (regcache, "pc", &newpc);
|
|
}
|
|
else
|
|
{
|
|
unsigned long newpc = pc;
|
|
supply_register_by_name (regcache, "pc", &newpc);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
ppc_get_auxv (unsigned long type, unsigned long *valp)
|
|
{
|
|
const struct target_desc *tdesc = current_process ()->tdesc;
|
|
int wordsize = register_size (tdesc, 0);
|
|
unsigned char *data = (unsigned char *) alloca (2 * wordsize);
|
|
int offset = 0;
|
|
|
|
while ((*the_target->read_auxv) (offset, data, 2 * wordsize) == 2 * wordsize)
|
|
{
|
|
if (wordsize == 4)
|
|
{
|
|
unsigned int *data_p = (unsigned int *)data;
|
|
if (data_p[0] == type)
|
|
{
|
|
*valp = data_p[1];
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned long *data_p = (unsigned long *)data;
|
|
if (data_p[0] == type)
|
|
{
|
|
*valp = data_p[1];
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
offset += 2 * wordsize;
|
|
}
|
|
|
|
*valp = 0;
|
|
return 0;
|
|
}
|
|
|
|
#ifndef __powerpc64__
|
|
static int ppc_regmap_adjusted;
|
|
#endif
|
|
|
|
|
|
/* Correct in either endianness.
|
|
This instruction is "twge r2, r2", which GDB uses as a software
|
|
breakpoint. */
|
|
static const unsigned int ppc_breakpoint = 0x7d821008;
|
|
#define ppc_breakpoint_len 4
|
|
|
|
/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
|
|
|
|
static const gdb_byte *
|
|
ppc_sw_breakpoint_from_kind (int kind, int *size)
|
|
{
|
|
*size = ppc_breakpoint_len;
|
|
return (const gdb_byte *) &ppc_breakpoint;
|
|
}
|
|
|
|
static int
|
|
ppc_breakpoint_at (CORE_ADDR where)
|
|
{
|
|
unsigned int insn;
|
|
|
|
if (where & ((CORE_ADDR)1 << 63))
|
|
{
|
|
char mem_annex[32];
|
|
sprintf (mem_annex, "%d/mem", (int)((where >> 32) & 0x7fffffff));
|
|
(*the_target->qxfer_spu) (mem_annex, (unsigned char *) &insn,
|
|
NULL, where & 0xffffffff, 4);
|
|
if (insn == 0x3fff)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
(*the_target->read_memory) (where, (unsigned char *) &insn, 4);
|
|
if (insn == ppc_breakpoint)
|
|
return 1;
|
|
/* If necessary, recognize more trap instructions here. GDB only uses
|
|
the one. */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Implement supports_z_point_type target-ops.
|
|
Returns true if type Z_TYPE breakpoint is supported.
|
|
|
|
Handling software breakpoint at server side, so tracepoints
|
|
and breakpoints can be inserted at the same location. */
|
|
|
|
static int
|
|
ppc_supports_z_point_type (char z_type)
|
|
{
|
|
switch (z_type)
|
|
{
|
|
case Z_PACKET_SW_BP:
|
|
return 1;
|
|
case Z_PACKET_HW_BP:
|
|
case Z_PACKET_WRITE_WP:
|
|
case Z_PACKET_ACCESS_WP:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Implement insert_point target-ops.
|
|
Returns 0 on success, -1 on failure and 1 on unsupported. */
|
|
|
|
static int
|
|
ppc_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
|
|
int size, struct raw_breakpoint *bp)
|
|
{
|
|
switch (type)
|
|
{
|
|
case raw_bkpt_type_sw:
|
|
return insert_memory_breakpoint (bp);
|
|
|
|
case raw_bkpt_type_hw:
|
|
case raw_bkpt_type_write_wp:
|
|
case raw_bkpt_type_access_wp:
|
|
default:
|
|
/* Unsupported. */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Implement remove_point target-ops.
|
|
Returns 0 on success, -1 on failure and 1 on unsupported. */
|
|
|
|
static int
|
|
ppc_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
|
|
int size, struct raw_breakpoint *bp)
|
|
{
|
|
switch (type)
|
|
{
|
|
case raw_bkpt_type_sw:
|
|
return remove_memory_breakpoint (bp);
|
|
|
|
case raw_bkpt_type_hw:
|
|
case raw_bkpt_type_write_wp:
|
|
case raw_bkpt_type_access_wp:
|
|
default:
|
|
/* Unsupported. */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Provide only a fill function for the general register set. ps_lgetregs
|
|
will use this for NPTL support. */
|
|
|
|
static void ppc_fill_gregset (struct regcache *regcache, void *buf)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++)
|
|
ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
|
|
|
|
for (i = 64; i < 70; i++)
|
|
ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
|
|
|
|
for (i = 71; i < 73; i++)
|
|
ppc_collect_ptrace_register (regcache, i, (char *) buf + ppc_regmap[i]);
|
|
}
|
|
|
|
/* Program Priority Register regset fill function. */
|
|
|
|
static void
|
|
ppc_fill_pprregset (struct regcache *regcache, void *buf)
|
|
{
|
|
char *ppr = (char *) buf;
|
|
|
|
collect_register_by_name (regcache, "ppr", ppr);
|
|
}
|
|
|
|
/* Program Priority Register regset store function. */
|
|
|
|
static void
|
|
ppc_store_pprregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *ppr = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "ppr", ppr);
|
|
}
|
|
|
|
/* Data Stream Control Register regset fill function. */
|
|
|
|
static void
|
|
ppc_fill_dscrregset (struct regcache *regcache, void *buf)
|
|
{
|
|
char *dscr = (char *) buf;
|
|
|
|
collect_register_by_name (regcache, "dscr", dscr);
|
|
}
|
|
|
|
/* Data Stream Control Register regset store function. */
|
|
|
|
static void
|
|
ppc_store_dscrregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *dscr = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "dscr", dscr);
|
|
}
|
|
|
|
/* Target Address Register regset fill function. */
|
|
|
|
static void
|
|
ppc_fill_tarregset (struct regcache *regcache, void *buf)
|
|
{
|
|
char *tar = (char *) buf;
|
|
|
|
collect_register_by_name (regcache, "tar", tar);
|
|
}
|
|
|
|
/* Target Address Register regset store function. */
|
|
|
|
static void
|
|
ppc_store_tarregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *tar = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "tar", tar);
|
|
}
|
|
|
|
/* Event-Based Branching regset store function. Unless the inferior
|
|
has a perf event open, ptrace can return in error when reading and
|
|
writing to the regset, with ENODATA. For reading, the registers
|
|
will correctly show as unavailable. For writing, gdbserver
|
|
currently only caches any register writes from P and G packets and
|
|
the stub always tries to write all the regsets when resuming the
|
|
inferior, which would result in frequent warnings. For this
|
|
reason, we don't define a fill function. This also means that the
|
|
client-side regcache will be dirty if the user tries to write to
|
|
the EBB registers. G packets that the client sends to write to
|
|
unrelated registers will also include data for EBB registers, even
|
|
if they are unavailable. */
|
|
|
|
static void
|
|
ppc_store_ebbregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *regset = (const char *) buf;
|
|
|
|
/* The order in the kernel regset is: EBBRR, EBBHR, BESCR. In the
|
|
.dat file is BESCR, EBBHR, EBBRR. */
|
|
supply_register_by_name (regcache, "ebbrr", ®set[0]);
|
|
supply_register_by_name (regcache, "ebbhr", ®set[8]);
|
|
supply_register_by_name (regcache, "bescr", ®set[16]);
|
|
}
|
|
|
|
/* Performance Monitoring Unit regset fill function. */
|
|
|
|
static void
|
|
ppc_fill_pmuregset (struct regcache *regcache, void *buf)
|
|
{
|
|
char *regset = (char *) buf;
|
|
|
|
/* The order in the kernel regset is SIAR, SDAR, SIER, MMCR2, MMCR0.
|
|
In the .dat file is MMCR0, MMCR2, SIAR, SDAR, SIER. */
|
|
collect_register_by_name (regcache, "siar", ®set[0]);
|
|
collect_register_by_name (regcache, "sdar", ®set[8]);
|
|
collect_register_by_name (regcache, "sier", ®set[16]);
|
|
collect_register_by_name (regcache, "mmcr2", ®set[24]);
|
|
collect_register_by_name (regcache, "mmcr0", ®set[32]);
|
|
}
|
|
|
|
/* Performance Monitoring Unit regset store function. */
|
|
|
|
static void
|
|
ppc_store_pmuregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *regset = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "siar", ®set[0]);
|
|
supply_register_by_name (regcache, "sdar", ®set[8]);
|
|
supply_register_by_name (regcache, "sier", ®set[16]);
|
|
supply_register_by_name (regcache, "mmcr2", ®set[24]);
|
|
supply_register_by_name (regcache, "mmcr0", ®set[32]);
|
|
}
|
|
|
|
/* Hardware Transactional Memory special-purpose register regset fill
|
|
function. */
|
|
|
|
static void
|
|
ppc_fill_tm_sprregset (struct regcache *regcache, void *buf)
|
|
{
|
|
int i, base;
|
|
char *regset = (char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "tfhar");
|
|
for (i = 0; i < 3; i++)
|
|
collect_register (regcache, base + i, ®set[i * 8]);
|
|
}
|
|
|
|
/* Hardware Transactional Memory special-purpose register regset store
|
|
function. */
|
|
|
|
static void
|
|
ppc_store_tm_sprregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base;
|
|
const char *regset = (const char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "tfhar");
|
|
for (i = 0; i < 3; i++)
|
|
supply_register (regcache, base + i, ®set[i * 8]);
|
|
}
|
|
|
|
/* For the same reasons as the EBB regset, none of the HTM
|
|
checkpointed regsets have a fill function. These registers are
|
|
only available if the inferior is in a transaction. */
|
|
|
|
/* Hardware Transactional Memory checkpointed general-purpose regset
|
|
store function. */
|
|
|
|
static void
|
|
ppc_store_tm_cgprregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base, size, endian_offset;
|
|
const char *regset = (const char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "cr0");
|
|
size = register_size (regcache->tdesc, base);
|
|
|
|
gdb_assert (size == 4 || size == 8);
|
|
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, base + i, ®set[i * size]);
|
|
|
|
endian_offset = 0;
|
|
|
|
if ((size == 8) && (__BYTE_ORDER == __BIG_ENDIAN))
|
|
endian_offset = 4;
|
|
|
|
supply_register_by_name (regcache, "ccr",
|
|
®set[PT_CCR * size + endian_offset]);
|
|
|
|
supply_register_by_name (regcache, "cxer",
|
|
®set[PT_XER * size + endian_offset]);
|
|
|
|
supply_register_by_name (regcache, "clr", ®set[PT_LNK * size]);
|
|
supply_register_by_name (regcache, "cctr", ®set[PT_CTR * size]);
|
|
}
|
|
|
|
/* Hardware Transactional Memory checkpointed floating-point regset
|
|
store function. */
|
|
|
|
static void
|
|
ppc_store_tm_cfprregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base;
|
|
const char *regset = (const char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "cf0");
|
|
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, base + i, ®set[i * 8]);
|
|
|
|
supply_register_by_name (regcache, "cfpscr", ®set[32 * 8]);
|
|
}
|
|
|
|
/* Hardware Transactional Memory checkpointed vector regset store
|
|
function. */
|
|
|
|
static void
|
|
ppc_store_tm_cvrregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base;
|
|
const char *regset = (const char *) buf;
|
|
int vscr_offset = 0;
|
|
|
|
base = find_regno (regcache->tdesc, "cvr0");
|
|
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, base + i, ®set[i * 16]);
|
|
|
|
if (__BYTE_ORDER == __BIG_ENDIAN)
|
|
vscr_offset = 12;
|
|
|
|
supply_register_by_name (regcache, "cvscr",
|
|
®set[32 * 16 + vscr_offset]);
|
|
|
|
supply_register_by_name (regcache, "cvrsave", ®set[33 * 16]);
|
|
}
|
|
|
|
/* Hardware Transactional Memory checkpointed vector-scalar regset
|
|
store function. */
|
|
|
|
static void
|
|
ppc_store_tm_cvsxregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base;
|
|
const char *regset = (const char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "cvs0h");
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, base + i, ®set[i * 8]);
|
|
}
|
|
|
|
/* Hardware Transactional Memory checkpointed Program Priority
|
|
Register regset store function. */
|
|
|
|
static void
|
|
ppc_store_tm_cpprregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *cppr = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "cppr", cppr);
|
|
}
|
|
|
|
/* Hardware Transactional Memory checkpointed Data Stream Control
|
|
Register regset store function. */
|
|
|
|
static void
|
|
ppc_store_tm_cdscrregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *cdscr = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "cdscr", cdscr);
|
|
}
|
|
|
|
/* Hardware Transactional Memory checkpointed Target Address Register
|
|
regset store function. */
|
|
|
|
static void
|
|
ppc_store_tm_ctarregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
const char *ctar = (const char *) buf;
|
|
|
|
supply_register_by_name (regcache, "ctar", ctar);
|
|
}
|
|
|
|
static void
|
|
ppc_fill_vsxregset (struct regcache *regcache, void *buf)
|
|
{
|
|
int i, base;
|
|
char *regset = (char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "vs0h");
|
|
for (i = 0; i < 32; i++)
|
|
collect_register (regcache, base + i, ®set[i * 8]);
|
|
}
|
|
|
|
static void
|
|
ppc_store_vsxregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base;
|
|
const char *regset = (const char *) buf;
|
|
|
|
base = find_regno (regcache->tdesc, "vs0h");
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, base + i, ®set[i * 8]);
|
|
}
|
|
|
|
static void
|
|
ppc_fill_vrregset (struct regcache *regcache, void *buf)
|
|
{
|
|
int i, base;
|
|
char *regset = (char *) buf;
|
|
int vscr_offset = 0;
|
|
|
|
base = find_regno (regcache->tdesc, "vr0");
|
|
for (i = 0; i < 32; i++)
|
|
collect_register (regcache, base + i, ®set[i * 16]);
|
|
|
|
if (__BYTE_ORDER == __BIG_ENDIAN)
|
|
vscr_offset = 12;
|
|
|
|
collect_register_by_name (regcache, "vscr",
|
|
®set[32 * 16 + vscr_offset]);
|
|
|
|
collect_register_by_name (regcache, "vrsave", ®set[33 * 16]);
|
|
}
|
|
|
|
static void
|
|
ppc_store_vrregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, base;
|
|
const char *regset = (const char *) buf;
|
|
int vscr_offset = 0;
|
|
|
|
base = find_regno (regcache->tdesc, "vr0");
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, base + i, ®set[i * 16]);
|
|
|
|
if (__BYTE_ORDER == __BIG_ENDIAN)
|
|
vscr_offset = 12;
|
|
|
|
supply_register_by_name (regcache, "vscr",
|
|
®set[32 * 16 + vscr_offset]);
|
|
supply_register_by_name (regcache, "vrsave", ®set[33 * 16]);
|
|
}
|
|
|
|
struct gdb_evrregset_t
|
|
{
|
|
unsigned long evr[32];
|
|
unsigned long long acc;
|
|
unsigned long spefscr;
|
|
};
|
|
|
|
static void
|
|
ppc_fill_evrregset (struct regcache *regcache, void *buf)
|
|
{
|
|
int i, ev0;
|
|
struct gdb_evrregset_t *regset = (struct gdb_evrregset_t *) buf;
|
|
|
|
ev0 = find_regno (regcache->tdesc, "ev0h");
|
|
for (i = 0; i < 32; i++)
|
|
collect_register (regcache, ev0 + i, ®set->evr[i]);
|
|
|
|
collect_register_by_name (regcache, "acc", ®set->acc);
|
|
collect_register_by_name (regcache, "spefscr", ®set->spefscr);
|
|
}
|
|
|
|
static void
|
|
ppc_store_evrregset (struct regcache *regcache, const void *buf)
|
|
{
|
|
int i, ev0;
|
|
const struct gdb_evrregset_t *regset = (const struct gdb_evrregset_t *) buf;
|
|
|
|
ev0 = find_regno (regcache->tdesc, "ev0h");
|
|
for (i = 0; i < 32; i++)
|
|
supply_register (regcache, ev0 + i, ®set->evr[i]);
|
|
|
|
supply_register_by_name (regcache, "acc", ®set->acc);
|
|
supply_register_by_name (regcache, "spefscr", ®set->spefscr);
|
|
}
|
|
|
|
/* Support for hardware single step. */
|
|
|
|
static int
|
|
ppc_supports_hardware_single_step (void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static struct regset_info ppc_regsets[] = {
|
|
/* List the extra register sets before GENERAL_REGS. That way we will
|
|
fetch them every time, but still fall back to PTRACE_PEEKUSER for the
|
|
general registers. Some kernels support these, but not the newer
|
|
PPC_PTRACE_GETREGS. */
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CTAR, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_ctarregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CDSCR, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_cdscrregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CPPR, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_cpprregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CVSX, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_cvsxregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CVMX, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_cvrregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CFPR, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_cfprregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_CGPR, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_tm_cgprregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TM_SPR, 0, EXTENDED_REGS,
|
|
ppc_fill_tm_sprregset, ppc_store_tm_sprregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_EBB, 0, EXTENDED_REGS,
|
|
NULL, ppc_store_ebbregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PMU, 0, EXTENDED_REGS,
|
|
ppc_fill_pmuregset, ppc_store_pmuregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TAR, 0, EXTENDED_REGS,
|
|
ppc_fill_tarregset, ppc_store_tarregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PPR, 0, EXTENDED_REGS,
|
|
ppc_fill_pprregset, ppc_store_pprregset },
|
|
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_DSCR, 0, EXTENDED_REGS,
|
|
ppc_fill_dscrregset, ppc_store_dscrregset },
|
|
{ PTRACE_GETVSXREGS, PTRACE_SETVSXREGS, 0, 0, EXTENDED_REGS,
|
|
ppc_fill_vsxregset, ppc_store_vsxregset },
|
|
{ PTRACE_GETVRREGS, PTRACE_SETVRREGS, 0, 0, EXTENDED_REGS,
|
|
ppc_fill_vrregset, ppc_store_vrregset },
|
|
{ PTRACE_GETEVRREGS, PTRACE_SETEVRREGS, 0, 0, EXTENDED_REGS,
|
|
ppc_fill_evrregset, ppc_store_evrregset },
|
|
{ 0, 0, 0, 0, GENERAL_REGS, ppc_fill_gregset, NULL },
|
|
NULL_REGSET
|
|
};
|
|
|
|
static struct usrregs_info ppc_usrregs_info =
|
|
{
|
|
ppc_num_regs,
|
|
ppc_regmap,
|
|
};
|
|
|
|
static struct regsets_info ppc_regsets_info =
|
|
{
|
|
ppc_regsets, /* regsets */
|
|
0, /* num_regsets */
|
|
NULL, /* disabled_regsets */
|
|
};
|
|
|
|
static struct regs_info regs_info =
|
|
{
|
|
NULL, /* regset_bitmap */
|
|
&ppc_usrregs_info,
|
|
&ppc_regsets_info
|
|
};
|
|
|
|
static const struct regs_info *
|
|
ppc_regs_info (void)
|
|
{
|
|
return ®s_info;
|
|
}
|
|
|
|
static void
|
|
ppc_arch_setup (void)
|
|
{
|
|
const struct target_desc *tdesc;
|
|
struct regset_info *regset;
|
|
struct ppc_linux_features features = ppc_linux_no_features;
|
|
|
|
int tid = lwpid_of (current_thread);
|
|
|
|
features.wordsize = ppc_linux_target_wordsize (tid);
|
|
|
|
if (features.wordsize == 4)
|
|
tdesc = tdesc_powerpc_32l;
|
|
else
|
|
tdesc = tdesc_powerpc_64l;
|
|
|
|
current_process ()->tdesc = tdesc;
|
|
|
|
/* The value of current_process ()->tdesc needs to be set for this
|
|
call. */
|
|
ppc_get_auxv (AT_HWCAP, &ppc_hwcap);
|
|
ppc_get_auxv (AT_HWCAP2, &ppc_hwcap2);
|
|
|
|
features.isa205 = ppc_linux_has_isa205 (ppc_hwcap);
|
|
|
|
if (ppc_hwcap & PPC_FEATURE_HAS_VSX)
|
|
features.vsx = true;
|
|
|
|
if (ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC)
|
|
features.altivec = true;
|
|
|
|
if ((ppc_hwcap2 & PPC_FEATURE2_DSCR)
|
|
&& ppc_check_regset (tid, NT_PPC_DSCR, PPC_LINUX_SIZEOF_DSCRREGSET)
|
|
&& ppc_check_regset (tid, NT_PPC_PPR, PPC_LINUX_SIZEOF_PPRREGSET))
|
|
{
|
|
features.ppr_dscr = true;
|
|
if ((ppc_hwcap2 & PPC_FEATURE2_ARCH_2_07)
|
|
&& (ppc_hwcap2 & PPC_FEATURE2_TAR)
|
|
&& (ppc_hwcap2 & PPC_FEATURE2_EBB)
|
|
&& ppc_check_regset (tid, NT_PPC_TAR,
|
|
PPC_LINUX_SIZEOF_TARREGSET)
|
|
&& ppc_check_regset (tid, NT_PPC_EBB,
|
|
PPC_LINUX_SIZEOF_EBBREGSET)
|
|
&& ppc_check_regset (tid, NT_PPC_PMU,
|
|
PPC_LINUX_SIZEOF_PMUREGSET))
|
|
{
|
|
features.isa207 = true;
|
|
if ((ppc_hwcap2 & PPC_FEATURE2_HTM)
|
|
&& ppc_check_regset (tid, NT_PPC_TM_SPR,
|
|
PPC_LINUX_SIZEOF_TM_SPRREGSET))
|
|
features.htm = true;
|
|
}
|
|
}
|
|
|
|
if (ppc_hwcap & PPC_FEATURE_CELL)
|
|
features.cell = true;
|
|
|
|
tdesc = ppc_linux_match_description (features);
|
|
|
|
/* On 32-bit machines, check for SPE registers.
|
|
Set the low target's regmap field as appropriately. */
|
|
#ifndef __powerpc64__
|
|
if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
|
|
tdesc = tdesc_powerpc_e500l;
|
|
|
|
if (!ppc_regmap_adjusted)
|
|
{
|
|
if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
|
|
ppc_usrregs_info.regmap = ppc_regmap_e500;
|
|
|
|
/* If the FPSCR is 64-bit wide, we need to fetch the whole
|
|
64-bit slot and not just its second word. The PT_FPSCR
|
|
supplied in a 32-bit GDB compilation doesn't reflect
|
|
this. */
|
|
if (register_size (tdesc, 70) == 8)
|
|
ppc_regmap[70] = (48 + 2*32) * sizeof (long);
|
|
|
|
ppc_regmap_adjusted = 1;
|
|
}
|
|
#endif
|
|
|
|
current_process ()->tdesc = tdesc;
|
|
|
|
for (regset = ppc_regsets; regset->size >= 0; regset++)
|
|
switch (regset->get_request)
|
|
{
|
|
case PTRACE_GETVRREGS:
|
|
regset->size = features.altivec ? PPC_LINUX_SIZEOF_VRREGSET : 0;
|
|
break;
|
|
case PTRACE_GETVSXREGS:
|
|
regset->size = features.vsx ? PPC_LINUX_SIZEOF_VSXREGSET : 0;
|
|
break;
|
|
case PTRACE_GETEVRREGS:
|
|
if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
|
|
regset->size = 32 * 4 + 8 + 4;
|
|
else
|
|
regset->size = 0;
|
|
break;
|
|
case PTRACE_GETREGSET:
|
|
switch (regset->nt_type)
|
|
{
|
|
case NT_PPC_PPR:
|
|
regset->size = (features.ppr_dscr ?
|
|
PPC_LINUX_SIZEOF_PPRREGSET : 0);
|
|
break;
|
|
case NT_PPC_DSCR:
|
|
regset->size = (features.ppr_dscr ?
|
|
PPC_LINUX_SIZEOF_DSCRREGSET : 0);
|
|
break;
|
|
case NT_PPC_TAR:
|
|
regset->size = (features.isa207 ?
|
|
PPC_LINUX_SIZEOF_TARREGSET : 0);
|
|
break;
|
|
case NT_PPC_EBB:
|
|
regset->size = (features.isa207 ?
|
|
PPC_LINUX_SIZEOF_EBBREGSET : 0);
|
|
break;
|
|
case NT_PPC_PMU:
|
|
regset->size = (features.isa207 ?
|
|
PPC_LINUX_SIZEOF_PMUREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_SPR:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_TM_SPRREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CGPR:
|
|
if (features.wordsize == 4)
|
|
regset->size = (features.htm ?
|
|
PPC32_LINUX_SIZEOF_CGPRREGSET : 0);
|
|
else
|
|
regset->size = (features.htm ?
|
|
PPC64_LINUX_SIZEOF_CGPRREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CFPR:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_CFPRREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CVMX:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_CVMXREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CVSX:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_CVSXREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CPPR:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_CPPRREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CDSCR:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_CDSCRREGSET : 0);
|
|
break;
|
|
case NT_PPC_TM_CTAR:
|
|
regset->size = (features.htm ?
|
|
PPC_LINUX_SIZEOF_CTARREGSET : 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Implementation of linux_target_ops method "supports_tracepoints". */
|
|
|
|
static int
|
|
ppc_supports_tracepoints (void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* Get the thread area address. This is used to recognize which
|
|
thread is which when tracing with the in-process agent library. We
|
|
don't read anything from the address, and treat it as opaque; it's
|
|
the address itself that we assume is unique per-thread. */
|
|
|
|
static int
|
|
ppc_get_thread_area (int lwpid, CORE_ADDR *addr)
|
|
{
|
|
struct lwp_info *lwp = find_lwp_pid (ptid_t (lwpid));
|
|
struct thread_info *thr = get_lwp_thread (lwp);
|
|
struct regcache *regcache = get_thread_regcache (thr, 1);
|
|
ULONGEST tp = 0;
|
|
|
|
#ifdef __powerpc64__
|
|
if (register_size (regcache->tdesc, 0) == 8)
|
|
collect_register_by_name (regcache, "r13", &tp);
|
|
else
|
|
#endif
|
|
collect_register_by_name (regcache, "r2", &tp);
|
|
|
|
*addr = tp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __powerpc64__
|
|
|
|
/* Older glibc doesn't provide this. */
|
|
|
|
#ifndef EF_PPC64_ABI
|
|
#define EF_PPC64_ABI 3
|
|
#endif
|
|
|
|
/* Returns 1 if inferior is using ELFv2 ABI. Undefined for 32-bit
|
|
inferiors. */
|
|
|
|
static int
|
|
is_elfv2_inferior (void)
|
|
{
|
|
/* To be used as fallback if we're unable to determine the right result -
|
|
assume inferior uses the same ABI as gdbserver. */
|
|
#if _CALL_ELF == 2
|
|
const int def_res = 1;
|
|
#else
|
|
const int def_res = 0;
|
|
#endif
|
|
unsigned long phdr;
|
|
Elf64_Ehdr ehdr;
|
|
|
|
if (!ppc_get_auxv (AT_PHDR, &phdr))
|
|
return def_res;
|
|
|
|
/* Assume ELF header is at the beginning of the page where program headers
|
|
are located. If it doesn't look like one, bail. */
|
|
|
|
read_inferior_memory (phdr & ~0xfff, (unsigned char *) &ehdr, sizeof ehdr);
|
|
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG))
|
|
return def_res;
|
|
|
|
return (ehdr.e_flags & EF_PPC64_ABI) == 2;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Generate a ds-form instruction in BUF and return the number of bytes written
|
|
|
|
0 6 11 16 30 32
|
|
| OPCD | RST | RA | DS |XO| */
|
|
|
|
__attribute__((unused)) /* Maybe unused due to conditional compilation. */
|
|
static int
|
|
gen_ds_form (uint32_t *buf, int opcd, int rst, int ra, int ds, int xo)
|
|
{
|
|
uint32_t insn;
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
gdb_assert ((rst & ~0x1f) == 0);
|
|
gdb_assert ((ra & ~0x1f) == 0);
|
|
gdb_assert ((xo & ~0x3) == 0);
|
|
|
|
insn = (rst << 21) | (ra << 16) | (ds & 0xfffc) | (xo & 0x3);
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* Followings are frequently used ds-form instructions. */
|
|
|
|
#define GEN_STD(buf, rs, ra, offset) gen_ds_form (buf, 62, rs, ra, offset, 0)
|
|
#define GEN_STDU(buf, rs, ra, offset) gen_ds_form (buf, 62, rs, ra, offset, 1)
|
|
#define GEN_LD(buf, rt, ra, offset) gen_ds_form (buf, 58, rt, ra, offset, 0)
|
|
#define GEN_LDU(buf, rt, ra, offset) gen_ds_form (buf, 58, rt, ra, offset, 1)
|
|
|
|
/* Generate a d-form instruction in BUF.
|
|
|
|
0 6 11 16 32
|
|
| OPCD | RST | RA | D | */
|
|
|
|
static int
|
|
gen_d_form (uint32_t *buf, int opcd, int rst, int ra, int si)
|
|
{
|
|
uint32_t insn;
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
gdb_assert ((rst & ~0x1f) == 0);
|
|
gdb_assert ((ra & ~0x1f) == 0);
|
|
|
|
insn = (rst << 21) | (ra << 16) | (si & 0xffff);
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* Followings are frequently used d-form instructions. */
|
|
|
|
#define GEN_ADDI(buf, rt, ra, si) gen_d_form (buf, 14, rt, ra, si)
|
|
#define GEN_ADDIS(buf, rt, ra, si) gen_d_form (buf, 15, rt, ra, si)
|
|
#define GEN_LI(buf, rt, si) GEN_ADDI (buf, rt, 0, si)
|
|
#define GEN_LIS(buf, rt, si) GEN_ADDIS (buf, rt, 0, si)
|
|
#define GEN_ORI(buf, rt, ra, si) gen_d_form (buf, 24, rt, ra, si)
|
|
#define GEN_ORIS(buf, rt, ra, si) gen_d_form (buf, 25, rt, ra, si)
|
|
#define GEN_LWZ(buf, rt, ra, si) gen_d_form (buf, 32, rt, ra, si)
|
|
#define GEN_STW(buf, rt, ra, si) gen_d_form (buf, 36, rt, ra, si)
|
|
#define GEN_STWU(buf, rt, ra, si) gen_d_form (buf, 37, rt, ra, si)
|
|
|
|
/* Generate a xfx-form instruction in BUF and return the number of bytes
|
|
written.
|
|
|
|
0 6 11 21 31 32
|
|
| OPCD | RST | RI | XO |/| */
|
|
|
|
static int
|
|
gen_xfx_form (uint32_t *buf, int opcd, int rst, int ri, int xo)
|
|
{
|
|
uint32_t insn;
|
|
unsigned int n = ((ri & 0x1f) << 5) | ((ri >> 5) & 0x1f);
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
gdb_assert ((rst & ~0x1f) == 0);
|
|
gdb_assert ((xo & ~0x3ff) == 0);
|
|
|
|
insn = (rst << 21) | (n << 11) | (xo << 1);
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* Followings are frequently used xfx-form instructions. */
|
|
|
|
#define GEN_MFSPR(buf, rt, spr) gen_xfx_form (buf, 31, rt, spr, 339)
|
|
#define GEN_MTSPR(buf, rt, spr) gen_xfx_form (buf, 31, rt, spr, 467)
|
|
#define GEN_MFCR(buf, rt) gen_xfx_form (buf, 31, rt, 0, 19)
|
|
#define GEN_MTCR(buf, rt) gen_xfx_form (buf, 31, rt, 0x3cf, 144)
|
|
#define GEN_SYNC(buf, L, E) gen_xfx_form (buf, 31, L & 0x3, \
|
|
E & 0xf, 598)
|
|
#define GEN_LWSYNC(buf) GEN_SYNC (buf, 1, 0)
|
|
|
|
|
|
/* Generate a x-form instruction in BUF and return the number of bytes written.
|
|
|
|
0 6 11 16 21 31 32
|
|
| OPCD | RST | RA | RB | XO |RC| */
|
|
|
|
static int
|
|
gen_x_form (uint32_t *buf, int opcd, int rst, int ra, int rb, int xo, int rc)
|
|
{
|
|
uint32_t insn;
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
gdb_assert ((rst & ~0x1f) == 0);
|
|
gdb_assert ((ra & ~0x1f) == 0);
|
|
gdb_assert ((rb & ~0x1f) == 0);
|
|
gdb_assert ((xo & ~0x3ff) == 0);
|
|
gdb_assert ((rc & ~1) == 0);
|
|
|
|
insn = (rst << 21) | (ra << 16) | (rb << 11) | (xo << 1) | rc;
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* Followings are frequently used x-form instructions. */
|
|
|
|
#define GEN_OR(buf, ra, rs, rb) gen_x_form (buf, 31, rs, ra, rb, 444, 0)
|
|
#define GEN_MR(buf, ra, rs) GEN_OR (buf, ra, rs, rs)
|
|
#define GEN_LWARX(buf, rt, ra, rb) gen_x_form (buf, 31, rt, ra, rb, 20, 0)
|
|
#define GEN_STWCX(buf, rs, ra, rb) gen_x_form (buf, 31, rs, ra, rb, 150, 1)
|
|
/* Assume bf = cr7. */
|
|
#define GEN_CMPW(buf, ra, rb) gen_x_form (buf, 31, 28, ra, rb, 0, 0)
|
|
|
|
|
|
/* Generate a md-form instruction in BUF and return the number of bytes written.
|
|
|
|
0 6 11 16 21 27 30 31 32
|
|
| OPCD | RS | RA | sh | mb | XO |sh|Rc| */
|
|
|
|
static int
|
|
gen_md_form (uint32_t *buf, int opcd, int rs, int ra, int sh, int mb,
|
|
int xo, int rc)
|
|
{
|
|
uint32_t insn;
|
|
unsigned int n = ((mb & 0x1f) << 1) | ((mb >> 5) & 0x1);
|
|
unsigned int sh0_4 = sh & 0x1f;
|
|
unsigned int sh5 = (sh >> 5) & 1;
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
gdb_assert ((rs & ~0x1f) == 0);
|
|
gdb_assert ((ra & ~0x1f) == 0);
|
|
gdb_assert ((sh & ~0x3f) == 0);
|
|
gdb_assert ((mb & ~0x3f) == 0);
|
|
gdb_assert ((xo & ~0x7) == 0);
|
|
gdb_assert ((rc & ~0x1) == 0);
|
|
|
|
insn = (rs << 21) | (ra << 16) | (sh0_4 << 11) | (n << 5)
|
|
| (sh5 << 1) | (xo << 2) | (rc & 1);
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* The following are frequently used md-form instructions. */
|
|
|
|
#define GEN_RLDICL(buf, ra, rs ,sh, mb) \
|
|
gen_md_form (buf, 30, rs, ra, sh, mb, 0, 0)
|
|
#define GEN_RLDICR(buf, ra, rs ,sh, mb) \
|
|
gen_md_form (buf, 30, rs, ra, sh, mb, 1, 0)
|
|
|
|
/* Generate a i-form instruction in BUF and return the number of bytes written.
|
|
|
|
0 6 30 31 32
|
|
| OPCD | LI |AA|LK| */
|
|
|
|
static int
|
|
gen_i_form (uint32_t *buf, int opcd, int li, int aa, int lk)
|
|
{
|
|
uint32_t insn;
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
|
|
insn = (li & 0x3fffffc) | (aa & 1) | (lk & 1);
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* The following are frequently used i-form instructions. */
|
|
|
|
#define GEN_B(buf, li) gen_i_form (buf, 18, li, 0, 0)
|
|
#define GEN_BL(buf, li) gen_i_form (buf, 18, li, 0, 1)
|
|
|
|
/* Generate a b-form instruction in BUF and return the number of bytes written.
|
|
|
|
0 6 11 16 30 31 32
|
|
| OPCD | BO | BI | BD |AA|LK| */
|
|
|
|
static int
|
|
gen_b_form (uint32_t *buf, int opcd, int bo, int bi, int bd,
|
|
int aa, int lk)
|
|
{
|
|
uint32_t insn;
|
|
|
|
gdb_assert ((opcd & ~0x3f) == 0);
|
|
gdb_assert ((bo & ~0x1f) == 0);
|
|
gdb_assert ((bi & ~0x1f) == 0);
|
|
|
|
insn = (bo << 21) | (bi << 16) | (bd & 0xfffc) | (aa & 1) | (lk & 1);
|
|
*buf = (opcd << 26) | insn;
|
|
return 1;
|
|
}
|
|
|
|
/* The following are frequently used b-form instructions. */
|
|
/* Assume bi = cr7. */
|
|
#define GEN_BNE(buf, bd) gen_b_form (buf, 16, 0x4, (7 << 2) | 2, bd, 0 ,0)
|
|
|
|
/* GEN_LOAD and GEN_STORE generate 64- or 32-bit load/store for ppc64 or ppc32
|
|
respectively. They are primary used for save/restore GPRs in jump-pad,
|
|
not used for bytecode compiling. */
|
|
|
|
#ifdef __powerpc64__
|
|
#define GEN_LOAD(buf, rt, ra, si, is_64) (is_64 ? \
|
|
GEN_LD (buf, rt, ra, si) : \
|
|
GEN_LWZ (buf, rt, ra, si))
|
|
#define GEN_STORE(buf, rt, ra, si, is_64) (is_64 ? \
|
|
GEN_STD (buf, rt, ra, si) : \
|
|
GEN_STW (buf, rt, ra, si))
|
|
#else
|
|
#define GEN_LOAD(buf, rt, ra, si, is_64) GEN_LWZ (buf, rt, ra, si)
|
|
#define GEN_STORE(buf, rt, ra, si, is_64) GEN_STW (buf, rt, ra, si)
|
|
#endif
|
|
|
|
/* Generate a sequence of instructions to load IMM in the register REG.
|
|
Write the instructions in BUF and return the number of bytes written. */
|
|
|
|
static int
|
|
gen_limm (uint32_t *buf, int reg, uint64_t imm, int is_64)
|
|
{
|
|
uint32_t *p = buf;
|
|
|
|
if ((imm + 32768) < 65536)
|
|
{
|
|
/* li reg, imm[15:0] */
|
|
p += GEN_LI (p, reg, imm);
|
|
}
|
|
else if ((imm >> 32) == 0)
|
|
{
|
|
/* lis reg, imm[31:16]
|
|
ori reg, reg, imm[15:0]
|
|
rldicl reg, reg, 0, 32 */
|
|
p += GEN_LIS (p, reg, (imm >> 16) & 0xffff);
|
|
if ((imm & 0xffff) != 0)
|
|
p += GEN_ORI (p, reg, reg, imm & 0xffff);
|
|
/* Clear upper 32-bit if sign-bit is set. */
|
|
if (imm & (1u << 31) && is_64)
|
|
p += GEN_RLDICL (p, reg, reg, 0, 32);
|
|
}
|
|
else
|
|
{
|
|
gdb_assert (is_64);
|
|
/* lis reg, <imm[63:48]>
|
|
ori reg, reg, <imm[48:32]>
|
|
rldicr reg, reg, 32, 31
|
|
oris reg, reg, <imm[31:16]>
|
|
ori reg, reg, <imm[15:0]> */
|
|
p += GEN_LIS (p, reg, ((imm >> 48) & 0xffff));
|
|
if (((imm >> 32) & 0xffff) != 0)
|
|
p += GEN_ORI (p, reg, reg, ((imm >> 32) & 0xffff));
|
|
p += GEN_RLDICR (p, reg, reg, 32, 31);
|
|
if (((imm >> 16) & 0xffff) != 0)
|
|
p += GEN_ORIS (p, reg, reg, ((imm >> 16) & 0xffff));
|
|
if ((imm & 0xffff) != 0)
|
|
p += GEN_ORI (p, reg, reg, (imm & 0xffff));
|
|
}
|
|
|
|
return p - buf;
|
|
}
|
|
|
|
/* Generate a sequence for atomically exchange at location LOCK.
|
|
This code sequence clobbers r6, r7, r8. LOCK is the location for
|
|
the atomic-xchg, OLD_VALUE is expected old value stored in the
|
|
location, and R_NEW is a register for the new value. */
|
|
|
|
static int
|
|
gen_atomic_xchg (uint32_t *buf, CORE_ADDR lock, int old_value, int r_new,
|
|
int is_64)
|
|
{
|
|
const int r_lock = 6;
|
|
const int r_old = 7;
|
|
const int r_tmp = 8;
|
|
uint32_t *p = buf;
|
|
|
|
/*
|
|
1: lwarx TMP, 0, LOCK
|
|
cmpwi TMP, OLD
|
|
bne 1b
|
|
stwcx. NEW, 0, LOCK
|
|
bne 1b */
|
|
|
|
p += gen_limm (p, r_lock, lock, is_64);
|
|
p += gen_limm (p, r_old, old_value, is_64);
|
|
|
|
p += GEN_LWARX (p, r_tmp, 0, r_lock);
|
|
p += GEN_CMPW (p, r_tmp, r_old);
|
|
p += GEN_BNE (p, -8);
|
|
p += GEN_STWCX (p, r_new, 0, r_lock);
|
|
p += GEN_BNE (p, -16);
|
|
|
|
return p - buf;
|
|
}
|
|
|
|
/* Generate a sequence of instructions for calling a function
|
|
at address of FN. Return the number of bytes are written in BUF. */
|
|
|
|
static int
|
|
gen_call (uint32_t *buf, CORE_ADDR fn, int is_64, int is_opd)
|
|
{
|
|
uint32_t *p = buf;
|
|
|
|
/* Must be called by r12 for caller to calculate TOC address. */
|
|
p += gen_limm (p, 12, fn, is_64);
|
|
if (is_opd)
|
|
{
|
|
p += GEN_LOAD (p, 11, 12, 16, is_64);
|
|
p += GEN_LOAD (p, 2, 12, 8, is_64);
|
|
p += GEN_LOAD (p, 12, 12, 0, is_64);
|
|
}
|
|
p += GEN_MTSPR (p, 12, 9); /* mtctr r12 */
|
|
*p++ = 0x4e800421; /* bctrl */
|
|
|
|
return p - buf;
|
|
}
|
|
|
|
/* Copy the instruction from OLDLOC to *TO, and update *TO to *TO + size
|
|
of instruction. This function is used to adjust pc-relative instructions
|
|
when copying. */
|
|
|
|
static void
|
|
ppc_relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
|
|
{
|
|
uint32_t insn, op6;
|
|
long rel, newrel;
|
|
|
|
read_inferior_memory (oldloc, (unsigned char *) &insn, 4);
|
|
op6 = PPC_OP6 (insn);
|
|
|
|
if (op6 == 18 && (insn & 2) == 0)
|
|
{
|
|
/* branch && AA = 0 */
|
|
rel = PPC_LI (insn);
|
|
newrel = (oldloc - *to) + rel;
|
|
|
|
/* Out of range. Cannot relocate instruction. */
|
|
if (newrel >= (1 << 25) || newrel < -(1 << 25))
|
|
return;
|
|
|
|
insn = (insn & ~0x3fffffc) | (newrel & 0x3fffffc);
|
|
}
|
|
else if (op6 == 16 && (insn & 2) == 0)
|
|
{
|
|
/* conditional branch && AA = 0 */
|
|
|
|
/* If the new relocation is too big for even a 26-bit unconditional
|
|
branch, there is nothing we can do. Just abort.
|
|
|
|
Otherwise, if it can be fit in 16-bit conditional branch, just
|
|
copy the instruction and relocate the address.
|
|
|
|
If the it's big for conditional-branch (16-bit), try to invert the
|
|
condition and jump with 26-bit branch. For example,
|
|
|
|
beq .Lgoto
|
|
INSN1
|
|
|
|
=>
|
|
|
|
bne 1f (+8)
|
|
b .Lgoto
|
|
1:INSN1
|
|
|
|
After this transform, we are actually jump from *TO+4 instead of *TO,
|
|
so check the relocation again because it will be 1-insn farther then
|
|
before if *TO is after OLDLOC.
|
|
|
|
|
|
For BDNZT (or so) is transformed from
|
|
|
|
bdnzt eq, .Lgoto
|
|
INSN1
|
|
|
|
=>
|
|
|
|
bdz 1f (+12)
|
|
bf eq, 1f (+8)
|
|
b .Lgoto
|
|
1:INSN1
|
|
|
|
See also "BO field encodings". */
|
|
|
|
rel = PPC_BD (insn);
|
|
newrel = (oldloc - *to) + rel;
|
|
|
|
if (newrel < (1 << 15) && newrel >= -(1 << 15))
|
|
insn = (insn & ~0xfffc) | (newrel & 0xfffc);
|
|
else if ((PPC_BO (insn) & 0x14) == 0x4 || (PPC_BO (insn) & 0x14) == 0x10)
|
|
{
|
|
newrel -= 4;
|
|
|
|
/* Out of range. Cannot relocate instruction. */
|
|
if (newrel >= (1 << 25) || newrel < -(1 << 25))
|
|
return;
|
|
|
|
if ((PPC_BO (insn) & 0x14) == 0x4)
|
|
insn ^= (1 << 24);
|
|
else if ((PPC_BO (insn) & 0x14) == 0x10)
|
|
insn ^= (1 << 22);
|
|
|
|
/* Jump over the unconditional branch. */
|
|
insn = (insn & ~0xfffc) | 0x8;
|
|
write_inferior_memory (*to, (unsigned char *) &insn, 4);
|
|
*to += 4;
|
|
|
|
/* Build a unconditional branch and copy LK bit. */
|
|
insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
|
|
write_inferior_memory (*to, (unsigned char *) &insn, 4);
|
|
*to += 4;
|
|
|
|
return;
|
|
}
|
|
else if ((PPC_BO (insn) & 0x14) == 0)
|
|
{
|
|
uint32_t bdnz_insn = (16 << 26) | (0x10 << 21) | 12;
|
|
uint32_t bf_insn = (16 << 26) | (0x4 << 21) | 8;
|
|
|
|
newrel -= 8;
|
|
|
|
/* Out of range. Cannot relocate instruction. */
|
|
if (newrel >= (1 << 25) || newrel < -(1 << 25))
|
|
return;
|
|
|
|
/* Copy BI field. */
|
|
bf_insn |= (insn & 0x1f0000);
|
|
|
|
/* Invert condition. */
|
|
bdnz_insn |= (insn ^ (1 << 22)) & (1 << 22);
|
|
bf_insn |= (insn ^ (1 << 24)) & (1 << 24);
|
|
|
|
write_inferior_memory (*to, (unsigned char *) &bdnz_insn, 4);
|
|
*to += 4;
|
|
write_inferior_memory (*to, (unsigned char *) &bf_insn, 4);
|
|
*to += 4;
|
|
|
|
/* Build a unconditional branch and copy LK bit. */
|
|
insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
|
|
write_inferior_memory (*to, (unsigned char *) &insn, 4);
|
|
*to += 4;
|
|
|
|
return;
|
|
}
|
|
else /* (BO & 0x14) == 0x14, branch always. */
|
|
{
|
|
/* Out of range. Cannot relocate instruction. */
|
|
if (newrel >= (1 << 25) || newrel < -(1 << 25))
|
|
return;
|
|
|
|
/* Build a unconditional branch and copy LK bit. */
|
|
insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
|
|
write_inferior_memory (*to, (unsigned char *) &insn, 4);
|
|
*to += 4;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
write_inferior_memory (*to, (unsigned char *) &insn, 4);
|
|
*to += 4;
|
|
}
|
|
|
|
/* Implement install_fast_tracepoint_jump_pad of target_ops.
|
|
See target.h for details. */
|
|
|
|
static int
|
|
ppc_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
|
CORE_ADDR collector,
|
|
CORE_ADDR lockaddr,
|
|
ULONGEST orig_size,
|
|
CORE_ADDR *jump_entry,
|
|
CORE_ADDR *trampoline,
|
|
ULONGEST *trampoline_size,
|
|
unsigned char *jjump_pad_insn,
|
|
ULONGEST *jjump_pad_insn_size,
|
|
CORE_ADDR *adjusted_insn_addr,
|
|
CORE_ADDR *adjusted_insn_addr_end,
|
|
char *err)
|
|
{
|
|
uint32_t buf[256];
|
|
uint32_t *p = buf;
|
|
int j, offset;
|
|
CORE_ADDR buildaddr = *jump_entry;
|
|
const CORE_ADDR entryaddr = *jump_entry;
|
|
int rsz, min_frame, frame_size, tp_reg;
|
|
#ifdef __powerpc64__
|
|
struct regcache *regcache = get_thread_regcache (current_thread, 0);
|
|
int is_64 = register_size (regcache->tdesc, 0) == 8;
|
|
int is_opd = is_64 && !is_elfv2_inferior ();
|
|
#else
|
|
int is_64 = 0, is_opd = 0;
|
|
#endif
|
|
|
|
#ifdef __powerpc64__
|
|
if (is_64)
|
|
{
|
|
/* Minimum frame size is 32 bytes for ELFv2, and 112 bytes for ELFv1. */
|
|
rsz = 8;
|
|
min_frame = 112;
|
|
frame_size = (40 * rsz) + min_frame;
|
|
tp_reg = 13;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
rsz = 4;
|
|
min_frame = 16;
|
|
frame_size = (40 * rsz) + min_frame;
|
|
tp_reg = 2;
|
|
#ifdef __powerpc64__
|
|
}
|
|
#endif
|
|
|
|
/* Stack frame layout for this jump pad,
|
|
|
|
High thread_area (r13/r2) |
|
|
tpoint - collecting_t obj
|
|
PC/<tpaddr> | +36
|
|
CTR | +35
|
|
LR | +34
|
|
XER | +33
|
|
CR | +32
|
|
R31 |
|
|
R29 |
|
|
... |
|
|
R1 | +1
|
|
R0 - collected registers
|
|
... |
|
|
... |
|
|
Low Back-chain -
|
|
|
|
|
|
The code flow of this jump pad,
|
|
|
|
1. Adjust SP
|
|
2. Save GPR and SPR
|
|
3. Prepare argument
|
|
4. Call gdb_collector
|
|
5. Restore GPR and SPR
|
|
6. Restore SP
|
|
7. Build a jump for back to the program
|
|
8. Copy/relocate original instruction
|
|
9. Build a jump for replacing orignal instruction. */
|
|
|
|
/* Adjust stack pointer. */
|
|
if (is_64)
|
|
p += GEN_STDU (p, 1, 1, -frame_size); /* stdu r1,-frame_size(r1) */
|
|
else
|
|
p += GEN_STWU (p, 1, 1, -frame_size); /* stwu r1,-frame_size(r1) */
|
|
|
|
/* Store GPRs. Save R1 later, because it had just been modified, but
|
|
we want the original value. */
|
|
for (j = 2; j < 32; j++)
|
|
p += GEN_STORE (p, j, 1, min_frame + j * rsz, is_64);
|
|
p += GEN_STORE (p, 0, 1, min_frame + 0 * rsz, is_64);
|
|
/* Set r0 to the original value of r1 before adjusting stack frame,
|
|
and then save it. */
|
|
p += GEN_ADDI (p, 0, 1, frame_size);
|
|
p += GEN_STORE (p, 0, 1, min_frame + 1 * rsz, is_64);
|
|
|
|
/* Save CR, XER, LR, and CTR. */
|
|
p += GEN_MFCR (p, 3); /* mfcr r3 */
|
|
p += GEN_MFSPR (p, 4, 1); /* mfxer r4 */
|
|
p += GEN_MFSPR (p, 5, 8); /* mflr r5 */
|
|
p += GEN_MFSPR (p, 6, 9); /* mfctr r6 */
|
|
p += GEN_STORE (p, 3, 1, min_frame + 32 * rsz, is_64);/* std r3, 32(r1) */
|
|
p += GEN_STORE (p, 4, 1, min_frame + 33 * rsz, is_64);/* std r4, 33(r1) */
|
|
p += GEN_STORE (p, 5, 1, min_frame + 34 * rsz, is_64);/* std r5, 34(r1) */
|
|
p += GEN_STORE (p, 6, 1, min_frame + 35 * rsz, is_64);/* std r6, 35(r1) */
|
|
|
|
/* Save PC<tpaddr> */
|
|
p += gen_limm (p, 3, tpaddr, is_64);
|
|
p += GEN_STORE (p, 3, 1, min_frame + 36 * rsz, is_64);
|
|
|
|
|
|
/* Setup arguments to collector. */
|
|
/* Set r4 to collected registers. */
|
|
p += GEN_ADDI (p, 4, 1, min_frame);
|
|
/* Set r3 to TPOINT. */
|
|
p += gen_limm (p, 3, tpoint, is_64);
|
|
|
|
/* Prepare collecting_t object for lock. */
|
|
p += GEN_STORE (p, 3, 1, min_frame + 37 * rsz, is_64);
|
|
p += GEN_STORE (p, tp_reg, 1, min_frame + 38 * rsz, is_64);
|
|
/* Set R5 to collecting object. */
|
|
p += GEN_ADDI (p, 5, 1, 37 * rsz);
|
|
|
|
p += GEN_LWSYNC (p);
|
|
p += gen_atomic_xchg (p, lockaddr, 0, 5, is_64);
|
|
p += GEN_LWSYNC (p);
|
|
|
|
/* Call to collector. */
|
|
p += gen_call (p, collector, is_64, is_opd);
|
|
|
|
/* Simply write 0 to release the lock. */
|
|
p += gen_limm (p, 3, lockaddr, is_64);
|
|
p += gen_limm (p, 4, 0, is_64);
|
|
p += GEN_LWSYNC (p);
|
|
p += GEN_STORE (p, 4, 3, 0, is_64);
|
|
|
|
/* Restore stack and registers. */
|
|
p += GEN_LOAD (p, 3, 1, min_frame + 32 * rsz, is_64); /* ld r3, 32(r1) */
|
|
p += GEN_LOAD (p, 4, 1, min_frame + 33 * rsz, is_64); /* ld r4, 33(r1) */
|
|
p += GEN_LOAD (p, 5, 1, min_frame + 34 * rsz, is_64); /* ld r5, 34(r1) */
|
|
p += GEN_LOAD (p, 6, 1, min_frame + 35 * rsz, is_64); /* ld r6, 35(r1) */
|
|
p += GEN_MTCR (p, 3); /* mtcr r3 */
|
|
p += GEN_MTSPR (p, 4, 1); /* mtxer r4 */
|
|
p += GEN_MTSPR (p, 5, 8); /* mtlr r5 */
|
|
p += GEN_MTSPR (p, 6, 9); /* mtctr r6 */
|
|
|
|
/* Restore GPRs. */
|
|
for (j = 2; j < 32; j++)
|
|
p += GEN_LOAD (p, j, 1, min_frame + j * rsz, is_64);
|
|
p += GEN_LOAD (p, 0, 1, min_frame + 0 * rsz, is_64);
|
|
/* Restore SP. */
|
|
p += GEN_ADDI (p, 1, 1, frame_size);
|
|
|
|
/* Flush instructions to inferior memory. */
|
|
write_inferior_memory (buildaddr, (unsigned char *) buf, (p - buf) * 4);
|
|
|
|
/* Now, insert the original instruction to execute in the jump pad. */
|
|
*adjusted_insn_addr = buildaddr + (p - buf) * 4;
|
|
*adjusted_insn_addr_end = *adjusted_insn_addr;
|
|
ppc_relocate_instruction (adjusted_insn_addr_end, tpaddr);
|
|
|
|
/* Verify the relocation size. If should be 4 for normal copy,
|
|
8 or 12 for some conditional branch. */
|
|
if ((*adjusted_insn_addr_end - *adjusted_insn_addr == 0)
|
|
|| (*adjusted_insn_addr_end - *adjusted_insn_addr > 12))
|
|
{
|
|
sprintf (err, "E.Unexpected instruction length = %d"
|
|
"when relocate instruction.",
|
|
(int) (*adjusted_insn_addr_end - *adjusted_insn_addr));
|
|
return 1;
|
|
}
|
|
|
|
buildaddr = *adjusted_insn_addr_end;
|
|
p = buf;
|
|
/* Finally, write a jump back to the program. */
|
|
offset = (tpaddr + 4) - buildaddr;
|
|
if (offset >= (1 << 25) || offset < -(1 << 25))
|
|
{
|
|
sprintf (err, "E.Jump back from jump pad too far from tracepoint "
|
|
"(offset 0x%x > 26-bit).", offset);
|
|
return 1;
|
|
}
|
|
/* b <tpaddr+4> */
|
|
p += GEN_B (p, offset);
|
|
write_inferior_memory (buildaddr, (unsigned char *) buf, (p - buf) * 4);
|
|
*jump_entry = buildaddr + (p - buf) * 4;
|
|
|
|
/* The jump pad is now built. Wire in a jump to our jump pad. This
|
|
is always done last (by our caller actually), so that we can
|
|
install fast tracepoints with threads running. This relies on
|
|
the agent's atomic write support. */
|
|
offset = entryaddr - tpaddr;
|
|
if (offset >= (1 << 25) || offset < -(1 << 25))
|
|
{
|
|
sprintf (err, "E.Jump back from jump pad too far from tracepoint "
|
|
"(offset 0x%x > 26-bit).", offset);
|
|
return 1;
|
|
}
|
|
/* b <jentry> */
|
|
GEN_B ((uint32_t *) jjump_pad_insn, offset);
|
|
*jjump_pad_insn_size = 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Returns the minimum instruction length for installing a tracepoint. */
|
|
|
|
static int
|
|
ppc_get_min_fast_tracepoint_insn_len (void)
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
/* Emits a given buffer into the target at current_insn_ptr. Length
|
|
is in units of 32-bit words. */
|
|
|
|
static void
|
|
emit_insns (uint32_t *buf, int n)
|
|
{
|
|
n = n * sizeof (uint32_t);
|
|
write_inferior_memory (current_insn_ptr, (unsigned char *) buf, n);
|
|
current_insn_ptr += n;
|
|
}
|
|
|
|
#define __EMIT_ASM(NAME, INSNS) \
|
|
do \
|
|
{ \
|
|
extern uint32_t start_bcax_ ## NAME []; \
|
|
extern uint32_t end_bcax_ ## NAME []; \
|
|
emit_insns (start_bcax_ ## NAME, \
|
|
end_bcax_ ## NAME - start_bcax_ ## NAME); \
|
|
__asm__ (".section .text.__ppcbcax\n\t" \
|
|
"start_bcax_" #NAME ":\n\t" \
|
|
INSNS "\n\t" \
|
|
"end_bcax_" #NAME ":\n\t" \
|
|
".previous\n\t"); \
|
|
} while (0)
|
|
|
|
#define _EMIT_ASM(NAME, INSNS) __EMIT_ASM (NAME, INSNS)
|
|
#define EMIT_ASM(INSNS) _EMIT_ASM (__LINE__, INSNS)
|
|
|
|
/*
|
|
|
|
Bytecode execution stack frame - 32-bit
|
|
|
|
| LR save area (SP + 4)
|
|
SP' -> +- Back chain (SP + 0)
|
|
| Save r31 for access saved arguments
|
|
| Save r30 for bytecode stack pointer
|
|
| Save r4 for incoming argument *value
|
|
| Save r3 for incoming argument regs
|
|
r30 -> +- Bytecode execution stack
|
|
|
|
|
| 64-byte (8 doublewords) at initial.
|
|
| Expand stack as needed.
|
|
|
|
|
+-
|
|
| Some padding for minimum stack frame and 16-byte alignment.
|
|
| 16 bytes.
|
|
SP +- Back-chain (SP')
|
|
|
|
initial frame size
|
|
= 16 + (4 * 4) + 64
|
|
= 96
|
|
|
|
r30 is the stack-pointer for bytecode machine.
|
|
It should point to next-empty, so we can use LDU for pop.
|
|
r3 is used for cache of the high part of TOP value.
|
|
It was the first argument, pointer to regs.
|
|
r4 is used for cache of the low part of TOP value.
|
|
It was the second argument, pointer to the result.
|
|
We should set *result = TOP after leaving this function.
|
|
|
|
Note:
|
|
* To restore stack at epilogue
|
|
=> sp = r31
|
|
* To check stack is big enough for bytecode execution.
|
|
=> r30 - 8 > SP + 8
|
|
* To return execution result.
|
|
=> 0(r4) = TOP
|
|
|
|
*/
|
|
|
|
/* Regardless of endian, register 3 is always high part, 4 is low part.
|
|
These defines are used when the register pair is stored/loaded.
|
|
Likewise, to simplify code, have a similiar define for 5:6. */
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
#define TOP_FIRST "4"
|
|
#define TOP_SECOND "3"
|
|
#define TMP_FIRST "6"
|
|
#define TMP_SECOND "5"
|
|
#else
|
|
#define TOP_FIRST "3"
|
|
#define TOP_SECOND "4"
|
|
#define TMP_FIRST "5"
|
|
#define TMP_SECOND "6"
|
|
#endif
|
|
|
|
/* Emit prologue in inferior memory. See above comments. */
|
|
|
|
static void
|
|
ppc_emit_prologue (void)
|
|
{
|
|
EMIT_ASM (/* Save return address. */
|
|
"mflr 0 \n"
|
|
"stw 0, 4(1) \n"
|
|
/* Adjust SP. 96 is the initial frame size. */
|
|
"stwu 1, -96(1) \n"
|
|
/* Save r30 and incoming arguments. */
|
|
"stw 31, 96-4(1) \n"
|
|
"stw 30, 96-8(1) \n"
|
|
"stw 4, 96-12(1) \n"
|
|
"stw 3, 96-16(1) \n"
|
|
/* Point r31 to original r1 for access arguments. */
|
|
"addi 31, 1, 96 \n"
|
|
/* Set r30 to pointing stack-top. */
|
|
"addi 30, 1, 64 \n"
|
|
/* Initial r3/TOP to 0. */
|
|
"li 3, 0 \n"
|
|
"li 4, 0 \n");
|
|
}
|
|
|
|
/* Emit epilogue in inferior memory. See above comments. */
|
|
|
|
static void
|
|
ppc_emit_epilogue (void)
|
|
{
|
|
EMIT_ASM (/* *result = TOP */
|
|
"lwz 5, -12(31) \n"
|
|
"stw " TOP_FIRST ", 0(5) \n"
|
|
"stw " TOP_SECOND ", 4(5) \n"
|
|
/* Restore registers. */
|
|
"lwz 31, -4(31) \n"
|
|
"lwz 30, -8(31) \n"
|
|
/* Restore SP. */
|
|
"lwz 1, 0(1) \n"
|
|
/* Restore LR. */
|
|
"lwz 0, 4(1) \n"
|
|
/* Return 0 for no-error. */
|
|
"li 3, 0 \n"
|
|
"mtlr 0 \n"
|
|
"blr \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] + TOP */
|
|
|
|
static void
|
|
ppc_emit_add (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30)\n"
|
|
"addc 4, 6, 4 \n"
|
|
"adde 3, 5, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] - TOP */
|
|
|
|
static void
|
|
ppc_emit_sub (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"subfc 4, 4, 6 \n"
|
|
"subfe 3, 3, 5 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] * TOP */
|
|
|
|
static void
|
|
ppc_emit_mul (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"mulhwu 7, 6, 4 \n"
|
|
"mullw 3, 6, 3 \n"
|
|
"mullw 5, 4, 5 \n"
|
|
"mullw 4, 6, 4 \n"
|
|
"add 3, 5, 3 \n"
|
|
"add 3, 7, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] << TOP */
|
|
|
|
static void
|
|
ppc_emit_lsh (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"subfic 3, 4, 32\n" /* r3 = 32 - TOP */
|
|
"addi 7, 4, -32\n" /* r7 = TOP - 32 */
|
|
"slw 5, 5, 4\n" /* Shift high part left */
|
|
"slw 4, 6, 4\n" /* Shift low part left */
|
|
"srw 3, 6, 3\n" /* Shift low to high if shift < 32 */
|
|
"slw 7, 6, 7\n" /* Shift low to high if shift >= 32 */
|
|
"or 3, 5, 3\n"
|
|
"or 3, 7, 3\n"); /* Assemble high part */
|
|
}
|
|
|
|
/* Top = stack[--sp] >> TOP
|
|
(Arithmetic shift right) */
|
|
|
|
static void
|
|
ppc_emit_rsh_signed (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"addi 7, 4, -32\n" /* r7 = TOP - 32 */
|
|
"sraw 3, 5, 4\n" /* Shift high part right */
|
|
"cmpwi 7, 1\n"
|
|
"blt 0, 1f\n" /* If shift <= 32, goto 1: */
|
|
"sraw 4, 5, 7\n" /* Shift high to low */
|
|
"b 2f\n"
|
|
"1:\n"
|
|
"subfic 7, 4, 32\n" /* r7 = 32 - TOP */
|
|
"srw 4, 6, 4\n" /* Shift low part right */
|
|
"slw 5, 5, 7\n" /* Shift high to low */
|
|
"or 4, 4, 5\n" /* Assemble low part */
|
|
"2:\n");
|
|
}
|
|
|
|
/* Top = stack[--sp] >> TOP
|
|
(Logical shift right) */
|
|
|
|
static void
|
|
ppc_emit_rsh_unsigned (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"subfic 3, 4, 32\n" /* r3 = 32 - TOP */
|
|
"addi 7, 4, -32\n" /* r7 = TOP - 32 */
|
|
"srw 6, 6, 4\n" /* Shift low part right */
|
|
"slw 3, 5, 3\n" /* Shift high to low if shift < 32 */
|
|
"srw 7, 5, 7\n" /* Shift high to low if shift >= 32 */
|
|
"or 6, 6, 3\n"
|
|
"srw 3, 5, 4\n" /* Shift high part right */
|
|
"or 4, 6, 7\n"); /* Assemble low part */
|
|
}
|
|
|
|
/* Emit code for signed-extension specified by ARG. */
|
|
|
|
static void
|
|
ppc_emit_ext (int arg)
|
|
{
|
|
switch (arg)
|
|
{
|
|
case 8:
|
|
EMIT_ASM ("extsb 4, 4\n"
|
|
"srawi 3, 4, 31");
|
|
break;
|
|
case 16:
|
|
EMIT_ASM ("extsh 4, 4\n"
|
|
"srawi 3, 4, 31");
|
|
break;
|
|
case 32:
|
|
EMIT_ASM ("srawi 3, 4, 31");
|
|
break;
|
|
default:
|
|
emit_error = 1;
|
|
}
|
|
}
|
|
|
|
/* Emit code for zero-extension specified by ARG. */
|
|
|
|
static void
|
|
ppc_emit_zero_ext (int arg)
|
|
{
|
|
switch (arg)
|
|
{
|
|
case 8:
|
|
EMIT_ASM ("clrlwi 4,4,24\n"
|
|
"li 3, 0\n");
|
|
break;
|
|
case 16:
|
|
EMIT_ASM ("clrlwi 4,4,16\n"
|
|
"li 3, 0\n");
|
|
break;
|
|
case 32:
|
|
EMIT_ASM ("li 3, 0");
|
|
break;
|
|
default:
|
|
emit_error = 1;
|
|
}
|
|
}
|
|
|
|
/* TOP = !TOP
|
|
i.e., TOP = (TOP == 0) ? 1 : 0; */
|
|
|
|
static void
|
|
ppc_emit_log_not (void)
|
|
{
|
|
EMIT_ASM ("or 4, 3, 4 \n"
|
|
"cntlzw 4, 4 \n"
|
|
"srwi 4, 4, 5 \n"
|
|
"li 3, 0 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] & TOP */
|
|
|
|
static void
|
|
ppc_emit_bit_and (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"and 4, 6, 4 \n"
|
|
"and 3, 5, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] | TOP */
|
|
|
|
static void
|
|
ppc_emit_bit_or (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"or 4, 6, 4 \n"
|
|
"or 3, 5, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] ^ TOP */
|
|
|
|
static void
|
|
ppc_emit_bit_xor (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"xor 4, 6, 4 \n"
|
|
"xor 3, 5, 3 \n");
|
|
}
|
|
|
|
/* TOP = ~TOP
|
|
i.e., TOP = ~(TOP | TOP) */
|
|
|
|
static void
|
|
ppc_emit_bit_not (void)
|
|
{
|
|
EMIT_ASM ("nor 3, 3, 3 \n"
|
|
"nor 4, 4, 4 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] == TOP */
|
|
|
|
static void
|
|
ppc_emit_equal (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"xor 4, 6, 4 \n"
|
|
"xor 3, 5, 3 \n"
|
|
"or 4, 3, 4 \n"
|
|
"cntlzw 4, 4 \n"
|
|
"srwi 4, 4, 5 \n"
|
|
"li 3, 0 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] < TOP
|
|
(Signed comparison) */
|
|
|
|
static void
|
|
ppc_emit_less_signed (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"cmplw 6, 6, 4 \n"
|
|
"cmpw 7, 5, 3 \n"
|
|
/* CR6 bit 0 = low less and high equal */
|
|
"crand 6*4+0, 6*4+0, 7*4+2\n"
|
|
/* CR7 bit 0 = (low less and high equal) or high less */
|
|
"cror 7*4+0, 7*4+0, 6*4+0\n"
|
|
"mfcr 4 \n"
|
|
"rlwinm 4, 4, 29, 31, 31 \n"
|
|
"li 3, 0 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] < TOP
|
|
(Unsigned comparison) */
|
|
|
|
static void
|
|
ppc_emit_less_unsigned (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"cmplw 6, 6, 4 \n"
|
|
"cmplw 7, 5, 3 \n"
|
|
/* CR6 bit 0 = low less and high equal */
|
|
"crand 6*4+0, 6*4+0, 7*4+2\n"
|
|
/* CR7 bit 0 = (low less and high equal) or high less */
|
|
"cror 7*4+0, 7*4+0, 6*4+0\n"
|
|
"mfcr 4 \n"
|
|
"rlwinm 4, 4, 29, 31, 31 \n"
|
|
"li 3, 0 \n");
|
|
}
|
|
|
|
/* Access the memory address in TOP in size of SIZE.
|
|
Zero-extend the read value. */
|
|
|
|
static void
|
|
ppc_emit_ref (int size)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 1:
|
|
EMIT_ASM ("lbz 4, 0(4)\n"
|
|
"li 3, 0");
|
|
break;
|
|
case 2:
|
|
EMIT_ASM ("lhz 4, 0(4)\n"
|
|
"li 3, 0");
|
|
break;
|
|
case 4:
|
|
EMIT_ASM ("lwz 4, 0(4)\n"
|
|
"li 3, 0");
|
|
break;
|
|
case 8:
|
|
if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
EMIT_ASM ("lwz 3, 4(4)\n"
|
|
"lwz 4, 0(4)");
|
|
else
|
|
EMIT_ASM ("lwz 3, 0(4)\n"
|
|
"lwz 4, 4(4)");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* TOP = NUM */
|
|
|
|
static void
|
|
ppc_emit_const (LONGEST num)
|
|
{
|
|
uint32_t buf[10];
|
|
uint32_t *p = buf;
|
|
|
|
p += gen_limm (p, 3, num >> 32 & 0xffffffff, 0);
|
|
p += gen_limm (p, 4, num & 0xffffffff, 0);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Set TOP to the value of register REG by calling get_raw_reg function
|
|
with two argument, collected buffer and register number. */
|
|
|
|
static void
|
|
ppc_emit_reg (int reg)
|
|
{
|
|
uint32_t buf[13];
|
|
uint32_t *p = buf;
|
|
|
|
/* fctx->regs is passed in r3 and then saved in -16(31). */
|
|
p += GEN_LWZ (p, 3, 31, -16);
|
|
p += GEN_LI (p, 4, reg); /* li r4, reg */
|
|
p += gen_call (p, get_raw_reg_func_addr (), 0, 0);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
|
|
if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
{
|
|
EMIT_ASM ("mr 5, 4\n"
|
|
"mr 4, 3\n"
|
|
"mr 3, 5\n");
|
|
}
|
|
}
|
|
|
|
/* TOP = stack[--sp] */
|
|
|
|
static void
|
|
ppc_emit_pop (void)
|
|
{
|
|
EMIT_ASM ("lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30) \n");
|
|
}
|
|
|
|
/* stack[sp++] = TOP
|
|
|
|
Because we may use up bytecode stack, expand 8 doublewords more
|
|
if needed. */
|
|
|
|
static void
|
|
ppc_emit_stack_flush (void)
|
|
{
|
|
/* Make sure bytecode stack is big enough before push.
|
|
Otherwise, expand 64-byte more. */
|
|
|
|
EMIT_ASM (" stw " TOP_FIRST ", 0(30) \n"
|
|
" stw " TOP_SECOND ", 4(30)\n"
|
|
" addi 5, 30, -(8 + 8) \n"
|
|
" cmpw 7, 5, 1 \n"
|
|
" bgt 7, 1f \n"
|
|
" stwu 31, -64(1) \n"
|
|
"1:addi 30, 30, -8 \n");
|
|
}
|
|
|
|
/* Swap TOP and stack[sp-1] */
|
|
|
|
static void
|
|
ppc_emit_swap (void)
|
|
{
|
|
EMIT_ASM ("lwz " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 12(30) \n"
|
|
"stw " TOP_FIRST ", 8(30) \n"
|
|
"stw " TOP_SECOND ", 12(30) \n"
|
|
"mr 3, 5 \n"
|
|
"mr 4, 6 \n");
|
|
}
|
|
|
|
/* Discard N elements in the stack. Also used for ppc64. */
|
|
|
|
static void
|
|
ppc_emit_stack_adjust (int n)
|
|
{
|
|
uint32_t buf[6];
|
|
uint32_t *p = buf;
|
|
|
|
n = n << 3;
|
|
if ((n >> 15) != 0)
|
|
{
|
|
emit_error = 1;
|
|
return;
|
|
}
|
|
|
|
p += GEN_ADDI (p, 30, 30, n);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Call function FN. */
|
|
|
|
static void
|
|
ppc_emit_call (CORE_ADDR fn)
|
|
{
|
|
uint32_t buf[11];
|
|
uint32_t *p = buf;
|
|
|
|
p += gen_call (p, fn, 0, 0);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* FN's prototype is `LONGEST(*fn)(int)'.
|
|
TOP = fn (arg1)
|
|
*/
|
|
|
|
static void
|
|
ppc_emit_int_call_1 (CORE_ADDR fn, int arg1)
|
|
{
|
|
uint32_t buf[15];
|
|
uint32_t *p = buf;
|
|
|
|
/* Setup argument. arg1 is a 16-bit value. */
|
|
p += gen_limm (p, 3, (uint32_t) arg1, 0);
|
|
p += gen_call (p, fn, 0, 0);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
|
|
if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
{
|
|
EMIT_ASM ("mr 5, 4\n"
|
|
"mr 4, 3\n"
|
|
"mr 3, 5\n");
|
|
}
|
|
}
|
|
|
|
/* FN's prototype is `void(*fn)(int,LONGEST)'.
|
|
fn (arg1, TOP)
|
|
|
|
TOP should be preserved/restored before/after the call. */
|
|
|
|
static void
|
|
ppc_emit_void_call_2 (CORE_ADDR fn, int arg1)
|
|
{
|
|
uint32_t buf[21];
|
|
uint32_t *p = buf;
|
|
|
|
/* Save TOP. 0(30) is next-empty. */
|
|
p += GEN_STW (p, 3, 30, 0);
|
|
p += GEN_STW (p, 4, 30, 4);
|
|
|
|
/* Setup argument. arg1 is a 16-bit value. */
|
|
if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
|
{
|
|
p += GEN_MR (p, 5, 4);
|
|
p += GEN_MR (p, 6, 3);
|
|
}
|
|
else
|
|
{
|
|
p += GEN_MR (p, 5, 3);
|
|
p += GEN_MR (p, 6, 4);
|
|
}
|
|
p += gen_limm (p, 3, (uint32_t) arg1, 0);
|
|
p += gen_call (p, fn, 0, 0);
|
|
|
|
/* Restore TOP */
|
|
p += GEN_LWZ (p, 3, 30, 0);
|
|
p += GEN_LWZ (p, 4, 30, 4);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Note in the following goto ops:
|
|
|
|
When emitting goto, the target address is later relocated by
|
|
write_goto_address. OFFSET_P is the offset of the branch instruction
|
|
in the code sequence, and SIZE_P is how to relocate the instruction,
|
|
recognized by ppc_write_goto_address. In current implementation,
|
|
SIZE can be either 24 or 14 for branch of conditional-branch instruction.
|
|
*/
|
|
|
|
/* If TOP is true, goto somewhere. Otherwise, just fall-through. */
|
|
|
|
static void
|
|
ppc_emit_if_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("or. 3, 3, 4 \n"
|
|
"lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30) \n"
|
|
"1:bne 0, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Unconditional goto. Also used for ppc64. */
|
|
|
|
static void
|
|
ppc_emit_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("1:b 1b");
|
|
|
|
if (offset_p)
|
|
*offset_p = 0;
|
|
if (size_p)
|
|
*size_p = 24;
|
|
}
|
|
|
|
/* Goto if stack[--sp] == TOP */
|
|
|
|
static void
|
|
ppc_emit_eq_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"xor 4, 6, 4 \n"
|
|
"xor 3, 5, 3 \n"
|
|
"or. 3, 3, 4 \n"
|
|
"lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30) \n"
|
|
"1:beq 0, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 28;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] != TOP */
|
|
|
|
static void
|
|
ppc_emit_ne_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"xor 4, 6, 4 \n"
|
|
"xor 3, 5, 3 \n"
|
|
"or. 3, 3, 4 \n"
|
|
"lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30) \n"
|
|
"1:bne 0, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 28;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] < TOP */
|
|
|
|
static void
|
|
ppc_emit_lt_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"cmplw 6, 6, 4 \n"
|
|
"cmpw 7, 5, 3 \n"
|
|
/* CR6 bit 0 = low less and high equal */
|
|
"crand 6*4+0, 6*4+0, 7*4+2\n"
|
|
/* CR7 bit 0 = (low less and high equal) or high less */
|
|
"cror 7*4+0, 7*4+0, 6*4+0\n"
|
|
"lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30)\n"
|
|
"1:blt 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 32;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] <= TOP */
|
|
|
|
static void
|
|
ppc_emit_le_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"cmplw 6, 6, 4 \n"
|
|
"cmpw 7, 5, 3 \n"
|
|
/* CR6 bit 0 = low less/equal and high equal */
|
|
"crandc 6*4+0, 7*4+2, 6*4+1\n"
|
|
/* CR7 bit 0 = (low less/eq and high equal) or high less */
|
|
"cror 7*4+0, 7*4+0, 6*4+0\n"
|
|
"lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30)\n"
|
|
"1:blt 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 32;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] > TOP */
|
|
|
|
static void
|
|
ppc_emit_gt_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"cmplw 6, 6, 4 \n"
|
|
"cmpw 7, 5, 3 \n"
|
|
/* CR6 bit 0 = low greater and high equal */
|
|
"crand 6*4+0, 6*4+1, 7*4+2\n"
|
|
/* CR7 bit 0 = (low greater and high equal) or high greater */
|
|
"cror 7*4+0, 7*4+1, 6*4+0\n"
|
|
"lwzu " TOP_FIRST ", 8(30) \n"
|
|
"lwz " TOP_SECOND ", 4(30)\n"
|
|
"1:blt 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 32;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] >= TOP */
|
|
|
|
static void
|
|
ppc_emit_ge_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("lwzu " TMP_FIRST ", 8(30) \n"
|
|
"lwz " TMP_SECOND ", 4(30) \n"
|
|
"cmplw 6, 6, 4 \n"
|
|
"cmpw 7, 5, 3 \n"
|
|
/* CR6 bit 0 = low ge and high equal */
|
|
"crandc 6*4+0, 7*4+2, 6*4+0\n"
|
|
/* CR7 bit 0 = (low ge and high equal) or high greater */
|
|
"cror 7*4+0, 7*4+1, 6*4+0\n"
|
|
"lwzu " TOP_FIRST ", 8(30)\n"
|
|
"lwz " TOP_SECOND ", 4(30)\n"
|
|
"1:blt 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 32;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Relocate previous emitted branch instruction. FROM is the address
|
|
of the branch instruction, TO is the goto target address, and SIZE
|
|
if the value we set by *SIZE_P before. Currently, it is either
|
|
24 or 14 of branch and conditional-branch instruction.
|
|
Also used for ppc64. */
|
|
|
|
static void
|
|
ppc_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
|
|
{
|
|
long rel = to - from;
|
|
uint32_t insn;
|
|
int opcd;
|
|
|
|
read_inferior_memory (from, (unsigned char *) &insn, 4);
|
|
opcd = (insn >> 26) & 0x3f;
|
|
|
|
switch (size)
|
|
{
|
|
case 14:
|
|
if (opcd != 16
|
|
|| (rel >= (1 << 15) || rel < -(1 << 15)))
|
|
emit_error = 1;
|
|
insn = (insn & ~0xfffc) | (rel & 0xfffc);
|
|
break;
|
|
case 24:
|
|
if (opcd != 18
|
|
|| (rel >= (1 << 25) || rel < -(1 << 25)))
|
|
emit_error = 1;
|
|
insn = (insn & ~0x3fffffc) | (rel & 0x3fffffc);
|
|
break;
|
|
default:
|
|
emit_error = 1;
|
|
}
|
|
|
|
if (!emit_error)
|
|
write_inferior_memory (from, (unsigned char *) &insn, 4);
|
|
}
|
|
|
|
/* Table of emit ops for 32-bit. */
|
|
|
|
static struct emit_ops ppc_emit_ops_impl =
|
|
{
|
|
ppc_emit_prologue,
|
|
ppc_emit_epilogue,
|
|
ppc_emit_add,
|
|
ppc_emit_sub,
|
|
ppc_emit_mul,
|
|
ppc_emit_lsh,
|
|
ppc_emit_rsh_signed,
|
|
ppc_emit_rsh_unsigned,
|
|
ppc_emit_ext,
|
|
ppc_emit_log_not,
|
|
ppc_emit_bit_and,
|
|
ppc_emit_bit_or,
|
|
ppc_emit_bit_xor,
|
|
ppc_emit_bit_not,
|
|
ppc_emit_equal,
|
|
ppc_emit_less_signed,
|
|
ppc_emit_less_unsigned,
|
|
ppc_emit_ref,
|
|
ppc_emit_if_goto,
|
|
ppc_emit_goto,
|
|
ppc_write_goto_address,
|
|
ppc_emit_const,
|
|
ppc_emit_call,
|
|
ppc_emit_reg,
|
|
ppc_emit_pop,
|
|
ppc_emit_stack_flush,
|
|
ppc_emit_zero_ext,
|
|
ppc_emit_swap,
|
|
ppc_emit_stack_adjust,
|
|
ppc_emit_int_call_1,
|
|
ppc_emit_void_call_2,
|
|
ppc_emit_eq_goto,
|
|
ppc_emit_ne_goto,
|
|
ppc_emit_lt_goto,
|
|
ppc_emit_le_goto,
|
|
ppc_emit_gt_goto,
|
|
ppc_emit_ge_goto
|
|
};
|
|
|
|
#ifdef __powerpc64__
|
|
|
|
/*
|
|
|
|
Bytecode execution stack frame - 64-bit
|
|
|
|
| LR save area (SP + 16)
|
|
| CR save area (SP + 8)
|
|
SP' -> +- Back chain (SP + 0)
|
|
| Save r31 for access saved arguments
|
|
| Save r30 for bytecode stack pointer
|
|
| Save r4 for incoming argument *value
|
|
| Save r3 for incoming argument regs
|
|
r30 -> +- Bytecode execution stack
|
|
|
|
|
| 64-byte (8 doublewords) at initial.
|
|
| Expand stack as needed.
|
|
|
|
|
+-
|
|
| Some padding for minimum stack frame.
|
|
| 112 for ELFv1.
|
|
SP +- Back-chain (SP')
|
|
|
|
initial frame size
|
|
= 112 + (4 * 8) + 64
|
|
= 208
|
|
|
|
r30 is the stack-pointer for bytecode machine.
|
|
It should point to next-empty, so we can use LDU for pop.
|
|
r3 is used for cache of TOP value.
|
|
It was the first argument, pointer to regs.
|
|
r4 is the second argument, pointer to the result.
|
|
We should set *result = TOP after leaving this function.
|
|
|
|
Note:
|
|
* To restore stack at epilogue
|
|
=> sp = r31
|
|
* To check stack is big enough for bytecode execution.
|
|
=> r30 - 8 > SP + 112
|
|
* To return execution result.
|
|
=> 0(r4) = TOP
|
|
|
|
*/
|
|
|
|
/* Emit prologue in inferior memory. See above comments. */
|
|
|
|
static void
|
|
ppc64v1_emit_prologue (void)
|
|
{
|
|
/* On ELFv1, function pointers really point to function descriptor,
|
|
so emit one here. We don't care about contents of words 1 and 2,
|
|
so let them just overlap out code. */
|
|
uint64_t opd = current_insn_ptr + 8;
|
|
uint32_t buf[2];
|
|
|
|
/* Mind the strict aliasing rules. */
|
|
memcpy (buf, &opd, sizeof buf);
|
|
emit_insns(buf, 2);
|
|
EMIT_ASM (/* Save return address. */
|
|
"mflr 0 \n"
|
|
"std 0, 16(1) \n"
|
|
/* Save r30 and incoming arguments. */
|
|
"std 31, -8(1) \n"
|
|
"std 30, -16(1) \n"
|
|
"std 4, -24(1) \n"
|
|
"std 3, -32(1) \n"
|
|
/* Point r31 to current r1 for access arguments. */
|
|
"mr 31, 1 \n"
|
|
/* Adjust SP. 208 is the initial frame size. */
|
|
"stdu 1, -208(1) \n"
|
|
/* Set r30 to pointing stack-top. */
|
|
"addi 30, 1, 168 \n"
|
|
/* Initial r3/TOP to 0. */
|
|
"li 3, 0 \n");
|
|
}
|
|
|
|
/* Emit prologue in inferior memory. See above comments. */
|
|
|
|
static void
|
|
ppc64v2_emit_prologue (void)
|
|
{
|
|
EMIT_ASM (/* Save return address. */
|
|
"mflr 0 \n"
|
|
"std 0, 16(1) \n"
|
|
/* Save r30 and incoming arguments. */
|
|
"std 31, -8(1) \n"
|
|
"std 30, -16(1) \n"
|
|
"std 4, -24(1) \n"
|
|
"std 3, -32(1) \n"
|
|
/* Point r31 to current r1 for access arguments. */
|
|
"mr 31, 1 \n"
|
|
/* Adjust SP. 208 is the initial frame size. */
|
|
"stdu 1, -208(1) \n"
|
|
/* Set r30 to pointing stack-top. */
|
|
"addi 30, 1, 168 \n"
|
|
/* Initial r3/TOP to 0. */
|
|
"li 3, 0 \n");
|
|
}
|
|
|
|
/* Emit epilogue in inferior memory. See above comments. */
|
|
|
|
static void
|
|
ppc64_emit_epilogue (void)
|
|
{
|
|
EMIT_ASM (/* Restore SP. */
|
|
"ld 1, 0(1) \n"
|
|
/* *result = TOP */
|
|
"ld 4, -24(1) \n"
|
|
"std 3, 0(4) \n"
|
|
/* Restore registers. */
|
|
"ld 31, -8(1) \n"
|
|
"ld 30, -16(1) \n"
|
|
/* Restore LR. */
|
|
"ld 0, 16(1) \n"
|
|
/* Return 0 for no-error. */
|
|
"li 3, 0 \n"
|
|
"mtlr 0 \n"
|
|
"blr \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] + TOP */
|
|
|
|
static void
|
|
ppc64_emit_add (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"add 3, 4, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] - TOP */
|
|
|
|
static void
|
|
ppc64_emit_sub (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"sub 3, 4, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] * TOP */
|
|
|
|
static void
|
|
ppc64_emit_mul (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"mulld 3, 4, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] << TOP */
|
|
|
|
static void
|
|
ppc64_emit_lsh (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"sld 3, 4, 3 \n");
|
|
}
|
|
|
|
/* Top = stack[--sp] >> TOP
|
|
(Arithmetic shift right) */
|
|
|
|
static void
|
|
ppc64_emit_rsh_signed (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"srad 3, 4, 3 \n");
|
|
}
|
|
|
|
/* Top = stack[--sp] >> TOP
|
|
(Logical shift right) */
|
|
|
|
static void
|
|
ppc64_emit_rsh_unsigned (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"srd 3, 4, 3 \n");
|
|
}
|
|
|
|
/* Emit code for signed-extension specified by ARG. */
|
|
|
|
static void
|
|
ppc64_emit_ext (int arg)
|
|
{
|
|
switch (arg)
|
|
{
|
|
case 8:
|
|
EMIT_ASM ("extsb 3, 3");
|
|
break;
|
|
case 16:
|
|
EMIT_ASM ("extsh 3, 3");
|
|
break;
|
|
case 32:
|
|
EMIT_ASM ("extsw 3, 3");
|
|
break;
|
|
default:
|
|
emit_error = 1;
|
|
}
|
|
}
|
|
|
|
/* Emit code for zero-extension specified by ARG. */
|
|
|
|
static void
|
|
ppc64_emit_zero_ext (int arg)
|
|
{
|
|
switch (arg)
|
|
{
|
|
case 8:
|
|
EMIT_ASM ("rldicl 3,3,0,56");
|
|
break;
|
|
case 16:
|
|
EMIT_ASM ("rldicl 3,3,0,48");
|
|
break;
|
|
case 32:
|
|
EMIT_ASM ("rldicl 3,3,0,32");
|
|
break;
|
|
default:
|
|
emit_error = 1;
|
|
}
|
|
}
|
|
|
|
/* TOP = !TOP
|
|
i.e., TOP = (TOP == 0) ? 1 : 0; */
|
|
|
|
static void
|
|
ppc64_emit_log_not (void)
|
|
{
|
|
EMIT_ASM ("cntlzd 3, 3 \n"
|
|
"srdi 3, 3, 6 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] & TOP */
|
|
|
|
static void
|
|
ppc64_emit_bit_and (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"and 3, 4, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] | TOP */
|
|
|
|
static void
|
|
ppc64_emit_bit_or (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"or 3, 4, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] ^ TOP */
|
|
|
|
static void
|
|
ppc64_emit_bit_xor (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"xor 3, 4, 3 \n");
|
|
}
|
|
|
|
/* TOP = ~TOP
|
|
i.e., TOP = ~(TOP | TOP) */
|
|
|
|
static void
|
|
ppc64_emit_bit_not (void)
|
|
{
|
|
EMIT_ASM ("nor 3, 3, 3 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] == TOP */
|
|
|
|
static void
|
|
ppc64_emit_equal (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"xor 3, 3, 4 \n"
|
|
"cntlzd 3, 3 \n"
|
|
"srdi 3, 3, 6 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] < TOP
|
|
(Signed comparison) */
|
|
|
|
static void
|
|
ppc64_emit_less_signed (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"mfcr 3 \n"
|
|
"rlwinm 3, 3, 29, 31, 31 \n");
|
|
}
|
|
|
|
/* TOP = stack[--sp] < TOP
|
|
(Unsigned comparison) */
|
|
|
|
static void
|
|
ppc64_emit_less_unsigned (void)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpld 7, 4, 3 \n"
|
|
"mfcr 3 \n"
|
|
"rlwinm 3, 3, 29, 31, 31 \n");
|
|
}
|
|
|
|
/* Access the memory address in TOP in size of SIZE.
|
|
Zero-extend the read value. */
|
|
|
|
static void
|
|
ppc64_emit_ref (int size)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 1:
|
|
EMIT_ASM ("lbz 3, 0(3)");
|
|
break;
|
|
case 2:
|
|
EMIT_ASM ("lhz 3, 0(3)");
|
|
break;
|
|
case 4:
|
|
EMIT_ASM ("lwz 3, 0(3)");
|
|
break;
|
|
case 8:
|
|
EMIT_ASM ("ld 3, 0(3)");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* TOP = NUM */
|
|
|
|
static void
|
|
ppc64_emit_const (LONGEST num)
|
|
{
|
|
uint32_t buf[5];
|
|
uint32_t *p = buf;
|
|
|
|
p += gen_limm (p, 3, num, 1);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Set TOP to the value of register REG by calling get_raw_reg function
|
|
with two argument, collected buffer and register number. */
|
|
|
|
static void
|
|
ppc64v1_emit_reg (int reg)
|
|
{
|
|
uint32_t buf[15];
|
|
uint32_t *p = buf;
|
|
|
|
/* fctx->regs is passed in r3 and then saved in 176(1). */
|
|
p += GEN_LD (p, 3, 31, -32);
|
|
p += GEN_LI (p, 4, reg);
|
|
p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
|
|
p += gen_call (p, get_raw_reg_func_addr (), 1, 1);
|
|
p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Likewise, for ELFv2. */
|
|
|
|
static void
|
|
ppc64v2_emit_reg (int reg)
|
|
{
|
|
uint32_t buf[12];
|
|
uint32_t *p = buf;
|
|
|
|
/* fctx->regs is passed in r3 and then saved in 176(1). */
|
|
p += GEN_LD (p, 3, 31, -32);
|
|
p += GEN_LI (p, 4, reg);
|
|
p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
|
|
p += gen_call (p, get_raw_reg_func_addr (), 1, 0);
|
|
p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* TOP = stack[--sp] */
|
|
|
|
static void
|
|
ppc64_emit_pop (void)
|
|
{
|
|
EMIT_ASM ("ldu 3, 8(30)");
|
|
}
|
|
|
|
/* stack[sp++] = TOP
|
|
|
|
Because we may use up bytecode stack, expand 8 doublewords more
|
|
if needed. */
|
|
|
|
static void
|
|
ppc64_emit_stack_flush (void)
|
|
{
|
|
/* Make sure bytecode stack is big enough before push.
|
|
Otherwise, expand 64-byte more. */
|
|
|
|
EMIT_ASM (" std 3, 0(30) \n"
|
|
" addi 4, 30, -(112 + 8) \n"
|
|
" cmpd 7, 4, 1 \n"
|
|
" bgt 7, 1f \n"
|
|
" stdu 31, -64(1) \n"
|
|
"1:addi 30, 30, -8 \n");
|
|
}
|
|
|
|
/* Swap TOP and stack[sp-1] */
|
|
|
|
static void
|
|
ppc64_emit_swap (void)
|
|
{
|
|
EMIT_ASM ("ld 4, 8(30) \n"
|
|
"std 3, 8(30) \n"
|
|
"mr 3, 4 \n");
|
|
}
|
|
|
|
/* Call function FN - ELFv1. */
|
|
|
|
static void
|
|
ppc64v1_emit_call (CORE_ADDR fn)
|
|
{
|
|
uint32_t buf[13];
|
|
uint32_t *p = buf;
|
|
|
|
p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
|
|
p += gen_call (p, fn, 1, 1);
|
|
p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Call function FN - ELFv2. */
|
|
|
|
static void
|
|
ppc64v2_emit_call (CORE_ADDR fn)
|
|
{
|
|
uint32_t buf[10];
|
|
uint32_t *p = buf;
|
|
|
|
p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
|
|
p += gen_call (p, fn, 1, 0);
|
|
p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* FN's prototype is `LONGEST(*fn)(int)'.
|
|
TOP = fn (arg1)
|
|
*/
|
|
|
|
static void
|
|
ppc64v1_emit_int_call_1 (CORE_ADDR fn, int arg1)
|
|
{
|
|
uint32_t buf[13];
|
|
uint32_t *p = buf;
|
|
|
|
/* Setup argument. arg1 is a 16-bit value. */
|
|
p += gen_limm (p, 3, arg1, 1);
|
|
p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
|
|
p += gen_call (p, fn, 1, 1);
|
|
p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Likewise for ELFv2. */
|
|
|
|
static void
|
|
ppc64v2_emit_int_call_1 (CORE_ADDR fn, int arg1)
|
|
{
|
|
uint32_t buf[10];
|
|
uint32_t *p = buf;
|
|
|
|
/* Setup argument. arg1 is a 16-bit value. */
|
|
p += gen_limm (p, 3, arg1, 1);
|
|
p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
|
|
p += gen_call (p, fn, 1, 0);
|
|
p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* FN's prototype is `void(*fn)(int,LONGEST)'.
|
|
fn (arg1, TOP)
|
|
|
|
TOP should be preserved/restored before/after the call. */
|
|
|
|
static void
|
|
ppc64v1_emit_void_call_2 (CORE_ADDR fn, int arg1)
|
|
{
|
|
uint32_t buf[17];
|
|
uint32_t *p = buf;
|
|
|
|
/* Save TOP. 0(30) is next-empty. */
|
|
p += GEN_STD (p, 3, 30, 0);
|
|
|
|
/* Setup argument. arg1 is a 16-bit value. */
|
|
p += GEN_MR (p, 4, 3); /* mr r4, r3 */
|
|
p += gen_limm (p, 3, arg1, 1);
|
|
p += GEN_STD (p, 2, 1, 40); /* Save TOC. */
|
|
p += gen_call (p, fn, 1, 1);
|
|
p += GEN_LD (p, 2, 1, 40); /* Restore TOC. */
|
|
|
|
/* Restore TOP */
|
|
p += GEN_LD (p, 3, 30, 0);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* Likewise for ELFv2. */
|
|
|
|
static void
|
|
ppc64v2_emit_void_call_2 (CORE_ADDR fn, int arg1)
|
|
{
|
|
uint32_t buf[14];
|
|
uint32_t *p = buf;
|
|
|
|
/* Save TOP. 0(30) is next-empty. */
|
|
p += GEN_STD (p, 3, 30, 0);
|
|
|
|
/* Setup argument. arg1 is a 16-bit value. */
|
|
p += GEN_MR (p, 4, 3); /* mr r4, r3 */
|
|
p += gen_limm (p, 3, arg1, 1);
|
|
p += GEN_STD (p, 2, 1, 24); /* Save TOC. */
|
|
p += gen_call (p, fn, 1, 0);
|
|
p += GEN_LD (p, 2, 1, 24); /* Restore TOC. */
|
|
|
|
/* Restore TOP */
|
|
p += GEN_LD (p, 3, 30, 0);
|
|
|
|
emit_insns (buf, p - buf);
|
|
gdb_assert ((p - buf) <= (sizeof (buf) / sizeof (*buf)));
|
|
}
|
|
|
|
/* If TOP is true, goto somewhere. Otherwise, just fall-through. */
|
|
|
|
static void
|
|
ppc64_emit_if_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("cmpdi 7, 3, 0 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:bne 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 8;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] == TOP */
|
|
|
|
static void
|
|
ppc64_emit_eq_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:beq 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] != TOP */
|
|
|
|
static void
|
|
ppc64_emit_ne_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:bne 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] < TOP */
|
|
|
|
static void
|
|
ppc64_emit_lt_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:blt 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] <= TOP */
|
|
|
|
static void
|
|
ppc64_emit_le_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:ble 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] > TOP */
|
|
|
|
static void
|
|
ppc64_emit_gt_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:bgt 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Goto if stack[--sp] >= TOP */
|
|
|
|
static void
|
|
ppc64_emit_ge_goto (int *offset_p, int *size_p)
|
|
{
|
|
EMIT_ASM ("ldu 4, 8(30) \n"
|
|
"cmpd 7, 4, 3 \n"
|
|
"ldu 3, 8(30) \n"
|
|
"1:bge 7, 1b \n");
|
|
|
|
if (offset_p)
|
|
*offset_p = 12;
|
|
if (size_p)
|
|
*size_p = 14;
|
|
}
|
|
|
|
/* Table of emit ops for 64-bit ELFv1. */
|
|
|
|
static struct emit_ops ppc64v1_emit_ops_impl =
|
|
{
|
|
ppc64v1_emit_prologue,
|
|
ppc64_emit_epilogue,
|
|
ppc64_emit_add,
|
|
ppc64_emit_sub,
|
|
ppc64_emit_mul,
|
|
ppc64_emit_lsh,
|
|
ppc64_emit_rsh_signed,
|
|
ppc64_emit_rsh_unsigned,
|
|
ppc64_emit_ext,
|
|
ppc64_emit_log_not,
|
|
ppc64_emit_bit_and,
|
|
ppc64_emit_bit_or,
|
|
ppc64_emit_bit_xor,
|
|
ppc64_emit_bit_not,
|
|
ppc64_emit_equal,
|
|
ppc64_emit_less_signed,
|
|
ppc64_emit_less_unsigned,
|
|
ppc64_emit_ref,
|
|
ppc64_emit_if_goto,
|
|
ppc_emit_goto,
|
|
ppc_write_goto_address,
|
|
ppc64_emit_const,
|
|
ppc64v1_emit_call,
|
|
ppc64v1_emit_reg,
|
|
ppc64_emit_pop,
|
|
ppc64_emit_stack_flush,
|
|
ppc64_emit_zero_ext,
|
|
ppc64_emit_swap,
|
|
ppc_emit_stack_adjust,
|
|
ppc64v1_emit_int_call_1,
|
|
ppc64v1_emit_void_call_2,
|
|
ppc64_emit_eq_goto,
|
|
ppc64_emit_ne_goto,
|
|
ppc64_emit_lt_goto,
|
|
ppc64_emit_le_goto,
|
|
ppc64_emit_gt_goto,
|
|
ppc64_emit_ge_goto
|
|
};
|
|
|
|
/* Table of emit ops for 64-bit ELFv2. */
|
|
|
|
static struct emit_ops ppc64v2_emit_ops_impl =
|
|
{
|
|
ppc64v2_emit_prologue,
|
|
ppc64_emit_epilogue,
|
|
ppc64_emit_add,
|
|
ppc64_emit_sub,
|
|
ppc64_emit_mul,
|
|
ppc64_emit_lsh,
|
|
ppc64_emit_rsh_signed,
|
|
ppc64_emit_rsh_unsigned,
|
|
ppc64_emit_ext,
|
|
ppc64_emit_log_not,
|
|
ppc64_emit_bit_and,
|
|
ppc64_emit_bit_or,
|
|
ppc64_emit_bit_xor,
|
|
ppc64_emit_bit_not,
|
|
ppc64_emit_equal,
|
|
ppc64_emit_less_signed,
|
|
ppc64_emit_less_unsigned,
|
|
ppc64_emit_ref,
|
|
ppc64_emit_if_goto,
|
|
ppc_emit_goto,
|
|
ppc_write_goto_address,
|
|
ppc64_emit_const,
|
|
ppc64v2_emit_call,
|
|
ppc64v2_emit_reg,
|
|
ppc64_emit_pop,
|
|
ppc64_emit_stack_flush,
|
|
ppc64_emit_zero_ext,
|
|
ppc64_emit_swap,
|
|
ppc_emit_stack_adjust,
|
|
ppc64v2_emit_int_call_1,
|
|
ppc64v2_emit_void_call_2,
|
|
ppc64_emit_eq_goto,
|
|
ppc64_emit_ne_goto,
|
|
ppc64_emit_lt_goto,
|
|
ppc64_emit_le_goto,
|
|
ppc64_emit_gt_goto,
|
|
ppc64_emit_ge_goto
|
|
};
|
|
|
|
#endif
|
|
|
|
/* Implementation of linux_target_ops method "emit_ops". */
|
|
|
|
static struct emit_ops *
|
|
ppc_emit_ops (void)
|
|
{
|
|
#ifdef __powerpc64__
|
|
struct regcache *regcache = get_thread_regcache (current_thread, 0);
|
|
|
|
if (register_size (regcache->tdesc, 0) == 8)
|
|
{
|
|
if (is_elfv2_inferior ())
|
|
return &ppc64v2_emit_ops_impl;
|
|
else
|
|
return &ppc64v1_emit_ops_impl;
|
|
}
|
|
#endif
|
|
return &ppc_emit_ops_impl;
|
|
}
|
|
|
|
/* Implementation of linux_target_ops method "get_ipa_tdesc_idx". */
|
|
|
|
static int
|
|
ppc_get_ipa_tdesc_idx (void)
|
|
{
|
|
struct regcache *regcache = get_thread_regcache (current_thread, 0);
|
|
const struct target_desc *tdesc = regcache->tdesc;
|
|
|
|
#ifdef __powerpc64__
|
|
if (tdesc == tdesc_powerpc_64l)
|
|
return PPC_TDESC_BASE;
|
|
if (tdesc == tdesc_powerpc_altivec64l)
|
|
return PPC_TDESC_ALTIVEC;
|
|
if (tdesc == tdesc_powerpc_cell64l)
|
|
return PPC_TDESC_CELL;
|
|
if (tdesc == tdesc_powerpc_vsx64l)
|
|
return PPC_TDESC_VSX;
|
|
if (tdesc == tdesc_powerpc_isa205_64l)
|
|
return PPC_TDESC_ISA205;
|
|
if (tdesc == tdesc_powerpc_isa205_altivec64l)
|
|
return PPC_TDESC_ISA205_ALTIVEC;
|
|
if (tdesc == tdesc_powerpc_isa205_vsx64l)
|
|
return PPC_TDESC_ISA205_VSX;
|
|
if (tdesc == tdesc_powerpc_isa205_ppr_dscr_vsx64l)
|
|
return PPC_TDESC_ISA205_PPR_DSCR_VSX;
|
|
if (tdesc == tdesc_powerpc_isa207_vsx64l)
|
|
return PPC_TDESC_ISA207_VSX;
|
|
if (tdesc == tdesc_powerpc_isa207_htm_vsx64l)
|
|
return PPC_TDESC_ISA207_HTM_VSX;
|
|
#endif
|
|
|
|
if (tdesc == tdesc_powerpc_32l)
|
|
return PPC_TDESC_BASE;
|
|
if (tdesc == tdesc_powerpc_altivec32l)
|
|
return PPC_TDESC_ALTIVEC;
|
|
if (tdesc == tdesc_powerpc_cell32l)
|
|
return PPC_TDESC_CELL;
|
|
if (tdesc == tdesc_powerpc_vsx32l)
|
|
return PPC_TDESC_VSX;
|
|
if (tdesc == tdesc_powerpc_isa205_32l)
|
|
return PPC_TDESC_ISA205;
|
|
if (tdesc == tdesc_powerpc_isa205_altivec32l)
|
|
return PPC_TDESC_ISA205_ALTIVEC;
|
|
if (tdesc == tdesc_powerpc_isa205_vsx32l)
|
|
return PPC_TDESC_ISA205_VSX;
|
|
if (tdesc == tdesc_powerpc_isa205_ppr_dscr_vsx32l)
|
|
return PPC_TDESC_ISA205_PPR_DSCR_VSX;
|
|
if (tdesc == tdesc_powerpc_isa207_vsx32l)
|
|
return PPC_TDESC_ISA207_VSX;
|
|
if (tdesc == tdesc_powerpc_isa207_htm_vsx32l)
|
|
return PPC_TDESC_ISA207_HTM_VSX;
|
|
if (tdesc == tdesc_powerpc_e500l)
|
|
return PPC_TDESC_E500;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct linux_target_ops the_low_target = {
|
|
ppc_arch_setup,
|
|
ppc_regs_info,
|
|
ppc_cannot_fetch_register,
|
|
ppc_cannot_store_register,
|
|
NULL, /* fetch_register */
|
|
ppc_get_pc,
|
|
ppc_set_pc,
|
|
NULL, /* breakpoint_kind_from_pc */
|
|
ppc_sw_breakpoint_from_kind,
|
|
NULL,
|
|
0,
|
|
ppc_breakpoint_at,
|
|
ppc_supports_z_point_type,
|
|
ppc_insert_point,
|
|
ppc_remove_point,
|
|
NULL,
|
|
NULL,
|
|
ppc_collect_ptrace_register,
|
|
ppc_supply_ptrace_register,
|
|
NULL, /* siginfo_fixup */
|
|
NULL, /* new_process */
|
|
NULL, /* delete_process */
|
|
NULL, /* new_thread */
|
|
NULL, /* delete_thread */
|
|
NULL, /* new_fork */
|
|
NULL, /* prepare_to_resume */
|
|
NULL, /* process_qsupported */
|
|
ppc_supports_tracepoints,
|
|
ppc_get_thread_area,
|
|
ppc_install_fast_tracepoint_jump_pad,
|
|
ppc_emit_ops,
|
|
ppc_get_min_fast_tracepoint_insn_len,
|
|
NULL, /* supports_range_stepping */
|
|
NULL, /* breakpoint_kind_from_current_state */
|
|
ppc_supports_hardware_single_step,
|
|
NULL, /* get_syscall_trapinfo */
|
|
ppc_get_ipa_tdesc_idx,
|
|
};
|
|
|
|
void
|
|
initialize_low_arch (void)
|
|
{
|
|
/* Initialize the Linux target descriptions. */
|
|
|
|
init_registers_powerpc_32l ();
|
|
init_registers_powerpc_altivec32l ();
|
|
init_registers_powerpc_cell32l ();
|
|
init_registers_powerpc_vsx32l ();
|
|
init_registers_powerpc_isa205_32l ();
|
|
init_registers_powerpc_isa205_altivec32l ();
|
|
init_registers_powerpc_isa205_vsx32l ();
|
|
init_registers_powerpc_isa205_ppr_dscr_vsx32l ();
|
|
init_registers_powerpc_isa207_vsx32l ();
|
|
init_registers_powerpc_isa207_htm_vsx32l ();
|
|
init_registers_powerpc_e500l ();
|
|
#if __powerpc64__
|
|
init_registers_powerpc_64l ();
|
|
init_registers_powerpc_altivec64l ();
|
|
init_registers_powerpc_cell64l ();
|
|
init_registers_powerpc_vsx64l ();
|
|
init_registers_powerpc_isa205_64l ();
|
|
init_registers_powerpc_isa205_altivec64l ();
|
|
init_registers_powerpc_isa205_vsx64l ();
|
|
init_registers_powerpc_isa205_ppr_dscr_vsx64l ();
|
|
init_registers_powerpc_isa207_vsx64l ();
|
|
init_registers_powerpc_isa207_htm_vsx64l ();
|
|
#endif
|
|
|
|
initialize_regsets_info (&ppc_regsets_info);
|
|
}
|