Patchwork Add programmer for the MSTAR I2C ISP protocol

login
register
about
Submitter Alexandre Boeglin
Date 2014-05-04 16:20:04
Message ID <20140504162004.GA8521@boeglin.org>
Download mbox | patch
Permalink /patch/4138/
State Superseded
Headers show

Comments

Alexandre Boeglin - 2014-05-04 16:20:04
Hi,

Here is a patch, that provides support for the MSTAR ISP protocol.

Basically, among other chips, MSTAR manufactures SoCs that equip TV sets
and computer screens, and it seems that all of their products use the
same in-system programming protocol. Basically, they use the DDC channel
of VGA or DVI connectors, which is actually an I2C bus, to encapsulate
SPI frames (the flash chip is connected to the SoC through an SPI bus).

I wrote this patch since the screen I bought had a software bug, and the
manufacturer only released a new firmware binary, but no tool or
instructions on flashing it.

More details can be found here:
http://boeglin.org/blog/index.php?entry=Flashing-a-BenQ-Z-series-for-free(dom)

I only read code from Linux kernel archives published by Acer to figure
out the protocol (for a touchscreen controller and a NFC chip, both by
MSTAR, that shared the same ISP protocol), so I don't think there are
any legal problems with it.

Please let me know what you think about this patch, and whether you're
interested in including it.


Best regards,
Alex
Stefan Tauner - 2014-05-04 23:10:24
On Sun, 4 May 2014 18:20:04 +0200
Alexandre Boeglin <alex@boeglin.org> wrote:

> Hi,
> 
> Here is a patch, that provides support for the MSTAR ISP protocol.
> 
> Basically, among other chips, MSTAR manufactures SoCs that equip TV sets
> and computer screens, and it seems that all of their products use the
> same in-system programming protocol. Basically, they use the DDC channel
> of VGA or DVI connectors, which is actually an I2C bus, to encapsulate
> SPI frames (the flash chip is connected to the SoC through an SPI bus).
> 
> I wrote this patch since the screen I bought had a software bug, and the
> manufacturer only released a new firmware binary, but no tool or
> instructions on flashing it.
> 
> More details can be found here:
> http://boeglin.org/blog/index.php?entry=Flashing-a-BenQ-Z-series-for-free(dom)
> 
> I only read code from Linux kernel archives published by Acer to figure
> out the protocol (for a touchscreen controller and a NFC chip, both by
> MSTAR, that shared the same ISP protocol), so I don't think there are
> any legal problems with it.
> 
> Please let me know what you think about this patch, and whether you're
> interested in including it.
> 

Hello Alexandre,

that's a hilarious story, thanks for sharing! I just skimmed through
your blog post but it really looks like an awesome RE experience, and
the whole tunnel flash access is just insane :)
I would really love to include this just because of that... I am not
so sure if it would be useful to many... OTOH one of our community
members immediately popped up his screen after reading your story... and
found a MSTAR chip and a flash and already got it to probe successfully,
so... :)

I did not carefully review the patch yet, just a few remarks:
- We need a proper sign-off from you to include the patch, see
  http://www.coreboot.org/Development_Guidelines#Sign-off_Procedure
- I'd like to have at least a dedicated paragraph in the manpage that
  gives some background information, and some explanation regarding
  the dev syntax of course.
- The coding style does not completely follow ours, e.g. the braces
  after ifs should be on the same line.
- The progress print needs to go, I think. It has been decided long ago
  that we either do this right(tm) or not at all. flashrom should never
  go into infinite loops and it should also not be interrupted, hence a
  progress bar is only a placebo, he just have to be patient.
Stefan Tauner - 2014-05-07 22:23:55
On Thu, 8 May 2014 00:07:29 +0200
Idwer Vollering <vidwer@gmail.com> wrote:

> Adding the lead developers to CC.

And the mailing list... this should be public IMHO.

