commit 1f80a3a3dcb47af6fed9e3552e5ccfcb289525dd Author: Shawn Nock Date: Wed Jun 1 15:07:50 2016 -0400 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e35d885 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d1e4c03 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +# List all source files the application uses. +APPLICATION_SRCS = $(notdir $(wildcard ./*.c)) + +PROJECT_NAME = cmac_aes128_test +VERSION = 0.1 + +DEVICE = NRF51 + +#USE_SOFTDEVICE = s110 + +SDK_PATH = $(HOME)/devel/nrf-sdk/10.0/ + +TEMPLATE_PATH = ./template/ + +CFLAGS = -Os -Wall -Werror -flto -g +LDFLAGS = -Os -flto -g + +include $(TEMPLATE_PATH)Makefile diff --git a/README.md b/README.md new file mode 100644 index 0000000..67658f5 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# CMAC-AES128 for nRF51 + +- Uses AES-ECB hardware peripheral +- Includes target test app that runs the NIST 800-38B Examples +- Includes host test app that checks the NIST 800-38B Examples + (requires OpenSSL) + + +## Usage + +1. Edit `Makefile`, special note to edit the path to the NRF-SDK + (tested against SDK 10.0) + +2. Edit main.c: + +> #define ERROR_LED_PIN 12 +> #define ERROR_LED_ACTIVE LOW + +3. `make`: + + Output files in `_build` directory. Program to nRF51 dev. board to + run tests on target. + +4. `make test`: + + Fakes AES128 peripheral using openssl, then runs tests on host. diff --git a/aes.c b/aes.c new file mode 100644 index 0000000..f56e862 --- /dev/null +++ b/aes.c @@ -0,0 +1,41 @@ +#include "aes.h" +#include "block.h" + +#ifndef HOST_BUILD +#include "nrf.h" +#else +#include +#endif /* HOST_BUILD */ + +#include + +static ecbdata_t g_ecbdata; + +void aes128_init(uint8_t *key) { + memmove(g_ecbdata.key, key, 16); +#ifndef HOST_BUILD + NRF_ECB->ECBDATAPTR = (uint32_t)&g_ecbdata; +#endif /* HOST_BUILD */ + return; +} + +block_t aes128_ecb(block_t const * const in) { +#ifdef HOST_BUILD + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, g_ecbdata.key, NULL); + + int outlen, tmplen; + EVP_EncryptUpdate(ctx, g_ecbdata.out, &outlen, in->ui8, 16); + EVP_EncryptFinal_ex(ctx, g_ecbdata.out + outlen, &tmplen); + + /* Why the fuck does this segfault... I don't care. It's for testing + on the host only. Let it leak */ + //EVP_CIPHER_CTX_free(ctx); +#else + memmove(g_ecbdata.in, in, 16); + NRF_ECB->TASKS_STARTECB = 1; + while (!NRF_ECB->EVENTS_ENDECB); + NRF_ECB->EVENTS_ENDECB = 0; +#endif /* HOST_BUILD */ + return *((block_t *)g_ecbdata.out); +} diff --git a/aes.h b/aes.h new file mode 100644 index 0000000..1108ffb --- /dev/null +++ b/aes.h @@ -0,0 +1,15 @@ +#pragma once + +#include "block.h" + +#include + +typedef struct { + uint8_t key[16]; + uint8_t in[16]; + uint8_t out[16]; +} ecbdata_t; + + +void aes128_init(uint8_t *); +block_t aes128_ecb(block_t const * const); diff --git a/block.c b/block.c new file mode 100644 index 0000000..8ba248b --- /dev/null +++ b/block.c @@ -0,0 +1,71 @@ +/* Utility functions for manipulating block_t structures; 128bit + blocks of data for AES & CMAC */ + +#include "block.h" + +#include +#include +#include + +bool block_cmp(block_t const * const a, block_t const * const b) { + for (uint_fast8_t i = 0; i < 4; i++) { + if (a->ui32[i] != b->ui32[i]) { + return false; + } + } + return true; +} + +void block_print(char const * const label, + block_t const * const b) { + if (label != NULL) { + printf("\n%s: ", label); + } else { + printf("\n"); + } + for(int i = 0; i < 16; i++) { + printf("%.2x", b->ui8[i]); + if (!((i+1) % 4)) { + printf(" "); + } + } + printf("\n"); + return; +} +block_t block_xor(block_t const * const a, block_t const * const b) { + block_t c; + for (uint_fast8_t i = 0; i < 4; i++) { + c.ui32[i] = a->ui32[i] ^ b->ui32[i]; + } + return c; +} + +block_t block_shiftr(block_t const * const a, uint_fast8_t const num) { + block_t c; + uint_fast8_t n = (num <= 8 ? num : 8); + for (int_fast8_t i = 15; i >= 0; i--) { + /* Shift right from LSB to MSB */ + c.ui8[i] = a->ui8[i] >> n; + if (i != 0) { + /* If theres a more significant uint32, we need to pull in its lsb */ + c.ui8[i] |= (a->ui8[i-1] << (8 - n)); + } + } + return (num - n == 0 ? c : block_shiftr(&c, num - n)); +} + +block_t block_shiftl(block_t const * const a, uint_fast8_t const num) { + /* Shifts block b right by num bits (where num <= 32) */ + block_t c; + uint_fast8_t n = (num <= 8 ? num : 8); + for (int_fast8_t i = 0; i < 16; i++) { + /* Shift right from LSB to MSB */ + c.ui8[i] = a->ui8[i] << n; + if (i != 15) { + /* If theres a less significant uint32, we need to pull in its msb */ + c.ui8[i] |= (a->ui8[i+1] >> (8 - n)); + } + } + return (num - n == 0 ? c : block_shiftl(&c, num - n)); + } + diff --git a/block.h b/block.h new file mode 100644 index 0000000..cc5615e --- /dev/null +++ b/block.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +typedef union { + uint32_t ui32[4]; + uint8_t ui8[16]; +} block_t; + +bool block_cmp(block_t const * const, block_t const * const); +void block_print(char const * const, block_t const * const); +block_t block_shiftl(block_t const * const, uint_fast8_t); +block_t block_shiftr(block_t const * const, uint_fast8_t); +block_t block_xor(block_t const * const, block_t const * const); + diff --git a/cmac.c b/cmac.c new file mode 100644 index 0000000..5ed2cd2 --- /dev/null +++ b/cmac.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include + +#include "aes.h" +#include "block.h" +#include "cmac.h" + +static const block_t zeros = {.ui32={0,0,0,0}}; + +static block_t g_k[2]; + +void cmac_aes128_init(block_t *key) { + aes128_init(key->ui8); + cmac_aes128_expand_key(key, g_k); +} + +void cmac_aes128_expand_key(block_t *key, block_t *out) { + /* Given AES key k, generate the subkeys needed for CMAC */ + block_t *k1 = out, + *k2 = (out+1); + + block_t l = {0}, + Rb = {.ui8={0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x87}}; + + l = aes128_ecb(&zeros); + if ((l.ui8[0] >> 7) == 0) { + *k1 = block_shiftl(&l, 1); + } else { + block_t tmp = block_shiftl(&l, 1); + *k1 = block_xor(&tmp, &Rb); + } + if (!(k1->ui8[0] >> 7)) { + *k2 = block_shiftl(k1, 1); + } else { + fflush(stdout); + block_t tmp = block_shiftl(k1, 1); + *k2 = block_xor(&tmp, &Rb); + } +} + +void cmac_truncate_tag(uint8_t *dest, block_t *tag, uint_fast8_t tag_len_bits) { + uint_fast8_t num_bytes = tag_len_bits / 8, + last_byte_mask = 0xff << (8 - tag_len_bits % 8); + memcpy(dest, tag->ui8, num_bytes); + dest[num_bytes] = tag->ui8[num_bytes] & last_byte_mask; +} + +/* Address of the first uint8_t in a particular message block */ +#define BLOCK(x) (&alt_msg[x-1]) + +block_t cmac_aes128(uint8_t *msg, size_t msg_len) { + /* Simulate ceiling integer division by adding a block if remainder */ + block_t *k1 = &g_k[0], + *k2 = &g_k[1]; + uint_fast16_t num_blocks = msg_len / 16 + (msg_len % 16 ? 1 : 0); + bool last_block_complete = !(msg_len % 16 ? 1 : 0); + if (msg_len == 0) { + num_blocks = 1; + last_block_complete = false; + } + + block_t alt_msg[num_blocks]; + memset(&alt_msg, 0, num_blocks*16); + memcpy(&alt_msg, msg, msg_len); + + if (!last_block_complete) { + /* Padding is single 1 bit, run out on 0s.. find the next byte, + set it to 0b1000000 */ + alt_msg[num_blocks-1].ui8[msg_len % 16] = 0x80; + alt_msg[num_blocks-1] = block_xor(BLOCK(num_blocks), k2); + } else { + alt_msg[num_blocks-1] = block_xor(BLOCK(num_blocks), k1); + } + + block_t x = { .ui32={0, 0, 0, 0}}, + y = { .ui32={0, 0, 0, 0}}; + + /* CBC */ + for (uint32_t i = 1; i <= num_blocks - 1; i++) { + y = block_xor(&x, BLOCK(i)); + x = aes128_ecb(&y); + } + y = block_xor(&x, BLOCK(num_blocks)); + block_t tag = aes128_ecb(&y); + return tag; +} + diff --git a/cmac.h b/cmac.h new file mode 100644 index 0000000..deaade4 --- /dev/null +++ b/cmac.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#include "block.h" + +block_t cmac_aes128(uint8_t *, size_t); +void cmac_aes128_expand_key(block_t *, block_t *); +void cmac_aes128_init(block_t *); +void cmac_truncate_tag(uint8_t *, block_t *, uint_fast8_t); diff --git a/main.c b/main.c new file mode 100644 index 0000000..7bbf454 --- /dev/null +++ b/main.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2016 Jaycon Systems, LLC. All Rights Reserved */ + +#include + +#include "nrf.h" +#include "nrf_gpio.h" + +#include "cmac.h" + +#define ERROR_LED_PIN 12 +#define ERROR_LED_ACTIVE LOW + +#if (ERROR_LED_ACTIVE == LOW) +#define ERROR_LED_OFF nrf_gpio_pin_set(ERROR_LED_PIN) +#define ERROR_LED_ON nrf_gpio_pin_clear(ERROR_LED_PIN) +#else +#define ERROR_LED_OFF nrf_gpio_pin_clear(ERROR_LED_PIN) +#define ERROR_LED_ON nrf_gpio_pin_set(ERROR_LED_PIN) +#endif /* ERROR_LET_ACTIVE */ + +void test_cmac(void) { + /* NIST Examples */ + block_t k = { .ui8={0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}}; + cmac_aes128_init(&k); + /* Test Null Message, NIST Example */ + uint8_t msg1[] = ""; + block_t case1 = {.ui8={0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46}}; + block_t tag = cmac_aes128(msg1, 0); + if (!block_cmp(&tag, &case1)) { + ERROR_LED_ON; + while(1); + } + /* 16b example */ + uint8_t msg2[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; + block_t case2 = {.ui8={0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c}}; + tag = cmac_aes128(msg2, sizeof(msg2)); + if (!block_cmp(&tag, &case2)) { + ERROR_LED_ON; + while(1); + } + /* 40b example */ + uint8_t msg3[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11}; + block_t case3 = {.ui8={0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27}}; + tag = cmac_aes128(msg3, sizeof(msg3)); + if (!block_cmp(&tag, &case3)) { + ERROR_LED_ON; + while(1); + } + /* 64b example */ + uint8_t msg4[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}; + block_t case4 = {.ui8={0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe}}; + tag = cmac_aes128(msg4, sizeof(msg4)); + if (!block_cmp(&tag, &case4)) { + ERROR_LED_ON; + while(1); + } +} + +int main(void) { + nrf_gpio_cfg_output(ERROR_LED_PIN); + ERROR_LED_OFF; + for (;;) { + test_cmac(); + } +} diff --git a/template/Makefile b/template/Makefile new file mode 100644 index 0000000..2c39a6e --- /dev/null +++ b/template/Makefile @@ -0,0 +1,174 @@ +TOOLCHAIN_PREFIX ?= arm-none-eabi + +AS = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-as +CC = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-gcc +LD = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-gcc +OBJCOPY = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-objcopy +OBJDUMP = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-objdump +SIZE = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-size +GDB = $(TOOLCHAIN_PATH)$(TOOLCHAIN_PREFIX)-gdb + +START_CODE ?= startup_nrf51.s +SYSTEM_FILE ?= system_nrf51.c + +ifdef DEV_BOARD + CFLAGS += -DDEV_BOARD +endif + +ifeq ($(USE_SOFTDEVICE), s110) + CFLAGS += -DS110 + USE_BLE = 1 +endif + +ifeq ($(USE_SOFTDEVICE), s120) + CFLAGS += -DS120 + USE_BLE = 1 +endif + +ifeq ($(USE_SOFTDEVICE), s310) + USE_BLE = 1 + USE_ANT = 1 + CFLAGS += -DS310 +endif + +LIBRARY_PATHS += $(TEMPLATE_PATH) +LIBRARY_PATHS += $(SDK_PATH)components/toolchain/gcc/ +LIBRARY_PATHS += $(SDK_PATH)components/toolchain/ +LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/libraries/*/) + +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/ble_flash/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/clock/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/common/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/config/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/delay/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/gpiote/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/hal/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/lpcomp/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/ppi/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/pstorage/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/qdec/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/radio_config/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/rng/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/rtc/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/sdio/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/spi_master/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/spi_slave/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/swi/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/timer/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/twi_master/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/uart/ +LIBRARY_PATHS += $(SDK_PATH)/components/drivers_nrf/wdt/ + +LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/device/) +LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/serialization/*/) + +ifdef USE_BLE + LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/ble/*/) + LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/ble/ble_services/*/) + CFLAGS += -DBLE_STACK_SUPPORT_REQD +endif + +ifdef USE_ANT + CFLAGS += -DANT_STACK_SUPPORT_REQD +endif + +ifdef USE_SOFTDEVICE + CFLAGS += -DSOFTDEVICE_PRESENT + LIBRARY_PATHS += $(SDK_PATH)components/softdevice/common/softdevice_handler/ + LIBRARY_PATHS += $(SDK_PATH)components/softdevice/$(USE_SOFTDEVICE)/headers/ +else + LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/drivers_nrf/nrf_soc_nosd/) + USE_SOFTDEVICE = blank +endif + +LINKER_SCRIPT ?= gcc_nrf51_$(USE_SOFTDEVICE).ld +OUTPUT_NAME ?= $(addsuffix _$(USE_SOFTDEVICE)_$(VERSION), $(PROJECT_NAME)) + +ifdef USE_EXT_SENSORS + LIBRARY_PATHS += $(wildcard $(SDK_PATH)components/ext_sensors/*/) +endif + +LIBRARY_INCLUDES = $(addprefix -I,$(LIBRARY_PATHS)) + +VPATH = $(LIBRARY_PATHS) + +OUTPUT_PATH = _build/ + +CPUFLAGS += -mthumb -mcpu=cortex-m0 -mabi=aapcs -mfloat-abi=soft +ifdef BOARD +CFLAGS += -DBOARD=$(BOARD) +endif +CFLAGS += -std=gnu99 -c $(CPUFLAGS) -Wall -D$(DEVICE) $(LIBRARY_INCLUDES) -MD +# keep every function in separate section. This will allow linker to dump unused functions +CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing +CFLAGS += -fno-builtin --short-enums + +# keep every function in separate section. This will allow linker to dump unused functions +LDFLAGS += -mthumb -mabi=aapcs -L $(TEMPLATE_PATH) -T$(LINKER_SCRIPT) +LDFLAGS += -mcpu=cortex-m0 +LDFLAGS += -Wl,--gc-sections +# use newlib in nano version +LDFLAGS += --specs=nano.specs -lc -lnosys +LDFLAGS += -Wl,-Map=$(OUTPUT_PATH)$(OUTPUT_NAME).Map + +HEX = $(OUTPUT_PATH)$(OUTPUT_NAME).hex +ELF = $(OUTPUT_PATH)$(OUTPUT_NAME).elf +BIN = $(OUTPUT_PATH)$(OUTPUT_NAME).bin + +SRCS = $(SYSTEM_FILE) $(notdir $(APPLICATION_SRCS)) +OBJS = $(addprefix $(OUTPUT_PATH), $(SRCS:.c=.o)) $(addprefix $(OUTPUT_PATH),$(APPLICATION_LIBS)) +DEPS = $(addprefix $(OUTPUT_PATH), $(SRCS:.c=.d)) +SRCS_AS = $(START_CODE) +OBJS_AS = $(addprefix $(OUTPUT_PATH), $(SRCS_AS:.s=.os)) + +JLINK_OPTIONS = -device nrf51822 -if swd -speed 1000 + +all: $(OBJS) $(OBJS_AS) $(HEX) + +rebuild: clean all + +bootloader: clean + make -C bootloader + +combined: bootloader all + srec_cat \ + softdevice/softdevice.hex -intel \ + bootloader/_build/sdk10_bootloader_s110.hex -intel -exclude 0x3FC00 0x3FC04 \ + _build/hiro_s110_$(VERSION).hex -intel \ + -generate 0x3FC00 0x3FC04 -const-le 0x01 4 \ + -output bulk-program/$(PROJECT_NAME)-combined-$(VERSION).hex -intel + ln bulk-program/$(PROJECT_NAME)-combined-$(VERSION).hex bulk-program/$(PROJECT_NAME)-combined.hex + +# combined: rebuild +# srec_cat \ +# softdevice/softdevice.hex -intel \ +# _build/$(PROJECT_NAME)_$(USE_SOFTDEVICE)_$(VERSION).hex -intel \ +# -output bulk-program/combined.hex -intel +# ln -s bulk-program/combined.hex bulk-program/$(PROJECT_NAME)-$(VERSION)-combined.hex + +ifeq ($(OS),Windows_NT) +include $(TEMPLATE_PATH)Makefile.windows +else +include $(TEMPLATE_PATH)Makefile.posix +endif + +$(HEX): $(OBJS) $(OBJS_AS) + $(LD) $(LDFLAGS) $(OBJS_AS) $(OBJS) -o $(ELF) + $(OBJCOPY) -Oihex $(ELF) $(HEX) + $(OBJCOPY) -Obinary $(ELF) $(BIN) + $(SIZE) $(ELF) + +size: $(ELF) + $(SIZE) $(ELF) + +$(OUTPUT_PATH)%.o: %.c + $(MAKE_BUILD_FOLDER) + $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@ + +$(OUTPUT_PATH)%.os: %.s + $(MAKE_BUILD_FOLDER) + $(AS) $< -o $@ + +-include $(DEPS) + +.PHONY: all clean rebuild size diff --git a/template/Makefile.posix b/template/Makefile.posix new file mode 100644 index 0000000..60c7c12 --- /dev/null +++ b/template/Makefile.posix @@ -0,0 +1,83 @@ +# Set toolchain path if arm-none-eabi- binaries are not in user path +#TOOLCHAIN_PATH ?= +TERMINAL ?= gnome-terminal -e + +FLASH_START_ADDRESS = $(shell $(OBJDUMP) -h $(ELF) -j .text | grep .text | awk '{print $$4}') + +ifdef SEGGER_SERIAL +JLINKEXE_OPTION = -SelectEmuBySn $(SEGGER_SERIAL) +JLINKGDBSERVER_OPTION = -select USB=$(SEGGER_SERIAL) +endif + +MAKE_BUILD_FOLDER = mkdir -p $(OUTPUT_PATH) + +JLINK = -JLinkExe $(JLINK_OPTIONS) $(JLINKEXE_OPTION) +JLINKGDBSERVER = JLinkGDBServer $(JLINK_OPTIONS) $(JLINKGDBSERVER_OPTION) + +SOFTDEVICE_OUTPUT = $(OUTPUT_PATH)$(notdir $(SOFTDEVICE)) + +clean: + rm -rf $(OUTPUT_PATH) + rm -f *.jlink + rm -f JLink.log + rm -f .gdbinit + rm -f bulk-program/*.hex + +flash: all flash.jlink + $(JLINK) flash.jlink + +flash.jlink: + printf "r\nloadbin $(BIN) $(FLASH_START_ADDRESS)\nr\ng\nexit\n" > flash.jlink + +flash-softdevice: erase-all flash-softdevice.jlink +ifndef SOFTDEVICE + $(error "You need to set the SOFTDEVICE command-line parameter to a path (without spaces) to the softdevice hex-file") +endif + + $(OBJCOPY) -Iihex -Obinary $(SOFTDEVICE) $(SOFTDEVICE_OUTPUT:.hex=.bin) + $(JLINK) flash-softdevice.jlink + +flash-softdevice.jlink: + # Write to NVMC to enable write. Write mainpart, write UICR. Assumes device is erased. + printf "w4 4001e504 1\nloadbin \"$(SOFTDEVICE_OUTPUT:.hex=.bin)\" 0\nr\ng\nexit\n" > flash-softdevice.jlink + +recover: recover.jlink erase-all.jlink pin-reset.jlink + $(JLINK) recover.jlink + $(JLINK) erase-all.jlink + $(JLINK) pin-reset.jlink + +recover.jlink: + printf "si 0\nt0\nsleep 1\ntck1\nsleep 1\nt1\nsleep 2\nt0\nsleep 2\nt1\nsleep 2\nt0\nsleep 2\nt1\nsleep 2\nt0\nsleep 2\nt1\nsleep 2\nt0\nsleep 2\nt1\nsleep 2\nt0\nsleep 2\nt1\nsleep 2\nt0\nsleep 2\nt1\nsleep 2\ntck0\nsleep 100\nsi 1\nr\nexit\n" > recover.jlink + +pin-reset.jlink: + printf "w4 40000544 1\nr\nexit\n" > pin-reset.jlink + +pin-reset: pin-reset.jlink + $(JLINK) pin-reset.jlink + +reset: reset.jlink + $(JLINK) reset.jlink + +reset.jlink: + printf "r\ng\nexit\n" > reset.jlink + +erase-all: erase-all.jlink + $(JLINK) erase-all.jlink + +erase-all.jlink: + # Write to NVMC to enable erase, do erase all, wait for completion. reset + printf "w4 4001e504 2\nw4 4001e50c 1\nsleep 100\nr\nexit\n" > erase-all.jlink + +startdebug: debug-gdbinit + $(TERMINAL) "$(JLINKGDBSERVER) -port $(GDB_PORT_NUMBER)" + sleep 1 + $(TERMINAL) "$(GDB) $(ELF)" + +debug-gdbinit: + printf "target remote localhost:$(GDB_PORT_NUMBER)\nbreak main\n" > .gdbinit + +.PHONY: flash flash-softdevice erase-all startdebug + +test: + gcc -g -std=gnu99 -Wall -Werror -I.. -DHOST_BUILD -lcrypto block.c aes.c cmac.c tests/test_cmac.c -o tests/test_cmac + ./tests/test_cmac diff --git a/template/Makefile.windows b/template/Makefile.windows new file mode 100644 index 0000000..aa78e69 --- /dev/null +++ b/template/Makefile.windows @@ -0,0 +1,51 @@ +TOOLCHAIN_PATH = "c:\Program Files (x86)\GNU Tools ARM Embedded\4.8 2013q4\bin\" +JLINK_PATH = "C:\Program Files (x86)\SEGGER\JLinkARM_V480\" + +MAKE_BUILD_FOLDER = if not exist $(OUTPUT_PATH:/=\\) mkdir $(OUTPUT_PATH:/=\\) + +# Make GDB server die after one run. +JLINKGDBSERVER_OPTION = -s + +ifdef SEGGER_SERIAL +NRFJPROG_OPTIONS = --snr $(SEGGER_SERIAL) +JLINKGDBSERVER_OPTION += -select USB=$(SEGGER_SERIAL) +endif + +NRFJPROG = nrfjprog $(NRFJPROG_OPTIONS) + +clean: + if exist .\$(OUTPUT_PATH:/=\\) rmdir /Q /S $(OUTPUT_PATH:/=) + if exist *.jlink del /q *.jlink + if exist JLink.log del /q JLink.log + if exist .gdbinit del /q .gdbinit + +$(OUTPUT_PATH): + if not exist .\$(OUTPUT_PATH:/=\\) md $(OUTPUT_PATH) + +flash: + $(NRFJPROG) --program $(HEX) -r + +flash-softdevice: +ifndef SOFTDEVICE + $(error "You need to set the SOFTDEVICE command-line parameter to a path (without spaces) to the softdevice hex-file") +endif + $(NRFJPROG) -e --programs "$(SOFTDEVICE)" + +recover: + $(NRFJPROG) --recover + +pin-reset: + $(NRFJPROG) --pinreset + +reset: + $(NRFJPROG) --reset + +startdebug: debug-gdbinit + start /D $(JLINK_PATH) JLinkGDBServer $(JLINKGDBSERVER_OPTION) $(JLINK_OPTIONS) -port $(GDB_PORT_NUMBER) + timeout /t 1 + $(GDB) $(ELF) -x gdbinit + +debug-gdbinit: + @(echo target remote localhost:$(GDB_PORT_NUMBER) & echo break main) > gdbinit + +.PHONY: clean flash startdebug debug-gdbinit diff --git a/template/gcc_nrf51_blank.ld b/template/gcc_nrf51_blank.ld new file mode 100644 index 0000000..e498f39 --- /dev/null +++ b/template/gcc_nrf51_blank.ld @@ -0,0 +1,7 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x40000 /* 256k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x4000 /* 16k */ +} +INCLUDE "gcc_nrf51_common.ld" diff --git a/template/gcc_nrf51_common.ld b/template/gcc_nrf51_common.ld new file mode 100644 index 0000000..9bdef16 --- /dev/null +++ b/template/gcc_nrf51_common.ld @@ -0,0 +1,140 @@ +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.isr_vector)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE (__preinit_array_start = .); + *(.preinit_array) + PROVIDE (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE (__init_array_start = .); + *(SORT(.init_array.*)) + *(.init_array) + PROVIDE (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE (__fini_array_end = .); + + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + __bss_start__ = .; + *(.bss*) + *(COMMON) + __bss_end__ = .; + } > RAM + + .heap : + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy : + { + *(.stack) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/template/gcc_nrf51_s110.ld b/template/gcc_nrf51_s110.ld new file mode 100644 index 0000000..483719b --- /dev/null +++ b/template/gcc_nrf51_s110.ld @@ -0,0 +1,7 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x0 + 96K, LENGTH = 128K - 96K /* 88 kB is taken by S110, rest for app. */ + RAM (rwx) : ORIGIN = 0x20000000 + 8K, LENGTH = 16K - 8K /* 8 kB is taken by S110, 8 kB for app. */ +} +INCLUDE "gcc_nrf51_common.ld" diff --git a/template/gcc_nrf51_s120.ld b/template/gcc_nrf51_s120.ld new file mode 100644 index 0000000..6c6bdf1 --- /dev/null +++ b/template/gcc_nrf51_s120.ld @@ -0,0 +1,7 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x18000, LENGTH = 0x27000 /* 96 kB is taken by S120, 160 kB for app. */ + RAM (rwx) : ORIGIN = 0x20002800, LENGTH = 0x1800 /* 10 kB is taken by S120, 6 kB for app. */ +} +INCLUDE "gcc_nrf51_common.ld" diff --git a/template/gcc_nrf51_s210.ld b/template/gcc_nrf51_s210.ld new file mode 100644 index 0000000..dbb535b --- /dev/null +++ b/template/gcc_nrf51_s210.ld @@ -0,0 +1,7 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0xC000, LENGTH = 0x34000 /* 48 kB is taken by S210, 80 kB for app. */ + RAM (rwx) : ORIGIN = 0x20000900, LENGTH = 0x3700 /* 2.25 kB is taken by S210, 13.75 kB for app. */ +} +INCLUDE "gcc_nrf51_common.ld" diff --git a/template/gcc_nrf51_s310.ld b/template/gcc_nrf51_s310.ld new file mode 100644 index 0000000..b210618 --- /dev/null +++ b/template/gcc_nrf51_s310.ld @@ -0,0 +1,7 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x20000, LENGTH = 0x20000 /* 128 kB is taken by S310, 128 kB for app. */ + RAM (rwx) : ORIGIN = 0x20002800, LENGTH = 0x1800 /* 10 kB is taken by S310, 6 kB for app. */ +} +INCLUDE "gcc_nrf51_common.ld" diff --git a/template/startup_nrf51.s b/template/startup_nrf51.s new file mode 100644 index 0000000..1040f94 --- /dev/null +++ b/template/startup_nrf51.s @@ -0,0 +1,212 @@ +/* File: startup_ARMCM0.S + * Purpose: startup file for Cortex-M0 devices. Should use with + * GCC for ARM Embedded Processors + * Version: V1.2 + * Date: 15 Nov 2011 + * + * Copyright (c) 2011, ARM Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the ARM Limited nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + .syntax unified + .arch armv6-m + + .section .stack + .align 3 +#ifdef __STACK_SIZE + .equ Stack_Size, __STACK_SIZE +#else + .equ Stack_Size, 0xc00 +#endif + .globl __StackTop + .globl __StackLimit +__StackLimit: + .space Stack_Size + .size __StackLimit, . - __StackLimit +__StackTop: + .size __StackTop, . - __StackTop + + .section .heap + .align 3 +#ifdef __HEAP_SIZE + .equ Heap_Size, __HEAP_SIZE +#else + .equ Heap_Size, 0x100 +#endif + .globl __HeapBase + .globl __HeapLimit +__HeapBase: + .space Heap_Size + .size __HeapBase, . - __HeapBase +__HeapLimit: + .size __HeapLimit, . - __HeapLimit + + .section .isr_vector + .align 2 + .globl __Vectors +__Vectors: + .long __StackTop /* Top of Stack */ + .long Reset_Handler /* Reset Handler */ + .long NMI_Handler /* NMI Handler */ + .long HardFault_Handler /* Hard Fault Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long SVC_Handler /* SVCall Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long PendSV_Handler /* PendSV Handler */ + .long SysTick_Handler /* SysTick Handler */ + + /* External interrupts */ + .long POWER_CLOCK_IRQHandler /* POWER_CLOCK */ + .long RADIO_IRQHandler /* RADIO */ + .long UART0_IRQHandler /* UART0 */ + .long SPI0_TWI0_IRQHandler /* SPI0_TWI0 */ + .long SPI1_TWI1_IRQHandler /* SPI1_TWI1 */ + .long 0 /* Reserved */ + .long GPIOTE_IRQHandler /* GPIOTE */ + .long ADC_IRQHandler /* ADC */ + .long TIMER0_IRQHandler /* TIMER0 */ + .long TIMER1_IRQHandler /* TIMER1 */ + .long TIMER2_IRQHandler /* TIMER2 */ + .long RTC0_IRQHandler /* RTC0 */ + .long TEMP_IRQHandler /* TEMP */ + .long RNG_IRQHandler /* RNG */ + .long ECB_IRQHandler /* ECB */ + .long CCM_AAR_IRQHandler /* CCM_AAR */ + .long WDT_IRQHandler /* WDT */ + .long RTC1_IRQHandler /* RTC1 */ + .long QDEC_IRQHandler /* QDEC */ + .long 0 /* Reserved */ + .long SWI0_IRQHandler /* SWI0 */ + .long SWI1_IRQHandler /* SWI1 */ + .long SWI2_IRQHandler /* SWI2 */ + .long SWI3_IRQHandler /* SWI3 */ + .long SWI4_IRQHandler /* SWI4 */ + .long SWI5_IRQHandler /* SWI5 */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + + .size __Vectors, . - __Vectors + + .text + .thumb + .thumb_func + .align 2 + .globl Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + .equ NRF_POWER_RAMON_ADDRESS, 0x40000524 + .equ NRF_POWER_RAMON_RAM1ON_ONMODE_Msk, 0x3 + ldr r0, =NRF_POWER_RAMON_ADDRESS + ldr r2, [r0] + movs r1, #NRF_POWER_RAMON_RAM1ON_ONMODE_Msk + orrs r2, r1 + str r2, [r0] + +/* Loop to copy data from read only memory to RAM. The ranges + * of copy from/to are specified by following symbols evaluated in + * linker script. + * __etext: End of code section, i.e., begin of data sections to copy from. + * __data_start__/__data_end__: RAM address range that data should be + * copied to. Both must be aligned to 4 bytes boundary. */ + ldr r1, =__etext + ldr r2, =__data_start__ + ldr r3, =__data_end__ + + subs r3, r2 + ble .flash_to_ram_loop_end + + movs r4, 0 +.flash_to_ram_loop: + ldr r0, [r1,r4] + str r0, [r2,r4] + adds r4, 4 + cmp r4, r3 + blt .flash_to_ram_loop +.flash_to_ram_loop_end: + ldr r0, =SystemInit + blx r0 + ldr r0, =_start + bx r0 + .pool + .size Reset_Handler, . - Reset_Handler + +/* Macro to define default handlers. Default handler + * will be weak symbol and just dead loops. They can be + * overwritten by other handlers */ + .macro def_default_handler handler_name + .align 1 + .thumb_func + .weak \handler_name + .type \handler_name, %function +\handler_name : + b . + .size \handler_name, . - \handler_name + .endm + + def_default_handler NMI_Handler + def_default_handler HardFault_Handler + def_default_handler SVC_Handler + def_default_handler PendSV_Handler + def_default_handler SysTick_Handler + def_default_handler Default_Handler + def_default_handler POWER_CLOCK_IRQHandler + def_default_handler RADIO_IRQHandler + def_default_handler UART0_IRQHandler + def_default_handler SPI0_TWI0_IRQHandler + def_default_handler SPI1_TWI1_IRQHandler + def_default_handler GPIOTE_IRQHandler + def_default_handler ADC_IRQHandler + def_default_handler TIMER0_IRQHandler + def_default_handler TIMER1_IRQHandler + def_default_handler TIMER2_IRQHandler + def_default_handler RTC0_IRQHandler + def_default_handler TEMP_IRQHandler + def_default_handler RNG_IRQHandler + def_default_handler ECB_IRQHandler + def_default_handler CCM_AAR_IRQHandler + def_default_handler WDT_IRQHandler + def_default_handler RTC1_IRQHandler + def_default_handler QDEC_IRQHandler + def_default_handler SWI0_IRQHandler + def_default_handler SWI1_IRQHandler + def_default_handler SWI2_IRQHandler + def_default_handler SWI3_IRQHandler + def_default_handler SWI4_IRQHandler + def_default_handler SWI5_IRQHandler + + .weak DEF_IRQHandler + .set DEF_IRQHandler, Default_Handler + + .end