1da0c48c4Sopenharmony_ci/* Get previous frame state for an existing frame state.
2da0c48c4Sopenharmony_ci   Copyright (C) 2013 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of either
7da0c48c4Sopenharmony_ci
8da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
9da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
10da0c48c4Sopenharmony_ci       your option) any later version
11da0c48c4Sopenharmony_ci
12da0c48c4Sopenharmony_ci   or
13da0c48c4Sopenharmony_ci
14da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
15da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
16da0c48c4Sopenharmony_ci       your option) any later version
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci   or both in parallel, as here.
19da0c48c4Sopenharmony_ci
20da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
21da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
22da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23da0c48c4Sopenharmony_ci   General Public License for more details.
24da0c48c4Sopenharmony_ci
25da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
26da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
27da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
28da0c48c4Sopenharmony_ci
29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
30da0c48c4Sopenharmony_ci# include <config.h>
31da0c48c4Sopenharmony_ci#endif
32da0c48c4Sopenharmony_ci
33da0c48c4Sopenharmony_ci#include <stdlib.h>
34da0c48c4Sopenharmony_ci#include <assert.h>
35da0c48c4Sopenharmony_ci
36da0c48c4Sopenharmony_ci#define BACKEND s390_
37da0c48c4Sopenharmony_ci#include "libebl_CPU.h"
38da0c48c4Sopenharmony_ci
39da0c48c4Sopenharmony_ci/* s390/s390x do not annotate signal handler frame by CFI.  It would be also
40da0c48c4Sopenharmony_ci   difficult as PC points into a stub built on stack.  Function below is called
41da0c48c4Sopenharmony_ci   only if unwinder could not find CFI.  Function then verifies the register
42da0c48c4Sopenharmony_ci   state for this frame really belongs to a signal frame.  In such case it
43da0c48c4Sopenharmony_ci   fetches original registers saved by the signal frame.  */
44da0c48c4Sopenharmony_ci
45da0c48c4Sopenharmony_cibool
46da0c48c4Sopenharmony_cis390_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc,
47da0c48c4Sopenharmony_ci	     ebl_tid_registers_get_t *getfunc, ebl_pid_memory_read_t *readfunc,
48da0c48c4Sopenharmony_ci	     void *arg, bool *signal_framep)
49da0c48c4Sopenharmony_ci{
50da0c48c4Sopenharmony_ci  /* Caller already assumed caller adjustment but S390 instructions are 4 bytes
51da0c48c4Sopenharmony_ci     long.  Undo it.  */
52da0c48c4Sopenharmony_ci  if ((pc & 0x3) != 0x3)
53da0c48c4Sopenharmony_ci    return false;
54da0c48c4Sopenharmony_ci  pc++;
55da0c48c4Sopenharmony_ci  /* We can assume big-endian read here.  */
56da0c48c4Sopenharmony_ci  Dwarf_Word instr;
57da0c48c4Sopenharmony_ci  if (! readfunc (pc, &instr, arg))
58da0c48c4Sopenharmony_ci    return false;
59da0c48c4Sopenharmony_ci  /* Fetch only the very first two bytes.  */
60da0c48c4Sopenharmony_ci  instr = (instr >> (ebl->class == ELFCLASS64 ? 48 : 16)) & 0xffff;
61da0c48c4Sopenharmony_ci  /* See GDB s390_sigtramp_frame_sniffer.  */
62da0c48c4Sopenharmony_ci  /* Check for 'svc' as the first instruction.  */
63da0c48c4Sopenharmony_ci  if (((instr >> 8) & 0xff) != 0x0a)
64da0c48c4Sopenharmony_ci    return false;
65da0c48c4Sopenharmony_ci  /* Check for 'sigreturn' or 'rt_sigreturn' as the second instruction.  */
66da0c48c4Sopenharmony_ci  if ((instr & 0xff) != 119 && (instr & 0xff) != 173)
67da0c48c4Sopenharmony_ci    return false;
68da0c48c4Sopenharmony_ci  /* See GDB s390_sigtramp_frame_unwind_cache.  */
69da0c48c4Sopenharmony_ci  Dwarf_Word this_sp;
70da0c48c4Sopenharmony_ci  if (! getfunc (0 + 15, 1, &this_sp, arg))
71da0c48c4Sopenharmony_ci    return false;
72da0c48c4Sopenharmony_ci  unsigned word_size = ebl->class == ELFCLASS64 ? 8 : 4;
73da0c48c4Sopenharmony_ci  Dwarf_Addr next_cfa = this_sp + 16 * word_size + 32;
74da0c48c4Sopenharmony_ci  /* "New-style RT frame" is not supported,
75da0c48c4Sopenharmony_ci     assuming "Old-style RT frame and all non-RT frames".
76da0c48c4Sopenharmony_ci     Pointer to the array of saved registers is at NEXT_CFA + 8.  */
77da0c48c4Sopenharmony_ci  Dwarf_Word sigreg_ptr;
78da0c48c4Sopenharmony_ci  if (! readfunc (next_cfa + 8, &sigreg_ptr, arg))
79da0c48c4Sopenharmony_ci    return false;
80da0c48c4Sopenharmony_ci  /* Skip PSW mask.  */
81da0c48c4Sopenharmony_ci  sigreg_ptr += word_size;
82da0c48c4Sopenharmony_ci  /* Read PSW address.  */
83da0c48c4Sopenharmony_ci  Dwarf_Word val;
84da0c48c4Sopenharmony_ci  if (! readfunc (sigreg_ptr, &val, arg))
85da0c48c4Sopenharmony_ci    return false;
86da0c48c4Sopenharmony_ci  if (! setfunc (-1, 1, &val, arg))
87da0c48c4Sopenharmony_ci    return false;
88da0c48c4Sopenharmony_ci  sigreg_ptr += word_size;
89da0c48c4Sopenharmony_ci  /* Then the GPRs.  */
90da0c48c4Sopenharmony_ci  Dwarf_Word gprs[16];
91da0c48c4Sopenharmony_ci  for (int i = 0; i < 16; i++)
92da0c48c4Sopenharmony_ci    {
93da0c48c4Sopenharmony_ci      if (! readfunc (sigreg_ptr, &gprs[i], arg))
94da0c48c4Sopenharmony_ci	return false;
95da0c48c4Sopenharmony_ci      sigreg_ptr += word_size;
96da0c48c4Sopenharmony_ci    }
97da0c48c4Sopenharmony_ci  /* Then the ACRs.  Skip them, they are not used in CFI.  */
98da0c48c4Sopenharmony_ci  for (int i = 0; i < 16; i++)
99da0c48c4Sopenharmony_ci    sigreg_ptr += 4;
100da0c48c4Sopenharmony_ci  /* The floating-point control word.  */
101da0c48c4Sopenharmony_ci  sigreg_ptr += 8;
102da0c48c4Sopenharmony_ci  /* And finally the FPRs.  */
103da0c48c4Sopenharmony_ci  Dwarf_Word fprs[16];
104da0c48c4Sopenharmony_ci  for (int i = 0; i < 16; i++)
105da0c48c4Sopenharmony_ci    {
106da0c48c4Sopenharmony_ci      if (! readfunc (sigreg_ptr, &val, arg))
107da0c48c4Sopenharmony_ci	return false;
108da0c48c4Sopenharmony_ci      if (ebl->class == ELFCLASS32)
109da0c48c4Sopenharmony_ci	{
110da0c48c4Sopenharmony_ci	  Dwarf_Addr val_low;
111da0c48c4Sopenharmony_ci	  if (! readfunc (sigreg_ptr + 4, &val_low, arg))
112da0c48c4Sopenharmony_ci	    return false;
113da0c48c4Sopenharmony_ci	  val = (val << 32) | val_low;
114da0c48c4Sopenharmony_ci	}
115da0c48c4Sopenharmony_ci      fprs[i] = val;
116da0c48c4Sopenharmony_ci      sigreg_ptr += 8;
117da0c48c4Sopenharmony_ci    }
118da0c48c4Sopenharmony_ci  /* If we have them, the GPR upper halves are appended at the end.  */
119da0c48c4Sopenharmony_ci  if (ebl->class == ELFCLASS32)
120da0c48c4Sopenharmony_ci    {
121da0c48c4Sopenharmony_ci      /* Skip signal number.  */
122da0c48c4Sopenharmony_ci      sigreg_ptr += 4;
123da0c48c4Sopenharmony_ci      for (int i = 0; i < 16; i++)
124da0c48c4Sopenharmony_ci	{
125da0c48c4Sopenharmony_ci	  if (! readfunc (sigreg_ptr, &val, arg))
126da0c48c4Sopenharmony_ci	    return false;
127da0c48c4Sopenharmony_ci	  Dwarf_Word val_low = gprs[i];
128da0c48c4Sopenharmony_ci	  val = (val << 32) | val_low;
129da0c48c4Sopenharmony_ci	  gprs[i] = val;
130da0c48c4Sopenharmony_ci	  sigreg_ptr += 4;
131da0c48c4Sopenharmony_ci	}
132da0c48c4Sopenharmony_ci    }
133da0c48c4Sopenharmony_ci  if (! setfunc (0, 16, gprs, arg))
134da0c48c4Sopenharmony_ci    return false;
135da0c48c4Sopenharmony_ci  if (! setfunc (16, 16, fprs, arg))
136da0c48c4Sopenharmony_ci    return false;
137da0c48c4Sopenharmony_ci  *signal_framep = true;
138da0c48c4Sopenharmony_ci  return true;
139da0c48c4Sopenharmony_ci}
140