483 lines
11 KiB
C
483 lines
11 KiB
C
/*
|
|
* Main function.
|
|
*
|
|
* Copyright (C) 2008 Hugo Villeneuve <hugo@hugovil.com>
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h> /* Standard input/output definitions */
|
|
#include <string.h> /* String function definitions */
|
|
#include <unistd.h> /* UNIX standard function definitions */
|
|
#include <fcntl.h> /* File control definitions */
|
|
#include <assert.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include "common.h"
|
|
#include "options.h"
|
|
#include "serial.h"
|
|
#include "cmd.h"
|
|
#include "crc.h"
|
|
|
|
#define UBL_BIN_MAX_SIZE 0x3800
|
|
#define SREC_MAX_SIZE 600000
|
|
|
|
#define UBL_MAGIC_SAFE 0xA1ACED00
|
|
#define UBL_MAGIC_NAND_FLASH 0xA1ACEDCC /* Download via UART & Flash NAND
|
|
* with UBL and U-Boot */
|
|
#define UBL_MAGIC_BIN_IMG 0xA1ACED66 /* Describes the application image
|
|
* in Flash - indicates that it is
|
|
* binary */
|
|
|
|
#define DEFAULT_CROSS_COMPILE "arm-linux-"
|
|
|
|
static char cross_compile[MAX_ENV_VAR_LENGTH];
|
|
|
|
static int
|
|
parse_environment_variables(void)
|
|
{
|
|
char *env_var;
|
|
|
|
env_var = getenv("CROSS_COMPILE");
|
|
|
|
if (env_var != NULL)
|
|
hv_strncpy(cross_compile, env_var, MAX_ENV_VAR_LENGTH);
|
|
else {
|
|
/* CROSS_COMPILE isn't set, using default value. */
|
|
hv_strncpy(cross_compile, DEFAULT_CROSS_COMPILE,
|
|
MAX_ENV_VAR_LENGTH);
|
|
}
|
|
|
|
log_info("Using cross compiler \"%s\"", cross_compile);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_app_entry_point(u_int32_t *entry_point, const char *elf_file)
|
|
{
|
|
int ret;
|
|
char cmd[MAX_CMD_LENGTH];
|
|
char buf[3200];
|
|
char *endptr;
|
|
|
|
sprintf(cmd,
|
|
"%sreadelf -h %s | grep 'Entry point' | " \
|
|
"sed 's/.*\\(0x[0-9a-f]*\\)/\\1/'", cross_compile, elf_file);
|
|
|
|
ret = run_cmd_get_string(cmd, buf, sizeof(buf));
|
|
if (ret)
|
|
goto error;
|
|
|
|
*entry_point = strtoul(buf, &endptr, 16);
|
|
if (*endptr != '\0')
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
log_fail("Entry point not found");
|
|
return 1;
|
|
}
|
|
|
|
/* size_in_bytes must be initialized with the size of the buffer.
|
|
* it will be written with the actual buffer size. */
|
|
static int
|
|
convert_elf_to_srec(const char *elf_file, const char *temp_file, u_int8_t *data,
|
|
ssize_t *size_in_bytes, u_int32_t *entry_point)
|
|
{
|
|
ssize_t len;
|
|
char cmd[MAX_CMD_LENGTH];
|
|
FILE *fp;
|
|
int ret;
|
|
u_int32_t offset = 0;
|
|
/*
|
|
* OBJCOPY FLAGS:
|
|
* -S
|
|
* Do not copy relocation and symbol information from the source file
|
|
* -R sectionname
|
|
* Remove any section named sectionname from the output file
|
|
* Get rid of the section .selfcopy (first 256 bytes) because
|
|
* it contains self-copy stuff. This will remove 256 from bytes
|
|
* the application size. The entry point is not modified.
|
|
*/
|
|
char *objcopy_flags = "-R .ddrram -R .ddrram2 -R .selfcopy " \
|
|
"--gap-fill 0xFF --srec-forceS3 -S";
|
|
|
|
/* Find application entry point */
|
|
ret = get_app_entry_point(entry_point, elf_file);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (*entry_point < 0x80000000) {
|
|
log_info("Adding offset to SREC file");
|
|
/* We must add offset starting at 0x81070000 because the UBL
|
|
* first allocate a buffer in RAM between 0x80000000 and
|
|
* 0x81070000 */
|
|
offset = 0x81070000;
|
|
*entry_point += offset;
|
|
}
|
|
|
|
if (*size_in_bytes == 0) {
|
|
log_fail("buffer size not specified");
|
|
return 1;
|
|
}
|
|
|
|
sprintf(cmd,
|
|
"%sobjcopy %s --change-addresses=0x%08X -O srec %s %s 2>&1",
|
|
cross_compile, objcopy_flags, offset, elf_file, temp_file);
|
|
|
|
ret = system(cmd);
|
|
if (ret) {
|
|
log_fail("Error converting ELF to srec file");
|
|
return 1;
|
|
}
|
|
|
|
fp = fopen(temp_file, "r");
|
|
if (!fp) {
|
|
log_fail("Error opening temporary file %s", temp_file);
|
|
return 1;
|
|
}
|
|
|
|
/* Copy temporary srec file data inside our buffer */
|
|
len = fread(data, 1, *size_in_bytes, fp);
|
|
|
|
/* Check that size is u32 aligned. */
|
|
*size_in_bytes = len;
|
|
|
|
fclose(fp);
|
|
|
|
#ifdef REMOVE_TEMP_FILES
|
|
/* Remove temporary file. */
|
|
ret = remove_file(temp_file);
|
|
if (ret) {
|
|
log_fail("Error removing temporary file");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* size_in_bytes must be initialized with the size of the buffer.
|
|
* it will be written with the actual buffer size. */
|
|
static int
|
|
convert_elf_to_binary(const char *elf_file, const char *temp_file,
|
|
u_int8_t *data, ssize_t *size_in_bytes,
|
|
u_int32_t *entry_point)
|
|
{
|
|
ssize_t len;
|
|
char cmd[MAX_CMD_LENGTH];
|
|
FILE *fp;
|
|
int ret;
|
|
/*
|
|
* OBJCOPY FLAGS:
|
|
* -S
|
|
* Do not copy relocation and symbol information from the source file
|
|
* -R sectionname
|
|
* Remove any section named sectionname from the output file
|
|
* Get rid of the section .selfcopy (first 256 bytes) because
|
|
* it contains self-copy stuff. This will remove 256 from bytes
|
|
* the application size. The entry point is not modified.
|
|
*/
|
|
char *objcopy_flags = "-R .ddrram -R .ddrram2 -R .selfcopy " \
|
|
"--gap-fill 0xFF -S";
|
|
|
|
if (*size_in_bytes == 0) {
|
|
log_fail("buffer size not specified");
|
|
return 1;
|
|
}
|
|
|
|
sprintf(cmd, "%sobjcopy %s -O binary %s %s 2>&1",
|
|
cross_compile, objcopy_flags, elf_file, temp_file);
|
|
|
|
ret = system(cmd);
|
|
if (ret) {
|
|
log_fail("Error converting ELF to binary file");
|
|
return 1;
|
|
}
|
|
|
|
fp = fopen(temp_file, "r");
|
|
if (!fp) {
|
|
log_fail("Error opening temporary file %s", temp_file);
|
|
return 1;
|
|
}
|
|
|
|
/* Copy temporary binary file data inside our buffer */
|
|
len = fread(data, 1, *size_in_bytes, fp);
|
|
|
|
/* Check that size is u32 aligned. */
|
|
*size_in_bytes = len;
|
|
|
|
fclose(fp);
|
|
|
|
#ifdef REMOVE_TEMP_FILES
|
|
/* Remove temporary file. */
|
|
ret = remove_file(temp_file);
|
|
if (ret) {
|
|
log_fail("Error removing temporary file");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* Find application entry point */
|
|
ret = get_app_entry_point(entry_point, elf_file);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
send_ubl_binary(const char *elf_file)
|
|
{
|
|
int found;
|
|
int ret;
|
|
ssize_t size;
|
|
u_int8_t data[UBL_BIN_MAX_SIZE];
|
|
u_int32_t entry_point;
|
|
u_int32_t crc;
|
|
|
|
/* Specify max size of our buffers */
|
|
size = sizeof(data);
|
|
|
|
/* Convert UBL ELF to binary and get the resulting binary file size and
|
|
* entry point. */
|
|
ret = convert_elf_to_binary(elf_file, "/tmp/ubl.bin", data, &size,
|
|
&entry_point);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Wait for the device to send the BOOTME sequence.
|
|
* If the UBL is already running, it will send BOOTPSP. */
|
|
found = wait_for_message("BOOTME", "BOOTPSP", NULL);
|
|
if (found == 0)
|
|
goto error;
|
|
else if (found == 2)
|
|
/* The UBL is already loaded and running. Don't reload it. */
|
|
goto bootpsp_received;
|
|
|
|
log_info("Sending ACK sequence");
|
|
send_message(" ACK\n");
|
|
|
|
crc = crc32_dv_compute(data, size);
|
|
|
|
log_info("Sending UBL CRC ($%08X)", crc);
|
|
send_number(crc, 8);
|
|
|
|
log_info("Sending UBL size ($%04X bytes)", size);
|
|
send_number(size, 4);
|
|
|
|
log_info("Sending UBL entry point ($%08X)", entry_point);
|
|
send_number(entry_point, 4);
|
|
|
|
log_info("Sending termination sequence");
|
|
send_number(0x0000, 4);
|
|
|
|
found = wait_for_message("BEGIN", NULL, NULL);
|
|
if (!found)
|
|
goto error;
|
|
|
|
log_info("Sending CRC table");
|
|
crc_send_table();
|
|
|
|
found = wait_for_message("DONE", NULL, NULL);
|
|
if (!found)
|
|
goto error;
|
|
|
|
log_info("Sending UBL data");
|
|
send_buffer(data, size);
|
|
|
|
found = wait_for_message("DONE", "CORRUPT", NULL);
|
|
if (found != 1)
|
|
goto error;
|
|
|
|
found = wait_for_message("PSPBootMode = UART", NULL, NULL);
|
|
if (!found)
|
|
goto error;
|
|
|
|
found = wait_for_message("BOOTPSP", NULL, NULL);
|
|
if (!found)
|
|
goto error;
|
|
|
|
bootpsp_received:
|
|
return 0;
|
|
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
send_srec(const char *elf_file, const char *temp_file, u_int32_t magic_number)
|
|
{
|
|
int found;
|
|
int ret;
|
|
u_int32_t entry_point;
|
|
ssize_t size;
|
|
u_int8_t data[SREC_MAX_SIZE];
|
|
|
|
/* Specify max size of our buffers */
|
|
size = sizeof(data);
|
|
|
|
/* Convert UBL ELF to srec and get the resulting srec file size and
|
|
* entry point. */
|
|
ret = convert_elf_to_srec(elf_file, temp_file, data, &size,
|
|
&entry_point);
|
|
if (ret)
|
|
return ret;
|
|
|
|
log_info("Sending ACK sequence");
|
|
send_message(" ACK\n");
|
|
|
|
log_info("Sending magic number ($%08X)", magic_number);
|
|
send_number(magic_number, 8);
|
|
|
|
log_info("Sending application entry point ($%08X)", entry_point);
|
|
send_number(entry_point, 8);
|
|
|
|
log_info("Sending application size ($%08X bytes)", size);
|
|
send_number(size, 8);
|
|
|
|
log_info("Sending termination sequence");
|
|
send_number(0x0000, 4);
|
|
|
|
/* We can also receive BADCNT if the size is invalid. */
|
|
found = wait_for_message("BEGIN", "BADADDR", "BADCNT");
|
|
if (found != 1)
|
|
goto error;
|
|
|
|
log_info("Sending SREC data");
|
|
send_ascii_data(data, size);
|
|
|
|
/* First DONE message indicates UBL received all data. */
|
|
found = wait_for_message("DONE", NULL, NULL);
|
|
if (!found)
|
|
goto error;
|
|
|
|
/* The second DONE message indicates the S-record was decoded
|
|
* successfully. */
|
|
found = wait_for_message("DONE", "BOOTME", NULL);
|
|
if (found != 1)
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
send_ubl_srec(char *elf_file)
|
|
{
|
|
int found;
|
|
int ret;
|
|
|
|
log_info("Sending CMD sequence");
|
|
send_message(" CMD\n");
|
|
|
|
log_info("Sending magic number ($%08X)", UBL_MAGIC_NAND_FLASH);
|
|
send_number(UBL_MAGIC_NAND_FLASH, 8);
|
|
|
|
found = wait_for_message("SENDUBL", NULL, NULL);
|
|
if (!found)
|
|
return 1;
|
|
|
|
ret = send_srec(elf_file, "/tmp/ubl.srec", UBL_MAGIC_SAFE);
|
|
if (ret) {
|
|
log_info("Error sending UBL for flashing");
|
|
return ret;
|
|
}
|
|
|
|
found = wait_for_message("SENDAPP", NULL, NULL);
|
|
if (!found)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
send_app_srec(char *elf_file)
|
|
{
|
|
int found;
|
|
int ret;
|
|
|
|
ret = send_srec(elf_file, "/tmp/app.srec", UBL_MAGIC_BIN_IMG);
|
|
if (ret) {
|
|
log_info("Error sending application for flashing");
|
|
return ret;
|
|
}
|
|
|
|
/* Wait for third DONE message that indicates booting */
|
|
found = wait_for_message("DONE", NULL, NULL);
|
|
if (!found)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Main function
|
|
******************************************************************************/
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int ret;
|
|
struct options_t *options;
|
|
|
|
options = parse_command_line_options(argc, argv);
|
|
|
|
parse_environment_variables();
|
|
|
|
ret = check_source_files(options->ubl, options->app);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = serial_port_open(options->port_string, options->baud_rate_index);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Send UBL in binary format for RBL */
|
|
ret = send_ubl_binary(options->ubl);
|
|
if (ret)
|
|
goto error;
|
|
|
|
/* UBL is now running.
|
|
* Send UBL for flashing in srec format. */
|
|
ret = send_ubl_srec(options->ubl);
|
|
if (ret)
|
|
goto error;
|
|
|
|
/* Send application for flashing in srec format. */
|
|
ret = send_app_srec(options->app);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
/* Close serial port. */
|
|
serial_port_close();
|
|
|
|
return ret;
|
|
|
|
error:
|
|
ret = 1;
|
|
goto end;
|
|
}
|