From 5cca01f3943d888f9ee5f1efcf9faa0269bf8533 Mon Sep 17 00:00:00 2001 From: Carl-Daniel Hailfinger Date: Tue, 24 Nov 2009 00:20:03 +0000 Subject: Cleanly validate ICH SPI preopcodes The code should work on Linux/*BSD/MacOSX and relies on the serial code implementation in serial.c. Support for additional platforms (Windows) will have to be added to serial.c for this to work. For tests without a Bus Pirate (or with non-functional serial code) it is possible to #define FAKE_COMMUNICATION in buspirate_spi.c. Thanks to Sean Nelson for the SPI mode settings code. I tweaked it a bit to make configuration from a commandline easier should anybody want that feature. Tested-by: Sean Nelson Corresponding to flashrom svn r772. Signed-off-by: Carl-Daniel Hailfinger Acked-by: Stefan Reinauer Acked-by: Sean Nelson --- Makefile | 15 ++- buspirate_spi.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ flash.h | 12 ++ flashrom.c | 19 +++ spi.c | 9 ++ spi.h | 1 + 6 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 buspirate_spi.c diff --git a/Makefile b/Makefile index e12b7acc..430c8fd9 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ INSTALL = install DIFF = diff PREFIX ?= /usr/local MANDIR ?= $(PREFIX)/share/man -CFLAGS ?= -Os -Wall -Werror +CFLAGS ?= -Os -Wall -Werror -g EXPORTDIR ?= . OS_ARCH = $(shell uname) @@ -89,6 +89,9 @@ CONFIG_DUMMY ?= yes # Always enable Dr. Kaiser for now. CONFIG_DRKAISER ?= yes +# Always enable Bus Pirate SPI for now. +CONFIG_BUSPIRATESPI ?= yes + # Disable wiki printing by default. It is only useful if you have wiki access. CONFIG_PRINT_WIKI ?= no @@ -138,8 +141,18 @@ FEATURE_CFLAGS += -D'DRKAISER_SUPPORT=1' OBJS += drkaiser.o endif +ifeq ($(CONFIG_BUSPIRATESPI), yes) +FEATURE_CFLAGS += -D'BUSPIRATE_SPI_SUPPORT=1' +OBJS += buspirate_spi.o +endif + +# Ugly, but there's no elif/elseif. ifeq ($(CONFIG_SERPROG), yes) OBJS += serial.o +else +ifeq ($(CONFIG_BUSPIRATESPI), yes) +OBJS += serial.o +endif endif ifeq ($(CONFIG_PRINT_WIKI), yes) diff --git a/buspirate_spi.c b/buspirate_spi.c new file mode 100644 index 00000000..259e4343 --- /dev/null +++ b/buspirate_spi.c @@ -0,0 +1,358 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2009 Carl-Daniel Hailfinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "flash.h" +#include "spi.h" + +/* Change this to #define if you want to test without a serial implementation */ +#undef FAKE_COMMUNICATION + +#ifndef FAKE_COMMUNICATION +int buspirate_serialport_setup(char *dev) +{ + /* 115200bps, 8 databits, no parity, 1 stopbit */ + sp_fd = sp_openserport(dev, 115200); + return 0; +} + +int buspirate_serialport_shutdown(void) +{ + close(sp_fd); + return 0; +} + +int serialport_write(unsigned char *buf, unsigned int writecnt) +{ + int tmp = 0; + + while (tmp != writecnt) { + tmp = write(sp_fd, buf + tmp, writecnt - tmp); + if (tmp == -1) + return 1; + if (!tmp) + printf_debug("Empty write\n"); + } + + return 0; +} + +int serialport_read(unsigned char *buf, unsigned int readcnt) +{ + int tmp = 0; + + while (tmp != readcnt) { + tmp = read(sp_fd, buf + tmp, readcnt - tmp); + if (tmp == -1) + return 1; + if (!tmp) + printf_debug("Empty read\n"); + } + + return 0; +} + +int buspirate_discard_read(void) +{ + int flags; + + printf_debug("%s\n", __func__); + flags = fcntl(sp_fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(sp_fd, F_SETFL, flags); + sp_flush_incoming(); + flags &= ~O_NONBLOCK; + fcntl(sp_fd, F_SETFL, flags); + + return 0; +} +#else +#define buspirate_serialport_setup(...) 0 +#define buspirate_serialport_shutdown(...) 0 +#define serialport_write(...) 0 +#define serialport_read(...) 0 +#define buspirate_discard_read(...) 0 +#endif + +int buspirate_sendrecv(unsigned char *buf, unsigned int writecnt, unsigned int readcnt) +{ + int i, ret = 0; + + printf_debug("%s: write %i, read %i\n", __func__, writecnt, readcnt); + if (!writecnt && !readcnt) { + fprintf(stderr, "Zero length command!\n"); + return 1; + } + printf_debug("Sending"); + for (i = 0; i < writecnt; i++) + printf_debug(" 0x%02x", buf[i]); +#ifdef FAKE_COMMUNICATION + /* Placate the caller for now. */ + if (readcnt) { + buf[0] = 0x01; + memset(buf + 1, 0xff, readcnt - 1); + } + ret = 0; +#else + if (writecnt) + ret = serialport_write(buf, writecnt); + if (ret) + return ret; + if (readcnt) + ret = serialport_read(buf, readcnt); + if (ret) + return ret; +#endif + printf_debug(", receiving"); + for (i = 0; i < readcnt; i++) + printf_debug(" 0x%02x", buf[i]); + printf_debug("\n"); + return 0; +} + +int buspirate_spi_init(void) +{ + unsigned char buf[512]; + int ret = 0; + int i; + char *devpos = NULL; + char *dev = NULL; + int devlen; + + if (programmer_param && !strlen(programmer_param)) { + free(programmer_param); + programmer_param = NULL; + } + if (programmer_param) { + devpos = strstr(programmer_param, "dev="); + if (devpos) { + devpos += 4; + devlen = strcspn(devpos, ",:"); + if (devlen) { + dev = malloc(devlen + 1); + if (!dev) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + strncpy(dev, devpos, devlen); + dev[devlen] = '\0'; + } + } + free(programmer_param); + programmer_param = NULL; + } + if (!dev) { + fprintf(stderr, "No serial device given. Use flashrom -p " + "buspiratespi:dev=/dev/ttyUSB0\n"); + return 1; + } + + ret = buspirate_serialport_setup(dev); + if (ret) + return ret; + + /* This is the brute force version, but it should work. */ + for (i = 0; i < 19; i++) { + /* Enter raw bitbang mode */ + buf[0] = 0x00; + /* Send the command, don't read the response. */ + ret = buspirate_sendrecv(buf, 1, 0); + if (ret) + return ret; + /* Read any response and discard it. */ + ret = buspirate_discard_read(); + if (ret) + return ret; + } + /* Enter raw bitbang mode */ + buf[0] = 0x00; + ret = buspirate_sendrecv(buf, 1, 5); + if (ret) + return ret; + if (memcmp(buf, "BBIO", 4)) { + fprintf(stderr, "Entering raw bitbang mode failed!\n"); + return 1; + } + printf_debug("Raw bitbang mode version %c\n", buf[4]); + if (buf[4] != '1') { + fprintf(stderr, "Can't handle raw bitbang mode version %c!\n", + buf[4]); + return 1; + } + /* Enter raw SPI mode */ + buf[0] = 0x01; + ret = buspirate_sendrecv(buf, 1, 4); + if (memcmp(buf, "SPI", 3)) { + fprintf(stderr, "Entering raw SPI mode failed!\n"); + return 1; + } + printf_debug("Raw SPI mode version %c\n", buf[3]); + if (buf[3] != '1') { + fprintf(stderr, "Can't handle raw SPI mode version %c!\n", + buf[3]); + return 1; + } + + /* Initial setup (SPI peripherals config): Enable power, CS high, AUX */ + buf[0] = 0x40 | 0xb; + ret = buspirate_sendrecv(buf, 1, 1); + if (ret) + return 1; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while setting power/CS/AUX!\n"); + return 1; + } + + /* Set speed to 8 MHz */ + buf[0] = 0x60 | 0x7; + ret = buspirate_sendrecv(buf, 1, 1); + if (ret) + return 1; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while setting SPI speed!\n"); + return 1; + } + + /* Set SPI config: output type, idle, clock edge, sample */ + buf[0] = 0x80 | 0xa; + ret = buspirate_sendrecv(buf, 1, 1); + if (ret) + return 1; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while setting SPI config!\n"); + return 1; + } + + /* De-assert CS# */ + buf[0] = 0x03; + ret = buspirate_sendrecv(buf, 1, 1); + if (ret) + return 1; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while raising CS#!\n"); + return 1; + } + + buses_supported = CHIP_BUSTYPE_SPI; + spi_controller = SPI_CONTROLLER_BUSPIRATE; + + return 0; +} + +int buspirate_spi_shutdown(void) +{ + unsigned char buf[5]; + int ret = 0; + + /* Exit raw SPI mode (enter raw bitbang mode) */ + buf[0] = 0x00; + ret = buspirate_sendrecv(buf, 1, 5); + if (ret) + return ret; + if (memcmp(buf, "BBIO", 4)) { + fprintf(stderr, "Entering raw bitbang mode failed!\n"); + return 1; + } + printf_debug("Raw bitbang mode version %c\n", buf[4]); + if (buf[4] != '1') { + fprintf(stderr, "Can't handle raw bitbang mode version %c!\n", + buf[4]); + return 1; + } + /* Reset Bus Pirate (return to user terminal) */ + buf[0] = 0x0f; + ret = buspirate_sendrecv(buf, 1, 0); + if (ret) + return ret; + + /* Shut down serial port communication */ + ret = buspirate_serialport_shutdown(); + if (ret) + return ret; + printf_debug("Bus Pirate shutdown completed.\n"); + + return 0; +} + +int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr) +{ + static unsigned char *buf = NULL; + int i = 0, ret = 0; + + if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16) + return SPI_INVALID_LENGTH; + + /* +2 is pretty arbitrary. */ + buf = realloc(buf, writecnt + readcnt + 2); + if (!buf) { + fprintf(stderr, "Out of memory!\n"); + exit(1); // -1 + } + + /* Assert CS# */ + buf[i++] = 0x02; + ret = buspirate_sendrecv(buf, 1, 1); + if (ret) + return SPI_GENERIC_ERROR; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while lowering CS#!\n"); + return SPI_GENERIC_ERROR; + } + + i = 0; + buf[i++] = 0x10 | (writecnt + readcnt - 1); + memcpy(buf + i, writearr, writecnt); + i += writecnt; + memset(buf + i, 0, readcnt); + ret = buspirate_sendrecv(buf, i + readcnt, i + readcnt); + if (ret) + return SPI_GENERIC_ERROR; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while reading/writing SPI!\n"); + return SPI_GENERIC_ERROR; + } + memcpy(readarr, buf + i, readcnt); + + i = 0; + /* De-assert CS# */ + buf[i++] = 0x03; + ret = buspirate_sendrecv(buf, 1, 1); + if (ret) + return SPI_GENERIC_ERROR; + if (buf[0] != 0x01) { + fprintf(stderr, "Protocol error while raising CS#!\n"); + return SPI_GENERIC_ERROR; + } + + return ret; +} + +int buspirate_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) +{ + /* Maximum read length is 12 bytes, use 8 for now. */ + return spi_read_chunked(flash, buf, start, len, 8); +} + +/* We could do 12-byte writes, but for now we use the generic 1-byte code. */ diff --git a/flash.h b/flash.h index 9f6a8bed..b271b8ac 100644 --- a/flash.h +++ b/flash.h @@ -103,6 +103,9 @@ enum programmer { #endif #if SERPROG_SUPPORT == 1 PROGRAMMER_SERPROG, +#endif +#if BUSPIRATE_SPI_SUPPORT == 1 + PROGRAMMER_BUSPIRATESPI, #endif PROGRAMMER_INVALID /* This must always be the last entry. */ }; @@ -484,6 +487,12 @@ int bitbang_spi_send_command(unsigned int writecnt, unsigned int readcnt, const int bitbang_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); int bitbang_spi_write_256(struct flashchip *flash, uint8_t *buf); +/* buspirate_spi.c */ +int buspirate_spi_init(void); +int buspirate_spi_shutdown(void); +int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); +int buspirate_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); + /* flashrom.c */ extern char *programmer_param; extern int verbose; @@ -526,6 +535,9 @@ enum spi_controller { #endif #if DUMMY_SUPPORT == 1 SPI_CONTROLLER_DUMMY, +#endif +#if BUSPIRATE_SPI_SUPPORT == 1 + SPI_CONTROLLER_BUSPIRATE, #endif SPI_CONTROLLER_INVALID /* This must always be the last entry. */ }; diff --git a/flashrom.c b/flashrom.c index c4cfeef6..7e681ccd 100644 --- a/flashrom.c +++ b/flashrom.c @@ -204,6 +204,25 @@ const struct programmer_entry programmer_table[] = { }, #endif +#if BUSPIRATE_SPI_SUPPORT == 1 + { + .name = "buspiratespi", + .init = buspirate_spi_init, + .shutdown = buspirate_spi_shutdown, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .chip_readb = noop_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = fallback_chip_readn, + .chip_writeb = noop_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, + .delay = internal_delay, + }, +#endif + {}, /* This entry corresponds to PROGRAMMER_INVALID. */ }; diff --git a/spi.c b/spi.c index 29c202c1..f117c8fe 100644 --- a/spi.c +++ b/spi.c @@ -100,6 +100,15 @@ const struct spi_programmer spi_programmer[] = { }, #endif +#if BUSPIRATE_SPI_SUPPORT == 1 + { /* SPI_CONTROLLER_BUSPIRATE */ + .command = buspirate_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = buspirate_spi_read, + .write_256 = spi_chip_write_1, + }, +#endif + {}, /* This entry corresponds to SPI_CONTROLLER_INVALID. */ }; diff --git a/spi.h b/spi.h index 3050ccf9..2fa7dcd1 100644 --- a/spi.h +++ b/spi.h @@ -106,6 +106,7 @@ #define JEDEC_BYTE_PROGRAM_INSIZE 0x00 /* Error codes */ +#define SPI_GENERIC_ERROR -1 #define SPI_INVALID_OPCODE -2 #define SPI_INVALID_ADDRESS -3 #define SPI_INVALID_LENGTH -4 -- cgit v1.2.3 14'>314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473