@ -2,30 +2,52 @@
/*
* Copyright ( C ) 2014 Charles Manning < cdhmanning @ gmail . com >
*
* Reference doc http : //www.altera.com.cn/literature/hb/cyclone-v/cv_5400A.pdf
* Note this doc is not entirely accurate . Of particular interest to us is the
* " header " length field being in U32s and not bytes .
* Reference documents :
* Cyclone V SoC : https : //www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/cyclone-v/cv_5400a.pdf
* Arria V SoC : https : //www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/arria-v/av_5400a.pdf
* Arria 10 SoC : https : //www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/arria-10/a10_5400a.pdf
*
* " Header " i s a structure of the following format .
* this is positioned at 0x40 .
* Bootable SoCFPGA image require s a structure of the following format
* positioned at offset 0x40 of the bootable image . Endian is LSB .
*
* Endian is LSB .
* There are two versions of the SoCFPGA header format , v0 and v1 .
* The version 0 is used by Cyclone V SoC and Arria V SoC , while
* the version 1 is used by the Arria 10 SoC .
*
* Version 0 :
* Offset Length Usage
* - - - - - - - - - - - - - - - - - - - - - - -
* 0x40 4 Validation word 0x31305341
* 0x44 1 Version ( whatever , zero is fine )
* 0x45 1 Flags ( unused , zero is fine )
* 0x46 2 Length ( in units of u32 , including the end checksum ) .
* 0x48 2 Zero
* 0x40 4 Validation word ( 0x31305341 )
* 0x44 1 Version ( 0x0 )
* 0x45 1 Flags ( unused , zero is fine )
* 0x46 2 Length ( in units of u32 , including the end checksum ) .
* 0x48 2 Zero ( 0x0 )
* 0x4A 2 Checksum over the header . NB Not CRC32
*
* Version 1 :
* Offset Length Usage
* - - - - - - - - - - - - - - - - - - - - - - -
* 0x40 4 Validation word ( 0x31305341 )
* 0x44 1 Version ( 0x1 )
* 0x45 1 Flags ( unused , zero is fine )
* 0x46 2 Header length ( in units of u8 ) .
* 0x48 4 Length ( in units of u8 ) .
* 0x4C 4 Image entry offset from standard of header
* 0x50 2 Zero ( 0x0 )
* 0x52 2 Checksum over the header . NB Not CRC32
*
* At the end of the code we have a 32 - bit CRC checksum over whole binary
* excluding the CRC .
*
* Note that the CRC used here is * * not * * the zlib / Adler crc32 . It is the
* CRC - 32 used in bzip2 , ethernet and elsewhere .
*
* The Image entry offset in version 1 image is relative the the start of
* the header , 0x40 , and must not be a negative number . Therefore , it is
* only possible to make the SoCFPGA jump forward . The U - Boot bootloader
* places a trampoline instruction at offset 0x5c , 0x14 bytes from the
* start of the SoCFPGA header , which jumps to the reset vector .
*
* The image is padded out to 64 k , because that is what is
* typically used to write the image to the boot medium .
*/
@ -38,32 +60,57 @@
# define HEADER_OFFSET 0x40
# define VALIDATION_WORD 0x31305341
# define PADDED_SIZE 0x10000
/* To allow for adding CRC, the max input size is a bit smaller. */
# define MAX_INPUT_SIZE (PADDED_SIZE - sizeof(uint32_t))
static uint8_t buffer_v0 [ 0x10000 ] ;
static uint8_t buffer_v1 [ 0x40000 ] ;
static uint8_t buffer [ PADDED_SIZE ] ;
struct socfpga_header_v0 {
uint32_t validation ;
uint8_t version ;
uint8_t flags ;
uint16_t length_u32 ;
uint16_t zero ;
uint16_t checksum ;
} ;
struct socfpga_header {
uint32_t validation ;
uint8_t version ;
uint8_t flags ;
uint16_t length_u32 ;
uint16_t zero ;
uint16_t checksum ;
struct socfpga_header_v1 {
uint32_t validation ;
uint8_t version ;
uint8_t flags ;
uint16_t header_u8 ;
uint32_t length_u8 ;
uint32_t entry_offset ;
uint16_t zero ;
uint16_t checksum ;
} ;
static unsigned int sfp_hdr_size ( uint8_t ver )
{
if ( ver = = 0 )
return sizeof ( struct socfpga_header_v0 ) ;
if ( ver = = 1 )
return sizeof ( struct socfpga_header_v1 ) ;
return 0 ;
}
static unsigned int sfp_pad_size ( uint8_t ver )
{
if ( ver = = 0 )
return sizeof ( buffer_v0 ) ;
if ( ver = = 1 )
return sizeof ( buffer_v1 ) ;
return 0 ;
}
/*
* The header checksum is just a very simple checksum over
* the header area .
* There is still a crc32 over the whole lot .
*/
static uint16_t hdr_checksum ( struct socfpga_header * header )
static uint16_t sfp_ hdr_checksum( uint8_t * buf , unsigned char v er)
{
int len = sizeof ( * header ) - sizeof ( header - > checksum ) ;
uint8_t * buf = ( uint8_t * ) header ;
uint16_t ret = 0 ;
int len = sfp_hdr_size ( ver ) - sizeof ( ret ) ;
while ( - - len )
ret + = * buf + + ;
@ -71,52 +118,93 @@ static uint16_t hdr_checksum(struct socfpga_header *header)
return ret ;
}
static void build_header ( uint8_t * buf , uint8_t version , uint8_t flags ,
uint16_t length_bytes )
static void sfp_build_header ( uint8_t * buf , uint8_t ver , uint8_t flags ,
uint32_t length_bytes )
{
struct socfpga_header header ;
header . validation = cpu_to_le32 ( VALIDATION_WORD ) ;
header . version = version ;
header . flags = flags ;
header . length_u32 = cpu_to_le16 ( length_bytes / 4 ) ;
header . zero = 0 ;
header . checksum = cpu_to_le16 ( hdr_checksum ( & header ) ) ;
memcpy ( buf , & header , sizeof ( header ) ) ;
struct socfpga_header_v0 header_v0 = {
. validation = cpu_to_le32 ( VALIDATION_WORD ) ,
. version = 0 ,
. flags = flags ,
. length_u32 = cpu_to_le16 ( length_bytes / 4 ) ,
. zero = 0 ,
} ;
struct socfpga_header_v1 header_v1 = {
. validation = cpu_to_le32 ( VALIDATION_WORD ) ,
. version = 1 ,
. flags = flags ,
. header_u8 = cpu_to_le16 ( sizeof ( header_v1 ) ) ,
. length_u8 = cpu_to_le32 ( length_bytes ) ,
. entry_offset = cpu_to_le32 ( 0x14 ) , /* Trampoline offset */
. zero = 0 ,
} ;
uint16_t csum ;
if ( ver = = 0 ) {
csum = sfp_hdr_checksum ( ( uint8_t * ) & header_v0 , 0 ) ;
header_v0 . checksum = cpu_to_le16 ( csum ) ;
memcpy ( buf , & header_v0 , sizeof ( header_v0 ) ) ;
} else {
csum = sfp_hdr_checksum ( ( uint8_t * ) & header_v1 , 1 ) ;
header_v1 . checksum = cpu_to_le16 ( csum ) ;
memcpy ( buf , & header_v1 , sizeof ( header_v1 ) ) ;
}
}
/*
* Perform a rudimentary verification of header and return
* size of image .
*/
static int verify_header ( const uint8_t * buf )
static int sfp_ verify_header( const uint8_t * buf , uint8_t * ver )
{
struct socfpga_header header ;
struct socfpga_header_v0 header_v0 ;
struct socfpga_header_v1 header_v1 ;
uint16_t hdr_csum , sfp_csum ;
uint32_t img_len ;
memcpy ( & header , buf , sizeof ( header ) ) ;
/*
* Header v0 is always smaller than Header v1 and the validation
* word and version field is at the same place , so use Header v0
* to check for version during verifiction and upgrade to Header
* v1 if needed .
*/
memcpy ( & header_v0 , buf , sizeof ( header_v0 ) ) ;
if ( le32_to_cpu ( header . validation ) ! = VALIDATION_WORD )
return - 1 ;
if ( le16_to_cpu ( header . checksum ) ! = hdr_checksum ( & header ) )
if ( le32_to_cpu ( header_v0 . validation ) ! = VALIDATION_WORD )
return - 1 ;
return le16_to_cpu ( header . length_u32 ) * 4 ;
if ( header_v0 . version = = 0 ) {
hdr_csum = le16_to_cpu ( header_v0 . checksum ) ;
sfp_csum = sfp_hdr_checksum ( ( uint8_t * ) & header_v0 , 0 ) ;
img_len = le16_to_cpu ( header_v0 . length_u32 ) * 4 ;
} else if ( header_v0 . version = = 1 ) {
memcpy ( & header_v1 , buf , sizeof ( header_v1 ) ) ;
hdr_csum = le16_to_cpu ( header_v1 . checksum ) ;
sfp_csum = sfp_hdr_checksum ( ( uint8_t * ) & header_v1 , 1 ) ;
img_len = le32_to_cpu ( header_v1 . length_u8 ) ;
} else { /* Invalid version */
return - EINVAL ;
}
/* Verify checksum */
if ( hdr_csum ! = sfp_csum )
return - EINVAL ;
return img_len ;
}
/* Sign the buffer and return the signed buffer size */
static int sign_buffer ( uint8_t * buf ,
uint8_t version , uint8_t flags ,
int len , int pad_64k )
static int sfp_sign_buffer ( uint8_t * buf , uint8_t ver , uint8_t flags ,
int len , int pad_64k )
{
uint32_t calc_crc ;
/* Align the length up */
len = ( len + 3 ) & ( ~ 3 ) ;
len = ( len + 3 ) & ~ 3 ;
/* Build header, adding 4 bytes to length to hold the CRC32. */
build_header ( buf + HEADER_OFFSET , version , flags , len + 4 ) ;
sfp_ build_header( buf + HEADER_OFFSET , ver , flags , len + 4 ) ;
/* Calculate and apply the CRC */
calc_crc = ~ pbl_crc32 ( 0 , ( char * ) buf , len ) ;
@ -126,23 +214,24 @@ static int sign_buffer(uint8_t *buf,
if ( ! pad_64k )
return len + 4 ;
return PADDED_SIZE ;
return sfp_pad_size ( ver ) ;
}
/* Verify that the buffer looks sane */
static int verify_buffer ( const uint8_t * buf )
static int sfp_ verify_buffer( const uint8_t * buf )
{
int len ; /* Including 32bit CRC */
uint32_t calc_crc ;
uint32_t buf_crc ;
uint8_t ver = 0 ;
len = verify_header ( buf + HEADER_OFFSET ) ;
len = sfp_ verify_header( buf + HEADER_OFFSET , & ver ) ;
if ( len < 0 ) {
debug ( " Invalid header \n " ) ;
return - 1 ;
}
if ( len < HEADER_OFFSET | | len > PADDED_SIZE ) {
if ( len < HEADER_OFFSET | | len > sfp_pad_size ( ver ) ) {
debug ( " Invalid header length (%i) \n " , len ) ;
return - 1 ;
}
@ -168,17 +257,17 @@ static int verify_buffer(const uint8_t *buf)
/* mkimage glue functions */
static int socfpgaimage_verify_header ( unsigned char * ptr , int image_size ,
struct image_tool_params * params )
struct image_tool_params * params )
{
if ( image_size ! = PADDED_SIZE )
if ( image_size < 0x80 )
return - 1 ;
return verify_buffer ( ptr ) ;
return sfp_ verify_buffer( ptr ) ;
}
static void socfpgaimage_print_header ( const void * ptr )
{
if ( verify_buffer ( ptr ) = = 0 )
if ( sfp_ verify_buffer( ptr ) = = 0 )
printf ( " Looks like a sane SOCFPGA preloader \n " ) ;
else
printf ( " Not a sane SOCFPGA preloader \n " ) ;
@ -192,18 +281,25 @@ static int socfpgaimage_check_params(struct image_tool_params *params)
( params - > lflag & & ( params - > dflag | | params - > fflag ) ) ;
}
static int socfpgaimage_check_image_types ( uint8_t type )
static int socfpgaimage_check_image_types_v0 ( uint8_t type )
{
if ( type = = IH_TYPE_SOCFPGAIMAGE )
return EXIT_SUCCESS ;
return EXIT_FAILURE ;
}
static int socfpgaimage_check_image_types_v1 ( uint8_t type )
{
if ( type = = IH_TYPE_SOCFPGAIMAGE_V1 )
return EXIT_SUCCESS ;
return EXIT_FAILURE ;
}
/*
* To work in with the mkimage framework , we do some ugly stuff . . .
*
* First , socfpgaimage_vrec_header ( ) is called .
* We prepend a fake header big enough to make the file PADDED_SIZE .
* We prepend a fake header big enough to make the file sfp_pad_size ( ) .
* This gives us enough space to do what we want later .
*
* Next , socfpgaimage_set_header ( ) is called .
@ -212,51 +308,94 @@ static int socfpgaimage_check_image_types(uint8_t type)
*/
static int data_size ;
# define FAKE_HEADER_SIZE (PADDED_SIZE - data_size)
static int socfpgaimage_vrec_header ( struct image_tool_params * params ,
struct image_type_params * tparams )
static int sfp_fake_header_size ( unsigned int size , uint8_t ver )
{
return sfp_pad_size ( ver ) - size ;
}
static int sfp_vrec_header ( struct image_tool_params * params ,
struct image_type_params * tparams , uint8_t ver )
{
struct stat sbuf ;
if ( params - > datafile & &
stat ( params - > datafile , & sbuf ) = = 0 & &
sbuf . st_size < = MAX_INPUT_SIZE ) {
sbuf . st_size < = ( sfp_pad_size ( ver ) - sizeof ( uint32_t ) ) ) {
data_size = sbuf . st_size ;
tparams - > header_size = FAKE_HEADER_SIZE ;
tparams - > header_size = sfp_fake_header_size ( data_size , ver ) ;
}
return 0 ;
}
static void socfpgaimage_set_header ( void * ptr , struct stat * sbuf , int ifd ,
struct image_tool_params * params )
static int socfpgaimage_vrec_header_v0 ( struct image_tool_params * params ,
struct image_type_params * tparams )
{
return sfp_vrec_header ( params , tparams , 0 ) ;
}
static int socfpgaimage_vrec_header_v1 ( struct image_tool_params * params ,
struct image_type_params * tparams )
{
return sfp_vrec_header ( params , tparams , 1 ) ;
}
static void sfp_set_header ( void * ptr , unsigned char ver )
{
uint8_t * buf = ( uint8_t * ) ptr ;
/*
* This function is called after vrec_header ( ) has been called .
* At this stage we have the FAKE_HEADER_SIZE dummy bytes followed by
* data_size image bytes . Total = PADDED_SIZE .
* At this stage we have the sfp_fake_header_size ( ) dummy bytes
* followed by data_size image bytes . Total = sfp_pad_size ( ) .
* We need to fix the buffer by moving the image bytes back to
* the beginning of the buffer , then actually do the signing stuff . . .
*/
memmove ( buf , buf + FAKE_HEADER_SIZE , data_size ) ;
memset ( buf + data_size , 0 , FAKE_HEADER_SIZE ) ;
memmove ( buf , buf + sfp_fake_header_size ( data_size , ver ) , data_size ) ;
memset ( buf + data_size , 0 , sfp_fake_header_size ( data_size , ver ) ) ;
sfp_sign_buffer ( buf , ver , 0 , data_size , 0 ) ;
}
sign_buffer ( buf , 0 , 0 , data_size , 0 ) ;
static void socfpgaimage_set_header_v0 ( void * ptr , struct stat * sbuf , int ifd ,
struct image_tool_params * params )
{
sfp_set_header ( ptr , 0 ) ;
}
static void socfpgaimage_set_header_v1 ( void * ptr , struct stat * sbuf , int ifd ,
struct image_tool_params * params )
{
sfp_set_header ( ptr , 1 ) ;
}
U_BOOT_IMAGE_TYPE (
socfpgaimage ,
" Altera SOCFPGA preloader support " ,
" Altera SoCFPGA Cyclone V / Arria V image support " ,
0 , /* This will be modified by vrec_header() */
( void * ) buffer_v0 ,
socfpgaimage_check_params ,
socfpgaimage_verify_header ,
socfpgaimage_print_header ,
socfpgaimage_set_header_v0 ,
NULL ,
socfpgaimage_check_image_types_v0 ,
NULL ,
socfpgaimage_vrec_header_v0
) ;
U_BOOT_IMAGE_TYPE (
socfpgaimage_v1 ,
" Altera SoCFPGA Arria10 image support " ,
0 , /* This will be modified by vrec_header() */
( void * ) buffer ,
( void * ) buffer_v1 ,
socfpgaimage_check_params ,
socfpgaimage_verify_header ,
socfpgaimage_print_header ,
socfpgaimage_set_header ,
socfpgaimage_set_header_v1 ,
NULL ,
socfpgaimage_check_image_types ,
socfpgaimage_check_image_types_v1 ,
NULL ,
socfpgaimage_vrec_header
socfpgaimage_vrec_header_v1
) ;