add pflash
diff --git a/objects/pflash/pflash.c b/objects/pflash/pflash.c
new file mode 100644
index 0000000..031ccb2
--- /dev/null
+++ b/objects/pflash/pflash.c
@@ -0,0 +1,793 @@
+#include <stdio.h>

+#include <stdlib.h>

+#include <string.h>

+#include <fcntl.h>

+#include <sys/mman.h>

+#include <sys/types.h>

+#include <sys/stat.h>

+#include <unistd.h>

+#include <byteswap.h>

+#include <stdint.h>

+#include <stdbool.h>

+#include <getopt.h>

+#include <limits.h>

+#include <arpa/inet.h>

+#include <assert.h>

+

+#include <libflash/libflash.h>

+#include <libflash/libffs.h>

+#include "progress.h"

+#include "io.h"

+#include "ast.h"

+#include "sfc-ctrl.h"

+

+#define __aligned(x)			__attribute__((aligned(x)))

+

+#define PFLASH_VERSION	"0.8.6"

+

+static bool must_confirm = false;

+static bool dummy_run;

+static bool need_relock;

+static bool bmc_flash;

+#ifdef __powerpc__

+static bool using_sfc;

+#endif

+

+#define FILE_BUF_SIZE	0x10000

+static uint8_t file_buf[FILE_BUF_SIZE] __aligned(0x1000);

+

+static struct spi_flash_ctrl	*fl_ctrl;

+static struct flash_chip	*fl_chip;

+static struct ffs_handle	*ffsh;

+static uint32_t			fl_total_size, fl_erase_granule;

+static const char		*fl_name;

+static int32_t			ffs_index = -1;

+

+static void check_confirm(void)

+{

+	char yes[8], *p;

+

+	if (!must_confirm)

+		return;

+

+	printf("WARNING ! This will modify your %s flash chip content !\n",

+	       bmc_flash ? "BMC" : "HOST");

+	printf("Enter \"yes\" to confirm:");

+	memset(yes, 0, sizeof(yes));

+	if (!fgets(yes, 7, stdin))

+		exit(1);

+	p = strchr(yes, 10);

+	if (p)

+		*p = 0;

+	p = strchr(yes, 13);

+	if (p)

+		*p = 0;

+	if (strcmp(yes, "yes")) {

+		printf("Operation cancelled !\n");

+		exit(1);

+	}

+	must_confirm = false;

+}

+

+static void print_flash_info(void)

+{

+	uint32_t i;

+	int rc;

+

+	printf("Flash info:\n");

+	printf("-----------\n");

+	printf("Name          = %s\n", fl_name);

+	printf("Total size    = %dMB \n", fl_total_size >> 20);

+	printf("Erase granule = %dKB \n", fl_erase_granule >> 10);

+

+	if (bmc_flash)

+		return;

+

+	if (!ffsh) {

+		rc = ffs_open_flash(fl_chip, 0, 0, &ffsh);

+		if (rc) {

+			fprintf(stderr, "Error %d opening ffs !\n", rc);

+			ffsh = NULL;

+		}

+	}

+	if (!ffsh)

+		return;

+

+	printf("\n");

+	printf("Partitions:\n");

+	printf("-----------\n");

+

+	for (i = 0;; i++) {

+		uint32_t start, size, act, end;

+		char *name;

+

+		rc = ffs_part_info(ffsh, i, &name, &start, &size, &act);

+		if (rc == FFS_ERR_PART_NOT_FOUND)

+			break;

+		if (rc) {

+			fprintf(stderr, "Error %d scanning partitions\n", rc);

+			break;

+		}

+		end = start + size;

+		printf("ID=%02d %15s %08x..%08x (actual=%08x)\n",

+		       i, name, start, end, act);

+		free(name);

+	}

+}

+

+static void lookup_partition(const char *name)

+{

+	uint32_t index;

+	int rc;

+

+	/* Open libffs if needed */

+	if (!ffsh) {

+		rc = ffs_open_flash(fl_chip, 0, 0, &ffsh);

+		if (rc) {

+			fprintf(stderr, "Error %d opening ffs !\n", rc);

+			exit(1);

+		}

+	}

+

+	/* Find partition */

+	rc = ffs_lookup_part(ffsh, name, &index);

+	if (rc == FFS_ERR_PART_NOT_FOUND) {

+		fprintf(stderr, "Partition '%s' not found !\n", name);

+		exit(1);

+	}

+	if (rc) {

+		fprintf(stderr, "Error %d looking for partition '%s' !\n",

+			rc, name);

+		exit(1);

+	}

+	ffs_index = index;

+}

