From 1f80a3a3dcb47af6fed9e3552e5ccfcb289525dd Mon Sep 17 00:00:00 2001 From: Shawn Nock Date: Wed, 1 Jun 2016 15:07:50 -0400 Subject: [PATCH] Initial Commit --- .gitignore | 1 + Makefile | 18 +++ README.md | 26 +++++ aes.c | 41 +++++++ aes.h | 15 +++ block.c | 71 ++++++++++++ block.h | 16 +++ cmac.c | 92 +++++++++++++++ cmac.h | 11 ++ main.c | 82 ++++++++++++++ template/Makefile | 174 ++++++++++++++++++++++++++++ template/Makefile.posix | 83 ++++++++++++++ template/Makefile.windows | 51 +++++++++ template/gcc_nrf51_blank.ld | 7 ++ template/gcc_nrf51_common.ld | 140 +++++++++++++++++++++++ template/gcc_nrf51_s110.ld | 7 ++ template/gcc_nrf51_s120.ld | 7 ++ template/gcc_nrf51_s210.ld | 7 ++ template/gcc_nrf51_s310.ld | 7 ++ template/startup_nrf51.s | 212 +++++++++++++++++++++++++++++++++++ 20 files changed, 1068 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 aes.c create mode 100644 aes.h create mode 100644 block.c create mode 100644 block.h create mode 100644 cmac.c create mode 100644 cmac.h create mode 100644 main.c create mode 100644 template/Makefile create mode 100644 template/Makefile.posix create mode 100644 template/Makefile.windows create mode 100644 template/gcc_nrf51_blank.ld create mode 100644 template/gcc_nrf51_common.ld create mode 100644 template/gcc_nrf51_s110.ld create mode 100644 template/gcc_nrf51_s120.ld create mode 100644 template/gcc_nrf51_s210.ld create mode 100644 template/gcc_nrf51_s310.ld create mode 100644 template/startup_nrf51.s 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