view loadtools/flcmplboot.c @ 809:c30c2f6a55c2

CHANGES: fc-host-tools-r15 released
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 27 Mar 2021 18:48:36 +0000
parents 730a9f919dcd
children
line wrap: on
line source

/*
 * This module contains the implementation of the flash erase-program-boot
 * hack for brickable Compal phones.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include "flash.h"

extern struct flash_bank_info flash_bank_info[2];
extern struct flash_cmdset flash_cmdset_intel;
extern struct flash_cmdset flash_cmdset_intel_w30;

extern uint32_t crc32_table[];

static int hack_enabled;
static uint32_t boot_sector_size;
static uint32_t ram_buffer_addr;

/* called from hwparam.c config file parser */
void
set_boot_reflash_hack(arg, filename_for_errs, lineno_for_errs)
	char *arg;
	char *filename_for_errs;
	int lineno_for_errs;
{
	char *cp, *np, *ep;

	if (hack_enabled) {
		fprintf(stderr,
			"%s line %d: duplicate boot-reflash-hack setting\n",
			filename_for_errs, lineno_for_errs);
		exit(1);
	}
	for (cp = arg; isspace(*cp); cp++)
		;
	if (!*cp || *cp == '#') {
too_few_arg:	fprintf(stderr,
		"%s line %d: boot-reflash-hack setting: too few arguments\n",
			filename_for_errs, lineno_for_errs);
		exit(1);
	}
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (!*cp)
		goto too_few_arg;
	*cp++ = '\0';
	ram_buffer_addr = strtoul(np, &ep, 16);
	if (*ep) {
invhex:		fprintf(stderr,
			"%s line %d: syntax error (hex arguments expected)\n",
			filename_for_errs, lineno_for_errs);
		exit(1);
	}
	while (isspace(*cp))
		cp++;
	if (!*cp || *cp == '#')
		goto too_few_arg;
	for (np = cp; *cp && !isspace(*cp); cp++)
		;
	if (*cp)
		*cp++ = '\0';
	boot_sector_size = strtoul(np, &ep, 16);
	if (*ep)
		goto invhex;
	while (isspace(*cp))
		cp++;
	if (*cp && *cp != '#') {
		fprintf(stderr,
		"%s line %d: boot-reflash-hack setting: too many arguments\n",
			filename_for_errs, lineno_for_errs);
		exit(1);
	}
	hack_enabled = 1;
}

static void
make_s3_record(buf, dest_addr, datalen)
	u_char *buf;
	uint32_t dest_addr;
{
	int totlen, i;
	u_char accum;

	buf[0] = totlen = datalen + 5;
	buf[1] = dest_addr >> 24;
	buf[2] = dest_addr >> 16;
	buf[3] = dest_addr >> 8;
	buf[4] = dest_addr;
	accum = 0;
	for (i = 0; i < totlen; i++)
		accum += buf[i];
	buf[i] = ~accum;
}

static void
make_ml_arg(rec, buf)
	u_char *rec;
	char *buf;
{
	register int i, len;
	register char *s;

	len = rec[0] + 1;
	s = buf;
	for (i = 0; i < len; i++) {
		sprintf(s, "%02X", rec[i]);
		s += 2;
	}
	*s = '\0';
}

static
op_status_filter(line)
	char *line;
{
	puts(line);
	if (strcmp(line, "Operation complete, final SR: 80"))
		return(1);
	else
		return(0);
}