+

+static void erase_chip(void)

+{

+	int rc;

+

+	printf("About to erase chip !\n");

+	check_confirm();

+

+	printf("Erasing... (may take a while !) ");

+	fflush(stdout);

+

+	if (dummy_run) {

+		printf("skipped (dummy)\n");

+		return;

+	}

+

+	rc = flash_erase_chip(fl_chip);

+	if (rc) {

+		fprintf(stderr, "Error %d erasing chip\n", rc);

+		exit(1);

+	}

+

+	printf("done !\n");

+}

+

+static void erase_range(uint32_t start, uint32_t size, bool will_program)

+{

+	uint32_t done = 0;

+	int rc;

+

+	printf("About to erase 0x%08x..0x%08x !\n", start, start + size);

+	check_confirm();

+

+	if (dummy_run) {

+		printf("skipped (dummy)\n");

+		return;

+	}

+

+	printf("Erasing...\n");

+	progress_init(size >> 8);

+	while(size) {

+		/* If aligned to 64k and at least 64k, use 64k erase */

+		if ((start & 0xffff) == 0 && size >= 0x10000) {

+			rc = flash_erase(fl_chip, start, 0x10000);

+			if (rc) {

+				fprintf(stderr, "Error %d erasing 0x%08x\n",

+					rc, start);

+				exit(1);

+			}

+			start += 0x10000;

+			size -= 0x10000;

+			done += 0x10000;

+		} else {

+			rc = flash_erase(fl_chip, start, 0x1000);

+			if (rc) {

+				fprintf(stderr, "Error %d erasing 0x%08x\n",

+					rc, start);

+				exit(1);

+			}

+			start += 0x1000;

+			size -= 0x1000;

+			done += 0x1000;

+		}

+		progress_tick(done >> 8);

+	}

+	progress_end();

+

+	/* If this is a flash partition, mark it empty if we aren't

+	 * going to program over it as well

+	 */

+	if (ffsh && ffs_index >= 0 && !will_program) {

+		printf("Updating actual size in partition header...\n");

+		ffs_update_act_size(ffsh, ffs_index, 0);

+	}

+}

+

+static void program_file(const char *file, uint32_t start, uint32_t size)

+{

+	int fd, rc;

+	ssize_t len;

+	uint32_t actual_size = 0;

+

+	fd = open(file, O_RDONLY);

+	if (fd == -1) {

+		perror("Failed to open file");

+		exit(1);

+	}

+	printf("About to program \"%s\" at 0x%08x..0x%08x !\n",

+	       file, start, size);

+	check_confirm();

+

+	if (dummy_run) {

+		printf("skipped (dummy)\n");

+		return;

+	}

+

+	printf("Programming & Verifying...\n");

+	progress_init(size >> 8);

+	while(size) {

+		len = read(fd, file_buf, FILE_BUF_SIZE);

+		if (len < 0) {

+			perror("Error reading file");

+			exit(1);

+		}

+		if (len == 0)

+			break;

+		if (len > size)

+			len = size;

+		size -= len;

+		actual_size += len;

+		rc = flash_write(fl_chip, start, file_buf, len, true);

+		if (rc) {

+			if (rc == FLASH_ERR_VERIFY_FAILURE)

+				fprintf(stderr, "Verification failed for"

+					" chunk at 0x%08x\n", start);

+			else

+				fprintf(stderr, "Flash write error %d for"

+					" chunk at 0x%08x\n", rc, start);

+			exit(1);

+		}

+		start += len;

+		progress_tick(actual_size >> 8);

+	}

+	progress_end();

+	close(fd);

+

+	/* If this is a flash partition, adjust its size */

+	if (ffsh && ffs_index >= 0) {

+		printf("Updating actual size in partition header...\n");

+		ffs_update_act_size(ffsh, ffs_index, actual_size);

+	}

+}

+

+static void do_read_file(const char *file, uint32_t start, uint32_t size)

