FreeCalypso > hg > freecalypso-reveng
diff fluid-mnf/machine.c @ 311:9cecc930d78f
fluid-mnf: original source from TI,
defenestrated line endings and rearranged directory structure,
but no *.[ch] source file content changes yet
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sat, 29 Feb 2020 05:36:07 +0000 |
parents | |
children | 435e041897f2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fluid-mnf/machine.c Sat Feb 29 05:36:07 2020 +0000 @@ -0,0 +1,2635 @@ +/****************************************************************************** + * FLUID (Flash Loader Utility Independent of Device) + * + * Copyright Texas Instruments, 2001. + * Mads Meisner-Jensen, mmj@ti.com. + * Original state-machine logic by Delta Technologies, Copyright, 2001. + * + * Core functionality. State machines and support functions. + * + * $Id: machine.c 1.35.1.37 Mon, 28 Apr 2003 08:49:16 +0200 tsj $ + * + ******************************************************************************/ + +#include "fluid.h" +#include "flash.h" +#include "target.h" +#include "fileio.h" +#include "../target/protocol.h" +#include "misc.h" +#include "lz.h" +#include "trace.h" +// Secure Calypso Plus +#include "../inc/ram_load.h" +#include "../inc/secure_types.h" + +#include <stdio.h> +#include <malloc.h> +#include <stdlib.h> +#include <string.h> + +/****************************************************************************** + * Constants and typedefs + ******************************************************************************/ + +// Due to memory wrap/mirror used in flash_read_machine(), IMAGE_SIZE_MAX +// *must* be a power of two. +#define IMAGE_SIZE_MAX (8 * 1024 * 1024) + +#define IMAGE_CHUNK_SIZE 8192 + +#define ERASE_LIST_SIZE_MAX 256 +#define SECTOR_MAP_SIZE_MAX 256 + +#define TARGET_PROGRAM_SIZE_MAX (8 * 1024) + +#define READ_LIST_SIZE_MAX 40 + +#define RETRIES_MAX 30 + +// Default target receive delay in milli-seconds. +#define TARGET_RECV_DELAY 250 +#define TARGET_RECV_LONG_DELAY 1000 + +// Default target reset delay in milli-seconds. +#define TARGET_RESET_DELAY 200 + +// CS_MODE 0: Flash at 0x04000000 @ CS5 on E-Sample +#define CALP_OFFSET 0x04000000 + +/****************************************************************************** + * Globals + ******************************************************************************/ + +// Secure Calypso Plus +T_RAM_LOADER d_ram_loader; +T_FRAME d_frame; +UWORD16 d_certificate_length = 0; +char a_certified_cmd_file_name[] = "cmdp_cert.m0"; + +struct target_s { + uint32 cmd_load_addr; + uint32 method_load_addr; + const char type; + const char *name; + const char *prefix; +}; +// Note that the ROM bootloader of Calypso rev. A devices are unable to +// write the bottom 128kB of internal RAM. Therefore the address have been +// changed from 0x804000 to 0x0820000 +const struct target_s target[] = +{ + { 0x0000000, 0x0000000, 'u', "Unknown", "oops" }, + { 0x0000000, 0x0000000, 'u', "Unknown1", "oops1" }, + { 0x0000000, 0x0000000, 'G', "Gemini?", "gem?" }, + { 0x3004000, 0x3000000, 'U', "Ulysses", "uly" }, + { 0x0820000, 0x0800000, 'C', "Calypso", "cal" }, + { 0x0804000, 0x0800000, 'c', "Calypso Lite", "cal_l" }, + { 0x8020000, 0x8000100, 'P', "Calypso Plus", "cal_p" } +}; +int target_type; +int target_clk; // 13000000 or 14745600 + +struct device_s *device; + +uint8 target_program[TARGET_PROGRAM_SIZE_MAX]; +int target_program_size; +int target_fifo_size; + +uint8 image[IMAGE_SIZE_MAX]; +int image_size; // size of image buffer +int image_chunk_size; // number of bytes in each chunk +int image_size_in_chunks; // size of image buffer in chunks + +// Each byte in the image_map represents one chunk of the image. Each byte +// can have one of the following values: +// '\0' = no data in this chunk +// 's' = skip this chunk when programming +// 'c' = chunk checksum ok, skip this chunk when programming +// 'x' = used +uint8 image_map[IMAGE_SIZE_MAX / IMAGE_CHUNK_SIZE]; +int image_map_size; // number of entries in image_map + +// Each byte in the sector_map represents one sector. Each byte can have one +// of the following values: +// '\0' = sector unused (don't erase) +// 'c' = sector already empty/erased (don't erase) (not used) +// 's' = skip this sector when erasing (due to erase override) +// 'X' = force erase this sector (due to erase override) +// 'x' = erase this sector (normal case --- sector is covered by image) +int sector_map[SECTOR_MAP_SIZE_MAX]; +int sector_map_size; + +int erase_list[ERASE_LIST_SIZE_MAX]; +int erase_list_size; + +struct image_part_s read_list[READ_LIST_SIZE_MAX]; +int read_list_size; +int read_total_size; + +int image_map_count_used_chunks(void); +int image_map_update(void); +int sector_map_init(void); + +void image_map_show(void); +void sector_map_show(void); +void target_timers_show(void); + +void parse_arg_erase_override(char *p); +void parse_arg_read(char *p); +void parse_arg_write(char *p); + +void error_proto(char ch, char ch_expected); + +int target_type_set(uint16 code); + +// Secure Calypso Plus +void f_print_signalling_response(int level); +void f_print_certificate_platform_data(int level, T_MANUFACTURER_CERTIFICATE_PLATFORM_DATA *p_certificate); +void f_print_certificate(int level, T_MANUFACTURER_CERTIFICATE_FLASH_PROGRAMMER *p_certificate); +void f_print_parameter_nack_status(int level, UWORD8 d_parameter_nack_sts); +void f_print_write_nack_status(int level, UWORD8 d_write_nack_sts); +UWORD8 f_convert_uart_baud_rate(UWORD32 d_baud_rate); +void target_imei_protection(void); + +/****************************************************************************** + * Main + ******************************************************************************/ + +void bootloader_machine(void); +void cmd_machine(void); +void flash_checksum_machine(void); +void flash_detect_machine(void); +void method_download_machine(void); +void flash_program_machine(void); +void flash_read_machine(void); +void target_reset_machine(void); + +void fluid_machine(void) +{ + int error; + + // If user has specifically asked fluid just to reset the target, we do + // that and only that! This way, we can use fluid just to reset the + // target without doing anything else! + if (arg_target_reset == 2 && arg_dry_run) { + flowf(VERBOSE, "Resetting target: "); + target_reset(0); + target_wait(0, TARGET_RESET_DELAY); + target_reset(1); + flowf(VERBOSE, "ok\n"); + } + + file_read_devices("devices.txt"); + + if (arg_list_devices) { + devices_list(); + exit(0); + } + + image_size = IMAGE_SIZE_MAX; + image_chunk_size = IMAGE_CHUNK_SIZE; + image_map_size = IMAGE_SIZE_MAX / image_chunk_size; + image_size_in_chunks = + (image_size + image_chunk_size - 1) / image_chunk_size; + + // Read the flash image file(s) unless user wants to read from the flash + if (*arg_read == 0) { + file_read_image(image, IMAGE_SIZE_MAX, image_map, image_chunk_size); + + // Optionally overwrite image with values given on the command-line + parse_arg_write(arg_write); + + if (arg_image_map_show) + image_map_show(); + } + + if ((error = target_driver_init(arg_uart_port, 115200, + arg_uart_flowcontrol)) < 0) + main_fatal(error); + + if (target_type_set(arg_target_type) != 0) + flowf(VERBOSE, "Target type '%s' selected.\n", target[target_type].name); + + // Conditionally disable tracing + if (arg_debug_trace_pe) + tr_enable(0); + + if (arg_debug_resume) { + // If resuming in command interpreter, we should set the baudrate + // because we do not pass through the code that normally sets it. + if ((error = target_driver_baudrate(arg_uart_baudrate)) < 0) + main_fatal(error); + } + else { + bootloader_machine(); + cmd_machine(); + } + if (*arg_read != 0) { + parse_arg_read(arg_read); + flash_read_machine(); + file_write_image(image, image_size, read_list); + } + else { + if (arg_checksum) + flash_checksum_machine(); + flash_detect_machine(); + if (!arg_debug_resume) { + method_download_machine(); + } + tr_enable(1); // Ensure full tracing is on + flash_program_machine(); + if (*arg_imeisv && *arg_platform_certificate_addr) + target_imei_protection(); + if (arg_timers_show) + target_timers_show(); + if (arg_target_reset == 1) + target_reset_machine(); + } +} + + +/****************************************************************************** + * Bootloader (TI Target Monitor) Access + ******************************************************************************/ + +// Invoke the Fluid bootloader embedded in the TI bootloader/target-monitor +// code. The command to enter the loader is 0xAA 0x01 0xDD, which must be +// received by the phone within 50ms from reset. + +int bootloader_is_rom; +int bootloader_is_secure_rom; // Secure Calypso Plus +int bootloader_blocksize_max; + +void bootloader_fluid_init(void); +void bootloader_rom_init(void); +void bootloader_secure_rom_init(void); // Secure Calypso Plus + +void bootloader_machine(void) +{ + // FIXME: why do we have to send an additional char (0) ? + uint8 sendbuf_rom[3] = { '<', 'i', 0x0 }; + uint8 sendbuf_fluid[3] = { 0xAA, 0x01, 0xDD }; + int i, error; + char ch1, ch2; + // Secure Calypso Plus + UWORD16 d_j; + + target_trace_enable(0); + + flowf(NORMAL, "Bootloader: "); + + // Wait a short while with power removed, then flush receive buffer. + target_power(1); + target_reset(0); + target_wait(0, TARGET_RESET_DELAY); + target_reset(1); + target_recv_reset(); + + // Continuously send Fluid Bootloader escape sequence until we get an + // acknowledgement. Note that we have to establish contact within 50ms + // from reset, otherwise we lose our chance! Note that we actually wait + // up to 500ms because the target may take some time to reset. + i = 0; + flowf(DEBUG, "(ROM/fluid-delay = %d/%d, ", + arg_boot_delay_rom, arg_boot_delay_fluid); + + switch (arg_rom_bootloader) { + case -1: + if ((error = target_driver_baudrate(115200)) < 0) + main_fatal(error); + break; + case +1: + if ((error = target_driver_baudrate(19200)) < 0) + main_fatal(error); + break; + } + while (1) + { + target_recv_reset(); + + // If we are allowed to try fluid bootloader... + if (arg_rom_bootloader != +1) { + // If we should ONLY try fluid bootloader... + flowf(DEBUG, "F"); + if (arg_rom_bootloader == 0) + if ((error = target_driver_baudrate(115200)) < 0) + main_fatal(error); + target_send(sendbuf_fluid, 3); + if (target_wait(1, arg_boot_delay_fluid) > 0 && + target_getchar() == PROTO_HELLO) { + bootloader_is_rom = 0; + bootloader_is_secure_rom = 0; // Secure Calypso Plus + break; + } + } + // If we are allowed to try ROM bootloader... + if (arg_rom_bootloader != -1) { + flowf(DEBUG, "R"); + if (arg_rom_bootloader == 0) + if ((error = target_driver_baudrate(19200)) < 0) + main_fatal(error); + target_send(sendbuf_rom, 3); + + if (target_wait(1, arg_boot_delay_rom) >= 1) { + ch1 = target_getchar(); + if (ch1 == '>') { + if (target_wait(1, arg_boot_delay_rom) >= 1) { + ch2 = target_getchar(); + if (ch2 == 'i') { + bootloader_is_rom = 1; + + // Secure Calypso Plus + if (target_wait(2, arg_boot_delay_rom) < 2) + flowf(DEBUG, ", Non-secure boot ROM code"); + else { + flowf(DEBUG, ", Secure Calypso Plus ROM)"); + ch1 = target_getchar(); + ch2 = target_getchar(); + d_ram_loader.d_romcode_version = (UWORD16)(ch1) & 0xFF; + d_ram_loader.d_romcode_version |= ((UWORD16)(ch2 & 0xFF)) << 8; + //d_ram_loader.d_romcode_version |= (((UWORD16)ch2) << 8) & 0xFF00; + + if (d_ram_loader.d_romcode_version == 0x0410 || d_ram_loader.d_romcode_version == 0x0411) { + bootloader_is_secure_rom = 1; + + if (target_wait((C_WORD32LGB * C_MD5HASHLG), TARGET_RECV_DELAY) < (C_WORD32LGB * C_MD5HASHLG)) + main_fatal(E_RECV_TIMEOUT); + + for (d_j = 0 ; d_j < (C_WORD32LGB * C_MD5HASHLG) ; d_j++) + d_ram_loader.a_hash_man_pub_key[d_j] = target_getchar(); + + if (target_wait((C_WORD32LGB * C_DIE_ID_SIZE), TARGET_RECV_DELAY) < (C_WORD32LGB * C_DIE_ID_SIZE)) + main_fatal(E_RECV_TIMEOUT); + + for (d_j = 0 ; d_j < (C_WORD32LGB * C_DIE_ID_SIZE) ; d_j++) + d_ram_loader.a_die_id[d_j] = target_getchar(); + + // SIGNALLING RESPONSE + f_print_signalling_response(VERBOSE); + + if (*arg_die_id_file_name != 0) { + if ((error = file_write_die_id(&d_ram_loader.a_die_id[0], arg_die_id_file_name)) < 0) { + flowf(NORMAL, "\n"); + main_fatal(error); + } + + flowf(NORMAL, "\nDie id retrieved and written to %s.\n", arg_die_id_file_name); + exit(0); + } + } + else { + flowf(NORMAL, "\nSecure Calypso Plus ROM Code Version 0x%4.4x is not supported by FLUID.\n", d_ram_loader.d_romcode_version); + main_fatal(E_BOOTLOADER); + } + } + // End Secure Calypso Plus + + break; + } + else + flowf(DEBUG, "?"); + } + } + } + } + // If target is still not responding, we could not control the reset + // line, so we ask the user to reset the target + if (i++ == RETRIES_MAX) + flowf (NORMAL, "(reset target) "); + } + if (!bootloader_is_secure_rom) + flowf(DEBUG, ") "); + + // Read the command interpreter image file. + if (bootloader_is_secure_rom) + target_program_size = file_read_cmd(target_program, TARGET_PROGRAM_SIZE_MAX, a_certified_cmd_file_name); + else + target_program_size = file_read_cmd(target_program, TARGET_PROGRAM_SIZE_MAX, "cmd.m0"); + + if (target_program_size == 0) + main_error(E_FILE_EMPTY); + + if (bootloader_is_secure_rom) + bootloader_secure_rom_init(); + else if (bootloader_is_rom) + bootloader_rom_init(); + else + bootloader_fluid_init(); + + flowf(NORMAL, ") ok\n"); +} + +void bootloader_fluid_init(void) +{ + int divider; + char version; + uint8 data[4]; + uint16 chip_id_code; + + flowf(NORMAL, "(fluid"); + + target_clk = 13000000; + + // Now send baudrate to make the target stop sending 'H'ello. + divider = target_uart_baudrate_divider_get(target_clk, 115200); + flowf(DEBUG, ", baudrate = "); + target_putchar(0); + target_putchar((char) divider); + flowf(DEBUG, "%d, ", 115200); + + // Wait a short while before flushing receive buffer. Then make sure + // that the target is indeed silent + target_wait(0, 10); + target_recv_reset(); + if (target_wait(1, 50) > 0) + main_fatal(E_RECV_ANTITIMEOUT); + + // NOTEME: The sequence below to query the Bootloader VERSION *has* to + // have 100ms + 100ms delays. It seems as unnecessarily large delays but + // practice shows that they cannot be shorter. To be investigated... + + // Get version of bootloader + target_putchar(PROTO_VERSION); + if (target_wait(1, TARGET_RECV_DELAY) < 1) + main_fatal(E_RECV_TIMEOUT); + version = target_getchar(); + flowf(NORMAL, ", version %c", version); + + // Bootloader revision 3 and upwards supports generic query + if (version < '3') { + main_fatal(E_BOOTLOADER); + } + + target_trace_enable(1); + target_putchar(PROTO_QUERY); + target_putchar(PROTO_QUERY_CHIP); + if (target_wait(4, TARGET_RECV_DELAY) < 4) + main_fatal(E_RECV_TIMEOUT); + target_recv(data, 4); + chip_id_code = data[0] + (data[1] << 8); + if (target_type_set(chip_id_code) == 0 || arg_verbose >= VERBOSE) { + flowf(VERBOSE, ", chipid = 0x%04X", chip_id_code); + if (target_type_set(chip_id_code) == 0) + main_fatal(E_TARGET_TYPE); + } + flowf(VERBOSE, ", %s", target[target_type].name); +} + +void bootloader_rom_init(void) +{ + int error; + char ch, version = '?'; + + // Initialization: 115200 bps, 39 MHz DPLL, no timeout + uint8 sendbuf[11] = { '<', 'p', 0x00, 0x0D, 0x14, 0x25, + 0x22, 0x00, 0x00, 0x00, 0x00}; + // Initialization: 115200 bps, 13 MHz DPLL, no timeout. + //uint8 sendbuf[11] = { '<', 'p', 0x00, 0x00, 0x1C, 0xE7, + // 0x22, 0x00, 0x00, 0x00, 0x00 }; + + flowf(NORMAL, "(ROM"); + + // Configure/init the ROM bootloader + flowf(VERBOSE, ", baudrate = %d", 115200); + target_send(sendbuf, 11); + + // Wait until DPLL is settled and target responds + if (target_wait(4, 300) < 4) + main_fatal(E_RECV_TIMEOUT); + if ((ch = target_getchar ()) != '>') + error_proto(ch, '>'); + if ((ch = target_getchar ()) != 'p') + error_proto(ch, 'p'); + + // Receive maximum blocksize + bootloader_blocksize_max = target_getchar(); + bootloader_blocksize_max += target_getchar() << 8; + bootloader_blocksize_max -= 10; // Subtract the block header + flowf(DEBUG, ", blocksize = %iB", bootloader_blocksize_max); + + if ((error = target_driver_baudrate (115200)) < 0) + main_fatal(error); + + flowf(NORMAL, ", version %c", version); + + // NOTEME: Can we be sure that it is always a Calypso type? + target_type_set('c'); +} + +// Secure Calypso Plus +void bootloader_secure_rom_init(void) +{ + int error; + UWORD8 d_char; + UWORD16 d_j; + + // cmdp_cert.m0 has been 16 bit aligned using hex470, so the memory width + // parameter is set to 2 bytes. + buffer_endian_convert(target_program, target_program_size, 2); + + // Check if firmware manufacturer certificate stored in flash is requested + if (arg_request_certificate) { + // Clear the request + arg_request_certificate = 0; + d_ram_loader.b_certificate_request = C_FALSE; + + // Certificate Request + d_frame.a_data[0] = '<'; + d_frame.a_data[1] = 'c'; + d_frame.d_max_byte = 2; + + target_send(&d_frame.a_data[0], d_frame.d_max_byte); + + // Certificate Response + target_expect_char('>', arg_boot_delay_rom); + target_expect_char('c', arg_boot_delay_rom); + + if (target_wait(2, TARGET_RECV_DELAY) < 2) + main_fatal(E_RECV_TIMEOUT); + + d_ram_loader.u_firm_cert.a_firm_cert[0] = target_getchar (); + d_ram_loader.u_firm_cert.a_firm_cert[1] = target_getchar (); + + d_certificate_length = (UWORD16)(d_ram_loader.u_firm_cert.a_firm_cert[0]) & 0xFF; + d_certificate_length |= ((UWORD16)(d_ram_loader.u_firm_cert.a_firm_cert[1]) << 8) & 0xFF00; + + // We have already read two bytes of the certificate for the CERT_SIZE + // and the certificate signature is handled later. + for (d_j = 2 ; d_j < d_certificate_length - (C_WORD32LGB * C_MANUF_SIG_SIZE); d_j++) { + if (target_wait(1, arg_boot_delay_rom * 4) > 0) + d_ram_loader.u_firm_cert.a_firm_cert[d_j] = target_getchar (); + else + main_fatal(E_RECV_TIMEOUT); + } + + for (d_j = 0; d_j < (C_WORD32LGB * C_MANUF_SIG_SIZE); d_j++) { + if (target_wait(1, arg_boot_delay_rom * 4) > 0) + d_ram_loader.a_Certsig[d_j] = target_getchar (); + else + main_fatal(E_RECV_TIMEOUT); + + d_ram_loader.u_firm_cert.a_firm_cert[d_j + sizeof(T_MANUFACTURER_CERTIFICATE_PLATFORM_DATA) - (C_WORD32LGB * C_MANUF_SIG_SIZE)] = d_ram_loader.a_Certsig[d_j]; + } + + f_print_certificate_platform_data(NORMAL, &d_ram_loader.u_firm_cert.d_firm_cert); + } + + // Parameter Request + d_ram_loader.d_baud_rate = f_convert_uart_baud_rate(arg_uart_baudrate_during_cmd_download); + d_ram_loader.d_uart_timeout = arg_uart_timeout_configuration; // TODO: Verify that MSB byte is sent in first position. + + d_frame.a_data[0] = '<'; + d_frame.a_data[1] = 'p'; + d_frame.a_data[2] = d_ram_loader.d_baud_rate; + memcpy(&d_frame.a_data[3], &d_ram_loader.d_uart_timeout, sizeof(UWORD32)); + + d_frame.d_max_byte = 3 + sizeof(UWORD32); + + d_certificate_length = (UWORD16)(target_program[0]) & 0xFF; + d_certificate_length |= ((UWORD16)(target_program[1]) & 0xFF) << 8; + + for (d_j = 0 ; d_j < d_certificate_length; d_j++) + d_ram_loader.u_code_certificate.a_code_certificate[d_j] = target_program[d_j]; + + f_print_certificate(DEBUG, &d_ram_loader.u_code_certificate.d_code_certificate); + + // Send the data + target_send(&d_frame.a_data[0], d_frame.d_max_byte); + target_send(&target_program[0], d_certificate_length); + + // Parameter Response + if (target_wait(2, TARGET_RECV_DELAY) < 2) + main_fatal(E_RECV_TIMEOUT); + + if ((d_char = target_getchar ()) != '>') + error_proto(d_char, '>'); + + if ((d_char = target_getchar ()) != 'p') { + if (d_char == 'P') { + // PARAMETER_NACK_RESPONSE + if (target_wait(1, arg_boot_delay_rom) >= 1) { + d_ram_loader.d_param_req_sts = target_getchar(); + f_print_parameter_nack_status(NORMAL, d_ram_loader.d_param_req_sts); + } + } + error_proto(d_char, 'p'); + } + + // PARAMETER_ACK_RESPONSE + + flowf(NORMAL, "\n(Secure ROM"); + + if ((error = target_driver_baudrate (arg_uart_baudrate_during_cmd_download)) < 0) + main_fatal(error); + + // Configure/init the ROM bootloader + flowf(VERBOSE, ", UART baud rate during download of flash programmer, %s = %d Kbps", a_certified_cmd_file_name, arg_uart_baudrate_during_cmd_download); +} +// End Secure Calypso Plus + +/****************************************************************************** + * Command Interpreter Download + ******************************************************************************/ + +int cmd_baudrate(int baudrate); +void cmd_machine_fluid(void); +void cmd_machine_rom(void); +void cmd_machine_secure_rom(void); // Secure Calypso Plus + +void cmd_machine(void) +{ + char ramcs0, ch; + uint8 count = 0; + uint16 chip_id; + + flowf(VERBOSE, "Command Interpreter: ("); + + target_trace_enable(0); + + // Secure Calypso Plus + if (bootloader_is_secure_rom) + cmd_machine_secure_rom(); + else if (bootloader_is_rom) + cmd_machine_rom(); + else + cmd_machine_fluid(); + + // Now send 'H'ello command to target and get the response, such as + // hardware type etc. + while (count < RETRIES_MAX) { + target_putchar(PROTO_HELLO); + if (target_wait(1, TARGET_RECV_DELAY) >= 1) { + ch = target_getchar(); + flowf(DEBUG, ", received %c (0x%2.2x)", ch, ch); + if (ch == PROTO_READY) + break; + } + count++; + } + + if (count == RETRIES_MAX) + main_fatal(E_RECV_TIMEOUT); + + if (target_wait(5, TARGET_RECV_DELAY) < 4) + main_fatal(E_RECV_TIMEOUT); + + chip_id = (target_getchar() & 0xFF); + chip_id |= (target_getchar() & 0xFF) << 8; + ramcs0 = (target_getchar() == 'R'); + ch = target_getchar(); + flowf(BLABBER, ", SRAM = %dk", (1 << ch) / 1024); + target_fifo_size = target_getchar(); + flowf(DEBUG, ", fifo = %d", target_fifo_size); + + if (target_type_set(chip_id) == 0 || arg_verbose >= VERBOSE) { + flowf(BLABBER, ", "); + flowf(VERBOSE, "chipid = 0x%04X", chip_id); + if (target_type_set(chip_id) == 0) + main_fatal(E_TARGET_TYPE); + } + flowf(VERBOSE, ", %s", target[target_type].name); + + if (ramcs0) + flowf(VERBOSE, ", RAM"); + + // Configure target + target_putchar(PROTO_INIT); + target_putchar(target[target_type].type); + target_expect_char(PROTO_READY, TARGET_RECV_DELAY); + + // Change baudrate. First try to change to new baudrate. If this fails, + // try 115.2 Kbps. If this also fails, panic and bail out. + if (arg_uart_baudrate != 115200) { + flowf(VERBOSE, ", baudrate = "); + flowf(VERBOSE, "%d", arg_uart_baudrate); + if (!cmd_baudrate(arg_uart_baudrate)) { + arg_uart_baudrate = 115200; + flowf(VERBOSE, " %d", arg_uart_baudrate); + if (!cmd_baudrate(arg_uart_baudrate)) + main_fatal(E_RECV_TIMEOUT); + } + } + flowf(VERBOSE, ") "); + flowf(VERBOSE, "ok\n"); + + // Secure Calypso Plus + + // There is a bug in ROM code 0x0410 which means that the secure boot + // loader cannot boot if the firmware certificate size is greater than the + // max size of 0xFFF8. A workaround is to change the memory mapping on CS5 + // by initially setting DIP switches 9 and 10 to ON and thereby exchanging + // the mapping of RAM and flash. While waiting, set the DIP switches back + // to OFF. See SECURITY.txt and BUG03314 in CALPLUS228. + if (arg_delay_for_changing_cs5 != 0) { + flowf(NORMAL, "\nWaiting %d seconds for changing memory mapping on CS5 or connecting via JTAG...\n", arg_delay_for_changing_cs5); + target_wait(0, arg_delay_for_changing_cs5 * 1000); + } +} + +int cmd_baudrate(int baudrate) +{ + int error; + int divider; + char xxo; + + target_putchar(PROTO_BAUDRATE); + if (baudrate == 230400 || baudrate == 460800 || baudrate == 921600) { + // Changing the target clock frequency to 14 MHz + target_clk = 14745600; + // If we are using a 14 MHz compatible baud rate, we should enable the + // eXternal Xtal Oscillator of the target + xxo = PROTO_BAUDRATE_XXO; + } + else { + target_clk = 13000000; + xxo = 0; + } + + divider = target_uart_baudrate_divider_get(target_clk, baudrate); + target_putchar(xxo); + target_putchar((char) divider); + + if ((error = target_driver_baudrate(baudrate)) < 0) + main_fatal(error); + + // Wait for acknowledgement + if (target_wait(1, 2 * TARGET_RECV_DELAY) >= 1 && + target_getchar() == PROTO_READY) { + return 1; + } + else { + if ((error = target_driver_baudrate(115200)) < 0) + main_fatal(error); + return 0; + } +} + +void cmd_machine_fluid(void) +{ + uint8 sendbuf[1+4+2]; + int error; + + // Send 'Download' command header. Then wait for acknowledgement. + buf_put1(&sendbuf[0], PROTO_DOWNLOAD); + buf_put4(&sendbuf[1], target[target_type].cmd_load_addr); + buf_put2(&sendbuf[1+4], (uint16) target_program_size / 2); + target_send(sendbuf, 1+4+2); + flowf(BLABBER, "0x%X", target[target_type].cmd_load_addr); + if ((error = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0) + main_fatal(error); + + // Send data. Then wait for acknowledgement. + target_trace_enable(1); + target_send(target_program, target_program_size); + flowf(BLABBER, ", %d", target_program_size); +} + +void cmd_machine_rom(void) +{ + uint8 sendbuf[10], *buf; + int error, i; + int block_addr; + int block_offset = 0; + int block_size; + uint8 ch, blksum, cksum = 0; + + // NOTEME: Can we be sure that it is always a Calypso type? + target_type_set('c'); + + block_addr = target[target_type].cmd_load_addr; + + buffer_endian_convert(target_program, target_program_size, 2); + + flowf(BLABBER, "0x%X, ", target[target_type].cmd_load_addr); + + // Transfer the target program as blocks + while (block_offset < target_program_size) + { + block_size = target_program_size - block_offset; + if (block_size > bootloader_blocksize_max) + block_size = bootloader_blocksize_max; + + // Initialize block transfer + buf = sendbuf; + buf += buf_put1(buf, '<'); + buf += buf_put1(buf, 'w'); + buf += buf_put1(buf, 0x01); // block index - just use #1 - not important + buf += buf_put1(buf, 0x01); // block number - just use #1 - not important + buf += buf_put2no(buf, (uint16) block_size); + buf += buf_put4no(buf, block_addr + block_offset); + target_send(sendbuf, 10); + + // Send the data + target_send(&target_program[block_offset], block_size); + + // Calculate block check-sum + blksum = 5; + blksum += block_size & 0x00ff; + blksum += (((block_addr + block_offset) & 0xff000000) >> 24); + blksum += (((block_addr + block_offset) & 0x00ff0000) >> 16); + blksum += (((block_addr + block_offset) & 0x0000ff00) >> 8); + blksum += ( (block_addr + block_offset) & 0x000000ff); + for (i = block_offset; i < (block_offset + block_size); i++) + blksum += target_program[i]; + cksum += ~blksum; + + block_offset = block_offset + block_size; + + if ((error = target_expect_char ('>', TARGET_RECV_DELAY)) < 0) + main_fatal (error); + if ((error = target_expect_char ('w', TARGET_RECV_DELAY)) < 0) + main_fatal (error); + + flowf(BLABBER, "."); + } + flowf(BLABBER, ", %d", target_program_size); + target_wait(0, 100); + + // Request compare of checksum + target_putchar('<'); + target_putchar('c'); + target_putchar((char) ~cksum); + + if ((error = target_expect_char('>', TARGET_RECV_DELAY)) < 0) + main_fatal(error); + if ((error = target_expect_char('c', TARGET_RECV_DELAY)) < 0) + main_fatal(error); + if ((error = target_wait(1, TARGET_RECV_DELAY)) < 1) + main_fatal(error); + ch = target_getchar(); + flowf(DEBUG, ", cksum = 0x%x (0x%x)", ch, cksum); + if (ch != cksum) + main_fatal(E_SEND_CHECKSUM); + + // Branch to code + buf = sendbuf; + buf += buf_put1(buf, '<'); + buf += buf_put1(buf, 'b'); + buf += buf_put4no(buf, target[target_type].cmd_load_addr); + target_send(sendbuf, 6); + + if (target_wait(2, TARGET_RECV_DELAY) < 2) + main_fatal(E_RECV_TIMEOUT); + if ((ch = target_getchar()) != '>') + error_proto(ch, '>'); + if ((ch = target_getchar()) != 'b') + error_proto(ch, 'b'); + + target_wait(0, 100); +} + +// Secure Calypso Plus +void cmd_machine_secure_rom(void) +{ + UWORD8 d_char; + UWORD16 d_i; + + target_type_set('p'); + flowf(BLABBER, "0x%X", d_ram_loader.u_code_certificate.d_code_certificate.d_Addcode); + flowf(DEBUG, ")"); + + // Prepare the write command + d_ram_loader.d_nb_byte_in_block = (UWORD32) arg_block_size; + d_ram_loader.d_nb_byte_sent = 0; + d_ram_loader.d_block_address = d_ram_loader.u_code_certificate.d_code_certificate.d_Addcode; + + while (d_ram_loader.d_nb_byte_sent < d_ram_loader.u_code_certificate.d_code_certificate.d_Codesize) { + flowf(DEBUG, "\nCurrent write parameters:\n"); + flowf(DEBUG, " Max Number of Bytes in Block: %ld\n", d_ram_loader.d_nb_byte_in_block); + flowf(DEBUG, " Block Address : 0x%8.8lx\n", d_ram_loader.d_block_address); + flowf(DEBUG, " Code Size : %ld\n", d_ram_loader.u_code_certificate.d_code_certificate.d_Codesize); + flowf(DEBUG, " Bytes sent : %ld\n", d_ram_loader.d_nb_byte_sent); + + // Set block size + d_ram_loader.d_block_size = d_ram_loader.u_code_certificate.d_code_certificate.d_Codesize - d_ram_loader.d_nb_byte_sent; + if (d_ram_loader.d_block_size > d_ram_loader.d_nb_byte_in_block) + d_ram_loader.d_block_size = d_ram_loader.d_nb_byte_in_block; + + flowf(DEBUG, " Block Size : %ld\n", d_ram_loader.d_block_size); + + // Write Request + d_frame.a_data[0] = '<'; + d_frame.a_data[1] = 'w'; + + // Send block size + for (d_i = 0; d_i < 32; d_i += 8) + d_frame.a_data[2 + d_i / 8] = (UWORD8)(d_ram_loader.d_block_size >> (24 - d_i)); + + // Send block address + for (d_i = 0; d_i < 32; d_i += 8) + d_frame.a_data[2 + sizeof(UWORD32) + d_i / 8] = (UWORD8)(d_ram_loader.d_block_address >> (24 - d_i)); + + d_frame.d_max_byte = 2 + sizeof(UWORD32) + sizeof(UWORD32); + + // Send the data + target_send(&d_frame.a_data[0], d_frame.d_max_byte); + target_send(&target_program[d_certificate_length + d_ram_loader.d_nb_byte_sent], d_ram_loader.d_block_size); + + // Update next block address + d_ram_loader.d_block_address += d_ram_loader.d_block_size; + d_ram_loader.d_nb_byte_sent += d_ram_loader.d_block_size; + + // Write Response + if (target_wait(2, TARGET_RECV_DELAY) < 2) + main_fatal(E_RECV_TIMEOUT); + + if ((d_char = target_getchar ()) != '>') + error_proto(d_char, '>'); + + if ((d_char = target_getchar ()) != 'w') { + if (d_char == 'W') { + // WRITE_NACK_RESPONSE + if (target_wait(1, arg_boot_delay_rom) >= 1) { + d_ram_loader.d_write_status = target_getchar(); + f_print_write_nack_status(NORMAL, d_ram_loader.d_write_status); + } + } + error_proto(d_char, 'w'); + } + + // WRITE_ACK_RESPONSE + } // End while + if (arg_verbose == BLABBER) + flowf(BLABBER, ", "); + else + flowf(DEBUG, "("); + + flowf(BLABBER, "%d", d_ram_loader.d_nb_byte_sent); + target_wait(0, 100); + + // Abort Request + d_frame.a_data[0] = '<'; + d_frame.a_data[1] = 'a'; + d_frame.d_max_byte = 2; + + target_send(&d_frame.a_data[0], d_frame.d_max_byte); + target_wait(0, 100); +} + +void f_print_signalling_response(int level) { + UWORD16 d_i; + + flowf(level, "\nSignalling Response:\n"); + + flowf(level, " ROM Code Version: 0x%4.4x\n", d_ram_loader.d_romcode_version); + + flowf(level, " Hash (ManPubKey): "); + for (d_i = 0; d_i < (C_WORD32LGB * C_MD5HASHLG); d_i++) { + if (d_i == (C_WORD32LGB * C_MD5HASHLG) / 2) + flowf(level, "\n "); + flowf(level, "0x%2.2x ", d_ram_loader.a_hash_man_pub_key[d_i]); + } + flowf(level, "\n"); + + flowf(level, " Die Id : "); + for (d_i = 0; d_i < (C_WORD32LGB * C_DIE_ID_SIZE); d_i++) + flowf(level, "0x%2.2x ", d_ram_loader.a_die_id[d_i]); + flowf(level, "\n"); +} /* f_print_signalling_response() */ + +void f_print_certificate_platform_data(int level, T_MANUFACTURER_CERTIFICATE_PLATFORM_DATA *p_certificate) { + UWORD16 d_i, d_j, d_max = 4; + + flowf(level, "\nFirmware Manufacturer Certificate:\n"); + flowf(level, "----------------------------------\n"); + + flowf(level, " Size of Certificate: %d bytes\n", p_certificate->d_manufacturer_certificate.d_Certsize); + flowf(level, " Type of Certificate: 0x%2.2x\n", p_certificate->d_manufacturer_certificate.d_Certtype); + flowf(level, " Emulation Request : 0x%2.2x\n", p_certificate->d_manufacturer_certificate.d_Debugrequest); + flowf(level, " Address of Code : 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Addcode); + flowf(level, " Size of Code : %ld bytes\n", p_certificate->d_manufacturer_certificate.d_Codesize); + flowf(level, " Entry Point Address: 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_CodeStartAdd); + + flowf(level, " Manufacturer Public Key:\n"); + flowf(level, " Public Modulus:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.d_Manpubkey.a_Modulus[d_i]); + } + flowf(level, "\n"); + flowf(level, " Public Modulus Length:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Manpubkey.d_ModulusLength); + flowf(level, " Public Exponent:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Manpubkey.d_Exponent); + + flowf(level, " Originator Public Key:\n"); + flowf(level, " Public Modulus:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.d_Origpubkey.a_Modulus[d_i]); + } + flowf(level, "\n"); + flowf(level, " Public Modulus Length:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Origpubkey.d_ModulusLength); + flowf(level, " Public Exponent:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_manufacturer_certificate.d_Origpubkey.d_Exponent); + + flowf(level, " Originator Public Key Signature:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSASIGLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.a_Origpubkeysig[d_i]); + } + flowf(level, "\n"); + + flowf(level, " Software Signature:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSASIGLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.a_Swsig[d_i]); + } + flowf(level, "\n"); + + flowf(level, " Configuration Parameters:\n"); + flowf(level, " CONF_CS5 register: %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_conf_cs5); + flowf(level, " EXWS_CS5 register: %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_exws_cs5); + flowf(level, " EX_CTRL register : %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_ex_ctrl); + flowf(level, " CS image request : %4.4x\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_cs_img_req); + flowf(level, " Flash size : %ld bytes\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_flash_size); + flowf(level, " Granularity : %ld words\n",p_certificate->d_manufacturer_certificate.d_Confparam.d_granularity); + + flowf(level, " Die Id: "); + for(d_i = 0; d_i < (C_DIE_ID_SIZE); d_i++) { + flowf(level, "0x%8.8lx ", p_certificate->d_manufacturer_certificate.a_die_id[d_i]); + } + flowf(level, "\n"); + + if (p_certificate->d_manufacturer_certificate.d_Certsize > (sizeof(T_MANUFACTURER_CERTIFICATE) + (C_WORD32LGB * C_MANUF_SIG_SIZE))) { + flowf(level, " Platform Data:"); + d_j = d_max; + for(d_i = 0; d_i < (p_certificate->d_manufacturer_certificate.d_Certsize - sizeof(T_MANUFACTURER_CERTIFICATE) - (C_WORD32LGB * C_MANUF_SIG_SIZE)) / sizeof(UWORD32); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->a_platform_data[d_i]); + } + flowf(level, "\n"); + } + + flowf(level, " Certificate Signature:"); + d_j = d_max; + for (d_i = 0; d_i < (C_MANUF_SIG_SIZE); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->a_Certsig[d_i]); + } + flowf(level, "\n"); +} /* f_print_certificate_platform_data() */ + +void f_print_certificate(int level, T_MANUFACTURER_CERTIFICATE_FLASH_PROGRAMMER *p_certificate) { + UWORD16 d_i, d_j, d_max = 4; + + flowf(level, "\nFlash Programmer Manufacturer Certificate:\n"); + flowf(level, "------------------------------------------\n"); + + flowf(level, " Size of Certificate: %d bytes\n", p_certificate->d_Certsize); + flowf(level, " Type of Certificate: 0x%2.2x\n", p_certificate->d_Certtype); + flowf(level, " Emulation Request : 0x%2.2x\n", p_certificate->d_Debugrequest); + flowf(level, " Address of Code : 0x%8.8lx\n", p_certificate->d_Addcode); + flowf(level, " Size of Code : %ld bytes\n", p_certificate->d_Codesize); + flowf(level, " Entry Point Address: 0x%8.8lx\n", p_certificate->d_CodeStartAdd); + + flowf(level, " Manufacturer Public key:\n"); + flowf(level, " Public Modulus:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->d_Manpubkey.a_Modulus[d_i]); + } + flowf(level, "\n"); + flowf(level, " Public Modulus Length:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_Manpubkey.d_ModulusLength); + flowf(level, " Public Exponent:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_Manpubkey.d_Exponent); + + flowf(level, " Originator Public key:\n"); + flowf(level, " Public Modulus:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSAKEYLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->d_Origpubkey.a_Modulus[d_i]); + } + flowf(level, "\n"); + flowf(level, " Public Modulus Length:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_Origpubkey.d_ModulusLength); + flowf(level, " Public Exponent:\n"); + flowf(level, " 0x%8.8lx\n", p_certificate->d_Origpubkey.d_Exponent); + + flowf(level, " Originator Public Key Signature:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSASIGLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->a_Origpubkeysig[d_i]); + } + flowf(level, "\n"); + + flowf(level, " Software Signature:"); + d_j = d_max; + for (d_i = 0; d_i < (C_RSASIGLG); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->a_Swsig[d_i]); + } + flowf(level, "\n"); + + flowf(level, " Configuration Parameters:\n"); + flowf(level, " CONF_CS5 register: %4.4x\n",p_certificate->d_Confparam.d_conf_cs5); + flowf(level, " EXWS_CS5 register: %4.4x\n",p_certificate->d_Confparam.d_exws_cs5); + flowf(level, " EX_CTRL register : %4.4x\n",p_certificate->d_Confparam.d_ex_ctrl); + flowf(level, " CS image request : %4.4x\n",p_certificate->d_Confparam.d_cs_img_req); + flowf(level, " Flash size : %ld bytes\n",p_certificate->d_Confparam.d_flash_size); + flowf(level, " Granularity : %ld words\n",p_certificate->d_Confparam.d_granularity); + + flowf(level, " Die Id: "); + for(d_i = 0; d_i < (C_DIE_ID_SIZE); d_i++) { + flowf(level, "0x%8.8lx ", p_certificate->a_die_id[d_i]); + } + flowf(level, "\n"); + + flowf(level, " Certificate Signature:"); + d_j = d_max; + for (d_i = 0; d_i < (C_MANUF_SIG_SIZE); d_i++) { + if (d_j++ == d_max) { + flowf(level, "\n "); + d_j = 1; + } + flowf(level, "0x%8.8lx ", p_certificate->a_Certsig[d_i]); + } + flowf(level, "\n"); +} /* f_print_certificate() */ + +void f_print_parameter_nack_status(int level, UWORD8 d_parameter_nack_sts) { + flowf(level, "\nParameter NAck Response Status:\n"); + flowf(level, " 0x%2.2x", d_parameter_nack_sts); + + switch(d_parameter_nack_sts) { + case 0x01 : { flowf(level, " Incorrect baud rate\n"); break; } + case 0x02 : { flowf(level, " Incorrect certificate\n"); break; } + case 0x03 : { flowf(level, " Incorrect code address\n"); break; } + } + + flowf(level, "\n"); +} /* f_print_parameter_nack_status() */ + +void f_print_write_nack_status(int level, UWORD8 d_write_nack_sts) { + flowf(level, "\nWrite NAck Response Status:\n"); + flowf(level, " 0x%2.2x", d_write_nack_sts); + + switch(d_write_nack_sts) { + case 0x01 : { flowf(level, " Incorrect block address\n"); break; } + case 0x02 : { flowf(level, " Non-64 bytes block size\n"); break; } + case 0x03 : { flowf(level, " First block is not code address\n"); break; } + case 0x04 : { flowf(level, " Error in firmware signature check\n"); break; } + case 0x05 : { flowf(level, " Received code size does not match the code size in certificate\n"); break; } + case 0x06 : { flowf(level, " Error during block hashing\n"); break; } + } + + flowf(level, "\n"); +} /* f_print_write_nack_status() */ + +UWORD8 f_convert_uart_baud_rate(UWORD32 d_baud_rate) { + UWORD8 d_converted_baud_rate; + + switch (d_baud_rate) { + case 0: + case 812: + case 812500: { + d_converted_baud_rate = 0x00; + arg_uart_baudrate_during_cmd_download = 812500; + break; + } + case 1: + case 406: + case 406250: { + d_converted_baud_rate = 0x01; + arg_uart_baudrate_during_cmd_download = 406250; + break; + } + case 2: + case 203: + case 203125: { + d_converted_baud_rate = 0x02; + arg_uart_baudrate_during_cmd_download = 203125; + break; + } + case 3: + case 115: + case 115200: { + d_converted_baud_rate = 0x03; + arg_uart_baudrate_during_cmd_download = 115200; + break; + } + case 4: + case 57: + case 57600: { + d_converted_baud_rate = 0x04; + arg_uart_baudrate_during_cmd_download = 57600; + break; + } + case 5: + case 38: + case 38400: { + d_converted_baud_rate = 0x05; + arg_uart_baudrate_during_cmd_download = 38400; + break; + } + case 6: + case 28: + case 28800: { + d_converted_baud_rate = 0x06; + arg_uart_baudrate_during_cmd_download = 28800; + break; + } + case 7: + case 19: + case 19200: { + d_converted_baud_rate = 0x07; + arg_uart_baudrate_during_cmd_download = 19200; + break; + } + default: { + d_converted_baud_rate = 0x03; + arg_uart_baudrate_during_cmd_download = 115200; + break; + } + } + + return d_converted_baud_rate; +} /* f_convert_uart_baud_rate() */ + +void target_imei_protection(void) +{ + uint8 a_imeisv[C_IMEISV_BYTES]; + uint8 a_platform_cert_addr[C_PLATFORM_CERT_ADDR_BYTES]; + uint8 d_i, d_digit, d_temp; + uint8 d_error; + + flowf(NORMAL, "\nIMEI protection: "); + + if (arg_dry_run) { + flowf(NORMAL, "(dry-run) ok\n"); + return; + } + + target_putchar(PROTO_IMEI_PROTECT); + + // Send IMEI-SV + for (d_i = 0; d_i < C_IMEISV_DIGITS; d_i++) { + sscanf(arg_imeisv++, "%1d", &d_digit); + if (!(d_i & 1)) + d_temp = d_digit << 4; + else { + d_temp |= d_digit; + a_imeisv[d_i / 2] = d_temp; + } + } + + target_send(a_imeisv, C_IMEISV_BYTES); + target_expect_char(PROTO_READY, TARGET_RECV_DELAY); + + // Send platform certificate address + for (d_i = 0; d_i < C_PLATFORM_CERT_ADDR_DIGITS; d_i++) { + sscanf(arg_platform_certificate_addr++, "%1x", &d_digit); + if (!(d_i & 1)) + d_temp = d_digit << 4; + else { + d_temp |= d_digit; + a_platform_cert_addr[d_i / 2] = d_temp; + } + } + + target_send(a_platform_cert_addr, C_PLATFORM_CERT_ADDR_BYTES); + target_expect_char(PROTO_READY, TARGET_RECV_DELAY); + + // Check if address range is erased + if (target_wait(1, TARGET_RECV_DELAY) < 1) + main_fatal(E_RECV_TIMEOUT); + + d_error = target_getchar(); + if (d_error == PROTO_ERROR_VERIFY) { + flowf(NORMAL, "\n The address range for platform certificate and IMEI-SV is not erased.\n"); + main_fatal(E_FLASH_VERIFY); + } + + if (d_error != PROTO_READY) + error_proto(d_error, PROTO_READY); + + // Verify that the binding service call in target has succeeded + if (target_wait(1, TARGET_RECV_DELAY) < 1) + main_fatal(E_RECV_TIMEOUT); + + d_error = target_getchar(); + if (d_error == PROTO_ERROR_ROM_SSERVICE) { + flowf(NORMAL, "\n The Run-Time Loader (Binding) Service failed.\n"); + main_fatal(E_ROM_SSERVICE); + } + + if (d_error != PROTO_FLASH_START) + error_proto(d_error, PROTO_FLASH_START); + + // Receive acknowledgement for programming the platform certificate + if (target_wait(1, TARGET_RECV_DELAY) < 1) + main_fatal(E_RECV_TIMEOUT); + + d_error = target_getchar(); + if (d_error != PROTO_PROGRAM) { + flowf(NORMAL, "\n Platform certificate was not stored correctly in flash.\n"); + error_proto(d_error, PROTO_PROGRAM); + } + + // Receive acknowledgement for programming the IMEI-SV + if (target_wait(1, TARGET_RECV_DELAY) < 1) + main_fatal(E_RECV_TIMEOUT); + + d_error = target_getchar(); + if (d_error != PROTO_PROGRAM) { + flowf(NORMAL, "\n IMEI-SV was not stored correctly in flash.\n"); + error_proto(d_error, PROTO_PROGRAM); + } + + flowf(NORMAL, "Platform certificate and IMEI-SV stored in flash.\n"); +} + +// End Secure Calypso Plus + +/****************************************************************************** + * Flash Detect + ******************************************************************************/ + +void flash_detect_machine(void) +{ + uint8 data[12]; + uint16 m0, d0, m1, d1, d1ex1, d1ex2; + + flowf(NORMAL, "Flash Detect: "); + target_trace_enable(0); + + if (arg_device_id0 != -1 || arg_device_id1 != -1) { + // Device auto-detection is disabled + m0 = m1 = arg_device_id0; + d0 = d1 = arg_device_id1; + flowf(VERBOSE, "(ID override) "); + } + else { + target_putchar(PROTO_DETECT); + if (target_wait(12, TARGET_RECV_DELAY) < 12) + main_fatal(E_RECV_TIMEOUT); + + target_recv(data, 12); + m0 = data[0] + (data[1] << 8); // Intel manufacturer id + d0 = data[2] + (data[3] << 8); // Intel device id + m1 = data[4] + (data[5] << 8); // AMD manufacturer id + d1 = data[6] + (data[7] << 8); // AMD device di + d1ex1 = data[8] + (data[9] << 8); // AMD extended device id + d1ex2 = data[10] + (data[11] << 8); // AMD extended device id + } + + // Lookup multi-id device + if ((m1 == MANUFACT_AMD || m1 == MANUFACT_FUJITSU) && d1 == 0x227E) { + flowf(NORMAL, "Multi-id device detected: (0x%04X, 0x%04X, 0x%04X)\n" + , d1, d1ex1, d1ex2); + d1 = (d1ex1 << 8) | (d1ex2 & 0xFF); + flowf(DEBUG, "Multi-id converted to: 0x%04X\n", d1); + + if ((device = device_lookup_by_id(m1, d1)) == NULL) { + flowf(NORMAL, "Id not found, lookup default multi-id conf.\n"); + // Backward compatible/keep default multi-id configuration + device = device_lookup_by_id(m1, 0x227E); + } + } + + // Lookup device with AMD and Intel ids + else if ((device = device_lookup_by_id(m1, d1)) == NULL) + device = device_lookup_by_id(m0, d0); + + if (device == NULL || arg_verbose >= DEBUG) { + flowf(NORMAL, "(0x%02X, 0x%04X, 0x%02X, 0x%04X) ", m0, d0, m1, d1); + if (device == NULL) + main_fatal(E_FLASH_UNKNOWN); + } + + if (arg_verbose >= NORMAL) { + device_print(device, 's'); + flowf(NORMAL, " ok\n"); + } + + // Note that device_id is only 0x227E if the Multi-id configuration not + // is found in device.txt (else device_id will be the 'converted' id) + if (arg_device_id0 == -1 && arg_device_id1 == -1 && + (device->manufacturer_id == MANUFACT_FUJITSU || + device->manufacturer_id == MANUFACT_AMD ) + && device->device_id == 0x227E) { + flowf(NORMAL, "Warning: Extended device codes are supported when detecting flash devices,\n"); + flowf(NORMAL, " but the detected multi-id configuration is not found in device.txt. \n"); + flowf(NORMAL, " Update the device.txt manually if the default flash device is invalid.\n"); + flowf(NORMAL, " Currently, %s %s is used as default.\n\n", + manufacturer_name_lookup_by_id(device->manufacturer_id), device->name); + } +} + + +/****************************************************************************** + * Method Load + ******************************************************************************/ + +void method_download_machine(void) +{ + char sendbuf[1+2+4]; + int error; + + target_program_size = file_read_method(target_program, + TARGET_PROGRAM_SIZE_MAX, + device); + + flowf(BLABBER, "Method Download: "); + target_trace_enable(0); + + // Send 'Download' command header. Then wait for acknowledgement. + buf_put1(&sendbuf[0], PROTO_DOWNLOAD); + buf_put4(&sendbuf[1], target[target_type].method_load_addr); + buf_put2(&sendbuf[1+4], (uint16) target_program_size / 2); + target_send(sendbuf, 1+4+2); + flowf(BLABBER, "(0x%X", target[target_type].method_load_addr); + if ((error = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0) + main_fatal(error); + + // Send data. Then wait for acknowledgement. + target_trace_enable(1); + target_send(target_program, target_program_size); + flowf(BLABBER, ", %d) ", target_program_size); + if ((error = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0) + main_fatal(error); + + flowf(BLABBER, "ok\n"); +} + + +/****************************************************************************** + * Flash Checksum + ******************************************************************************/ + +int time_checksum; + +#define CHECKSUMS 8 + +void flash_checksum_machine(void) +{ + unsigned char sendbuf[1+1+4+4*CHECKSUMS], *buf; + uint32 addr, cksum, mycksum; + uint16 word; + uint8 data[4]; + int index = 0, chunks, n, i, j; + int line = -1; + struct { + uint32 index; + uint32 addr; + uint32 mycksum; + } block[CHECKSUMS]; + + chunks = image_map_count_used_chunks(); + flowf(NORMAL, "Checksumming (%d * %dkB = %dkB): ", + chunks, image_chunk_size / 1024, + chunks * image_chunk_size / 1024); + target_trace_enable(0); + + time_checksum = stopwatch_start(); + while (chunks > 0) + { + n = (chunks > CHECKSUMS ? CHECKSUMS : chunks); + + buf = sendbuf; + buf += buf_put1(buf, PROTO_CHECKSUM); + buf += buf_put1(buf, (unsigned char) n); + buf += buf_put4(buf, image_chunk_size); + + for (i = 0; i < n; i++) { + // Find next used entry in image_map + while (image_map[index] != 'x' && index < image_map_size) + index++; + + if (index == image_map_size) + break; + + addr = index * image_chunk_size; + block[i].index = index; + block[i].addr = addr; + + buf += buf_put4(buf, addr); + + index++; + } + target_send(sendbuf, buf - sendbuf); + + // Compute checksums while we wait for reply + for (i = 0; i < n; i++) { + index = block[i].index; + addr = block[i].addr; + for (j = 0, mycksum = 0; j < image_chunk_size; j += 2) { + word = (image[addr + j + 1] << 8) | image[addr + j + 0]; + mycksum += word * ((addr + j) & 0xFFFF); + } + block[i].mycksum = mycksum; + } + + if (target_wait(4 * n, TARGET_RECV_LONG_DELAY) <= 0) + main_fatal(E_RECV_TIMEOUT); + + for (i = 0; i < n; i++) { + target_recv(data, 4); + cksum = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); + mycksum = block[i].mycksum; + index = block[i].index; + + if (cksum == mycksum) + image_map[index] = 'c'; + + if (arg_checksum_show) { + // This is a far from perfect dump of checksums... We don't know + // the exact block number of each checksum in a line. + if (line != (int) index / 4) { + flowf(NORMAL, "\n%4d:", index); + line = index / 4; + } + if (cksum == mycksum) + flowf(NORMAL, "%08X ", cksum); + else + flowf(NORMAL, "%08X/%08X ", cksum, mycksum); + + } + else if (arg_verbose >= VERBOSE) { + flowf(VERBOSE, "%c", (image_map[index] == 'c' ? 'c' : '.')); + } + } + index++; + chunks -= n; + } + time_checksum = (stopwatch_stop(time_checksum) + 50) / 100; + flowf(BLABBER, " (%d.%ds)", time_checksum / 10, time_checksum % 10); + flowf(NORMAL, " ok\n"); +} + + +/****************************************************************************** + * Flash Program + ******************************************************************************/ + +int time_program; +int programs_recv, programs_send, erasures; +int index, src_size; +uint32 dst; +char *src; + +void flash_erase_machine(void); +int flash_operation_wait(int delay); +int flash_program_next(void); + +void flash_program_machine(void) +{ + uint8 cksum, sendbuf[1+4+4]; + int i, te, tp, tt; + int expected, chunks, total_size, sectors; + int image_chunk_size_old = image_chunk_size; + char ch; + + programs_recv = programs_send = erasures = 0; + index = 0; + + // Prepare erase + erase_list_size = sectors = sector_map_init(); + i = image_map_count_used_chunks(); + + if (arg_skip_erase) + erase_list_size = 0; + + time_compute(device, erase_list_size, + i * image_chunk_size, i, &te, &tp, &tt); + + flowf(BLABBER, "Estimated time (uncompressed) "); + if (te + tp > tt) + flowf(BLABBER, "(erase + program = total): %ds + %ds = %ds\n", + te/1000, tp/1000, te/1000 + tp/1000); + else + flowf(BLABBER, "transfer: %ds\n", tt/1000); + + chunks = image_map_count_used_chunks(); + flowf(NORMAL, "Program: (%d sectors, %d*%dk=%dk) ", + sectors, + chunks, image_chunk_size / 1024, + chunks * image_chunk_size / 1024); + target_trace_enable(1); + + // Expected total number of acknowledgements + expected = erase_list_size + chunks; + + if (arg_dry_run) { + flowf(NORMAL, "(dry-run) "); + if (arg_dry_run == 2) + image_chunk_size = 0; + } + + if (arg_skip_erase) + flowf(NORMAL, "(skip erase) "); + + // Start erase + flash_erase_machine(); + + if (arg_dry_run == 0 || arg_dry_run == 2) + progress_begin(expected); + + // Start the flash state machine in the target + target_putchar(PROTO_FLASH_START); + target_putchar(arg_compress ? PROTO_COMPRESS : 0); + ch = flash_operation_wait(TARGET_RECV_DELAY); + if (ch != PROTO_READY) + error_proto(ch, PROTO_READY); + if (arg_compress) { + compress_init(); + total_size = 0; + // Worst case "compressed" buffer is 9/8 of original data size. + if ((src = malloc(9 * image_chunk_size / 8)) == NULL) + main_fatal(E_MEMORY); + } + + time_program = stopwatch_start(); + + if (flash_program_next() && *arg_erase_override == 0) { + //Nothing to program + target_putchar(PROTO_FLASH_END); + if ((ch = target_expect_char(PROTO_FLASH_END, TARGET_RECV_DELAY)) < 0) + main_fatal(ch); + } + else { + while (programs_recv + erasures < expected) { + total_size += src_size; + + if (arg_progress == 'x') + flowf(NORMAL, "(0x%06X, %d) ", dst, src_size); + + if (src_size > 0 && + (arg_dry_run == 0 || arg_dry_run == 2)) + { + buf_put1(&sendbuf[0], PROTO_PROGRAM); + buf_put4(&sendbuf[1], src_size); + buf_put4(&sendbuf[1+4], dst); + target_send(sendbuf, 1+4+4); + + for (i = 0, cksum = 0; i < src_size; i++) + cksum += src[i]; + cksum = (0x100 - cksum) & 0xFF; + + // Wait for acknowledgement of command start + //while ((ch = flash_operation_wait(4000)) != PROTO_READY) + // ; + + // Make sure that the target FIFO not is full + while (programs_send - programs_recv >= target_fifo_size) + flash_operation_wait(4000); + + // Send data bytes and checksum. + target_send(src, src_size); + target_putchar(cksum); + programs_send++; + + if (arg_verbose >= DEBUG) + progress_update_simple('T'); + } + + // We compress and send next block while sending + // current block to target + if (arg_dry_run == 0 || arg_dry_run == 2) { + if (flash_program_next()) { + // Wait for remaining acknowledgements + flowf(DEBUG, "W%d", expected - (programs_recv + erasures)); + while (programs_recv + erasures < expected) + flash_operation_wait(4000); + target_putchar(PROTO_FLASH_END); + if ((ch = target_expect_char(PROTO_FLASH_END, TARGET_RECV_DELAY)) < 0) + main_fatal(ch); + break; + } + else { + // Wait for acknowledgement of data transfer + while ((ch = flash_operation_wait(4000)) != PROTO_READY) + ; + } + } + else { + if (flash_program_next()) + break; + } + } + } + + if (arg_dry_run == 0 || arg_dry_run == 2) + progress_end(programs_recv + erasures); + flowf(NORMAL, "ok\n"); + + time_program = stopwatch_stop(time_program); + flowf(VERBOSE, "Used time: "); + if (arg_compress && chunks) { + image_chunk_size = image_chunk_size_old; + flowf(VERBOSE, "(compressed to %d%%) ", + 100 * total_size / (chunks * image_chunk_size)); + } + flowf(VERBOSE, "%.1fs ok\n", (double) time_program / 1000); +} + + +/****************************************************************************** + * Flash Program sub functions + ******************************************************************************/ + +void flash_erase_machine(void) +{ + uint8 sendbuf[1+2+4*256]; // NOTEME: static limit! + int i; + char ch; + + if (arg_dry_run) + erase_list_size = 0; + + if (erase_list_size > 0) { + buf_put1(&sendbuf[0], PROTO_ERASE); + buf_put2(&sendbuf[1], (uint16) erase_list_size); + for (i = 0; i < erase_list_size; i++) + buf_put4(&sendbuf[1+2+4*i], erase_list[i]); + target_send(sendbuf, 1+2+4*erase_list_size); + + if ((ch = target_expect_char(PROTO_READY, TARGET_RECV_DELAY)) < 0) + main_fatal(ch); + } +} + +// Setup address and size of next block to transfer. Also compress the +// block. Return non-zero if this was the last block. Otherwise return zero. +int flash_program_next(void) +{ + int oldindex; + + // Find next used entry in image_map + while (index < image_map_size && image_map[index] != 'x') + index++; + + oldindex = index; + + if (index < image_map_size) { + dst = index * image_chunk_size; + if (arg_compress) { + src_size = compress(src, &image[dst], image_chunk_size); + } + else { + src = &image[dst]; + src_size = image_chunk_size; + } + index++; + } + else { + dst = 0xFFFFFFFF; + src_size = 0; + } + + return (oldindex >= image_map_size); +} + +int flash_operation_wait(int delay) +{ + int n; + char ch; + + tr(TrMachines, "fow() "); + + if ((n = target_wait(1, delay)) < 1) + main_fatal(E_RECV_TIMEOUT); + + // Minor optimization to avoid waiting for every char... + while (n--) { + ch = target_getchar(); + + if (ch == PROTO_READY) { + if (arg_verbose >= DEBUG) + progress_update_simple('R'); + if (programs_send - programs_recv >= target_fifo_size) + progress_update_simple('W'); // Wait target FIFO is full + break; + } + + switch (ch) { + case PROTO_READY: + break; // just return the char + case PROTO_PROGRAM: + programs_recv++; + progress_update_simple('P'); + progress_update(programs_recv + erasures); + break; // just return the char + case PROTO_ERASE: + erasures++; + progress_update_simple('E'); + progress_update(programs_recv + erasures); + if (arg_progress == 'x') + flowf(NORMAL, "E "); + break; // just return the char + case PROTO_FLASH_END: + progress_update_simple('Z'); + break; + case PROTO_ERROR_CKSUM: + main_fatal(E_SEND_CHECKSUM); + case PROTO_ERROR_MEMORY: + main_fatal(E_MEMORY); + case PROTO_ERROR_VERIFY: + main_fatal(E_FLASH_VERIFY); + case PROTO_ERROR_FLASH_TIMEOUT: + main_fatal(E_FLASH_TIMEOUT); + case PROTO_ERROR_FLASH_COMMAND: + main_fatal(E_FLASH_COMMAND); + case PROTO_ERROR_FLASH_VPP: + main_fatal(E_FLASH_VPPRANGE); + case PROTO_ERROR_FLASH_LOCKED: + main_fatal(E_FLASH_LOCKED); + case PROTO_ERROR_FLASH_UNKNOWN: + main_fatal(E_FLASH_ERROR); + case PROTO_ERROR_INVALID: + main_fatal(E_INVALID); + case PROTO_ERROR_FIFO_OVERFLOW: + main_fatal(E_FIFO_OVERFLOW); + case PROTO_ERROR: + default: + flowf(NORMAL, "flash_operation_wait() got unexpected char '%c' 0x%02X (%d chars waiting)\n", (' ' <= ch && ch < 127 ? ch : '.'), ch, n); +// main_fatal(E_PROTO_ERROR); + } + } + + return ch; +} + +void target_timers_show(void) +{ + int data[8], i; + struct { + double erase; + double program; + double recvonly; + double recv; + double comm; + double setup; + double overhead; + double dezip; + double erase_sector; + double program_word; + } timer; + double tmp, total, resolution = (double) (16 * 32 * 1000 / 13e6); + + // Use only 14 MHz for D-/E-Sample specific rates (not for e.g. 812.5K). + if ((arg_uart_baudrate == 230400) || (arg_uart_baudrate == 460800) || (arg_uart_baudrate == 921600)) + resolution = resolution * 13 / 14.746; + + flowf(NORMAL, + "Target Timers:\n" + " (erase + program + recvonly + recv + comm + setup + overhead + dezip)\n"); + + if (arg_dry_run) + for (i = 0; i < 8; i++) data[i] = 0; + else { + target_putchar(PROTO_TIMERS); + if (target_wait(sizeof(data), TARGET_RECV_DELAY) <= 0) + main_fatal(E_RECV_TIMEOUT); + target_recv(&data, sizeof(data)); + } + + // Convert all timers to milliseconds + timer.erase = resolution * data[0]; + timer.program = resolution * data[1]; + timer.recvonly = resolution * data[2]; + timer.recv = resolution * data[3]; + timer.comm = resolution * data[4]; + timer.setup = resolution * data[5]; + timer.overhead = resolution * data[6]; + timer.dezip = resolution * data[7]; + + // Because we do erase-while-transfer in the target the reported erase + // time has some inherent tolerance, especially at low baudrates. It + // might make sense to adjust the sector erase time by half the chunk + // transfer time, although this is just another approximation!? + +// total = timer.erase + timer.program + timer.recvonly + timer.recv + timer.comm + timer.setup; + flowf(NORMAL, "%8.0f + %7.0f + %8.0f + %6.0f + %5.0f + %5.0f + %8.0f + %5.0f\n", + timer.erase, timer.program, timer.recvonly, timer.recv, + timer.comm, timer.setup, timer.overhead, timer.dezip); + + flowf(DEBUG, "resolution = %f, %d, %d, %d, %d, %d, %d, %d, %d\n", + resolution, data[0], data[1], data[2], data[3], data[4], + data[5], data[6], data[7]); + + if (arg_timers_extended_show) { + int chunks, size_total, not_zero = 1; + flowf(NORMAL, "Target Timers Extended:\n"); + + if (erase_list_size == 0 || arg_dry_run) + flowf(NORMAL, " Erase time = 0.0ms/sector\n"); + else { + timer.erase_sector = timer.erase / erase_list_size / 1000; + flowf(NORMAL, " Erase time = %.1fs / %d sectors = %.0fms/sector\n", + timer.erase / 1000, erase_list_size, timer.erase_sector * 1000); + } + + chunks = image_map_count_used_chunks(); + size_total = chunks * image_chunk_size; + if (size_total == 0 || arg_dry_run) { + size_total = 1; + not_zero = 0; + flowf(NORMAL, " Program time = 0.0us/word = 0ms/MB\n"); + } + else { + timer.program_word = timer.program / 1000 / (size_total / 2); + flowf(NORMAL, " Program time = %.1fs / %dkwords = %.2fus/word = %.0fms/MB\n", + timer.program / 1000, size_total / 1024 / 2, + timer.program_word * 1000000, + timer.program_word * 1000 * 512 * 1024); + } + + flowf(NORMAL, " Receive-only time = %.1fs = %.0fms/MB\n", + timer.recvonly / 1000, + not_zero * timer.recvonly * 1024 * 1024 / size_total); + + flowf(NORMAL, " Overhead time = %.1fs = %.0fms/MB\n", + timer.overhead / 1000, + not_zero * timer.overhead * 1024 * 1024 / size_total); + + flowf(NORMAL, " Setup time = %.1fs = %.0fms/MB\n", + timer.setup / 1000, + not_zero * timer.setup * 1024 * 1024 / size_total); + + total = timer.erase + timer.program + timer.recvonly + timer.overhead + + timer.setup; + flowf(NORMAL, + "Total time: (erase + prog + recvonly + overhead + setup) = %.1fs\n", + total / 1000); + + flowf(NORMAL, " Receive time = %.1fs = %.0fms/MB\n", + timer.recv / 1000, timer.recv * 1024 * 1024 / size_total); + + flowf(NORMAL, " Communication time = %.1fs = %.0fms/MB\n", + timer.comm / 1000, timer.comm * 1024 * 1024 / size_total); + + flowf(NORMAL, " Dezip time = %.1fs = %.0fms/MB\n", + timer.dezip / 1000, timer.dezip * 1024 * 1024 / size_total); + + // Now compute the overall performance. Note that the theoretical + // limit compuation is somewhat flawed in that it assumes 64kB + // sector sizes. + + tmp = (double) not_zero * total / 1000 * 1024 * 1024 / size_total; + flowf(NORMAL, " Performance = %.0fs/MB\n", tmp); +#if 0 + flowf(BLABBER, " (%.1f * min possible, %.1f * theoretical limit)", + time_program / (timer.program + timer.erase), + tmp / (16 * timer.erase_sector + 512 * 1024 * timer.program_word)); + flowf(NORMAL, "\n"); +#endif + } +} + + +/****************************************************************************** + * Flash Read + ******************************************************************************/ + +int time_read; + +void flash_read_machine(void) +{ + int i; + unsigned char sendbuf[1+4+4]; + int size, read_size, done_size = 0; + int read_size_max = image_chunk_size; + uint32 addr, index = 0; + uint8 cksum, mycksum; + + flowf(NORMAL, "Reading Flash: (%dkB) ", read_total_size / 1024); + target_trace_enable(0); + + //read_size_max = 256; + time_read = stopwatch_start(); + progress_begin(read_total_size / read_size_max); + + while (read_list[index].size > 0) + { + // Find next range to read + size = read_list[index].size; + addr = read_list[index].addr; + index++; + + while (size > 0) + { + read_size = (size > read_size_max ? read_size_max : size); + + // Read address interval is [MIN..MAX[ + // If odd MAX address was specified round up to next even + if ((read_size % 2 == 1) && (read_size < read_size_max)) read_size++; + + if (arg_progress == 'x') + flowf(DEBUG, "(0x%06X, %d) ", addr, read_size); + + if (!arg_dry_run) + { + buf_put1(&sendbuf[0], PROTO_READ); + buf_put4(&sendbuf[1] , read_size); + buf_put4(&sendbuf[1+4], addr); + target_send(sendbuf, 1+4+4); + + if (target_wait(read_size, TARGET_RECV_LONG_DELAY) <= 0) + main_fatal(E_RECV_TIMEOUT); + // Note that we wrap/mirror memory each 'image_size' bytes + target_recv(&image[addr & (image_size - 1)], read_size); + for (i = 0, mycksum = 0; i < read_size; i++) + mycksum ^= image[(addr + i) & (image_size - 1)]; + + if (target_wait(1, TARGET_RECV_DELAY) <= 0) + main_fatal(E_RECV_TIMEOUT); + cksum = target_getchar(); + + if (cksum != mycksum) + main_fatal(E_RECV_CHECKSUM); + } + done_size += read_size; + size -= read_size; + addr += read_size; + + progress_update(done_size / read_size_max); + progress_update_simple('0' + read_size / 1024); + } + } + progress_end(done_size / read_size_max); + flowf(NORMAL, " ok\n"); + + time_read = stopwatch_stop(time_read); + flowf(VERBOSE, "Used time: %ds ok\n", time_read); +} + + +/****************************************************************************** + * Flash Reset + ******************************************************************************/ + +void target_reset_machine(void) +{ + int error; + + flowf(VERBOSE, "Resetting target: "); + + if (arg_dry_run) { + flowf(VERBOSE, "(dry-run) "); + } + else { + target_putchar(PROTO_RESET); + target_expect_char(PROTO_READY, TARGET_RECV_DELAY); + } + flowf(VERBOSE, "ok\n"); +} + + +/****************************************************************************** + * Show Functions (dump internal data structures) + ******************************************************************************/ + +void image_map_show(void) +{ + uint32 addr; + int i; + + flowf(ALWAYS, "image map of %d * %dkB chunks (x = used, s = skip, c = checksum ok):", + image_size_in_chunks, image_chunk_size / 1024); + + // For each chunk of the image usage map... + for (i = 0, addr = 0; i < image_size_in_chunks; i++) + { + if ((i % 64) == 0) { + flowf(ALWAYS, "\n%4dkB: ", (int) addr >> 10); + } + putchar(image_map[i] != 0 ? image_map[i] : '.'); + addr += image_chunk_size; + } + putchar('\n'); +} + +void sector_map_show(void) +{ + struct sector_s *sectors = device->memmap->sectors; + int i; + + char n; + uint32 addr = 0; + + flowf(ALWAYS, "sector map (x = used, s = skip, X = force erase):"); + + // For each sector of the device definition... + for (i = 0; i < device->memmap->size; i++) + { + if ((addr & 0xFFFFF) == 0) { + flowf(ALWAYS, "\n%2dMB: ", (int) addr >> 20); + } + n = (sector_map[i] ? sector_map[i] : '.'); + putchar(n); + + addr += sectors[i].size; + } + putchar('\n'); +} + + +/****************************************************************************** + * Utility Functions + ******************************************************************************/ + +int image_is_within(int start, int end) +{ + return !(start < 0 || image_size <= start || + end < 0 || image_size < end || + end < start); +} + +// Set the image usage map in the range [start..end[ as used +int image_map_set(int start, int end) +{ + if (!image_is_within(start, end)) + return -1; + + do { + image_map[start / image_chunk_size] = 'x'; + start += image_chunk_size; + } while (start < end); + + return 0; +} + +int target_type_set(uint16 code) +{ + // If there is an override from the command-line, use that + if (arg_target_type != 0) + code = arg_target_type; + + switch (code) + { + case 'h': // Hercules + case 'u': // Ulysses + case '3': // Chipset 3 + case CHIP_ID_ULYSSES_0: + case CHIP_ID_ULYSSES_A: + case CHIP_ID_HERCULES_A: + case CHIP_ID_HERCULES_B: + target_type = 3; + break; + + case 'c': // Calypso + case '4': // Chipset 4 + // case 's': // Samson + case CHIP_ID_CALYPSO_A: + case CHIP_ID_CALYPSO_B: + case CHIP_ID_CALYPSO_C: + target_type = 4; + break; + + case 'l': // Calypso Lite + case CHIP_ID_CALYPSO_L: + target_type = 5; + break; + + case 'p': // Calypso Plus + case CHIP_ID_CALYPSO_PLUS: + case CHIP_ID_CALYPSO_PLUS_A: + target_type = 6; + break; + + default: + target_type = 0; + } + + return target_type; +} + +int image_map_count_used_chunks(void) +{ + int used, i; + + // For each image chunk + for (i = 0, used = 0; i < image_size_in_chunks; i++) { + if (image_map[i] == 'x') + used++; + } + return used; +} + +// When the sector_map have been changed, we have to update the image_map +// such that we don't attempt to program within sectors that are not going +// to be programmed anyway (due to the erase override). We also have to +// program chunks contained in sectors that *are* going to be erased, even +// though the chunks have been check-summed ok. +int image_map_update(void) +{ + struct sector_s *sectors = device->memmap->sectors; + int changed, chunks, i, j; + int map_index; + + map_index = 0; + changed = 0; + + // For each sector of the device definition... + for (i = 0; i < device->memmap->size; i++) { + chunks = sectors[i].size / image_chunk_size; + // For each image--map-chunk contained in current sector... + for (j = 0; j < chunks; j++) { + if (!(sector_map[i] == 'x' || sector_map[i] == 'X') && + image_map[map_index + j] == 'x') { + image_map[map_index + j] = 's'; // skip + changed++; + } + else if ((sector_map[i] == 'x' || sector_map[i] == 'X') + && image_map[map_index + j] == 'c') { + image_map[map_index + j] = 'x'; // include! + changed++; + } + } + map_index += chunks; + } + return changed; +} + +int sector_map_init(void) +{ + struct sector_s *sectors = device->memmap->sectors; + int map_index; + int used_list_index; + int used, chunks, i, j; + + sector_map_size = SECTOR_MAP_SIZE_MAX; + + memset(sector_map, 0, sector_map_size); + + // Generate the sector_map from the image_map + map_index = 0; + + // For each sector of the device definition... + for (i = 0; i < device->memmap->size; i++) { + + chunks = sectors[i].size / image_chunk_size; + + // For each image-usage-map-chunk contained in current sector... + for (j = 0, used = 0; j < chunks; j++) + if (image_map[map_index + j] == 'x') + used++; + + sector_map[i] = (used ? 'x' : 0); + map_index += chunks; + } + + parse_arg_erase_override(arg_erase_override); + + // Generate the erase_list + // For each sector of the device definition... + used_list_index = 0; + for (i = 0; i < device->memmap->size; i++) { + if (sector_map[i] == 'x' || sector_map[i] == 'X') + erase_list[used_list_index++] = sectors[i].addr; + } + + if (arg_sector_map_show) + sector_map_show(); + + if (arg_sector_list_show) { + flowf(ALWAYS, "sector used list: "); + for (i = 0; i < used_list_index; i++) + flowf(ALWAYS, "0x%06X ", erase_list[i]); + putchar('\n'); + } + + return used_list_index; +} + +int parse_range(char *p, char **p_end, int *n1, int *n2) +{ + char *my_end; + int read_offset_calp = (*arg_read != 0 && target[target_type].type == 'P'); + + *n2 = 0; + + *n1 = strtol(p, &my_end, 0); + if (p == my_end) + return 0; // error: no chars converted + + if (*my_end == 'k' || *my_end == 'K') { + *n1 <<= 10; + if (read_offset_calp) *n1 += CALP_OFFSET; + my_end++; + } + else if (*my_end == 'M') { + *n1 <<= 20; + if (read_offset_calp) *n1 += CALP_OFFSET; + my_end++; + } + + *p_end = my_end; + if (my_end[0] != '.' || my_end[1] != '.') + return 1; + p = my_end + 2; + + *n2 = strtol(p, &my_end, 0); + if (p == my_end) + return 0; // error: no chars converted + + if (*my_end == 'k' || *my_end == 'K') { + *n2 <<= 10; + if (read_offset_calp) *n2 += CALP_OFFSET; + my_end++; + } + else if (*my_end == 'M') { + *n2 <<= 20; + if (read_offset_calp) *n2 += CALP_OFFSET; + my_end++; + } + + *p_end = my_end; + return 2; +} + +// Parse and decode the erase override command line option string. The +// sector_map is updated accordingly. +void parse_arg_erase_override(char *p) +{ + struct sector_s *sectors = device->memmap->sectors; + int sector_bottom, sector_top; + int changed, i; + + tr(TrBegin| TrArgParser, "erase_override:\n"); + while (*p) + { + char *p_end; + int num, sign, n1 = 0, n2 = 0; + + if (*p != '-' && *p != '+') + main_error(E_ERASE_SPEC); + + sign = (*p == '-' ? -1 : +1); + p++; + + if (*p == '*') { + sign = sign * 2; + p++; + } + else { + num = parse_range(p, &p_end, &n1, &n2); + if (num == 0) + main_error(E_ERASE_SPEC); + p = p_end; + } + + switch (sign) { + case -1: + case +1: + if (num == 1) { + // Support for absolute addresses on Calypso Plus + if (n1 >= CALP_OFFSET) n1 -= CALP_OFFSET; + + if (n1 > sector_map_size) { + // For all sectors... + for (i = 0; i < device->memmap->size; i++) { + sector_bottom = sectors[i].addr; + sector_top = sector_bottom + sectors[i].size - 1; + + if (n1 >= sector_bottom && n1 < sector_top) { + sector_map[i] = (sign > 0 ? 'X' : 's'); + break; + } + } + } + else + sector_map[n1] = (sign > 0 ? 'X' : 's'); + tr(TrArgParser, "%c%d\n", sign > 0 ? '+' : '-', n1); + } + else { + // Support for absolute addresses on Calypso Plus + if (n1 >= CALP_OFFSET && n2 >= CALP_OFFSET) { + n1 -= CALP_OFFSET; + n2 -= CALP_OFFSET; + } + + if (n1 > sector_map_size || n2 > sector_map_size) { + // n1 and n2 must respresent an address range + if (n1 >= n2) + main_error(E_ERASE_SPEC); + + // For all sectors... + for (i = 0; i < device->memmap->size; i++) + { + sector_bottom = sectors[i].addr; + sector_top = sector_bottom + sectors[i].size - 1; + + // If either sector bottom or top is contained in + // [n1..n2[ + if ((n1 <= sector_bottom && sector_bottom < n2) || + (n1 <= sector_top && sector_top < n2)) { + sector_map[i] = (sign > 0 ? 'X' : 's'); + } + } + } + else { + // n1 and n2 must respresent a sector range + for (i = n1; i < n2; i++) + sector_map[i] = (sign > 0 ? 'X' : 's'); + } + tr(TrArgParser, + "%c%d..%d\n", sign > 0 ? '+' : '-', n1, n2); + } + break; + case -2: + case +2: + // Fill whole sector_map... + for (i = 0; i < device->memmap->size; i++) + sector_map[i] = (sign > 0 ? 'X' : 's'); + tr(TrArgParser, "%c*\n", sign > 0 ? '+' : '-'); + break; + } + // skip optional comma + if (*p == ',') + p++; + } + tr(TrEnd| TrArgParser, ""); + + changed = image_map_update(); + + // If image map was changed, we trace it again. + if (changed && arg_image_map_show) + image_map_show(); +} + +void parse_arg_read(char *p) +{ + read_total_size = 0; + read_list_size = 0; + + tr(TrArgParser, "parse_arg_read() {\n"); + while (*p) + { + char *p_end; + int num, n1, n2; + + if (*p == '*') { + n1 = 0; + n2 = 0x1000000; // sufficiently large (16MB) + p++; + } + else { + num = parse_range(p, &p_end, &n1, &n2); + tr(TrArgParser, "parse_range('%s', ...)\n" + " { #%d, p_end = '%s', n1 = 0x%x, n2 = 0x%x } %d\n", + p, read_list_size, p_end, n1, n2, num); + if (num != 2 || n1 > n2) + main_error(E_ADDR_RANGE); + p = p_end; + } + read_list[read_list_size].addr = n1; + read_list[read_list_size].size = n2 - n1; + read_total_size += n2 - n1; + read_list_size++; + if (read_list_size >= READ_LIST_SIZE_MAX) + main_error(E_ARG_TOOMANY); + + if (*p == 0) + break; + + if (*p == ',') + p++; + else + main_error(E_READ_SPEC); + } + // Terminate the read_list with zeroes. + read_list[read_list_size].addr = 0; + read_list[read_list_size].size = 0; + + tr(TrArgParser, "}\n"); +} + +void parse_arg_write(char *p) +{ + tr(TrArgParser, "parse_arg_write() {\n"); + + while (*p) + { + char bytes[1024]; + char *p_end; + int num, n1, n2; + int index, value, size, i; + + num = parse_range(p, &p_end, &n1, &n2); + tr(TrArgParser, "parse_range('%s', ...)\n" + " { p_end = '%s', n1 = 0x%x, n2 = 0x%x } %d\n", + p, p_end, n1, n2, num); + + if (num < 1 || num > 2) + main_error(E_ADDR_RANGE); + + p = p_end; + + if (*p++ != '=') + main_error(E_WRITE_SPEC); + + tr(TrArgParser, " { "); + if (*p == 0) + main_error(E_WRITE_SPEC); + + index = 0; + while (*p) { + if (*p == '\"') { + // Collect text string + p++; + tr(TrCont|TrArgParser, "'"); + while (*p) { + if (*p == '\"') + break; + if (p[0] == '\\' && p[1] == '\"') + p++; // skip leading backslash + if (p[0] == '\\' && p[1] == '\\') + p++; // skip leading backslash + tr(TrCont|TrArgParser, "%c", *p); + bytes[index] = *p++; + if (index++ >= sizeof(bytes)) + main_error(E_WRITE_SPEC); + } + if (*p++ != '\"') + main_error(E_WRITE_SPEC); + tr(TrCont|TrArgParser, "' "); + } + else { + // Collect byte string + value = strtol(p, &p_end, 0); + if (p == p_end) + main_error(E_WRITE_SPEC); + if (value < 0 || 255 < value) + main_error(E_WRITE_SPEC); + bytes[index] = value; + if (index++ >= sizeof(bytes)) + main_error(E_WRITE_SPEC); + p = p_end; + + tr(TrCont|TrArgParser, "0x%x ", value); + } + if (*p == ':' || *p == 0) + break; + if (*p == ',') + p++; + } + size = index; + tr(TrArgParser, "} %d\n", size); + + if (num == 1) { + // Support for absolute addresses on Calypso Plus + if (n1 >= CALP_OFFSET) n1 -= CALP_OFFSET; + + n2 = n1 + size; + } + else if (n1 >= CALP_OFFSET && n2 >= CALP_OFFSET) { + n1 -= CALP_OFFSET; + n2 -= CALP_OFFSET; + } + + if (image_map_set(n1, n2) < 0) + main_error(E_ADDR_RANGE); + + index = 0; + for (i = n1; i < n2; i++) { + image[i] = bytes[index++]; + if (index >= size) + index = 0; + } + if (*p == ':') + p++; + } + tr(TrArgParser, "}\n"); +}