diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c829f540..597698bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,8 @@ add_library(dynarmic ../include/dynarmic/A32/coprocessor_util.h ../include/dynarmic/A32/disassembler.h common/address_range.h + common/aes.cpp + common/aes.h common/assert.h common/bit_util.h common/common_types.h @@ -79,6 +81,7 @@ add_library(dynarmic frontend/A64/translate/impl/load_store_register_pair.cpp frontend/A64/translate/impl/load_store_register_unprivileged.cpp frontend/A64/translate/impl/move_wide.cpp + frontend/A64/translate/impl/simd_aes.cpp frontend/A64/translate/impl/simd_copy.cpp frontend/A64/translate/impl/simd_three_same.cpp frontend/A64/translate/impl/system.cpp @@ -139,6 +142,7 @@ if (ARCHITECTURE_x86_64) backend_x64/disassemble_x64.h backend_x64/emit_x64.cpp backend_x64/emit_x64.h + backend_x64/emit_x64_aes.cpp backend_x64/emit_x64_crc32.cpp backend_x64/emit_x64_data_processing.cpp backend_x64/emit_x64_floating_point.cpp diff --git a/src/backend_x64/emit_x64_aes.cpp b/src/backend_x64/emit_x64_aes.cpp new file mode 100644 index 00000000..7dd2d706 --- /dev/null +++ b/src/backend_x64/emit_x64_aes.cpp @@ -0,0 +1,64 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "backend_x64/abi.h" +#include "backend_x64/block_of_code.h" +#include "backend_x64/emit_x64.h" +#include "common/aes.h" +#include "common/common_types.h" +#include "frontend/ir/microinstruction.h" +#include "frontend/ir/opcodes.h" + +namespace Dynarmic::BackendX64 { + +using namespace Xbyak::util; + +using MixColumnsFn = void(Common::AESState&, const Common::AESState&); + +static void EmitMixColumns(std::array args, EmitContext& ctx, BlockOfCode& code, + IR::Inst* inst, MixColumnsFn fn) { + constexpr u32 stack_space = static_cast(sizeof(Common::AESState)) * 2; + const Xbyak::Xmm input = ctx.reg_alloc.UseXmm(args[0]); + ctx.reg_alloc.EndOfAllocScope(); + + ctx.reg_alloc.HostCall(nullptr); + code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE]); + code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + sizeof(Common::AESState)]); + + code.movaps(xword[code.ABI_PARAM2], input); + + code.CallFunction(fn); + + code.movaps(xmm0, xword[rsp + ABI_SHADOW_SPACE]); + + // Free memory + code.add(rsp, stack_space + ABI_SHADOW_SPACE); + + ctx.reg_alloc.DefineValue(inst, xmm0); +} + +void EmitX64::EmitAESInverseMixColumns(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + if (code->DoesCpuSupport(Xbyak::util::Cpu::tAESNI)) { + const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]); + const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(); + + code->aesimc(result, operand); + + ctx.reg_alloc.DefineValue(inst, result); + } else { + EmitMixColumns(args, ctx, *code, inst, Common::InverseMixColumns); + } +} + +void EmitX64::EmitAESMixColumns(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + EmitMixColumns(args, ctx, *code, inst, Common::MixColumns); +} + +} // namespace Dynarmic::BackendX64 diff --git a/src/common/aes.cpp b/src/common/aes.cpp new file mode 100644 index 00000000..eab3e118 --- /dev/null +++ b/src/common/aes.cpp @@ -0,0 +1,58 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include + +#include "common/aes.h" +#include "common/common_types.h" + +namespace Dynarmic::Common { + +// See section 4.2.1 in FIPS 197. +static constexpr u8 xtime(u8 x) { + return static_cast((x << 1) ^ (((x >> 7) & 1) * 0x1B)); +} + +// Galois Field multiplication. +static constexpr u8 Multiply(u8 x, u8 y) { + return static_cast(((y & 1) * x) ^ + ((y >> 1 & 1) * xtime(x)) ^ + ((y >> 2 & 1) * xtime(xtime(x))) ^ + ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); +} + +void MixColumns(AESState& out_state, const AESState& state) { + for (size_t i = 0; i < out_state.size(); i += 4) { + const u8 a = state[i]; + const u8 b = state[i + 1]; + const u8 c = state[i + 2]; + const u8 d = state[i + 3]; + + const u8 tmp = a ^ b ^ c ^ d; + + out_state[i + 0] = a ^ xtime(a ^ b) ^ tmp; + out_state[i + 1] = b ^ xtime(b ^ c) ^ tmp; + out_state[i + 2] = c ^ xtime(c ^ d) ^ tmp; + out_state[i + 3] = d ^ xtime(d ^ a) ^ tmp; + } +} + +void InverseMixColumns(AESState& out_state, const AESState& state) { + for (size_t i = 0; i < out_state.size(); i += 4) { + const u8 a = state[i]; + const u8 b = state[i + 1]; + const u8 c = state[i + 2]; + const u8 d = state[i + 3]; + + out_state[i + 0] = Multiply(a, 0x0E) ^ Multiply(b, 0x0B) ^ Multiply(c, 0x0D) ^ Multiply(d, 0x09); + out_state[i + 1] = Multiply(a, 0x09) ^ Multiply(b, 0x0E) ^ Multiply(c, 0x0B) ^ Multiply(d, 0x0D); + out_state[i + 2] = Multiply(a, 0x0D) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0E) ^ Multiply(d, 0x0B); + out_state[i + 3] = Multiply(a, 0x0B) ^ Multiply(b, 0x0D) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0E); + } +} + +} // namespace Dynarmic::Common diff --git a/src/common/aes.h b/src/common/aes.h new file mode 100644 index 00000000..1a9a8767 --- /dev/null +++ b/src/common/aes.h @@ -0,0 +1,19 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#pragma once + +#include +#include "common/common_types.h" + +namespace Dynarmic::Common { + +using AESState = std::array; + +void MixColumns(AESState& out_state, const AESState& state); +void InverseMixColumns(AESState& out_state, const AESState& state); + +} // namespace Dynarmic::Common diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index 0190eee7..68d01fc1 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -343,8 +343,8 @@ INST(UMSUBL, "UMSUBL", "10011 // Data Processing - FP and SIMD - AES //INST(AESE, "AESE", "0100111000101000010010nnnnnddddd") //INST(AESD, "AESD", "0100111000101000010110nnnnnddddd") -//INST(AESMC, "AESMC", "0100111000101000011010nnnnnddddd") -//INST(AESIMC, "AESIMC", "0100111000101000011110nnnnnddddd") +INST(AESMC, "AESMC", "0100111000101000011010nnnnnddddd") +INST(AESIMC, "AESIMC", "0100111000101000011110nnnnnddddd") // Data Processing - FP and SIMD - SHA //INST(SHA1C, "SHA1C", "01011110000mmmmm000000nnnnnddddd") diff --git a/src/frontend/A64/translate/impl/simd_aes.cpp b/src/frontend/A64/translate/impl/simd_aes.cpp new file mode 100644 index 00000000..5566064b --- /dev/null +++ b/src/frontend/A64/translate/impl/simd_aes.cpp @@ -0,0 +1,27 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "frontend/A64/translate/impl/impl.h" + +namespace Dynarmic::A64 { + +bool TranslatorVisitor::AESIMC(Vec Vn, Vec Vd) { + const IR::U128 operand = ir.GetQ(Vn); + const IR::U128 result = ir.AESInverseMixColumns(operand); + + ir.SetQ(Vd, result); + return true; +} + +bool TranslatorVisitor::AESMC(Vec Vn, Vec Vd) { + const IR::U128 operand = ir.GetQ(Vn); + const IR::U128 result = ir.AESMixColumns(operand); + + ir.SetQ(Vd, result); + return true; +} + +} // namespace Dynarmic::A64 diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 44e41efe..34c2f27e 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -715,6 +715,14 @@ U32 IREmitter::CRC32ISO64(const U32& a, const U64& b) { return Inst(Opcode::CRC32ISO64, a, b); } +U128 IREmitter::AESInverseMixColumns(const U128 &a) { + return Inst(Opcode::AESInverseMixColumns, a); +} + +U128 IREmitter::AESMixColumns(const U128 &a) { + return Inst(Opcode::AESMixColumns, a); +} + UAny IREmitter::VectorGetElement(size_t esize, const U128& a, size_t index) { ASSERT_MSG(esize * index < 128, "Invalid index"); switch (esize) { diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index 7551c897..94cae668 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -195,6 +195,9 @@ public: U32 CRC32ISO32(const U32& a, const U32& b); U32 CRC32ISO64(const U32& a, const U64& b); + U128 AESInverseMixColumns(const U128& a); + U128 AESMixColumns(const U128& a); + UAny VectorGetElement(size_t esize, const U128& a, size_t index); U128 VectorAdd8(const U128& a, const U128& b); U128 VectorAdd16(const U128& a, const U128& b); diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 572fdf63..8bce8f76 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -178,6 +178,10 @@ OPCODE(CRC32ISO16, T::U32, T::U32, T::U32 OPCODE(CRC32ISO32, T::U32, T::U32, T::U32 ) OPCODE(CRC32ISO64, T::U32, T::U32, T::U64 ) +// AES instructions +OPCODE(AESInverseMixColumns, T::U128, T::U128 ) +OPCODE(AESMixColumns, T::U128, T::U128 ) + // Vector instructions OPCODE(VectorGetElement8, T::U8, T::U128, T::U8 ) OPCODE(VectorGetElement16, T::U16, T::U128, T::U8 )