view loadtools/flamdsec.c @ 1012:11391cb6bdc0

patch from fixeria: doc change from SE K2x0 to K2xx Since their discovery in late 2022, Sony Ericsson K200 and K220 phones were collectively referred to as SE K2x0 in FreeCalypso documentation. However, now that SE K205 has been discovered as yet another member of the same family (same PCBA in different case), it makes more sense to refer to the whole family as SE K2xx.
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 23 Sep 2024 12:23:20 +0000
parents ad3041e19884
children
line wrap: on
line source

/*
 * This module is a place to implement commands and functions for
 * sector write-protection (locking and unlocking, checking current
 * lock state) on AMD-style flash chips.
 */

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

extern struct flash_bank_info flash_bank_info[2];

/*
 * Some common functions for Spansion PL-N flash: common between
 * flash lock-state retrieval and active PPB programming.
 */

static
pln_special_mode_entry(base_addr, mode_opc)
	uint32_t base_addr;
	uint16_t mode_opc;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
"unexpected response to w16 in PL-N special mode entry sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr + 0x554, 0x55))
		goto bad_w16;
	if (do_w16(base_addr + 0xAAA, mode_opc))
		goto bad_w16;
	return(0);
}

static
pln_special_mode_exit(base_addr)
	uint32_t base_addr;
{
	if (do_w16(base_addr, 0x90)) {
bad_w16:	fprintf(stderr,
"unexpected response to w16 in PL-N special mode exit sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr, 0x00))
		goto bad_w16;
	return(0);
}

/*
 * flash lock-state implementation with its helper functions.
 */

static
issue_read_id(base_addr)
	uint32_t base_addr;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in read ID cmd sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr + 0x554, 0x55))
		goto bad_w16;
	if (do_w16(base_addr + 0xAAA, 0x90))
		goto bad_w16;
	return(0);
}

static
issue_reset_cmd(base_addr)
	uint32_t base_addr;
{
	if (do_w16(base_addr + 0xAAA, 0xF0)) {
		fprintf(stderr,
	"unexpected response to w16 when resetting flash to read mode!\n");
		return(-1);
	}
	return(0);
}

static
read_mode_lock_word(base_addr, word_offset, retp)
	uint32_t base_addr, word_offset;
	uint16_t *retp;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in mode lock query sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr + 0x554, 0x55))
		goto bad_w16;
	if (do_w16(base_addr + 0xAAA, 0x60))
		goto bad_w16;
	if (do_w16(base_addr + word_offset, 0x48))
		goto bad_w16;
	if (do_r16(base_addr + word_offset, retp) < 0)
		return(-1);
	return(0);
}

flashcmd_lock_state(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;
	struct lock_group_desc *grp;
	uint32_t offset, part_addr;
	uint16_t word;
	unsigned ng, nb;

	if (argc > 2) {
		fprintf(stderr, "error: too many arguments\n");
		return(-1);
	}
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	offset = 0;
	for (ng = 0; ng < li->ngroups; ng++) {
		grp = li->groups + ng;
		if (grp->part_begin) {
			part_addr = bi->base_addr + offset;
			if (issue_read_id(part_addr) < 0)
				return(-1);
		}
		if (offset == 0 && li->have_status_word_3) {
			if (do_r16(bi->base_addr + 6, &word) < 0)
				return(-1);
			printf("Global status word 3: %04X\n", word);
		}
		if (offset == 0 && li->have_status_word_7) {
			if (do_r16(bi->base_addr + 0xE, &word) < 0)
				return(-1);
			printf("Global status word 7: %04X\n", word);
		}
		for (nb = 0; nb < grp->nblocks; nb++) {
			if (do_r16(bi->base_addr + offset + 4, &word) < 0)
				return(-1);
			printf("Sector%s at 0x%X: %s\n",
				grp->is_group ? " group" : "", offset,
				(word & 1) ? "locked" : "unlocked");
			offset += grp->block_size;
		}
		if (grp->part_end) {
			if (issue_reset_cmd(part_addr) < 0)
				return(-1);
		}
	}
	if (li->have_mode_lock_bits) {
		if (read_mode_lock_word(bi->base_addr, 0x14, &word) < 0)
			return(-1);
		printf("Password Protection Mode lock: %04X\n", word);
		if (issue_reset_cmd(bi->base_addr) < 0)
			return(-1);
		if (read_mode_lock_word(bi->base_addr, 0x24, &word) < 0)
			return(-1);
		printf("Persistent Protection Mode lock: %04X\n", word);
		if (issue_reset_cmd(bi->base_addr) < 0)
			return(-1);
	}
	if (li->have_pln_lock_reg) {
		if (pln_special_mode_entry(bi->base_addr, 0x40) < 0)
			return(-1);
		if (do_r16(bi->base_addr, &word) < 0)
			return(-1);
		printf("PL-N Lock Register: %04X\n", word);
		if (pln_special_mode_exit(bi->base_addr) < 0)
			return(-1);
	}
	return(0);
}

