[PATCH 1/8] arm64: add kernel emulation for AES instructions
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
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) +{ +