+{

+	int fd, rc;

+	ssize_t len;

+	uint32_t done = 0;

+

+	fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 00666);

+	if (fd == -1) {

+		perror("Failed to open file");

+		exit(1);

+	}

+	printf("Reading to \"%s\" from 0x%08x..0x%08x !\n",

+	       file, start, size);

+

+	progress_init(size >> 8);

+	while(size) {

+		len = size > FILE_BUF_SIZE ? FILE_BUF_SIZE : size;

+		rc = flash_read(fl_chip, start, file_buf, len);

+		if (rc) {

+			fprintf(stderr, "Flash read error %d for"

+				" chunk at 0x%08x\n", rc, start);

+			exit(1);

+		}

+		rc = write(fd, file_buf, len);

+		if (rc < 0) {

+			perror("Error writing file");

+			exit(1);

+		}

+		start += len;

+		size -= len;

+		done += len;

+		progress_tick(done >> 8);

+	}

+	progress_end();

+	close(fd);

+}

+

+static void enable_4B_addresses(void)

+{

+	int rc;

+

+	printf("Switching to 4-bytes address mode\n");

+

+	rc = flash_force_4b_mode(fl_chip, true);

+	if (rc) {

+		fprintf(stderr, "Error %d enabling 4b mode\n", rc);

+		exit(1);

+	}

+}

+

+static void disable_4B_addresses(void)

+{

+	int rc;

+

+	printf("Switching to 3-bytes address mode\n");

+

+	rc = flash_force_4b_mode(fl_chip, false);

+	if (rc) {

+		fprintf(stderr, "Error %d disabling 4b mode\n", rc);

+		exit(1);

+	}

+}

+

+static void flash_access_cleanup_bmc(void)

+{

+	if (ffsh)

+		ffs_close(ffsh);

+	flash_exit(fl_chip);

+	ast_sf_close(fl_ctrl);

+	close_devs();

+}

+

+static void flash_access_setup_bmc(bool use_lpc, bool need_write)

+{

+	int rc;

+

+	/* Open and map devices */

+	open_devs(use_lpc, true);

+

+	/* Create the AST flash controller */

+	rc = ast_sf_open(AST_SF_TYPE_BMC, &fl_ctrl);

+	if (rc) {

+		fprintf(stderr, "Failed to open controller\n");

+		exit(1);

+	}

+

+	/* Open flash chip */

+	rc = flash_init(fl_ctrl, &fl_chip);

+	if (rc) {

+		fprintf(stderr, "Failed to open flash chip\n");

+		exit(1);

+	}

+

+	/* Setup cleanup function */

+	atexit(flash_access_cleanup_bmc);

+}

+

+static void flash_access_cleanup_pnor(void)

+{

+	/* Re-lock flash */

+	if (need_relock)

+		set_wrprotect(true);

+

+	if (ffsh)

+		ffs_close(ffsh);

+	flash_exit(fl_chip);

+#ifdef __powerpc__

+	if (using_sfc)

+		sfc_close(fl_ctrl);

+	else

+		ast_sf_close(fl_ctrl);

+#else

+	ast_sf_close(fl_ctrl);

+#endif

+	close_devs();

+}

+

+static void flash_access_setup_pnor(bool use_lpc, bool use_sfc, bool need_write)

+{

+	int rc;

+

+	/* Open and map devices */

+	open_devs(use_lpc, false);

+

+#ifdef __powerpc__

+	if (use_sfc) {

+		/* Create the SFC flash controller */

+		rc = sfc_open(&fl_ctrl);

+		if (rc) {

+			fprintf(stderr, "Failed to open controller\n");

+			exit(1);

+		}

+		using_sfc = true;

+	} else {

+#endif			

+		/* Create the AST flash controller */

+		rc = ast_sf_open(AST_SF_TYPE_PNOR, &fl_ctrl);

+		if (rc) {

+			fprintf(stderr, "Failed to open controller\n");

+			exit(1);

+		}

+#ifdef __powerpc__

+	}

+#endif

+

+	/* Open flash chip */

+	rc = flash_init(fl_ctrl, &fl_chip);

+	if (rc) {

+		fprintf(stderr, "Failed to open flash chip\n");

+		exit(1);

+	}

+

+	/* Unlock flash (PNOR only) */

+	if (need_write)

+		need_relock = set_wrprotect(false);

+

+	/* Setup cleanup function */

+	atexit(flash_access_cleanup_pnor);

+}

