@@ -2029,6 +2029,22 @@ static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr,
return 0;
}
+static int ftdi_write_eeprom(struct usb_serial_port *port, u8 addr, u16 data)
+{
+ struct usb_device *udev = port->serial->dev;
+ int rv;
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_WRITE_EEPROM_REQUEST,
+ FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE,
+ data, addr,
+ NULL, 0, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&port->dev, "Unable to write EEPROM: %i\n", rv);
+ return rv;
+}
+
static int ftdi_gpio_init_ft232h(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -2234,10 +2250,100 @@ static int ftdi_sio_probe(struct usb_serial *serial,
return 0;
}
+static u16 ftdi_checksum(u16 *data, int n)
+{
+ u16 checksum;
+ int i;
+
+ checksum = 0xaaaa;
+ for (i = 0; i < n - 1; i++) {
+ checksum ^= be16_to_cpu(data[i]);
+ checksum = (checksum << 1) | (checksum >> 15);
+ }
+
+ return cpu_to_be16(checksum);
+}
+
+static int ftdi_repair_brick(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int orig_latency;
+ int rv;
+ u16 *eeprom_data;
+ u16 checksum;
+ int eeprom_size;
+ int result;
+
+ switch (priv->chip_type) {
+ case FT232RL:
+ eeprom_size = 0x40;
+ break;
+ default:
+ /* Unsupported for brick repair */
+ return 0;
+ }
+
+ /* Latency timer needs to be 0x77 to unlock EEPROM programming */
+ if (priv->latency != 0x77) {
+ orig_latency = priv->latency;
+ priv->latency = 0x77;
+ rv = write_latency_timer(port);
+ priv->latency = orig_latency;
+ if (rv < 0)
+ return -EIO;
+ }
+
+ eeprom_data = kmalloc(eeprom_size * 2, GFP_KERNEL);
+ if (!eeprom_data)
+ return -ENOMEM;
+
+ /* Read in EEPROM */
+ result = ftdi_read_eeprom(port->serial, eeprom_data, 0x00, eeprom_size * 2);
+ if (result < 0)
+ goto end_repair_brick;
+
+ /* Verify EEPROM is valid */
+ checksum = ftdi_checksum(eeprom_data, eeprom_size);
+ if (checksum != eeprom_data[eeprom_size - 1])
+ goto end_repair_brick;
+
+ /* FTDI driver checksum preimage attack targets address 62 */
+ if (eeprom_data[62] == 0)
+ goto end_repair_brick;
+
+ /* Attempt to restore Product ID to 0x6001 */
+ eeprom_data[2] = FTDI_8U232AM_PID;
+
+ /* Clear preimage attack target address */
+ eeprom_data[62] = 0;
+
+ /* Calculate and verify new checksum */
+ checksum = ftdi_checksum(eeprom_data, eeprom_size);
+ if (checksum != eeprom_data[eeprom_size - 1])
+ goto end_repair_brick;
+
+ /* Restore EEPROM PID to original pre-brick state */
+ if (ftdi_write_eeprom(port, 2, eeprom_data[2]) < 0)
+ goto end_repair_brick;
+
+ /* Restore EEPROM preimage target address to original pre-brick state */
+ if (ftdi_write_eeprom(port, 62, eeprom_data[62]) < 0)
+ goto end_repair_brick;
+
+ dev_info(&port->dev, "Successfully repaired eeprom bricked by FTDI's malicious Windows driver.\n");
+
+end_repair_brick:
+ kfree(eeprom_data);
+
+ return result;
+}
+
static int ftdi_sio_port_probe(struct usb_serial_port *port)
{
struct ftdi_private *priv;
const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
+ u16 vendor_id;
+ u16 product_id;
int result;
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
@@ -2255,6 +2361,12 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
ftdi_set_max_packet_size(port);
if (read_latency_timer(port) < 0)
priv->latency = 16;
+ vendor_id = le16_to_cpu(port->serial->dev->descriptor.idVendor);
+ product_id = le16_to_cpu(port->serial->dev->descriptor.idProduct);
+ if (vendor_id == FTDI_VID &&
+ product_id == FTDI_BRICK_PID &&
+ priv->chip_type == FT232RL)
+ ftdi_repair_brick(port);
write_latency_timer(port);
create_sysfs_attrs(port);
@@ -39,6 +39,7 @@
#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */
#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
+#define FTDI_SIO_WRITE_EEPROM 0x91 /* Write EEPROM */
/* Interface indices for FT2232, FT2232H and FT4232H devices */
#define INTERFACE_A 1
@@ -457,6 +458,9 @@ enum ftdi_sio_baudrate {
#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0
#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
+#define FTDI_SIO_WRITE_EEPROM_REQUEST_TYPE 0x40
+#define FTDI_SIO_WRITE_EEPROM_REQUEST FTDI_SIO_WRITE_EEPROM
+
#define FTDI_FTX_CBUS_MUX_GPIO 0x8
#define FTDI_FT232R_CBUS_MUX_GPIO 0xa