flashcmd_erase_program_boot(argc, argv)
	char **argv;
{
	FILE *binf;
	struct stat filestat;
	size_t len;
	char *strtoul_endp;
	char *targv[5], longarg[513];
	char shortarg1[9], shortarg2[9], shortarg3[9];
	u_char databuf[256];
	int reclen, cc, i;
	uint32_t ramaddr, remlen, crcaccum;
	u_long crc_from_target;

	if (!hack_enabled) {
		fprintf(stderr,
			"Operation not applicable to this target device\n");
		return(-1);
	}
	if (argc < 3 || argc > 4) {
inv:		fprintf(stderr, "usage: %s %s binfile [length]\n",
			argv[0], argv[1]);
		return(-1);
	}
	if (flash_detect(0, 0) < 0)
		return(-1);
	if (flash_bank_info[0].geom->regions[0].sector_size
			!= boot_sector_size) {
		fprintf(stderr,
		"error: detected flash boot sector size differs from config\n");
		return(-1);
	}
	if (flash_bank_info[0].ops != &flash_cmdset_intel &&
	    flash_bank_info[0].ops != &flash_cmdset_intel_w30) {
		fprintf(stderr,
			"error: operation implemented for Intel flash only\n");
		return(-1);
	}

	binf = fopen(argv[2], "r");
	if (!binf) {
		perror(argv[2]);
		return(-1);
	}
	fstat(fileno(binf), &filestat);
	if (!S_ISREG(filestat.st_mode)) {
		fprintf(stderr, "%s is not a regular file\n", argv[2]);
		fclose(binf);
		return(-1);
	}
	if (argc > 3) {
		len = strtoul(argv[3], &strtoul_endp, 16);
		if (*strtoul_endp) {
			fclose(binf);
			goto inv;
		}
		if (len > filestat.st_size) {
			fprintf(stderr,
			    "error: specified length exceeds file length\n");
			fclose(binf);
			return(-1);
		}
	} else
		len = filestat.st_size;
	if (len > boot_sector_size) {
		fprintf(stderr, "error: length exceeds boot sector size\n");
		fclose(binf);
		return(-1);
	}
	if (len & 1) {
		fprintf(stderr, "error: length must be even\n");
		fclose(binf);
		return(-1);
	}

	printf("Loading new boot code into target RAM at %lx\n",
		(u_long) ram_buffer_addr);
	targv[0] = "ML";
	targv[1] = longarg;
	targv[2] = 0;
	ramaddr = ram_buffer_addr;
	crcaccum = 0xFFFFFFFF;
	for (remlen = len; remlen; remlen -= reclen) {
		if (remlen >= 250)
			reclen = 250;
		else
			reclen = remlen;
		cc = fread(databuf + 5, 1, reclen, binf);
		if (cc != reclen) {
			fclose(binf);
			fprintf(stderr, "error reading from %s\n", argv[2]);
			return(-1);
		}
		for (i = 0; i < reclen; i++)	/* update running CRC */
			crcaccum = crc32_table[crcaccum & 0xFF ^ databuf[i+5]]
				^ (crcaccum >> 8);
		make_s3_record(databuf, ramaddr, reclen);
		make_ml_arg(databuf, longarg);
		tpinterf_make_cmd(targv);
		if (tpinterf_send_cmd() < 0) {
			fclose(binf);
			return(-1);
		}
		cc = tpinterf_pass_output(1);
		if (cc) {
			fclose(binf);
			return(cc);
		}
		ramaddr += reclen;
		putchar('.');
		fflush(stdout);
	}
	putchar('\n');
	fclose(binf);

	printf("Verifying CRC-32 in target RAM\n");
	if (crc32_on_target((u_long) ram_buffer_addr, (u_long) len,
				&crc_from_target) < 0)
		return(-1);
	if (crc_from_target == crcaccum)
		printf("match (%08lX)\n", crc_from_target);
	else {
		fprintf(stderr, "error: CRC mismatch!\n");
		return(-1);
	}

	printf("Commanding flash erase+program operation on the target\n");
	sprintf(shortarg1, "%lx", (u_long) ram_buffer_addr);
	sprintf(shortarg2, "%lx", (u_long) flash_bank_info[0].base_addr);
	sprintf(shortarg3, "%lx", (u_long) len);
	targv[0] = "intel-rewrite-sector";
	targv[1] = shortarg1;
	targv[2] = shortarg2;
	targv[3] = shortarg3;
	targv[4] = 0;
	tpinterf_make_cmd(targv);
	if (tpinterf_send_cmd() < 0)
		return(-1);
	return tpinterf_capture_output(20, op_status_filter);
}