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); 173bc79c9a7SVineet Gupta static void init_unwind_hdr(struct unwind_table *table, 174bc79c9a7SVineet Gupta void *(*alloc) (unsigned long)); 175bc79c9a7SVineet Gupta 176bc79c9a7SVineet Gupta /* 177bc79c9a7SVineet Gupta * wrappers for header alloc (vs. calling one vs. other at call site) 178bc79c9a7SVineet Gupta * to elide section mismatches warnings 179bc79c9a7SVineet Gupta */ 180bc79c9a7SVineet Gupta static void *__init unw_hdr_alloc_early(unsigned long sz) 181bc79c9a7SVineet Gupta { 182bc79c9a7SVineet Gupta return __alloc_bootmem_nopanic(sz, sizeof(unsigned int), 183bc79c9a7SVineet Gupta MAX_DMA_ADDRESS); 184bc79c9a7SVineet Gupta } 185bc79c9a7SVineet Gupta 186bc79c9a7SVineet Gupta static void *unw_hdr_alloc(unsigned long sz) 187bc79c9a7SVineet Gupta { 188bc79c9a7SVineet Gupta return kmalloc(sz, GFP_KERNEL); 189bc79c9a7SVineet Gupta } 190854a0d95SVineet Gupta 191854a0d95SVineet Gupta static void init_unwind_table(struct unwind_table *table, const char *name, 192854a0d95SVineet Gupta const void *core_start, unsigned long core_size, 193854a0d95SVineet Gupta const void *init_start, unsigned long init_size, 194854a0d95SVineet Gupta const void *table_start, unsigned long table_size, 195854a0d95SVineet Gupta const u8 *header_start, unsigned long header_size) 196854a0d95SVineet Gupta { 197854a0d95SVineet Gupta const u8 *ptr = header_start + 4; 198854a0d95SVineet Gupta const u8 *end = header_start + header_size; 199854a0d95SVineet Gupta 200854a0d95SVineet Gupta table->core.pc = (unsigned long)core_start; 201854a0d95SVineet Gupta table->core.range = core_size; 202854a0d95SVineet Gupta table->init.pc = (unsigned long)init_start; 203854a0d95SVineet Gupta table->init.range = init_size; 204854a0d95SVineet Gupta table->address = table_start; 205854a0d95SVineet Gupta table->size = table_size; 206854a0d95SVineet Gupta 207854a0d95SVineet Gupta /* See if the linker provided table looks valid. */ 208854a0d95SVineet Gupta if (header_size <= 4 209854a0d95SVineet Gupta || header_start[0] != 1 210854a0d95SVineet Gupta || (void *)read_pointer(&ptr, end, header_start[1]) != table_start 211854a0d95SVineet Gupta || header_start[2] == DW_EH_PE_omit 212854a0d95SVineet Gupta || read_pointer(&ptr, end, header_start[2]) <= 0 213854a0d95SVineet Gupta || header_start[3] == DW_EH_PE_omit) 214854a0d95SVineet Gupta header_start = NULL; 215854a0d95SVineet Gupta 216854a0d95SVineet Gupta table->hdrsz = header_size; 217854a0d95SVineet Gupta smp_wmb(); 218854a0d95SVineet Gupta table->header = header_start; 219854a0d95SVineet Gupta table->link = NULL; 220854a0d95SVineet Gupta table->name = name; 221854a0d95SVineet Gupta } 222854a0d95SVineet Gupta 223854a0d95SVineet Gupta void __init arc_unwind_init(void) 224854a0d95SVineet Gupta { 225854a0d95SVineet Gupta init_unwind_table(&root_table, "kernel", _text, _end - _text, NULL, 0, 226854a0d95SVineet Gupta __start_unwind, __end_unwind - __start_unwind, 227854a0d95SVineet Gupta NULL, 0); 228854a0d95SVineet Gupta /*__start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr);*/ 229bc79c9a7SVineet Gupta 230bc79c9a7SVineet Gupta init_unwind_hdr(&root_table, unw_hdr_alloc_early); 231854a0d95SVineet Gupta } 232854a0d95SVineet Gupta 233854a0d95SVineet Gupta static const u32 bad_cie, not_fde; 234854a0d95SVineet Gupta static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *); 235854a0d95SVineet Gupta static signed fde_pointer_type(const u32 *cie); 236854a0d95SVineet Gupta 237854a0d95SVineet Gupta struct eh_frame_hdr_table_entry { 238854a0d95SVineet Gupta unsigned long start, fde; 239854a0d95SVineet Gupta }; 240854a0d95SVineet Gupta 241854a0d95SVineet Gupta static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2) 242854a0d95SVineet Gupta { 243854a0d95SVineet Gupta const struct eh_frame_hdr_table_entry *e1 = p1; 244854a0d95SVineet Gupta const struct eh_frame_hdr_table_entry *e2 = p2; 245854a0d95SVineet Gupta 246854a0d95SVineet Gupta return (e1->start > e2->start) - (e1->start < e2->start); 247854a0d95SVineet Gupta } 248854a0d95SVineet Gupta 249854a0d95SVineet Gupta static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size) 250854a0d95SVineet Gupta { 251854a0d95SVineet Gupta struct eh_frame_hdr_table_entry *e1 = p1; 252854a0d95SVineet Gupta struct eh_frame_hdr_table_entry *e2 = p2; 253854a0d95SVineet Gupta unsigned long v; 254854a0d95SVineet Gupta 255854a0d95SVineet Gupta v = e1->start; 256854a0d95SVineet Gupta e1->start = e2->start; 257854a0d95SVineet Gupta e2->start = v; 258854a0d95SVineet Gupta v = e1->fde; 259854a0d95SVineet Gupta e1->fde = e2->fde; 260854a0d95SVineet Gupta e2->fde = v; 261854a0d95SVineet Gupta } 262854a0d95SVineet Gupta 263bc79c9a7SVineet Gupta static void init_unwind_hdr(struct unwind_table *table, 264854a0d95SVineet Gupta void *(*alloc) (unsigned long)) 265854a0d95SVineet Gupta { 266854a0d95SVineet Gupta const u8 *ptr; 267854a0d95SVineet Gupta unsigned long tableSize = table->size, hdrSize; 268854a0d95SVineet Gupta unsigned n; 269854a0d95SVineet Gupta const u32 *fde; 270854a0d95SVineet Gupta struct { 271854a0d95SVineet Gupta u8 version; 272854a0d95SVineet Gupta u8 eh_frame_ptr_enc; 273854a0d95SVineet Gupta u8 fde_count_enc; 274854a0d95SVineet Gupta u8 table_enc; 275854a0d95SVineet Gupta unsigned long eh_frame_ptr; 276854a0d95SVineet Gupta unsigned int fde_count; 277854a0d95SVineet Gupta struct eh_frame_hdr_table_entry table[]; 278854a0d95SVineet Gupta } __attribute__ ((__packed__)) *header; 279854a0d95SVineet Gupta 280854a0d95SVineet Gupta if (table->header) 281854a0d95SVineet Gupta return; 282854a0d95SVineet Gupta 283854a0d95SVineet Gupta if (table->hdrsz) 284854a0d95SVineet Gupta pr_warn(".eh_frame_hdr for '%s' present but unusable\n", 285854a0d95SVineet Gupta table->name); 286854a0d95SVineet Gupta 287854a0d95SVineet Gupta if (tableSize & (sizeof(*fde) - 1)) 288854a0d95SVineet Gupta return; 289854a0d95SVineet Gupta 290854a0d95SVineet Gupta for (fde = table->address, n = 0; 291854a0d95SVineet Gupta tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; 292854a0d95SVineet Gupta tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { 293854a0d95SVineet Gupta const u32 *cie = cie_for_fde(fde, table); 294854a0d95SVineet Gupta signed ptrType; 295854a0d95SVineet Gupta 2962d64affcSVineet Gupta if (cie == ¬_fde) 297854a0d95SVineet Gupta continue; 298854a0d95SVineet Gupta if (cie == NULL || cie == &bad_cie) 2992d64affcSVineet Gupta return; 300854a0d95SVineet Gupta ptrType = fde_pointer_type(cie); 301854a0d95SVineet Gupta if (ptrType < 0) 3022d64affcSVineet Gupta return; 303854a0d95SVineet Gupta 304854a0d95SVineet Gupta ptr = (const u8 *)(fde + 2); 305854a0d95SVineet Gupta if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, 306854a0d95SVineet Gupta ptrType)) { 307854a0d95SVineet Gupta /* FIXME_Rajesh We have 4 instances of null addresses 308854a0d95SVineet Gupta * instead of the initial loc addr 309854a0d95SVineet Gupta * return; 310854a0d95SVineet Gupta */ 311baadb8fdSVineet Gupta WARN(1, "unwinder: FDE->initial_location NULL %p\n", 312baadb8fdSVineet Gupta (const u8 *)(fde + 1) + *fde); 313854a0d95SVineet Gupta } 314854a0d95SVineet Gupta ++n; 315854a0d95SVineet Gupta } 316854a0d95SVineet Gupta 317854a0d95SVineet Gupta if (tableSize || !n) 318854a0d95SVineet Gupta return; 319854a0d95SVineet Gupta 320854a0d95SVineet Gupta hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) 321854a0d95SVineet Gupta + 2 * n * sizeof(unsigned long); 322bc79c9a7SVineet Gupta 323854a0d95SVineet Gupta header = alloc(hdrSize); 324854a0d95SVineet Gupta if (!header) 325854a0d95SVineet Gupta return; 326bc79c9a7SVineet Gupta 327854a0d95SVineet Gupta header->version = 1; 328854a0d95SVineet Gupta header->eh_frame_ptr_enc = DW_EH_PE_abs | DW_EH_PE_native; 329854a0d95SVineet Gupta header->fde_count_enc = DW_EH_PE_abs | DW_EH_PE_data4; 330854a0d95SVineet Gupta header->table_enc = DW_EH_PE_abs | DW_EH_PE_native; 331854a0d95SVineet Gupta put_unaligned((unsigned long)table->address, &header->eh_frame_ptr); 332854a0d95SVineet Gupta BUILD_BUG_ON(offsetof(typeof(*header), fde_count) 333854a0d95SVineet Gupta % __alignof(typeof(header->fde_count))); 334854a0d95SVineet Gupta header->fde_count = n; 335854a0d95SVineet Gupta 336854a0d95SVineet Gupta BUILD_BUG_ON(offsetof(typeof(*header), table) 337854a0d95SVineet Gupta % __alignof(typeof(*header->table))); 338854a0d95SVineet Gupta for (fde = table->address, tableSize = table->size, n = 0; 339854a0d95SVineet Gupta tableSize; 340854a0d95SVineet Gupta tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { 341854a0d95SVineet Gupta /* const u32 *cie = fde + 1 - fde[1] / sizeof(*fde); */ 342854a0d95SVineet Gupta const u32 *cie = (const u32 *)(fde[1]); 343854a0d95SVineet Gupta 344854a0d95SVineet Gupta if (fde[1] == 0xffffffff) 345854a0d95SVineet Gupta continue; /* this is a CIE */ 346854a0d95SVineet Gupta ptr = (const u8 *)(fde + 2); 347854a0d95SVineet Gupta header->table[n].start = read_pointer(&ptr, 348854a0d95SVineet Gupta (const u8 *)(fde + 1) + 349854a0d95SVineet Gupta *fde, 350854a0d95SVineet Gupta fde_pointer_type(cie)); 351854a0d95SVineet Gupta header->table[n].fde = (unsigned long)fde; 352854a0d95SVineet Gupta ++n; 353854a0d95SVineet Gupta } 354854a0d95SVineet Gupta WARN_ON(n != header->fde_count); 355854a0d95SVineet Gupta 356854a0d95SVineet Gupta sort(header->table, 357854a0d95SVineet Gupta n, 358854a0d95SVineet Gupta sizeof(*header->table), 359854a0d95SVineet Gupta cmp_eh_frame_hdr_table_entries, swap_eh_frame_hdr_table_entries); 360854a0d95SVineet Gupta 361854a0d95SVineet Gupta table->hdrsz = hdrSize; 362854a0d95SVineet Gupta smp_wmb(); 363854a0d95SVineet Gupta table->header = (const void *)header; 364854a0d95SVineet Gupta } 365854a0d95SVineet Gupta 366854a0d95SVineet Gupta #ifdef CONFIG_MODULES 367854a0d95SVineet Gupta 368854a0d95SVineet Gupta static struct unwind_table *last_table; 369854a0d95SVineet Gupta 370854a0d95SVineet Gupta /* Must be called with module_mutex held. */ 371854a0d95SVineet Gupta void *unwind_add_table(struct module *module, const void *table_start, 372854a0d95SVineet Gupta unsigned long table_size) 373854a0d95SVineet Gupta { 374854a0d95SVineet Gupta struct unwind_table *table; 375854a0d95SVineet Gupta 376854a0d95SVineet Gupta if (table_size <= 0) 377854a0d95SVineet Gupta return NULL; 378854a0d95SVineet Gupta 379854a0d95SVineet Gupta table = kmalloc(sizeof(*table), GFP_KERNEL); 380854a0d95SVineet Gupta if (!table) 381854a0d95SVineet Gupta return NULL; 382854a0d95SVineet Gupta 383854a0d95SVineet Gupta init_unwind_table(table, module->name, 384854a0d95SVineet Gupta module->module_core, module->core_size, 385854a0d95SVineet Gupta module->module_init, module->init_size, 386854a0d95SVineet Gupta table_start, table_size, 387854a0d95SVineet Gupta NULL, 0); 388854a0d95SVineet Gupta 389bc79c9a7SVineet Gupta init_unwind_hdr(table, unw_hdr_alloc); 390bc79c9a7SVineet Gupta 391854a0d95SVineet Gupta #ifdef UNWIND_DEBUG 392854a0d95SVineet Gupta unw_debug("Table added for [%s] %lx %lx\n", 393854a0d95SVineet Gupta module->name, table->core.pc, table->core.range); 394854a0d95SVineet Gupta #endif 395854a0d95SVineet Gupta if (last_table) 396854a0d95SVineet Gupta last_table->link = table; 397854a0d95SVineet Gupta else 398854a0d95SVineet Gupta root_table.link = table; 399854a0d95SVineet Gupta last_table = table; 400854a0d95SVineet Gupta 401854a0d95SVineet Gupta return table; 402854a0d95SVineet Gupta } 403854a0d95SVineet Gupta 404854a0d95SVineet Gupta struct unlink_table_info { 405854a0d95SVineet Gupta struct unwind_table *table; 406854a0d95SVineet Gupta int init_only; 407854a0d95SVineet Gupta }; 408854a0d95SVineet Gupta 409854a0d95SVineet Gupta static int unlink_table(void *arg) 410854a0d95SVineet Gupta { 411854a0d95SVineet Gupta struct unlink_table_info *info = arg; 412854a0d95SVineet Gupta struct unwind_table *table = info->table, *prev; 413854a0d95SVineet Gupta 414854a0d95SVineet Gupta for (prev = &root_table; prev->link && prev->link != table; 415854a0d95SVineet Gupta prev = prev->link) 416854a0d95SVineet Gupta ; 417854a0d95SVineet Gupta 418854a0d95SVineet Gupta if (prev->link) { 419854a0d95SVineet Gupta if (info->init_only) { 420854a0d95SVineet Gupta table->init.pc = 0; 421854a0d95SVineet Gupta table->init.range = 0; 422854a0d95SVineet Gupta info->table = NULL; 423854a0d95SVineet Gupta } else { 424854a0d95SVineet Gupta prev->link = table->link; 425854a0d95SVineet Gupta if (!prev->link) 426854a0d95SVineet Gupta last_table = prev; 427854a0d95SVineet Gupta } 428854a0d95SVineet Gupta } else 429854a0d95SVineet Gupta info->table = NULL; 430854a0d95SVineet Gupta 431854a0d95SVineet Gupta return 0; 432854a0d95SVineet Gupta } 433854a0d95SVineet Gupta 434854a0d95SVineet Gupta /* Must be called with module_mutex held. */ 435854a0d95SVineet Gupta void unwind_remove_table(void *handle, int init_only) 436854a0d95SVineet Gupta { 437854a0d95SVineet Gupta struct unwind_table *table = handle; 438854a0d95SVineet Gupta struct unlink_table_info info; 439854a0d95SVineet Gupta 440854a0d95SVineet Gupta if (!table || table == &root_table) 441854a0d95SVineet Gupta return; 442854a0d95SVineet Gupta 443854a0d95SVineet Gupta if (init_only && table == last_table) { 444854a0d95SVineet Gupta table->init.pc = 0; 445854a0d95SVineet Gupta table->init.range = 0; 446854a0d95SVineet Gupta return; 447854a0d95SVineet Gupta } 448854a0d95SVineet Gupta 449854a0d95SVineet Gupta info.table = table; 450854a0d95SVineet Gupta info.init_only = init_only; 451854a0d95SVineet Gupta 452854a0d95SVineet Gupta unlink_table(&info); /* XXX: SMP */ 453bc79c9a7SVineet Gupta kfree(table->header); 454854a0d95SVineet Gupta kfree(table); 455854a0d95SVineet Gupta } 456854a0d95SVineet Gupta 457854a0d95SVineet Gupta #endif /* CONFIG_MODULES */ 458854a0d95SVineet Gupta 459854a0d95SVineet Gupta static uleb128_t get_uleb128(const u8 **pcur, const u8 *end) 460854a0d95SVineet Gupta { 461854a0d95SVineet Gupta const u8 *cur = *pcur; 462854a0d95SVineet Gupta uleb128_t value; 463854a0d95SVineet Gupta unsigned shift; 464854a0d95SVineet Gupta 465854a0d95SVineet Gupta for (shift = 0, value = 0; cur < end; shift += 7) { 466854a0d95SVineet Gupta if (shift + 7 > 8 * sizeof(value) 467854a0d95SVineet Gupta && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { 468854a0d95SVineet Gupta cur = end + 1; 469854a0d95SVineet Gupta break; 470854a0d95SVineet Gupta } 471854a0d95SVineet Gupta value |= (uleb128_t) (*cur & 0x7f) << shift; 472854a0d95SVineet Gupta if (!(*cur++ & 0x80)) 473854a0d95SVineet Gupta break; 474854a0d95SVineet Gupta } 475854a0d95SVineet Gupta *pcur = cur; 476854a0d95SVineet Gupta 477854a0d95SVineet Gupta return value; 478854a0d95SVineet Gupta } 479854a0d95SVineet Gupta 480854a0d95SVineet Gupta static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) 481854a0d95SVineet Gupta { 482854a0d95SVineet Gupta const u8 *cur = *pcur; 483854a0d95SVineet Gupta sleb128_t value; 484854a0d95SVineet Gupta unsigned shift; 485854a0d95SVineet Gupta 486854a0d95SVineet Gupta for (shift = 0, value = 0; cur < end; shift += 7) { 487854a0d95SVineet Gupta if (shift + 7 > 8 * sizeof(value) 488854a0d95SVineet Gupta && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { 489854a0d95SVineet Gupta cur = end + 1; 490854a0d95SVineet Gupta break; 491854a0d95SVineet Gupta } 492854a0d95SVineet Gupta value |= (sleb128_t) (*cur & 0x7f) << shift; 493854a0d95SVineet Gupta if (!(*cur & 0x80)) { 494854a0d95SVineet Gupta value |= -(*cur++ & 0x40) << shift; 495854a0d95SVineet Gupta break; 496854a0d95SVineet Gupta } 497854a0d95SVineet Gupta } 498854a0d95SVineet Gupta *pcur = cur; 499854a0d95SVineet Gupta 500854a0d95SVineet Gupta return value; 501854a0d95SVineet Gupta } 502854a0d95SVineet Gupta 503854a0d95SVineet Gupta static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table) 504854a0d95SVineet Gupta { 505854a0d95SVineet Gupta const u32 *cie; 506854a0d95SVineet Gupta 507854a0d95SVineet Gupta if (!*fde || (*fde & (sizeof(*fde) - 1))) 508854a0d95SVineet Gupta return &bad_cie; 509854a0d95SVineet Gupta 510854a0d95SVineet Gupta if (fde[1] == 0xffffffff) 511854a0d95SVineet Gupta return ¬_fde; /* this is a CIE */ 512854a0d95SVineet Gupta 513854a0d95SVineet Gupta if ((fde[1] & (sizeof(*fde) - 1))) 514854a0d95SVineet Gupta /* || fde[1] > (unsigned long)(fde + 1) - (unsigned long)table->address) */ 515854a0d95SVineet Gupta return NULL; /* this is not a valid FDE */ 516854a0d95SVineet Gupta 517854a0d95SVineet Gupta /* cie = fde + 1 - fde[1] / sizeof(*fde); */ 518854a0d95SVineet Gupta cie = (u32 *) fde[1]; 519854a0d95SVineet Gupta 520854a0d95SVineet Gupta if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) 521854a0d95SVineet Gupta || (*cie & (sizeof(*cie) - 1)) 5222d64affcSVineet Gupta || (cie[1] != 0xffffffff)) 523854a0d95SVineet Gupta return NULL; /* this is not a (valid) CIE */ 524854a0d95SVineet Gupta return cie; 525854a0d95SVineet Gupta } 526854a0d95SVineet Gupta 527854a0d95SVineet Gupta static unsigned long read_pointer(const u8 **pLoc, const void *end, 528854a0d95SVineet Gupta signed ptrType) 529854a0d95SVineet Gupta { 530854a0d95SVineet Gupta unsigned long value = 0; 531854a0d95SVineet Gupta union { 532854a0d95SVineet Gupta const u8 *p8; 533854a0d95SVineet Gupta const u16 *p16u; 534854a0d95SVineet Gupta const s16 *p16s; 535854a0d95SVineet Gupta const u32 *p32u; 536854a0d95SVineet Gupta const s32 *p32s; 537854a0d95SVineet Gupta const unsigned long *pul; 538854a0d95SVineet Gupta } ptr; 539854a0d95SVineet Gupta 540854a0d95SVineet Gupta if (ptrType < 0 || ptrType == DW_EH_PE_omit) 541854a0d95SVineet Gupta return 0; 542854a0d95SVineet Gupta ptr.p8 = *pLoc; 543854a0d95SVineet Gupta switch (ptrType & DW_EH_PE_FORM) { 544854a0d95SVineet Gupta case DW_EH_PE_data2: 545854a0d95SVineet Gupta if (end < (const void *)(ptr.p16u + 1)) 546854a0d95SVineet Gupta return 0; 547854a0d95SVineet Gupta if (ptrType & DW_EH_PE_signed) 548854a0d95SVineet Gupta value = get_unaligned((u16 *) ptr.p16s++); 549854a0d95SVineet Gupta else 550854a0d95SVineet Gupta value = get_unaligned((u16 *) ptr.p16u++); 551854a0d95SVineet Gupta break; 552854a0d95SVineet Gupta case DW_EH_PE_data4: 553854a0d95SVineet Gupta #ifdef CONFIG_64BIT 554854a0d95SVineet Gupta if (end < (const void *)(ptr.p32u + 1)) 555854a0d95SVineet Gupta return 0; 556854a0d95SVineet Gupta if (ptrType & DW_EH_PE_signed) 557854a0d95SVineet Gupta value = get_unaligned(ptr.p32s++); 558854a0d95SVineet Gupta else 559854a0d95SVineet Gupta value = get_unaligned(ptr.p32u++); 560854a0d95SVineet Gupta break; 561854a0d95SVineet Gupta case DW_EH_PE_data8: 562854a0d95SVineet Gupta BUILD_BUG_ON(sizeof(u64) != sizeof(value)); 563854a0d95SVineet Gupta #else 564854a0d95SVineet Gupta BUILD_BUG_ON(sizeof(u32) != sizeof(value)); 565854a0d95SVineet Gupta #endif 566854a0d95SVineet Gupta case DW_EH_PE_native: 567854a0d95SVineet Gupta if (end < (const void *)(ptr.pul + 1)) 568854a0d95SVineet Gupta return 0; 569854a0d95SVineet Gupta value = get_unaligned((unsigned long *)ptr.pul++); 570854a0d95SVineet Gupta break; 571854a0d95SVineet Gupta case DW_EH_PE_leb128: 572854a0d95SVineet Gupta BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); 573854a0d95SVineet Gupta value = ptrType & DW_EH_PE_signed ? get_sleb128(&ptr.p8, end) 574854a0d95SVineet Gupta : get_uleb128(&ptr.p8, end); 575854a0d95SVineet Gupta if ((const void *)ptr.p8 > end) 576854a0d95SVineet Gupta return 0; 577854a0d95SVineet Gupta break; 578854a0d95SVineet Gupta default: 579854a0d95SVineet Gupta return 0; 580854a0d95SVineet Gupta } 581854a0d95SVineet Gupta switch (ptrType & DW_EH_PE_ADJUST) { 582854a0d95SVineet Gupta case DW_EH_PE_abs: 583854a0d95SVineet Gupta break; 584854a0d95SVineet Gupta case DW_EH_PE_pcrel: 585854a0d95SVineet Gupta value += (unsigned long)*pLoc; 586854a0d95SVineet Gupta break; 587854a0d95SVineet Gupta default: 588854a0d95SVineet Gupta return 0; 589854a0d95SVineet Gupta } 590854a0d95SVineet Gupta if ((ptrType & DW_EH_PE_indirect) 591854a0d95SVineet Gupta && __get_user(value, (unsigned long __user *)value)) 592854a0d95SVineet Gupta return 0; 593854a0d95SVineet Gupta *pLoc = ptr.p8; 594854a0d95SVineet Gupta 595854a0d95SVineet Gupta return value; 596854a0d95SVineet Gupta } 597854a0d95SVineet Gupta 598854a0d95SVineet Gupta static signed fde_pointer_type(const u32 *cie) 599854a0d95SVineet Gupta { 600854a0d95SVineet Gupta const u8 *ptr = (const u8 *)(cie + 2); 601854a0d95SVineet Gupta unsigned version = *ptr; 602854a0d95SVineet Gupta 603854a0d95SVineet Gupta if (version != 1) 604854a0d95SVineet Gupta return -1; /* unsupported */ 605854a0d95SVineet Gupta 606854a0d95SVineet Gupta if (*++ptr) { 607854a0d95SVineet Gupta const char *aug; 608854a0d95SVineet Gupta const u8 *end = (const u8 *)(cie + 1) + *cie; 609854a0d95SVineet Gupta uleb128_t len; 610854a0d95SVineet Gupta 611854a0d95SVineet Gupta /* check if augmentation size is first (and thus present) */ 612854a0d95SVineet Gupta if (*ptr != 'z') 613854a0d95SVineet Gupta return -1; 614854a0d95SVineet Gupta 615854a0d95SVineet Gupta /* check if augmentation string is nul-terminated */ 616854a0d95SVineet Gupta aug = (const void *)ptr; 617854a0d95SVineet Gupta ptr = memchr(aug, 0, end - ptr); 618854a0d95SVineet Gupta if (ptr == NULL) 619854a0d95SVineet Gupta return -1; 620854a0d95SVineet Gupta 621854a0d95SVineet Gupta ++ptr; /* skip terminator */ 622854a0d95SVineet Gupta get_uleb128(&ptr, end); /* skip code alignment */ 623854a0d95SVineet Gupta get_sleb128(&ptr, end); /* skip data alignment */ 624854a0d95SVineet Gupta /* skip return address column */ 625854a0d95SVineet Gupta version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end); 626854a0d95SVineet Gupta len = get_uleb128(&ptr, end); /* augmentation length */ 627854a0d95SVineet Gupta 628854a0d95SVineet Gupta if (ptr + len < ptr || ptr + len > end) 629854a0d95SVineet Gupta return -1; 630854a0d95SVineet Gupta 631854a0d95SVineet Gupta end = ptr + len; 632854a0d95SVineet Gupta while (*++aug) { 633854a0d95SVineet Gupta if (ptr >= end) 634854a0d95SVineet Gupta return -1; 635854a0d95SVineet Gupta switch (*aug) { 636854a0d95SVineet Gupta case 'L': 637854a0d95SVineet Gupta ++ptr; 638854a0d95SVineet Gupta break; 639854a0d95SVineet Gupta case 'P':{ 640854a0d95SVineet Gupta signed ptrType = *ptr++; 641854a0d95SVineet Gupta 642854a0d95SVineet Gupta if (!read_pointer(&ptr, end, ptrType) 643854a0d95SVineet Gupta || ptr > end) 644854a0d95SVineet Gupta return -1; 645854a0d95SVineet Gupta } 646854a0d95SVineet Gupta break; 647854a0d95SVineet Gupta case 'R': 648854a0d95SVineet Gupta return *ptr; 649854a0d95SVineet Gupta default: 650854a0d95SVineet Gupta return -1; 651854a0d95SVineet Gupta } 652854a0d95SVineet Gupta } 653854a0d95SVineet Gupta } 654854a0d95SVineet Gupta return DW_EH_PE_native | DW_EH_PE_abs; 655854a0d95SVineet Gupta } 656854a0d95SVineet Gupta 657854a0d95SVineet Gupta static int advance_loc(unsigned long delta, struct unwind_state *state) 658854a0d95SVineet Gupta { 659854a0d95SVineet Gupta state->loc += delta * state->codeAlign; 660854a0d95SVineet Gupta 661854a0d95SVineet Gupta /* FIXME_Rajesh: Probably we are defining for the initial range as well; 662854a0d95SVineet Gupta return delta > 0; 663854a0d95SVineet Gupta */ 664854a0d95SVineet Gupta unw_debug("delta %3lu => loc 0x%lx: ", delta, state->loc); 665854a0d95SVineet Gupta return 1; 666854a0d95SVineet Gupta } 667854a0d95SVineet Gupta 668854a0d95SVineet Gupta static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value, 669854a0d95SVineet Gupta struct unwind_state *state) 670854a0d95SVineet Gupta { 671854a0d95SVineet Gupta if (reg < ARRAY_SIZE(state->regs)) { 672854a0d95SVineet Gupta state->regs[reg].where = where; 673854a0d95SVineet Gupta state->regs[reg].value = value; 674854a0d95SVineet Gupta 675854a0d95SVineet Gupta #ifdef UNWIND_DEBUG 676854a0d95SVineet Gupta unw_debug("r%lu: ", reg); 677854a0d95SVineet Gupta switch (where) { 678854a0d95SVineet Gupta case Nowhere: 679854a0d95SVineet Gupta unw_debug("s "); 680854a0d95SVineet Gupta break; 681854a0d95SVineet Gupta case Memory: 682854a0d95SVineet Gupta unw_debug("c(%lu) ", value); 683854a0d95SVineet Gupta break; 684854a0d95SVineet Gupta case Register: 685854a0d95SVineet Gupta unw_debug("r(%lu) ", value); 686854a0d95SVineet Gupta break; 687854a0d95SVineet Gupta case Value: 688854a0d95SVineet Gupta unw_debug("v(%lu) ", value); 689854a0d95SVineet Gupta break; 690854a0d95SVineet Gupta default: 691854a0d95SVineet Gupta break; 692854a0d95SVineet Gupta } 693854a0d95SVineet Gupta #endif 694854a0d95SVineet Gupta } 695854a0d95SVineet Gupta } 696854a0d95SVineet Gupta 697854a0d95SVineet Gupta static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, 698854a0d95SVineet Gupta signed ptrType, struct unwind_state *state) 699854a0d95SVineet Gupta { 700854a0d95SVineet Gupta union { 701854a0d95SVineet Gupta const u8 *p8; 702854a0d95SVineet Gupta const u16 *p16; 703854a0d95SVineet Gupta const u32 *p32; 704854a0d95SVineet Gupta } ptr; 705854a0d95SVineet Gupta int result = 1; 706854a0d95SVineet Gupta u8 opcode; 707854a0d95SVineet Gupta 708854a0d95SVineet Gupta if (start != state->cieStart) { 709854a0d95SVineet Gupta state->loc = state->org; 710854a0d95SVineet Gupta result = 711854a0d95SVineet Gupta processCFI(state->cieStart, state->cieEnd, 0, ptrType, 712854a0d95SVineet Gupta state); 713854a0d95SVineet Gupta if (targetLoc == 0 && state->label == NULL) 714854a0d95SVineet Gupta return result; 715854a0d95SVineet Gupta } 716854a0d95SVineet Gupta for (ptr.p8 = start; result && ptr.p8 < end;) { 717854a0d95SVineet Gupta switch (*ptr.p8 >> 6) { 718854a0d95SVineet Gupta uleb128_t value; 719854a0d95SVineet Gupta 720854a0d95SVineet Gupta case 0: 721854a0d95SVineet Gupta opcode = *ptr.p8++; 722854a0d95SVineet Gupta 723854a0d95SVineet Gupta switch (opcode) { 724854a0d95SVineet Gupta case DW_CFA_nop: 725854a0d95SVineet Gupta unw_debug("cfa nop "); 726854a0d95SVineet Gupta break; 727854a0d95SVineet Gupta case DW_CFA_set_loc: 728854a0d95SVineet Gupta state->loc = read_pointer(&ptr.p8, end, 729854a0d95SVineet Gupta ptrType); 730854a0d95SVineet Gupta if (state->loc == 0) 731854a0d95SVineet Gupta result = 0; 732854a0d95SVineet Gupta unw_debug("cfa_set_loc: 0x%lx ", state->loc); 733854a0d95SVineet Gupta break; 734854a0d95SVineet Gupta case DW_CFA_advance_loc1: 735854a0d95SVineet Gupta unw_debug("\ncfa advance loc1:"); 736854a0d95SVineet Gupta result = ptr.p8 < end 737854a0d95SVineet Gupta && advance_loc(*ptr.p8++, state); 738854a0d95SVineet Gupta break; 739854a0d95SVineet Gupta case DW_CFA_advance_loc2: 740854a0d95SVineet Gupta value = *ptr.p8++; 741854a0d95SVineet Gupta value += *ptr.p8++ << 8; 742854a0d95SVineet Gupta unw_debug("\ncfa advance loc2:"); 743854a0d95SVineet Gupta result = ptr.p8 <= end + 2 744854a0d95SVineet Gupta /* && advance_loc(*ptr.p16++, state); */ 745854a0d95SVineet Gupta && advance_loc(value, state); 746854a0d95SVineet Gupta break; 747854a0d95SVineet Gupta case DW_CFA_advance_loc4: 748854a0d95SVineet Gupta unw_debug("\ncfa advance loc4:"); 749854a0d95SVineet Gupta result = ptr.p8 <= end + 4 750854a0d95SVineet Gupta && advance_loc(*ptr.p32++, state); 751854a0d95SVineet Gupta break; 752854a0d95SVineet Gupta case DW_CFA_offset_extended: 753854a0d95SVineet Gupta value = get_uleb128(&ptr.p8, end); 754854a0d95SVineet Gupta unw_debug("cfa_offset_extended: "); 755854a0d95SVineet Gupta set_rule(value, Memory, 756854a0d95SVineet Gupta get_uleb128(&ptr.p8, end), state); 757854a0d95SVineet Gupta break; 758854a0d95SVineet Gupta case DW_CFA_val_offset: 759854a0d95SVineet Gupta value = get_uleb128(&ptr.p8, end); 760854a0d95SVineet Gupta set_rule(value, Value, 761854a0d95SVineet Gupta get_uleb128(&ptr.p8, end), state); 762854a0d95SVineet Gupta break; 763854a0d95SVineet Gupta case DW_CFA_offset_extended_sf: 764854a0d95SVineet Gupta value = get_uleb128(&ptr.p8, end); 765854a0d95SVineet Gupta set_rule(value, Memory, 766854a0d95SVineet Gupta get_sleb128(&ptr.p8, end), state); 767854a0d95SVineet Gupta break; 768854a0d95SVineet Gupta case DW_CFA_val_offset_sf: 769854a0d95SVineet Gupta value = get_uleb128(&ptr.p8, end); 770854a0d95SVineet Gupta set_rule(value, Value, 771854a0d95SVineet Gupta get_sleb128(&ptr.p8, end), state); 772854a0d95SVineet Gupta break; 773854a0d95SVineet Gupta case DW_CFA_restore_extended: 774854a0d95SVineet Gupta unw_debug("cfa_restore_extended: "); 775854a0d95SVineet Gupta case DW_CFA_undefined: 776854a0d95SVineet Gupta unw_debug("cfa_undefined: "); 777854a0d95SVineet Gupta case DW_CFA_same_value: 778854a0d95SVineet Gupta unw_debug("cfa_same_value: "); 779854a0d95SVineet Gupta set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, 780854a0d95SVineet Gupta state); 781854a0d95SVineet Gupta break; 782854a0d95SVineet Gupta case DW_CFA_register: 783854a0d95SVineet Gupta unw_debug("cfa_register: "); 784854a0d95SVineet Gupta value = get_uleb128(&ptr.p8, end); 785854a0d95SVineet Gupta set_rule(value, 786854a0d95SVineet Gupta Register, 787854a0d95SVineet Gupta get_uleb128(&ptr.p8, end), state); 788854a0d95SVineet Gupta break; 789854a0d95SVineet Gupta case DW_CFA_remember_state: 790854a0d95SVineet Gupta unw_debug("cfa_remember_state: "); 791854a0d95SVineet Gupta if (ptr.p8 == state->label) { 792854a0d95SVineet Gupta state->label = NULL; 793854a0d95SVineet Gupta return 1; 794854a0d95SVineet Gupta } 795854a0d95SVineet Gupta if (state->stackDepth >= MAX_STACK_DEPTH) 796854a0d95SVineet Gupta return 0; 797854a0d95SVineet Gupta state->stack[state->stackDepth++] = ptr.p8; 798854a0d95SVineet Gupta break; 799854a0d95SVineet Gupta case DW_CFA_restore_state: 800854a0d95SVineet Gupta unw_debug("cfa_restore_state: "); 801854a0d95SVineet Gupta if (state->stackDepth) { 802854a0d95SVineet Gupta const uleb128_t loc = state->loc; 803854a0d95SVineet Gupta const u8 *label = state->label; 804854a0d95SVineet Gupta 805854a0d95SVineet Gupta state->label = 806854a0d95SVineet Gupta state->stack[state->stackDepth - 1]; 807854a0d95SVineet Gupta memcpy(&state->cfa, &badCFA, 808854a0d95SVineet Gupta sizeof(state->cfa)); 809854a0d95SVineet Gupta memset(state->regs, 0, 810854a0d95SVineet Gupta sizeof(state->regs)); 811854a0d95SVineet Gupta state->stackDepth = 0; 812854a0d95SVineet Gupta result = 813854a0d95SVineet Gupta processCFI(start, end, 0, ptrType, 814854a0d95SVineet Gupta state); 815854a0d95SVineet Gupta state->loc = loc; 816854a0d95SVineet Gupta state->label = label; 817854a0d95SVineet Gupta } else 818854a0d95SVineet Gupta return 0; 819854a0d95SVineet Gupta break; 820854a0d95SVineet Gupta case DW_CFA_def_cfa: 821854a0d95SVineet Gupta state->cfa.reg = get_uleb128(&ptr.p8, end); 822854a0d95SVineet Gupta unw_debug("cfa_def_cfa: r%lu ", state->cfa.reg); 823854a0d95SVineet Gupta /*nobreak*/ 824854a0d95SVineet Gupta case DW_CFA_def_cfa_offset: 825854a0d95SVineet Gupta state->cfa.offs = get_uleb128(&ptr.p8, end); 826854a0d95SVineet Gupta unw_debug("cfa_def_cfa_offset: 0x%lx ", 827854a0d95SVineet Gupta state->cfa.offs); 828854a0d95SVineet Gupta break; 829854a0d95SVineet Gupta case DW_CFA_def_cfa_sf: 830854a0d95SVineet Gupta state->cfa.reg = get_uleb128(&ptr.p8, end); 831854a0d95SVineet Gupta /*nobreak */ 832854a0d95SVineet Gupta case DW_CFA_def_cfa_offset_sf: 833854a0d95SVineet Gupta state->cfa.offs = get_sleb128(&ptr.p8, end) 834854a0d95SVineet Gupta * state->dataAlign; 835854a0d95SVineet Gupta break; 836854a0d95SVineet Gupta case DW_CFA_def_cfa_register: 837854a0d95SVineet Gupta unw_debug("cfa_def_cfa_regsiter: "); 838854a0d95SVineet Gupta state->cfa.reg = get_uleb128(&ptr.p8, end); 839854a0d95SVineet Gupta break; 840854a0d95SVineet Gupta /*todo case DW_CFA_def_cfa_expression: */ 841854a0d95SVineet Gupta /*todo case DW_CFA_expression: */ 842854a0d95SVineet Gupta /*todo case DW_CFA_val_expression: */ 843854a0d95SVineet Gupta case DW_CFA_GNU_args_size: 844854a0d95SVineet Gupta get_uleb128(&ptr.p8, end); 845854a0d95SVineet Gupta break; 846854a0d95SVineet Gupta case DW_CFA_GNU_negative_offset_extended: 847854a0d95SVineet Gupta value = get_uleb128(&ptr.p8, end); 848854a0d95SVineet Gupta set_rule(value, 849854a0d95SVineet Gupta Memory, 850854a0d95SVineet Gupta (uleb128_t) 0 - get_uleb128(&ptr.p8, 851854a0d95SVineet Gupta end), 852854a0d95SVineet Gupta state); 853854a0d95SVineet Gupta break; 854854a0d95SVineet Gupta case DW_CFA_GNU_window_save: 855854a0d95SVineet Gupta default: 856d939be3aSMasanari Iida unw_debug("UNKNOWN OPCODE 0x%x\n", opcode); 857854a0d95SVineet Gupta result = 0; 858854a0d95SVineet Gupta break; 859854a0d95SVineet Gupta } 860854a0d95SVineet Gupta break; 861854a0d95SVineet Gupta case 1: 862854a0d95SVineet Gupta unw_debug("\ncfa_adv_loc: "); 863854a0d95SVineet Gupta result = advance_loc(*ptr.p8++ & 0x3f, state); 864854a0d95SVineet Gupta break; 865854a0d95SVineet Gupta case 2: 866854a0d95SVineet Gupta unw_debug("cfa_offset: "); 867854a0d95SVineet Gupta value = *ptr.p8++ & 0x3f; 868854a0d95SVineet Gupta set_rule(value, Memory, get_uleb128(&ptr.p8, end), 869854a0d95SVineet Gupta state); 870854a0d95SVineet Gupta break; 871854a0d95SVineet Gupta case 3: 872854a0d95SVineet Gupta unw_debug("cfa_restore: "); 873854a0d95SVineet Gupta set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); 874854a0d95SVineet Gupta break; 875854a0d95SVineet Gupta } 876854a0d95SVineet Gupta 877854a0d95SVineet Gupta if (ptr.p8 > end) 878854a0d95SVineet Gupta result = 0; 879854a0d95SVineet Gupta if (result && targetLoc != 0 && targetLoc < state->loc) 880854a0d95SVineet Gupta return 1; 881854a0d95SVineet Gupta } 882854a0d95SVineet Gupta 883854a0d95SVineet Gupta return result && ptr.p8 == end && (targetLoc == 0 || ( 884854a0d95SVineet Gupta /*todo While in theory this should apply, gcc in practice omits 885854a0d95SVineet Gupta everything past the function prolog, and hence the location 886854a0d95SVineet Gupta never reaches the end of the function. 887854a0d95SVineet Gupta targetLoc < state->loc && */ state->label == NULL)); 888854a0d95SVineet Gupta } 889854a0d95SVineet Gupta 890854a0d95SVineet Gupta /* Unwind to previous to frame. Returns 0 if successful, negative 891854a0d95SVineet Gupta * number in case of an error. */ 892854a0d95SVineet Gupta int arc_unwind(struct unwind_frame_info *frame) 893854a0d95SVineet Gupta { 894854a0d95SVineet Gupta #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) 895854a0d95SVineet Gupta const u32 *fde = NULL, *cie = NULL; 896854a0d95SVineet Gupta const u8 *ptr = NULL, *end = NULL; 897854a0d95SVineet Gupta unsigned long pc = UNW_PC(frame) - frame->call_frame; 898854a0d95SVineet Gupta unsigned long startLoc = 0, endLoc = 0, cfa; 899854a0d95SVineet Gupta unsigned i; 900854a0d95SVineet Gupta signed ptrType = -1; 901854a0d95SVineet Gupta uleb128_t retAddrReg = 0; 902854a0d95SVineet Gupta const struct unwind_table *table; 903854a0d95SVineet Gupta struct unwind_state state; 904854a0d95SVineet Gupta unsigned long *fptr; 905854a0d95SVineet Gupta unsigned long addr; 906854a0d95SVineet Gupta 907854a0d95SVineet Gupta unw_debug("\n\nUNWIND FRAME:\n"); 908854a0d95SVineet Gupta unw_debug("PC: 0x%lx BLINK: 0x%lx, SP: 0x%lx, FP: 0x%x\n", 909854a0d95SVineet Gupta UNW_PC(frame), UNW_BLINK(frame), UNW_SP(frame), 910854a0d95SVineet Gupta UNW_FP(frame)); 911854a0d95SVineet Gupta 912854a0d95SVineet Gupta if (UNW_PC(frame) == 0) 913854a0d95SVineet Gupta return -EINVAL; 914854a0d95SVineet Gupta 915854a0d95SVineet Gupta #ifdef UNWIND_DEBUG 916854a0d95SVineet Gupta { 917854a0d95SVineet Gupta unsigned long *sptr = (unsigned long *)UNW_SP(frame); 918854a0d95SVineet Gupta unw_debug("\nStack Dump:\n"); 919854a0d95SVineet Gupta for (i = 0; i < 20; i++, sptr++) 920854a0d95SVineet Gupta unw_debug("0x%p: 0x%lx\n", sptr, *sptr); 921854a0d95SVineet Gupta unw_debug("\n"); 922854a0d95SVineet Gupta } 923854a0d95SVineet Gupta #endif 924854a0d95SVineet Gupta 925854a0d95SVineet Gupta table = find_table(pc); 926854a0d95SVineet Gupta if (table != NULL 927854a0d95SVineet Gupta && !(table->size & (sizeof(*fde) - 1))) { 928854a0d95SVineet Gupta const u8 *hdr = table->header; 929854a0d95SVineet Gupta unsigned long tableSize; 930854a0d95SVineet Gupta 931854a0d95SVineet Gupta smp_rmb(); 932854a0d95SVineet Gupta if (hdr && hdr[0] == 1) { 933854a0d95SVineet Gupta switch (hdr[3] & DW_EH_PE_FORM) { 934854a0d95SVineet Gupta case DW_EH_PE_native: 935854a0d95SVineet Gupta tableSize = sizeof(unsigned long); 936854a0d95SVineet Gupta break; 937854a0d95SVineet Gupta case DW_EH_PE_data2: 938854a0d95SVineet Gupta tableSize = 2; 939854a0d95SVineet Gupta break; 940854a0d95SVineet Gupta case DW_EH_PE_data4: 941854a0d95SVineet Gupta tableSize = 4; 942854a0d95SVineet Gupta break; 943854a0d95SVineet Gupta case DW_EH_PE_data8: 944854a0d95SVineet Gupta tableSize = 8; 945854a0d95SVineet Gupta break; 946854a0d95SVineet Gupta default: 947854a0d95SVineet Gupta tableSize = 0; 948854a0d95SVineet Gupta break; 949854a0d95SVineet Gupta } 950854a0d95SVineet Gupta ptr = hdr + 4; 951854a0d95SVineet Gupta end = hdr + table->hdrsz; 952854a0d95SVineet Gupta if (tableSize && read_pointer(&ptr, end, hdr[1]) 953854a0d95SVineet Gupta == (unsigned long)table->address 954854a0d95SVineet Gupta && (i = read_pointer(&ptr, end, hdr[2])) > 0 955854a0d95SVineet Gupta && i == (end - ptr) / (2 * tableSize) 956854a0d95SVineet Gupta && !((end - ptr) % (2 * tableSize))) { 957854a0d95SVineet Gupta do { 958854a0d95SVineet Gupta const u8 *cur = 959854a0d95SVineet Gupta ptr + (i / 2) * (2 * tableSize); 960854a0d95SVineet Gupta 961854a0d95SVineet Gupta startLoc = read_pointer(&cur, 962854a0d95SVineet Gupta cur + tableSize, 963854a0d95SVineet Gupta hdr[3]); 964854a0d95SVineet Gupta if (pc < startLoc) 965854a0d95SVineet Gupta i /= 2; 966854a0d95SVineet Gupta else { 967854a0d95SVineet Gupta ptr = cur - tableSize; 968854a0d95SVineet Gupta i = (i + 1) / 2; 969854a0d95SVineet Gupta } 970854a0d95SVineet Gupta } while (startLoc && i > 1); 971854a0d95SVineet Gupta if (i == 1 972854a0d95SVineet Gupta && (startLoc = read_pointer(&ptr, 973854a0d95SVineet Gupta ptr + tableSize, 974854a0d95SVineet Gupta hdr[3])) != 0 975854a0d95SVineet Gupta && pc >= startLoc) 976854a0d95SVineet Gupta fde = (void *)read_pointer(&ptr, 977854a0d95SVineet Gupta ptr + 978854a0d95SVineet Gupta tableSize, 979854a0d95SVineet Gupta hdr[3]); 980854a0d95SVineet Gupta } 981854a0d95SVineet Gupta } 982854a0d95SVineet Gupta 983854a0d95SVineet Gupta if (fde != NULL) { 984854a0d95SVineet Gupta cie = cie_for_fde(fde, table); 985854a0d95SVineet Gupta ptr = (const u8 *)(fde + 2); 986854a0d95SVineet Gupta if (cie != NULL 987854a0d95SVineet Gupta && cie != &bad_cie 988854a0d95SVineet Gupta && cie != ¬_fde 989854a0d95SVineet Gupta && (ptrType = fde_pointer_type(cie)) >= 0 990854a0d95SVineet Gupta && read_pointer(&ptr, 991854a0d95SVineet Gupta (const u8 *)(fde + 1) + *fde, 992854a0d95SVineet Gupta ptrType) == startLoc) { 993854a0d95SVineet Gupta if (!(ptrType & DW_EH_PE_indirect)) 994854a0d95SVineet Gupta ptrType &= 995854a0d95SVineet Gupta DW_EH_PE_FORM | DW_EH_PE_signed; 996854a0d95SVineet Gupta endLoc = 997854a0d95SVineet Gupta startLoc + read_pointer(&ptr, 998854a0d95SVineet Gupta (const u8 *)(fde + 999854a0d95SVineet Gupta 1) + 1000854a0d95SVineet Gupta *fde, ptrType); 10012e22502cSVineet Gupta if (pc >= endLoc) { 1002854a0d95SVineet Gupta fde = NULL; 1003854a0d95SVineet Gupta cie = NULL; 1004854a0d95SVineet Gupta } 10052e22502cSVineet Gupta } else { 10062e22502cSVineet Gupta fde = NULL; 10072e22502cSVineet Gupta cie = NULL; 1008854a0d95SVineet Gupta } 1009854a0d95SVineet Gupta } 1010854a0d95SVineet Gupta } 1011854a0d95SVineet Gupta if (cie != NULL) { 1012854a0d95SVineet Gupta memset(&state, 0, sizeof(state)); 1013854a0d95SVineet Gupta state.cieEnd = ptr; /* keep here temporarily */ 1014854a0d95SVineet Gupta ptr = (const u8 *)(cie + 2); 1015854a0d95SVineet Gupta end = (const u8 *)(cie + 1) + *cie; 1016854a0d95SVineet Gupta frame->call_frame = 1; 1017854a0d95SVineet Gupta if ((state.version = *ptr) != 1) 1018854a0d95SVineet Gupta cie = NULL; /* unsupported version */ 1019854a0d95SVineet Gupta else if (*++ptr) { 1020854a0d95SVineet Gupta /* check if augmentation size is first (thus present) */ 1021854a0d95SVineet Gupta if (*ptr == 'z') { 1022854a0d95SVineet Gupta while (++ptr < end && *ptr) { 1023854a0d95SVineet Gupta switch (*ptr) { 1024854a0d95SVineet Gupta /* chk for ignorable or already handled 1025854a0d95SVineet Gupta * nul-terminated augmentation string */ 1026854a0d95SVineet Gupta case 'L': 1027854a0d95SVineet Gupta case 'P': 1028854a0d95SVineet Gupta case 'R': 1029854a0d95SVineet Gupta continue; 1030854a0d95SVineet Gupta case 'S': 1031854a0d95SVineet Gupta frame->call_frame = 0; 1032854a0d95SVineet Gupta continue; 1033854a0d95SVineet Gupta default: 1034854a0d95SVineet Gupta break; 1035854a0d95SVineet Gupta } 1036854a0d95SVineet Gupta break; 1037854a0d95SVineet Gupta } 1038854a0d95SVineet Gupta } 1039854a0d95SVineet Gupta if (ptr >= end || *ptr) 1040854a0d95SVineet Gupta cie = NULL; 1041854a0d95SVineet Gupta } 1042854a0d95SVineet Gupta ++ptr; 1043854a0d95SVineet Gupta } 1044854a0d95SVineet Gupta if (cie != NULL) { 1045854a0d95SVineet Gupta /* get code aligment factor */ 1046854a0d95SVineet Gupta state.codeAlign = get_uleb128(&ptr, end); 1047854a0d95SVineet Gupta /* get data aligment factor */ 1048854a0d95SVineet Gupta state.dataAlign = get_sleb128(&ptr, end); 1049854a0d95SVineet Gupta if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) 1050854a0d95SVineet Gupta cie = NULL; 1051854a0d95SVineet Gupta else { 1052854a0d95SVineet Gupta retAddrReg = 1053854a0d95SVineet Gupta state.version <= 1 ? *ptr++ : get_uleb128(&ptr, 1054854a0d95SVineet Gupta end); 1055854a0d95SVineet Gupta unw_debug("CIE Frame Info:\n"); 1056854a0d95SVineet Gupta unw_debug("return Address register 0x%lx\n", 1057854a0d95SVineet Gupta retAddrReg); 1058854a0d95SVineet Gupta unw_debug("data Align: %ld\n", state.dataAlign); 1059854a0d95SVineet Gupta unw_debug("code Align: %lu\n", state.codeAlign); 1060854a0d95SVineet Gupta /* skip augmentation */ 1061854a0d95SVineet Gupta if (((const char *)(cie + 2))[1] == 'z') { 1062854a0d95SVineet Gupta uleb128_t augSize = get_uleb128(&ptr, end); 1063854a0d95SVineet Gupta 1064854a0d95SVineet Gupta ptr += augSize; 1065854a0d95SVineet Gupta } 1066854a0d95SVineet Gupta if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info) 1067854a0d95SVineet Gupta || REG_INVALID(retAddrReg) 1068854a0d95SVineet Gupta || reg_info[retAddrReg].width != 1069854a0d95SVineet Gupta sizeof(unsigned long)) 1070854a0d95SVineet Gupta cie = NULL; 1071854a0d95SVineet Gupta } 1072854a0d95SVineet Gupta } 1073854a0d95SVineet Gupta if (cie != NULL) { 1074854a0d95SVineet Gupta state.cieStart = ptr; 1075854a0d95SVineet Gupta ptr = state.cieEnd; 1076854a0d95SVineet Gupta state.cieEnd = end; 1077854a0d95SVineet Gupta end = (const u8 *)(fde + 1) + *fde; 1078854a0d95SVineet Gupta /* skip augmentation */ 1079854a0d95SVineet Gupta if (((const char *)(cie + 2))[1] == 'z') { 1080854a0d95SVineet Gupta uleb128_t augSize = get_uleb128(&ptr, end); 1081854a0d95SVineet Gupta 1082854a0d95SVineet Gupta if ((ptr += augSize) > end) 1083854a0d95SVineet Gupta fde = NULL; 1084854a0d95SVineet Gupta } 1085854a0d95SVineet Gupta } 1086854a0d95SVineet Gupta if (cie == NULL || fde == NULL) { 1087854a0d95SVineet Gupta #ifdef CONFIG_FRAME_POINTER 1088854a0d95SVineet Gupta unsigned long top, bottom; 1089854a0d95SVineet Gupta 1090854a0d95SVineet Gupta top = STACK_TOP_UNW(frame->task); 1091854a0d95SVineet Gupta bottom = STACK_BOTTOM_UNW(frame->task); 1092854a0d95SVineet Gupta #if FRAME_RETADDR_OFFSET < 0 1093854a0d95SVineet Gupta if (UNW_SP(frame) < top && UNW_FP(frame) <= UNW_SP(frame) 1094854a0d95SVineet Gupta && bottom < UNW_FP(frame) 1095854a0d95SVineet Gupta #else 1096854a0d95SVineet Gupta if (UNW_SP(frame) > top && UNW_FP(frame) >= UNW_SP(frame) 1097854a0d95SVineet Gupta && bottom > UNW_FP(frame) 1098854a0d95SVineet Gupta #endif 1099854a0d95SVineet Gupta && !((UNW_SP(frame) | UNW_FP(frame)) 1100854a0d95SVineet Gupta & (sizeof(unsigned long) - 1))) { 1101854a0d95SVineet Gupta unsigned long link; 1102854a0d95SVineet Gupta 1103854a0d95SVineet Gupta if (!__get_user(link, (unsigned long *) 1104854a0d95SVineet Gupta (UNW_FP(frame) + FRAME_LINK_OFFSET)) 1105854a0d95SVineet Gupta #if FRAME_RETADDR_OFFSET < 0 1106854a0d95SVineet Gupta && link > bottom && link < UNW_FP(frame) 1107854a0d95SVineet Gupta #else 1108854a0d95SVineet Gupta && link > UNW_FP(frame) && link < bottom 1109854a0d95SVineet Gupta #endif 1110854a0d95SVineet Gupta && !(link & (sizeof(link) - 1)) 1111854a0d95SVineet Gupta && !__get_user(UNW_PC(frame), 1112854a0d95SVineet Gupta (unsigned long *)(UNW_FP(frame) 1113854a0d95SVineet Gupta + FRAME_RETADDR_OFFSET))) 1114854a0d95SVineet Gupta { 1115854a0d95SVineet Gupta UNW_SP(frame) = 1116854a0d95SVineet Gupta UNW_FP(frame) + FRAME_RETADDR_OFFSET 1117854a0d95SVineet Gupta #if FRAME_RETADDR_OFFSET < 0 1118854a0d95SVineet Gupta - 1119854a0d95SVineet Gupta #else 1120854a0d95SVineet Gupta + 1121854a0d95SVineet Gupta #endif 1122854a0d95SVineet Gupta sizeof(UNW_PC(frame)); 1123854a0d95SVineet Gupta UNW_FP(frame) = link; 1124854a0d95SVineet Gupta return 0; 1125854a0d95SVineet Gupta } 1126854a0d95SVineet Gupta } 1127854a0d95SVineet Gupta #endif 1128854a0d95SVineet Gupta return -ENXIO; 1129854a0d95SVineet Gupta } 1130854a0d95SVineet Gupta state.org = startLoc; 1131854a0d95SVineet Gupta memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); 1132854a0d95SVineet Gupta 1133854a0d95SVineet Gupta unw_debug("\nProcess instructions\n"); 1134854a0d95SVineet Gupta 1135854a0d95SVineet Gupta /* process instructions 1136854a0d95SVineet Gupta * For ARC, we optimize by having blink(retAddrReg) with 1137854a0d95SVineet Gupta * the sameValue in the leaf function, so we should not check 1138854a0d95SVineet Gupta * state.regs[retAddrReg].where == Nowhere 1139854a0d95SVineet Gupta */ 1140854a0d95SVineet Gupta if (!processCFI(ptr, end, pc, ptrType, &state) 1141854a0d95SVineet Gupta || state.loc > endLoc 1142854a0d95SVineet Gupta /* || state.regs[retAddrReg].where == Nowhere */ 1143854a0d95SVineet Gupta || state.cfa.reg >= ARRAY_SIZE(reg_info) 1144854a0d95SVineet Gupta || reg_info[state.cfa.reg].width != sizeof(unsigned long) 1145854a0d95SVineet Gupta || state.cfa.offs % sizeof(unsigned long)) 1146854a0d95SVineet Gupta return -EIO; 1147854a0d95SVineet Gupta 1148854a0d95SVineet Gupta #ifdef UNWIND_DEBUG 1149854a0d95SVineet Gupta unw_debug("\n"); 1150854a0d95SVineet Gupta 1151854a0d95SVineet Gupta unw_debug("\nRegister State Based on the rules parsed from FDE:\n"); 1152854a0d95SVineet Gupta for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { 1153854a0d95SVineet Gupta 1154854a0d95SVineet Gupta if (REG_INVALID(i)) 1155854a0d95SVineet Gupta continue; 1156854a0d95SVineet Gupta 1157854a0d95SVineet Gupta switch (state.regs[i].where) { 1158854a0d95SVineet Gupta case Nowhere: 1159854a0d95SVineet Gupta break; 1160854a0d95SVineet Gupta case Memory: 1161854a0d95SVineet Gupta unw_debug(" r%d: c(%lu),", i, state.regs[i].value); 1162854a0d95SVineet Gupta break; 1163854a0d95SVineet Gupta case Register: 1164854a0d95SVineet Gupta unw_debug(" r%d: r(%lu),", i, state.regs[i].value); 1165854a0d95SVineet Gupta break; 1166854a0d95SVineet Gupta case Value: 1167854a0d95SVineet Gupta unw_debug(" r%d: v(%lu),", i, state.regs[i].value); 1168854a0d95SVineet Gupta break; 1169854a0d95SVineet Gupta } 1170854a0d95SVineet Gupta } 1171854a0d95SVineet Gupta 1172854a0d95SVineet Gupta unw_debug("\n"); 1173854a0d95SVineet Gupta #endif 1174854a0d95SVineet Gupta 1175854a0d95SVineet Gupta /* update frame */ 1176854a0d95SVineet Gupta #ifndef CONFIG_AS_CFI_SIGNAL_FRAME 1177854a0d95SVineet Gupta if (frame->call_frame 1178854a0d95SVineet Gupta && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) 1179854a0d95SVineet Gupta frame->call_frame = 0; 1180854a0d95SVineet Gupta #endif 1181854a0d95SVineet Gupta cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; 1182854a0d95SVineet Gupta startLoc = min_t(unsigned long, UNW_SP(frame), cfa); 1183854a0d95SVineet Gupta endLoc = max_t(unsigned long, UNW_SP(frame), cfa); 1184854a0d95SVineet Gupta if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { 1185854a0d95SVineet Gupta startLoc = min(STACK_LIMIT(cfa), cfa); 1186854a0d95SVineet Gupta endLoc = max(STACK_LIMIT(cfa), cfa); 1187854a0d95SVineet Gupta } 1188854a0d95SVineet Gupta 1189854a0d95SVineet Gupta unw_debug("\nCFA reg: 0x%lx, offset: 0x%lx => 0x%lx\n", 1190854a0d95SVineet Gupta state.cfa.reg, state.cfa.offs, cfa); 1191854a0d95SVineet Gupta 1192854a0d95SVineet Gupta for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { 1193854a0d95SVineet Gupta if (REG_INVALID(i)) { 1194854a0d95SVineet Gupta if (state.regs[i].where == Nowhere) 1195854a0d95SVineet Gupta continue; 1196854a0d95SVineet Gupta return -EIO; 1197854a0d95SVineet Gupta } 1198854a0d95SVineet Gupta switch (state.regs[i].where) { 1199854a0d95SVineet Gupta default: 1200854a0d95SVineet Gupta break; 1201854a0d95SVineet Gupta case Register: 1202854a0d95SVineet Gupta if (state.regs[i].value >= ARRAY_SIZE(reg_info) 1203854a0d95SVineet Gupta || REG_INVALID(state.regs[i].value) 1204854a0d95SVineet Gupta || reg_info[i].width > 1205854a0d95SVineet Gupta reg_info[state.regs[i].value].width) 1206854a0d95SVineet Gupta return -EIO; 1207854a0d95SVineet Gupta switch (reg_info[state.regs[i].value].width) { 1208854a0d95SVineet Gupta case sizeof(u8): 1209854a0d95SVineet Gupta state.regs[i].value = 1210854a0d95SVineet Gupta FRAME_REG(state.regs[i].value, const u8); 1211854a0d95SVineet Gupta break; 1212854a0d95SVineet Gupta case sizeof(u16): 1213854a0d95SVineet Gupta state.regs[i].value = 1214854a0d95SVineet Gupta FRAME_REG(state.regs[i].value, const u16); 1215854a0d95SVineet Gupta break; 1216854a0d95SVineet Gupta case sizeof(u32): 1217854a0d95SVineet Gupta state.regs[i].value = 1218854a0d95SVineet Gupta FRAME_REG(state.regs[i].value, const u32); 1219854a0d95SVineet Gupta break; 1220854a0d95SVineet Gupta #ifdef CONFIG_64BIT 1221854a0d95SVineet Gupta case sizeof(u64): 1222854a0d95SVineet Gupta state.regs[i].value = 1223854a0d95SVineet Gupta FRAME_REG(state.regs[i].value, const u64); 1224854a0d95SVineet Gupta break; 1225854a0d95SVineet Gupta #endif 1226854a0d95SVineet Gupta default: 1227854a0d95SVineet Gupta return -EIO; 1228854a0d95SVineet Gupta } 1229854a0d95SVineet Gupta break; 1230854a0d95SVineet Gupta } 1231854a0d95SVineet Gupta } 1232854a0d95SVineet Gupta 1233854a0d95SVineet Gupta unw_debug("\nRegister state after evaluation with realtime Stack:\n"); 1234854a0d95SVineet Gupta fptr = (unsigned long *)(&frame->regs); 1235854a0d95SVineet Gupta for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) { 1236854a0d95SVineet Gupta 1237854a0d95SVineet Gupta if (REG_INVALID(i)) 1238854a0d95SVineet Gupta continue; 1239854a0d95SVineet Gupta switch (state.regs[i].where) { 1240854a0d95SVineet Gupta case Nowhere: 1241854a0d95SVineet Gupta if (reg_info[i].width != sizeof(UNW_SP(frame)) 1242854a0d95SVineet Gupta || &FRAME_REG(i, __typeof__(UNW_SP(frame))) 1243854a0d95SVineet Gupta != &UNW_SP(frame)) 1244854a0d95SVineet Gupta continue; 1245854a0d95SVineet Gupta UNW_SP(frame) = cfa; 1246854a0d95SVineet Gupta break; 1247854a0d95SVineet Gupta case Register: 1248854a0d95SVineet Gupta switch (reg_info[i].width) { 1249854a0d95SVineet Gupta case sizeof(u8): 1250854a0d95SVineet Gupta FRAME_REG(i, u8) = state.regs[i].value; 1251854a0d95SVineet Gupta break; 1252854a0d95SVineet Gupta case sizeof(u16): 1253854a0d95SVineet Gupta FRAME_REG(i, u16) = state.regs[i].value; 1254854a0d95SVineet Gupta break; 1255854a0d95SVineet Gupta case sizeof(u32): 1256854a0d95SVineet Gupta FRAME_REG(i, u32) = state.regs[i].value; 1257854a0d95SVineet Gupta break; 1258854a0d95SVineet Gupta #ifdef CONFIG_64BIT 1259854a0d95SVineet Gupta case sizeof(u64): 1260854a0d95SVineet Gupta FRAME_REG(i, u64) = state.regs[i].value; 1261854a0d95SVineet Gupta break; 1262854a0d95SVineet Gupta #endif 1263854a0d95SVineet Gupta default: 1264854a0d95SVineet Gupta return -EIO; 1265854a0d95SVineet Gupta } 1266854a0d95SVineet Gupta break; 1267854a0d95SVineet Gupta case Value: 1268854a0d95SVineet Gupta if (reg_info[i].width != sizeof(unsigned long)) 1269854a0d95SVineet Gupta return -EIO; 1270854a0d95SVineet Gupta FRAME_REG(i, unsigned long) = cfa + state.regs[i].value 1271854a0d95SVineet Gupta * state.dataAlign; 1272854a0d95SVineet Gupta break; 1273854a0d95SVineet Gupta case Memory: 1274854a0d95SVineet Gupta addr = cfa + state.regs[i].value * state.dataAlign; 1275854a0d95SVineet Gupta 1276854a0d95SVineet Gupta if ((state.regs[i].value * state.dataAlign) 1277854a0d95SVineet Gupta % sizeof(unsigned long) 1278854a0d95SVineet Gupta || addr < startLoc 1279854a0d95SVineet Gupta || addr + sizeof(unsigned long) < addr 1280854a0d95SVineet Gupta || addr + sizeof(unsigned long) > endLoc) 1281854a0d95SVineet Gupta return -EIO; 1282854a0d95SVineet Gupta 1283854a0d95SVineet Gupta switch (reg_info[i].width) { 1284854a0d95SVineet Gupta case sizeof(u8): 1285854a0d95SVineet Gupta __get_user(FRAME_REG(i, u8), 1286854a0d95SVineet Gupta (u8 __user *)addr); 1287854a0d95SVineet Gupta break; 1288854a0d95SVineet Gupta case sizeof(u16): 1289854a0d95SVineet Gupta __get_user(FRAME_REG(i, u16), 1290854a0d95SVineet Gupta (u16 __user *)addr); 1291854a0d95SVineet Gupta break; 1292854a0d95SVineet Gupta case sizeof(u32): 1293854a0d95SVineet Gupta __get_user(FRAME_REG(i, u32), 1294854a0d95SVineet Gupta (u32 __user *)addr); 1295854a0d95SVineet Gupta break; 1296854a0d95SVineet Gupta #ifdef CONFIG_64BIT 1297854a0d95SVineet Gupta case sizeof(u64): 1298854a0d95SVineet Gupta __get_user(FRAME_REG(i, u64), 1299854a0d95SVineet Gupta (u64 __user *)addr); 1300854a0d95SVineet Gupta break; 1301854a0d95SVineet Gupta #endif 1302854a0d95SVineet Gupta default: 1303854a0d95SVineet Gupta return -EIO; 1304854a0d95SVineet Gupta } 1305854a0d95SVineet Gupta 1306854a0d95SVineet Gupta break; 1307854a0d95SVineet Gupta } 1308854a0d95SVineet Gupta unw_debug("r%d: 0x%lx ", i, *fptr); 1309854a0d95SVineet Gupta } 1310854a0d95SVineet Gupta 1311854a0d95SVineet Gupta return 0; 1312854a0d95SVineet Gupta #undef FRAME_REG 1313854a0d95SVineet Gupta } 1314854a0d95SVineet Gupta EXPORT_SYMBOL(arc_unwind); 1315