/* * 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 BINAPP_MAX_SIZE (15 * 1024 * 1024) /* 15 Mbytes */ /* RBL & UBL magic numbers */ #define RBL_MAGIC_SAFE 0xA1ACED00 /* Describes UBL flash image type for * RBL. */ #define UBL_MAGIC_BIN_IMG 0xA1ACED66 /* Describes binary flash image type * for UBL. */ #define UBL_MAGIC_GZIP_IMG 0xA1ACED77 /* Describes gzipped binary flash * image type for UBL. */ /* UBL commands */ #define UBL_CMD_FLASH_UBL_APP 0xA1ACEDCC /* Download UBL & application via * UART and burn in flash. */ #define UBL_CMD_FLASH_DATA 0xA1ACEDCD /* Download data via UART and * burn in flash (no header in flash). */ #define UBL_CMD_FLASH_ERASE 0xA1ACEDCE /* Erase the whole flash. */ #define UBL_CMD_RUN_APP 0xA1ACEDDD /* Load and run application via UART. */ #define UBL_CMD_DDR_TEST 0xA1ACEDEE /* Test DDR2 memory. */ #define DEFAULT_CROSS_COMPILE "arm-poky-linux-gnueabi-" static char cross_compile[MAX_ENV_VAR_LENGTH]; static struct options_t *options; 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; } static int read_file_data(const char*file, u_int8_t *data, ssize_t *size_in_bytes) { int ret; ssize_t len; FILE *fp; fp = fopen(file, "r"); if (!fp) { log_fail("Error opening file %s", file); return 1; } /* Copy compressed file data inside our buffer */ len = fread(data, 1, *size_in_bytes, fp); if (len == *size_in_bytes) { log_fail("Size of file data exceeds our buffer size (%d kbytes)", *size_in_bytes / 1024); return 1; } /* Check that size is u32 aligned. */ *size_in_bytes = len; fclose(fp); ret = 0; return ret; } /* 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) { char cmd[MAX_CMD_LENGTH]; 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 bytes from * the application size. The entry point is not modified. */ char *objcopy_flags = "-R .ddrram -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); log_debug1("Running command: %s", cmd); ret = system(cmd); if (ret) { log_fail("Error converting ELF to binary file"); return 1; } ret = read_file_data(temp_file, data, size_in_bytes); if (ret) { log_fail("Error reading file data"); return ret; } /* Find application entry point */ ret = get_app_entry_point(entry_point, elf_file); if (ret) return ret; return 0; } #define SEND_UBL 0 #define SEND_APP_ELF 1 #define SEND_APP_BIN 2 static int send_binary(const char *orig_file, const char *tmp_file, int format, u_int32_t magic_number, int blocknum) { int found; int ret; u_int32_t entry_point; ssize_t size; u_int8_t *data; u_int32_t crc; int size_len = 4; /* RBL format is default */ int entry_point_len = 4; /* RBL format is default */ data = malloc(BINAPP_MAX_SIZE); if (!data) { log_fail("Memory allocation error"); return -1; } /* Specify max size of our buffers */ size = BINAPP_MAX_SIZE; if (format == SEND_APP_BIN) { ret = copy_file(orig_file, tmp_file); if (ret) goto error_handled; entry_point = blocknum; } else { /* Convert source ELF to binary and get the resulting file size and * entry point. */ ret = convert_elf_to_binary(orig_file, tmp_file, data, &size, &entry_point); if (ret) goto error_handled; } log_info("Sending ACK sequence"); send_message(" ACK\n"); if (format != SEND_UBL) { /* Configure for UBL format. */ size_len = 8; entry_point_len = 8; log_info("Sending magic number ($%08X)", magic_number); send_number(magic_number, 8); } crc = crc32_dv_compute(data, size); log_info("Sending CRC ($%08X)", crc); send_number(crc, 8); log_info("Sending size ($%08X bytes)", size); send_number(size, size_len); log_info("Sending entry point ($%08X)", entry_point); send_number(entry_point, entry_point_len); log_info("Sending termination sequence"); send_number(0x0000, 4); found = wait_for_message("BEGIN", NULL, NULL); if (!found) goto error_unhandled; if (format != SEND_UBL) { /* UBL format */ log_info("Sending binary data"); send_buffer_bin(data, size); /* First DONE message indicates UBL received all data. */ found = wait_for_message("DONE", NULL, NULL); if (!found) goto error_unhandled; /* Second DONE message indicates UBL validated data. */ found = wait_for_message("DONE", "BADCRC", "GZIPERR"); if (found != 1) goto error_unhandled; /* The third DONE message indicates successfull programming in flash. */ found = wait_for_message("DONE", "BOOTME", NULL); if (found != 1) goto error_unhandled; } else { /* RBL format */ log_info("Sending CRC table"); crc_send_table(); found = wait_for_message("DONE", NULL, NULL); if (!found) goto error_unhandled; log_info("Sending UBL data"); send_buffer(data, size); found = wait_for_message("DONE", "CORRUPT", NULL); if (found != 1) goto error_unhandled; /* * It is possible that bootmode=nand but the UBL partition is * blank. In that case the RBL will load the UBL via the UART * and we have just provided the UBL. Once UBL is started it * will recognize that it is in bootmode=nand and attempts to * load the U-Boot payload. If it is present U-Boot will be * started and UBL will not be programmed to the nand. * * We are using the the I_ME feature to interrupt the boot. */ found = wait_for_message("BootMode = UART", "I_ME", NULL); if (!found) log_info("Warning: invalid boot mode !!"); else if (found == 2) { log_info("Sending interrupt command to UBL ($%08X)", 0x23); send_message(" CMD\n"); send_number(0x23, 8); } found = wait_for_message("BOOTPSP", NULL, NULL); if (!found) goto error_unhandled; } ret = 0; error_handled: free(data); return ret; error_unhandled: ret = 1; goto error_handled; } /******************************************************************************* * Main function ******************************************************************************/ int main(int argc, char *argv[]) { int ret; int found; u_int32_t cmd; options = parse_command_line_options(argc, argv); parse_environment_variables(); ret = check_source_files(options->app_header, options->ubl, options->app); if (ret) return ret; ret = serial_port_open(options->port_string, options->baud_rate_index); if (ret) return ret; /* Put the board in UART boot and reset it */ set_rts(0); set_dtr(1); usleep(500 * 1000); set_rts(1); usleep(500 * 1000); set_rts(0); usleep(500 * 1000); /* 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", "I_ME"); if (found == 0) goto error; else if (found == 1) { /* Send UBL in binary format for RBL. * Magic and block numbers are not used. */ ret = send_binary(options->ubl, "/tmp/ubl.bin", SEND_UBL, 0, 0); if (ret) goto error; } else if (found == 3) { log_info("Sending interrupt command to UBL ($%08X)", 0x23); send_message(" CMD\n"); send_number(0x23, 8); } /* UBL is now running. Send command */ if (options->ddr_test) cmd = UBL_CMD_DDR_TEST; else if (options->app_header == 0) cmd = UBL_CMD_FLASH_DATA; else cmd = UBL_CMD_FLASH_UBL_APP; log_info("Sending command to UBL ($%08X)", cmd); send_message(" CMD\n"); send_number(cmd, 8); if (options->ddr_test) { found = wait_for_message("DDRTEST_SUCCESS", "DDRTEST_FAILURE", NULL); if (found != 1) goto error; } else if (options->app_header == 0) { found = wait_for_message("SENDDATA", NULL, NULL); if (!found) goto error; /* Send DATA in binary format for flashing. * Magic number is not used. */ ret = send_binary(options->app, "/tmp/data.bin", SEND_APP_BIN, 0, options->blocknum); if (ret) { log_info("Error sending DATA for flashing"); goto error; } } else { found = wait_for_message("SENDUBL", NULL, NULL); if (!found) goto error; /* Send UBL in binary format for flashing. * Block number is not used. */ ret = send_binary(options->ubl, "/tmp/ubl.bin", SEND_APP_ELF, RBL_MAGIC_SAFE, 0); if (ret) { log_info("Error sending UBL for flashing"); goto error; } found = wait_for_message("SENDAPP", NULL, NULL); if (!found) goto error; /* Send application in binary format for flashing. * Block number is not used. */ ret = send_binary(options->app, "/tmp/app.bin", SEND_APP_ELF, UBL_MAGIC_GZIP_IMG, 0); if (ret) { log_info("Error sending application for flashing"); goto error; } } ret = 0; end: set_dtr(0); set_rts(1); usleep(100 * 1000); set_rts(0); /* Close serial port. */ serial_port_close(); return ret; error: ret = 1; goto end; }