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;
+}