162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 462306a36Sopenharmony_ci * Copyright (C) 2002-2006 Novell, Inc. 562306a36Sopenharmony_ci * Jan Beulich <jbeulich@novell.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * A simple API for unwinding kernel stacks. This is used for 862306a36Sopenharmony_ci * debugging and error reporting purposes. The kernel doesn't need 962306a36Sopenharmony_ci * full-blown stack unwinding with all the bells and whistles, so there 1062306a36Sopenharmony_ci * is not much point in implementing the full Dwarf2 unwind API. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/memblock.h> 1662306a36Sopenharmony_ci#include <linux/sort.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/stop_machine.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci#include <linux/ptrace.h> 2162306a36Sopenharmony_ci#include <asm/sections.h> 2262306a36Sopenharmony_ci#include <asm/unaligned.h> 2362306a36Sopenharmony_ci#include <asm/unwind.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciextern char __start_unwind[], __end_unwind[]; 2662306a36Sopenharmony_ci/* extern const u8 __start_unwind_hdr[], __end_unwind_hdr[];*/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* #define UNWIND_DEBUG */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#ifdef UNWIND_DEBUG 3162306a36Sopenharmony_ciint dbg_unw; 3262306a36Sopenharmony_ci#define unw_debug(fmt, ...) \ 3362306a36Sopenharmony_cido { \ 3462306a36Sopenharmony_ci if (dbg_unw) \ 3562306a36Sopenharmony_ci pr_info(fmt, ##__VA_ARGS__); \ 3662306a36Sopenharmony_ci} while (0); 3762306a36Sopenharmony_ci#else 3862306a36Sopenharmony_ci#define unw_debug(fmt, ...) 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MAX_STACK_DEPTH 8 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define EXTRA_INFO(f) { \ 4462306a36Sopenharmony_ci BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \ 4562306a36Sopenharmony_ci % sizeof_field(struct unwind_frame_info, f)) \ 4662306a36Sopenharmony_ci + offsetof(struct unwind_frame_info, f) \ 4762306a36Sopenharmony_ci / sizeof_field(struct unwind_frame_info, f), \ 4862306a36Sopenharmony_ci sizeof_field(struct unwind_frame_info, f) \ 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci#define PTREGS_INFO(f) EXTRA_INFO(regs.f) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const struct { 5362306a36Sopenharmony_ci unsigned offs:BITS_PER_LONG / 2; 5462306a36Sopenharmony_ci unsigned width:BITS_PER_LONG / 2; 5562306a36Sopenharmony_ci} reg_info[] = { 5662306a36Sopenharmony_ciUNW_REGISTER_INFO}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#undef PTREGS_INFO 5962306a36Sopenharmony_ci#undef EXTRA_INFO 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#ifndef REG_INVALID 6262306a36Sopenharmony_ci#define REG_INVALID(r) (reg_info[r].width == 0) 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define DW_CFA_nop 0x00 6662306a36Sopenharmony_ci#define DW_CFA_set_loc 0x01 6762306a36Sopenharmony_ci#define DW_CFA_advance_loc1 0x02 6862306a36Sopenharmony_ci#define DW_CFA_advance_loc2 0x03 6962306a36Sopenharmony_ci#define DW_CFA_advance_loc4 0x04 7062306a36Sopenharmony_ci#define DW_CFA_offset_extended 0x05 7162306a36Sopenharmony_ci#define DW_CFA_restore_extended 0x06 7262306a36Sopenharmony_ci#define DW_CFA_undefined 0x07 7362306a36Sopenharmony_ci#define DW_CFA_same_value 0x08 7462306a36Sopenharmony_ci#define DW_CFA_register 0x09 7562306a36Sopenharmony_ci#define DW_CFA_remember_state 0x0a 7662306a36Sopenharmony_ci#define DW_CFA_restore_state 0x0b 7762306a36Sopenharmony_ci#define DW_CFA_def_cfa 0x0c 7862306a36Sopenharmony_ci#define DW_CFA_def_cfa_register 0x0d 7962306a36Sopenharmony_ci#define DW_CFA_def_cfa_offset 0x0e 8062306a36Sopenharmony_ci#define DW_CFA_def_cfa_expression 0x0f 8162306a36Sopenharmony_ci#define DW_CFA_expression 0x10 8262306a36Sopenharmony_ci#define DW_CFA_offset_extended_sf 0x11 8362306a36Sopenharmony_ci#define DW_CFA_def_cfa_sf 0x12 8462306a36Sopenharmony_ci#define DW_CFA_def_cfa_offset_sf 0x13 8562306a36Sopenharmony_ci#define DW_CFA_val_offset 0x14 8662306a36Sopenharmony_ci#define DW_CFA_val_offset_sf 0x15 8762306a36Sopenharmony_ci#define DW_CFA_val_expression 0x16 8862306a36Sopenharmony_ci#define DW_CFA_lo_user 0x1c 8962306a36Sopenharmony_ci#define DW_CFA_GNU_window_save 0x2d 9062306a36Sopenharmony_ci#define DW_CFA_GNU_args_size 0x2e 9162306a36Sopenharmony_ci#define DW_CFA_GNU_negative_offset_extended 0x2f 9262306a36Sopenharmony_ci#define DW_CFA_hi_user 0x3f 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define DW_EH_PE_FORM 0x07 9562306a36Sopenharmony_ci#define DW_EH_PE_native 0x00 9662306a36Sopenharmony_ci#define DW_EH_PE_leb128 0x01 9762306a36Sopenharmony_ci#define DW_EH_PE_data2 0x02 9862306a36Sopenharmony_ci#define DW_EH_PE_data4 0x03 9962306a36Sopenharmony_ci#define DW_EH_PE_data8 0x04 10062306a36Sopenharmony_ci#define DW_EH_PE_signed 0x08 10162306a36Sopenharmony_ci#define DW_EH_PE_ADJUST 0x70 10262306a36Sopenharmony_ci#define DW_EH_PE_abs 0x00 10362306a36Sopenharmony_ci#define DW_EH_PE_pcrel 0x10 10462306a36Sopenharmony_ci#define DW_EH_PE_textrel 0x20 10562306a36Sopenharmony_ci#define DW_EH_PE_datarel 0x30 10662306a36Sopenharmony_ci#define DW_EH_PE_funcrel 0x40 10762306a36Sopenharmony_ci#define DW_EH_PE_aligned 0x50 10862306a36Sopenharmony_ci#define DW_EH_PE_indirect 0x80 10962306a36Sopenharmony_ci#define DW_EH_PE_omit 0xff 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define CIE_ID 0 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_citypedef unsigned long uleb128_t; 11462306a36Sopenharmony_citypedef signed long sleb128_t; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct unwind_table { 11762306a36Sopenharmony_ci struct { 11862306a36Sopenharmony_ci unsigned long pc; 11962306a36Sopenharmony_ci unsigned long range; 12062306a36Sopenharmony_ci } core, init; 12162306a36Sopenharmony_ci const void *address; 12262306a36Sopenharmony_ci unsigned long size; 12362306a36Sopenharmony_ci const unsigned char *header; 12462306a36Sopenharmony_ci unsigned long hdrsz; 12562306a36Sopenharmony_ci struct unwind_table *link; 12662306a36Sopenharmony_ci const char *name; 12762306a36Sopenharmony_ci} root_table; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistruct unwind_item { 13062306a36Sopenharmony_ci enum item_location { 13162306a36Sopenharmony_ci Nowhere, 13262306a36Sopenharmony_ci Memory, 13362306a36Sopenharmony_ci Register, 13462306a36Sopenharmony_ci Value 13562306a36Sopenharmony_ci } where; 13662306a36Sopenharmony_ci uleb128_t value; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistruct unwind_state { 14062306a36Sopenharmony_ci uleb128_t loc, org; 14162306a36Sopenharmony_ci const u8 *cieStart, *cieEnd; 14262306a36Sopenharmony_ci uleb128_t codeAlign; 14362306a36Sopenharmony_ci sleb128_t dataAlign; 14462306a36Sopenharmony_ci struct cfa { 14562306a36Sopenharmony_ci uleb128_t reg, offs; 14662306a36Sopenharmony_ci } cfa; 14762306a36Sopenharmony_ci struct unwind_item regs[ARRAY_SIZE(reg_info)]; 14862306a36Sopenharmony_ci unsigned stackDepth:8; 14962306a36Sopenharmony_ci unsigned version:8; 15062306a36Sopenharmony_ci const u8 *label; 15162306a36Sopenharmony_ci const u8 *stack[MAX_STACK_DEPTH]; 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic struct unwind_table *find_table(unsigned long pc) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct unwind_table *table; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (table = &root_table; table; table = table->link) 16162306a36Sopenharmony_ci if ((pc >= table->core.pc 16262306a36Sopenharmony_ci && pc < table->core.pc + table->core.range) 16362306a36Sopenharmony_ci || (pc >= table->init.pc 16462306a36Sopenharmony_ci && pc < table->init.pc + table->init.range)) 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return table; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic unsigned long read_pointer(const u8 **pLoc, 17162306a36Sopenharmony_ci const void *end, signed ptrType); 17262306a36Sopenharmony_cistatic void init_unwind_hdr(struct unwind_table *table, 17362306a36Sopenharmony_ci void *(*alloc) (unsigned long)); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* 17662306a36Sopenharmony_ci * wrappers for header alloc (vs. calling one vs. other at call site) 17762306a36Sopenharmony_ci * to elide section mismatches warnings 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic void *__init unw_hdr_alloc_early(unsigned long sz) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci return memblock_alloc_from(sz, sizeof(unsigned int), MAX_DMA_ADDRESS); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void init_unwind_table(struct unwind_table *table, const char *name, 18562306a36Sopenharmony_ci const void *core_start, unsigned long core_size, 18662306a36Sopenharmony_ci const void *init_start, unsigned long init_size, 18762306a36Sopenharmony_ci const void *table_start, unsigned long table_size, 18862306a36Sopenharmony_ci const u8 *header_start, unsigned long header_size) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci table->core.pc = (unsigned long)core_start; 19162306a36Sopenharmony_ci table->core.range = core_size; 19262306a36Sopenharmony_ci table->init.pc = (unsigned long)init_start; 19362306a36Sopenharmony_ci table->init.range = init_size; 19462306a36Sopenharmony_ci table->address = table_start; 19562306a36Sopenharmony_ci table->size = table_size; 19662306a36Sopenharmony_ci /* To avoid the pointer addition with NULL pointer.*/ 19762306a36Sopenharmony_ci if (header_start != NULL) { 19862306a36Sopenharmony_ci const u8 *ptr = header_start + 4; 19962306a36Sopenharmony_ci const u8 *end = header_start + header_size; 20062306a36Sopenharmony_ci /* See if the linker provided table looks valid. */ 20162306a36Sopenharmony_ci if (header_size <= 4 20262306a36Sopenharmony_ci || header_start[0] != 1 20362306a36Sopenharmony_ci || (void *)read_pointer(&ptr, end, header_start[1]) 20462306a36Sopenharmony_ci != table_start 20562306a36Sopenharmony_ci || header_start[2] == DW_EH_PE_omit 20662306a36Sopenharmony_ci || read_pointer(&ptr, end, header_start[2]) <= 0 20762306a36Sopenharmony_ci || header_start[3] == DW_EH_PE_omit) 20862306a36Sopenharmony_ci header_start = NULL; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci table->hdrsz = header_size; 21162306a36Sopenharmony_ci smp_wmb(); 21262306a36Sopenharmony_ci table->header = header_start; 21362306a36Sopenharmony_ci table->link = NULL; 21462306a36Sopenharmony_ci table->name = name; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_civoid __init arc_unwind_init(void) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci init_unwind_table(&root_table, "kernel", _text, _end - _text, NULL, 0, 22062306a36Sopenharmony_ci __start_unwind, __end_unwind - __start_unwind, 22162306a36Sopenharmony_ci NULL, 0); 22262306a36Sopenharmony_ci /*__start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr);*/ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci init_unwind_hdr(&root_table, unw_hdr_alloc_early); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const u32 bad_cie, not_fde; 22862306a36Sopenharmony_cistatic const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *); 22962306a36Sopenharmony_cistatic const u32 *__cie_for_fde(const u32 *fde); 23062306a36Sopenharmony_cistatic signed fde_pointer_type(const u32 *cie); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistruct eh_frame_hdr_table_entry { 23362306a36Sopenharmony_ci unsigned long start, fde; 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci const struct eh_frame_hdr_table_entry *e1 = p1; 23962306a36Sopenharmony_ci const struct eh_frame_hdr_table_entry *e2 = p2; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return (e1->start > e2->start) - (e1->start < e2->start); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct eh_frame_hdr_table_entry *e1 = p1; 24762306a36Sopenharmony_ci struct eh_frame_hdr_table_entry *e2 = p2; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci swap(e1->start, e2->start); 25062306a36Sopenharmony_ci swap(e1->fde, e2->fde); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void init_unwind_hdr(struct unwind_table *table, 25462306a36Sopenharmony_ci void *(*alloc) (unsigned long)) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci const u8 *ptr; 25762306a36Sopenharmony_ci unsigned long tableSize = table->size, hdrSize; 25862306a36Sopenharmony_ci unsigned int n; 25962306a36Sopenharmony_ci const u32 *fde; 26062306a36Sopenharmony_ci struct { 26162306a36Sopenharmony_ci u8 version; 26262306a36Sopenharmony_ci u8 eh_frame_ptr_enc; 26362306a36Sopenharmony_ci u8 fde_count_enc; 26462306a36Sopenharmony_ci u8 table_enc; 26562306a36Sopenharmony_ci unsigned long eh_frame_ptr; 26662306a36Sopenharmony_ci unsigned int fde_count; 26762306a36Sopenharmony_ci struct eh_frame_hdr_table_entry table[]; 26862306a36Sopenharmony_ci } __attribute__ ((__packed__)) *header; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (table->header) 27162306a36Sopenharmony_ci return; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (table->hdrsz) 27462306a36Sopenharmony_ci pr_warn(".eh_frame_hdr for '%s' present but unusable\n", 27562306a36Sopenharmony_ci table->name); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (tableSize & (sizeof(*fde) - 1)) 27862306a36Sopenharmony_ci return; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (fde = table->address, n = 0; 28162306a36Sopenharmony_ci tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; 28262306a36Sopenharmony_ci tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { 28362306a36Sopenharmony_ci const u32 *cie = cie_for_fde(fde, table); 28462306a36Sopenharmony_ci signed ptrType; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (cie == ¬_fde) 28762306a36Sopenharmony_ci continue; 28862306a36Sopenharmony_ci if (cie == NULL || cie == &bad_cie) 28962306a36Sopenharmony_ci goto ret_err; 29062306a36Sopenharmony_ci ptrType = fde_pointer_type(cie); 29162306a36Sopenharmony_ci if (ptrType < 0) 29262306a36Sopenharmony_ci goto ret_err; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ptr = (const u8 *)(fde + 2); 29562306a36Sopenharmony_ci if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, 29662306a36Sopenharmony_ci ptrType)) { 29762306a36Sopenharmony_ci /* FIXME_Rajesh We have 4 instances of null addresses 29862306a36Sopenharmony_ci * instead of the initial loc addr 29962306a36Sopenharmony_ci * return; 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci WARN(1, "unwinder: FDE->initial_location NULL %p\n", 30262306a36Sopenharmony_ci (const u8 *)(fde + 1) + *fde); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci ++n; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (tableSize || !n) 30862306a36Sopenharmony_ci goto ret_err; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) 31162306a36Sopenharmony_ci + 2 * n * sizeof(unsigned long); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci header = alloc(hdrSize); 31462306a36Sopenharmony_ci if (!header) 31562306a36Sopenharmony_ci goto ret_err; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci header->version = 1; 31862306a36Sopenharmony_ci header->eh_frame_ptr_enc = DW_EH_PE_abs | DW_EH_PE_native; 31962306a36Sopenharmony_ci header->fde_count_enc = DW_EH_PE_abs | DW_EH_PE_data4; 32062306a36Sopenharmony_ci header->table_enc = DW_EH_PE_abs | DW_EH_PE_native; 32162306a36Sopenharmony_ci put_unaligned((unsigned long)table->address, &header->eh_frame_ptr); 32262306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(typeof(*header), fde_count) 32362306a36Sopenharmony_ci % __alignof(typeof(header->fde_count))); 32462306a36Sopenharmony_ci header->fde_count = n; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(typeof(*header), table) 32762306a36Sopenharmony_ci % __alignof(typeof(*header->table))); 32862306a36Sopenharmony_ci for (fde = table->address, tableSize = table->size, n = 0; 32962306a36Sopenharmony_ci tableSize; 33062306a36Sopenharmony_ci tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { 33162306a36Sopenharmony_ci const u32 *cie = __cie_for_fde(fde); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (fde[1] == CIE_ID) 33462306a36Sopenharmony_ci continue; /* this is a CIE */ 33562306a36Sopenharmony_ci ptr = (const u8 *)(fde + 2); 33662306a36Sopenharmony_ci header->table[n].start = read_pointer(&ptr, 33762306a36Sopenharmony_ci (const u8 *)(fde + 1) + 33862306a36Sopenharmony_ci *fde, 33962306a36Sopenharmony_ci fde_pointer_type(cie)); 34062306a36Sopenharmony_ci header->table[n].fde = (unsigned long)fde; 34162306a36Sopenharmony_ci ++n; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci WARN_ON(n != header->fde_count); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci sort(header->table, 34662306a36Sopenharmony_ci n, 34762306a36Sopenharmony_ci sizeof(*header->table), 34862306a36Sopenharmony_ci cmp_eh_frame_hdr_table_entries, swap_eh_frame_hdr_table_entries); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci table->hdrsz = hdrSize; 35162306a36Sopenharmony_ci smp_wmb(); 35262306a36Sopenharmony_ci table->header = (const void *)header; 35362306a36Sopenharmony_ci return; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciret_err: 35662306a36Sopenharmony_ci panic("Attention !!! Dwarf FDE parsing errors\n"); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci#ifdef CONFIG_MODULES 36062306a36Sopenharmony_cistatic void *unw_hdr_alloc(unsigned long sz) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci return kmalloc(sz, GFP_KERNEL); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic struct unwind_table *last_table; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* Must be called with module_mutex held. */ 36862306a36Sopenharmony_civoid *unwind_add_table(struct module *module, const void *table_start, 36962306a36Sopenharmony_ci unsigned long table_size) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct unwind_table *table; 37262306a36Sopenharmony_ci struct module_memory *core_text; 37362306a36Sopenharmony_ci struct module_memory *init_text; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (table_size <= 0) 37662306a36Sopenharmony_ci return NULL; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci table = kmalloc(sizeof(*table), GFP_KERNEL); 37962306a36Sopenharmony_ci if (!table) 38062306a36Sopenharmony_ci return NULL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci core_text = &module->mem[MOD_TEXT]; 38362306a36Sopenharmony_ci init_text = &module->mem[MOD_INIT_TEXT]; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci init_unwind_table(table, module->name, core_text->base, core_text->size, 38662306a36Sopenharmony_ci init_text->base, init_text->size, table_start, table_size, NULL, 0); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci init_unwind_hdr(table, unw_hdr_alloc); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci#ifdef UNWIND_DEBUG 39162306a36Sopenharmony_ci unw_debug("Table added for [%s] %lx %lx\n", 39262306a36Sopenharmony_ci module->name, table->core.pc, table->core.range); 39362306a36Sopenharmony_ci#endif 39462306a36Sopenharmony_ci if (last_table) 39562306a36Sopenharmony_ci last_table->link = table; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci root_table.link = table; 39862306a36Sopenharmony_ci last_table = table; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return table; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistruct unlink_table_info { 40462306a36Sopenharmony_ci struct unwind_table *table; 40562306a36Sopenharmony_ci int init_only; 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int unlink_table(void *arg) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct unlink_table_info *info = arg; 41162306a36Sopenharmony_ci struct unwind_table *table = info->table, *prev; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (prev = &root_table; prev->link && prev->link != table; 41462306a36Sopenharmony_ci prev = prev->link) 41562306a36Sopenharmony_ci ; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (prev->link) { 41862306a36Sopenharmony_ci if (info->init_only) { 41962306a36Sopenharmony_ci table->init.pc = 0; 42062306a36Sopenharmony_ci table->init.range = 0; 42162306a36Sopenharmony_ci info->table = NULL; 42262306a36Sopenharmony_ci } else { 42362306a36Sopenharmony_ci prev->link = table->link; 42462306a36Sopenharmony_ci if (!prev->link) 42562306a36Sopenharmony_ci last_table = prev; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } else 42862306a36Sopenharmony_ci info->table = NULL; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* Must be called with module_mutex held. */ 43462306a36Sopenharmony_civoid unwind_remove_table(void *handle, int init_only) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct unwind_table *table = handle; 43762306a36Sopenharmony_ci struct unlink_table_info info; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!table || table == &root_table) 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (init_only && table == last_table) { 44362306a36Sopenharmony_ci table->init.pc = 0; 44462306a36Sopenharmony_ci table->init.range = 0; 44562306a36Sopenharmony_ci return; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci info.table = table; 44962306a36Sopenharmony_ci info.init_only = init_only; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci unlink_table(&info); /* XXX: SMP */ 45262306a36Sopenharmony_ci kfree(table->header); 45362306a36Sopenharmony_ci kfree(table); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci#endif /* CONFIG_MODULES */ 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic uleb128_t get_uleb128(const u8 **pcur, const u8 *end) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci const u8 *cur = *pcur; 46162306a36Sopenharmony_ci uleb128_t value; 46262306a36Sopenharmony_ci unsigned int shift; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci for (shift = 0, value = 0; cur < end; shift += 7) { 46562306a36Sopenharmony_ci if (shift + 7 > 8 * sizeof(value) 46662306a36Sopenharmony_ci && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { 46762306a36Sopenharmony_ci cur = end + 1; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci value |= (uleb128_t) (*cur & 0x7f) << shift; 47162306a36Sopenharmony_ci if (!(*cur++ & 0x80)) 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci *pcur = cur; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return value; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic sleb128_t get_sleb128(const u8 **pcur, const u8 *end) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci const u8 *cur = *pcur; 48262306a36Sopenharmony_ci sleb128_t value; 48362306a36Sopenharmony_ci unsigned int shift; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (shift = 0, value = 0; cur < end; shift += 7) { 48662306a36Sopenharmony_ci if (shift + 7 > 8 * sizeof(value) 48762306a36Sopenharmony_ci && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { 48862306a36Sopenharmony_ci cur = end + 1; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci value |= (sleb128_t) (*cur & 0x7f) << shift; 49262306a36Sopenharmony_ci if (!(*cur & 0x80)) { 49362306a36Sopenharmony_ci value |= -(*cur++ & 0x40) << shift; 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci *pcur = cur; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return value; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic const u32 *__cie_for_fde(const u32 *fde) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci const u32 *cie; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci cie = fde + 1 - fde[1] / sizeof(*fde); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return cie; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci const u32 *cie; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (!*fde || (*fde & (sizeof(*fde) - 1))) 51662306a36Sopenharmony_ci return &bad_cie; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (fde[1] == CIE_ID) 51962306a36Sopenharmony_ci return ¬_fde; /* this is a CIE */ 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if ((fde[1] & (sizeof(*fde) - 1))) 52262306a36Sopenharmony_ci/* || fde[1] > (unsigned long)(fde + 1) - (unsigned long)table->address) */ 52362306a36Sopenharmony_ci return NULL; /* this is not a valid FDE */ 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci cie = __cie_for_fde(fde); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) 52862306a36Sopenharmony_ci || (*cie & (sizeof(*cie) - 1)) 52962306a36Sopenharmony_ci || (cie[1] != CIE_ID)) 53062306a36Sopenharmony_ci return NULL; /* this is not a (valid) CIE */ 53162306a36Sopenharmony_ci return cie; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic unsigned long read_pointer(const u8 **pLoc, const void *end, 53562306a36Sopenharmony_ci signed ptrType) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci unsigned long value = 0; 53862306a36Sopenharmony_ci union { 53962306a36Sopenharmony_ci const u8 *p8; 54062306a36Sopenharmony_ci const u16 *p16u; 54162306a36Sopenharmony_ci const s16 *p16s; 54262306a36Sopenharmony_ci const u32 *p32u; 54362306a36Sopenharmony_ci const s32 *p32s; 54462306a36Sopenharmony_ci const unsigned long *pul; 54562306a36Sopenharmony_ci } ptr; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (ptrType < 0 || ptrType == DW_EH_PE_omit) 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci ptr.p8 = *pLoc; 55062306a36Sopenharmony_ci switch (ptrType & DW_EH_PE_FORM) { 55162306a36Sopenharmony_ci case DW_EH_PE_data2: 55262306a36Sopenharmony_ci if (end < (const void *)(ptr.p16u + 1)) 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci if (ptrType & DW_EH_PE_signed) 55562306a36Sopenharmony_ci value = get_unaligned((u16 *) ptr.p16s++); 55662306a36Sopenharmony_ci else 55762306a36Sopenharmony_ci value = get_unaligned((u16 *) ptr.p16u++); 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case DW_EH_PE_data4: 56062306a36Sopenharmony_ci#ifdef CONFIG_64BIT 56162306a36Sopenharmony_ci if (end < (const void *)(ptr.p32u + 1)) 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci if (ptrType & DW_EH_PE_signed) 56462306a36Sopenharmony_ci value = get_unaligned(ptr.p32s++); 56562306a36Sopenharmony_ci else 56662306a36Sopenharmony_ci value = get_unaligned(ptr.p32u++); 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci case DW_EH_PE_data8: 56962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(u64) != sizeof(value)); 57062306a36Sopenharmony_ci#else 57162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(u32) != sizeof(value)); 57262306a36Sopenharmony_ci#endif 57362306a36Sopenharmony_ci fallthrough; 57462306a36Sopenharmony_ci case DW_EH_PE_native: 57562306a36Sopenharmony_ci if (end < (const void *)(ptr.pul + 1)) 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci value = get_unaligned((unsigned long *)ptr.pul++); 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case DW_EH_PE_leb128: 58062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); 58162306a36Sopenharmony_ci value = ptrType & DW_EH_PE_signed ? get_sleb128(&ptr.p8, end) 58262306a36Sopenharmony_ci : get_uleb128(&ptr.p8, end); 58362306a36Sopenharmony_ci if ((const void *)ptr.p8 > end) 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci default: 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci switch (ptrType & DW_EH_PE_ADJUST) { 59062306a36Sopenharmony_ci case DW_EH_PE_abs: 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci case DW_EH_PE_pcrel: 59362306a36Sopenharmony_ci value += (unsigned long)*pLoc; 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci default: 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci if ((ptrType & DW_EH_PE_indirect) 59962306a36Sopenharmony_ci && __get_user(value, (unsigned long __user *)value)) 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci *pLoc = ptr.p8; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return value; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic signed fde_pointer_type(const u32 *cie) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci const u8 *ptr = (const u8 *)(cie + 2); 60962306a36Sopenharmony_ci unsigned int version = *ptr; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (*++ptr) { 61262306a36Sopenharmony_ci const char *aug; 61362306a36Sopenharmony_ci const u8 *end = (const u8 *)(cie + 1) + *cie; 61462306a36Sopenharmony_ci uleb128_t len; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* check if augmentation size is first (and thus present) */ 61762306a36Sopenharmony_ci if (*ptr != 'z') 61862306a36Sopenharmony_ci return -1; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* check if augmentation string is nul-terminated */ 62162306a36Sopenharmony_ci aug = (const void *)ptr; 62262306a36Sopenharmony_ci ptr = memchr(aug, 0, end - ptr); 62362306a36Sopenharmony_ci if (ptr == NULL) 62462306a36Sopenharmony_ci return -1; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci ++ptr; /* skip terminator */ 62762306a36Sopenharmony_ci get_uleb128(&ptr, end); /* skip code alignment */ 62862306a36Sopenharmony_ci get_sleb128(&ptr, end); /* skip data alignment */ 62962306a36Sopenharmony_ci /* skip return address column */ 63062306a36Sopenharmony_ci version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end); 63162306a36Sopenharmony_ci len = get_uleb128(&ptr, end); /* augmentation length */ 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (ptr + len < ptr || ptr + len > end) 63462306a36Sopenharmony_ci return -1; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci end = ptr + len; 63762306a36Sopenharmony_ci while (*++aug) { 63862306a36Sopenharmony_ci if (ptr >= end) 63962306a36Sopenharmony_ci return -1; 64062306a36Sopenharmony_ci switch (*aug) { 64162306a36Sopenharmony_ci case 'L': 64262306a36Sopenharmony_ci ++ptr; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case 'P':{ 64562306a36Sopenharmony_ci signed ptrType = *ptr++; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (!read_pointer(&ptr, end, ptrType) 64862306a36Sopenharmony_ci || ptr > end) 64962306a36Sopenharmony_ci return -1; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case 'R': 65362306a36Sopenharmony_ci return *ptr; 65462306a36Sopenharmony_ci default: 65562306a36Sopenharmony_ci return -1; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci return DW_EH_PE_native | DW_EH_PE_abs; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int advance_loc(unsigned long delta, struct unwind_state *state) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci state->loc += delta * state->codeAlign; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* FIXME_Rajesh: Probably we are defining for the initial range as well; 66762306a36Sopenharmony_ci return delta > 0; 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci unw_debug("delta %3lu => loc 0x%lx: ", delta, state->loc); 67062306a36Sopenharmony_ci return 1; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void set_rule(uleb128_t reg, enum item_location where, uleb128_t value, 67462306a36Sopenharmony_ci struct unwind_state *state) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci if (reg < ARRAY_SIZE(state->regs)) { 67762306a36Sopenharmony_ci state->regs[reg].where = where; 67862306a36Sopenharmony_ci state->regs[reg].value = value; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci#ifdef UNWIND_DEBUG 68162306a36Sopenharmony_ci unw_debug("r%lu: ", reg); 68262306a36Sopenharmony_ci switch (where) { 68362306a36Sopenharmony_ci case Nowhere: 68462306a36Sopenharmony_ci unw_debug("s "); 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci case Memory: 68762306a36Sopenharmony_ci unw_debug("c(%lu) ", value); 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci case Register: 69062306a36Sopenharmony_ci unw_debug("r(%lu) ", value); 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci case Value: 69362306a36Sopenharmony_ci unw_debug("v(%lu) ", value); 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci default: 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci#endif 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, 70362306a36Sopenharmony_ci signed ptrType, struct unwind_state *state) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci union { 70662306a36Sopenharmony_ci const u8 *p8; 70762306a36Sopenharmony_ci const u16 *p16; 70862306a36Sopenharmony_ci const u32 *p32; 70962306a36Sopenharmony_ci } ptr; 71062306a36Sopenharmony_ci int result = 1; 71162306a36Sopenharmony_ci u8 opcode; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (start != state->cieStart) { 71462306a36Sopenharmony_ci state->loc = state->org; 71562306a36Sopenharmony_ci result = 71662306a36Sopenharmony_ci processCFI(state->cieStart, state->cieEnd, 0, ptrType, 71762306a36Sopenharmony_ci state); 71862306a36Sopenharmony_ci if (targetLoc == 0 && state->label == NULL) 71962306a36Sopenharmony_ci return result; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci for (ptr.p8 = start; result && ptr.p8 < end;) { 72262306a36Sopenharmony_ci switch (*ptr.p8 >> 6) { 72362306a36Sopenharmony_ci uleb128_t value; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci case 0: 72662306a36Sopenharmony_ci opcode = *ptr.p8++; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci switch (opcode) { 72962306a36Sopenharmony_ci case DW_CFA_nop: 73062306a36Sopenharmony_ci unw_debug("cfa nop "); 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case DW_CFA_set_loc: 73362306a36Sopenharmony_ci state->loc = read_pointer(&ptr.p8, end, 73462306a36Sopenharmony_ci ptrType); 73562306a36Sopenharmony_ci if (state->loc == 0) 73662306a36Sopenharmony_ci result = 0; 73762306a36Sopenharmony_ci unw_debug("cfa_set_loc: 0x%lx ", state->loc); 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci case DW_CFA_advance_loc1: 74062306a36Sopenharmony_ci unw_debug("\ncfa advance loc1:"); 74162306a36Sopenharmony_ci result = ptr.p8 < end 74262306a36Sopenharmony_ci && advance_loc(*ptr.p8++, state); 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case DW_CFA_advance_loc2: 74562306a36Sopenharmony_ci value = *ptr.p8++; 74662306a36Sopenharmony_ci value += *ptr.p8++ << 8; 74762306a36Sopenharmony_ci unw_debug("\ncfa advance loc2:"); 74862306a36Sopenharmony_ci result = ptr.p8 <= end + 2 74962306a36Sopenharmony_ci /* && advance_loc(*ptr.p16++, state); */ 75062306a36Sopenharmony_ci && advance_loc(value, state); 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci case DW_CFA_advance_loc4: 75362306a36Sopenharmony_ci unw_debug("\ncfa advance loc4:"); 75462306a36Sopenharmony_ci result = ptr.p8 <= end + 4 75562306a36Sopenharmony_ci && advance_loc(*ptr.p32++, state); 75662306a36Sopenharmony_ci break; 75762306a36Sopenharmony_ci case DW_CFA_offset_extended: 75862306a36Sopenharmony_ci value = get_uleb128(&ptr.p8, end); 75962306a36Sopenharmony_ci unw_debug("cfa_offset_extended: "); 76062306a36Sopenharmony_ci set_rule(value, Memory, 76162306a36Sopenharmony_ci get_uleb128(&ptr.p8, end), state); 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case DW_CFA_val_offset: 76462306a36Sopenharmony_ci value = get_uleb128(&ptr.p8, end); 76562306a36Sopenharmony_ci set_rule(value, Value, 76662306a36Sopenharmony_ci get_uleb128(&ptr.p8, end), state); 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci case DW_CFA_offset_extended_sf: 76962306a36Sopenharmony_ci value = get_uleb128(&ptr.p8, end); 77062306a36Sopenharmony_ci set_rule(value, Memory, 77162306a36Sopenharmony_ci get_sleb128(&ptr.p8, end), state); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci case DW_CFA_val_offset_sf: 77462306a36Sopenharmony_ci value = get_uleb128(&ptr.p8, end); 77562306a36Sopenharmony_ci set_rule(value, Value, 77662306a36Sopenharmony_ci get_sleb128(&ptr.p8, end), state); 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci case DW_CFA_restore_extended: 77962306a36Sopenharmony_ci unw_debug("cfa_restore_extended: "); 78062306a36Sopenharmony_ci case DW_CFA_undefined: 78162306a36Sopenharmony_ci unw_debug("cfa_undefined: "); 78262306a36Sopenharmony_ci case DW_CFA_same_value: 78362306a36Sopenharmony_ci unw_debug("cfa_same_value: "); 78462306a36Sopenharmony_ci set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, 78562306a36Sopenharmony_ci state); 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci case DW_CFA_register: 78862306a36Sopenharmony_ci unw_debug("cfa_register: "); 78962306a36Sopenharmony_ci value = get_uleb128(&ptr.p8, end); 79062306a36Sopenharmony_ci set_rule(value, 79162306a36Sopenharmony_ci Register, 79262306a36Sopenharmony_ci get_uleb128(&ptr.p8, end), state); 79362306a36Sopenharmony_ci break; 79462306a36Sopenharmony_ci case DW_CFA_remember_state: 79562306a36Sopenharmony_ci unw_debug("cfa_remember_state: "); 79662306a36Sopenharmony_ci if (ptr.p8 == state->label) { 79762306a36Sopenharmony_ci state->label = NULL; 79862306a36Sopenharmony_ci return 1; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci if (state->stackDepth >= MAX_STACK_DEPTH) 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci state->stack[state->stackDepth++] = ptr.p8; 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci case DW_CFA_restore_state: 80562306a36Sopenharmony_ci unw_debug("cfa_restore_state: "); 80662306a36Sopenharmony_ci if (state->stackDepth) { 80762306a36Sopenharmony_ci const uleb128_t loc = state->loc; 80862306a36Sopenharmony_ci const u8 *label = state->label; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci state->label = 81162306a36Sopenharmony_ci state->stack[state->stackDepth - 1]; 81262306a36Sopenharmony_ci memcpy(&state->cfa, &badCFA, 81362306a36Sopenharmony_ci sizeof(state->cfa)); 81462306a36Sopenharmony_ci memset(state->regs, 0, 81562306a36Sopenharmony_ci sizeof(state->regs)); 81662306a36Sopenharmony_ci state->stackDepth = 0; 81762306a36Sopenharmony_ci result = 81862306a36Sopenharmony_ci processCFI(start, end, 0, ptrType, 81962306a36Sopenharmony_ci state); 82062306a36Sopenharmony_ci state->loc = loc; 82162306a36Sopenharmony_ci state->label = label; 82262306a36Sopenharmony_ci } else 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci case DW_CFA_def_cfa: 82662306a36Sopenharmony_ci state->cfa.reg = get_uleb128(&ptr.p8, end); 82762306a36Sopenharmony_ci unw_debug("cfa_def_cfa: r%lu ", state->cfa.reg); 82862306a36Sopenharmony_ci fallthrough; 82962306a36Sopenharmony_ci case DW_CFA_def_cfa_offset: 83062306a36Sopenharmony_ci state->cfa.offs = get_uleb128(&ptr.p8, end); 83162306a36Sopenharmony_ci unw_debug("cfa_def_cfa_offset: 0x%lx ", 83262306a36Sopenharmony_ci state->cfa.offs); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci case DW_CFA_def_cfa_sf: 83562306a36Sopenharmony_ci state->cfa.reg = get_uleb128(&ptr.p8, end); 83662306a36Sopenharmony_ci fallthrough; 83762306a36Sopenharmony_ci case DW_CFA_def_cfa_offset_sf: 83862306a36Sopenharmony_ci state->cfa.offs = get_sleb128(&ptr.p8, end) 83962306a36Sopenharmony_ci * state->dataAlign; 84062306a36Sopenharmony_ci break; 84162306a36Sopenharmony_ci case DW_CFA_def_cfa_register: 84262306a36Sopenharmony_ci unw_debug("cfa_def_cfa_register: "); 84362306a36Sopenharmony_ci state->cfa.reg = get_uleb128(&ptr.p8, end); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci /*todo case DW_CFA_def_cfa_expression: */ 84662306a36Sopenharmony_ci /*todo case DW_CFA_expression: */ 84762306a36Sopenharmony_ci /*todo case DW_CFA_val_expression: */ 84862306a36Sopenharmony_ci case DW_CFA_GNU_args_size: 84962306a36Sopenharmony_ci get_uleb128(&ptr.p8, end); 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case DW_CFA_GNU_negative_offset_extended: 85262306a36Sopenharmony_ci value = get_uleb128(&ptr.p8, end); 85362306a36Sopenharmony_ci set_rule(value, 85462306a36Sopenharmony_ci Memory, 85562306a36Sopenharmony_ci (uleb128_t) 0 - get_uleb128(&ptr.p8, 85662306a36Sopenharmony_ci end), 85762306a36Sopenharmony_ci state); 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci case DW_CFA_GNU_window_save: 86062306a36Sopenharmony_ci default: 86162306a36Sopenharmony_ci unw_debug("UNKNOWN OPCODE 0x%x\n", opcode); 86262306a36Sopenharmony_ci result = 0; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci case 1: 86762306a36Sopenharmony_ci unw_debug("\ncfa_adv_loc: "); 86862306a36Sopenharmony_ci result = advance_loc(*ptr.p8++ & 0x3f, state); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case 2: 87162306a36Sopenharmony_ci unw_debug("cfa_offset: "); 87262306a36Sopenharmony_ci value = *ptr.p8++ & 0x3f; 87362306a36Sopenharmony_ci set_rule(value, Memory, get_uleb128(&ptr.p8, end), 87462306a36Sopenharmony_ci state); 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci case 3: 87762306a36Sopenharmony_ci unw_debug("cfa_restore: "); 87862306a36Sopenharmony_ci set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (ptr.p8 > end) 88362306a36Sopenharmony_ci result = 0; 88462306a36Sopenharmony_ci if (result && targetLoc != 0 && targetLoc < state->loc) 88562306a36Sopenharmony_ci return 1; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return result && ptr.p8 == end && (targetLoc == 0 || ( 88962306a36Sopenharmony_ci /*todo While in theory this should apply, gcc in practice omits 89062306a36Sopenharmony_ci everything past the function prolog, and hence the location 89162306a36Sopenharmony_ci never reaches the end of the function. 89262306a36Sopenharmony_ci targetLoc < state->loc && */ state->label == NULL)); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci/* Unwind to previous to frame. Returns 0 if successful, negative 89662306a36Sopenharmony_ci * number in case of an error. */ 89762306a36Sopenharmony_ciint arc_unwind(struct unwind_frame_info *frame) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) 90062306a36Sopenharmony_ci const u32 *fde = NULL, *cie = NULL; 90162306a36Sopenharmony_ci const u8 *ptr = NULL, *end = NULL; 90262306a36Sopenharmony_ci unsigned long pc = UNW_PC(frame) - frame->call_frame; 90362306a36Sopenharmony_ci unsigned long startLoc = 0, endLoc = 0, cfa; 90462306a36Sopenharmony_ci unsigned int i; 90562306a36Sopenharmony_ci signed ptrType = -1; 90662306a36Sopenharmony_ci uleb128_t retAddrReg = 0; 90762306a36Sopenharmony_ci const struct unwind_table *table; 90862306a36Sopenharmony_ci struct unwind_state state; 90962306a36Sopenharmony_ci unsigned long *fptr; 91062306a36Sopenharmony_ci unsigned long addr; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci unw_debug("\n\nUNWIND FRAME:\n"); 91362306a36Sopenharmony_ci unw_debug("PC: 0x%lx BLINK: 0x%lx, SP: 0x%lx, FP: 0x%x\n", 91462306a36Sopenharmony_ci UNW_PC(frame), UNW_BLINK(frame), UNW_SP(frame), 91562306a36Sopenharmony_ci UNW_FP(frame)); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (UNW_PC(frame) == 0) 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci#ifdef UNWIND_DEBUG 92162306a36Sopenharmony_ci { 92262306a36Sopenharmony_ci unsigned long *sptr = (unsigned long *)UNW_SP(frame); 92362306a36Sopenharmony_ci unw_debug("\nStack Dump:\n"); 92462306a36Sopenharmony_ci for (i = 0; i < 20; i++, sptr++) 92562306a36Sopenharmony_ci unw_debug("0x%p: 0x%lx\n", sptr, *sptr); 92662306a36Sopenharmony_ci unw_debug("\n"); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci#endif 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci table = find_table(pc); 93162306a36Sopenharmony_ci if (table != NULL 93262306a36Sopenharmony_ci && !(table->size & (sizeof(*fde) - 1))) { 93362306a36Sopenharmony_ci const u8 *hdr = table->header; 93462306a36Sopenharmony_ci unsigned long tableSize; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci smp_rmb(); 93762306a36Sopenharmony_ci if (hdr && hdr[0] == 1) { 93862306a36Sopenharmony_ci switch (hdr[3] & DW_EH_PE_FORM) { 93962306a36Sopenharmony_ci case DW_EH_PE_native: 94062306a36Sopenharmony_ci tableSize = sizeof(unsigned long); 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci case DW_EH_PE_data2: 94362306a36Sopenharmony_ci tableSize = 2; 94462306a36Sopenharmony_ci break; 94562306a36Sopenharmony_ci case DW_EH_PE_data4: 94662306a36Sopenharmony_ci tableSize = 4; 94762306a36Sopenharmony_ci break; 94862306a36Sopenharmony_ci case DW_EH_PE_data8: 94962306a36Sopenharmony_ci tableSize = 8; 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci default: 95262306a36Sopenharmony_ci tableSize = 0; 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci ptr = hdr + 4; 95662306a36Sopenharmony_ci end = hdr + table->hdrsz; 95762306a36Sopenharmony_ci if (tableSize && read_pointer(&ptr, end, hdr[1]) 95862306a36Sopenharmony_ci == (unsigned long)table->address 95962306a36Sopenharmony_ci && (i = read_pointer(&ptr, end, hdr[2])) > 0 96062306a36Sopenharmony_ci && i == (end - ptr) / (2 * tableSize) 96162306a36Sopenharmony_ci && !((end - ptr) % (2 * tableSize))) { 96262306a36Sopenharmony_ci do { 96362306a36Sopenharmony_ci const u8 *cur = 96462306a36Sopenharmony_ci ptr + (i / 2) * (2 * tableSize); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci startLoc = read_pointer(&cur, 96762306a36Sopenharmony_ci cur + tableSize, 96862306a36Sopenharmony_ci hdr[3]); 96962306a36Sopenharmony_ci if (pc < startLoc) 97062306a36Sopenharmony_ci i /= 2; 97162306a36Sopenharmony_ci else { 97262306a36Sopenharmony_ci ptr = cur - tableSize; 97362306a36Sopenharmony_ci i = (i + 1) / 2; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci } while (startLoc && i > 1); 97662306a36Sopenharmony_ci if (i == 1 97762306a36Sopenharmony_ci && (startLoc = read_pointer(&ptr, 97862306a36Sopenharmony_ci ptr + tableSize, 97962306a36Sopenharmony_ci hdr[3])) != 0 98062306a36Sopenharmony_ci && pc >= startLoc) 98162306a36Sopenharmony_ci fde = (void *)read_pointer(&ptr, 98262306a36Sopenharmony_ci ptr + 98362306a36Sopenharmony_ci tableSize, 98462306a36Sopenharmony_ci hdr[3]); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (fde != NULL) { 98962306a36Sopenharmony_ci cie = cie_for_fde(fde, table); 99062306a36Sopenharmony_ci ptr = (const u8 *)(fde + 2); 99162306a36Sopenharmony_ci if (cie != NULL 99262306a36Sopenharmony_ci && cie != &bad_cie 99362306a36Sopenharmony_ci && cie != ¬_fde 99462306a36Sopenharmony_ci && (ptrType = fde_pointer_type(cie)) >= 0 99562306a36Sopenharmony_ci && read_pointer(&ptr, 99662306a36Sopenharmony_ci (const u8 *)(fde + 1) + *fde, 99762306a36Sopenharmony_ci ptrType) == startLoc) { 99862306a36Sopenharmony_ci if (!(ptrType & DW_EH_PE_indirect)) 99962306a36Sopenharmony_ci ptrType &= 100062306a36Sopenharmony_ci DW_EH_PE_FORM | DW_EH_PE_signed; 100162306a36Sopenharmony_ci endLoc = 100262306a36Sopenharmony_ci startLoc + read_pointer(&ptr, 100362306a36Sopenharmony_ci (const u8 *)(fde + 100462306a36Sopenharmony_ci 1) + 100562306a36Sopenharmony_ci *fde, ptrType); 100662306a36Sopenharmony_ci if (pc >= endLoc) { 100762306a36Sopenharmony_ci fde = NULL; 100862306a36Sopenharmony_ci cie = NULL; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci } else { 101162306a36Sopenharmony_ci fde = NULL; 101262306a36Sopenharmony_ci cie = NULL; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci if (cie != NULL) { 101762306a36Sopenharmony_ci memset(&state, 0, sizeof(state)); 101862306a36Sopenharmony_ci state.cieEnd = ptr; /* keep here temporarily */ 101962306a36Sopenharmony_ci ptr = (const u8 *)(cie + 2); 102062306a36Sopenharmony_ci end = (const u8 *)(cie + 1) + *cie; 102162306a36Sopenharmony_ci frame->call_frame = 1; 102262306a36Sopenharmony_ci if (*++ptr) { 102362306a36Sopenharmony_ci /* check if augmentation size is first (thus present) */ 102462306a36Sopenharmony_ci if (*ptr == 'z') { 102562306a36Sopenharmony_ci while (++ptr < end && *ptr) { 102662306a36Sopenharmony_ci switch (*ptr) { 102762306a36Sopenharmony_ci /* chk for ignorable or already handled 102862306a36Sopenharmony_ci * nul-terminated augmentation string */ 102962306a36Sopenharmony_ci case 'L': 103062306a36Sopenharmony_ci case 'P': 103162306a36Sopenharmony_ci case 'R': 103262306a36Sopenharmony_ci continue; 103362306a36Sopenharmony_ci case 'S': 103462306a36Sopenharmony_ci frame->call_frame = 0; 103562306a36Sopenharmony_ci continue; 103662306a36Sopenharmony_ci default: 103762306a36Sopenharmony_ci break; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci if (ptr >= end || *ptr) 104362306a36Sopenharmony_ci cie = NULL; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci ++ptr; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci if (cie != NULL) { 104862306a36Sopenharmony_ci /* get code alignment factor */ 104962306a36Sopenharmony_ci state.codeAlign = get_uleb128(&ptr, end); 105062306a36Sopenharmony_ci /* get data alignment factor */ 105162306a36Sopenharmony_ci state.dataAlign = get_sleb128(&ptr, end); 105262306a36Sopenharmony_ci if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) 105362306a36Sopenharmony_ci cie = NULL; 105462306a36Sopenharmony_ci else { 105562306a36Sopenharmony_ci retAddrReg = 105662306a36Sopenharmony_ci state.version <= 1 ? *ptr++ : get_uleb128(&ptr, 105762306a36Sopenharmony_ci end); 105862306a36Sopenharmony_ci unw_debug("CIE Frame Info:\n"); 105962306a36Sopenharmony_ci unw_debug("return Address register 0x%lx\n", 106062306a36Sopenharmony_ci retAddrReg); 106162306a36Sopenharmony_ci unw_debug("data Align: %ld\n", state.dataAlign); 106262306a36Sopenharmony_ci unw_debug("code Align: %lu\n", state.codeAlign); 106362306a36Sopenharmony_ci /* skip augmentation */ 106462306a36Sopenharmony_ci if (((const char *)(cie + 2))[1] == 'z') { 106562306a36Sopenharmony_ci uleb128_t augSize = get_uleb128(&ptr, end); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci ptr += augSize; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info) 107062306a36Sopenharmony_ci || REG_INVALID(retAddrReg) 107162306a36Sopenharmony_ci || reg_info[retAddrReg].width != 107262306a36Sopenharmony_ci sizeof(unsigned long)) 107362306a36Sopenharmony_ci cie = NULL; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci if (cie != NULL) { 107762306a36Sopenharmony_ci state.cieStart = ptr; 107862306a36Sopenharmony_ci ptr = state.cieEnd; 107962306a36Sopenharmony_ci state.cieEnd = end; 108062306a36Sopenharmony_ci end = (const u8 *)(fde + 1) + *fde; 108162306a36Sopenharmony_ci /* skip augmentation */ 108262306a36Sopenharmony_ci if (((const char *)(cie + 2))[1] == 'z') { 108362306a36Sopenharmony_ci uleb128_t augSize = get_uleb128(&ptr, end); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if ((ptr += augSize) > end) 108662306a36Sopenharmony_ci fde = NULL; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci if (cie == NULL || fde == NULL) { 109062306a36Sopenharmony_ci#ifdef CONFIG_FRAME_POINTER 109162306a36Sopenharmony_ci unsigned long top, bottom; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci top = STACK_TOP_UNW(frame->task); 109462306a36Sopenharmony_ci bottom = STACK_BOTTOM_UNW(frame->task); 109562306a36Sopenharmony_ci#if FRAME_RETADDR_OFFSET < 0 109662306a36Sopenharmony_ci if (UNW_SP(frame) < top && UNW_FP(frame) <= UNW_SP(frame) 109762306a36Sopenharmony_ci && bottom < UNW_FP(frame) 109862306a36Sopenharmony_ci#else 109962306a36Sopenharmony_ci if (UNW_SP(frame) > top && UNW_FP(frame) >= UNW_SP(frame) 110062306a36Sopenharmony_ci && bottom > UNW_FP(frame) 110162306a36Sopenharmony_ci#endif 110262306a36Sopenharmony_ci && !((UNW_SP(frame) | UNW_FP(frame)) 110362306a36Sopenharmony_ci & (sizeof(unsigned long) - 1))) { 110462306a36Sopenharmony_ci unsigned long link; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (!__get_user(link, (unsigned long *) 110762306a36Sopenharmony_ci (UNW_FP(frame) + FRAME_LINK_OFFSET)) 110862306a36Sopenharmony_ci#if FRAME_RETADDR_OFFSET < 0 110962306a36Sopenharmony_ci && link > bottom && link < UNW_FP(frame) 111062306a36Sopenharmony_ci#else 111162306a36Sopenharmony_ci && link > UNW_FP(frame) && link < bottom 111262306a36Sopenharmony_ci#endif 111362306a36Sopenharmony_ci && !(link & (sizeof(link) - 1)) 111462306a36Sopenharmony_ci && !__get_user(UNW_PC(frame), 111562306a36Sopenharmony_ci (unsigned long *)(UNW_FP(frame) 111662306a36Sopenharmony_ci + FRAME_RETADDR_OFFSET))) 111762306a36Sopenharmony_ci { 111862306a36Sopenharmony_ci UNW_SP(frame) = 111962306a36Sopenharmony_ci UNW_FP(frame) + FRAME_RETADDR_OFFSET 112062306a36Sopenharmony_ci#if FRAME_RETADDR_OFFSET < 0 112162306a36Sopenharmony_ci - 112262306a36Sopenharmony_ci#else 112362306a36Sopenharmony_ci + 112462306a36Sopenharmony_ci#endif 112562306a36Sopenharmony_ci sizeof(UNW_PC(frame)); 112662306a36Sopenharmony_ci UNW_FP(frame) = link; 112762306a36Sopenharmony_ci return 0; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci#endif 113162306a36Sopenharmony_ci return -ENXIO; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci state.org = startLoc; 113462306a36Sopenharmony_ci memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci unw_debug("\nProcess instructions\n"); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* process instructions 113962306a36Sopenharmony_ci * For ARC, we optimize by having blink(retAddrReg) with 114062306a36Sopenharmony_ci * the sameValue in the leaf function, so we should not check 114162306a36Sopenharmony_ci * state.regs[retAddrReg].where == Nowhere 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_ci if (!processCFI(ptr, end, pc, ptrType, &state) 114462306a36Sopenharmony_ci || state.loc > endLoc 114562306a36Sopenharmony_ci/* || state.regs[retAddrReg].where == Nowhere */ 114662306a36Sopenharmony_ci || state.cfa.reg >= ARRAY_SIZE(reg_info) 114762306a36Sopenharmony_ci || reg_info[state.cfa.reg].width != sizeof(unsigned long) 114862306a36Sopenharmony_ci || state.cfa.offs % sizeof(unsigned long)) 114962306a36Sopenharmony_ci return -EIO; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci#ifdef UNWIND_DEBUG 115262306a36Sopenharmony_ci unw_debug("\n"); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci unw_debug("\nRegister State Based on the rules parsed from FDE:\n"); 115562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (REG_INVALID(i)) 115862306a36Sopenharmony_ci continue; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci switch (state.regs[i].where) { 116162306a36Sopenharmony_ci case Nowhere: 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci case Memory: 116462306a36Sopenharmony_ci unw_debug(" r%d: c(%lu),", i, state.regs[i].value); 116562306a36Sopenharmony_ci break; 116662306a36Sopenharmony_ci case Register: 116762306a36Sopenharmony_ci unw_debug(" r%d: r(%lu),", i, state.regs[i].value); 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci case Value: 117062306a36Sopenharmony_ci unw_debug(" r%d: v(%lu),", i, state.regs[i].value); 117162306a36Sopenharmony_ci break; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci unw_debug("\n"); 117662306a36Sopenharmony_ci#endif 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* update frame */ 117962306a36Sopenharmony_ci if (frame->call_frame 118062306a36Sopenharmony_ci && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) 118162306a36Sopenharmony_ci frame->call_frame = 0; 118262306a36Sopenharmony_ci cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; 118362306a36Sopenharmony_ci startLoc = min_t(unsigned long, UNW_SP(frame), cfa); 118462306a36Sopenharmony_ci endLoc = max_t(unsigned long, UNW_SP(frame), cfa); 118562306a36Sopenharmony_ci if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { 118662306a36Sopenharmony_ci startLoc = min(STACK_LIMIT(cfa), cfa); 118762306a36Sopenharmony_ci endLoc = max(STACK_LIMIT(cfa), cfa); 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci unw_debug("\nCFA reg: 0x%lx, offset: 0x%lx => 0x%lx\n", 119162306a36Sopenharmony_ci state.cfa.reg, state.cfa.offs, cfa); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { 119462306a36Sopenharmony_ci if (REG_INVALID(i)) { 119562306a36Sopenharmony_ci if (state.regs[i].where == Nowhere) 119662306a36Sopenharmony_ci continue; 119762306a36Sopenharmony_ci return -EIO; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci switch (state.regs[i].where) { 120062306a36Sopenharmony_ci default: 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case Register: 120362306a36Sopenharmony_ci if (state.regs[i].value >= ARRAY_SIZE(reg_info) 120462306a36Sopenharmony_ci || REG_INVALID(state.regs[i].value) 120562306a36Sopenharmony_ci || reg_info[i].width > 120662306a36Sopenharmony_ci reg_info[state.regs[i].value].width) 120762306a36Sopenharmony_ci return -EIO; 120862306a36Sopenharmony_ci switch (reg_info[state.regs[i].value].width) { 120962306a36Sopenharmony_ci case sizeof(u8): 121062306a36Sopenharmony_ci state.regs[i].value = 121162306a36Sopenharmony_ci FRAME_REG(state.regs[i].value, const u8); 121262306a36Sopenharmony_ci break; 121362306a36Sopenharmony_ci case sizeof(u16): 121462306a36Sopenharmony_ci state.regs[i].value = 121562306a36Sopenharmony_ci FRAME_REG(state.regs[i].value, const u16); 121662306a36Sopenharmony_ci break; 121762306a36Sopenharmony_ci case sizeof(u32): 121862306a36Sopenharmony_ci state.regs[i].value = 121962306a36Sopenharmony_ci FRAME_REG(state.regs[i].value, const u32); 122062306a36Sopenharmony_ci break; 122162306a36Sopenharmony_ci#ifdef CONFIG_64BIT 122262306a36Sopenharmony_ci case sizeof(u64): 122362306a36Sopenharmony_ci state.regs[i].value = 122462306a36Sopenharmony_ci FRAME_REG(state.regs[i].value, const u64); 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci#endif 122762306a36Sopenharmony_ci default: 122862306a36Sopenharmony_ci return -EIO; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci unw_debug("\nRegister state after evaluation with realtime Stack:\n"); 123562306a36Sopenharmony_ci fptr = (unsigned long *)(&frame->regs); 123662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) { 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (REG_INVALID(i)) 123962306a36Sopenharmony_ci continue; 124062306a36Sopenharmony_ci switch (state.regs[i].where) { 124162306a36Sopenharmony_ci case Nowhere: 124262306a36Sopenharmony_ci if (reg_info[i].width != sizeof(UNW_SP(frame)) 124362306a36Sopenharmony_ci || &FRAME_REG(i, __typeof__(UNW_SP(frame))) 124462306a36Sopenharmony_ci != &UNW_SP(frame)) 124562306a36Sopenharmony_ci continue; 124662306a36Sopenharmony_ci UNW_SP(frame) = cfa; 124762306a36Sopenharmony_ci break; 124862306a36Sopenharmony_ci case Register: 124962306a36Sopenharmony_ci switch (reg_info[i].width) { 125062306a36Sopenharmony_ci case sizeof(u8): 125162306a36Sopenharmony_ci FRAME_REG(i, u8) = state.regs[i].value; 125262306a36Sopenharmony_ci break; 125362306a36Sopenharmony_ci case sizeof(u16): 125462306a36Sopenharmony_ci FRAME_REG(i, u16) = state.regs[i].value; 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci case sizeof(u32): 125762306a36Sopenharmony_ci FRAME_REG(i, u32) = state.regs[i].value; 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci#ifdef CONFIG_64BIT 126062306a36Sopenharmony_ci case sizeof(u64): 126162306a36Sopenharmony_ci FRAME_REG(i, u64) = state.regs[i].value; 126262306a36Sopenharmony_ci break; 126362306a36Sopenharmony_ci#endif 126462306a36Sopenharmony_ci default: 126562306a36Sopenharmony_ci return -EIO; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci break; 126862306a36Sopenharmony_ci case Value: 126962306a36Sopenharmony_ci if (reg_info[i].width != sizeof(unsigned long)) 127062306a36Sopenharmony_ci return -EIO; 127162306a36Sopenharmony_ci FRAME_REG(i, unsigned long) = cfa + state.regs[i].value 127262306a36Sopenharmony_ci * state.dataAlign; 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci case Memory: 127562306a36Sopenharmony_ci addr = cfa + state.regs[i].value * state.dataAlign; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if ((state.regs[i].value * state.dataAlign) 127862306a36Sopenharmony_ci % sizeof(unsigned long) 127962306a36Sopenharmony_ci || addr < startLoc 128062306a36Sopenharmony_ci || addr + sizeof(unsigned long) < addr 128162306a36Sopenharmony_ci || addr + sizeof(unsigned long) > endLoc) 128262306a36Sopenharmony_ci return -EIO; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci switch (reg_info[i].width) { 128562306a36Sopenharmony_ci case sizeof(u8): 128662306a36Sopenharmony_ci __get_user(FRAME_REG(i, u8), 128762306a36Sopenharmony_ci (u8 __user *)addr); 128862306a36Sopenharmony_ci break; 128962306a36Sopenharmony_ci case sizeof(u16): 129062306a36Sopenharmony_ci __get_user(FRAME_REG(i, u16), 129162306a36Sopenharmony_ci (u16 __user *)addr); 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci case sizeof(u32): 129462306a36Sopenharmony_ci __get_user(FRAME_REG(i, u32), 129562306a36Sopenharmony_ci (u32 __user *)addr); 129662306a36Sopenharmony_ci break; 129762306a36Sopenharmony_ci#ifdef CONFIG_64BIT 129862306a36Sopenharmony_ci case sizeof(u64): 129962306a36Sopenharmony_ci __get_user(FRAME_REG(i, u64), 130062306a36Sopenharmony_ci (u64 __user *)addr); 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci#endif 130362306a36Sopenharmony_ci default: 130462306a36Sopenharmony_ci return -EIO; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci break; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci unw_debug("r%d: 0x%lx ", i, *fptr); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci return 0; 131362306a36Sopenharmony_ci#undef FRAME_REG 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ciEXPORT_SYMBOL(arc_unwind); 1316