> 2014-05-07 23:59 GMT+02:00 Alexandre Boeglin <alex@boeglin.org>:
> > Le mardi 06 mai 2014 à 16:09, Alexandre Boeglin a écrit:
> >> Le mardi 06 mai 2014 à 15:11, Idwer Vollering a écrit:
> >> > However, using my Dell screen, every file written/read with -p
> >> > mstarddc_spi will have the first byte rotated, so verify will always
> >> > fail:
> >>
> >> I have other screens available, I'll check if they use MSTAR SoCs…
> >
> > Well, I tried with a BenQ FP93GX I had, which also seem to have the same
> > ISP port.
> >
> > I tried to probe it, but with no luck.
> >
> > After closer inspection, it seems that I have the same rotation issue,
> > even in the RES command, which makes it impossible to identify the flash
> > chip:
> >
> > Probing for PMC Pm25LV010, 128 kB: programmer_map_flash_region: mapping
> > flash chip from 0x00000000fffe0000 to 0x0000000000000000
> > RES returned 0x9d 0x7c 0x7f. probe_spi_res3: id1 0x9d7c, id2 0x7f
> >
> > It returns "0x9d 0x7c 0x7f", whereas I think it should return "0x7f 0x9d
> > 0x7c"…
> >
> > But then, without documentation from MSTAR, I don't really know how to
> > differentiate between screens that have the bug, and those that don't.

We could add a programmer parameter and let the user decide/override
until we understand this better. Does the DDC tunnel still work with a
broken firmware or is an external programmer needed if the first try to
modify the firmware fails and the SoC inside the device is reset?
Alexandre Boeglin - 2014-05-08 12:22:09
Le jeudi 08 mai 2014 à 00:23, Stefan Tauner a écrit:
> We could add a programmer parameter and let the user decide/override
> until we understand this better. Does the DDC tunnel still work with a
> broken firmware or is an external programmer needed if the first try to
> modify the firmware fails and the SoC inside the device is reset?

Well, it seems it depends. One user of this mstarddc programmer had an
error during the write operation due to (it seems) an I2C driver working
at less than half of the normal 100kb/s I2Cspeed, but even after
power-cycling the screen, he managed to successfully reflash it using
another laptop.

But in the mean time, the screen was not working properly, and I'm
guessing it was maybe in some kind of emergency mode, maybe an
in-silicon bootloader that allows access to the ISP port.

OTOH, another person, using the "official" MSTAR tool was unable to
reflash his screen after the initial failure. But I didn't get him to
try the programmer I wrote, so I don't know for sure what his problem
was (maybe even just a bug in the official tool).

Anyway, I slightly modified the programmer in the patch revision I sent
this morning. It does not send the reset command (which causes the
screen to exit ISP mode and re-read the flash firmware) if an error
occured. So, as long as the screen remains powered, another attempt can
be made.

But then, with the limited amount of info I had, most of it is just
guesses.

Best regards,
Alex
Idwer Vollering - 2015-02-08 23:53:40
2014-05-04 18:20 GMT+02:00 Alexandre Boeglin <alex@boeglin.org>:
> Hi,
>
> Here is a patch, that provides support for the MSTAR ISP protocol.
>
> Basically, among other chips, MSTAR manufactures SoCs that equip TV sets
> and computer screens, and it seems that all of their products use the
> same in-system programming protocol. Basically, they use the DDC channel
> of VGA or DVI connectors, which is actually an I2C bus, to encapsulate
> SPI frames (the flash chip is connected to the SoC through an SPI bus).
>
> I wrote this patch since the screen I bought had a software bug, and the
> manufacturer only released a new firmware binary, but no tool or
> instructions on flashing it.
>
> More details can be found here:
> http://boeglin.org/blog/index.php?entry=Flashing-a-BenQ-Z-series-for-free(dom)

There seems to be a silicon (=SoC) bug as well.

One can recover by rotating the original image (YOU DO HAVE BACKUPS,
RIGHT :) ) _back_wards - the example assumes the read file is 128
kilobyte large:
tail -c 1 orig_read.bin > realimage.bin; head -c 131071 orig_read.bin
>> realimage.bin; flashrom -p mstar -w realimage.bin --noverify

HTH,

Idwer
Stefan Tauner - 2015-02-14 22:09:08
On Mon, 9 Feb 2015 00:53:40 +0100
Idwer Vollering <vidwer@gmail.com> wrote:

