1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <ddk/binding.h> 6#include <ddk/debug.h> 7#include <ddk/device.h> 8#include <ddk/usb/usb.h> 9#include <zircon/device/usb-test-fwloader.h> 10#include <zircon/hw/usb.h> 11#include <zircon/assert.h> 12 13#include <stdlib.h> 14#include <string.h> 15 16#include "fx3.h" 17 18// The header contains the 2 byte "CY" signature, and 2 byte image metadata. 19#define IMAGE_HEADER_SIZE 4 20 21#define VENDOR_REQ_MAX_SIZE 4096 22#define VENDOR_REQ_TIMEOUT_SECS 1 23 24#define LSW(x) ((x) & 0xffff) 25#define MSW(x) ((x) >> 16) 26#define MIN(a, b) (((a) < (b)) ? (a) : (b)) 27 28typedef struct { 29 zx_device_t* zxdev; 30 usb_protocol_t usb; 31} fx3_t; 32 33static zx_status_t fx3_write(fx3_t* fx3, uint8_t* buf, size_t len, uint32_t addr) { 34 if (len > VENDOR_REQ_MAX_SIZE) { 35 return ZX_ERR_INVALID_ARGS; 36 } 37 size_t out_len; 38 zx_status_t status = usb_control(&fx3->usb, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 39 FX3_REQ_FIRMWARE_TRANSFER, LSW(addr), MSW(addr), buf, len, 40 ZX_SEC(VENDOR_REQ_TIMEOUT_SECS), &out_len); 41 if (status != ZX_OK) { 42 return status; 43 } else if (out_len != len) { 44 zxlogf(ERROR, "fx3_write returned bad len, want: %lu, got: %lu\n", len, out_len); 45 return ZX_ERR_IO; 46 } 47 return ZX_OK; 48} 49 50// Jumps to the given address on FX3 System RAM. 51static zx_status_t fx3_program_entry(fx3_t* fx3, uint32_t ram_addr) { 52 return fx3_write(fx3, NULL, 0, ram_addr); 53} 54 55static zx_status_t fx3_validate_image_header(fx3_t* fx3, zx_handle_t fw_vmo) { 56 uint8_t header[IMAGE_HEADER_SIZE]; 57 zx_status_t status = zx_vmo_read(fw_vmo, &header, 0, IMAGE_HEADER_SIZE); 58 if (status != ZX_OK) { 59 return status; 60 } 61 if (header[0] != 'C' || header[1] != 'Y') { 62 return ZX_ERR_BAD_STATE; 63 } 64 zxlogf(TRACE, "image header: ctl 0x%02x type 0x%02x\n", header[2], header[3]); 65 return ZX_OK; 66} 67 68// Writes the section data at the given device RAM address. 69// Returns ZX_OK if successful, and increments checksum with the section checksum. 70static zx_status_t fx3_write_section(fx3_t* fx3, zx_handle_t fw_vmo, size_t offset, 71 size_t len, uint32_t ram_addr, uint32_t* checksum) { 72 uint8_t write_buf[VENDOR_REQ_MAX_SIZE]; 73 74 while (len > 0) { 75 size_t len_to_write = MIN(len, VENDOR_REQ_MAX_SIZE); 76 ZX_DEBUG_ASSERT(len_to_write % 4 == 0); 77 zx_status_t status = zx_vmo_read(fw_vmo, write_buf, offset, len_to_write); 78 if (status != ZX_OK) { 79 return status; 80 } 81 status = fx3_write(fx3, write_buf, len_to_write, ram_addr); 82 if (status != ZX_OK) { 83 return status; 84 } 85 uint32_t* image_data = (uint32_t*)write_buf; 86 for (uint32_t i = 0; i < len_to_write / 4; ++i) { 87 *checksum += image_data[i]; 88 } 89 len -= len_to_write; 90 offset += len_to_write; 91 ram_addr += len_to_write; 92 } 93 return ZX_OK; 94} 95 96// Writes the firmware to the device RAM and boots it. 97static zx_status_t fx3_load_firmware(fx3_t* fx3, zx_handle_t fw_vmo) { 98 size_t vmo_size; 99 zx_status_t status = zx_vmo_get_size(fw_vmo, &vmo_size); 100 if (status != ZX_OK) { 101 zxlogf(ERROR, "failed to get firmware vmo size, err: %d\n", status); 102 return ZX_ERR_INVALID_ARGS; 103 } 104 // The fwloader expects the firmware image file to be in the format shown in 105 // EZ-USB/FX3 Boot Options, Table 14. 106 status = fx3_validate_image_header(fx3, fw_vmo); 107 if (status != ZX_OK) { 108 zxlogf(ERROR, "invalid firmware image header, err: %d\n", status); 109 return status; 110 } 111 112 size_t offset = IMAGE_HEADER_SIZE; 113 uint32_t checksum = 0; 114 // Section header fields. 115 uint32_t len_dwords = 0; 116 uint32_t ram_addr = 0; 117 while (offset < vmo_size) { 118 // Read the section header, containing the section length in long words, and ram address. 119 status = zx_vmo_read(fw_vmo, &len_dwords, offset, sizeof(len_dwords)); 120 if (status != ZX_OK) { 121 return status; 122 } 123 offset += sizeof(len_dwords); 124 status = zx_vmo_read(fw_vmo, &ram_addr, offset, sizeof(ram_addr)); 125 if (status != ZX_OK) { 126 return status; 127 } 128 offset += sizeof(ram_addr); 129 zxlogf(TRACE, "section len %u B ram addr 0x%x\n", len_dwords * 4, ram_addr); 130 131 if (len_dwords == 0) { 132 // Reached termination of image. 133 break; 134 } 135 status = fx3_write_section(fx3, fw_vmo, offset, len_dwords * 4, ram_addr, &checksum); 136 if (status != ZX_OK) { 137 zxlogf(ERROR, "fx3_write_section failed, err: %d\n", status); 138 return status; 139 } 140 offset += (len_dwords * 4); 141 } 142 if (len_dwords != 0) { 143 // Didn't get termination of image indicator. 144 return ZX_ERR_BAD_STATE; 145 } 146 uint32_t expected_checksum; 147 status = zx_vmo_read(fw_vmo, &expected_checksum, offset, sizeof(expected_checksum)); 148 if (status != ZX_OK) { 149 zxlogf(ERROR, "could not read expected checksum, err: %d\n", status); 150 return status; 151 } 152 if (checksum != expected_checksum) { 153 zxlogf(ERROR, "got bad checksum %u, want %u\n", checksum, expected_checksum); 154 return ZX_ERR_BAD_STATE; 155 } 156 status = fx3_program_entry(fx3, ram_addr); 157 if (status == ZX_OK) { 158 return ZX_OK; 159 } else if (status == ZX_ERR_IO_REFUSED) { 160 // When using the second stage bootloader, the control request may send an error code 161 // back after we jump to the program entry. 162 zxlogf(TRACE, "fx3_program_entry got expected err: %d\n", status); 163 return ZX_OK; 164 } else { 165 zxlogf(ERROR, "fx3_program_entry got unexpected err: %d\n", status); 166 return status; 167 } 168} 169 170static zx_status_t fx3_ioctl(void* ctx, uint32_t op, const void* in_buf, size_t in_len, 171 void* out_buf, size_t out_len, size_t* out_actual) { 172 fx3_t* fx3 = ctx; 173 174 switch (op) { 175 case IOCTL_USB_TEST_FWLOADER_LOAD_FIRMWARE: { 176 if (in_buf == NULL || in_len != sizeof(zx_handle_t)) { 177 return ZX_ERR_INVALID_ARGS; 178 } 179 const zx_handle_t* fw_vmo = in_buf; 180 zx_status_t status = fx3_load_firmware(fx3, *fw_vmo); 181 zx_handle_close(*fw_vmo); 182 return status; 183 } 184 default: 185 return ZX_ERR_NOT_SUPPORTED; 186 } 187} 188 189static void fx3_free(fx3_t* ctx) { 190 free(ctx); 191} 192 193static void fx3_unbind(void* ctx) { 194 zxlogf(INFO, "fx3_unbind\n"); 195 fx3_t* fx3 = ctx; 196 197 device_remove(fx3->zxdev); 198} 199 200static void fx3_release(void* ctx) { 201 fx3_t* fx3 = ctx; 202 fx3_free(fx3); 203} 204 205static zx_protocol_device_t fx3_device_protocol = { 206 .version = DEVICE_OPS_VERSION, 207 .ioctl = fx3_ioctl, 208 .unbind = fx3_unbind, 209 .release = fx3_release, 210}; 211 212static zx_status_t fx3_bind(void* ctx, zx_device_t* device) { 213 zxlogf(TRACE, "fx3_bind\n"); 214 215 fx3_t* fx3 = calloc(1, sizeof(fx3_t)); 216 if (!fx3) { 217 return ZX_ERR_NO_MEMORY; 218 } 219 zx_status_t result = device_get_protocol(device, ZX_PROTOCOL_USB, &fx3->usb); 220 if (result != ZX_OK) { 221 fx3_free(fx3); 222 return result; 223 } 224 225 device_add_args_t args = { 226 .version = DEVICE_ADD_ARGS_VERSION, 227 .name = "fx3", 228 .ctx = fx3, 229 .ops = &fx3_device_protocol, 230 .flags = DEVICE_ADD_NON_BINDABLE, 231 .proto_id = ZX_PROTOCOL_USB_TEST_FWLOADER, 232 }; 233 234 zx_status_t status = device_add(device, &args, &fx3->zxdev); 235 if (status != ZX_OK) { 236 fx3_free(fx3); 237 return status; 238 } 239 return ZX_OK; 240} 241 242static zx_driver_ops_t fx3_driver_ops = { 243 .version = DRIVER_OPS_VERSION, 244 .bind = fx3_bind, 245}; 246 247ZIRCON_DRIVER_BEGIN(fx3, fx3_driver_ops, "zircon", "0.1", 4) 248 BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_USB), 249 BI_ABORT_IF(NE, BIND_USB_VID, CYPRESS_VID), 250 BI_MATCH_IF(EQ, BIND_USB_PID, FX3_DEFAULT_BOOTLOADER_PID), 251 BI_MATCH_IF(EQ, BIND_USB_PID, FX3_SECOND_STAGE_BOOTLOADER_PID), 252ZIRCON_DRIVER_END(fx3) 253