/*
 * Here comes a version of the above lock-state checking code,
 * modified for use from within ppb-program-all and ppb-erase-all
 * functions for chips that don't do this work internally.
 */

static
int_lock_state_check(bi, sought_state)
	struct flash_bank_info *bi;
{
	struct amd_lock_info *li = bi->amd_lock;
	struct lock_group_desc *grp;
	uint32_t offset, part_addr;
	uint16_t word;
	unsigned ng, nb;
	int lock_state;

	offset = 0;
	for (ng = 0; ng < li->ngroups; ng++) {
		grp = li->groups + ng;
		if (grp->part_begin) {
			part_addr = bi->base_addr + offset;
			if (issue_read_id(part_addr) < 0)
				return(-1);
		}
		for (nb = 0; nb < grp->nblocks; nb++) {
			if (do_r16(bi->base_addr + offset + 4, &word) < 0)
				return(-1);
			lock_state = word & 1;
			if (lock_state != sought_state)
				break;
			offset += grp->block_size;
		}
		if (lock_state != sought_state) {
			if (issue_reset_cmd(part_addr) < 0)
				return(-1);
			return(0);
		}
		if (grp->part_end) {
			if (issue_reset_cmd(part_addr) < 0)
				return(-1);
		}
	}
	return(1);
}

/*
 * Spansion PL-J PPB write functions, referenced from lock_info structures
 * in fldevs.c device descriptions.
 */

static
plj_ppb_write_op(base_addr, is_erase, retp)
	uint32_t base_addr;
	uint16_t *retp;
{
	if (do_w16(base_addr + 0xAAA, 0xAA)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in PPB command sequence - aborting\n");
		return(-1);
	}
	if (do_w16(base_addr + 0x554, 0x55))
		goto bad_w16;
	if (do_w16(base_addr + 0xAAA, 0x60))
		goto bad_w16;
	if (do_w16(base_addr + 4, is_erase ? 0x60 : 0x68))
		goto bad_w16;
	usleep(1200);			/* per S29PL-J datasheet */
	if (do_w16(base_addr + 4, is_erase ? 0x40 : 0x48))
		goto bad_w16;
	if (do_r16(base_addr + 4, retp) < 0)
		return(-1);
	return(0);
}

plj_ppb_program_one(bi, sector_addr)
	struct flash_bank_info *bi;
	uint32_t sector_addr;
{
	uint16_t stat;
	unsigned pulsecnt;
	int rc;

	for (pulsecnt = 0; pulsecnt < 25; ) {
		rc = plj_ppb_write_op(bi->base_addr + sector_addr, 0, &stat);
		if (rc < 0)
			return(rc);
		pulsecnt++;
		if (!(stat & 1))
			continue;
		printf("PPB 0x%X programmed with %u pulse%s\n", sector_addr,
			pulsecnt, pulsecnt > 1 ? "s" : "");
		return amd_reset_cmd(bi);
	}
	fprintf(stderr, "PPB 0x%X programming FAILED, tried %u pulses\n",
		sector_addr, pulsecnt);
	return(-1);
}

plj_ppb_program_all(bi)
	struct flash_bank_info *bi;
{
	struct amd_lock_info *li = bi->amd_lock;
	struct lock_group_desc *grp;
	uint32_t offset;
	unsigned ng, nb;
	int rc;