> 2014-05-04 18:20 GMT+02:00 Alexandre Boeglin <alex@boeglin.org>:
> > Hi,
> >
> > Here is a patch, that provides support for the MSTAR ISP protocol.
> >
> > Basically, among other chips, MSTAR manufactures SoCs that equip TV sets
> > and computer screens, and it seems that all of their products use the
> > same in-system programming protocol. Basically, they use the DDC channel
> > of VGA or DVI connectors, which is actually an I2C bus, to encapsulate
> > SPI frames (the flash chip is connected to the SoC through an SPI bus).
> >
> > I wrote this patch since the screen I bought had a software bug, and the
> > manufacturer only released a new firmware binary, but no tool or
> > instructions on flashing it.
> >
> > More details can be found here:
> > http://boeglin.org/blog/index.php?entry=Flashing-a-BenQ-Z-series-for-free(dom)
> 
> There seems to be a silicon (=SoC) bug as well.
> 
> One can recover by rotating the original image (YOU DO HAVE BACKUPS,
> RIGHT :) ) _back_wards - the example assumes the read file is 128
> kilobyte large:
> tail -c 1 orig_read.bin > realimage.bin; head -c 131071 orig_read.bin
> >> realimage.bin; flashrom -p mstar -w realimage.bin --noverify

hm... I wonder if that is a bug in how we do it... but the write
routine looks really trivial. Maybe it would be a good idea to start a
list with monitors, SoC revisions... (whatever ID data is accessible)
that require this hack.
Alexandre Boeglin - 2015-02-16 14:22:26
Hi,

Le samedi 14 février 2015 à 23:09, Stefan Tauner a écrit:
> hm... I wonder if that is a bug in how we do it... but the write
> routine looks really trivial.

Yes, all it does is encapsulating and forwarding data, so I don't really
understand why some bytes are rotated on some models.

> Maybe it would be a good idea to start a list with monitors, SoC
> revisions... (whatever ID data is accessible) that require this hack.

Well, to my knowledge, the BenQ XL2411Z, XL2420Z and XL2720Z were the
only monitors for which a manufacturer released an updated firmware
without providing a tool to flash it.

And I haven't heard either of people attempting to mod LCD firmwares.


Best regards,
Alex

Patch

From 5d7f9b766f59b4b567488fce21a70a6d967549c2 Mon Sep 17 00:00:00 2001
From: Alexandre Boeglin <alex@boeglin.org>
Date: Tue, 29 Apr 2014 21:35:26 +0200
Subject: [PATCH] Add programmer for the MSTAR I2C ISP protocol

---
 Makefile        |  13 ++++
 flashrom.8.tmpl |   2 +
 flashrom.c      |  12 +++
 mstarddc_spi.c  | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 programmer.h    |  11 +++
 5 files changed, 275 insertions(+)
 create mode 100644 mstarddc_spi.c

diff --git a/Makefile b/Makefile
index 346d46a..d6e932e 100644
--- a/Makefile
+++ b/Makefile
@@ -280,6 +280,11 @@  UNSUPPORTED_FEATURES += CONFIG_LINUX_SPI=yes
 else
 override CONFIG_LINUX_SPI = no
 endif
