[PATCH 1/8] arm64: add kernel emulation for AES instructions

2014-01-06 Thread Ard Biesheuvel
NOTE: this patch is not intended for merging upstream, but is only
included as a bonus so mere mortals (i.e., those whose ARMv8 system
does not support the AES crypto instructions) can test the series
if they like.

Signed-off-by: Ard Biesheuvel 
---
 arch/arm64/Makefile|   1 +
 arch/arm64/emu/Makefile|  11 ++
 arch/arm64/emu/ce-aes.c| 331 +
 arch/arm64/include/asm/traps.h |  10 ++
 arch/arm64/kernel/entry.S  |   4 +-
 arch/arm64/kernel/traps.c  |  49 ++
 6 files changed, 405 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/emu/Makefile
 create mode 100644 arch/arm64/emu/ce-aes.c

diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 2fceb71ac3b7..e0b75464b7f1 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -42,6 +42,7 @@ TEXT_OFFSET := 0x0008
 
 export TEXT_OFFSET GZFLAGS
 
+core-y += arch/arm64/emu/
 core-y += arch/arm64/kernel/ arch/arm64/mm/
 core-$(CONFIG_KVM) += arch/arm64/kvm/
 core-$(CONFIG_XEN) += arch/arm64/xen/
diff --git a/arch/arm64/emu/Makefile b/arch/arm64/emu/Makefile
new file mode 100644
index ..224b4e19ff6f
--- /dev/null
+++ b/arch/arm64/emu/Makefile
@@ -0,0 +1,11 @@
+#
+# linux/arch/arm64/emu/Makefile
+#
+# Copyright (C) 2013 Linaro Ltd 
+#
+# 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.
+#
+
+obj-y += ce-aes.o
diff --git a/arch/arm64/emu/ce-aes.c b/arch/arm64/emu/ce-aes.c
new file mode 100644
index ..d50fadbd7336
--- /dev/null
+++ b/arch/arm64/emu/ce-aes.c
@@ -0,0 +1,331 @@
+/*
+ * ce-aes.c - emulate aese/aesd/aesmc/aesimc instructions
+ *
+ * Copyright (C) 2013 Linaro Ltd 
+ *
+ * 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.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+union AES_STATE {
+   u8  bytes[16];
+   u32 cols[4];
+   u64 l[2];
+} __aligned(8);
+
+static void add_sub_shift(union AES_STATE *st, union AES_STATE *rk, int inv);
+static void mix_columns(union AES_STATE *out, union AES_STATE *in, int inv);
+
+#define REG_ACCESS(op, r, mem) \
+   do { case r: asm(#op " {v" #r ".16b}, [%0]" : : "r"(mem)); goto out; \
+   } while (0)
+
+#define REG_SWITCH(reg, op, m) do { switch (reg) { \
+   REG_ACCESS(op,  0, m);  REG_ACCESS(op,  1, m);  REG_ACCESS(op,  2, m); \
+   REG_ACCESS(op,  3, m);  REG_ACCESS(op,  4, m);  REG_ACCESS(op,  5, m); \
+   REG_ACCESS(op,  6, m);  REG_ACCESS(op,  7, m);  REG_ACCESS(op,  8, m); \
+   REG_ACCESS(op,  9, m);  REG_ACCESS(op, 10, m);  REG_ACCESS(op, 11, m); \
+   REG_ACCESS(op, 12, m);  REG_ACCESS(op, 13, m);  REG_ACCESS(op, 14, m); \
+   REG_ACCESS(op, 15, m);  REG_ACCESS(op, 16, m);  REG_ACCESS(op, 17, m); \
+   REG_ACCESS(op, 18, m);  REG_ACCESS(op, 19, m);  REG_ACCESS(op, 20, m); \
+   REG_ACCESS(op, 21, m);  REG_ACCESS(op, 22, m);  REG_ACCESS(op, 23, m); \
+   REG_ACCESS(op, 24, m);  REG_ACCESS(op, 25, m);  REG_ACCESS(op, 26, m); \
+   REG_ACCESS(op, 27, m);  REG_ACCESS(op, 28, m);  REG_ACCESS(op, 29, m); \
+   REG_ACCESS(op, 30, m);  REG_ACCESS(op, 31, m); \
+   } out:; } while (0)
+
+static void load_neon_reg(union AES_STATE *st, int reg)
+{
+   REG_SWITCH(reg, st1, st->bytes);
+}
+
+static void save_neon_reg(union AES_STATE *st, int reg)
+{
+   REG_SWITCH(reg, ld1, st->bytes);
+}
+
+static void aesce_do_emulate(unsigned int instr)
+{
+   enum { AESE, AESD, AESMC, AESIMC } kind = (instr >> 12) & 3;
+   int rn = (instr >> 5) & 0x1f;
+   int rd = instr & 0x1f;
+   union AES_STATE in, out;
+
+   load_neon_reg(, rn);
+
+   switch (kind) {
+   case AESE:
+   case AESD:
+   load_neon_reg(, rd);
+   add_sub_shift(, , kind & 1);
+   break;
+   case AESMC:
+   case AESIMC:
+   mix_columns(, , kind & 1);
+   break;
+   }
+   save_neon_reg(, rd);
+}
+
+static int aesce_emu_instr(struct pt_regs *regs, unsigned int instr);
+
+static struct undef_hook aesce_emu_uh = {
+   .instr_val  = 0x4e284800,
+   .instr_mask = 0xcc00,
+   .fn = aesce_emu_instr,
+};
+
+static int aesce_emu_instr(struct pt_regs *regs, unsigned int instr)
+{
+   do {
+   aesce_do_emulate(instr);
+   regs->pc += 4;
+   get_user(instr, (u32 __user *)regs->pc);
+   } while ((instr & aesce_emu_uh.instr_mask) == aesce_emu_uh.instr_val);
+
+   return 0;
+}
+
+static int aesce_emu_init(void)
+{
+   register_undef_hook(_emu_uh);
+   elf_hwcap |= HWCAP_AES;
+   return 0;
+}
+
+arch_initcall(aesce_emu_init);
+
+static void add_sub_shift(union 

[PATCH 1/8] arm64: add kernel emulation for AES instructions

2014-01-06 Thread Ard Biesheuvel
NOTE: this patch is not intended for merging upstream, but is only
included as a bonus so mere mortals (i.e., those whose ARMv8 system
does not support the AES crypto instructions) can test the series
if they like.

Signed-off-by: Ard Biesheuvel ard.biesheu...@linaro.org
---
 arch/arm64/Makefile|   1 +
 arch/arm64/emu/Makefile|  11 ++
 arch/arm64/emu/ce-aes.c| 331 +
 arch/arm64/include/asm/traps.h |  10 ++
 arch/arm64/kernel/entry.S  |   4 +-
 arch/arm64/kernel/traps.c  |  49 ++
 6 files changed, 405 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/emu/Makefile
 create mode 100644 arch/arm64/emu/ce-aes.c

diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 2fceb71ac3b7..e0b75464b7f1 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -42,6 +42,7 @@ TEXT_OFFSET := 0x0008
 
 export TEXT_OFFSET GZFLAGS
 
+core-y += arch/arm64/emu/
 core-y += arch/arm64/kernel/ arch/arm64/mm/
 core-$(CONFIG_KVM) += arch/arm64/kvm/
 core-$(CONFIG_XEN) += arch/arm64/xen/
diff --git a/arch/arm64/emu/Makefile b/arch/arm64/emu/Makefile
new file mode 100644
index ..224b4e19ff6f
--- /dev/null
+++ b/arch/arm64/emu/Makefile
@@ -0,0 +1,11 @@
+#
+# linux/arch/arm64/emu/Makefile
+#
+# Copyright (C) 2013 Linaro Ltd ard.biesheu...@linaro.org
+#
+# 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.
+#
+
+obj-y += ce-aes.o
diff --git a/arch/arm64/emu/ce-aes.c b/arch/arm64/emu/ce-aes.c
new file mode 100644
index ..d50fadbd7336
--- /dev/null
+++ b/arch/arm64/emu/ce-aes.c
@@ -0,0 +1,331 @@
+/*
+ * ce-aes.c - emulate aese/aesd/aesmc/aesimc instructions
+ *
+ * Copyright (C) 2013 Linaro Ltd ard.biesheu...@linaro.org
+ *
+ * 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.
+ */
+
+#include linux/init.h
+#include linux/printk.h
+#include linux/ptrace.h
+#include linux/types.h
+#include linux/uaccess.h
+#include asm/traps.h
+#include asm/hwcap.h
+
+union AES_STATE {
+   u8  bytes[16];
+   u32 cols[4];
+   u64 l[2];
+} __aligned(8);
+
+static void add_sub_shift(union AES_STATE *st, union AES_STATE *rk, int inv);
+static void mix_columns(union AES_STATE *out, union AES_STATE *in, int inv);
+
+#define REG_ACCESS(op, r, mem) \
+   do { case r: asm(#op  {v #r .16b}, [%0] : : r(mem)); goto out; \
+   } while (0)
+
+#define REG_SWITCH(reg, op, m) do { switch (reg) { \
+   REG_ACCESS(op,  0, m);  REG_ACCESS(op,  1, m);  REG_ACCESS(op,  2, m); \
+   REG_ACCESS(op,  3, m);  REG_ACCESS(op,  4, m);  REG_ACCESS(op,  5, m); \
+   REG_ACCESS(op,  6, m);  REG_ACCESS(op,  7, m);  REG_ACCESS(op,  8, m); \
+   REG_ACCESS(op,  9, m);  REG_ACCESS(op, 10, m);  REG_ACCESS(op, 11, m); \
+   REG_ACCESS(op, 12, m);  REG_ACCESS(op, 13, m);  REG_ACCESS(op, 14, m); \
+   REG_ACCESS(op, 15, m);  REG_ACCESS(op, 16, m);  REG_ACCESS(op, 17, m); \
+   REG_ACCESS(op, 18, m);  REG_ACCESS(op, 19, m);  REG_ACCESS(op, 20, m); \
+   REG_ACCESS(op, 21, m);  REG_ACCESS(op, 22, m);  REG_ACCESS(op, 23, m); \
+   REG_ACCESS(op, 24, m);  REG_ACCESS(op, 25, m);  REG_ACCESS(op, 26, m); \
+   REG_ACCESS(op, 27, m);  REG_ACCESS(op, 28, m);  REG_ACCESS(op, 29, m); \
+   REG_ACCESS(op, 30, m);  REG_ACCESS(op, 31, m); \
+   } out:; } while (0)
+
+static void load_neon_reg(union AES_STATE *st, int reg)
+{
+   REG_SWITCH(reg, st1, st-bytes);
+}
+
+static void save_neon_reg(union AES_STATE *st, int reg)
+{
+   REG_SWITCH(reg, ld1, st-bytes);
+}
+
+static void aesce_do_emulate(unsigned int instr)
+{
+   enum { AESE, AESD, AESMC, AESIMC } kind = (instr  12)  3;
+   int rn = (instr  5)  0x1f;
+   int rd = instr  0x1f;
+   union AES_STATE in, out;
+
+   load_neon_reg(in, rn);
+
+   switch (kind) {
+   case AESE:
+   case AESD:
+   load_neon_reg(out, rd);
+   add_sub_shift(out, in, kind  1);
+   break;
+   case AESMC:
+   case AESIMC:
+   mix_columns(out, in, kind  1);
+   break;
+   }
+   save_neon_reg(out, rd);
+}
+
+static int aesce_emu_instr(struct pt_regs *regs, unsigned int instr);
+
+static struct undef_hook aesce_emu_uh = {
+   .instr_val  = 0x4e284800,
+   .instr_mask = 0xcc00,
+   .fn = aesce_emu_instr,
+};
+
+static int aesce_emu_instr(struct pt_regs *regs, unsigned int instr)
+{
+   do {
+   aesce_do_emulate(instr);
+   regs-pc += 4;
+   get_user(instr, (u32 __user *)regs-pc);
+   } while ((instr  aesce_emu_uh.instr_mask) == aesce_emu_uh.instr_val);
+
+   return 0;
+}
+
+static int aesce_emu_init(void)
+{
+