xref: /openbmc/linux/arch/arc/kernel/unwind.c (revision ac3b4328)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2854a0d95SVineet Gupta /*
3854a0d95SVineet Gupta  * Copyright (C) 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
4854a0d95SVineet Gupta  * Copyright (C) 2002-2006 Novell, Inc.
5854a0d95SVineet Gupta  *	Jan Beulich <jbeulich@novell.com>
6854a0d95SVineet Gupta  *
7854a0d95SVineet Gupta  * A simple API for unwinding kernel stacks.  This is used for
8854a0d95SVineet Gupta  * debugging and error reporting purposes.  The kernel doesn't need
9854a0d95SVineet Gupta  * full-blown stack unwinding with all the bells and whistles, so there
10854a0d95SVineet Gupta  * is not much point in implementing the full Dwarf2 unwind API.
11854a0d95SVineet Gupta  */
12854a0d95SVineet Gupta 
13854a0d95SVineet Gupta #include <linux/sched.h>
14854a0d95SVineet Gupta #include <linux/module.h>
1557c8a661SMike Rapoport #include <linux/memblock.h>
16854a0d95SVineet Gupta #include <linux/sort.h>
17854a0d95SVineet Gupta #include <linux/slab.h>
18854a0d95SVineet Gupta #include <linux/stop_machine.h>
19854a0d95SVineet Gupta #include <linux/uaccess.h>
20854a0d95SVineet Gupta #include <linux/ptrace.h>
21854a0d95SVineet Gupta #include <asm/sections.h>
22854a0d95SVineet Gupta #include <asm/unaligned.h>
23854a0d95SVineet Gupta #include <asm/unwind.h>
24854a0d95SVineet Gupta 
25854a0d95SVineet Gupta extern char __start_unwind[], __end_unwind[];
26854a0d95SVineet Gupta /* extern const u8 __start_unwind_hdr[], __end_unwind_hdr[];*/
27854a0d95SVineet Gupta 
28854a0d95SVineet Gupta /* #define UNWIND_DEBUG */
29854a0d95SVineet Gupta 
30854a0d95SVineet Gupta #ifdef UNWIND_DEBUG
31854a0d95SVineet Gupta int dbg_unw;
32854a0d95SVineet Gupta #define unw_debug(fmt, ...)			\
33854a0d95SVineet Gupta do {						\
34854a0d95SVineet Gupta 	if (dbg_unw)				\
35854a0d95SVineet Gupta 		pr_info(fmt, ##__VA_ARGS__);	\
36854a0d95SVineet Gupta } while (0);
37854a0d95SVineet Gupta #else
38854a0d95SVineet Gupta #define unw_debug(fmt, ...)
39854a0d95SVineet Gupta #endif
40854a0d95SVineet Gupta 
41854a0d95SVineet Gupta #define MAX_STACK_DEPTH 8
42854a0d95SVineet Gupta 
43854a0d95SVineet Gupta #define EXTRA_INFO(f) { \
44854a0d95SVineet Gupta 		BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \
45c593642cSPankaj Bharadiya 				% sizeof_field(struct unwind_frame_info, f)) \
46854a0d95SVineet Gupta 				+ offsetof(struct unwind_frame_info, f) \
47c593642cSPankaj Bharadiya 				/ sizeof_field(struct unwind_frame_info, f), \
48c593642cSPankaj Bharadiya 				sizeof_field(struct unwind_frame_info, f) \
49854a0d95SVineet Gupta 	}
50854a0d95SVineet Gupta #define PTREGS_INFO(f) EXTRA_INFO(regs.f)
51854a0d95SVineet Gupta 
52854a0d95SVineet Gupta static const struct {
53854a0d95SVineet Gupta 	unsigned offs:BITS_PER_LONG / 2;
54854a0d95SVineet Gupta 	unsigned width:BITS_PER_LONG / 2;
55854a0d95SVineet Gupta } reg_info[] = {
56854a0d95SVineet Gupta UNW_REGISTER_INFO};
57854a0d95SVineet Gupta 
58854a0d95SVineet Gupta #undef PTREGS_INFO
59854a0d95SVineet Gupta #undef EXTRA_INFO
60854a0d95SVineet Gupta 
61854a0d95SVineet Gupta #ifndef REG_INVALID
62854a0d95SVineet Gupta #define REG_INVALID(r) (reg_info[r].width == 0)
63854a0d95SVineet Gupta #endif
64854a0d95SVineet Gupta 
65854a0d95SVineet Gupta #define DW_CFA_nop                          0x00
66854a0d95SVineet Gupta #define DW_CFA_set_loc                      0x01
67854a0d95SVineet Gupta #define DW_CFA_advance_loc1                 0x02
68854a0d95SVineet Gupta #define DW_CFA_advance_loc2                 0x03
69854a0d95SVineet Gupta #define DW_CFA_advance_loc4                 0x04
70854a0d95SVineet Gupta #define DW_CFA_offset_extended              0x05
71854a0d95SVineet Gupta #define DW_CFA_restore_extended             0x06
72854a0d95SVineet Gupta #define DW_CFA_undefined                    0x07
73854a0d95SVineet Gupta #define DW_CFA_same_value                   0x08
74854a0d95SVineet Gupta #define DW_CFA_register                     0x09
75854a0d95SVineet Gupta #define DW_CFA_remember_state               0x0a
76854a0d95SVineet Gupta #define DW_CFA_restore_state                0x0b
77854a0d95SVineet Gupta #define DW_CFA_def_cfa                      0x0c
78854a0d95SVineet Gupta #define DW_CFA_def_cfa_register             0x0d
79854a0d95SVineet Gupta #define DW_CFA_def_cfa_offset               0x0e
80854a0d95SVineet Gupta #define DW_CFA_def_cfa_expression           0x0f
81854a0d95SVineet Gupta #define DW_CFA_expression                   0x10
82854a0d95SVineet Gupta #define DW_CFA_offset_extended_sf           0x11
83854a0d95SVineet Gupta #define DW_CFA_def_cfa_sf                   0x12
84854a0d95SVineet Gupta #define DW_CFA_def_cfa_offset_sf            0x13
85854a0d95SVineet Gupta #define DW_CFA_val_offset                   0x14
86854a0d95SVineet Gupta #define DW_CFA_val_offset_sf                0x15
87854a0d95SVineet Gupta #define DW_CFA_val_expression               0x16
88854a0d95SVineet Gupta #define DW_CFA_lo_user                      0x1c
89854a0d95SVineet Gupta #define DW_CFA_GNU_window_save              0x2d
90854a0d95SVineet Gupta #define DW_CFA_GNU_args_size                0x2e
91854a0d95SVineet Gupta #define DW_CFA_GNU_negative_offset_extended 0x2f
92854a0d95SVineet Gupta #define DW_CFA_hi_user                      0x3f
93854a0d95SVineet Gupta 
94854a0d95SVineet Gupta #define DW_EH_PE_FORM     0x07
95854a0d95SVineet Gupta #define DW_EH_PE_native   0x00
96854a0d95SVineet Gupta #define DW_EH_PE_leb128   0x01
97854a0d95SVineet Gupta #define DW_EH_PE_data2    0x02
98854a0d95SVineet Gupta #define DW_EH_PE_data4    0x03
99854a0d95SVineet Gupta #define DW_EH_PE_data8    0x04
100854a0d95SVineet Gupta #define DW_EH_PE_signed   0x08
101854a0d95SVineet Gupta #define DW_EH_PE_ADJUST   0x70
102854a0d95SVineet Gupta #define DW_EH_PE_abs      0x00
103854a0d95SVineet Gupta #define DW_EH_PE_pcrel    0x10
104854a0d95SVineet Gupta #define DW_EH_PE_textrel  0x20
105854a0d95SVineet Gupta #define DW_EH_PE_datarel  0x30
106854a0d95SVineet Gupta #define DW_EH_PE_funcrel  0x40
107854a0d95SVineet Gupta #define DW_EH_PE_aligned  0x50
108854a0d95SVineet Gupta #define DW_EH_PE_indirect 0x80
109854a0d95SVineet Gupta #define DW_EH_PE_omit     0xff
110854a0d95SVineet Gupta 
1116716dbbdSVineet Gupta #define CIE_ID	0
112d040876bSVineet Gupta 
113854a0d95SVineet Gupta typedef unsigned long uleb128_t;
114854a0d95SVineet Gupta typedef signed long sleb128_t;
115854a0d95SVineet Gupta 
116854a0d95SVineet Gupta static struct unwind_table {
117854a0d95SVineet Gupta 	struct {
118854a0d95SVineet Gupta 		unsigned long pc;
119854a0d95SVineet Gupta 		unsigned long range;
120854a0d95SVineet Gupta 	} core, init;
121854a0d95SVineet Gupta 	const void *address;
122854a0d95SVineet Gupta 	unsigned long size;
123854a0d95SVineet Gupta 	const unsigned char *header;
124854a0d95SVineet Gupta 	unsigned long hdrsz;
125854a0d95SVineet Gupta 	struct unwind_table *link;
126854a0d95SVineet Gupta 	const char *name;
127854a0d95SVineet Gupta } root_table;
128854a0d95SVineet Gupta 
129854a0d95SVineet Gupta struct unwind_item {
130854a0d95SVineet Gupta 	enum item_location {
131854a0d95SVineet Gupta 		Nowhere,
132854a0d95SVineet Gupta 		Memory,
133854a0d95SVineet Gupta 		Register,
134854a0d95SVineet Gupta 		Value
135854a0d95SVineet Gupta 	} where;
136854a0d95SVineet Gupta 	uleb128_t value;
137854a0d95SVineet Gupta };
138854a0d95SVineet Gupta 
139854a0d95SVineet Gupta struct unwind_state {
140854a0d95SVineet Gupta 	uleb128_t loc, org;
141854a0d95SVineet Gupta 	const u8 *cieStart, *cieEnd;
142854a0d95SVineet Gupta 	uleb128_t codeAlign;
143854a0d95SVineet Gupta 	sleb128_t dataAlign;
144854a0d95SVineet Gupta 	struct cfa {
145854a0d95SVineet Gupta 		uleb128_t reg, offs;
146854a0d95SVineet Gupta 	} cfa;
147854a0d95SVineet Gupta 	struct unwind_item regs[ARRAY_SIZE(reg_info)];
148854a0d95SVineet Gupta 	unsigned stackDepth:8;
149854a0d95SVineet Gupta 	unsigned version:8;
150854a0d95SVineet Gupta 	const u8 *label;
151854a0d95SVineet Gupta 	const u8 *stack[MAX_STACK_DEPTH];
152854a0d95SVineet Gupta };
153854a0d95SVineet Gupta 
154854a0d95SVineet Gupta static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
155854a0d95SVineet Gupta 
find_table(unsigned long pc)156854a0d95SVineet Gupta static struct unwind_table *find_table(unsigned long pc)
157854a0d95SVineet Gupta {
158854a0d95SVineet Gupta 	struct unwind_table *table;
159854a0d95SVineet Gupta 
160854a0d95SVineet Gupta 	for (table = &root_table; table; table = table->link)
161854a0d95SVineet Gupta 		if ((pc >= table->core.pc
162854a0d95SVineet Gupta 		     && pc < table->core.pc + table->core.range)
163854a0d95SVineet Gupta 		    || (pc >= table->init.pc
164854a0d95SVineet Gupta 			&& pc < table->init.pc + table->init.range))
165854a0d95SVineet Gupta 			break;
166854a0d95SVineet Gupta 
167854a0d95SVineet Gupta 	return table;
168854a0d95SVineet Gupta }
169854a0d95SVineet Gupta 
170854a0d95SVineet Gupta static unsigned long read_pointer(const u8 **pLoc,
171854a0d95SVineet Gupta 				  const void *end, signed ptrType);
172bc79c9a7SVineet Gupta static void init_unwind_hdr(struct unwind_table *table,
173bc79c9a7SVineet Gupta 			    void *(*alloc) (unsigned long));
174bc79c9a7SVineet Gupta 
175bc79c9a7SVineet Gupta /*
176bc79c9a7SVineet Gupta  * wrappers for header alloc (vs. calling one vs. other at call site)
177bc79c9a7SVineet Gupta  * to elide section mismatches warnings
178bc79c9a7SVineet Gupta  */
unw_hdr_alloc_early(unsigned long sz)179bc79c9a7SVineet Gupta static void *__init unw_hdr_alloc_early(unsigned long sz)
180bc79c9a7SVineet Gupta {
18126fb3daeSMike Rapoport 	return memblock_alloc_from(sz, sizeof(unsigned int), MAX_DMA_ADDRESS);
182bc79c9a7SVineet Gupta }
183bc79c9a7SVineet Gupta 
init_unwind_table(struct unwind_table * table,const char * name,const void * core_start,unsigned long core_size,const void * init_start,unsigned long init_size,const void * table_start,unsigned long table_size,const u8 * header_start,unsigned long header_size)184854a0d95SVineet Gupta static void init_unwind_table(struct unwind_table *table, const char *name,
185854a0d95SVineet Gupta 			      const void *core_start, unsigned long core_size,
186854a0d95SVineet Gupta 			      const void *init_start, unsigned long init_size,
187854a0d95SVineet Gupta 			      const void *table_start, unsigned long table_size,
188854a0d95SVineet Gupta 			      const u8 *header_start, unsigned long header_size)
189854a0d95SVineet Gupta {
190854a0d95SVineet Gupta 	table->core.pc = (unsigned long)core_start;
191854a0d95SVineet Gupta 	table->core.range = core_size;
192854a0d95SVineet Gupta 	table->init.pc = (unsigned long)init_start;
193854a0d95SVineet Gupta 	table->init.range = init_size;
194854a0d95SVineet Gupta 	table->address = table_start;
195854a0d95SVineet Gupta 	table->size = table_size;
19683520d62Sdean.yang_cp 	/* To avoid the pointer addition with NULL pointer.*/
19783520d62Sdean.yang_cp 	if (header_start != NULL) {
19883520d62Sdean.yang_cp 		const u8 *ptr = header_start + 4;
19983520d62Sdean.yang_cp 		const u8 *end = header_start + header_size;
200854a0d95SVineet Gupta 		/* See if the linker provided table looks valid. */
201854a0d95SVineet Gupta 		if (header_size <= 4
202854a0d95SVineet Gupta 		|| header_start[0] != 1
20383520d62Sdean.yang_cp 		|| (void *)read_pointer(&ptr, end, header_start[1])
20483520d62Sdean.yang_cp 				!= table_start
205854a0d95SVineet Gupta 		|| header_start[2] == DW_EH_PE_omit
206854a0d95SVineet Gupta 		|| read_pointer(&ptr, end, header_start[2]) <= 0
207854a0d95SVineet Gupta 		|| header_start[3] == DW_EH_PE_omit)
208854a0d95SVineet Gupta 			header_start = NULL;
20983520d62Sdean.yang_cp 	}
210854a0d95SVineet Gupta 	table->hdrsz = header_size;
211854a0d95SVineet Gupta 	smp_wmb();
212854a0d95SVineet Gupta 	table->header = header_start;
213854a0d95SVineet Gupta 	table->link = NULL;
214854a0d95SVineet Gupta 	table->name = name;
215854a0d95SVineet Gupta }
216854a0d95SVineet Gupta 
arc_unwind_init(void)217854a0d95SVineet Gupta void __init arc_unwind_init(void)
218854a0d95SVineet Gupta {
219854a0d95SVineet Gupta 	init_unwind_table(&root_table, "kernel", _text, _end - _text, NULL, 0,
220854a0d95SVineet Gupta 			  __start_unwind, __end_unwind - __start_unwind,
221854a0d95SVineet Gupta 			  NULL, 0);
222854a0d95SVineet Gupta 	  /*__start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr);*/
223bc79c9a7SVineet Gupta 
224bc79c9a7SVineet Gupta 	init_unwind_hdr(&root_table, unw_hdr_alloc_early);
225854a0d95SVineet Gupta }
226854a0d95SVineet Gupta 
227854a0d95SVineet Gupta static const u32 bad_cie, not_fde;
228854a0d95SVineet Gupta static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *);
229d040876bSVineet Gupta static const u32 *__cie_for_fde(const u32 *fde);
230854a0d95SVineet Gupta static signed fde_pointer_type(const u32 *cie);
231854a0d95SVineet Gupta 
232854a0d95SVineet Gupta struct eh_frame_hdr_table_entry {
233854a0d95SVineet Gupta 	unsigned long start, fde;
234854a0d95SVineet Gupta };
235854a0d95SVineet Gupta 
cmp_eh_frame_hdr_table_entries(const void * p1,const void * p2)236854a0d95SVineet Gupta static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2)
237854a0d95SVineet Gupta {
238854a0d95SVineet Gupta 	const struct eh_frame_hdr_table_entry *e1 = p1;
239854a0d95SVineet Gupta 	const struct eh_frame_hdr_table_entry *e2 = p2;
240854a0d95SVineet Gupta 
241854a0d95SVineet Gupta 	return (e1->start > e2->start) - (e1->start < e2->start);
242854a0d95SVineet Gupta }
243854a0d95SVineet Gupta 
swap_eh_frame_hdr_table_entries(void * p1,void * p2,int size)244854a0d95SVineet Gupta static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size)
245854a0d95SVineet Gupta {
246854a0d95SVineet Gupta 	struct eh_frame_hdr_table_entry *e1 = p1;
247854a0d95SVineet Gupta 	struct eh_frame_hdr_table_entry *e2 = p2;
248854a0d95SVineet Gupta 
2498f67f65dSYihao Han 	swap(e1->start, e2->start);
2508f67f65dSYihao Han 	swap(e1->fde, e2->fde);
251854a0d95SVineet Gupta }
252854a0d95SVineet Gupta 
init_unwind_hdr(struct unwind_table * table,void * (* alloc)(unsigned long))253bc79c9a7SVineet Gupta static void init_unwind_hdr(struct unwind_table *table,
254854a0d95SVineet Gupta 			    void *(*alloc) (unsigned long))
255854a0d95SVineet Gupta {
256854a0d95SVineet Gupta 	const u8 *ptr;
257854a0d95SVineet Gupta 	unsigned long tableSize = table->size, hdrSize;
258d4067395SJinchao Wang 	unsigned int n;
259854a0d95SVineet Gupta 	const u32 *fde;
260854a0d95SVineet Gupta 	struct {
261854a0d95SVineet Gupta 		u8 version;
262854a0d95SVineet Gupta 		u8 eh_frame_ptr_enc;
263854a0d95SVineet Gupta 		u8 fde_count_enc;
264854a0d95SVineet Gupta 		u8 table_enc;
265854a0d95SVineet Gupta 		unsigned long eh_frame_ptr;
266854a0d95SVineet Gupta 		unsigned int fde_count;
267854a0d95SVineet Gupta 		struct eh_frame_hdr_table_entry table[];
268854a0d95SVineet Gupta 	} __attribute__ ((__packed__)) *header;
269854a0d95SVineet Gupta 
270854a0d95SVineet Gupta 	if (table->header)
271854a0d95SVineet Gupta 		return;
272854a0d95SVineet Gupta 
273854a0d95SVineet Gupta 	if (table->hdrsz)
274854a0d95SVineet Gupta 		pr_warn(".eh_frame_hdr for '%s' present but unusable\n",
275854a0d95SVineet Gupta 			table->name);
276854a0d95SVineet Gupta 
277854a0d95SVineet Gupta 	if (tableSize & (sizeof(*fde) - 1))
278854a0d95SVineet Gupta 		return;
279854a0d95SVineet Gupta 
280854a0d95SVineet Gupta 	for (fde = table->address, n = 0;
281854a0d95SVineet Gupta 	     tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde;
282854a0d95SVineet Gupta 	     tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
283854a0d95SVineet Gupta 		const u32 *cie = cie_for_fde(fde, table);
284854a0d95SVineet Gupta 		signed ptrType;
285854a0d95SVineet Gupta 
2862d64affcSVineet Gupta 		if (cie == &not_fde)
287854a0d95SVineet Gupta 			continue;
288854a0d95SVineet Gupta 		if (cie == NULL || cie == &bad_cie)
2896b538db7SVineet Gupta 			goto ret_err;
290854a0d95SVineet Gupta 		ptrType = fde_pointer_type(cie);
291854a0d95SVineet Gupta 		if (ptrType < 0)
2926b538db7SVineet Gupta 			goto ret_err;
293854a0d95SVineet Gupta 
294854a0d95SVineet Gupta 		ptr = (const u8 *)(fde + 2);
295854a0d95SVineet Gupta 		if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde,
296854a0d95SVineet Gupta 								ptrType)) {
297854a0d95SVineet Gupta 			/* FIXME_Rajesh We have 4 instances of null addresses
298854a0d95SVineet Gupta 			 * instead of the initial loc addr
299854a0d95SVineet Gupta 			 * return;
300854a0d95SVineet Gupta 			 */
301baadb8fdSVineet Gupta 			WARN(1, "unwinder: FDE->initial_location NULL %p\n",
302baadb8fdSVineet Gupta 				(const u8 *)(fde + 1) + *fde);
303854a0d95SVineet Gupta 		}
304854a0d95SVineet Gupta 		++n;
305854a0d95SVineet Gupta 	}
306854a0d95SVineet Gupta 
307854a0d95SVineet Gupta 	if (tableSize || !n)
3086b538db7SVineet Gupta 		goto ret_err;
309854a0d95SVineet Gupta 
310854a0d95SVineet Gupta 	hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int)
311854a0d95SVineet Gupta 	    + 2 * n * sizeof(unsigned long);
312bc79c9a7SVineet Gupta 
313854a0d95SVineet Gupta 	header = alloc(hdrSize);
314854a0d95SVineet Gupta 	if (!header)
3156b538db7SVineet Gupta 		goto ret_err;
316bc79c9a7SVineet Gupta 
317854a0d95SVineet Gupta 	header->version = 1;
318854a0d95SVineet Gupta 	header->eh_frame_ptr_enc = DW_EH_PE_abs | DW_EH_PE_native;
319854a0d95SVineet Gupta 	header->fde_count_enc = DW_EH_PE_abs | DW_EH_PE_data4;
320854a0d95SVineet Gupta 	header->table_enc = DW_EH_PE_abs | DW_EH_PE_native;
321854a0d95SVineet Gupta 	put_unaligned((unsigned long)table->address, &header->eh_frame_ptr);
322854a0d95SVineet Gupta 	BUILD_BUG_ON(offsetof(typeof(*header), fde_count)
323854a0d95SVineet Gupta 		     % __alignof(typeof(header->fde_count)));
324854a0d95SVineet Gupta 	header->fde_count = n;
325854a0d95SVineet Gupta 
326854a0d95SVineet Gupta 	BUILD_BUG_ON(offsetof(typeof(*header), table)
327854a0d95SVineet Gupta 		     % __alignof(typeof(*header->table)));
328854a0d95SVineet Gupta 	for (fde = table->address, tableSize = table->size, n = 0;
329854a0d95SVineet Gupta 	     tableSize;
330854a0d95SVineet Gupta 	     tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
331d040876bSVineet Gupta 		const u32 *cie = __cie_for_fde(fde);
332854a0d95SVineet Gupta 
333d040876bSVineet Gupta 		if (fde[1] == CIE_ID)
334854a0d95SVineet Gupta 			continue;	/* this is a CIE */
335854a0d95SVineet Gupta 		ptr = (const u8 *)(fde + 2);
336854a0d95SVineet Gupta 		header->table[n].start = read_pointer(&ptr,
337854a0d95SVineet Gupta 						      (const u8 *)(fde + 1) +
338854a0d95SVineet Gupta 						      *fde,
339854a0d95SVineet Gupta 						      fde_pointer_type(cie));
340854a0d95SVineet Gupta 		header->table[n].fde = (unsigned long)fde;
341854a0d95SVineet Gupta 		++n;
342854a0d95SVineet Gupta 	}
343854a0d95SVineet Gupta 	WARN_ON(n != header->fde_count);
344854a0d95SVineet Gupta 
345854a0d95SVineet Gupta 	sort(header->table,
346854a0d95SVineet Gupta 	     n,
347854a0d95SVineet Gupta 	     sizeof(*header->table),
348854a0d95SVineet Gupta 	     cmp_eh_frame_hdr_table_entries, swap_eh_frame_hdr_table_entries);
349854a0d95SVineet Gupta 
350854a0d95SVineet Gupta 	table->hdrsz = hdrSize;
351854a0d95SVineet Gupta 	smp_wmb();
352854a0d95SVineet Gupta 	table->header = (const void *)header;
3536b538db7SVineet Gupta 	return;
3546b538db7SVineet Gupta 
3556b538db7SVineet Gupta ret_err:
356ed7158baSIngo Molnar 	panic("Attention !!! Dwarf FDE parsing errors\n");
357854a0d95SVineet Gupta }
358854a0d95SVineet Gupta 
359854a0d95SVineet Gupta #ifdef CONFIG_MODULES
unw_hdr_alloc(unsigned long sz)360fd5de272SArnd Bergmann static void *unw_hdr_alloc(unsigned long sz)
361fd5de272SArnd Bergmann {
362fd5de272SArnd Bergmann 	return kmalloc(sz, GFP_KERNEL);
363fd5de272SArnd Bergmann }
364854a0d95SVineet Gupta 
365854a0d95SVineet Gupta static struct unwind_table *last_table;
366854a0d95SVineet Gupta 
367854a0d95SVineet Gupta /* Must be called with module_mutex held. */
unwind_add_table(struct module * module,const void * table_start,unsigned long table_size)368854a0d95SVineet Gupta void *unwind_add_table(struct module *module, const void *table_start,
369854a0d95SVineet Gupta 		       unsigned long table_size)
370854a0d95SVineet Gupta {
371854a0d95SVineet Gupta 	struct unwind_table *table;
372*ac3b4328SSong Liu 	struct module_memory *core_text;
373*ac3b4328SSong Liu 	struct module_memory *init_text;
374854a0d95SVineet Gupta 
375854a0d95SVineet Gupta 	if (table_size <= 0)
376854a0d95SVineet Gupta 		return NULL;
377854a0d95SVineet Gupta 
378854a0d95SVineet Gupta 	table = kmalloc(sizeof(*table), GFP_KERNEL);
379854a0d95SVineet Gupta 	if (!table)
380854a0d95SVineet Gupta 		return NULL;
381854a0d95SVineet Gupta 
382*ac3b4328SSong Liu 	core_text = &module->mem[MOD_TEXT];
383*ac3b4328SSong Liu 	init_text = &module->mem[MOD_INIT_TEXT];
384*ac3b4328SSong Liu 
385*ac3b4328SSong Liu 	init_unwind_table(table, module->name, core_text->base, core_text->size,
386*ac3b4328SSong Liu 			  init_text->base, init_text->size, table_start, table_size, NULL, 0);
387854a0d95SVineet Gupta 
388bc79c9a7SVineet Gupta 	init_unwind_hdr(table, unw_hdr_alloc);
389bc79c9a7SVineet Gupta 
390854a0d95SVineet Gupta #ifdef UNWIND_DEBUG
391854a0d95SVineet Gupta 	unw_debug("Table added for [%s] %lx %lx\n",
392854a0d95SVineet Gupta 		module->name, table->core.pc, table->core.range);
393854a0d95SVineet Gupta #endif
394854a0d95SVineet Gupta 	if (last_table)
395854a0d95SVineet Gupta 		last_table->link = table;
396854a0d95SVineet Gupta 	else
397854a0d95SVineet Gupta 		root_table.link = table;
398854a0d95SVineet Gupta 	last_table = table;
399854a0d95SVineet Gupta 
400854a0d95SVineet Gupta 	return table;
401854a0d95SVineet Gupta }
402854a0d95SVineet Gupta 
403854a0d95SVineet Gupta struct unlink_table_info {
404854a0d95SVineet Gupta 	struct unwind_table *table;
405854a0d95SVineet Gupta 	int init_only;
406854a0d95SVineet Gupta };
407854a0d95SVineet Gupta 
unlink_table(void * arg)408854a0d95SVineet Gupta static int unlink_table(void *arg)
409854a0d95SVineet Gupta {
410854a0d95SVineet Gupta 	struct unlink_table_info *info = arg;
411854a0d95SVineet Gupta 	struct unwind_table *table = info->table, *prev;
412854a0d95SVineet Gupta 
413854a0d95SVineet Gupta 	for (prev = &root_table; prev->link && prev->link != table;
414854a0d95SVineet Gupta 	     prev = prev->link)
415854a0d95SVineet Gupta 		;
416854a0d95SVineet Gupta 
417854a0d95SVineet Gupta 	if (prev->link) {
418854a0d95SVineet Gupta 		if (info->init_only) {
419854a0d95SVineet Gupta 			table->init.pc = 0;
420854a0d95SVineet Gupta 			table->init.range = 0;
421854a0d95SVineet Gupta 			info->table = NULL;
422854a0d95SVineet Gupta 		} else {
423854a0d95SVineet Gupta 			prev->link = table->link;
424854a0d95SVineet Gupta 			if (!prev->link)
425854a0d95SVineet Gupta 				last_table = prev;
426854a0d95SVineet Gupta 		}
427854a0d95SVineet Gupta 	} else
428854a0d95SVineet Gupta 		info->table = NULL;
429854a0d95SVineet Gupta 
430854a0d95SVineet Gupta 	return 0;
431854a0d95SVineet Gupta }
432854a0d95SVineet Gupta 
433854a0d95SVineet Gupta /* Must be called with module_mutex held. */
unwind_remove_table(void * handle,int init_only)434854a0d95SVineet Gupta void unwind_remove_table(void *handle, int init_only)
435854a0d95SVineet Gupta {
436854a0d95SVineet Gupta 	struct unwind_table *table = handle;
437854a0d95SVineet Gupta 	struct unlink_table_info info;
438854a0d95SVineet Gupta 
439854a0d95SVineet Gupta 	if (!table || table == &root_table)
440854a0d95SVineet Gupta 		return;
441854a0d95SVineet Gupta 
442854a0d95SVineet Gupta 	if (init_only && table == last_table) {
443854a0d95SVineet Gupta 		table->init.pc = 0;
444854a0d95SVineet Gupta 		table->init.range = 0;
445854a0d95SVineet Gupta 		return;
446854a0d95SVineet Gupta 	}
447854a0d95SVineet Gupta 
448854a0d95SVineet Gupta 	info.table = table;
449854a0d95SVineet Gupta 	info.init_only = init_only;
450854a0d95SVineet Gupta 
451854a0d95SVineet Gupta 	unlink_table(&info); /* XXX: SMP */
452bc79c9a7SVineet Gupta 	kfree(table->header);
453854a0d95SVineet Gupta 	kfree(table);
454854a0d95SVineet Gupta }
455854a0d95SVineet Gupta 
456854a0d95SVineet Gupta #endif /* CONFIG_MODULES */
457854a0d95SVineet Gupta 
get_uleb128(const u8 ** pcur,const u8 * end)458854a0d95SVineet Gupta static uleb128_t get_uleb128(const u8 **pcur, const u8 *end)
459854a0d95SVineet Gupta {
460854a0d95SVineet Gupta 	const u8 *cur = *pcur;
461854a0d95SVineet Gupta 	uleb128_t value;
462d4067395SJinchao Wang 	unsigned int shift;
463854a0d95SVineet Gupta 
464854a0d95SVineet Gupta 	for (shift = 0, value = 0; cur < end; shift += 7) {
465854a0d95SVineet Gupta 		if (shift + 7 > 8 * sizeof(value)
466854a0d95SVineet Gupta 		    && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
467854a0d95SVineet Gupta 			cur = end + 1;
468854a0d95SVineet Gupta 			break;
469854a0d95SVineet Gupta 		}
470854a0d95SVineet Gupta 		value |= (uleb128_t) (*cur & 0x7f) << shift;
471854a0d95SVineet Gupta 		if (!(*cur++ & 0x80))
472854a0d95SVineet Gupta 			break;
473854a0d95SVineet Gupta 	}
474854a0d95SVineet Gupta 	*pcur = cur;
475854a0d95SVineet Gupta 
476854a0d95SVineet Gupta 	return value;
477854a0d95SVineet Gupta }
478854a0d95SVineet Gupta 
get_sleb128(const u8 ** pcur,const u8 * end)479854a0d95SVineet Gupta static sleb128_t get_sleb128(const u8 **pcur, const u8 *end)
480854a0d95SVineet Gupta {
481854a0d95SVineet Gupta 	const u8 *cur = *pcur;
482854a0d95SVineet Gupta 	sleb128_t value;
483d4067395SJinchao Wang 	unsigned int shift;
484854a0d95SVineet Gupta 
485854a0d95SVineet Gupta 	for (shift = 0, value = 0; cur < end; shift += 7) {
486854a0d95SVineet Gupta 		if (shift + 7 > 8 * sizeof(value)
487854a0d95SVineet Gupta 		    && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
488854a0d95SVineet Gupta 			cur = end + 1;
489854a0d95SVineet Gupta 			break;
490854a0d95SVineet Gupta 		}
491854a0d95SVineet Gupta 		value |= (sleb128_t) (*cur & 0x7f) << shift;
492854a0d95SVineet Gupta 		if (!(*cur & 0x80)) {
493854a0d95SVineet Gupta 			value |= -(*cur++ & 0x40) << shift;
494854a0d95SVineet Gupta 			break;
495854a0d95SVineet Gupta 		}
496854a0d95SVineet Gupta 	}
497854a0d95SVineet Gupta 	*pcur = cur;
498854a0d95SVineet Gupta 
499854a0d95SVineet Gupta 	return value;
500854a0d95SVineet Gupta }
501854a0d95SVineet Gupta 
__cie_for_fde(const u32 * fde)502d040876bSVineet Gupta static const u32 *__cie_for_fde(const u32 *fde)
503d040876bSVineet Gupta {
504d040876bSVineet Gupta 	const u32 *cie;
505d040876bSVineet Gupta 
5066716dbbdSVineet Gupta 	cie = fde + 1 - fde[1] / sizeof(*fde);
507d040876bSVineet Gupta 
508d040876bSVineet Gupta 	return cie;
509d040876bSVineet Gupta }
510d040876bSVineet Gupta 
cie_for_fde(const u32 * fde,const struct unwind_table * table)511854a0d95SVineet Gupta static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table)
512854a0d95SVineet Gupta {
513854a0d95SVineet Gupta 	const u32 *cie;
514854a0d95SVineet Gupta 
515854a0d95SVineet Gupta 	if (!*fde || (*fde & (sizeof(*fde) - 1)))
516854a0d95SVineet Gupta 		return &bad_cie;
517854a0d95SVineet Gupta 
518d040876bSVineet Gupta 	if (fde[1] == CIE_ID)
519854a0d95SVineet Gupta 		return &not_fde;	/* this is a CIE */
520854a0d95SVineet Gupta 
521854a0d95SVineet Gupta 	if ((fde[1] & (sizeof(*fde) - 1)))
522854a0d95SVineet Gupta /* || fde[1] > (unsigned long)(fde + 1) - (unsigned long)table->address) */
523854a0d95SVineet Gupta 		return NULL;	/* this is not a valid FDE */
524854a0d95SVineet Gupta 
525d040876bSVineet Gupta 	cie = __cie_for_fde(fde);
526854a0d95SVineet Gupta 
527854a0d95SVineet Gupta 	if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde)
528854a0d95SVineet Gupta 	    || (*cie & (sizeof(*cie) - 1))
529d040876bSVineet Gupta 	    || (cie[1] != CIE_ID))
530854a0d95SVineet Gupta 		return NULL;	/* this is not a (valid) CIE */
531854a0d95SVineet Gupta 	return cie;
532854a0d95SVineet Gupta }
533854a0d95SVineet Gupta 
read_pointer(const u8 ** pLoc,const void * end,signed ptrType)534854a0d95SVineet Gupta static unsigned long read_pointer(const u8 **pLoc, const void *end,
535854a0d95SVineet Gupta 				  signed ptrType)
536854a0d95SVineet Gupta {
537854a0d95SVineet Gupta 	unsigned long value = 0;
538854a0d95SVineet Gupta 	union {
539854a0d95SVineet Gupta 		const u8 *p8;
540854a0d95SVineet Gupta 		const u16 *p16u;
541854a0d95SVineet Gupta 		const s16 *p16s;
542854a0d95SVineet Gupta 		const u32 *p32u;
543854a0d95SVineet Gupta 		const s32 *p32s;
544854a0d95SVineet Gupta 		const unsigned long *pul;
545854a0d95SVineet Gupta 	} ptr;
546854a0d95SVineet Gupta 
547854a0d95SVineet Gupta 	if (ptrType < 0 || ptrType == DW_EH_PE_omit)
548854a0d95SVineet Gupta 		return 0;
549854a0d95SVineet Gupta 	ptr.p8 = *pLoc;
550854a0d95SVineet Gupta 	switch (ptrType & DW_EH_PE_FORM) {
551854a0d95SVineet Gupta 	case DW_EH_PE_data2:
552854a0d95SVineet Gupta 		if (end < (const void *)(ptr.p16u + 1))
553854a0d95SVineet Gupta 			return 0;
554854a0d95SVineet Gupta 		if (ptrType & DW_EH_PE_signed)
555854a0d95SVineet Gupta 			value = get_unaligned((u16 *) ptr.p16s++);
556854a0d95SVineet Gupta 		else
557854a0d95SVineet Gupta 			value = get_unaligned((u16 *) ptr.p16u++);
558854a0d95SVineet Gupta 		break;
559854a0d95SVineet Gupta 	case DW_EH_PE_data4:
560854a0d95SVineet Gupta #ifdef CONFIG_64BIT
561854a0d95SVineet Gupta 		if (end < (const void *)(ptr.p32u + 1))
562854a0d95SVineet Gupta 			return 0;
563854a0d95SVineet Gupta 		if (ptrType & DW_EH_PE_signed)
564854a0d95SVineet Gupta 			value = get_unaligned(ptr.p32s++);
565854a0d95SVineet Gupta 		else
566854a0d95SVineet Gupta 			value = get_unaligned(ptr.p32u++);
567854a0d95SVineet Gupta 		break;
568854a0d95SVineet Gupta 	case DW_EH_PE_data8:
569854a0d95SVineet Gupta 		BUILD_BUG_ON(sizeof(u64) != sizeof(value));
570854a0d95SVineet Gupta #else
571854a0d95SVineet Gupta 		BUILD_BUG_ON(sizeof(u32) != sizeof(value));
572854a0d95SVineet Gupta #endif
573df561f66SGustavo A. R. Silva 		fallthrough;
574854a0d95SVineet Gupta 	case DW_EH_PE_native:
575854a0d95SVineet Gupta 		if (end < (const void *)(ptr.pul + 1))
576854a0d95SVineet Gupta 			return 0;
577854a0d95SVineet Gupta 		value = get_unaligned((unsigned long *)ptr.pul++);
578854a0d95SVineet Gupta 		break;
579854a0d95SVineet Gupta 	case DW_EH_PE_leb128:
580854a0d95SVineet Gupta 		BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value));
581854a0d95SVineet Gupta 		value = ptrType & DW_EH_PE_signed ? get_sleb128(&ptr.p8, end)
582854a0d95SVineet Gupta 		    : get_uleb128(&ptr.p8, end);
583854a0d95SVineet Gupta 		if ((const void *)ptr.p8 > end)
584854a0d95SVineet Gupta 			return 0;
585854a0d95SVineet Gupta 		break;
586854a0d95SVineet Gupta 	default:
587854a0d95SVineet Gupta 		return 0;
588854a0d95SVineet Gupta 	}
589854a0d95SVineet Gupta 	switch (ptrType & DW_EH_PE_ADJUST) {
590854a0d95SVineet Gupta 	case DW_EH_PE_abs:
591854a0d95SVineet Gupta 		break;
592854a0d95SVineet Gupta 	case DW_EH_PE_pcrel:
593854a0d95SVineet Gupta 		value += (unsigned long)*pLoc;
594854a0d95SVineet Gupta 		break;
595854a0d95SVineet Gupta 	default:
596854a0d95SVineet Gupta 		return 0;
597854a0d95SVineet Gupta 	}
598854a0d95SVineet Gupta 	if ((ptrType & DW_EH_PE_indirect)
599854a0d95SVineet Gupta 	    && __get_user(value, (unsigned long __user *)value))
600854a0d95SVineet Gupta 		return 0;
601854a0d95SVineet Gupta 	*pLoc = ptr.p8;
602854a0d95SVineet Gupta 
603854a0d95SVineet Gupta 	return value;
604854a0d95SVineet Gupta }
605854a0d95SVineet Gupta 
fde_pointer_type(const u32 * cie)606854a0d95SVineet Gupta static signed fde_pointer_type(const u32 *cie)
607854a0d95SVineet Gupta {
608854a0d95SVineet Gupta 	const u8 *ptr = (const u8 *)(cie + 2);
609d4067395SJinchao Wang 	unsigned int version = *ptr;
610854a0d95SVineet Gupta 
611854a0d95SVineet Gupta 	if (*++ptr) {
612854a0d95SVineet Gupta 		const char *aug;
613854a0d95SVineet Gupta 		const u8 *end = (const u8 *)(cie + 1) + *cie;
614854a0d95SVineet Gupta 		uleb128_t len;
615854a0d95SVineet Gupta 
616854a0d95SVineet Gupta 		/* check if augmentation size is first (and thus present) */
617854a0d95SVineet Gupta 		if (*ptr != 'z')
618854a0d95SVineet Gupta 			return -1;
619854a0d95SVineet Gupta 
620854a0d95SVineet Gupta 		/* check if augmentation string is nul-terminated */
621854a0d95SVineet Gupta 		aug = (const void *)ptr;
622854a0d95SVineet Gupta 		ptr = memchr(aug, 0, end - ptr);
623854a0d95SVineet Gupta 		if (ptr == NULL)
624854a0d95SVineet Gupta 			return -1;
625854a0d95SVineet Gupta 
626854a0d95SVineet Gupta 		++ptr;		/* skip terminator */
627854a0d95SVineet Gupta 		get_uleb128(&ptr, end);	/* skip code alignment */
628854a0d95SVineet Gupta 		get_sleb128(&ptr, end);	/* skip data alignment */
629854a0d95SVineet Gupta 		/* skip return address column */
630854a0d95SVineet Gupta 		version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end);
631854a0d95SVineet Gupta 		len = get_uleb128(&ptr, end);	/* augmentation length */
632854a0d95SVineet Gupta 
633854a0d95SVineet Gupta 		if (ptr + len < ptr || ptr + len > end)
634854a0d95SVineet Gupta 			return -1;
635854a0d95SVineet Gupta 
636854a0d95SVineet Gupta 		end = ptr + len;
637854a0d95SVineet Gupta 		while (*++aug) {
638854a0d95SVineet Gupta 			if (ptr >= end)
639854a0d95SVineet Gupta 				return -1;
640854a0d95SVineet Gupta 			switch (*aug) {
641854a0d95SVineet Gupta 			case 'L':
642854a0d95SVineet Gupta 				++ptr;
643854a0d95SVineet Gupta 				break;
644854a0d95SVineet Gupta 			case 'P':{
645854a0d95SVineet Gupta 					signed ptrType = *ptr++;
646854a0d95SVineet Gupta 
647854a0d95SVineet Gupta 					if (!read_pointer(&ptr, end, ptrType)
648854a0d95SVineet Gupta 					    || ptr > end)
649854a0d95SVineet Gupta 						return -1;
650854a0d95SVineet Gupta 				}
651854a0d95SVineet Gupta 				break;
652854a0d95SVineet Gupta 			case 'R':
653854a0d95SVineet Gupta 				return *ptr;
654854a0d95SVineet Gupta 			default:
655854a0d95SVineet Gupta 				return -1;
656854a0d95SVineet Gupta 			}
657854a0d95SVineet Gupta 		}
658854a0d95SVineet Gupta 	}
659854a0d95SVineet Gupta 	return DW_EH_PE_native | DW_EH_PE_abs;
660854a0d95SVineet Gupta }
661854a0d95SVineet Gupta 
advance_loc(unsigned long delta,struct unwind_state * state)662854a0d95SVineet Gupta static int advance_loc(unsigned long delta, struct unwind_state *state)
663854a0d95SVineet Gupta {
664854a0d95SVineet Gupta 	state->loc += delta * state->codeAlign;
665854a0d95SVineet Gupta 
666854a0d95SVineet Gupta 	/* FIXME_Rajesh: Probably we are defining for the initial range as well;
667854a0d95SVineet Gupta 	   return delta > 0;
668854a0d95SVineet Gupta 	 */
669854a0d95SVineet Gupta 	unw_debug("delta %3lu => loc 0x%lx: ", delta, state->loc);
670854a0d95SVineet Gupta 	return 1;
671854a0d95SVineet Gupta }
672854a0d95SVineet Gupta 
set_rule(uleb128_t reg,enum item_location where,uleb128_t value,struct unwind_state * state)673854a0d95SVineet Gupta static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value,
674854a0d95SVineet Gupta 		     struct unwind_state *state)
675854a0d95SVineet Gupta {
676854a0d95SVineet Gupta 	if (reg < ARRAY_SIZE(state->regs)) {
677854a0d95SVineet Gupta 		state->regs[reg].where = where;
678854a0d95SVineet Gupta 		state->regs[reg].value = value;
679854a0d95SVineet Gupta 
680854a0d95SVineet Gupta #ifdef UNWIND_DEBUG
681854a0d95SVineet Gupta 		unw_debug("r%lu: ", reg);
682854a0d95SVineet Gupta 		switch (where) {
683854a0d95SVineet Gupta 		case Nowhere:
684854a0d95SVineet Gupta 			unw_debug("s ");
685854a0d95SVineet Gupta 			break;
686854a0d95SVineet Gupta 		case Memory:
687854a0d95SVineet Gupta 			unw_debug("c(%lu) ", value);
688854a0d95SVineet Gupta 			break;
689854a0d95SVineet Gupta 		case Register:
690854a0d95SVineet Gupta 			unw_debug("r(%lu) ", value);
691854a0d95SVineet Gupta 			break;
692854a0d95SVineet Gupta 		case Value:
693854a0d95SVineet Gupta 			unw_debug("v(%lu) ", value);
694854a0d95SVineet Gupta 			break;
695854a0d95SVineet Gupta 		default:
696854a0d95SVineet Gupta 			break;
697854a0d95SVineet Gupta 		}
698854a0d95SVineet Gupta #endif
699854a0d95SVineet Gupta 	}
700854a0d95SVineet Gupta }
701854a0d95SVineet Gupta 
processCFI(const u8 * start,const u8 * end,unsigned long targetLoc,signed ptrType,struct unwind_state * state)702854a0d95SVineet Gupta static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
703854a0d95SVineet Gupta 		      signed ptrType, struct unwind_state *state)
704854a0d95SVineet Gupta {
705854a0d95SVineet Gupta 	union {
706854a0d95SVineet Gupta 		const u8 *p8;
707854a0d95SVineet Gupta 		const u16 *p16;
708854a0d95SVineet Gupta 		const u32 *p32;
709854a0d95SVineet Gupta 	} ptr;
710854a0d95SVineet Gupta 	int result = 1;
711854a0d95SVineet Gupta 	u8 opcode;
712854a0d95SVineet Gupta 
713854a0d95SVineet Gupta 	if (start != state->cieStart) {
714854a0d95SVineet Gupta 		state->loc = state->org;
715854a0d95SVineet Gupta 		result =
716854a0d95SVineet Gupta 		    processCFI(state->cieStart, state->cieEnd, 0, ptrType,
717854a0d95SVineet Gupta 			       state);
718854a0d95SVineet Gupta 		if (targetLoc == 0 && state->label == NULL)
719854a0d95SVineet Gupta 			return result;
720854a0d95SVineet Gupta 	}
721854a0d95SVineet Gupta 	for (ptr.p8 = start; result && ptr.p8 < end;) {
722854a0d95SVineet Gupta 		switch (*ptr.p8 >> 6) {
723854a0d95SVineet Gupta 			uleb128_t value;
724854a0d95SVineet Gupta 
725854a0d95SVineet Gupta 		case 0:
726854a0d95SVineet Gupta 			opcode = *ptr.p8++;
727854a0d95SVineet Gupta 
728854a0d95SVineet Gupta 			switch (opcode) {
729854a0d95SVineet Gupta 			case DW_CFA_nop:
730854a0d95SVineet Gupta 				unw_debug("cfa nop ");
731854a0d95SVineet Gupta 				break;
732854a0d95SVineet Gupta 			case DW_CFA_set_loc:
733854a0d95SVineet Gupta 				state->loc = read_pointer(&ptr.p8, end,
734854a0d95SVineet Gupta 							  ptrType);
735854a0d95SVineet Gupta 				if (state->loc == 0)
736854a0d95SVineet Gupta 					result = 0;
737854a0d95SVineet Gupta 				unw_debug("cfa_set_loc: 0x%lx ", state->loc);
738854a0d95SVineet Gupta 				break;
739854a0d95SVineet Gupta 			case DW_CFA_advance_loc1:
740854a0d95SVineet Gupta 				unw_debug("\ncfa advance loc1:");
741854a0d95SVineet Gupta 				result = ptr.p8 < end
742854a0d95SVineet Gupta 				    && advance_loc(*ptr.p8++, state);
743854a0d95SVineet Gupta 				break;
744854a0d95SVineet Gupta 			case DW_CFA_advance_loc2:
745854a0d95SVineet Gupta 				value = *ptr.p8++;
746854a0d95SVineet Gupta 				value += *ptr.p8++ << 8;
747854a0d95SVineet Gupta 				unw_debug("\ncfa advance loc2:");
748854a0d95SVineet Gupta 				result = ptr.p8 <= end + 2
749854a0d95SVineet Gupta 				    /* && advance_loc(*ptr.p16++, state); */
750854a0d95SVineet Gupta 				    && advance_loc(value, state);
751854a0d95SVineet Gupta 				break;
752854a0d95SVineet Gupta 			case DW_CFA_advance_loc4:
753854a0d95SVineet Gupta 				unw_debug("\ncfa advance loc4:");
754854a0d95SVineet Gupta 				result = ptr.p8 <= end + 4
755854a0d95SVineet Gupta 				    && advance_loc(*ptr.p32++, state);
756854a0d95SVineet Gupta 				break;
757854a0d95SVineet Gupta 			case DW_CFA_offset_extended:
758854a0d95SVineet Gupta 				value = get_uleb128(&ptr.p8, end);
759854a0d95SVineet Gupta 				unw_debug("cfa_offset_extended: ");
760854a0d95SVineet Gupta 				set_rule(value, Memory,
761854a0d95SVineet Gupta 					 get_uleb128(&ptr.p8, end), state);
762854a0d95SVineet Gupta 				break;
763854a0d95SVineet Gupta 			case DW_CFA_val_offset:
764854a0d95SVineet Gupta 				value = get_uleb128(&ptr.p8, end);
765854a0d95SVineet Gupta 				set_rule(value, Value,
766854a0d95SVineet Gupta 					 get_uleb128(&ptr.p8, end), state);
767854a0d95SVineet Gupta 				break;
768854a0d95SVineet Gupta 			case DW_CFA_offset_extended_sf:
769854a0d95SVineet Gupta 				value = get_uleb128(&ptr.p8, end);
770854a0d95SVineet Gupta 				set_rule(value, Memory,
771854a0d95SVineet Gupta 					 get_sleb128(&ptr.p8, end), state);
772854a0d95SVineet Gupta 				break;
773854a0d95SVineet Gupta 			case DW_CFA_val_offset_sf:
774854a0d95SVineet Gupta 				value = get_uleb128(&ptr.p8, end);
775854a0d95SVineet Gupta 				set_rule(value, Value,
776854a0d95SVineet Gupta 					 get_sleb128(&ptr.p8, end), state);
777854a0d95SVineet Gupta 				break;
778854a0d95SVineet Gupta 			case DW_CFA_restore_extended:
779854a0d95SVineet Gupta 				unw_debug("cfa_restore_extended: ");
780854a0d95SVineet Gupta 			case DW_CFA_undefined:
781854a0d95SVineet Gupta 				unw_debug("cfa_undefined: ");
782854a0d95SVineet Gupta 			case DW_CFA_same_value:
783854a0d95SVineet Gupta 				unw_debug("cfa_same_value: ");
784854a0d95SVineet Gupta 				set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0,
785854a0d95SVineet Gupta 					 state);
786854a0d95SVineet Gupta 				break;
787854a0d95SVineet Gupta 			case DW_CFA_register:
788854a0d95SVineet Gupta 				unw_debug("cfa_register: ");
789854a0d95SVineet Gupta 				value = get_uleb128(&ptr.p8, end);
790854a0d95SVineet Gupta 				set_rule(value,
791854a0d95SVineet Gupta 					 Register,
792854a0d95SVineet Gupta 					 get_uleb128(&ptr.p8, end), state);
793854a0d95SVineet Gupta 				break;
794854a0d95SVineet Gupta 			case DW_CFA_remember_state:
795854a0d95SVineet Gupta 				unw_debug("cfa_remember_state: ");
796854a0d95SVineet Gupta 				if (ptr.p8 == state->label) {
797854a0d95SVineet Gupta 					state->label = NULL;
798854a0d95SVineet Gupta 					return 1;
799854a0d95SVineet Gupta 				}
800854a0d95SVineet Gupta 				if (state->stackDepth >= MAX_STACK_DEPTH)
801854a0d95SVineet Gupta 					return 0;
802854a0d95SVineet Gupta 				state->stack[state->stackDepth++] = ptr.p8;
803854a0d95SVineet Gupta 				break;
804854a0d95SVineet Gupta 			case DW_CFA_restore_state:
805854a0d95SVineet Gupta 				unw_debug("cfa_restore_state: ");
806854a0d95SVineet Gupta 				if (state->stackDepth) {
807854a0d95SVineet Gupta 					const uleb128_t loc = state->loc;
808854a0d95SVineet Gupta 					const u8 *label = state->label;
809854a0d95SVineet Gupta 
810854a0d95SVineet Gupta 					state->label =
811854a0d95SVineet Gupta 					    state->stack[state->stackDepth - 1];
812854a0d95SVineet Gupta 					memcpy(&state->cfa, &badCFA,
813854a0d95SVineet Gupta 					       sizeof(state->cfa));
814854a0d95SVineet Gupta 					memset(state->regs, 0,
815854a0d95SVineet Gupta 					       sizeof(state->regs));
816854a0d95SVineet Gupta 					state->stackDepth = 0;
817854a0d95SVineet Gupta 					result =
818854a0d95SVineet Gupta 					    processCFI(start, end, 0, ptrType,
819854a0d95SVineet Gupta 						       state);
820854a0d95SVineet Gupta 					state->loc = loc;
821854a0d95SVineet Gupta 					state->label = label;
822854a0d95SVineet Gupta 				} else
823854a0d95SVineet Gupta 					return 0;
824854a0d95SVineet Gupta 				break;
825854a0d95SVineet Gupta 			case DW_CFA_def_cfa:
826854a0d95SVineet Gupta 				state->cfa.reg = get_uleb128(&ptr.p8, end);
827854a0d95SVineet Gupta 				unw_debug("cfa_def_cfa: r%lu ", state->cfa.reg);
828df561f66SGustavo A. R. Silva 				fallthrough;
829854a0d95SVineet Gupta 			case DW_CFA_def_cfa_offset:
830854a0d95SVineet Gupta 				state->cfa.offs = get_uleb128(&ptr.p8, end);
831854a0d95SVineet Gupta 				unw_debug("cfa_def_cfa_offset: 0x%lx ",
832854a0d95SVineet Gupta 					  state->cfa.offs);
833854a0d95SVineet Gupta 				break;
834854a0d95SVineet Gupta 			case DW_CFA_def_cfa_sf:
835854a0d95SVineet Gupta 				state->cfa.reg = get_uleb128(&ptr.p8, end);
836df561f66SGustavo A. R. Silva 				fallthrough;
837854a0d95SVineet Gupta 			case DW_CFA_def_cfa_offset_sf:
838854a0d95SVineet Gupta 				state->cfa.offs = get_sleb128(&ptr.p8, end)
839854a0d95SVineet Gupta 				    * state->dataAlign;
840854a0d95SVineet Gupta 				break;
841854a0d95SVineet Gupta 			case DW_CFA_def_cfa_register:
842ad61dd30SStephen Boyd 				unw_debug("cfa_def_cfa_register: ");
843854a0d95SVineet Gupta 				state->cfa.reg = get_uleb128(&ptr.p8, end);
844854a0d95SVineet Gupta 				break;
845854a0d95SVineet Gupta 				/*todo case DW_CFA_def_cfa_expression: */
846854a0d95SVineet Gupta 				/*todo case DW_CFA_expression: */
847854a0d95SVineet Gupta 				/*todo case DW_CFA_val_expression: */
848854a0d95SVineet Gupta 			case DW_CFA_GNU_args_size:
849854a0d95SVineet Gupta 				get_uleb128(&ptr.p8, end);
850854a0d95SVineet Gupta 				break;
851854a0d95SVineet Gupta 			case DW_CFA_GNU_negative_offset_extended:
852854a0d95SVineet Gupta 				value = get_uleb128(&ptr.p8, end);
853854a0d95SVineet Gupta 				set_rule(value,
854854a0d95SVineet Gupta 					 Memory,
855854a0d95SVineet Gupta 					 (uleb128_t) 0 - get_uleb128(&ptr.p8,
856854a0d95SVineet Gupta 								     end),
857854a0d95SVineet Gupta 					 state);
858854a0d95SVineet Gupta 				break;
859854a0d95SVineet Gupta 			case DW_CFA_GNU_window_save:
860854a0d95SVineet Gupta 			default:
861d939be3aSMasanari Iida 				unw_debug("UNKNOWN OPCODE 0x%x\n", opcode);
862854a0d95SVineet Gupta 				result = 0;
863854a0d95SVineet Gupta 				break;
864854a0d95SVineet Gupta 			}
865854a0d95SVineet Gupta 			break;
866854a0d95SVineet Gupta 		case 1:
867854a0d95SVineet Gupta 			unw_debug("\ncfa_adv_loc: ");
868854a0d95SVineet Gupta 			result = advance_loc(*ptr.p8++ & 0x3f, state);
869854a0d95SVineet Gupta 			break;
870854a0d95SVineet Gupta 		case 2:
871854a0d95SVineet Gupta 			unw_debug("cfa_offset: ");
872854a0d95SVineet Gupta 			value = *ptr.p8++ & 0x3f;
873854a0d95SVineet Gupta 			set_rule(value, Memory, get_uleb128(&ptr.p8, end),
874854a0d95SVineet Gupta 				 state);
875854a0d95SVineet Gupta 			break;
876854a0d95SVineet Gupta 		case 3:
877854a0d95SVineet Gupta 			unw_debug("cfa_restore: ");
878854a0d95SVineet Gupta 			set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
879854a0d95SVineet Gupta 			break;
880854a0d95SVineet Gupta 		}
881854a0d95SVineet Gupta 
882854a0d95SVineet Gupta 		if (ptr.p8 > end)
883854a0d95SVineet Gupta 			result = 0;
884854a0d95SVineet Gupta 		if (result && targetLoc != 0 && targetLoc < state->loc)
885854a0d95SVineet Gupta 			return 1;
886854a0d95SVineet Gupta 	}
887854a0d95SVineet Gupta 
888854a0d95SVineet Gupta 	return result && ptr.p8 == end && (targetLoc == 0 || (
889854a0d95SVineet Gupta 		/*todo While in theory this should apply, gcc in practice omits
890854a0d95SVineet Gupta 		  everything past the function prolog, and hence the location
891854a0d95SVineet Gupta 		  never reaches the end of the function.
892854a0d95SVineet Gupta 		targetLoc < state->loc && */  state->label == NULL));
893854a0d95SVineet Gupta }
894854a0d95SVineet Gupta 
895854a0d95SVineet Gupta /* Unwind to previous to frame.  Returns 0 if successful, negative
896854a0d95SVineet Gupta  * number in case of an error. */
arc_unwind(struct unwind_frame_info * frame)897854a0d95SVineet Gupta int arc_unwind(struct unwind_frame_info *frame)
898854a0d95SVineet Gupta {
899854a0d95SVineet Gupta #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
900854a0d95SVineet Gupta 	const u32 *fde = NULL, *cie = NULL;
901854a0d95SVineet Gupta 	const u8 *ptr = NULL, *end = NULL;
902854a0d95SVineet Gupta 	unsigned long pc = UNW_PC(frame) - frame->call_frame;
903854a0d95SVineet Gupta 	unsigned long startLoc = 0, endLoc = 0, cfa;
904d4067395SJinchao Wang 	unsigned int i;
905854a0d95SVineet Gupta 	signed ptrType = -1;
906854a0d95SVineet Gupta 	uleb128_t retAddrReg = 0;
907854a0d95SVineet Gupta 	const struct unwind_table *table;
908854a0d95SVineet Gupta 	struct unwind_state state;
909854a0d95SVineet Gupta 	unsigned long *fptr;
910854a0d95SVineet Gupta 	unsigned long addr;
911854a0d95SVineet Gupta 
912854a0d95SVineet Gupta 	unw_debug("\n\nUNWIND FRAME:\n");
913854a0d95SVineet Gupta 	unw_debug("PC: 0x%lx BLINK: 0x%lx, SP: 0x%lx, FP: 0x%x\n",
914854a0d95SVineet Gupta 		  UNW_PC(frame), UNW_BLINK(frame), UNW_SP(frame),
915854a0d95SVineet Gupta 		  UNW_FP(frame));
916854a0d95SVineet Gupta 
917854a0d95SVineet Gupta 	if (UNW_PC(frame) == 0)
918854a0d95SVineet Gupta 		return -EINVAL;
919854a0d95SVineet Gupta 
920854a0d95SVineet Gupta #ifdef UNWIND_DEBUG
921854a0d95SVineet Gupta 	{
922854a0d95SVineet Gupta 		unsigned long *sptr = (unsigned long *)UNW_SP(frame);
923854a0d95SVineet Gupta 		unw_debug("\nStack Dump:\n");
924854a0d95SVineet Gupta 		for (i = 0; i < 20; i++, sptr++)
925854a0d95SVineet Gupta 			unw_debug("0x%p:  0x%lx\n", sptr, *sptr);
926854a0d95SVineet Gupta 		unw_debug("\n");
927854a0d95SVineet Gupta 	}
928854a0d95SVineet Gupta #endif
929854a0d95SVineet Gupta 
930854a0d95SVineet Gupta 	table = find_table(pc);
931854a0d95SVineet Gupta 	if (table != NULL
932854a0d95SVineet Gupta 	    && !(table->size & (sizeof(*fde) - 1))) {
933854a0d95SVineet Gupta 		const u8 *hdr = table->header;
934854a0d95SVineet Gupta 		unsigned long tableSize;
935854a0d95SVineet Gupta 
936854a0d95SVineet Gupta 		smp_rmb();
937854a0d95SVineet Gupta 		if (hdr && hdr[0] == 1) {
938854a0d95SVineet Gupta 			switch (hdr[3] & DW_EH_PE_FORM) {
939854a0d95SVineet Gupta 			case DW_EH_PE_native:
940854a0d95SVineet Gupta 				tableSize = sizeof(unsigned long);
941854a0d95SVineet Gupta 				break;
942854a0d95SVineet Gupta 			case DW_EH_PE_data2:
943854a0d95SVineet Gupta 				tableSize = 2;
944854a0d95SVineet Gupta 				break;
945854a0d95SVineet Gupta 			case DW_EH_PE_data4:
946854a0d95SVineet Gupta 				tableSize = 4;
947854a0d95SVineet Gupta 				break;
948854a0d95SVineet Gupta 			case DW_EH_PE_data8:
949854a0d95SVineet Gupta 				tableSize = 8;
950854a0d95SVineet Gupta 				break;
951854a0d95SVineet Gupta 			default:
952854a0d95SVineet Gupta 				tableSize = 0;
953854a0d95SVineet Gupta 				break;
954854a0d95SVineet Gupta 			}
955854a0d95SVineet Gupta 			ptr = hdr + 4;
956854a0d95SVineet Gupta 			end = hdr + table->hdrsz;
957854a0d95SVineet Gupta 			if (tableSize && read_pointer(&ptr, end, hdr[1])
958854a0d95SVineet Gupta 			    == (unsigned long)table->address
959854a0d95SVineet Gupta 			    && (i = read_pointer(&ptr, end, hdr[2])) > 0
960854a0d95SVineet Gupta 			    && i == (end - ptr) / (2 * tableSize)
961854a0d95SVineet Gupta 			    && !((end - ptr) % (2 * tableSize))) {
962854a0d95SVineet Gupta 				do {
963854a0d95SVineet Gupta 					const u8 *cur =
964854a0d95SVineet Gupta 					    ptr + (i / 2) * (2 * tableSize);
965854a0d95SVineet Gupta 
966854a0d95SVineet Gupta 					startLoc = read_pointer(&cur,
967854a0d95SVineet Gupta 								cur + tableSize,
968854a0d95SVineet Gupta 								hdr[3]);
969854a0d95SVineet Gupta 					if (pc < startLoc)
970854a0d95SVineet Gupta 						i /= 2;
971854a0d95SVineet Gupta 					else {
972854a0d95SVineet Gupta 						ptr = cur - tableSize;
973854a0d95SVineet Gupta 						i = (i + 1) / 2;
974854a0d95SVineet Gupta 					}
975854a0d95SVineet Gupta 				} while (startLoc && i > 1);
976854a0d95SVineet Gupta 				if (i == 1
977854a0d95SVineet Gupta 				    && (startLoc = read_pointer(&ptr,
978854a0d95SVineet Gupta 								ptr + tableSize,
979854a0d95SVineet Gupta 								hdr[3])) != 0
980854a0d95SVineet Gupta 				    && pc >= startLoc)
981854a0d95SVineet Gupta 					fde = (void *)read_pointer(&ptr,
982854a0d95SVineet Gupta 								   ptr +
983854a0d95SVineet Gupta 								   tableSize,
984854a0d95SVineet Gupta 								   hdr[3]);
985854a0d95SVineet Gupta 			}
986854a0d95SVineet Gupta 		}
987854a0d95SVineet Gupta 
988854a0d95SVineet Gupta 		if (fde != NULL) {
989854a0d95SVineet Gupta 			cie = cie_for_fde(fde, table);
990854a0d95SVineet Gupta 			ptr = (const u8 *)(fde + 2);
991854a0d95SVineet Gupta 			if (cie != NULL
992854a0d95SVineet Gupta 			    && cie != &bad_cie
993854a0d95SVineet Gupta 			    && cie != &not_fde
994854a0d95SVineet Gupta 			    && (ptrType = fde_pointer_type(cie)) >= 0
995854a0d95SVineet Gupta 			    && read_pointer(&ptr,
996854a0d95SVineet Gupta 					    (const u8 *)(fde + 1) + *fde,
997854a0d95SVineet Gupta 					    ptrType) == startLoc) {
998854a0d95SVineet Gupta 				if (!(ptrType & DW_EH_PE_indirect))
999854a0d95SVineet Gupta 					ptrType &=
1000854a0d95SVineet Gupta 					    DW_EH_PE_FORM | DW_EH_PE_signed;
1001854a0d95SVineet Gupta 				endLoc =
1002854a0d95SVineet Gupta 				    startLoc + read_pointer(&ptr,
1003854a0d95SVineet Gupta 							    (const u8 *)(fde +
1004854a0d95SVineet Gupta 									 1) +
1005854a0d95SVineet Gupta 							    *fde, ptrType);
10062e22502cSVineet Gupta 				if (pc >= endLoc) {
1007854a0d95SVineet Gupta 					fde = NULL;
1008854a0d95SVineet Gupta 					cie = NULL;
1009854a0d95SVineet Gupta 				}
10102e22502cSVineet Gupta 			} else {
10112e22502cSVineet Gupta 				fde = NULL;
10122e22502cSVineet Gupta 				cie = NULL;
1013854a0d95SVineet Gupta 			}
1014854a0d95SVineet Gupta 		}
1015854a0d95SVineet Gupta 	}
1016854a0d95SVineet Gupta 	if (cie != NULL) {
1017854a0d95SVineet Gupta 		memset(&state, 0, sizeof(state));
1018854a0d95SVineet Gupta 		state.cieEnd = ptr;	/* keep here temporarily */
1019854a0d95SVineet Gupta 		ptr = (const u8 *)(cie + 2);
1020854a0d95SVineet Gupta 		end = (const u8 *)(cie + 1) + *cie;
1021854a0d95SVineet Gupta 		frame->call_frame = 1;
10226d0d5060SVineet Gupta 		if (*++ptr) {
1023854a0d95SVineet Gupta 			/* check if augmentation size is first (thus present) */
1024854a0d95SVineet Gupta 			if (*ptr == 'z') {
1025854a0d95SVineet Gupta 				while (++ptr < end && *ptr) {
1026854a0d95SVineet Gupta 					switch (*ptr) {
1027854a0d95SVineet Gupta 					/* chk for ignorable or already handled
1028854a0d95SVineet Gupta 					 * nul-terminated augmentation string */
1029854a0d95SVineet Gupta 					case 'L':
1030854a0d95SVineet Gupta 					case 'P':
1031854a0d95SVineet Gupta 					case 'R':
1032854a0d95SVineet Gupta 						continue;
1033854a0d95SVineet Gupta 					case 'S':
1034854a0d95SVineet Gupta 						frame->call_frame = 0;
1035854a0d95SVineet Gupta 						continue;
1036854a0d95SVineet Gupta 					default:
1037854a0d95SVineet Gupta 						break;
1038854a0d95SVineet Gupta 					}
1039854a0d95SVineet Gupta 					break;
1040854a0d95SVineet Gupta 				}
1041854a0d95SVineet Gupta 			}
1042854a0d95SVineet Gupta 			if (ptr >= end || *ptr)
1043854a0d95SVineet Gupta 				cie = NULL;
1044854a0d95SVineet Gupta 		}
1045854a0d95SVineet Gupta 		++ptr;
1046854a0d95SVineet Gupta 	}
1047854a0d95SVineet Gupta 	if (cie != NULL) {
1048550116d2SMasahiro Yamada 		/* get code alignment factor */
1049854a0d95SVineet Gupta 		state.codeAlign = get_uleb128(&ptr, end);
1050550116d2SMasahiro Yamada 		/* get data alignment factor */
1051854a0d95SVineet Gupta 		state.dataAlign = get_sleb128(&ptr, end);
1052854a0d95SVineet Gupta 		if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
1053854a0d95SVineet Gupta 			cie = NULL;
1054854a0d95SVineet Gupta 		else {
1055854a0d95SVineet Gupta 			retAddrReg =
1056854a0d95SVineet Gupta 			    state.version <= 1 ? *ptr++ : get_uleb128(&ptr,
1057854a0d95SVineet Gupta 								      end);
1058854a0d95SVineet Gupta 			unw_debug("CIE Frame Info:\n");
1059854a0d95SVineet Gupta 			unw_debug("return Address register 0x%lx\n",
1060854a0d95SVineet Gupta 				  retAddrReg);
1061854a0d95SVineet Gupta 			unw_debug("data Align: %ld\n", state.dataAlign);
1062854a0d95SVineet Gupta 			unw_debug("code Align: %lu\n", state.codeAlign);
1063854a0d95SVineet Gupta 			/* skip augmentation */
1064854a0d95SVineet Gupta 			if (((const char *)(cie + 2))[1] == 'z') {
1065854a0d95SVineet Gupta 				uleb128_t augSize = get_uleb128(&ptr, end);
1066854a0d95SVineet Gupta 
1067854a0d95SVineet Gupta 				ptr += augSize;
1068854a0d95SVineet Gupta 			}
1069854a0d95SVineet Gupta 			if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info)
1070854a0d95SVineet Gupta 			    || REG_INVALID(retAddrReg)
1071854a0d95SVineet Gupta 			    || reg_info[retAddrReg].width !=
1072854a0d95SVineet Gupta 			    sizeof(unsigned long))
1073854a0d95SVineet Gupta 				cie = NULL;
1074854a0d95SVineet Gupta 		}
1075854a0d95SVineet Gupta 	}
1076854a0d95SVineet Gupta 	if (cie != NULL) {
1077854a0d95SVineet Gupta 		state.cieStart = ptr;
1078854a0d95SVineet Gupta 		ptr = state.cieEnd;
1079854a0d95SVineet Gupta 		state.cieEnd = end;
1080854a0d95SVineet Gupta 		end = (const u8 *)(fde + 1) + *fde;
1081854a0d95SVineet Gupta 		/* skip augmentation */
1082854a0d95SVineet Gupta 		if (((const char *)(cie + 2))[1] == 'z') {
1083854a0d95SVineet Gupta 			uleb128_t augSize = get_uleb128(&ptr, end);
1084854a0d95SVineet Gupta 
1085854a0d95SVineet Gupta 			if ((ptr += augSize) > end)
1086854a0d95SVineet Gupta 				fde = NULL;
1087854a0d95SVineet Gupta 		}
1088854a0d95SVineet Gupta 	}
1089854a0d95SVineet Gupta 	if (cie == NULL || fde == NULL) {
1090854a0d95SVineet Gupta #ifdef CONFIG_FRAME_POINTER
1091854a0d95SVineet Gupta 		unsigned long top, bottom;
1092854a0d95SVineet Gupta 
1093854a0d95SVineet Gupta 		top = STACK_TOP_UNW(frame->task);
1094854a0d95SVineet Gupta 		bottom = STACK_BOTTOM_UNW(frame->task);
1095854a0d95SVineet Gupta #if FRAME_RETADDR_OFFSET < 0
1096854a0d95SVineet Gupta 		if (UNW_SP(frame) < top && UNW_FP(frame) <= UNW_SP(frame)
1097854a0d95SVineet Gupta 		    && bottom < UNW_FP(frame)
1098854a0d95SVineet Gupta #else
1099854a0d95SVineet Gupta 		if (UNW_SP(frame) > top && UNW_FP(frame) >= UNW_SP(frame)
1100854a0d95SVineet Gupta 		    && bottom > UNW_FP(frame)
1101854a0d95SVineet Gupta #endif
1102854a0d95SVineet Gupta 		    && !((UNW_SP(frame) | UNW_FP(frame))
1103854a0d95SVineet Gupta 			 & (sizeof(unsigned long) - 1))) {
1104854a0d95SVineet Gupta 			unsigned long link;
1105854a0d95SVineet Gupta 
1106854a0d95SVineet Gupta 			if (!__get_user(link, (unsigned long *)
1107854a0d95SVineet Gupta 					(UNW_FP(frame) + FRAME_LINK_OFFSET))
1108854a0d95SVineet Gupta #if FRAME_RETADDR_OFFSET < 0
1109854a0d95SVineet Gupta 			    && link > bottom && link < UNW_FP(frame)
1110854a0d95SVineet Gupta #else
1111854a0d95SVineet Gupta 			    && link > UNW_FP(frame) && link < bottom
1112854a0d95SVineet Gupta #endif
1113854a0d95SVineet Gupta 			    && !(link & (sizeof(link) - 1))
1114854a0d95SVineet Gupta 			    && !__get_user(UNW_PC(frame),
1115854a0d95SVineet Gupta 					   (unsigned long *)(UNW_FP(frame)
1116854a0d95SVineet Gupta 						+ FRAME_RETADDR_OFFSET)))
1117854a0d95SVineet Gupta 			{
1118854a0d95SVineet Gupta 				UNW_SP(frame) =
1119854a0d95SVineet Gupta 				    UNW_FP(frame) + FRAME_RETADDR_OFFSET
1120854a0d95SVineet Gupta #if FRAME_RETADDR_OFFSET < 0
1121854a0d95SVineet Gupta 				    -
1122854a0d95SVineet Gupta #else
1123854a0d95SVineet Gupta 				    +
1124854a0d95SVineet Gupta #endif
1125854a0d95SVineet Gupta 				    sizeof(UNW_PC(frame));
1126854a0d95SVineet Gupta 				UNW_FP(frame) = link;
1127854a0d95SVineet Gupta 				return 0;
1128854a0d95SVineet Gupta 			}
1129854a0d95SVineet Gupta 		}
1130854a0d95SVineet Gupta #endif
1131854a0d95SVineet Gupta 		return -ENXIO;
1132854a0d95SVineet Gupta 	}
1133854a0d95SVineet Gupta 	state.org = startLoc;
1134854a0d95SVineet Gupta 	memcpy(&state.cfa, &badCFA, sizeof(state.cfa));
1135854a0d95SVineet Gupta 
1136854a0d95SVineet Gupta 	unw_debug("\nProcess instructions\n");
1137854a0d95SVineet Gupta 
1138854a0d95SVineet Gupta 	/* process instructions
1139854a0d95SVineet Gupta 	 * For ARC, we optimize by having blink(retAddrReg) with
1140854a0d95SVineet Gupta 	 * the sameValue in the leaf function, so we should not check
1141854a0d95SVineet Gupta 	 * state.regs[retAddrReg].where == Nowhere
1142854a0d95SVineet Gupta 	 */
1143854a0d95SVineet Gupta 	if (!processCFI(ptr, end, pc, ptrType, &state)
1144854a0d95SVineet Gupta 	    || state.loc > endLoc
1145854a0d95SVineet Gupta /*	   || state.regs[retAddrReg].where == Nowhere */
1146854a0d95SVineet Gupta 	    || state.cfa.reg >= ARRAY_SIZE(reg_info)
1147854a0d95SVineet Gupta 	    || reg_info[state.cfa.reg].width != sizeof(unsigned long)
1148854a0d95SVineet Gupta 	    || state.cfa.offs % sizeof(unsigned long))
1149854a0d95SVineet Gupta 		return -EIO;
1150854a0d95SVineet Gupta 
1151854a0d95SVineet Gupta #ifdef UNWIND_DEBUG
1152854a0d95SVineet Gupta 	unw_debug("\n");
1153854a0d95SVineet Gupta 
1154854a0d95SVineet Gupta 	unw_debug("\nRegister State Based on the rules parsed from FDE:\n");
1155854a0d95SVineet Gupta 	for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
1156854a0d95SVineet Gupta 
1157854a0d95SVineet Gupta 		if (REG_INVALID(i))
1158854a0d95SVineet Gupta 			continue;
1159854a0d95SVineet Gupta 
1160854a0d95SVineet Gupta 		switch (state.regs[i].where) {
1161854a0d95SVineet Gupta 		case Nowhere:
1162854a0d95SVineet Gupta 			break;
1163854a0d95SVineet Gupta 		case Memory:
1164854a0d95SVineet Gupta 			unw_debug(" r%d: c(%lu),", i, state.regs[i].value);
1165854a0d95SVineet Gupta 			break;
1166854a0d95SVineet Gupta 		case Register:
1167854a0d95SVineet Gupta 			unw_debug(" r%d: r(%lu),", i, state.regs[i].value);
1168854a0d95SVineet Gupta 			break;
1169854a0d95SVineet Gupta 		case Value:
1170854a0d95SVineet Gupta 			unw_debug(" r%d: v(%lu),", i, state.regs[i].value);
1171854a0d95SVineet Gupta 			break;
1172854a0d95SVineet Gupta 		}
1173854a0d95SVineet Gupta 	}
1174854a0d95SVineet Gupta 
1175854a0d95SVineet Gupta 	unw_debug("\n");
1176854a0d95SVineet Gupta #endif
1177854a0d95SVineet Gupta 
1178854a0d95SVineet Gupta 	/* update frame */
1179854a0d95SVineet Gupta 	if (frame->call_frame
1180854a0d95SVineet Gupta 	    && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign))
1181854a0d95SVineet Gupta 		frame->call_frame = 0;
1182854a0d95SVineet Gupta 	cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
1183854a0d95SVineet Gupta 	startLoc = min_t(unsigned long, UNW_SP(frame), cfa);
1184854a0d95SVineet Gupta 	endLoc = max_t(unsigned long, UNW_SP(frame), cfa);
1185854a0d95SVineet Gupta 	if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) {
1186854a0d95SVineet Gupta 		startLoc = min(STACK_LIMIT(cfa), cfa);
1187854a0d95SVineet Gupta 		endLoc = max(STACK_LIMIT(cfa), cfa);
1188854a0d95SVineet Gupta 	}
1189854a0d95SVineet Gupta 
1190854a0d95SVineet Gupta 	unw_debug("\nCFA reg: 0x%lx, offset: 0x%lx =>  0x%lx\n",
1191854a0d95SVineet Gupta 		  state.cfa.reg, state.cfa.offs, cfa);
1192854a0d95SVineet Gupta 
1193854a0d95SVineet Gupta 	for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
1194854a0d95SVineet Gupta 		if (REG_INVALID(i)) {
1195854a0d95SVineet Gupta 			if (state.regs[i].where == Nowhere)
1196854a0d95SVineet Gupta 				continue;
1197854a0d95SVineet Gupta 			return -EIO;
1198854a0d95SVineet Gupta 		}
1199854a0d95SVineet Gupta 		switch (state.regs[i].where) {
1200854a0d95SVineet Gupta 		default:
1201854a0d95SVineet Gupta 			break;
1202854a0d95SVineet Gupta 		case Register:
1203854a0d95SVineet Gupta 			if (state.regs[i].value >= ARRAY_SIZE(reg_info)
1204854a0d95SVineet Gupta 			    || REG_INVALID(state.regs[i].value)
1205854a0d95SVineet Gupta 			    || reg_info[i].width >
1206854a0d95SVineet Gupta 			    reg_info[state.regs[i].value].width)
1207854a0d95SVineet Gupta 				return -EIO;
1208854a0d95SVineet Gupta 			switch (reg_info[state.regs[i].value].width) {
1209854a0d95SVineet Gupta 			case sizeof(u8):
1210854a0d95SVineet Gupta 				state.regs[i].value =
1211854a0d95SVineet Gupta 				FRAME_REG(state.regs[i].value, const u8);
1212854a0d95SVineet Gupta 				break;
1213854a0d95SVineet Gupta 			case sizeof(u16):
1214854a0d95SVineet Gupta 				state.regs[i].value =
1215854a0d95SVineet Gupta 				FRAME_REG(state.regs[i].value, const u16);
1216854a0d95SVineet Gupta 				break;
1217854a0d95SVineet Gupta 			case sizeof(u32):
1218854a0d95SVineet Gupta 				state.regs[i].value =
1219854a0d95SVineet Gupta 				FRAME_REG(state.regs[i].value, const u32);
1220854a0d95SVineet Gupta 				break;
1221854a0d95SVineet Gupta #ifdef CONFIG_64BIT
1222854a0d95SVineet Gupta 			case sizeof(u64):
1223854a0d95SVineet Gupta 				state.regs[i].value =
1224854a0d95SVineet Gupta 				FRAME_REG(state.regs[i].value, const u64);
1225854a0d95SVineet Gupta 				break;
1226854a0d95SVineet Gupta #endif
1227854a0d95SVineet Gupta 			default:
1228854a0d95SVineet Gupta 				return -EIO;
1229854a0d95SVineet Gupta 			}
1230854a0d95SVineet Gupta 			break;
1231854a0d95SVineet Gupta 		}
1232854a0d95SVineet Gupta 	}
1233854a0d95SVineet Gupta 
1234854a0d95SVineet Gupta 	unw_debug("\nRegister state after evaluation with realtime Stack:\n");
1235854a0d95SVineet Gupta 	fptr = (unsigned long *)(&frame->regs);
1236854a0d95SVineet Gupta 	for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) {
1237854a0d95SVineet Gupta 
1238854a0d95SVineet Gupta 		if (REG_INVALID(i))
1239854a0d95SVineet Gupta 			continue;
1240854a0d95SVineet Gupta 		switch (state.regs[i].where) {
1241854a0d95SVineet Gupta 		case Nowhere:
1242854a0d95SVineet Gupta 			if (reg_info[i].width != sizeof(UNW_SP(frame))
1243854a0d95SVineet Gupta 			    || &FRAME_REG(i, __typeof__(UNW_SP(frame)))
1244854a0d95SVineet Gupta 			    != &UNW_SP(frame))
1245854a0d95SVineet Gupta 				continue;
1246854a0d95SVineet Gupta 			UNW_SP(frame) = cfa;
1247854a0d95SVineet Gupta 			break;
1248854a0d95SVineet Gupta 		case Register:
1249854a0d95SVineet Gupta 			switch (reg_info[i].width) {
1250854a0d95SVineet Gupta 			case sizeof(u8):
1251854a0d95SVineet Gupta 				FRAME_REG(i, u8) = state.regs[i].value;
1252854a0d95SVineet Gupta 				break;
1253854a0d95SVineet Gupta 			case sizeof(u16):
1254854a0d95SVineet Gupta 				FRAME_REG(i, u16) = state.regs[i].value;
1255854a0d95SVineet Gupta 				break;
1256854a0d95SVineet Gupta 			case sizeof(u32):
1257854a0d95SVineet Gupta 				FRAME_REG(i, u32) = state.regs[i].value;
1258854a0d95SVineet Gupta 				break;
1259854a0d95SVineet Gupta #ifdef CONFIG_64BIT
1260854a0d95SVineet Gupta 			case sizeof(u64):
1261854a0d95SVineet Gupta 				FRAME_REG(i, u64) = state.regs[i].value;
1262854a0d95SVineet Gupta 				break;
1263854a0d95SVineet Gupta #endif
1264854a0d95SVineet Gupta 			default:
1265854a0d95SVineet Gupta 				return -EIO;
1266854a0d95SVineet Gupta 			}
1267854a0d95SVineet Gupta 			break;
1268854a0d95SVineet Gupta 		case Value:
1269854a0d95SVineet Gupta 			if (reg_info[i].width != sizeof(unsigned long))
1270854a0d95SVineet Gupta 				return -EIO;
1271854a0d95SVineet Gupta 			FRAME_REG(i, unsigned long) = cfa + state.regs[i].value
1272854a0d95SVineet Gupta 			    * state.dataAlign;
1273854a0d95SVineet Gupta 			break;
1274854a0d95SVineet Gupta 		case Memory:
1275854a0d95SVineet Gupta 			addr = cfa + state.regs[i].value * state.dataAlign;
1276854a0d95SVineet Gupta 
1277854a0d95SVineet Gupta 			if ((state.regs[i].value * state.dataAlign)
1278854a0d95SVineet Gupta 			    % sizeof(unsigned long)
1279854a0d95SVineet Gupta 			    || addr < startLoc
1280854a0d95SVineet Gupta 			    || addr + sizeof(unsigned long) < addr
1281854a0d95SVineet Gupta 			    || addr + sizeof(unsigned long) > endLoc)
1282854a0d95SVineet Gupta 					return -EIO;
1283854a0d95SVineet Gupta 
1284854a0d95SVineet Gupta 			switch (reg_info[i].width) {
1285854a0d95SVineet Gupta 			case sizeof(u8):
1286854a0d95SVineet Gupta 				__get_user(FRAME_REG(i, u8),
1287854a0d95SVineet Gupta 					   (u8 __user *)addr);
1288854a0d95SVineet Gupta 				break;
1289854a0d95SVineet Gupta 			case sizeof(u16):
1290854a0d95SVineet Gupta 				__get_user(FRAME_REG(i, u16),
1291854a0d95SVineet Gupta 					   (u16 __user *)addr);
1292854a0d95SVineet Gupta 				break;
1293854a0d95SVineet Gupta 			case sizeof(u32):
1294854a0d95SVineet Gupta 				__get_user(FRAME_REG(i, u32),
1295854a0d95SVineet Gupta 					   (u32 __user *)addr);
1296854a0d95SVineet Gupta 				break;
1297854a0d95SVineet Gupta #ifdef CONFIG_64BIT
1298854a0d95SVineet Gupta 			case sizeof(u64):
1299854a0d95SVineet Gupta 				__get_user(FRAME_REG(i, u64),
1300854a0d95SVineet Gupta 					   (u64 __user *)addr);
1301854a0d95SVineet Gupta 				break;
1302854a0d95SVineet Gupta #endif
1303854a0d95SVineet Gupta 			default:
1304854a0d95SVineet Gupta 				return -EIO;
1305854a0d95SVineet Gupta 			}
1306854a0d95SVineet Gupta 
1307854a0d95SVineet Gupta 			break;
1308854a0d95SVineet Gupta 		}
1309854a0d95SVineet Gupta 		unw_debug("r%d: 0x%lx ", i, *fptr);
1310854a0d95SVineet Gupta 	}
1311854a0d95SVineet Gupta 
1312854a0d95SVineet Gupta 	return 0;
1313854a0d95SVineet Gupta #undef FRAME_REG
1314854a0d95SVineet Gupta }
1315854a0d95SVineet Gupta EXPORT_SYMBOL(arc_unwind);
1316