dvnixload/src/main.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;
}