+

+static void print_version(void)

+{

+	printf("Palmetto Flash tool " PFLASH_VERSION "\n");

+}

+

+static void print_help(const char *pname)

+{

+	printf("Usage: %s [options] commands...\n\n", pname);

+	printf(" Options:\n");

+	printf("\t-a address, --address=address\n");

+	printf("\t\tSpecify the start address for erasing, reading\n");

+	printf("\t\tor flashing\n\n");

+	printf("\t-s size, --size=size\n");

+	printf("\t\tSpecify the size in bytes for erasing, reading\n");

+	printf("\t\tor flashing\n\n");

+	printf("\t-P part_name, --partition=part_name\n");

+	printf("\t\tSpecify the partition whose content is to be erased\n");

+	printf("\t\tprogrammed or read. This is an alternative to -a and -s\n");

+	printf("\t\tif both -P and -s are specified, the smallest of the\n");

+	printf("\t\ttwo will be used\n\n");

+	printf("\t-f, --force\n");

+	printf("\t\tDon't ask for confirmation before erasing or flashing\n\n");

+	printf("\t-d, --dummy\n");

+	printf("\t\tDon't write to flash\n\n");

+#ifdef __powerpc__

+	printf("\t-l, --lpc\n");

+	printf("\t\tUse LPC accesses instead of PCI\n\n");

+#endif

+	printf("\t-b, --bmc\n");

+	printf("\t\tTarget BMC flash instead of host flash\n\n");

+	printf(" Commands:\n");

+	printf("\t-4, --enable-4B\n");

+	printf("\t\tSwitch the flash and controller to 4-bytes address\n");

+	printf("\t\tmode (no confirmation needed).\n\n");

+	printf("\t-3, --disable-4B\n");

+	printf("\t\tSwitch the flash and controller to 3-bytes address\n");

+	printf("\t\tmode (no confirmation needed).\n\n");

+	printf("\t-r file, --read=file\n");

+	printf("\t\tRead flash content from address into file, use -s\n");

+	printf("\t\tto specify the size to read (or it will use the source\n");

+	printf("\t\tfile size if used in conjunction with -p and -s is not\n");

+	printf("\t\tspecified). When using -r together with -e or -p, the\n");

+	printf("\t\tread will be peformed first\n\n");

+	printf("\t-E, --erase-all\n");

+	printf("\t\tErase entire flash chip\n");

+	printf("\t\t(Not supported on all chips/controllers)\n\n");

+	printf("\t-e, --erase\n");

+	printf("\t\tErase the specified region. If size or address are not\n");

+	printf("\t\tspecified, but \'--program\' is used, then the file\n");

+	printf("\t\tsize will be used (rounded to an erase block) and the\n");

+	printf("\t\taddress defaults to 0.\n\n");

+	printf("\t-p file, --program=file\n");

+	printf("\t\tWill program the file to flash. If the address is not\n");

+	printf("\t\tspecified, it will use 0. If the size is not specified\n");

+	printf("\t\tit will use the file size. Otherwise it will limit to\n");

+	printf("\t\tthe specified size (whatever is smaller). If used in\n");

+	printf("\t\tconjunction with any erase command, the erase will\n");

+	printf("\t\ttake place first.\n\n");

+	printf("\t-t, --tune\n");

+	printf("\t\tJust tune the flash controller & access size\n");

+	printf("\t\t(Implicit for all other operations)\n\n");

+	printf("\t-i, --info\n");

+	printf("\t\tDisplay some information about the flash.\n\n");

+	printf("\t-h, --help\n");

+	printf("\t\tThis message.\n\n");

+}

+

+int pflash_main(int argc, char *argv[])

