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 == ¬_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 ¬_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 != ¬_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