blob: db9a88219ea2891c0ecb5236ea690336e02d039d [file] [log] [blame]
/* GNU/Linux/PowerPC specific low level interface, for the remote server for
GDB.
Copyright (C) 1995-2021 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 "nat/linux-ptrace.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)
/* Linux target op definitions for the PowerPC architecture. */
class ppc_target : public linux_process_target
{
public:
const regs_info *get_regs_info () override;
const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
bool supports_z_point_type (char z_type) override;
void low_collect_ptrace_register (regcache *regcache, int regno,
char *buf) override;
void low_supply_ptrace_register (regcache *regcache, int regno,
const char *buf) override;
bool supports_tracepoints () override;
bool supports_fast_tracepoints () override;
int 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) override;
int get_min_fast_tracepoint_insn_len () override;
struct emit_ops *emit_ops () override;
int get_ipa_tdesc_idx () override;
protected:
void low_arch_setup () override;
bool low_cannot_fetch_register (int regno) override;
bool low_cannot_store_register (int regno) override;
bool low_supports_breakpoints () override;
CORE_ADDR low_get_pc (regcache *regcache) override;
void low_set_pc (regcache *regcache, CORE_ADDR newpc) override;
bool low_breakpoint_at (CORE_ADDR pc) override;
int low_insert_point (raw_bkpt_type type, CORE_ADDR addr,
int size, raw_breakpoint *bp) override;
int low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
int size, raw_breakpoint *bp) override;
int low_get_thread_area (int lwpid, CORE_ADDR *addrp) override;
};
/* The singleton target ops object. */
static ppc_target the_ppc_target;
/* 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;
}
bool
ppc_target::low_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 true;
#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 true;
return false;
}
bool
ppc_target::low_cannot_fetch_register (int regno)
{
return false;
}
void
ppc_target::low_collect_ptrace_register (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");
}
void
ppc_target::low_supply_ptrace_register (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");
}
bool
ppc_target::low_supports_breakpoints ()
{
return true;
}
CORE_ADDR
ppc_target::low_get_pc (regcache *regcache)
{
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;
}
}
void
ppc_target::low_set_pc (regcache *regcache, CORE_ADDR pc)
{
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);
}
}
#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 target ops method "sw_breakpoint_from_kind". */
const gdb_byte *
ppc_target::sw_breakpoint_from_kind (int kind, int *size)
{
*size = ppc_breakpoint_len;
return (const gdb_byte *) &ppc_breakpoint;
}
bool
ppc_target::low_breakpoint_at (CORE_ADDR where)
{
unsigned int insn;
read_memory (where, (unsigned char *) &insn, 4);
if (insn == ppc_breakpoint)
return true;
/* If necessary, recognize more trap instructions here. GDB only uses
the one. */
return false;
}
/* 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. */
bool
ppc_target::supports_z_point_type (char z_type)
{
switch (z_type)
{
case Z_PACKET_SW_BP:
return true;
case Z_PACKET_HW_BP:
case Z_PACKET_WRITE_WP:
case Z_PACKET_ACCESS_WP:
default:
return false;
}
}
/* Implement the low_insert_point linux target op.
Returns 0 on success, -1 on failure and 1 on unsupported. */
int
ppc_target::low_insert_point (raw_bkpt_type type, CORE_ADDR addr,
int size, 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 the low_remove_point linux target op.
Returns 0 on success, -1 on failure and 1 on unsupported. */
int
ppc_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
int size, 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;
ppc_target *my_ppc_target = (ppc_target *) the_linux_target;
for (i = 0; i < 32; i++)
my_ppc_target->low_collect_ptrace_register (regcache, i,
(char *) buf + ppc_regmap[i]);
for (i = 64; i < 70; i++)
my_ppc_target->low_collect_ptrace_register (regcache, i,
(char *) buf + ppc_regmap[i]);
for (i = 71; i < 73; i++)
my_ppc_target->low_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", &regset[0]);
supply_register_by_name (regcache, "ebbhr", &regset[8]);
supply_register_by_name (regcache, "bescr", &regset[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", &regset[0]);
collect_register_by_name (regcache, "sdar", &regset[8]);
collect_register_by_name (regcache, "sier", &regset[16]);
collect_register_by_name (regcache, "mmcr2", &regset[24]);
collect_register_by_name (regcache, "mmcr0", &regset[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", &regset[0]);
supply_register_by_name (regcache, "sdar", &regset[8]);
supply_register_by_name (regcache, "sier", &regset[16]);
supply_register_by_name (regcache, "mmcr2", &regset[24]);
supply_register_by_name (regcache, "mmcr0", &regset[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, &regset[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, &regset[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, &regset[i * size]);
endian_offset = 0;
if ((size == 8) && (__BYTE_ORDER == __BIG_ENDIAN))
endian_offset = 4;
supply_register_by_name (regcache, "ccr",
&regset[PT_CCR * size + endian_offset]);
supply_register_by_name (regcache, "cxer",
&regset[PT_XER * size + endian_offset]);
supply_register_by_name (regcache, "clr", &regset[PT_LNK * size]);
supply_register_by_name (regcache, "cctr", &regset[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, &regset[i * 8]);
supply_register_by_name (regcache, "cfpscr", &regset[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, &regset[i * 16]);
if (__BYTE_ORDER == __BIG_ENDIAN)
vscr_offset = 12;
supply_register_by_name (regcache, "cvscr",
&regset[32 * 16 + vscr_offset]);
supply_register_by_name (regcache, "cvrsave", &regset[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, &regset[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, &regset[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, &regset[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, &regset[i * 16]);
if (__BYTE_ORDER == __BIG_ENDIAN)
vscr_offset = 12;
collect_register_by_name (regcache, "vscr",
&regset[32 * 16 + vscr_offset]);
collect_register_by_name (regcache, "vrsave", &regset[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, &regset[i * 16]);
if (__BYTE_ORDER == __BIG_ENDIAN)
vscr_offset = 12;
supply_register_by_name (regcache, "vscr",
&regset[32 * 16 + vscr_offset]);
supply_register_by_name (regcache, "vrsave", &regset[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, &regset->evr[i]);
collect_register_by_name (regcache, "acc", &regset->acc);
collect_register_by_name (regcache, "spefscr", &regset->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, &regset->evr[i]);
supply_register_by_name (regcache, "acc", &regset->acc);
supply_register_by_name (regcache, "spefscr", &regset->spefscr);
}
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 myregs_info =
{
NULL, /* regset_bitmap */
&ppc_usrregs_info,
&ppc_regsets_info
};
const regs_info *
ppc_target::get_regs_info ()
{
return &myregs_info;
}
void
ppc_target::low_arch_setup ()
{
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_hwcap = linux_get_hwcap (features.wordsize);
ppc_hwcap2 = linux_get_hwcap2 (features.wordsize);
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;
}
}
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 target ops method "supports_tracepoints". */
bool
ppc_target::supports_tracepoints ()
{
return true;
}
/* 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. */
int
ppc_target::low_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
CORE_ADDR phdr;
Elf64_Ehdr ehdr;
const struct target_desc *tdesc = current_process ()->tdesc;
int wordsize = register_size (tdesc, 0);
if (!linux_get_auxv (wordsize, 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;
target_write_memory (*to, (unsigned char *) &insn, 4);
*to += 4;
/* Build a unconditional branch and copy LK bit. */
insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
target_write_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);
target_write_memory (*to, (unsigned char *) &bdnz_insn, 4);
*to += 4;
target_write_memory (*to, (unsigned char *) &bf_insn, 4);
*to += 4;
/* Build a unconditional branch and copy LK bit. */
insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
target_write_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);
target_write_memory (*to, (unsigned char *) &insn, 4);
*to += 4;
return;
}
}
target_write_memory (*to, (unsigned char *) &insn, 4);
*to += 4;
}
bool
ppc_target::supports_fast_tracepoints ()
{
return true;
}
/* Implement install_fast_tracepoint_jump_pad of target_ops.
See target.h for details. */
int
ppc_target::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 original 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. */
target_write_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);
target_write_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. */
int
ppc_target::get_min_fast_tracepoint_insn_len ()
{
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);
target_write_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)
target_write_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 target ops method "emit_ops". */
emit_ops *
ppc_target::emit_ops ()
{
#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 target ops method "get_ipa_tdesc_idx". */
int
ppc_target::get_ipa_tdesc_idx ()
{
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_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_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;
}
/* The linux target ops object. */
linux_process_target *the_linux_target = &the_ppc_target;
void
initialize_low_arch (void)
{
/* Initialize the Linux target descriptions. */
init_registers_powerpc_32l ();
init_registers_powerpc_altivec32l ();
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_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);
}