From patchwork Sat Sep 22 11:49:23 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Murzin X-Patchwork-Id: 1494331 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id F1646DF2D2 for ; Sat, 22 Sep 2012 12:03:50 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TFOPD-0000DT-At; Sat, 22 Sep 2012 12:01:55 +0000 Received: from mail-lb0-f177.google.com ([209.85.217.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TFOPA-0000DF-I0 for linux-arm-kernel@lists.infradead.org; Sat, 22 Sep 2012 12:01:53 +0000 Received: by lbbgf7 with SMTP id gf7so4881401lbb.36 for ; Sat, 22 Sep 2012 05:01:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer; bh=FkqdL/0t+b91aEK4MI1ZT3U4uiYkNuDuyYbSuTJk7UQ=; b=M4NCmvl9rbM/BwdmbS6o8qA7Swcyuxtm4nmHr6DS1CVjhRmnZRCfyZdsLUH6JqAG4g HHU5ftIkamrj0QKn3Tt/BfaK+abmO/KcQDkeFRbu//vlN4elCjK7CcH6kJ+1SnHkYUie Q0PNA6/oXINDhAhFYjm3eJ2gsYwfAEQWV+LJnc1Z2QPJxEzpZDdy3ng7i/vcGW+Ngo/P tVa7lnTqOfjXyjp9uvEexqJVi7XpT5B6O5pMduwBRDVlun5WZBPyj4/JBC1h9PfxJ35Q ds8KTu53lwlkvuUFffL2A5A0L36LHDfO4Cfcydgul0hsfwLOAbaAHY/XaaAW9q2uOJUK 0quQ== Received: by 10.152.132.202 with SMTP id ow10mr6342582lab.51.1348314833004; Sat, 22 Sep 2012 04:53:53 -0700 (PDT) Received: from localhost ([217.118.66.85]) by mx.google.com with ESMTPS id u10sm2896627lbf.11.2012.09.22.04.53.47 (version=TLSv1/SSLv3 cipher=OTHER); Sat, 22 Sep 2012 04:53:52 -0700 (PDT) From: Vladimir Murzin To: linux-arm-kernel@lists.infradead.org Subject: [PATCH] arm: add SDIV/UDIV emulation for ARMv7 processors Date: Sat, 22 Sep 2012 15:49:23 +0400 Message-Id: <1348314563-8436-1-git-send-email-murzin.v@gmail.com> X-Mailer: git-send-email 1.7.8.6 X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.217.177 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (murzin.v[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Vladimir Murzin , linaro-kernel@lists.linaro.org, linux@arm.linux.org.uk X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The Virtualization Extensions introduce the requirement for an ARMv7-A implementation to include SDIV and UDIV. Any implementation of the Virtualization Extensions must include the SDIV and UDIV instructions in the Thumb and ARM instruction sets. In an ARMv7-A implementation that does not include the Virtualization Extensions, it is IMPLEMENTATION DEFINED whether: * SDIV and UDIV are not implemented * SDIV and UDIV are implemented only in the Thumb instruction set * SDIV and UDIV are implemented in the Thumb and ARM instruction sets. This patch adds a handler to trap and emulate unimplemented SDIV and UDIV instructions in ARM and Thumb modes. Also some basic statistic is exported via /proc/cpu/idiv_emulation Signed-off-by: Vladimir Murzin --- arch/arm/kernel/Makefile | 1 + arch/arm/kernel/idiv_emulate.c | 194 ++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 26 ++++++ 3 files changed, 221 insertions(+), 0 deletions(-) create mode 100644 arch/arm/kernel/idiv_emulate.c -- 1.7.8.6 diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 7ad2d5c..057a630 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_OF) += devtree.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o CFLAGS_swp_emulate.o := -Wa,-march=armv7-a +obj-$(CONFIG_IDIV_EMULATE) += idiv_emulate.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o diff --git a/arch/arm/kernel/idiv_emulate.c b/arch/arm/kernel/idiv_emulate.c new file mode 100644 index 0000000..f021d0e --- /dev/null +++ b/arch/arm/kernel/idiv_emulate.c @@ -0,0 +1,195 @@ +/* + * linux/arch/arm/kernel/idiv_emulate.c + * + * This code is based on swp_emulate.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Implements emulation of the SDIV/UDIV instructions. They are defined for + * ARMv7-R and ARMv7-M profiles (in Thumb state only) and are UNDEFINED in the + * ARMv7-A profile. SDIV/UDIV are present by default on Cortex-A15. + * + * This emulation allow using integer divide instructions in case hardware is + * not presented + * + * Syntax of SDIV/UDIV instructions: SDIV/UDIV ,, + * Where: Rd = the destination register. + * Rn = the register that contains the dividend. + * Rm = the register that contains the divisor. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Macros/defines for extracting register numbers from instruction. + */ +#define EXTRACT_REG_NUM(instruction, offset) \ + (((instruction) & (0xf << (offset))) >> (offset)) + +/* + * Offsets for ARM mode + */ +#define RD_OFFSET 16 +#define RN_OFFSET 0 +#define RM_OFFSET 8 + +/* + * Offsets for Thumb mode + */ +#define RD_T_OFFSET 8 +#define RN_T_OFFSET 16 +#define RM_T_OFFSET 0 + +#define TYPE_OF_DIV (1 << 21) + +static unsigned long sdivcounter; +static unsigned long udivcounter; +static pid_t previous_pid; + +#ifdef CONFIG_PROC_FS +static int proc_read_status(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *p = page; + int len; + + p += sprintf(p, "Emulated UDIV:\t\t%lu\n", udivcounter); + p += sprintf(p, "Emulated SDIV:\t\t%lu\n", sdivcounter); + if (previous_pid != 0) + p += sprintf(p, "Last process:\t\t%d\n", previous_pid); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} +#endif + +static u32 emulate_udiv(u32 n, u32 base) +{ + udivcounter++; + + return n/base; +} + +static s32 emulate_sdiv(s32 n, s32 base) +{ + sdivcounter++; + + return n/base; +} + +static int idiv_handler(struct pt_regs *regs, unsigned int instr) +{ + long dividend, divisor, dest, res; + + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc); + + res = arm_check_condition(instr, regs->ARM_cpsr); + + switch (res) { + case ARM_OPCODE_CONDTEST_PASS: + break; + case ARM_OPCODE_CONDTEST_FAIL: + /* Condition failed - return to next instruction */ + regs->ARM_pc += 4; + return 0; + case ARM_OPCODE_CONDTEST_UNCOND: + if (!thumb_mode(regs)) + return -EFAULT; + break; + default: + return -EINVAL; + } + + if (current->pid != previous_pid) { + pr_debug("\"%s\" (%ld) uses idiv instruction\n", + current->comm, (unsigned long)current->pid); + previous_pid = current->pid; + } + + if (!thumb_mode(regs)) { + dividend = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)]; + divisor = regs->uregs[EXTRACT_REG_NUM(instr, RM_OFFSET)]; + dest = EXTRACT_REG_NUM(instr, RD_OFFSET); + } else { + dividend = regs->uregs[EXTRACT_REG_NUM(instr, RN_T_OFFSET)]; + divisor = regs->uregs[EXTRACT_REG_NUM(instr, RM_T_OFFSET)]; + dest = EXTRACT_REG_NUM(instr, RD_T_OFFSET); + } + +/* + * In an ARMv7-A profile implementation that supports the SDIV and UDIV + * instructions, divide-by-zero always returns a zero result. + * In fact, integer division emulation provided by gcc lib has already handle + * division by zero case sending the signal to the caused process. Emulate this + * behavior here as well. + */ + if (!divisor) { + siginfo_t info; + + info.si_code = FPE_INTDIV; + info.si_signo = SIGFPE; + info.si_errno = 0; + + arm_notify_die("Division by zero", regs, &info, 0, 0); + + goto out; + } + + if (instr & TYPE_OF_DIV) + res = emulate_udiv((u32)dividend, (u32)divisor); + else + res = emulate_sdiv((s32)dividend, (s32)divisor); + + regs->ARM_pc += 4; + regs->uregs[dest] = res; + +out: + return 0; +} + +static struct undef_hook idiv_hook = { + .instr_mask = 0x0310f010, + .instr_val = 0x0310f010, + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = idiv_handler +}; + +static int __init idiv_emulation_init(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + res = create_proc_entry("cpu/idiv_emulation", S_IRUGO, NULL); + + if (!res) + return -ENOMEM; + + res->read_proc = proc_read_status; +#endif /* CONFIG_PROC_FS */ + + pr_notice("Registering SDIV/UDIV emulation handler\n"); + + register_undef_hook(&idiv_hook); + + return 0; +} + +late_initcall(idiv_emulation_init); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 101b968..8e2fa94 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -651,6 +651,32 @@ config SWP_EMULATE If unsure, say Y. +config IDIV_EMULATE + bool "Emulate SDIV/UDIV instructions" + depends on !CPU_USE_DOMAINS && CPU_V7 + select HAVE_PROC_CPU if PROC_FS + default y if SMP + help + + The Virtualization Extensions introduce the requirement for an + ARMv7-A implementation to include SDIV and UDIV. Any + implementation of the Virtualization Extensions must include + the SDIV and UDIV instructions in the Thumb and ARM + instruction sets. + + In an ARMv7-A implementation that does not include the + Virtualization Extensions, it is IMPLEMENTATION DEFINED whether: + * SDIV and UDIV are not implemented + * SDIV and UDIV are implemented only in the Thumb instruction set + * SDIV and UDIV are implemented in the Thumb and ARM + instruction sets. + + This option allows to handle exeptions due to unimplemented + SDIV and UDIV instructions in ARM and Thumb modes. + + If unsure, say Y. + + config CPU_BIG_ENDIAN bool "Build big-endian kernel" depends on ARCH_SUPPORTS_BIG_ENDIAN