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