/* * Main function. * * Copyright (C) 2008 Hugo Villeneuve * * 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 #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include #include #include #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; }