[patch 08/12] sparc64: Optimized immediate value implementation.

Mathieu Desnoyers
2009-09-24T14:43:02Z

[-- Attachment #1: sparc64-immedate-values.patch --]
[-- Type: text/plain, Size: 5413 bytes --]

commit f2b14974b823a9cd9b6f5c0d423945caa15de8a2
Author: David S. Miller <davem@davemloft.net>
Date: Tue May 13 04:29:30 2008 -0700

We can only do byte sized values currently.

In order to support even 16-bit immediates we would need a 2
instruction sequence.

I believe that can be made to work with a suitable breakpoint or some
other kind of special patching sequence, but that isn't attempted
here.

[edit by Mathieu Desnoyers]
Use _ASM_PTR and _ASM_UAPTR 32/64 bits compatibility macros.

Use "unsigned long" type to encode pointers, with uaxword on sparc64 and uaword
on sparc32.

Disable immediate values on gcc < 4.0.0, because it seems to have problem with
passing pointers as "i" inline asm constraint.

Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
---
arch/sparc/Kconfig | 1
arch/sparc/Makefile | 4 +++
arch/sparc/include/asm/immediate.h | 40 ++++++++++++++++++++++++++++++
arch/sparc/kernel/Makefile | 1
arch/sparc/kernel/immediate.c | 48 +++++++++++++++++++++++++++++++++++++
5 files changed, 94 insertions(+)

Index: linux.trees.git/arch/sparc/Kconfig
===================================================================
--- linux.trees.git.orig/arch/sparc/Kconfig 2009-09-24 08:52:41.000000000 -0400
+++ linux.trees.git/arch/sparc/Kconfig 2009-09-24 09:01:13.000000000 -0400
@@ -48,6 +48,7 @@ config SPARC64
select RTC_DRV_SUN4V
select RTC_DRV_STARFIRE
select HAVE_PERF_EVENTS
+ select HAVE_IMMEDIATE

config ARCH_DEFCONFIG
string
Index: linux.trees.git/arch/sparc/kernel/Makefile
===================================================================
--- linux.trees.git.orig/arch/sparc/kernel/Makefile 2009-09-24 08:52:41.000000000 -0400
+++ linux.trees.git/arch/sparc/kernel/Makefile 2009-09-24 09:01:55.000000000 -0400
@@ -91,6 +91,7 @@ obj-$(CONFIG_SPARC64_PCI) += pci_sun4
obj-$(CONFIG_PCI_MSI) += pci_msi.o

obj-$(CONFIG_COMPAT) += sys32.o sys_sparc32.o signal32.o
+obj-$(USE_IMMEDIATE) += immediate.o

# sparc64 cpufreq
obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
Index: linux.trees.git/arch/sparc/kernel/immediate.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.trees.git/arch/sparc/kernel/immediate.c 2009-09-24 09:00:57.000000000 -0400
@@ -0,0 +1,48 @@
+#include <linux/module.h>
+#include <linux/immediate.h>
+#include <linux/string.h>
+#include <linux/kprobes.h>
+
+#include <asm/system.h>
+
+int arch_imv_update(const struct __imv *imv, int early)
+{
+ unsigned long imv_vaddr = imv->imv;
+ unsigned long var_vaddr = imv->var;
+ u32 insn, *ip = (u32 *) imv_vaddr;
+
+ insn = *ip;
+
+#ifdef CONFIG_KPROBES
+ switch (imv->size) {
+ case 1:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (unlikely(!early &&
+ (insn == BREAKPOINT_INSTRUCTION ||
+ insn == BREAKPOINT_INSTRUCTION_2))) {
+ printk(KERN_WARNING "Immediate value in conflict with kprobe. "
+ "Variable at %p, "
+ "instruction at %p, size %u\n",
+ ip, (void *)var_vaddr, imv->size);
+ return -EBUSY;
+ }
+#endif
+
+ switch (imv->size) {
+ case 1:
+ if ((insn & 0x1fff) == *(uint8_t *)var_vaddr)
+ return 0;
+ insn &= ~0x00001fff;
+ insn |= (u32) (*(uint8_t *)var_vaddr);
+ break;
+ default:
+ return -EINVAL;
+ }
+ *ip = insn;
+ flushi(ip);
+ return 0;
+}
Index: linux.trees.git/arch/sparc/include/asm/immediate.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.trees.git/arch/sparc/include/asm/immediate.h 2009-09-24 09:00:57.000000000 -0400
@@ -0,0 +1,40 @@
+#ifndef _ASM_SPARC_IMMEDIATE_H
+#define _ASM_SPARC_IMMEDIATE_H
+
+#include <asm/asm.h>
+
+struct __imv {
+ unsigned long var;
+ unsigned long imv;
+ unsigned char size;
+} __attribute__ ((packed));
+
+#define imv_read(name) \
+ ({ \
+ __typeof__(name##__imv) value; \
+ BUILD_BUG_ON(sizeof(value) > 8); \
+ switch (sizeof(value)) { \
+ case 1: \
+ asm(".section __imv,\"aw\",@progbits\n\t" \
+ _ASM_UAPTR " %c1, 1f\n\t" \
+ ".byte 1\n\t" \
+ ".previous\n\t" \
+ "1: mov 0, %0\n\t" \
+ : "=r" (value) \
+ : "i" (&name##__imv)); \
+ break; \
+ case 2: \
+ case 4: \
+ case 8: \
+ value = name##__imv; \
+ break; \
+ }; \
+ value; \
+ })
+
+#define imv_cond(name) imv_read(name)
+#define imv_cond_end()
+
+extern int arch_imv_update(const struct __imv *imv, int early);
+
+#endif /* _ASM_SPARC_IMMEDIATE_H */
Index: linux.trees.git/arch/sparc/Makefile
===================================================================
--- linux.trees.git.orig/arch/sparc/Makefile 2009-09-24 08:52:41.000000000 -0400
+++ linux.trees.git/arch/sparc/Makefile 2009-09-24 09:00:57.000000000 -0400
@@ -57,6 +57,10 @@ KBUILD_CFLAGS += -m64 -pipe -mno-fpu -mc
KBUILD_CFLAGS += $(call cc-option,-mtune=ultrasparc3)
KBUILD_AFLAGS += -m64 -mcpu=ultrasparc -Wa,--undeclared-regs

+# gcc 3.x has problems with passing symbol+offset in
+# asm "i" constraint.
+export USE_IMMEDIATE := $(call cc-ifversion, -ge, 0400, $(CONFIG_IMMEDIATE))
+
ifeq ($(CONFIG_MCOUNT),y)
KBUILD_CFLAGS += -pg
endif