+{

+	const char *pname = argv[0];

+	uint32_t address = 0, read_size = 0, write_size = 0;

+	uint32_t erase_start = 0, erase_size = 0;

+	bool erase = false;

+	bool program = false, erase_all = false, info = false, do_read = false;

+	bool enable_4B = false, disable_4B = false, use_lpc = true;

+	bool show_help = false, show_version = false;

+	bool has_sfc = false, has_ast = false;

+	bool no_action = false, tune = false;

+	char *write_file = NULL, *read_file = NULL, *part_name = NULL;

+	int rc;

+

+	while(1) {

+		static struct option long_opts[] = {

+			{"address",	required_argument,	NULL,	'a'},

+			{"size",	required_argument,	NULL,	's'},

+			{"partition",	required_argument,	NULL,	'P'},

+			{"lpc",		no_argument,		NULL,	'l'},

+			{"bmc",		no_argument,		NULL,	'b'},

+			{"enable-4B",	no_argument,		NULL,	'4'},

+			{"disable-4B",	no_argument,		NULL,	'3'},

+			{"read",	required_argument,	NULL,	'r'},

+			{"erase-all",	no_argument,		NULL,	'E'},

+			{"erase",	no_argument,		NULL,	'e'},

+			{"program",	required_argument,	NULL,	'p'},

+			{"force",	no_argument,		NULL,	'f'},

+			{"info",	no_argument,		NULL,	'i'},

+			{"tune",	no_argument,		NULL,	't'},

+			{"dummy",	no_argument,		NULL,	'd'},

+			{"help",	no_argument,		NULL,	'h'},

+			{"version",	no_argument,		NULL,	'v'},

+			{"debug",	no_argument,		NULL,	'g'},

+		};

+		int c, oidx = 0;

+

+		c = getopt_long(argc, argv, "a:s:P:r:43Eep:fdihlvbtg",

+				long_opts, &oidx);

+		if (c == EOF)

+			break;

+		switch(c) {

+		case 'a':

+			address = strtoul(optarg, NULL, 0);

+			break;

+		case 's':

+			read_size = write_size = strtoul(optarg, NULL, 0);

+			break;

+		case 'P':

+			part_name = strdup(optarg);

+			break;

+		case '4':

+			enable_4B = true;

+			break;

+		case '3':

+			disable_4B = true;

+			break;

+		case 'r':

+			do_read = true;

+			read_file = strdup(optarg);

+			break;

+		case 'E':

+			erase_all = erase = true;

+			break;

+		case 'e':

+			erase = true;

+			break;

+		case 'p':

+			program = true;

+			write_file = strdup(optarg);

+			break;

+		case 'f':

+			must_confirm = false;

+			break;

+		case 'd':

+			must_confirm = false;

+			dummy_run = true;

+			break;

+		case 'i':

+			info = true;

+			break;

+		case 'l':

+			use_lpc = true;

+			break;

+		case 'b':

+			bmc_flash = true;

+			break;

+		case 't':

+			tune = true;

+			break;

+		case 'v':

+			show_version = true;

+			break;

+		case 'h':

+			show_help = show_version = true;

+			break;

+		case 'g':

+			libflash_debug = true;

+			break;

+		default:

+			exit(1);

+		}

+	}

+

+	/* Check if we need to access the flash at all (which will

+	 * also tune them as a side effect

+	 */

+	no_action = !erase && !program && !info && !do_read &&

+		!enable_4B && !disable_4B && !tune;

+

+	/* Nothing to do, if we didn't already, print usage */

+	if (no_action && !show_version)

+		show_help = show_version = true;

+

+	if (show_version)

+		print_version();

+	if (show_help)

+		print_help(pname);

+

+	if (no_action)

+		return 0;

+

+	/* --enable-4B and --disable-4B are mutually exclusive */

+	if (enable_4B && disable_4B) {

+		fprintf(stderr, "--enable-4B and --disable-4B are mutually"

+			" exclusive !\n");

+		exit(1);

+	}

+

+	/* 4B not supported on BMC flash */

+	if (enable_4B && bmc_flash) {

+		fprintf(stderr, "--enable-4B not supported on BMC flash !\n");

+		exit(1);

+	}

+

+	/* partitions not supported on BMC flash */

+	if (part_name && bmc_flash) {

+		fprintf(stderr, "--partition not supported on BMC flash !\n");

+		exit(1);

+	}

+

+	/* part-name and erase-all make no sense together */

+	if (part_name && erase_all) {

+		fprintf(stderr, "--partition and --erase-all are mutually"

+			" exclusive !\n");

+		exit(1);

+	}

+

+	/* Read command should always come with a file */

+	if (do_read && !read_file) {

+		fprintf(stderr, "Read with no file specified !\n");

+		exit(1);

+	}

+

+	/* Program command should always come with a file */

+	if (program && !write_file) {

+		fprintf(stderr, "Program with no file specified !\n");

+		exit(1);

+	}

+

+	/* If both partition and address specified, error out */

+	if (address && part_name) {

+		fprintf(stderr, "Specify partition or address, not both !\n");

+		exit(1);

+	}

+

+	/* If file specified but not size, get size from file

+	 */

+	if (write_file && !write_size) {

+		struct stat stbuf;

+

+		if (stat(write_file, &stbuf)) {

+			perror("Failed to get file size");

+			exit(1);

+		}

+		write_size = stbuf.st_size;

+	}

+

+	/* Check platform */

+	check_platform(&has_sfc, &has_ast);

+

+	/* Prepare for access */

+	if (bmc_flash) {

+		if (!has_ast) {

+			fprintf(stderr, "No BMC on this platform\n");

+			exit(1);

+		}

+		flash_access_setup_bmc(use_lpc, erase || program);

+	} else {

+		if (!has_ast && !has_sfc) {

+			fprintf(stderr, "No BMC nor SFC on this platform\n");

+			exit(1);

+		}

+		flash_access_setup_pnor(use_lpc, has_sfc, erase || program);

+	}

+

+	rc = flash_get_info(fl_chip, &fl_name,

+			    &fl_total_size, &fl_erase_granule);

+	if (rc) {

+		fprintf(stderr, "Error %d getting flash info\n", rc);

+		exit(1);

+	}

+

+	/* If -t is passed, then print a nice message */

+	if (tune)

+		printf("Flash and controller tuned\n");

+

+	/* If read specified and no read_size, use flash size */

+	if (do_read && !read_size && !part_name)

+		read_size = fl_total_size;

+

+	/* We have a partition specified, grab the details */

+	if (part_name)

+		lookup_partition(part_name);

+

+	/* We have a partition, adjust read/write size if needed */

+	if (ffsh && ffs_index >= 0) {

+		uint32_t pstart, pmaxsz, pactsize;

+		int rc;

+

+		rc = ffs_part_info(ffsh, ffs_index, NULL,

+				   &pstart, &pmaxsz, &pactsize);

+		if (rc) {

+			fprintf(stderr,"Failed to get partition info\n");

+			exit(1);

+		}

+

+		/* Read size is obtained from partition "actual" size */

+		if (!read_size)

+			read_size = pactsize;

+

+		/* Write size is max size of partition */

+		if (!write_size)

+			write_size = pmaxsz;

+

+		/* Crop write size to partition size */

+		if (write_size > pmaxsz) {

+			printf("WARNING: Size (%d bytes) larger than partition"

+			       " (%d bytes), cropping to fit\n",

+			       write_size, pmaxsz);

+			write_size = pmaxsz;

+		}

+

+		/* If erasing, check partition alignment */

+		if (erase && ((pstart | pmaxsz) & 0xfff)) {

+			fprintf(stderr,"Partition not aligned properly\n");

+			exit(1);

+		}

+

+		/* Set address */

+		address = pstart;

+	}

+

+	/* Align erase boundaries */

+	if (erase && !erase_all) {

+		uint32_t mask = 0xfff;

+		uint32_t erase_end;

+

+		/* Dummy size for erase, will be adjusted later */

+		if (!write_size)

+			write_size = 1;

+		erase_start = address & ~mask;

+		erase_end = ((address + write_size) + mask) & ~mask;

+		erase_size = erase_end - erase_start;

+

+		if (erase_start != address || erase_size != write_size)

+			fprintf(stderr, "WARNING: Erase region adjusted"

+				" to 0x%08x..0x%08x\n",

+				erase_start, erase_end);

+	}

+

+	/* Process commands */

+	if (enable_4B)

+		enable_4B_addresses();

+	if (disable_4B)

+		disable_4B_addresses();

+	if (info)

+		print_flash_info();

+	if (do_read)

+		do_read_file(read_file, address, read_size);

+	if (erase_all)

+		erase_chip();

+	else if (erase)

+		erase_range(erase_start, erase_size, program);

+	if (program)

+		program_file(write_file, address, write_size);

+

+	return 0;

+}