18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mm/alignment.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 68c2ecf20Sopenharmony_ci * Modifications for ARM processor (c) 1995-2001 Russell King 78c2ecf20Sopenharmony_ci * Thumb alignment fault fixups (c) 2004 MontaVista Software, Inc. 88c2ecf20Sopenharmony_ci * - Adapted from gdb/sim/arm/thumbemu.c -- Thumb instruction emulation. 98c2ecf20Sopenharmony_ci * Copyright (C) 1996, Cygnus Software Technologies Ltd. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 128c2ecf20Sopenharmony_ci#include <linux/compiler.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 188c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <asm/cp15.h> 248c2ecf20Sopenharmony_ci#include <asm/extable.h> 258c2ecf20Sopenharmony_ci#include <asm/system_info.h> 268c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 278c2ecf20Sopenharmony_ci#include <asm/opcodes.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "fault.h" 308c2ecf20Sopenharmony_ci#include "mm.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 348c2ecf20Sopenharmony_ci * /proc/sys/debug/alignment, modified and integrated into 358c2ecf20Sopenharmony_ci * Linux 2.1 by Russell King 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Speed optimisations and better fault handling by Russell King. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * *** NOTE *** 408c2ecf20Sopenharmony_ci * This code is not portable to processors with late data abort handling. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#define CODING_BITS(i) (i & 0x0e000000) 438c2ecf20Sopenharmony_ci#define COND_BITS(i) (i & 0xf0000000) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */ 468c2ecf20Sopenharmony_ci#define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */ 478c2ecf20Sopenharmony_ci#define LDST_U_BIT(i) (i & (1 << 23)) /* Add offset */ 488c2ecf20Sopenharmony_ci#define LDST_W_BIT(i) (i & (1 << 21)) /* Writeback */ 498c2ecf20Sopenharmony_ci#define LDST_L_BIT(i) (i & (1 << 20)) /* Load */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define LDSTHD_I_BIT(i) (i & (1 << 22)) /* double/half-word immed */ 548c2ecf20Sopenharmony_ci#define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define RN_BITS(i) ((i >> 16) & 15) /* Rn */ 578c2ecf20Sopenharmony_ci#define RD_BITS(i) ((i >> 12) & 15) /* Rd */ 588c2ecf20Sopenharmony_ci#define RM_BITS(i) (i & 15) /* Rm */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define REGMASK_BITS(i) (i & 0xffff) 618c2ecf20Sopenharmony_ci#define OFFSET_BITS(i) (i & 0x0fff) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define IS_SHIFT(i) (i & 0x0ff0) 648c2ecf20Sopenharmony_ci#define SHIFT_BITS(i) ((i >> 7) & 0x1f) 658c2ecf20Sopenharmony_ci#define SHIFT_TYPE(i) (i & 0x60) 668c2ecf20Sopenharmony_ci#define SHIFT_LSL 0x00 678c2ecf20Sopenharmony_ci#define SHIFT_LSR 0x20 688c2ecf20Sopenharmony_ci#define SHIFT_ASR 0x40 698c2ecf20Sopenharmony_ci#define SHIFT_RORRRX 0x60 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define BAD_INSTR 0xdeadc0de 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Thumb-2 32 bit format per ARMv7 DDI0406A A6.3, either f800h,e800h,f800h */ 748c2ecf20Sopenharmony_ci#define IS_T32(hi16) \ 758c2ecf20Sopenharmony_ci (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800)) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic unsigned long ai_user; 788c2ecf20Sopenharmony_cistatic unsigned long ai_sys; 798c2ecf20Sopenharmony_cistatic void *ai_sys_last_pc; 808c2ecf20Sopenharmony_cistatic unsigned long ai_skipped; 818c2ecf20Sopenharmony_cistatic unsigned long ai_half; 828c2ecf20Sopenharmony_cistatic unsigned long ai_word; 838c2ecf20Sopenharmony_cistatic unsigned long ai_dword; 848c2ecf20Sopenharmony_cistatic unsigned long ai_multi; 858c2ecf20Sopenharmony_cistatic int ai_usermode; 868c2ecf20Sopenharmony_cistatic unsigned long cr_no_alignment; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cicore_param(alignment, ai_usermode, int, 0600); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define UM_WARN (1 << 0) 918c2ecf20Sopenharmony_ci#define UM_FIXUP (1 << 1) 928c2ecf20Sopenharmony_ci#define UM_SIGNAL (1 << 2) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Return true if and only if the ARMv6 unaligned access model is in use. */ 958c2ecf20Sopenharmony_cistatic bool cpu_is_v6_unaligned(void) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return cpu_architecture() >= CPU_ARCH_ARMv6 && get_cr() & CR_U; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int safe_usermode(int new_usermode, bool warn) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * ARMv6 and later CPUs can perform unaligned accesses for 1048c2ecf20Sopenharmony_ci * most single load and store instructions up to word size. 1058c2ecf20Sopenharmony_ci * LDM, STM, LDRD and STRD still need to be handled. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * Ignoring the alignment fault is not an option on these 1088c2ecf20Sopenharmony_ci * CPUs since we spin re-faulting the instruction without 1098c2ecf20Sopenharmony_ci * making any progress. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci if (cpu_is_v6_unaligned() && !(new_usermode & (UM_FIXUP | UM_SIGNAL))) { 1128c2ecf20Sopenharmony_ci new_usermode |= UM_FIXUP; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (warn) 1158c2ecf20Sopenharmony_ci pr_warn("alignment: ignoring faults is unsafe on this CPU. Defaulting to fixup mode.\n"); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return new_usermode; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 1228c2ecf20Sopenharmony_cistatic const char *usermode_action[] = { 1238c2ecf20Sopenharmony_ci "ignored", 1248c2ecf20Sopenharmony_ci "warn", 1258c2ecf20Sopenharmony_ci "fixup", 1268c2ecf20Sopenharmony_ci "fixup+warn", 1278c2ecf20Sopenharmony_ci "signal", 1288c2ecf20Sopenharmony_ci "signal+warn" 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int alignment_proc_show(struct seq_file *m, void *v) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci seq_printf(m, "User:\t\t%lu\n", ai_user); 1348c2ecf20Sopenharmony_ci seq_printf(m, "System:\t\t%lu (%pS)\n", ai_sys, ai_sys_last_pc); 1358c2ecf20Sopenharmony_ci seq_printf(m, "Skipped:\t%lu\n", ai_skipped); 1368c2ecf20Sopenharmony_ci seq_printf(m, "Half:\t\t%lu\n", ai_half); 1378c2ecf20Sopenharmony_ci seq_printf(m, "Word:\t\t%lu\n", ai_word); 1388c2ecf20Sopenharmony_ci if (cpu_architecture() >= CPU_ARCH_ARMv5TE) 1398c2ecf20Sopenharmony_ci seq_printf(m, "DWord:\t\t%lu\n", ai_dword); 1408c2ecf20Sopenharmony_ci seq_printf(m, "Multi:\t\t%lu\n", ai_multi); 1418c2ecf20Sopenharmony_ci seq_printf(m, "User faults:\t%i (%s)\n", ai_usermode, 1428c2ecf20Sopenharmony_ci usermode_action[ai_usermode]); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int alignment_proc_open(struct inode *inode, struct file *file) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return single_open(file, alignment_proc_show, NULL); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic ssize_t alignment_proc_write(struct file *file, const char __user *buffer, 1538c2ecf20Sopenharmony_ci size_t count, loff_t *pos) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci char mode; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (count > 0) { 1588c2ecf20Sopenharmony_ci if (get_user(mode, buffer)) 1598c2ecf20Sopenharmony_ci return -EFAULT; 1608c2ecf20Sopenharmony_ci if (mode >= '0' && mode <= '5') 1618c2ecf20Sopenharmony_ci ai_usermode = safe_usermode(mode - '0', true); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci return count; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct proc_ops alignment_proc_ops = { 1678c2ecf20Sopenharmony_ci .proc_open = alignment_proc_open, 1688c2ecf20Sopenharmony_ci .proc_read = seq_read, 1698c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 1708c2ecf20Sopenharmony_ci .proc_release = single_release, 1718c2ecf20Sopenharmony_ci .proc_write = alignment_proc_write, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciunion offset_union { 1768c2ecf20Sopenharmony_ci unsigned long un; 1778c2ecf20Sopenharmony_ci signed long sn; 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define TYPE_ERROR 0 1818c2ecf20Sopenharmony_ci#define TYPE_FAULT 1 1828c2ecf20Sopenharmony_ci#define TYPE_LDST 2 1838c2ecf20Sopenharmony_ci#define TYPE_DONE 3 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 1868c2ecf20Sopenharmony_ci#define BE 1 1878c2ecf20Sopenharmony_ci#define FIRST_BYTE_16 "mov %1, %1, ror #8\n" 1888c2ecf20Sopenharmony_ci#define FIRST_BYTE_32 "mov %1, %1, ror #24\n" 1898c2ecf20Sopenharmony_ci#define NEXT_BYTE "ror #24" 1908c2ecf20Sopenharmony_ci#else 1918c2ecf20Sopenharmony_ci#define BE 0 1928c2ecf20Sopenharmony_ci#define FIRST_BYTE_16 1938c2ecf20Sopenharmony_ci#define FIRST_BYTE_32 1948c2ecf20Sopenharmony_ci#define NEXT_BYTE "lsr #8" 1958c2ecf20Sopenharmony_ci#endif 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#define __get8_unaligned_check(ins,val,addr,err) \ 1988c2ecf20Sopenharmony_ci __asm__( \ 1998c2ecf20Sopenharmony_ci ARM( "1: "ins" %1, [%2], #1\n" ) \ 2008c2ecf20Sopenharmony_ci THUMB( "1: "ins" %1, [%2]\n" ) \ 2018c2ecf20Sopenharmony_ci THUMB( " add %2, %2, #1\n" ) \ 2028c2ecf20Sopenharmony_ci "2:\n" \ 2038c2ecf20Sopenharmony_ci " .pushsection .text.fixup,\"ax\"\n" \ 2048c2ecf20Sopenharmony_ci " .align 2\n" \ 2058c2ecf20Sopenharmony_ci "3: mov %0, #1\n" \ 2068c2ecf20Sopenharmony_ci " b 2b\n" \ 2078c2ecf20Sopenharmony_ci " .popsection\n" \ 2088c2ecf20Sopenharmony_ci " ex_entry 1b, 3b\n" \ 2098c2ecf20Sopenharmony_ci : "=r" (err), "=&r" (val), "=r" (addr) \ 2108c2ecf20Sopenharmony_ci : "0" (err), "2" (addr)) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci#define __get16_unaligned_check(ins,val,addr) \ 2138c2ecf20Sopenharmony_ci do { \ 2148c2ecf20Sopenharmony_ci unsigned int err = 0, v, a = addr; \ 2158c2ecf20Sopenharmony_ci __get8_unaligned_check(ins,v,a,err); \ 2168c2ecf20Sopenharmony_ci val = v << ((BE) ? 8 : 0); \ 2178c2ecf20Sopenharmony_ci __get8_unaligned_check(ins,v,a,err); \ 2188c2ecf20Sopenharmony_ci val |= v << ((BE) ? 0 : 8); \ 2198c2ecf20Sopenharmony_ci if (err) \ 2208c2ecf20Sopenharmony_ci goto fault; \ 2218c2ecf20Sopenharmony_ci } while (0) 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#define get16_unaligned_check(val,addr) \ 2248c2ecf20Sopenharmony_ci __get16_unaligned_check("ldrb",val,addr) 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci#define get16t_unaligned_check(val,addr) \ 2278c2ecf20Sopenharmony_ci __get16_unaligned_check("ldrbt",val,addr) 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci#define __get32_unaligned_check(ins,val,addr) \ 2308c2ecf20Sopenharmony_ci do { \ 2318c2ecf20Sopenharmony_ci unsigned int err = 0, v, a = addr; \ 2328c2ecf20Sopenharmony_ci __get8_unaligned_check(ins,v,a,err); \ 2338c2ecf20Sopenharmony_ci val = v << ((BE) ? 24 : 0); \ 2348c2ecf20Sopenharmony_ci __get8_unaligned_check(ins,v,a,err); \ 2358c2ecf20Sopenharmony_ci val |= v << ((BE) ? 16 : 8); \ 2368c2ecf20Sopenharmony_ci __get8_unaligned_check(ins,v,a,err); \ 2378c2ecf20Sopenharmony_ci val |= v << ((BE) ? 8 : 16); \ 2388c2ecf20Sopenharmony_ci __get8_unaligned_check(ins,v,a,err); \ 2398c2ecf20Sopenharmony_ci val |= v << ((BE) ? 0 : 24); \ 2408c2ecf20Sopenharmony_ci if (err) \ 2418c2ecf20Sopenharmony_ci goto fault; \ 2428c2ecf20Sopenharmony_ci } while (0) 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci#define get32_unaligned_check(val,addr) \ 2458c2ecf20Sopenharmony_ci __get32_unaligned_check("ldrb",val,addr) 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define get32t_unaligned_check(val,addr) \ 2488c2ecf20Sopenharmony_ci __get32_unaligned_check("ldrbt",val,addr) 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define __put16_unaligned_check(ins,val,addr) \ 2518c2ecf20Sopenharmony_ci do { \ 2528c2ecf20Sopenharmony_ci unsigned int err = 0, v = val, a = addr; \ 2538c2ecf20Sopenharmony_ci __asm__( FIRST_BYTE_16 \ 2548c2ecf20Sopenharmony_ci ARM( "1: "ins" %1, [%2], #1\n" ) \ 2558c2ecf20Sopenharmony_ci THUMB( "1: "ins" %1, [%2]\n" ) \ 2568c2ecf20Sopenharmony_ci THUMB( " add %2, %2, #1\n" ) \ 2578c2ecf20Sopenharmony_ci " mov %1, %1, "NEXT_BYTE"\n" \ 2588c2ecf20Sopenharmony_ci "2: "ins" %1, [%2]\n" \ 2598c2ecf20Sopenharmony_ci "3:\n" \ 2608c2ecf20Sopenharmony_ci " .pushsection .text.fixup,\"ax\"\n" \ 2618c2ecf20Sopenharmony_ci " .align 2\n" \ 2628c2ecf20Sopenharmony_ci "4: mov %0, #1\n" \ 2638c2ecf20Sopenharmony_ci " b 3b\n" \ 2648c2ecf20Sopenharmony_ci " .popsection\n" \ 2658c2ecf20Sopenharmony_ci " ex_entry 1b, 4b\n" \ 2668c2ecf20Sopenharmony_ci " ex_entry 2b, 4b\n" \ 2678c2ecf20Sopenharmony_ci : "=r" (err), "=&r" (v), "=&r" (a) \ 2688c2ecf20Sopenharmony_ci : "0" (err), "1" (v), "2" (a)); \ 2698c2ecf20Sopenharmony_ci if (err) \ 2708c2ecf20Sopenharmony_ci goto fault; \ 2718c2ecf20Sopenharmony_ci } while (0) 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#define put16_unaligned_check(val,addr) \ 2748c2ecf20Sopenharmony_ci __put16_unaligned_check("strb",val,addr) 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#define put16t_unaligned_check(val,addr) \ 2778c2ecf20Sopenharmony_ci __put16_unaligned_check("strbt",val,addr) 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci#define __put32_unaligned_check(ins,val,addr) \ 2808c2ecf20Sopenharmony_ci do { \ 2818c2ecf20Sopenharmony_ci unsigned int err = 0, v = val, a = addr; \ 2828c2ecf20Sopenharmony_ci __asm__( FIRST_BYTE_32 \ 2838c2ecf20Sopenharmony_ci ARM( "1: "ins" %1, [%2], #1\n" ) \ 2848c2ecf20Sopenharmony_ci THUMB( "1: "ins" %1, [%2]\n" ) \ 2858c2ecf20Sopenharmony_ci THUMB( " add %2, %2, #1\n" ) \ 2868c2ecf20Sopenharmony_ci " mov %1, %1, "NEXT_BYTE"\n" \ 2878c2ecf20Sopenharmony_ci ARM( "2: "ins" %1, [%2], #1\n" ) \ 2888c2ecf20Sopenharmony_ci THUMB( "2: "ins" %1, [%2]\n" ) \ 2898c2ecf20Sopenharmony_ci THUMB( " add %2, %2, #1\n" ) \ 2908c2ecf20Sopenharmony_ci " mov %1, %1, "NEXT_BYTE"\n" \ 2918c2ecf20Sopenharmony_ci ARM( "3: "ins" %1, [%2], #1\n" ) \ 2928c2ecf20Sopenharmony_ci THUMB( "3: "ins" %1, [%2]\n" ) \ 2938c2ecf20Sopenharmony_ci THUMB( " add %2, %2, #1\n" ) \ 2948c2ecf20Sopenharmony_ci " mov %1, %1, "NEXT_BYTE"\n" \ 2958c2ecf20Sopenharmony_ci "4: "ins" %1, [%2]\n" \ 2968c2ecf20Sopenharmony_ci "5:\n" \ 2978c2ecf20Sopenharmony_ci " .pushsection .text.fixup,\"ax\"\n" \ 2988c2ecf20Sopenharmony_ci " .align 2\n" \ 2998c2ecf20Sopenharmony_ci "6: mov %0, #1\n" \ 3008c2ecf20Sopenharmony_ci " b 5b\n" \ 3018c2ecf20Sopenharmony_ci " .popsection\n" \ 3028c2ecf20Sopenharmony_ci " ex_entry 1b, 6b\n" \ 3038c2ecf20Sopenharmony_ci " ex_entry 2b, 6b\n" \ 3048c2ecf20Sopenharmony_ci " ex_entry 3b, 6b\n" \ 3058c2ecf20Sopenharmony_ci " ex_entry 4b, 6b\n" \ 3068c2ecf20Sopenharmony_ci : "=r" (err), "=&r" (v), "=&r" (a) \ 3078c2ecf20Sopenharmony_ci : "0" (err), "1" (v), "2" (a)); \ 3088c2ecf20Sopenharmony_ci if (err) \ 3098c2ecf20Sopenharmony_ci goto fault; \ 3108c2ecf20Sopenharmony_ci } while (0) 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci#define put32_unaligned_check(val,addr) \ 3138c2ecf20Sopenharmony_ci __put32_unaligned_check("strb", val, addr) 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci#define put32t_unaligned_check(val,addr) \ 3168c2ecf20Sopenharmony_ci __put32_unaligned_check("strbt", val, addr) 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void 3198c2ecf20Sopenharmony_cido_alignment_finish_ldst(unsigned long addr, u32 instr, struct pt_regs *regs, union offset_union offset) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci if (!LDST_U_BIT(instr)) 3228c2ecf20Sopenharmony_ci offset.un = -offset.un; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!LDST_P_BIT(instr)) 3258c2ecf20Sopenharmony_ci addr += offset.un; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!LDST_P_BIT(instr) || LDST_W_BIT(instr)) 3288c2ecf20Sopenharmony_ci regs->uregs[RN_BITS(instr)] = addr; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int 3328c2ecf20Sopenharmony_cido_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci unsigned int rd = RD_BITS(instr); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ai_half += 1; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (user_mode(regs)) 3398c2ecf20Sopenharmony_ci goto user; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (LDST_L_BIT(instr)) { 3428c2ecf20Sopenharmony_ci unsigned long val; 3438c2ecf20Sopenharmony_ci get16_unaligned_check(val, addr); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* signed half-word? */ 3468c2ecf20Sopenharmony_ci if (instr & 0x40) 3478c2ecf20Sopenharmony_ci val = (signed long)((signed short) val); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 3508c2ecf20Sopenharmony_ci } else 3518c2ecf20Sopenharmony_ci put16_unaligned_check(regs->uregs[rd], addr); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return TYPE_LDST; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci user: 3568c2ecf20Sopenharmony_ci if (LDST_L_BIT(instr)) { 3578c2ecf20Sopenharmony_ci unsigned long val; 3588c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci get16t_unaligned_check(val, addr); 3618c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* signed half-word? */ 3648c2ecf20Sopenharmony_ci if (instr & 0x40) 3658c2ecf20Sopenharmony_ci val = (signed long)((signed short) val); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 3688c2ecf20Sopenharmony_ci } else { 3698c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 3708c2ecf20Sopenharmony_ci put16t_unaligned_check(regs->uregs[rd], addr); 3718c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return TYPE_LDST; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci fault: 3778c2ecf20Sopenharmony_ci return TYPE_FAULT; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int 3818c2ecf20Sopenharmony_cido_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci unsigned int rd = RD_BITS(instr); 3848c2ecf20Sopenharmony_ci unsigned int rd2; 3858c2ecf20Sopenharmony_ci int load; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if ((instr & 0xfe000000) == 0xe8000000) { 3888c2ecf20Sopenharmony_ci /* ARMv7 Thumb-2 32-bit LDRD/STRD */ 3898c2ecf20Sopenharmony_ci rd2 = (instr >> 8) & 0xf; 3908c2ecf20Sopenharmony_ci load = !!(LDST_L_BIT(instr)); 3918c2ecf20Sopenharmony_ci } else if (((rd & 1) == 1) || (rd == 14)) 3928c2ecf20Sopenharmony_ci goto bad; 3938c2ecf20Sopenharmony_ci else { 3948c2ecf20Sopenharmony_ci load = ((instr & 0xf0) == 0xd0); 3958c2ecf20Sopenharmony_ci rd2 = rd + 1; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ai_dword += 1; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (user_mode(regs)) 4018c2ecf20Sopenharmony_ci goto user; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (load) { 4048c2ecf20Sopenharmony_ci unsigned long val; 4058c2ecf20Sopenharmony_ci get32_unaligned_check(val, addr); 4068c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 4078c2ecf20Sopenharmony_ci get32_unaligned_check(val, addr + 4); 4088c2ecf20Sopenharmony_ci regs->uregs[rd2] = val; 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci put32_unaligned_check(regs->uregs[rd], addr); 4118c2ecf20Sopenharmony_ci put32_unaligned_check(regs->uregs[rd2], addr + 4); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return TYPE_LDST; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci user: 4178c2ecf20Sopenharmony_ci if (load) { 4188c2ecf20Sopenharmony_ci unsigned long val, val2; 4198c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci get32t_unaligned_check(val, addr); 4228c2ecf20Sopenharmony_ci get32t_unaligned_check(val2, addr + 4); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 4278c2ecf20Sopenharmony_ci regs->uregs[rd2] = val2; 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 4308c2ecf20Sopenharmony_ci put32t_unaligned_check(regs->uregs[rd], addr); 4318c2ecf20Sopenharmony_ci put32t_unaligned_check(regs->uregs[rd2], addr + 4); 4328c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return TYPE_LDST; 4368c2ecf20Sopenharmony_ci bad: 4378c2ecf20Sopenharmony_ci return TYPE_ERROR; 4388c2ecf20Sopenharmony_ci fault: 4398c2ecf20Sopenharmony_ci return TYPE_FAULT; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int 4438c2ecf20Sopenharmony_cido_alignment_ldrstr(unsigned long addr, u32 instr, struct pt_regs *regs) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci unsigned int rd = RD_BITS(instr); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ai_word += 1; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs)) 4508c2ecf20Sopenharmony_ci goto trans; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (LDST_L_BIT(instr)) { 4538c2ecf20Sopenharmony_ci unsigned int val; 4548c2ecf20Sopenharmony_ci get32_unaligned_check(val, addr); 4558c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 4568c2ecf20Sopenharmony_ci } else 4578c2ecf20Sopenharmony_ci put32_unaligned_check(regs->uregs[rd], addr); 4588c2ecf20Sopenharmony_ci return TYPE_LDST; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci trans: 4618c2ecf20Sopenharmony_ci if (LDST_L_BIT(instr)) { 4628c2ecf20Sopenharmony_ci unsigned int val; 4638c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 4648c2ecf20Sopenharmony_ci get32t_unaligned_check(val, addr); 4658c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 4668c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 4678c2ecf20Sopenharmony_ci } else { 4688c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 4698c2ecf20Sopenharmony_ci put32t_unaligned_check(regs->uregs[rd], addr); 4708c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci return TYPE_LDST; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci fault: 4758c2ecf20Sopenharmony_ci return TYPE_FAULT; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* 4798c2ecf20Sopenharmony_ci * LDM/STM alignment handler. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * There are 4 variants of this instruction: 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * B = rn pointer before instruction, A = rn pointer after instruction 4848c2ecf20Sopenharmony_ci * ------ increasing address -----> 4858c2ecf20Sopenharmony_ci * | | r0 | r1 | ... | rx | | 4868c2ecf20Sopenharmony_ci * PU = 01 B A 4878c2ecf20Sopenharmony_ci * PU = 11 B A 4888c2ecf20Sopenharmony_ci * PU = 00 A B 4898c2ecf20Sopenharmony_ci * PU = 10 A B 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic int 4928c2ecf20Sopenharmony_cido_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci unsigned int rd, rn, correction, nr_regs, regbits; 4958c2ecf20Sopenharmony_ci unsigned long eaddr, newaddr; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (LDM_S_BIT(instr)) 4988c2ecf20Sopenharmony_ci goto bad; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci correction = 4; /* processor implementation defined */ 5018c2ecf20Sopenharmony_ci regs->ARM_pc += correction; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ai_multi += 1; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* count the number of registers in the mask to be transferred */ 5068c2ecf20Sopenharmony_ci nr_regs = hweight16(REGMASK_BITS(instr)) * 4; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci rn = RN_BITS(instr); 5098c2ecf20Sopenharmony_ci newaddr = eaddr = regs->uregs[rn]; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!LDST_U_BIT(instr)) 5128c2ecf20Sopenharmony_ci nr_regs = -nr_regs; 5138c2ecf20Sopenharmony_ci newaddr += nr_regs; 5148c2ecf20Sopenharmony_ci if (!LDST_U_BIT(instr)) 5158c2ecf20Sopenharmony_ci eaddr = newaddr; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (LDST_P_EQ_U(instr)) /* U = P */ 5188c2ecf20Sopenharmony_ci eaddr += 4; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* 5218c2ecf20Sopenharmony_ci * For alignment faults on the ARM922T/ARM920T the MMU makes 5228c2ecf20Sopenharmony_ci * the FSR (and hence addr) equal to the updated base address 5238c2ecf20Sopenharmony_ci * of the multiple access rather than the restored value. 5248c2ecf20Sopenharmony_ci * Switch this message off if we've got a ARM92[02], otherwise 5258c2ecf20Sopenharmony_ci * [ls]dm alignment faults are noisy! 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci#if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T) 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * This is a "hint" - we already have eaddr worked out by the 5308c2ecf20Sopenharmony_ci * processor for us. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci if (addr != eaddr) { 5338c2ecf20Sopenharmony_ci pr_err("LDMSTM: PC = %08lx, instr = %08x, " 5348c2ecf20Sopenharmony_ci "addr = %08lx, eaddr = %08lx\n", 5358c2ecf20Sopenharmony_ci instruction_pointer(regs), instr, addr, eaddr); 5368c2ecf20Sopenharmony_ci show_regs(regs); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci#endif 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (user_mode(regs)) { 5418c2ecf20Sopenharmony_ci unsigned int __ua_flags = uaccess_save_and_enable(); 5428c2ecf20Sopenharmony_ci for (regbits = REGMASK_BITS(instr), rd = 0; regbits; 5438c2ecf20Sopenharmony_ci regbits >>= 1, rd += 1) 5448c2ecf20Sopenharmony_ci if (regbits & 1) { 5458c2ecf20Sopenharmony_ci if (LDST_L_BIT(instr)) { 5468c2ecf20Sopenharmony_ci unsigned int val; 5478c2ecf20Sopenharmony_ci get32t_unaligned_check(val, eaddr); 5488c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 5498c2ecf20Sopenharmony_ci } else 5508c2ecf20Sopenharmony_ci put32t_unaligned_check(regs->uregs[rd], eaddr); 5518c2ecf20Sopenharmony_ci eaddr += 4; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci uaccess_restore(__ua_flags); 5548c2ecf20Sopenharmony_ci } else { 5558c2ecf20Sopenharmony_ci for (regbits = REGMASK_BITS(instr), rd = 0; regbits; 5568c2ecf20Sopenharmony_ci regbits >>= 1, rd += 1) 5578c2ecf20Sopenharmony_ci if (regbits & 1) { 5588c2ecf20Sopenharmony_ci if (LDST_L_BIT(instr)) { 5598c2ecf20Sopenharmony_ci unsigned int val; 5608c2ecf20Sopenharmony_ci get32_unaligned_check(val, eaddr); 5618c2ecf20Sopenharmony_ci regs->uregs[rd] = val; 5628c2ecf20Sopenharmony_ci } else 5638c2ecf20Sopenharmony_ci put32_unaligned_check(regs->uregs[rd], eaddr); 5648c2ecf20Sopenharmony_ci eaddr += 4; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (LDST_W_BIT(instr)) 5698c2ecf20Sopenharmony_ci regs->uregs[rn] = newaddr; 5708c2ecf20Sopenharmony_ci if (!LDST_L_BIT(instr) || !(REGMASK_BITS(instr) & (1 << 15))) 5718c2ecf20Sopenharmony_ci regs->ARM_pc -= correction; 5728c2ecf20Sopenharmony_ci return TYPE_DONE; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cifault: 5758c2ecf20Sopenharmony_ci regs->ARM_pc -= correction; 5768c2ecf20Sopenharmony_ci return TYPE_FAULT; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cibad: 5798c2ecf20Sopenharmony_ci pr_err("Alignment trap: not handling ldm with s-bit set\n"); 5808c2ecf20Sopenharmony_ci return TYPE_ERROR; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci/* 5848c2ecf20Sopenharmony_ci * Convert Thumb ld/st instruction forms to equivalent ARM instructions so 5858c2ecf20Sopenharmony_ci * we can reuse ARM userland alignment fault fixups for Thumb. 5868c2ecf20Sopenharmony_ci * 5878c2ecf20Sopenharmony_ci * This implementation was initially based on the algorithm found in 5888c2ecf20Sopenharmony_ci * gdb/sim/arm/thumbemu.c. It is basically just a code reduction of same 5898c2ecf20Sopenharmony_ci * to convert only Thumb ld/st instruction forms to equivalent ARM forms. 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * NOTES: 5928c2ecf20Sopenharmony_ci * 1. Comments below refer to ARM ARM DDI0100E Thumb Instruction sections. 5938c2ecf20Sopenharmony_ci * 2. If for some reason we're passed an non-ld/st Thumb instruction to 5948c2ecf20Sopenharmony_ci * decode, we return 0xdeadc0de. This should never happen under normal 5958c2ecf20Sopenharmony_ci * circumstances but if it does, we've got other problems to deal with 5968c2ecf20Sopenharmony_ci * elsewhere and we obviously can't fix those problems here. 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic unsigned long 6008c2ecf20Sopenharmony_cithumb2arm(u16 tinstr) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci u32 L = (tinstr & (1<<11)) >> 11; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci switch ((tinstr & 0xf800) >> 11) { 6058c2ecf20Sopenharmony_ci /* 6.5.1 Format 1: */ 6068c2ecf20Sopenharmony_ci case 0x6000 >> 11: /* 7.1.52 STR(1) */ 6078c2ecf20Sopenharmony_ci case 0x6800 >> 11: /* 7.1.26 LDR(1) */ 6088c2ecf20Sopenharmony_ci case 0x7000 >> 11: /* 7.1.55 STRB(1) */ 6098c2ecf20Sopenharmony_ci case 0x7800 >> 11: /* 7.1.30 LDRB(1) */ 6108c2ecf20Sopenharmony_ci return 0xe5800000 | 6118c2ecf20Sopenharmony_ci ((tinstr & (1<<12)) << (22-12)) | /* fixup */ 6128c2ecf20Sopenharmony_ci (L<<20) | /* L==1? */ 6138c2ecf20Sopenharmony_ci ((tinstr & (7<<0)) << (12-0)) | /* Rd */ 6148c2ecf20Sopenharmony_ci ((tinstr & (7<<3)) << (16-3)) | /* Rn */ 6158c2ecf20Sopenharmony_ci ((tinstr & (31<<6)) >> /* immed_5 */ 6168c2ecf20Sopenharmony_ci (6 - ((tinstr & (1<<12)) ? 0 : 2))); 6178c2ecf20Sopenharmony_ci case 0x8000 >> 11: /* 7.1.57 STRH(1) */ 6188c2ecf20Sopenharmony_ci case 0x8800 >> 11: /* 7.1.32 LDRH(1) */ 6198c2ecf20Sopenharmony_ci return 0xe1c000b0 | 6208c2ecf20Sopenharmony_ci (L<<20) | /* L==1? */ 6218c2ecf20Sopenharmony_ci ((tinstr & (7<<0)) << (12-0)) | /* Rd */ 6228c2ecf20Sopenharmony_ci ((tinstr & (7<<3)) << (16-3)) | /* Rn */ 6238c2ecf20Sopenharmony_ci ((tinstr & (7<<6)) >> (6-1)) | /* immed_5[2:0] */ 6248c2ecf20Sopenharmony_ci ((tinstr & (3<<9)) >> (9-8)); /* immed_5[4:3] */ 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* 6.5.1 Format 2: */ 6278c2ecf20Sopenharmony_ci case 0x5000 >> 11: 6288c2ecf20Sopenharmony_ci case 0x5800 >> 11: 6298c2ecf20Sopenharmony_ci { 6308c2ecf20Sopenharmony_ci static const u32 subset[8] = { 6318c2ecf20Sopenharmony_ci 0xe7800000, /* 7.1.53 STR(2) */ 6328c2ecf20Sopenharmony_ci 0xe18000b0, /* 7.1.58 STRH(2) */ 6338c2ecf20Sopenharmony_ci 0xe7c00000, /* 7.1.56 STRB(2) */ 6348c2ecf20Sopenharmony_ci 0xe19000d0, /* 7.1.34 LDRSB */ 6358c2ecf20Sopenharmony_ci 0xe7900000, /* 7.1.27 LDR(2) */ 6368c2ecf20Sopenharmony_ci 0xe19000b0, /* 7.1.33 LDRH(2) */ 6378c2ecf20Sopenharmony_ci 0xe7d00000, /* 7.1.31 LDRB(2) */ 6388c2ecf20Sopenharmony_ci 0xe19000f0 /* 7.1.35 LDRSH */ 6398c2ecf20Sopenharmony_ci }; 6408c2ecf20Sopenharmony_ci return subset[(tinstr & (7<<9)) >> 9] | 6418c2ecf20Sopenharmony_ci ((tinstr & (7<<0)) << (12-0)) | /* Rd */ 6428c2ecf20Sopenharmony_ci ((tinstr & (7<<3)) << (16-3)) | /* Rn */ 6438c2ecf20Sopenharmony_ci ((tinstr & (7<<6)) >> (6-0)); /* Rm */ 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* 6.5.1 Format 3: */ 6478c2ecf20Sopenharmony_ci case 0x4800 >> 11: /* 7.1.28 LDR(3) */ 6488c2ecf20Sopenharmony_ci /* NOTE: This case is not technically possible. We're 6498c2ecf20Sopenharmony_ci * loading 32-bit memory data via PC relative 6508c2ecf20Sopenharmony_ci * addressing mode. So we can and should eliminate 6518c2ecf20Sopenharmony_ci * this case. But I'll leave it here for now. 6528c2ecf20Sopenharmony_ci */ 6538c2ecf20Sopenharmony_ci return 0xe59f0000 | 6548c2ecf20Sopenharmony_ci ((tinstr & (7<<8)) << (12-8)) | /* Rd */ 6558c2ecf20Sopenharmony_ci ((tinstr & 255) << (2-0)); /* immed_8 */ 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* 6.5.1 Format 4: */ 6588c2ecf20Sopenharmony_ci case 0x9000 >> 11: /* 7.1.54 STR(3) */ 6598c2ecf20Sopenharmony_ci case 0x9800 >> 11: /* 7.1.29 LDR(4) */ 6608c2ecf20Sopenharmony_ci return 0xe58d0000 | 6618c2ecf20Sopenharmony_ci (L<<20) | /* L==1? */ 6628c2ecf20Sopenharmony_ci ((tinstr & (7<<8)) << (12-8)) | /* Rd */ 6638c2ecf20Sopenharmony_ci ((tinstr & 255) << 2); /* immed_8 */ 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* 6.6.1 Format 1: */ 6668c2ecf20Sopenharmony_ci case 0xc000 >> 11: /* 7.1.51 STMIA */ 6678c2ecf20Sopenharmony_ci case 0xc800 >> 11: /* 7.1.25 LDMIA */ 6688c2ecf20Sopenharmony_ci { 6698c2ecf20Sopenharmony_ci u32 Rn = (tinstr & (7<<8)) >> 8; 6708c2ecf20Sopenharmony_ci u32 W = ((L<<Rn) & (tinstr&255)) ? 0 : 1<<21; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0xe8800000 | W | (L<<20) | (Rn<<16) | 6738c2ecf20Sopenharmony_ci (tinstr&255); 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* 6.6.1 Format 2: */ 6778c2ecf20Sopenharmony_ci case 0xb000 >> 11: /* 7.1.48 PUSH */ 6788c2ecf20Sopenharmony_ci case 0xb800 >> 11: /* 7.1.47 POP */ 6798c2ecf20Sopenharmony_ci if ((tinstr & (3 << 9)) == 0x0400) { 6808c2ecf20Sopenharmony_ci static const u32 subset[4] = { 6818c2ecf20Sopenharmony_ci 0xe92d0000, /* STMDB sp!,{registers} */ 6828c2ecf20Sopenharmony_ci 0xe92d4000, /* STMDB sp!,{registers,lr} */ 6838c2ecf20Sopenharmony_ci 0xe8bd0000, /* LDMIA sp!,{registers} */ 6848c2ecf20Sopenharmony_ci 0xe8bd8000 /* LDMIA sp!,{registers,pc} */ 6858c2ecf20Sopenharmony_ci }; 6868c2ecf20Sopenharmony_ci return subset[(L<<1) | ((tinstr & (1<<8)) >> 8)] | 6878c2ecf20Sopenharmony_ci (tinstr & 255); /* register_list */ 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci fallthrough; /* for illegal instruction case */ 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci default: 6928c2ecf20Sopenharmony_ci return BAD_INSTR; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/* 6978c2ecf20Sopenharmony_ci * Convert Thumb-2 32 bit LDM, STM, LDRD, STRD to equivalent instruction 6988c2ecf20Sopenharmony_ci * handlable by ARM alignment handler, also find the corresponding handler, 6998c2ecf20Sopenharmony_ci * so that we can reuse ARM userland alignment fault fixups for Thumb. 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * @pinstr: original Thumb-2 instruction; returns new handlable instruction 7028c2ecf20Sopenharmony_ci * @regs: register context. 7038c2ecf20Sopenharmony_ci * @poffset: return offset from faulted addr for later writeback 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * NOTES: 7068c2ecf20Sopenharmony_ci * 1. Comments below refer to ARMv7 DDI0406A Thumb Instruction sections. 7078c2ecf20Sopenharmony_ci * 2. Register name Rt from ARMv7 is same as Rd from ARMv6 (Rd is Rt) 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_cistatic void * 7108c2ecf20Sopenharmony_cido_alignment_t32_to_handler(u32 *pinstr, struct pt_regs *regs, 7118c2ecf20Sopenharmony_ci union offset_union *poffset) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci u32 instr = *pinstr; 7148c2ecf20Sopenharmony_ci u16 tinst1 = (instr >> 16) & 0xffff; 7158c2ecf20Sopenharmony_ci u16 tinst2 = instr & 0xffff; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci switch (tinst1 & 0xffe0) { 7188c2ecf20Sopenharmony_ci /* A6.3.5 Load/Store multiple */ 7198c2ecf20Sopenharmony_ci case 0xe880: /* STM/STMIA/STMEA,LDM/LDMIA, PUSH/POP T2 */ 7208c2ecf20Sopenharmony_ci case 0xe8a0: /* ...above writeback version */ 7218c2ecf20Sopenharmony_ci case 0xe900: /* STMDB/STMFD, LDMDB/LDMEA */ 7228c2ecf20Sopenharmony_ci case 0xe920: /* ...above writeback version */ 7238c2ecf20Sopenharmony_ci /* no need offset decision since handler calculates it */ 7248c2ecf20Sopenharmony_ci return do_alignment_ldmstm; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci case 0xf840: /* POP/PUSH T3 (single register) */ 7278c2ecf20Sopenharmony_ci if (RN_BITS(instr) == 13 && (tinst2 & 0x09ff) == 0x0904) { 7288c2ecf20Sopenharmony_ci u32 L = !!(LDST_L_BIT(instr)); 7298c2ecf20Sopenharmony_ci const u32 subset[2] = { 7308c2ecf20Sopenharmony_ci 0xe92d0000, /* STMDB sp!,{registers} */ 7318c2ecf20Sopenharmony_ci 0xe8bd0000, /* LDMIA sp!,{registers} */ 7328c2ecf20Sopenharmony_ci }; 7338c2ecf20Sopenharmony_ci *pinstr = subset[L] | (1<<RD_BITS(instr)); 7348c2ecf20Sopenharmony_ci return do_alignment_ldmstm; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci /* Else fall through for illegal instruction case */ 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* A6.3.6 Load/store double, STRD/LDRD(immed, lit, reg) */ 7408c2ecf20Sopenharmony_ci case 0xe860: 7418c2ecf20Sopenharmony_ci case 0xe960: 7428c2ecf20Sopenharmony_ci case 0xe8e0: 7438c2ecf20Sopenharmony_ci case 0xe9e0: 7448c2ecf20Sopenharmony_ci poffset->un = (tinst2 & 0xff) << 2; 7458c2ecf20Sopenharmony_ci fallthrough; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci case 0xe940: 7488c2ecf20Sopenharmony_ci case 0xe9c0: 7498c2ecf20Sopenharmony_ci return do_alignment_ldrdstrd; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* 7528c2ecf20Sopenharmony_ci * No need to handle load/store instructions up to word size 7538c2ecf20Sopenharmony_ci * since ARMv6 and later CPUs can perform unaligned accesses. 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_ci default: 7568c2ecf20Sopenharmony_ci break; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci return NULL; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int alignment_get_arm(struct pt_regs *regs, u32 *ip, u32 *inst) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci u32 instr = 0; 7648c2ecf20Sopenharmony_ci int fault; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (user_mode(regs)) 7678c2ecf20Sopenharmony_ci fault = get_user(instr, ip); 7688c2ecf20Sopenharmony_ci else 7698c2ecf20Sopenharmony_ci fault = get_kernel_nofault(instr, ip); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci *inst = __mem_to_opcode_arm(instr); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return fault; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int alignment_get_thumb(struct pt_regs *regs, u16 *ip, u16 *inst) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci u16 instr = 0; 7798c2ecf20Sopenharmony_ci int fault; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (user_mode(regs)) 7828c2ecf20Sopenharmony_ci fault = get_user(instr, ip); 7838c2ecf20Sopenharmony_ci else 7848c2ecf20Sopenharmony_ci fault = get_kernel_nofault(instr, ip); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci *inst = __mem_to_opcode_thumb16(instr); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return fault; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic int 7928c2ecf20Sopenharmony_cido_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci union offset_union offset; 7958c2ecf20Sopenharmony_ci unsigned long instrptr; 7968c2ecf20Sopenharmony_ci int (*handler)(unsigned long addr, u32 instr, struct pt_regs *regs); 7978c2ecf20Sopenharmony_ci unsigned int type; 7988c2ecf20Sopenharmony_ci u32 instr = 0; 7998c2ecf20Sopenharmony_ci u16 tinstr = 0; 8008c2ecf20Sopenharmony_ci int isize = 4; 8018c2ecf20Sopenharmony_ci int thumb2_32b = 0; 8028c2ecf20Sopenharmony_ci int fault; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (interrupts_enabled(regs)) 8058c2ecf20Sopenharmony_ci local_irq_enable(); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci instrptr = instruction_pointer(regs); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (thumb_mode(regs)) { 8108c2ecf20Sopenharmony_ci u16 *ptr = (u16 *)(instrptr & ~1); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci fault = alignment_get_thumb(regs, ptr, &tinstr); 8138c2ecf20Sopenharmony_ci if (!fault) { 8148c2ecf20Sopenharmony_ci if (cpu_architecture() >= CPU_ARCH_ARMv7 && 8158c2ecf20Sopenharmony_ci IS_T32(tinstr)) { 8168c2ecf20Sopenharmony_ci /* Thumb-2 32-bit */ 8178c2ecf20Sopenharmony_ci u16 tinst2; 8188c2ecf20Sopenharmony_ci fault = alignment_get_thumb(regs, ptr + 1, &tinst2); 8198c2ecf20Sopenharmony_ci instr = __opcode_thumb32_compose(tinstr, tinst2); 8208c2ecf20Sopenharmony_ci thumb2_32b = 1; 8218c2ecf20Sopenharmony_ci } else { 8228c2ecf20Sopenharmony_ci isize = 2; 8238c2ecf20Sopenharmony_ci instr = thumb2arm(tinstr); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci } else { 8278c2ecf20Sopenharmony_ci fault = alignment_get_arm(regs, (void *)instrptr, &instr); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (fault) { 8318c2ecf20Sopenharmony_ci type = TYPE_FAULT; 8328c2ecf20Sopenharmony_ci goto bad_or_fault; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (user_mode(regs)) 8368c2ecf20Sopenharmony_ci goto user; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ai_sys += 1; 8398c2ecf20Sopenharmony_ci ai_sys_last_pc = (void *)instruction_pointer(regs); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci fixup: 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci regs->ARM_pc += isize; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci switch (CODING_BITS(instr)) { 8468c2ecf20Sopenharmony_ci case 0x00000000: /* 3.13.4 load/store instruction extensions */ 8478c2ecf20Sopenharmony_ci if (LDSTHD_I_BIT(instr)) 8488c2ecf20Sopenharmony_ci offset.un = (instr & 0xf00) >> 4 | (instr & 15); 8498c2ecf20Sopenharmony_ci else 8508c2ecf20Sopenharmony_ci offset.un = regs->uregs[RM_BITS(instr)]; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if ((instr & 0x000000f0) == 0x000000b0 || /* LDRH, STRH */ 8538c2ecf20Sopenharmony_ci (instr & 0x001000f0) == 0x001000f0) /* LDRSH */ 8548c2ecf20Sopenharmony_ci handler = do_alignment_ldrhstrh; 8558c2ecf20Sopenharmony_ci else if ((instr & 0x001000f0) == 0x000000d0 || /* LDRD */ 8568c2ecf20Sopenharmony_ci (instr & 0x001000f0) == 0x000000f0) /* STRD */ 8578c2ecf20Sopenharmony_ci handler = do_alignment_ldrdstrd; 8588c2ecf20Sopenharmony_ci else if ((instr & 0x01f00ff0) == 0x01000090) /* SWP */ 8598c2ecf20Sopenharmony_ci goto swp; 8608c2ecf20Sopenharmony_ci else 8618c2ecf20Sopenharmony_ci goto bad; 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci case 0x04000000: /* ldr or str immediate */ 8658c2ecf20Sopenharmony_ci if (COND_BITS(instr) == 0xf0000000) /* NEON VLDn, VSTn */ 8668c2ecf20Sopenharmony_ci goto bad; 8678c2ecf20Sopenharmony_ci offset.un = OFFSET_BITS(instr); 8688c2ecf20Sopenharmony_ci handler = do_alignment_ldrstr; 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci case 0x06000000: /* ldr or str register */ 8728c2ecf20Sopenharmony_ci offset.un = regs->uregs[RM_BITS(instr)]; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (IS_SHIFT(instr)) { 8758c2ecf20Sopenharmony_ci unsigned int shiftval = SHIFT_BITS(instr); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci switch(SHIFT_TYPE(instr)) { 8788c2ecf20Sopenharmony_ci case SHIFT_LSL: 8798c2ecf20Sopenharmony_ci offset.un <<= shiftval; 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci case SHIFT_LSR: 8838c2ecf20Sopenharmony_ci offset.un >>= shiftval; 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci case SHIFT_ASR: 8878c2ecf20Sopenharmony_ci offset.sn >>= shiftval; 8888c2ecf20Sopenharmony_ci break; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci case SHIFT_RORRRX: 8918c2ecf20Sopenharmony_ci if (shiftval == 0) { 8928c2ecf20Sopenharmony_ci offset.un >>= 1; 8938c2ecf20Sopenharmony_ci if (regs->ARM_cpsr & PSR_C_BIT) 8948c2ecf20Sopenharmony_ci offset.un |= 1 << 31; 8958c2ecf20Sopenharmony_ci } else 8968c2ecf20Sopenharmony_ci offset.un = offset.un >> shiftval | 8978c2ecf20Sopenharmony_ci offset.un << (32 - shiftval); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci handler = do_alignment_ldrstr; 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci case 0x08000000: /* ldm or stm, or thumb-2 32bit instruction */ 9058c2ecf20Sopenharmony_ci if (thumb2_32b) { 9068c2ecf20Sopenharmony_ci offset.un = 0; 9078c2ecf20Sopenharmony_ci handler = do_alignment_t32_to_handler(&instr, regs, &offset); 9088c2ecf20Sopenharmony_ci } else { 9098c2ecf20Sopenharmony_ci offset.un = 0; 9108c2ecf20Sopenharmony_ci handler = do_alignment_ldmstm; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci default: 9158c2ecf20Sopenharmony_ci goto bad; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (!handler) 9198c2ecf20Sopenharmony_ci goto bad; 9208c2ecf20Sopenharmony_ci type = handler(addr, instr, regs); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (type == TYPE_ERROR || type == TYPE_FAULT) { 9238c2ecf20Sopenharmony_ci regs->ARM_pc -= isize; 9248c2ecf20Sopenharmony_ci goto bad_or_fault; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (type == TYPE_LDST) 9288c2ecf20Sopenharmony_ci do_alignment_finish_ldst(addr, instr, regs, offset); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (thumb_mode(regs)) 9318c2ecf20Sopenharmony_ci regs->ARM_cpsr = it_advance(regs->ARM_cpsr); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci return 0; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci bad_or_fault: 9368c2ecf20Sopenharmony_ci if (type == TYPE_ERROR) 9378c2ecf20Sopenharmony_ci goto bad; 9388c2ecf20Sopenharmony_ci /* 9398c2ecf20Sopenharmony_ci * We got a fault - fix it up, or die. 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_ci do_bad_area(addr, fsr, regs); 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci swp: 9458c2ecf20Sopenharmony_ci pr_err("Alignment trap: not handling swp instruction\n"); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci bad: 9488c2ecf20Sopenharmony_ci /* 9498c2ecf20Sopenharmony_ci * Oops, we didn't handle the instruction. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ci pr_err("Alignment trap: not handling instruction " 9528c2ecf20Sopenharmony_ci "%0*x at [<%08lx>]\n", 9538c2ecf20Sopenharmony_ci isize << 1, 9548c2ecf20Sopenharmony_ci isize == 2 ? tinstr : instr, instrptr); 9558c2ecf20Sopenharmony_ci ai_skipped += 1; 9568c2ecf20Sopenharmony_ci return 1; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci user: 9598c2ecf20Sopenharmony_ci ai_user += 1; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (ai_usermode & UM_WARN) 9628c2ecf20Sopenharmony_ci printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*x " 9638c2ecf20Sopenharmony_ci "Address=0x%08lx FSR 0x%03x\n", current->comm, 9648c2ecf20Sopenharmony_ci task_pid_nr(current), instrptr, 9658c2ecf20Sopenharmony_ci isize << 1, 9668c2ecf20Sopenharmony_ci isize == 2 ? tinstr : instr, 9678c2ecf20Sopenharmony_ci addr, fsr); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (ai_usermode & UM_FIXUP) 9708c2ecf20Sopenharmony_ci goto fixup; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (ai_usermode & UM_SIGNAL) { 9738c2ecf20Sopenharmony_ci force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr); 9748c2ecf20Sopenharmony_ci } else { 9758c2ecf20Sopenharmony_ci /* 9768c2ecf20Sopenharmony_ci * We're about to disable the alignment trap and return to 9778c2ecf20Sopenharmony_ci * user space. But if an interrupt occurs before actually 9788c2ecf20Sopenharmony_ci * reaching user space, then the IRQ vector entry code will 9798c2ecf20Sopenharmony_ci * notice that we were still in kernel space and therefore 9808c2ecf20Sopenharmony_ci * the alignment trap won't be re-enabled in that case as it 9818c2ecf20Sopenharmony_ci * is presumed to be always on from kernel space. 9828c2ecf20Sopenharmony_ci * Let's prevent that race by disabling interrupts here (they 9838c2ecf20Sopenharmony_ci * are disabled on the way back to user space anyway in 9848c2ecf20Sopenharmony_ci * entry-common.S) and disable the alignment trap only if 9858c2ecf20Sopenharmony_ci * there is no work pending for this thread. 9868c2ecf20Sopenharmony_ci */ 9878c2ecf20Sopenharmony_ci raw_local_irq_disable(); 9888c2ecf20Sopenharmony_ci if (!(current_thread_info()->flags & _TIF_WORK_MASK)) 9898c2ecf20Sopenharmony_ci set_cr(cr_no_alignment); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic int __init noalign_setup(char *__unused) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci set_cr(__clear_cr(CR_A)); 9988c2ecf20Sopenharmony_ci return 1; 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci__setup("noalign", noalign_setup); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci/* 10038c2ecf20Sopenharmony_ci * This needs to be done after sysctl_init, otherwise sys/ will be 10048c2ecf20Sopenharmony_ci * overwritten. Actually, this shouldn't be in sys/ at all since 10058c2ecf20Sopenharmony_ci * it isn't a sysctl, and it doesn't contain sysctl information. 10068c2ecf20Sopenharmony_ci * We now locate it in /proc/cpu/alignment instead. 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_cistatic int __init alignment_init(void) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 10118c2ecf20Sopenharmony_ci struct proc_dir_entry *res; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci res = proc_create("cpu/alignment", S_IWUSR | S_IRUGO, NULL, 10148c2ecf20Sopenharmony_ci &alignment_proc_ops); 10158c2ecf20Sopenharmony_ci if (!res) 10168c2ecf20Sopenharmony_ci return -ENOMEM; 10178c2ecf20Sopenharmony_ci#endif 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (cpu_is_v6_unaligned()) { 10208c2ecf20Sopenharmony_ci set_cr(__clear_cr(CR_A)); 10218c2ecf20Sopenharmony_ci ai_usermode = safe_usermode(ai_usermode, false); 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci cr_no_alignment = get_cr() & ~CR_A; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci hook_fault_code(FAULT_CODE_ALIGNMENT, do_alignment, SIGBUS, BUS_ADRALN, 10278c2ecf20Sopenharmony_ci "alignment exception"); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* 10308c2ecf20Sopenharmony_ci * ARMv6K and ARMv7 use fault status 3 (0b00011) as Access Flag section 10318c2ecf20Sopenharmony_ci * fault, not as alignment error. 10328c2ecf20Sopenharmony_ci * 10338c2ecf20Sopenharmony_ci * TODO: handle ARMv6K properly. Runtime check for 'K' extension is 10348c2ecf20Sopenharmony_ci * needed. 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci if (cpu_architecture() <= CPU_ARCH_ARMv6) { 10378c2ecf20Sopenharmony_ci hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN, 10388c2ecf20Sopenharmony_ci "alignment exception"); 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci return 0; 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cifs_initcall(alignment_init); 1045