[sw] Add sanity check for AES unit
Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/ci/run_sw_make.sh b/ci/run_sw_make.sh
index f1ec539..c1e2a92 100755
--- a/ci/run_sw_make.sh
+++ b/ci/run_sw_make.sh
@@ -31,6 +31,7 @@
readonly BUILD_TARGETS=("boot_rom"
"examples/hello_usbdev"
"examples/hello_world"
+ "tests/aes"
"tests/flash_ctrl"
"tests/hmac"
"tests/rv_timer"
diff --git a/ci/run_verilator_pytest.sh b/ci/run_verilator_pytest.sh
index 78228c7..e3492d0 100755
--- a/ci/run_verilator_pytest.sh
+++ b/ci/run_verilator_pytest.sh
@@ -15,6 +15,7 @@
BOOT_ROM_TARGET="boot_rom/boot_rom.elf"
TEST_TARGETS=(
+ "tests/aes/aes_test.elf"
"tests/flash_ctrl/flash_test.elf"
"tests/hmac/sha256_test.elf"
"tests/rv_timer/rv_timer_test.elf"
@@ -23,6 +24,7 @@
if [[ ! -z ${MAKE_BUILD+x} ]]; then
BOOT_ROM_TARGET="sw/device/sim/boot_rom/rom.elf"
TEST_TARGETS=(
+ "sw/device/sim/tests/aes/sw.elf"
"sw/device/sim/tests/flash_ctrl/sw.elf"
"sw/device/sim/tests/hmac/sw.elf"
"sw/device/sim/tests/rv_timer/sw.elf"
diff --git a/meson.build b/meson.build
index c8dd7e1..1cf5112 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,7 @@
)
# TODO: Considering moving these into hw/ip directories.
+hw_ip_aes_reg_h = gen_hw_hdr.process('hw/ip/aes/data/aes.hjson')
hw_ip_flash_ctrl_regs_h = gen_hw_hdr.process('hw/ip/flash_ctrl/data/flash_ctrl.hjson')
hw_ip_gpio_reg_h = gen_hw_hdr.process('hw/ip/gpio/data/gpio.hjson')
hw_ip_hmac_reg_h = gen_hw_hdr.process('hw/ip/hmac/data/hmac.hjson')
diff --git a/sw/device/lib/aes.c b/sw/device/lib/aes.c
new file mode 100644
index 0000000..c1da3c3
--- /dev/null
+++ b/sw/device/lib/aes.c
@@ -0,0 +1,104 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/aes.h"
+
+#include "aes_regs.h" // Generated.
+#include "sw/device/lib/common.h"
+
+#define AES0_BASE_ADDR 0x40110000
+#define AES_NUM_REGS_DATA 4
+#define AES_NUM_REGS_KEY 8
+
+void aes_init(aes_cfg_t aes_cfg) {
+ REG32(AES_CTRL(0)) =
+ (aes_cfg.mode << AES_CTRL_MODE) |
+ ((aes_cfg.key_len & AES_CTRL_KEY_LEN_MASK) << AES_CTRL_KEY_LEN_OFFSET) |
+ (aes_cfg.manual_start_trigger << AES_CTRL_MANUAL_START_TRIGGER) |
+ (aes_cfg.force_data_overwrite << AES_CTRL_FORCE_DATA_OVERWRITE);
+};
+
+void aes_key_put(const void *key, aes_key_len_t key_len) {
+ // Determine how many key registers to use.
+ size_t num_regs_key_used;
+ if (key_len == kAes256) {
+ num_regs_key_used = 8;
+ } else if (key_len == kAes192) {
+ num_regs_key_used = 6;
+ } else {
+ num_regs_key_used = 4;
+ }
+
+ // Write the used key registers.
+ for (int i = 0; i < num_regs_key_used; ++i) {
+ REG32(AES_KEY0(0) + i * sizeof(uint32_t)) = ((uint32_t *)key)[i];
+ }
+ // Write the unused key registers (the AES unit requires all key registers to
+ // be written).
+ for (int i = num_regs_key_used; i < AES_NUM_REGS_KEY; ++i) {
+ REG32(AES_KEY0(0) + i * sizeof(uint32_t)) = 0x0;
+ }
+}
+
+void aes_data_put_wait(const void *data) {
+ // Wait for AES unit to be ready for new input data.
+ while (!aes_data_ready()) {
+ }
+
+ // Provide the input data.
+ aes_data_put(data);
+}
+
+void aes_data_put(const void *data) {
+ // Write the four input data registers.
+ for (int i = 0; i < AES_NUM_REGS_DATA; ++i) {
+ REG32(AES_DATA_IN0(0) + i * sizeof(uint32_t)) = ((uint32_t *)data)[i];
+ }
+}
+
+void aes_data_get_wait(void *data) {
+ // Wait for AES unit to have valid output data.
+ while (!aes_data_valid()) {
+ }
+
+ // Get the data.
+ aes_data_get(data);
+}
+
+void aes_data_get(void *data) {
+ // Read the four output data registers.
+ for (int i = 0; i < AES_NUM_REGS_DATA; ++i) {
+ ((uint32_t *)data)[i] = REG32(AES_DATA_OUT0(0) + i * sizeof(uint32_t));
+ }
+}
+
+bool aes_data_ready(void) {
+ return (REG32(AES_STATUS(0)) & (0x1u << AES_STATUS_INPUT_READY));
+}
+
+bool aes_data_valid(void) {
+ return (REG32(AES_STATUS(0)) & (0x1u << AES_STATUS_OUTPUT_VALID));
+}
+
+bool aes_idle(void) {
+ return (REG32(AES_STATUS(0)) & (0x1u << AES_STATUS_IDLE));
+}
+
+void aes_clear(void) {
+ // Wait for AES unit to be idle.
+ while (!aes_idle()) {
+ }
+
+ // Disable autostart
+ REG32(AES_CTRL(0)) = 0x1u << AES_CTRL_MANUAL_START_TRIGGER;
+
+ // Clear internal key and output registers
+ REG32(AES_TRIGGER(0)) = (0x1u << AES_TRIGGER_KEY_CLEAR) |
+ (0x1u << AES_TRIGGER_DATA_IN_CLEAR) |
+ (0x1u << AES_TRIGGER_DATA_OUT_CLEAR);
+
+ // Wait for output not valid, and input ready
+ while (!(!aes_data_valid() && aes_data_ready())) {
+ }
+}
diff --git a/sw/device/lib/aes.h b/sw/device/lib/aes.h
new file mode 100644
index 0000000..99f655a
--- /dev/null
+++ b/sw/device/lib/aes.h
@@ -0,0 +1,112 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef _F_LIB_AES_H__
+#define _F_LIB_AES_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Supported AES modes: encode or decode.
+ */
+typedef enum aes_mode { kAesEnc = 0, kAesDec = 1 } aes_mode_t;
+
+/**
+ * Supported AES key lengths: 128 bit, 192 bit or 256 bit. The hardware uses a
+ * one-hot encoding.
+ */
+typedef enum aes_key_len {
+ kAes128 = 0x1,
+ kAes192 = 0x2,
+ kAes256 = 0x4
+} aes_key_len_t;
+
+/**
+ * AES unit configuration options.
+ */
+typedef struct aes_cfg {
+ /** Operational mode @see aes_mode. */
+ aes_mode_t mode;
+ /** Key length @see aes_key_len. */
+ aes_key_len_t key_len;
+ /** Set to 1 to only start upon getting a trigger signal. */
+ bool manual_start_trigger;
+ /** Set to 1 to not stall when previous output data has not been read. */
+ bool force_data_overwrite;
+} aes_cfg_t;
+
+/**
+ * Intialize AES unit to desired mode.
+ *
+ * @param aes_cfg AES configuration settings.
+ */
+void aes_init(aes_cfg_t aes_cfg);
+
+/**
+ * Pass initial encryption key to AES unit.
+ *
+ * @param key pointer to key.
+ * @param key_len_in_bytes key length in bytes (16, 24, 32)
+ */
+void aes_key_put(const void *key, size_t key_len_in_bytes);
+
+/**
+ * Wait for AES unit to be ready for new input data and then
+ * pass one 16B block of input data to AES unit.
+ *
+ * @param data pointer to input buffer.
+ */
+void aes_data_put_wait(const void *data);
+
+/**
+ * Pass one 16B block of input data to AES unit.
+ *
+ * @param data pointer to input buffer.
+ */
+void aes_data_put(const void *data);
+
+/**
+ * Wait for AES unit to have valid output data and then
+ * get one 16B block of output data from AES unit.
+ *
+ * @param data pointer to output buffer.
+ */
+void aes_data_get_wait(void *data);
+
+/**
+ * Get one 16B block of output data from AES unit.
+ *
+ * @param data pointer to output buffer.
+ */
+void aes_data_get(void *data);
+
+/**
+ * Check AES unit for being ready to accept new input data.
+ *
+ * @return true if ready for new input data, false otherwise.
+ */
+bool aes_data_ready(void);
+
+/**
+ * Check AES unit for having valid output data.
+ *
+ * @return true if valid output data available, false otherwise.
+ */
+bool aes_data_valid(void);
+
+/**
+ * Check AES unit for being idle.
+ *
+ * @return true if idle, false otherwise.
+ */
+bool aes_idle(void);
+
+/**
+ * Clear key, input and ouput registers of AES unit.
+ */
+void aes_clear(void);
+
+#endif // _F_LIB_AES_H__
diff --git a/sw/device/lib/meson.build b/sw/device/lib/meson.build
index eb8982b..17c99ba 100644
--- a/sw/device/lib/meson.build
+++ b/sw/device/lib/meson.build
@@ -66,6 +66,18 @@
)
)
+# AES library (sw_lib_aes)
+sw_lib_aes = declare_dependency(
+ sources: [hw_ip_aes_reg_h],
+ link_with: static_library(
+ 'aes_ot',
+ sources: [
+ hw_ip_aes_reg_h,
+ 'aes.c',
+ ]
+ )
+)
+
# HMAC library (sw_lib_hmac)
sw_lib_hmac = declare_dependency(
sources: [hw_ip_hmac_reg_h],
diff --git a/sw/device/lib/srcs.mk b/sw/device/lib/srcs.mk
index 5d46ffb..0d198c4 100644
--- a/sw/device/lib/srcs.mk
+++ b/sw/device/lib/srcs.mk
@@ -6,7 +6,7 @@
INCS += -I$(LIB_DIR)
GEN_HEADERS += $(LIB_LOC_DIF_SRCS:.c=_regs.h)
-LIB_LOC_DIF_SRCS += uart.c gpio.c spi_device.c flash_ctrl.c hmac.c usbdev.c rv_timer.c pinmux.c
+LIB_LOC_DIF_SRCS += uart.c gpio.c spi_device.c flash_ctrl.c aes.c hmac.c usbdev.c rv_timer.c pinmux.c
LIB_LOC_EXT_SRCS += usb_controlep.c usb_simpleserial.c irq.c handler.c print_log.c irq_vectors.S memcpy.c memset.c strlen.c abort.c
LIB_SRCS += $(addprefix $(LIB_DIR)/, $(LIB_LOC_DIF_SRCS) $(LIB_LOC_EXT_SRCS))
diff --git a/sw/device/tests/aes/aes_test.c b/sw/device/tests/aes/aes_test.c
new file mode 100644
index 0000000..68f529b
--- /dev/null
+++ b/sw/device/tests/aes/aes_test.c
@@ -0,0 +1,86 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/aes.h"
+#include "sw/device/lib/common.h"
+#include "sw/device/lib/uart.h"
+
+// The following plaintext, key and ciphertext are extracted from Appendix C of
+// the Advanced Encryption Standard (AES) FIPS Publication 197 available at
+// https://www.nist.gov/publications/advanced-encryption-standard-aes
+
+static const uint8_t plain_text_1[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+ 0xcc, 0xdd, 0xee, 0xff};
+
+static const uint8_t key_32_1[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
+
+static const uint8_t cipher_text_gold_32_1[16] = {
+ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
+ 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89};
+
+int main(int argc, char **argv) {
+ bool has_error = false;
+
+ // Wait for AES unit being idle
+ while (!aes_idle()) {
+ }
+
+ uint8_t buffer[16];
+
+ uart_init(UART_BAUD_RATE);
+ uart_send_str("Running AES test\r\n");
+
+ // Setup AES config
+ aes_cfg_t aes_cfg;
+ aes_cfg.mode = kAesEnc;
+ aes_cfg.key_len = kAes256;
+ aes_cfg.manual_start_trigger = false;
+ aes_cfg.force_data_overwrite = false;
+
+ aes_key_put((const void *)key_32_1, aes_cfg.key_len);
+
+ // Encode
+ aes_cfg.mode = kAesEnc;
+ aes_init(aes_cfg);
+ aes_data_put_wait((const void *)plain_text_1);
+ aes_data_get_wait((void *)buffer);
+
+ // Check against golden cipher text
+ for (int i = 0; i < 16; i++) {
+ if (cipher_text_gold_32_1[i] != buffer[i]) {
+ has_error = true;
+ }
+ }
+
+ // Decode
+ aes_cfg.mode = kAesDec;
+ aes_init(aes_cfg);
+ aes_data_put_wait((const void *)buffer);
+ aes_data_get_wait((void *)buffer);
+
+ // Check against input plain text
+ for (int i = 0; i < 16; i++) {
+ if (plain_text_1[i] != buffer[i]) {
+ has_error = true;
+ }
+ }
+
+ // Clear
+ aes_clear();
+
+ if (has_error) {
+ uart_send_str("FAIL!\r\n");
+ while (1) {
+ }
+ } else {
+ uart_send_str("PASS!\r\n");
+ __asm__ volatile("wfi;");
+ }
+
+ return 0;
+}
diff --git a/sw/device/tests/aes/meson.build b/sw/device/tests/aes/meson.build
new file mode 100644
index 0000000..17438f8
--- /dev/null
+++ b/sw/device/tests/aes/meson.build
@@ -0,0 +1,31 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+aes_test_elf = executable(
+ 'aes_test',
+ sources: ['aes_test.c'],
+ name_suffix: 'elf',
+ dependencies: [
+ sw_lib_aes,
+ sw_lib_uart,
+ riscv_crt,
+ ],
+)
+
+aes_test_embedded = custom_target(
+ 'aes_test',
+ command: make_embedded_target,
+ input: aes_test_elf,
+ output: make_embedded_target_outputs,
+ build_by_default: true,
+)
+
+custom_target(
+ 'aes_test_export',
+ command: export_embedded_target,
+ input: [aes_test_elf, aes_test_embedded],
+ output: 'aes_test',
+ build_always_stale: true,
+ build_by_default: true,
+)
diff --git a/sw/device/tests/aes/srcs.mk b/sw/device/tests/aes/srcs.mk
new file mode 100644
index 0000000..0bca6c2
--- /dev/null
+++ b/sw/device/tests/aes/srcs.mk
@@ -0,0 +1,11 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+SW_NAME ?= aes_test
+
+# list srcs for each test
+ifeq ($(SW_NAME), aes_test)
+ SW_SRCS += $(SW_DIR)/aes_test.c
+ SW_SRCS += $(SW_ROOT_DIR)/lib/aes.c
+endif
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index 298c2cf..a44c725 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -2,6 +2,7 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
+subdir('aes')
subdir('flash_ctrl')
subdir('hmac')
subdir('rv_timer')