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