	offset = 0;
	for (ng = 0; ng < li->ngroups; ng++) {
		grp = li->groups + ng;
		for (nb = 0; nb < grp->nblocks; nb++) {
			rc = plj_ppb_program_one(bi, offset);
			if (rc < 0)
				return(rc);
			offset += grp->block_size;
		}
	}
	printf("Verifying PPB programming\n");
	rc = int_lock_state_check(bi, 1);
	if (rc < 0)
		return(rc);
	if (rc)
		return(0);
	fprintf(stderr, "flash error: one or more PPBs failed to program\n");
	return(-1);
}

static
plj_ppb_erase_cycle(bi)
	struct flash_bank_info *bi;
{
	uint16_t stat;
	unsigned pulsecnt;
	int rc;

	printf("Performing PPB erase cycle\n");
	for (pulsecnt = 0; pulsecnt < 1000; ) {
		rc = plj_ppb_write_op(bi->base_addr, 1, &stat);
		if (rc < 0)
			return(rc);
		pulsecnt++;
		if (stat & 1)
			continue;
		printf("PPB erase cycle succeeded after %u pulse%s\n",
			pulsecnt, pulsecnt > 1 ? "s" : "");
		return amd_reset_cmd(bi);
	}
	fprintf(stderr, "PPB erase cycle FAILED, tried %u pulses\n", pulsecnt);
	return(-1);
}

plj_ppb_erase_all_single(bi, raw_mode)
	struct flash_bank_info *bi;
{
	unsigned pulsecnt;
	uint16_t stat;
	int rc;

	if (raw_mode)
		return plj_ppb_erase_cycle(bi);
	printf("Programming all PPBs before erase cycle\n");
	rc = plj_ppb_program_all(bi);
	if (rc < 0)
		return(-1);
	printf("Entering PPB erase and verify loop\n");
	for (pulsecnt = 0; ; ) {
		if (pulsecnt >= 1000) {
			fprintf(stderr,
		"flash error: unable to complete PPB erase after %u pulses\n",
				pulsecnt);
			return(-1);
		}
		rc = plj_ppb_write_op(bi->base_addr, 1, &stat);
		if (rc < 0)
			return(rc);
		pulsecnt++;
		if (stat & 1)
			continue;
		putchar('.');
		fflush(stdout);
		rc = amd_reset_cmd(bi);
		if (rc < 0)
			return(rc);
		rc = int_lock_state_check(bi, 0);
		if (rc < 0)
			return(rc);
		if (rc)
			break;
	}
	printf("\nPPB erase complete, total pulses: %u\n", pulsecnt);
	return(0);
}

plj_ppb_erase_all_dualbank(dummy_bi, raw_mode)
	void *dummy_bi;
{
	unsigned pulsecnt;
	uint16_t stat;
	int rc;

	if (flash_detect(0, 0) < 0)
		return(-1);
	if (flash_detect(1, 0) < 0)
		return(-1);
	if (flash_bank_info[0].device != flash_bank_info[1].device) {
		fprintf(stderr, "error: mismatch between two flash banks\n");
		return(-1);
	}
	if (raw_mode)
		return plj_ppb_erase_cycle(&flash_bank_info[0]);
	printf("Programming all PPBs in flash bank 0\n");
	rc = plj_ppb_program_all(&flash_bank_info[0]);
	if (rc < 0)
		return(-1);
	printf("Programming all PPBs in flash bank 1\n");
	rc = plj_ppb_program_all(&flash_bank_info[1]);
	if (rc < 0)
		return(-1);
	printf("Entering PPB erase and verify loop\n");
	for (pulsecnt = 0; ; ) {
		if (pulsecnt >= 1000) {
			fprintf(stderr,
		"flash error: unable to complete PPB erase after %u pulses\n",
				pulsecnt);
			return(-1);
		}
		rc = plj_ppb_write_op(flash_bank_info[0].base_addr, 1, &stat);
		if (rc < 0)
			return(rc);
		pulsecnt++;
		if (stat & 1)
			continue;
		putchar('.');
		fflush(stdout);
		rc = amd_reset_cmd(&flash_bank_info[0]);
		if (rc < 0)
			return(rc);
		rc = int_lock_state_check(&flash_bank_info[0], 0);
		if (rc < 0)
			return(rc);
		if (!rc)
			continue;
		rc = int_lock_state_check(&flash_bank_info[1], 0);
		if (rc < 0)
			return(rc);
		if (rc)
			break;
	}
	printf("\nPPB erase complete, total pulses: %u\n", pulsecnt);
	return(0);
}

