mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-26 13:03:40 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
339 lines
7 KiB
C
339 lines
7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2014
|
|
* Andreas Bießmann <andreas@biessmann.org>
|
|
*/
|
|
|
|
#include "imagetool.h"
|
|
#include "mkimage.h"
|
|
|
|
#include <image.h>
|
|
|
|
#define pr_err(fmt, args...) fprintf(stderr, "atmelimage Error: " fmt, ##args)
|
|
|
|
static int atmel_check_image_type(uint8_t type)
|
|
{
|
|
if (type == IH_TYPE_ATMELIMAGE)
|
|
return EXIT_SUCCESS;
|
|
else
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
static uint32_t nand_pmecc_header[52];
|
|
|
|
/*
|
|
* A helper struct for parsing the mkimage -n parameter
|
|
*
|
|
* Keep in same order as the configs array!
|
|
*/
|
|
static struct pmecc_config {
|
|
int use_pmecc;
|
|
int sector_per_page;
|
|
int spare_size;
|
|
int ecc_bits;
|
|
int sector_size;
|
|
int ecc_offset;
|
|
} pmecc;
|
|
|
|
/*
|
|
* Strings used for configure the PMECC header via -n mkimage switch
|
|
*
|
|
* We estimate a coma separated list of key=value pairs. The mkimage -n
|
|
* parameter argument should not contain any whitespace.
|
|
*
|
|
* Keep in same order as struct pmecc_config!
|
|
*/
|
|
static const char * const configs[] = {
|
|
"usePmecc",
|
|
"sectorPerPage",
|
|
"spareSize",
|
|
"eccBits",
|
|
"sectorSize",
|
|
"eccOffset"
|
|
};
|
|
|
|
static int atmel_find_pmecc_parameter_in_token(const char *token)
|
|
{
|
|
size_t pos;
|
|
char *param;
|
|
|
|
debug("token: '%s'\n", token);
|
|
|
|
for (pos = 0; pos < ARRAY_SIZE(configs); pos++) {
|
|
if (strncmp(token, configs[pos], strlen(configs[pos])) == 0) {
|
|
param = strstr(token, "=");
|
|
if (!param)
|
|
goto err;
|
|
|
|
param++;
|
|
debug("\t%s parameter: '%s'\n", configs[pos], param);
|
|
|
|
switch (pos) {
|
|
case 0:
|
|
pmecc.use_pmecc = strtol(param, NULL, 10);
|
|
return EXIT_SUCCESS;
|
|
case 1:
|
|
pmecc.sector_per_page = strtol(param, NULL, 10);
|
|
return EXIT_SUCCESS;
|
|
case 2:
|
|
pmecc.spare_size = strtol(param, NULL, 10);
|
|
return EXIT_SUCCESS;
|
|
case 3:
|
|
pmecc.ecc_bits = strtol(param, NULL, 10);
|
|
return EXIT_SUCCESS;
|
|
case 4:
|
|
pmecc.sector_size = strtol(param, NULL, 10);
|
|
return EXIT_SUCCESS;
|
|
case 5:
|
|
pmecc.ecc_offset = strtol(param, NULL, 10);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
err:
|
|
pr_err("Could not find parameter in token '%s'\n", token);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
static int atmel_parse_pmecc_params(char *txt)
|
|
{
|
|
char *token;
|
|
|
|
token = strtok(txt, ",");
|
|
while (token != NULL) {
|
|
if (atmel_find_pmecc_parameter_in_token(token))
|
|
return EXIT_FAILURE;
|
|
|
|
token = strtok(NULL, ",");
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static int atmel_verify_header(unsigned char *ptr, int image_size,
|
|
struct image_tool_params *params)
|
|
{
|
|
uint32_t *ints = (uint32_t *)ptr;
|
|
size_t pos;
|
|
size_t size = image_size;
|
|
|
|
/* check if we have an PMECC header attached */
|
|
for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
|
|
if (ints[pos] >> 28 != 0xC)
|
|
break;
|
|
|
|
if (pos == ARRAY_SIZE(nand_pmecc_header)) {
|
|
ints += ARRAY_SIZE(nand_pmecc_header);
|
|
size -= sizeof(nand_pmecc_header);
|
|
}
|
|
|
|
/* check the seven interrupt vectors of binary */
|
|
for (pos = 0; pos < 7; pos++) {
|
|
debug("atmelimage: interrupt vector #%zu is 0x%08X\n", pos+1,
|
|
ints[pos]);
|
|
/*
|
|
* all vectors except the 6'th one must contain valid
|
|
* LDR or B Opcode
|
|
*/
|
|
if (pos == 5)
|
|
/* 6'th vector has image size set, check later */
|
|
continue;
|
|
if ((ints[pos] & 0xff000000) == 0xea000000)
|
|
/* valid B Opcode */
|
|
continue;
|
|
if ((ints[pos] & 0xfffff000) == 0xe59ff000)
|
|
/* valid LDR (I=0, P=1, U=1, B=0, W=0, L=1) */
|
|
continue;
|
|
/* ouch, one of the checks has missed ... */
|
|
return 1;
|
|
}
|
|
|
|
return ints[5] != cpu_to_le32(size);
|
|
}
|
|
|
|
static void atmel_print_pmecc_header(const uint32_t word)
|
|
{
|
|
int val;
|
|
|
|
printf("\t\tPMECC header\n");
|
|
|
|
printf("\t\t====================\n");
|
|
|
|
val = (word >> 18) & 0x1ff;
|
|
printf("\t\teccOffset: %9i\n", val);
|
|
|
|
val = (((word >> 16) & 0x3) == 0) ? 512 : 1024;
|
|
printf("\t\tsectorSize: %8i\n", val);
|
|
|
|
if (((word >> 13) & 0x7) <= 2)
|
|
val = (2 << ((word >> 13) & 0x7));
|
|
else
|
|
val = (12 << (((word >> 13) & 0x7) - 3));
|
|
printf("\t\teccBitReq: %9i\n", val);
|
|
|
|
val = (word >> 4) & 0x1ff;
|
|
printf("\t\tspareSize: %9i\n", val);
|
|
|
|
val = (1 << ((word >> 1) & 0x3));
|
|
printf("\t\tnbSectorPerPage: %3i\n", val);
|
|
|
|
printf("\t\tusePmecc: %10i\n", word & 0x1);
|
|
printf("\t\t====================\n");
|
|
}
|
|
|
|
static void atmel_print_header(const void *ptr)
|
|
{
|
|
uint32_t *ints = (uint32_t *)ptr;
|
|
size_t pos;
|
|
|
|
/* check if we have an PMECC header attached */
|
|
for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
|
|
if (ints[pos] >> 28 != 0xC)
|
|
break;
|
|
|
|
if (pos == ARRAY_SIZE(nand_pmecc_header)) {
|
|
printf("Image Type:\tATMEL ROM-Boot Image with PMECC Header\n");
|
|
atmel_print_pmecc_header(ints[0]);
|
|
pos += 5;
|
|
} else {
|
|
printf("Image Type:\tATMEL ROM-Boot Image without PMECC Header\n");
|
|
pos = 5;
|
|
}
|
|
printf("\t\t6'th vector has %u set\n", le32_to_cpu(ints[pos]));
|
|
}
|
|
|
|
static void atmel_set_header(void *ptr, struct stat *sbuf, int ifd,
|
|
struct image_tool_params *params)
|
|
{
|
|
/* just save the image size into 6'th interrupt vector */
|
|
uint32_t *ints = (uint32_t *)ptr;
|
|
size_t cnt;
|
|
size_t pos = 5;
|
|
size_t size = sbuf->st_size;
|
|
|
|
for (cnt = 0; cnt < ARRAY_SIZE(nand_pmecc_header); cnt++)
|
|
if (ints[cnt] >> 28 != 0xC)
|
|
break;
|
|
|
|
if (cnt == ARRAY_SIZE(nand_pmecc_header)) {
|
|
pos += ARRAY_SIZE(nand_pmecc_header);
|
|
size -= sizeof(nand_pmecc_header);
|
|
}
|
|
|
|
ints[pos] = cpu_to_le32(size);
|
|
}
|
|
|
|
static int atmel_check_params(struct image_tool_params *params)
|
|
{
|
|
if (strlen(params->imagename) > 0)
|
|
if (atmel_parse_pmecc_params(params->imagename))
|
|
return EXIT_FAILURE;
|
|
|
|
return !(!params->eflag &&
|
|
!params->fflag &&
|
|
!params->xflag &&
|
|
((params->dflag && !params->lflag) ||
|
|
(params->lflag && !params->dflag)));
|
|
}
|
|
|
|
static int atmel_vrec_header(struct image_tool_params *params,
|
|
struct image_type_params *tparams)
|
|
{
|
|
uint32_t tmp;
|
|
size_t pos;
|
|
|
|
if (strlen(params->imagename) == 0)
|
|
return EXIT_SUCCESS;
|
|
|
|
tmp = 0xC << 28;
|
|
|
|
tmp |= (pmecc.ecc_offset & 0x1ff) << 18;
|
|
|
|
switch (pmecc.sector_size) {
|
|
case 512:
|
|
tmp |= 0 << 16;
|
|
break;
|
|
case 1024:
|
|
tmp |= 1 << 16;
|
|
break;
|
|
|
|
default:
|
|
pr_err("Wrong sectorSize (%i) for PMECC header\n",
|
|
pmecc.sector_size);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
switch (pmecc.ecc_bits) {
|
|
case 2:
|
|
tmp |= 0 << 13;
|
|
break;
|
|
case 4:
|
|
tmp |= 1 << 13;
|
|
break;
|
|
case 8:
|
|
tmp |= 2 << 13;
|
|
break;
|
|
case 12:
|
|
tmp |= 3 << 13;
|
|
break;
|
|
case 24:
|
|
tmp |= 4 << 13;
|
|
break;
|
|
|
|
default:
|
|
pr_err("Wrong eccBits (%i) for PMECC header\n",
|
|
pmecc.ecc_bits);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
tmp |= (pmecc.spare_size & 0x1ff) << 4;
|
|
|
|
switch (pmecc.sector_per_page) {
|
|
case 1:
|
|
tmp |= 0 << 1;
|
|
break;
|
|
case 2:
|
|
tmp |= 1 << 1;
|
|
break;
|
|
case 4:
|
|
tmp |= 2 << 1;
|
|
break;
|
|
case 8:
|
|
tmp |= 3 << 1;
|
|
break;
|
|
|
|
default:
|
|
pr_err("Wrong sectorPerPage (%i) for PMECC header\n",
|
|
pmecc.sector_per_page);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (pmecc.use_pmecc)
|
|
tmp |= 1;
|
|
|
|
for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
|
|
nand_pmecc_header[pos] = tmp;
|
|
|
|
debug("PMECC header filled 52 times with 0x%08X\n", tmp);
|
|
|
|
tparams->header_size = sizeof(nand_pmecc_header);
|
|
tparams->hdr = nand_pmecc_header;
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
U_BOOT_IMAGE_TYPE(
|
|
atmelimage,
|
|
"ATMEL ROM-Boot Image support",
|
|
0,
|
|
NULL,
|
|
atmel_check_params,
|
|
atmel_verify_header,
|
|
atmel_print_header,
|
|
atmel_set_header,
|
|
NULL,
|
|
atmel_check_image_type,
|
|
NULL,
|
|
atmel_vrec_header
|
|
);
|