+ifeq ($(CONFIG_MSTARDDC_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_MSTARDDC_SPI=yes
+else
+override CONFIG_MSTARDDC_SPI = no
+endif
 endif
 
 ###############################################################################
@@ -390,6 +395,9 @@  CONFIG_FT2232_SPI ?= yes
 # Always enable Altera USB-Blaster dongles for now.
 CONFIG_USBBLASTER_SPI ?= yes
 
+# Always enable MSTAR DDC for now.
+CONFIG_MSTARDDC_SPI ?= yes
+
 # Always enable dummy tracing for now.
 CONFIG_DUMMY ?= yes
 
@@ -549,6 +557,11 @@  NEED_FTDI := yes
 PROGRAMMER_OBJS += usbblaster_spi.o
 endif
 
+ifeq ($(CONFIG_MSTARDDC_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_MSTARDDC_SPI=1'
+PROGRAMMER_OBJS += mstarddc_spi.o
+endif
+
 ifeq ($(NEED_FTDI), yes)
 FTDILIBS := $(shell pkg-config --libs libftdi 2>/dev/null || printf "%s" "-lftdi -lusb")
 FEATURE_CFLAGS += $(shell LC_ALL=C grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'")
diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl
index 8d46ffe..fe0ac65 100644
--- a/flashrom.8.tmpl
+++ b/flashrom.8.tmpl
@@ -221,6 +221,8 @@  bitbanging adapter)
 .sp
 .BR "* usbblaster_spi" " (for SPI flash ROMs attached to an Altera USB-Blaster compatible cable)"
 .sp
+.BR "* mstarddc_spi" " (for SPI flash ROMs accessible through DDC in MSTAR-equipped displays)"
+.sp
 Some programmers have optional or mandatory parameters which are described
 in detail in the
 .B PROGRAMMER SPECIFIC INFO
diff --git a/flashrom.c b/flashrom.c
index 23728f6..ce0d760 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -321,6 +321,18 @@  const struct programmer_entry programmer_table[] = {
 	},
 #endif
 
+#if CONFIG_MSTARDDC_SPI == 1
+	{
+		.name			= "mstarddc_spi",
+		.type			= OTHER,
+		.devs.note		= "Device files /dev/i2c-*\n",
+		.init			= mstarddc_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
+	},
+#endif
+
 	{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
 };
 
diff --git a/mstarddc_spi.c b/mstarddc_spi.c
new file mode 100644
index 0000000..34f58ad
--- /dev/null
+++ b/mstarddc_spi.c
@@ -0,0 +1,237 @@ 
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2014 Alexandre Boeglin <alex@boeglin.org>
+ *
+ * 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
+ */
+
+#if CONFIG_MSTARDDC_SPI == 1
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+
+
+static const struct spi_programmer spi_programmer_mstarddc;
+
+static int mstarddc_fd;
+static int mstarddc_addr;
+
+// MSTAR DDC Commands
+#define MSTARDDC_SPI_WRITE	0x10
+#define MSTARDDC_SPI_READ	0x11
+#define MSTARDDC_SPI_END	0x12
+#define MSTARDDC_SPI_RESET	0x24
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int mstarddc_spi_shutdown(void *data)
+{
+	// Reset, disables ISP mode
+	uint8_t cmd = MSTARDDC_SPI_RESET;
+	if (write(mstarddc_fd, &cmd, 1) < 0)
+	{
+		int err = errno;
+		msg_perr("Error sending reset command: errno %d\n", err);
+		return -1;
+	}
+
+	if (close(mstarddc_fd) < 0)
+	{
+		int err = errno;
+		msg_perr("Error closing device: errno %d\n", err);
+		return -1;
+	}
+	return 0;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+int mstarddc_spi_init(void)
+{
+	char *i2c_device;
+	char *i2c_address;
+
+	// Get device, address from commandline
+	i2c_device = extract_programmer_param("dev");
+	if (i2c_device && strlen(i2c_device))
+	{
+		if ((i2c_address = strchr(i2c_device, ':')))
+		{
+			*i2c_address = '\0';
+			i2c_address++;
+		}
+		if (!i2c_address || !strlen(i2c_address))
+		{
+			msg_perr("Error: no address specified.\n"
+					"Use flashrom -p mstarddc_spi:dev=/dev/device:address\n");
+			return -1;
+		}
+		mstarddc_addr = strtol(i2c_address, NULL, 16);
+	}
+	else
+	{
+		msg_perr("Error: no device specified.\n"
+				"Use flashrom -p mstarddc_spi:dev=/dev/device:address\n");
+		return -1;
+	}
+	msg_pinfo("Info: Will try to use device %s and address 0x%02x\n", i2c_device, mstarddc_addr);
+
+	// Open device
+	if ((mstarddc_fd = open(i2c_device, O_RDWR)) < 0)
+	{
+		int err = errno;
+		switch (err)
+		{
+		case EACCES:
+			msg_perr("Error opening %s: Permission denied.\n"
+					"Please use sudo or run as root\n", i2c_device);
+			break;
+		case ENOENT:
+			msg_perr("Error opening %s: No such file.\n"
+					"Please check you specified the correct device\n", i2c_device);
+			break;
+		default:
+			msg_perr("Error opening %s: errno %d\n", i2c_device, err);
+		}
+		return -1;
+	}
+
+	// Set slave address
+	if (ioctl(mstarddc_fd, I2C_SLAVE, mstarddc_addr) < 0)
+	{
+		int err = errno;
+		msg_perr("Error setting slave address 0x%02x: errno %d\n", mstarddc_addr, err);
+		return -1;
+	}
+
+	// Enable ISP mode
+	uint8_t cmd[5] = {'M', 'S', 'T', 'A', 'R'};
+	if (write(mstarddc_fd, cmd, 5) < 0)
+	{
+		int err = errno;
+		char end_cmd = MSTARDDC_SPI_END;
+
+		// Assume device is already in ISP mode, try to send END command
+		if (write(mstarddc_fd, &end_cmd, 1) < 0)
+		{
+			msg_perr("Error enabling ISP mode: errno %d\n"
+					"Please check that device (%s) and address (0x%02x) are correct\n",
+					err, i2c_device, mstarddc_addr);
+			return -1;
+		}
+	}
+
+	// Register shutdown function
+	register_shutdown(mstarddc_spi_shutdown, NULL);
+
+	// Register programmer
+	register_spi_programmer(&spi_programmer_mstarddc);
+	return 0;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int mstarddc_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+		const unsigned char *writearr, unsigned char *readarr)
+{
+	int ret = 0;
+	uint8_t *cmd;
+	static int count = 0;
+
+	if (++count % 64 == 0)
+		// At 100kb/s, it's better to show the user something is still happening
+		msg_pinfo(".");
+
+	if ((cmd = malloc((writecnt + 1) * sizeof(uint8_t))) < 0)
+	{
+		int err = errno;
+		msg_perr("Error allocating memory: errno %d\n", err);
+		ret = -1;
+	}
+
+	if (!ret && writecnt)
+	{
+		cmd[0] = MSTARDDC_SPI_WRITE;
+		memcpy(cmd + 1, writearr, writecnt);
+		if (write(mstarddc_fd, cmd, writecnt + 1) < 0)
+		{
+			int err = errno;
+			msg_perr("Error sending write command: errno %d\n", err);
+			ret = -1;
+		}
+	}
+
+	if (!ret && readcnt)
+	{
+		struct i2c_rdwr_ioctl_data i2c_data;
+		struct i2c_msg msg[2];
+
+		cmd[0] = MSTARDDC_SPI_READ;
+		i2c_data.nmsgs = 2;
+		i2c_data.msgs = msg;
+		i2c_data.msgs[0].addr = mstarddc_addr;
+		i2c_data.msgs[0].len = 1;
+		i2c_data.msgs[0].flags = 0;
+		i2c_data.msgs[0].buf = cmd;
+		i2c_data.msgs[1].addr = mstarddc_addr;
+		i2c_data.msgs[1].len = readcnt;
+		i2c_data.msgs[1].flags = I2C_M_RD;
+		i2c_data.msgs[1].buf = readarr;
+
+		if (ioctl(mstarddc_fd, I2C_RDWR, &i2c_data) < 0)
+		{
+			int err = errno;
+			msg_perr("Error sending read command: errno %d\n", err);
+			ret = -1;
+		}
+	}
+
+	if (!ret && (writecnt || readcnt))
+	{
+		cmd[0] = MSTARDDC_SPI_END;
+		if (write(mstarddc_fd, cmd, 1) < 0)
+		{
+			int err = errno;
+			msg_perr("Error sending end command: errno %d\n", err);
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+
+static const struct spi_programmer spi_programmer_mstarddc = {
+	.type		= SPI_CONTROLLER_MSTARDDC,
+	.max_data_read	= 256,
+	.max_data_write	= 256,
+	.command	= mstarddc_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= default_spi_write_256,
+	.write_aai	= default_spi_write_aai,
+};
+
+#endif
diff --git a/programmer.h b/programmer.h
index 4f4cc02..9aa2c8d 100644
--- a/programmer.h
+++ b/programmer.h
@@ -90,6 +90,9 @@  enum programmer {
 #if CONFIG_USBBLASTER_SPI == 1
 	PROGRAMMER_USBBLASTER_SPI,
 #endif
+#if CONFIG_MSTARDDC_SPI == 1
+	PROGRAMMER_MSTARDDC_SPI,
+#endif
 	PROGRAMMER_INVALID /* This must always be the last entry. */
 };
 
@@ -446,6 +449,11 @@  int usbblaster_spi_init(void);
 extern const struct dev_entry devs_usbblasterspi[];
 #endif
 
+/* mstarddc_spi.c */
+#if CONFIG_MSTARDDC_SPI == 1
+int mstarddc_spi_init(void);
+#endif
+
 /* rayer_spi.c */
 #if CONFIG_RAYER_SPI == 1
 int rayer_spi_init(void);
@@ -527,6 +535,9 @@  enum spi_controller {
 #if CONFIG_USBBLASTER_SPI == 1
 	SPI_CONTROLLER_USBBLASTER,
 #endif
+#if CONFIG_MSTARDDC_SPI == 1
+	SPI_CONTROLLER_MSTARDDC,
+#endif
 };
 
 #define MAX_DATA_UNSPECIFIED 0
-- 
1.9.2