/*
 * Spansion PL-N PPB write functions, referenced from lock_info structures
 * in fldevs.c device descriptions.
 */

static
pln_ppb_write_op(oper_addr, write1, write2, expect_stat)
	uint32_t oper_addr;
	uint16_t write1, write2, expect_stat;
{
	int rc;
	uint16_t read_stat, prev_stat;
	time_t start_time, curtime;

	rc = pln_special_mode_entry(oper_addr, 0xC0);
	if (rc < 0)
		return(rc);
	if (do_w16(oper_addr, write1)) {
bad_w16:	fprintf(stderr,
	"unexpected response to w16 in PPB command sequence - aborting\n");
		return(-1);
	}
	if (do_w16(oper_addr, write2))
		goto bad_w16;
	printf("Polling for completion status\n");
	usleep(10000);	/* make sure we don't get state before op starts */
	start_time = time(0);
	rc = do_r16(oper_addr, &read_stat);
	if (rc < 0)
		return(rc);
	for (;;) {
		prev_stat = read_stat;
		rc = do_r16(oper_addr, &read_stat);
		if (rc < 0)
			return(rc);
		if (read_stat == expect_stat && prev_stat == expect_stat)
			break;
		curtime = time(0);
		if (curtime >= start_time + 10) {
			fprintf(stderr, "operation timeout, aborting\n");
			return(-1);
		}
	}
	printf("Operation completed successfully\n");
	return pln_special_mode_exit(oper_addr);
}

pln_ppb_program_one(bi, sector_addr)
	struct flash_bank_info *bi;
	uint32_t sector_addr;
{
	printf("Issuing PPB Program command\n");
	return pln_ppb_write_op(bi->base_addr + sector_addr, 0xA0, 0, 0);
}

pln_ppb_erase_all(bi, raw_mode)
	struct flash_bank_info *bi;
{
	printf("Issuing All PPB Erase command\n");
	return pln_ppb_write_op(bi->base_addr, 0x80, 0x30, 1);
}

/*
 * Front end functions for PPB operation commands.
 */

flashcmd_ppb_program(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;
	u_long offset_arg;
	struct sector_info *sp;
	char *strtoul_endp;

	if (argc != 3) {
inv:		fprintf(stderr, "usage: %s %s sector-offset\n",
			argv[0], argv[1]);
		return(-1);
	}
	offset_arg = strtoul(argv[2], &strtoul_endp, 16);
	if (*strtoul_endp)
		goto inv;
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li || !li->ppb_program_one) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	if (offset_arg >= bi->geom->total_size) {
		fprintf(stderr,
		"error: specified offset exceeds flash bank size (0x%lx)\n",
			(u_long) bi->geom->total_size);
		return(-1);
	}
	if (get_flash_sector_table(bi) < 0)
		return(-1);
	for (sp = bi->sectors; sp->size; sp++)
		if (sp->start == offset_arg)
			break;
	if (!sp->size) {
		fprintf(stderr,
	"error: specified offset not aligned to a flash sector boundary\n");
		return(-1);
	}
	return li->ppb_program_one(bi, sp->start);
}

flashcmd_ppb_program_all(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;

	if (argc > 2) {
		fprintf(stderr, "error: too many arguments\n");
		return(-1);
	}
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li || !li->ppb_program_all) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	return li->ppb_program_all(bi);
}

flashcmd_ppb_erase_all(argc, argv, bank)
	char **argv;
{
	struct flash_bank_info *bi;
	struct amd_lock_info *li;
	int raw_mode;

	switch (argc) {
	case 2:
		raw_mode = 0;
		break;
	case 3:
		if (!strcmp(argv[2], "raw")) {
			raw_mode = 1;
			break;
		}
		/* FALL THRU */
	default:
		fprintf(stderr, "usage: %s %s [raw]\n", argv[0], argv[1]);
		return(-1);
	}
	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	li = bi->amd_lock;
	if (!li || !li->ppb_erase_all) {
		fprintf(stderr,
			"Operation not supported for this flash chip type\n");
		return(-1);
	}
	return li->ppb_erase_all(bi, raw_mode);
}