diff --git a/NOTES b/NOTES index 420a7bc..69f19ce 100644 --- a/NOTES +++ b/NOTES @@ -90,6 +90,6 @@ electrical, LEDs: even less demanding authentication: -- shared secret (128 bit, MD5-hashed text with 128 bit salt) +- shared secret (128 bit, SHA1-hashed text with 128 bit salt) - salt (128 bit) -- MD5 +- SHA1 from avrcryptolib diff --git a/fw/Makefile b/fw/Makefile index f05d224..443f05b 100644 --- a/fw/Makefile +++ b/fw/Makefile @@ -1,8 +1,8 @@ # -# Makefile - Makefile of the ATUSB firmware +# Makefile - Makefile of the Antorcha firmware # -# Written 2010-2011 by Werner Almesberger -# Copyright 2010-2011 by Werner Almesberger +# Written 2012 by Werner Almesberger +# Copyright 2012 by Werner Almesberger # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,13 +14,16 @@ SHELL = /bin/bash NAME = antorcha -CFLAGS = -g -mmcu=$(CHIP) -DBOOT_ADDR=$(BOOT_ADDR) \ +CFLAGS = -g -mmcu=$(CHIP) \ + -DBOOT_ADDR=$(BOOT_ADDR) -DAPP_ADDR=$(APP_ADDR) -DAPP_END=$(APP_END) \ -Wall -Wextra -Wshadow -Werror -Wno-unused-parameter \ -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -CHIP=atmega168 -HOST=jlime -BOOT_ADDR=0x7000 +CHIP = atmega168 +HOST = jlime +BOOT_ADDR = 0 +APP_ADDR = 0x1000 +APP_END = 0x4000 AVR_PREFIX = $(BIN_PATH) avr- CC = $(AVR_PREFIX)gcc @@ -28,11 +31,9 @@ OBJCOPY = $(AVR_PREFIX)objcopy #OBJDUMP = $(AVR_PREFIX)objdump SIZE = $(AVR_PREFIX)size -USB_ID = 20b7:1540 - -OBJS = $(NAME).o rf.o spi.o - -CFLAGS += -Iinclude +OBJS = $(NAME).o $(COMMON_OBJS) +BOOT_OBJS = boot.o flash.o fw.o $(COMMON_OBJS) +COMMON_OBJS = dispatch.o hash.o rf.o spi.o # ----- Verbosity control ----------------------------------------------------- @@ -56,18 +57,19 @@ endif # ----- Rules ----------------------------------------------------------------- -.PHONY: all clean upload prog update version.c bindist +.PHONY: all clean upload prog update version.c .PHONY: prog-app prog-read on off reset -all: $(NAME).bin +all: $(NAME).bin boot.bin $(NAME).elf: $(OBJS) $(MAKE) version.o - $(CC) $(CFLAGS) -o $@ $(OBJS) version.o + $(CC) $(CFLAGS) -o $@ $(OBJS) version.o \ + -Wl,--section-start=.text=$(APP_ADDR) -#boot.elf: $(BOOT_OBJS) -# $(CC) $(CFLAGS) -o $@ $(BOOT_OBJS) \ -# -Wl,--section-start=.text=$(BOOT_ADDR) +boot.elf: $(BOOT_OBJS) + $(CC) $(CFLAGS) -o $@ $(BOOT_OBJS) \ + -Wl,--section-start=.text=$(BOOT_ADDR) %.bin: %.elf $(BUILD) $(OBJCOPY) -j .text -j .data -O binary $< $@ @@ -120,25 +122,10 @@ MKDEP = \ -include $(OBJS:.o=.d) -# ----- Distribution ---------------------------------------------------------- - -BINDIST_BASE=http://downloads.qi-hardware.com/people/werner/wpan/bindist -ATUSB_BIN_NAME=atusb-`git rev-parse HEAD | cut -c 1-7`.bin - -bindist: - qippl atusb.bin wpan/bindist/$(ATUSB_BIN_NAME) - @echo $(BINDIST_BASE)/$(ATUSB_BIN_NAME) - @echo md5sum: `md5sum atusb.bin | sed 's/ .*//'` - @echo atrf-id: \ - `sed '/.*number = \(.*\);/s//#\1/p;d' version.c` \ - `sed '/.*date = "\(.*\)";/s//\1/p;d' version.c` - # ----- Programming and device control ---------------------------------------- -#upload: $(NAME).bin boot.hex -# scp $(NAME).bin boot.hex $(HOST): -upload: $(NAME).bin - scp $(NAME).bin $(HOST): +upload: $(NAME).bin boot.hex + scp $(NAME).bin boot.hex $(HOST): # lfuse: external clock, slow start-up # hfuse: 4 kB boot loader, reset into boot loader @@ -146,20 +133,20 @@ upload: $(NAME).bin # Note: when trying to program 0xef, we get back 0x2f, failing # verification. So we just program 0x2f. -prog: +prog-app: ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_antorcha -e \ -U flash:w:antorcha.bin:r # -U lfuse:w:0x60:m -#prog: -# ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb -e \ -# -U flash:w:boot.hex:i \ +prog: + ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_antorcha -e \ + -U flash:w:boot.hex:i # -U lfuse:w:0x60:m \ # -U hfuse:w:0xd8:m \ # -U lock:w:0x2f:m prog-read: - ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb \ + ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_antorcha \ -U flash:r:mcu.bin:r on: diff --git a/fw/boot.c b/fw/boot.c new file mode 100644 index 0000000..b019560 --- /dev/null +++ b/fw/boot.c @@ -0,0 +1,81 @@ +/* + * fw/boot.c - Antorcha boot loader + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include + +#include "io.h" +#include "rf.h" +#include "proto.h" +#include "dispatch.h" +#include "fw.h" + + +#define MS_TO_LOOP(ms) ((uint32_t) (ms)*335) + + +static void wait_upload(void) +{ + uint8_t buf[PAYLOAD+5]; + uint32_t i; + uint8_t got; + +restart: + for (i = 0; i != MS_TO_LOOP(2000); i++) { + got = rf_recv(buf, sizeof(buf)); + if (got && dispatch(buf, got, fw_protos)) + goto restart; + } + +} + + +int main(void) +{ + volatile int zero = 0; + + /* Port B has two LEDs and the rest is SPI */ + PORTB = MASK(B, RF_nSS) | MASK(B, RF_nRST); + DDRB = MASK(B, RF_SCLK) | MASK(B, RF_MOSI) | MASK(B, RF_nSS) | + MASK(B, RF_nRST) | 0xc0; + + /* All port C pins drive LEDs */ + PORTC = 0; + DDRC = 0x3f; + + /* All port D pins drive LEDs */ + PORTD = 0; + DDRD = 0xff; + + /* disable pull-ups */ + MCUCR |= 1 << PUD; + + rf_init(); + + /* + * Switch the LED inside the loop so that we get a short pulse one can + * observe on a scope. + */ + do { + SET(LED_B8); + wait_upload(); + CLR(LED_B8); +while (1); + } while (pgm_read_byte(zero) != 0xff); + + + ((void (*)(void)) 0)(); + + /* not reached */ + return 0; +} diff --git a/fw/dispatch.c b/fw/dispatch.c new file mode 100644 index 0000000..e80d3c9 --- /dev/null +++ b/fw/dispatch.c @@ -0,0 +1,104 @@ +/* + * fw/dispatch.c - Wireless protocol dispatcher + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include + +#define F_CPU 8000000UL +#include + +#include "rf.h" +#include "proto.h" +#include "dispatch.h" +#include "io.h" + + +static uint8_t seq; /* last sequence number seen */ +static uint8_t type; /* type currently being processed */ +static uint8_t limit; /* last limit seen */ +static const struct handler *curr_proto = NULL; + + +static void send_ack(const uint8_t *buf) +{ + uint8_t ack[3] = { buf[0]+1, buf[1], 0 }; + +SET(LED_B6); + _delay_ms(1); + rf_send(ack, sizeof(ack)); +CLR(LED_B6); +} + + +static bool answer_ping(const uint8_t *buf) +{ + uint8_t pong[] = { PONG, 0, 0 }; + + if (buf[1]) + return 0; + rf_send(pong, sizeof(pong)); + return 1; +} + + +bool dispatch(const uint8_t *buf, uint8_t len, const struct handler **protos) +{ +SET(LED_B7); +CLR(LED_B7); + if (len == 3 && buf[0] == PING) + return answer_ping(buf); + + if (len != 64+3) + return 0; + + if (!buf[1]) { + while (*protos) { + if ((*protos)->type == buf[0]) + break; + protos++; + } + if (!*protos) + return 0; + if (!(*protos)->first(buf+3)) + return 0; + curr_proto = *protos; + type = buf[0]; + seq = 0; + limit = buf[2]; + send_ack(buf); + return 1; + } + + if (!curr_proto) + return 0; + if (buf[0] != type) + return 0; + if (buf[1] > limit) + return 0; + if (buf[2] != limit) + return 0; + + if (buf[1] == seq) { + send_ack(buf); + return 0; + } + if (buf[1] != seq+1) + return 0; + if (!curr_proto->more(buf[1], limit, buf+3)) + return 0; + seq++; + send_ack(buf); + return 1; + +} diff --git a/fw/dispatch.h b/fw/dispatch.h new file mode 100644 index 0000000..51a4bc3 --- /dev/null +++ b/fw/dispatch.h @@ -0,0 +1,29 @@ +/* + * fw/dispatch.h - Wireless protocol dispatcher + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef DISPATCH_H +#define DISPATCH_H + +#include +#include + + +struct handler { + enum pck_type type; + bool (*first)(const uint8_t *payload); + bool (*more)(uint8_t seq, uint8_t limit, const uint8_t *payload); +}; + + +bool dispatch(const uint8_t *buf, uint8_t len, const struct handler **protos); + +#endif /* !PROTO_H */ diff --git a/fw/flash.c b/fw/flash.c new file mode 100644 index 0000000..b0435ef --- /dev/null +++ b/fw/flash.c @@ -0,0 +1,73 @@ +/* + * fw/flash.c - Flash interface + * + * Written 2011, 2012 by Werner Almesberger + * Copyright 2011, 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Adapted from ben-wpan/atusb/fw/flash.c + */ + + +#include + +#include +//#include + +#include "flash.h" + + +static uint32_t payload; + + +void flash_start(uint32_t addr) +{ + payload = addr; +} + + +int flash_can_write(uint16_t size) +{ + return payload <= APP_END-size; +} + + +void flash_write(const uint8_t *buf, uint16_t size) +{ + static uint8_t last; + const uint8_t *p; + + for (p = buf; p != buf+size; p++) { + if (!(payload & (SPM_PAGESIZE-1))) { + boot_page_erase(payload); + boot_spm_busy_wait(); + } + + if (payload & 1) + boot_page_fill(payload, last | (*p << 8)); + else + last = *p; + payload++; + + if (!(payload & (SPM_PAGESIZE-1))) { + boot_page_write(payload-SPM_PAGESIZE); + boot_spm_busy_wait(); + } + } +} + + +void flash_end_write(void) +{ + if (payload & (SPM_PAGESIZE-1)) { + boot_page_write(payload & ~(SPM_PAGESIZE-1)); + boot_spm_busy_wait(); + } + boot_rww_enable(); +} diff --git a/fw/flash.h b/fw/flash.h new file mode 100644 index 0000000..c43c12a --- /dev/null +++ b/fw/flash.h @@ -0,0 +1,24 @@ +/* + * fw/flash.h - Flash interface + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef FLASH_H +#define FLASH_H + +#include + + +void flash_start(uint32_t addr); +int flash_can_write(uint16_t size); +void flash_write(const uint8_t *buf, uint16_t size); +void flash_end_write(void); + +#endif /* !FLASH_H */ diff --git a/fw/fw.c b/fw/fw.c new file mode 100644 index 0000000..e3bcec0 --- /dev/null +++ b/fw/fw.c @@ -0,0 +1,133 @@ +/* + * fw/fw.h - Firmware upload protocols + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include + +#include "hash.h" +#include "flash.h" +#include "proto.h" +#include "dispatch.h" +#include "fw.h" + + +static const uint8_t unlock_secret[] = { + #include "unlock-secret.inc" +}; + + +static void panic(void) +{ + /* ??? */ +} + + +/* ----- Unlocking --------------------------------------------------------- */ + + +static bool unlocked = 0; +static bool unlock_failed; + + +static bool unlock_first(const uint8_t *payload) +{ + hash_init(); + hash_merge(unlock_secret, sizeof(unlock_secret)); + hash_merge(payload, PAYLOAD); + unlocked = 0; + unlock_failed = 0; + return 1; +} + + +static bool unlock_more(uint8_t seq, uint8_t limit, const uint8_t *payload) +{ + switch (seq) { + case 1: + hash_merge(payload, PAYLOAD); + hash_end(); + break; + case 2: + if (!hash_eq(payload, PAYLOAD, 0)) + unlock_failed = 1; + break; + case 3: + if (unlock_failed) + return 1; + if (hash_eq(payload, PAYLOAD, PAYLOAD)) + unlocked = 1; + else + unlock_failed = 1; + break; + default: + return 0; + } + return 1; +} + + +static const struct handler unlock_proto = { + .type = UNLOCK, + .first = unlock_first, + .more = unlock_more, +}; + + +/* ----- Firmware upload --------------------------------------------------- */ + + +static bool fw_first(const uint8_t *payload) +{ +// if (!unlocked) +// return 0; + hash_init(); + hash_merge(payload, PAYLOAD); + flash_start(APP_ADDR); + flash_write(payload, PAYLOAD); + return 1; +} + + +static bool fw_more(uint8_t seq, uint8_t limit, const uint8_t *payload) +{ + if (!flash_can_write(PAYLOAD)) + return 0; + if (seq != limit) { + hash_merge(payload, PAYLOAD); + flash_write(payload, PAYLOAD); + return 1; + } + flash_end_write(); + hash_end(); + if (!hash_eq(payload, PAYLOAD, 0)) + panic(); + return 1; +} + + +static const struct handler fw_proto = { + .type = FIRMWARE, + .first = fw_first, + .more = fw_more, +}; + + +/* ----- Protocol table ---------------------------------------------------- */ + + +const struct handler *fw_protos[] = { + &unlock_proto, + &fw_proto, + NULL +}; diff --git a/fw/fw.h b/fw/fw.h new file mode 100644 index 0000000..62dfcb7 --- /dev/null +++ b/fw/fw.h @@ -0,0 +1,21 @@ +/* + * fw/fw.h - Firmware upload protocols + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef FW_H +#define FW_H + +#include "proto.h" + + +extern const struct handler *fw_protos[]; + +#endif /* !FW_H */ diff --git a/fw/hash.c b/fw/hash.c new file mode 100644 index 0000000..e962f0c --- /dev/null +++ b/fw/hash.c @@ -0,0 +1,61 @@ +/* + * fw/hash.h - Secure hash + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include + +#include "hash.h" + + +#define HASH_SIZE 128 + + +static uint8_t hash[HASH_SIZE]; + + +void hash_init(void) +{ + memset(hash, 0, HASH_SIZE); +} + + +void hash_merge(const uint8_t *buf, uint8_t len) +{ + uint8_t i; + + for (i = 0; i != len; i++) + hash[i & (HASH_SIZE-1)] ^= buf[i]; +} + + +void hash_end(void) +{ +} + + +bool hash_eq(const uint8_t *buf, uint8_t len, uint8_t off) +{ + uint8_t i; + + for (i = 0; i != len; i++) + if (hash[(off+i) & (HASH_SIZE-1)] != buf[i]) + return 0; + return 1; +} + + +void hash_cp(uint8_t *buf, uint8_t len, uint8_t off) +{ + memcpy(buf, hash+off, len); +} diff --git a/fw/hash.h b/fw/hash.h new file mode 100644 index 0000000..253b9db --- /dev/null +++ b/fw/hash.h @@ -0,0 +1,26 @@ +/* + * fw/hash.h - Secure hash + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef HASH_H +#define HASH_H + +#include +#include + + +void hash_init(void); +void hash_merge(const uint8_t *buf, uint8_t len); +void hash_end(void); +bool hash_eq(const uint8_t *buf, uint8_t len, uint8_t off); +void hash_cp(uint8_t *buf, uint8_t len, uint8_t off); + +#endif /* !HASH_H */ diff --git a/fw/io.h b/fw/io.h index 7e6979f..ab616bb 100644 --- a/fw/io.h +++ b/fw/io.h @@ -13,6 +13,11 @@ #ifndef IO_H #define IO_H +#include + + +#define LED_B6 D, 5 +#define LED_B7 D, 6 #define LED_B8 D, 7 #define RF_SCLK B, 5 diff --git a/fw/proto.h b/fw/proto.h new file mode 100644 index 0000000..1679529 --- /dev/null +++ b/fw/proto.h @@ -0,0 +1,31 @@ +/* + * fw/proto.h - Wireless protocol constants + * + * Written 2012 by Werner Almesberger + * Copyright 2012 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef PROTO_H +#define PROTO_H + +#define PAYLOAD 64 /* most messages use a fixed 64 bytes payload */ + +enum pck_type { + PING = 0, /* version query */ + PONG = 1, /* version response */ + UNLOCK = 2, /* unlock firmware upload */ + UNLOCK_ACK = 3, /* unlock acknowledgement */ + FIRMWARE = 4, /* firmware upload */ + FIRMWARE_ACK = 5, /* firmware upload acknowledgement */ + IMAGE = 6, /* image upload */ + IMAGE_ACK = 7, /* image upload acknowledgement */ + PARAM = 8, /* parameter upload */ + PARAM_ACK = 9, /* parameter upload acknowledgement */ +}; + +#endif /* !PROTO_H */ diff --git a/fw/rf.c b/fw/rf.c index ed5d819..a7a6303 100644 --- a/fw/rf.c +++ b/fw/rf.c @@ -44,8 +44,8 @@ static uint8_t reg_read(uint8_t reg) uint8_t value; spi_begin(); - spi_io(AT86RF230_REG_READ | reg); - value = spi_io(0); + spi_send(AT86RF230_REG_READ | reg); + value = spi_recv(); spi_end(); return value; @@ -55,11 +55,12 @@ static uint8_t reg_read(uint8_t reg) static void reg_write(uint8_t reg, uint8_t value) { spi_begin(); - spi_io(AT86RF230_REG_WRITE | reg); - spi_io(value); + spi_send(AT86RF230_REG_WRITE | reg); + spi_send(value); spi_end(); } + void rf_init(void) { spi_init(); @@ -89,10 +90,10 @@ void rf_send(const void *buf, uint8_t size) _delay_us(1); /* tTR9 = 1 us */ spi_begin(); - spi_io(AT86RF230_BUF_WRITE); - spi_io(size+2); /* CRC */ + spi_send(AT86RF230_BUF_WRITE); + spi_send(size+2); /* CRC */ for (i = 0; i != size; i++) - spi_io(((const uint8_t *) buf)[i]); + spi_send(((const uint8_t *) buf)[i]); spi_end(); reg_read(REG_IRQ_STATUS); @@ -122,8 +123,8 @@ uint8_t rf_recv(void *buf, uint8_t size) return 0; spi_begin(); - spi_io(AT86RF230_BUF_READ); - len = spi_io(0); + spi_send(AT86RF230_BUF_READ); + len = spi_recv(); if (!len || (len & 0x80)) { spi_end(); return 0; @@ -131,7 +132,7 @@ uint8_t rf_recv(void *buf, uint8_t size) if (size > len) size = len; for (i = 0; i != size; i++) - ((uint8_t *) buf)[i] = spi_io(0); + ((uint8_t *) buf)[i] = spi_recv(); spi_end(); return len; } diff --git a/fw/spi.h b/fw/spi.h index d07d592..e427300 100644 --- a/fw/spi.h +++ b/fw/spi.h @@ -21,4 +21,26 @@ void spi_begin(void); uint8_t spi_io(uint8_t v); void spi_end(void); + +/* + * Interestingly, the #defines below produce slightly shorter code (6 bytes) + * than the inlines. + */ + +#if 0 +static inline void spi_send(uint8_t v) +{ + spi_io(v); +} + + +static inline uint8_t spi_recv(void) +{ + return spi_io(0); +} +#endif + +#define spi_send(v) spi_io(v) +#define spi_recv() spi_io(0) + #endif /* !SPI_H */ diff --git a/fw/unlock-secret.inc b/fw/unlock-secret.inc new file mode 100644 index 0000000..2739d72 --- /dev/null +++ b/fw/unlock-secret.inc @@ -0,0 +1 @@ +1, 2, 3