From 8a6aec16d8c21d3c57457e5ec00eb83f5242feba Mon Sep 17 00:00:00 2001 From: "henryk@ploetzli.ch" Date: Fri, 28 Aug 2009 06:40:44 +0000 Subject: [PATCH] Implement version information storage and retrieval for the bootrom and the osimage. Use perl to create the version information (thereby re-creating the perl dependency and adding an svn dependency) but fall back in case of missing perl or svn --- armsrc/Makefile | 2 ++ armsrc/appmain.c | 18 ++++++++++ armsrc/apps.h | 1 + armsrc/ldscript | 1 + armsrc/util.c | 34 +++++++++++++++++++ bootrom/Makefile | 5 +-- bootrom/fromflash.c | 2 +- bootrom/ldscript-flash | 12 +++++++ common/Makefile.common | 7 ++++ common/default_version.c | 9 +++++ common/version.c | 10 ------ include/proxmark3.h | 10 ++++++ tools/mkversion.pl | 73 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 common/default_version.c delete mode 100644 common/version.c create mode 100644 tools/mkversion.pl diff --git a/armsrc/Makefile b/armsrc/Makefile index 5ed76ccc..13e43ba9 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -13,6 +13,7 @@ THUMBSRC = start.c \ lfops.c \ iso15693.c \ util.c \ + version.c \ usb.c # These are to be compiled in ARM mode @@ -43,6 +44,7 @@ clean: $(DELETE) $(OBJDIR)$(PATHSEP)*.s19 $(DELETE) $(OBJDIR)$(PATHSEP)*.map $(DELETE) $(OBJDIR)$(PATHSEP)*.d + $(DELETE) version.c .PHONY: all clean help help: diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 551ee694..9696b008 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -236,10 +236,28 @@ void ReadMem(int addr) DbpIntegers(0, data[i], data[i+1]); } +/* osimage version information is linked in */ +extern struct version_information version_information; void SendVersion(void) { char temp[48]; /* Limited data payload in USB packets */ DbpString("Prox/RFID mark3 RFID instrument"); + + /* Try to find the bootrom version information. For the time being, expect + * to find a pointer at address 0x1001fc, perform slight sanity checks on + * the pointer, then use it. + */ + void *bootrom_version = *(void**)0x1001fc; + if( bootrom_version < (void*)0x100000 || bootrom_version > (void*)0x101000 ) { + DbpString("bootrom version information appears invalid"); + } else { + FormatVersionInformation(temp, sizeof(temp), "bootrom: ", bootrom_version); + DbpString(temp); + } + + FormatVersionInformation(temp, sizeof(temp), "os: ", &version_information); + DbpString(temp); + FpgaGatherVersion(temp, sizeof(temp)); DbpString(temp); } diff --git a/armsrc/apps.h b/armsrc/apps.h index 7605baa3..6f146f78 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -112,5 +112,6 @@ void LED(int led, int ms); void LEDsoff(); int BUTTON_CLICKED(int ms); int BUTTON_HELD(int ms); +void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information); #endif diff --git a/armsrc/ldscript b/armsrc/ldscript index 3219a041..b99cea2f 100644 --- a/armsrc/ldscript +++ b/armsrc/ldscript @@ -12,6 +12,7 @@ SECTIONS *(.text.*) *(.glue_7) *(.glue_7t) + *(.version_information) } >osimage .rodata : { *(.rodata) diff --git a/armsrc/util.c b/armsrc/util.c index a102bbb5..650ba22b 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -235,3 +235,37 @@ void SpinDelay(int ms) // convert to uS and call microsecond delay function SpinDelayUs(ms*1000); } + +/* Similar to FpgaGatherVersion this formats stored version information + * into a string representation. It takes a pointer to the struct version_information, + * verifies the magic properties, then stores a formatted string, prefixed by + * prefix in dst. + */ +void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information) +{ + struct version_information *v = (struct version_information*)version_information; + dst[0] = 0; + strncat(dst, prefix, len); + if(v->magic != VERSION_INFORMATION_MAGIC) { + strncat(dst, "Missing/Invalid version information", len); + return; + } + if(v->versionversion != 1) { + strncat(dst, "Version information not understood", len); + return; + } + if(!v->present) { + strncat(dst, "Version information not available", len); + return; + } + + strncat(dst, v->svnversion, len); + if(v->clean == 0) { + strncat(dst, "-unclean", len); + } else if(v->clean == 2) { + strncat(dst, "-suspect", len); + } + + strncat(dst, " ", len); + strncat(dst, v->buildtime, len); +} diff --git a/bootrom/Makefile b/bootrom/Makefile index 2b7671c7..30b55466 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -2,7 +2,7 @@ # DO NOT use thumb mode in the phase 1 bootloader since that generates a section with glue code ARMSRC = fromflash.c -THUMBSRC = usb.c bootrom.c +THUMBSRC = usb.c version.c bootrom.c ASMSRC = ram-reset.s flash-reset.s # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC @@ -19,8 +19,9 @@ clean: $(DELETE) $(OBJDIR)$(PATHSEP)*.s19 $(DELETE) $(OBJDIR)$(PATHSEP)*.map $(DELETE) $(OBJDIR)$(PATHSEP)*.d + $(DELETE) version.c -.PHONY: all clean help +.PHONY: all clean help help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: diff --git a/bootrom/fromflash.c b/bootrom/fromflash.c index f6d5237c..0065e19e 100644 --- a/bootrom/fromflash.c +++ b/bootrom/fromflash.c @@ -1,6 +1,6 @@ #include -void __attribute__((section("bootphase1"))) CopyBootToRAM(void) +void __attribute__((section(".bootphase1"))) CopyBootToRAM(void) { int i; diff --git a/bootrom/ldscript-flash b/bootrom/ldscript-flash index 50218d68..d2e6648b 100644 --- a/bootrom/ldscript-flash +++ b/bootrom/ldscript-flash @@ -8,6 +8,18 @@ SECTIONS bootphase1 : { *(.startup) *(.bootphase1) + + /* It seems to be impossible to flush align a section at the + end of a memory segment. Instead, we'll put the version_information + wherever the linker wants it, and then put a pointer to the start + of the version information at the end of the section. + -- Henryk Plötz 2009-08-28 */ + + _version_information_start = .; + *(.version_information); + + . = LENGTH(bootphase1) - 0x4; /* Skip ahead to the end */ + LONG(_version_information_start) } >bootphase1 bootphase2 : { diff --git a/common/Makefile.common b/common/Makefile.common index a17b8a1b..04b0b330 100644 --- a/common/Makefile.common +++ b/common/Makefile.common @@ -20,6 +20,7 @@ ifeq ($(UNAME), Linux) # Linux. (Todo: Add MacOS X if appropriate) DELETE=rm -rf MOVE=mv +COPY=cp PATHSEP=/ DETECTED_OS=Linux # You may/should set this in your environment @@ -31,6 +32,7 @@ else # Assume that we are running on Windows. DELETE=del /q MOVE=ren +COPY=copy PATHSEP=\\# ARMLIB ?= ../../devkitARM/lib/gcc/arm-elf/4.1.0/interwork DETECTED_OS=Windows @@ -77,6 +79,11 @@ $(OBJDIR)/%.s19: $(OBJDIR)/%.elf --change-section-address .text-0x100000 \ --change-section-address .rodata-0x100000 $^ $@ +# version.c should be remade on every compilation +.PHONY: version.c +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + # Automatic dependency generation DEPENDENCY_FILES = $(patsubst %.c,$(OBJDIR)/%.d,$(notdir $(THUMBSRC))) \ $(patsubst %.c,$(OBJDIR)/%.d,$(notdir $(ARMSRC))) \ diff --git a/common/default_version.c b/common/default_version.c new file mode 100644 index 00000000..b2d19b2a --- /dev/null +++ b/common/default_version.c @@ -0,0 +1,9 @@ +#include +/* This is the default version.c file that Makefile.common falls back to if perl is not available */ +struct version_information __attribute__((section(".version_information"))) version_information = { + VERSION_INFORMATION_MAGIC, + 1, /* version version 1 */ + 0, /* version information not present */ + 2, /* cleanliness couldn't be determined */ + /* Remaining fields: zero */ +}; diff --git a/common/version.c b/common/version.c deleted file mode 100644 index cd78272c..00000000 --- a/common/version.c +++ /dev/null @@ -1,10 +0,0 @@ -#define VERSION "$Id $" -static const struct __attribute__((packed)) { - const char string[48]; - unsigned int length; - unsigned int magic; -} version __attribute__((unused,section("versioninformation"))) = { - VERSION, - sizeof(VERSION), - 0x48151623, -}; diff --git a/include/proxmark3.h b/include/proxmark3.h index 2f3b4838..7095fc85 100644 --- a/include/proxmark3.h +++ b/include/proxmark3.h @@ -60,4 +60,14 @@ void UsbStart(void); // if data are available. void UsbPacketReceived(BYTE *packet, int len); +#define VERSION_INFORMATION_MAGIC 0x56334d50 +struct version_information { + int magic; /* Magic sequence to identify this as a correct version information structure. Must be VERSION_INFORMATION_MAGIC */ + char versionversion; /* Must be 1 */ + char present; /* 1 if the version information could be created at compile time, otherwise 0 and the remaining fields (except for magic) are empty */ + char clean; /* 1: Tree was clean, no local changes. 0: Tree was unclean. 2: Couldn't be determined */ + char svnversion[9]; /* String with the SVN revision */ + char buildtime[30]; /* string with the build time */ +} __attribute__((packed)); + #endif diff --git a/tools/mkversion.pl b/tools/mkversion.pl new file mode 100644 index 00000000..1c4dfcd9 --- /dev/null +++ b/tools/mkversion.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# Output a version.c file that includes information about the current build +# Normally a couple of lines of bash would be enough (see openpcd project, original firmware by Harald Welte and Milosch Meriac) +# but this will, at least in theory, also work on Windows with our current compile environment. +# -- Henryk Plötz 2009-09-28 + +use strict; + +my $main_dir = shift; + +# Clear environment locale so that svn will not use localized strings +$ENV{'LC_ALL'} = "C"; +$ENV{'LANG'} = "C"; + +my $svnversion = 0; +my $present = 0; +my $clean = 2; +my @compiletime = gmtime(); + +# Strategy one: call svn info and extract last changed revision, call svn status and look for ^M +if(open(SVNINFO, "svn info $main_dir|")) { + while() { + if (/^Last Changed Rev: (.*)/) { + $present = 1; + $svnversion = $1; + ## last; # Do not abort here, since SVN tends to complain about a Broken pipe + } + } + close(SVNINFO); + + if(open(SVNSTATUS, "svn status $main_dir|")) { + $clean = 1; + while() { + if(/^M/) { + $clean = 0; + ## last; + } + } + close(SVNINFO); + } + +} else { + # Strategy two: look for .svn/entries. The third line should be "dir", the fourth line should contain the current + # revision. + if(open(ENTRIES, "$main_dir/.svn/entries")) { + my $i = 1; + while() { + last if($i == 3 and !/^dir/); + if($i == 4 and /^([0-9]*)/) { + $present = 1; + $svnversion = $1; + } + $i++; + } + } +} + +$compiletime[4] += 1; +$compiletime[5] += 1900; +my $ctime = sprintf("%6\$04i-%5\$02i-%4\$02i %3\$02i:%2\$02i:%1\$02i", @compiletime); + +print < +/* Generated file, do not edit */ +struct version_information __attribute__((section(".version_information"))) version_information = { + VERSION_INFORMATION_MAGIC, + 1, + $present, + $clean, + "svn $svnversion", + "$ctime", +}; +EOF -- 2.39.5